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
commitc61c7452d8c324eeadd129377bcb49144e3677bb (patch)
treeed1c5aabecb0df0d2bf47a07916d296e3de3146e
downloadcups-c61c7452d8c324eeadd129377bcb49144e3677bb.tar.gz
Import cups.org releasesrelease-1.1b3
git-svn-id: svn+ssh://src.apple.com/svn/cups/cups.org/tags/release-1.1b3@4306 a1ca3aef-8c08-0410-bb20-df032aa958be
-rw-r--r--CHANGES.txt299
-rw-r--r--CREDITS.txt26
-rw-r--r--LICENSE.html386
-rw-r--r--Makedefs.in147
-rw-r--r--Makefile94
-rw-r--r--README.txt242
-rw-r--r--backend/Makefile139
-rw-r--r--backend/betest.c85
-rw-r--r--backend/ipp.c675
-rw-r--r--backend/lpd.c420
-rw-r--r--backend/parallel.c528
-rw-r--r--backend/serial.c670
-rw-r--r--backend/socket.c259
-rw-r--r--backend/usb.c328
-rw-r--r--berkeley/Makefile103
-rw-r--r--berkeley/lpc.c466
-rw-r--r--berkeley/lpq.c488
-rw-r--r--berkeley/lpr.c315
-rw-r--r--berkeley/lprm.c221
-rw-r--r--cgi-bin/Makefile117
-rw-r--r--cgi-bin/admin.c1357
-rw-r--r--cgi-bin/cgi.h83
-rw-r--r--cgi-bin/classes.c301
-rw-r--r--cgi-bin/html.c89
-rw-r--r--cgi-bin/ipp-var.c211
-rw-r--r--cgi-bin/ipp-var.h55
-rw-r--r--cgi-bin/jobs.c139
-rw-r--r--cgi-bin/printers.c301
-rw-r--r--cgi-bin/template.c454
-rw-r--r--cgi-bin/var.c656
-rw-r--r--conf/Makefile72
-rw-r--r--conf/classes.conf95
-rw-r--r--conf/client.conf50
-rw-r--r--conf/cupsd.conf482
-rw-r--r--conf/mime.convs64
-rw-r--r--conf/mime.types124
-rw-r--r--conf/printcap2
-rw-r--r--conf/printers.conf102
-rw-r--r--config.h.in127
-rw-r--r--configure.in418
-rw-r--r--cups.dsw128
-rw-r--r--cups.list441
-rwxr-xr-xcups.sh112
-rw-r--r--cups.spec138
-rw-r--r--cups/Makefile153
-rw-r--r--cups/cups.dsp180
-rw-r--r--cups/cups.h145
-rw-r--r--cups/cups_C.h132
-rw-r--r--cups/debug.h57
-rw-r--r--cups/dest.c472
-rw-r--r--cups/emit.c301
-rw-r--r--cups/http.c1546
-rw-r--r--cups/http.h315
-rw-r--r--cups/ipp.c1683
-rw-r--r--cups/ipp.h363
-rw-r--r--cups/language.c403
-rw-r--r--cups/language.h222
-rw-r--r--cups/mark.c431
-rw-r--r--cups/md5.c392
-rw-r--r--cups/md5.h94
-rw-r--r--cups/options.c379
-rw-r--r--cups/page.c189
-rw-r--r--cups/ppd.c1818
-rw-r--r--cups/ppd.h241
-rw-r--r--cups/snprintf.c287
-rw-r--r--cups/string.c125
-rw-r--r--cups/string.h94
-rw-r--r--cups/testhttp.c109
-rw-r--r--cups/testmime.dsp102
-rw-r--r--cups/testppd.c183
-rw-r--r--cups/testppd.dsp102
-rw-r--r--cups/usersys.c193
-rw-r--r--cups/util.c1273
-rw-r--r--data/8859-1216
-rw-r--r--data/8859-14217
-rw-r--r--data/8859-15217
-rw-r--r--data/8859-2218
-rw-r--r--data/8859-3209
-rw-r--r--data/8859-4216
-rw-r--r--data/8859-5216
-rw-r--r--data/8859-6171
-rw-r--r--data/8859-7210
-rw-r--r--data/8859-8178
-rw-r--r--data/8859-9218
-rw-r--r--data/HPGLprolog37
-rw-r--r--data/Makefile86
-rw-r--r--data/cp1250254
-rw-r--r--data/cp1251258
-rw-r--r--data/cp1252254
-rw-r--r--data/cp1253243
-rw-r--r--data/cp1254252
-rw-r--r--data/cp1255236
-rw-r--r--data/cp1256259
-rw-r--r--data/cp1257247
-rw-r--r--data/cp1258250
-rw-r--r--data/cp874228
-rw-r--r--data/iso-8859-1251
-rw-r--r--data/iso-8859-10251
-rw-r--r--data/iso-8859-13251
-rw-r--r--data/iso-8859-14251
-rw-r--r--data/iso-8859-15251
-rw-r--r--data/iso-8859-2253
-rw-r--r--data/iso-8859-3244
-rw-r--r--data/iso-8859-4251
-rw-r--r--data/iso-8859-5251
-rw-r--r--data/iso-8859-6206
-rw-r--r--data/iso-8859-7246
-rw-r--r--data/iso-8859-8214
-rw-r--r--data/iso-8859-9251
-rw-r--r--data/psglyphs1051
-rw-r--r--data/testprint.ps505
-rw-r--r--data/utf-838
-rw-r--r--doc/Makefile158
-rw-r--r--doc/cmp.html676
-rw-r--r--doc/cmp.pdf765
-rw-r--r--doc/cmp.shtml595
-rw-r--r--doc/cups.css4
-rw-r--r--doc/cupsdoc.css9
-rw-r--r--doc/documentation.html77
-rw-r--r--doc/figures.scbin0 -> 51020 bytes
-rw-r--r--doc/glossary.shtml76
-rw-r--r--doc/idd.html1058
-rw-r--r--doc/idd.pdf814
-rw-r--r--doc/idd.shtml1417
-rw-r--r--doc/images/accept-jobs.gifbin0 -> 259 bytes
-rw-r--r--doc/images/add-class.gifbin0 -> 242 bytes
-rw-r--r--doc/images/add-printer.gifbin0 -> 252 bytes
-rw-r--r--doc/images/cancel-job.gifbin0 -> 248 bytes
-rw-r--r--doc/images/cancel-jobs.gifbin0 -> 255 bytes
-rw-r--r--doc/images/cancel.gifbin0 -> 210 bytes
-rw-r--r--doc/images/classes.gifbin0 -> 591 bytes
-rw-r--r--doc/images/config-printer.gifbin0 -> 296 bytes
-rw-r--r--doc/images/continue.gifbin0 -> 224 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/delete-class.gifbin0 -> 259 bytes
-rw-r--r--doc/images/delete-printer.gifbin0 -> 267 bytes
-rw-r--r--doc/images/draft.gifbin0 -> 926 bytes
-rw-r--r--doc/images/hold-job.gifbin0 -> 228 bytes
-rw-r--r--doc/images/left.gifbin0 -> 110 bytes
-rw-r--r--doc/images/logo.gifbin0 -> 1958 bytes
-rw-r--r--doc/images/manage-classes.gifbin0 -> 289 bytes
-rw-r--r--doc/images/manage-jobs.gifbin0 -> 266 bytes
-rw-r--r--doc/images/manage-printers.gifbin0 -> 296 bytes
-rw-r--r--doc/images/modify-class.gifbin0 -> 267 bytes
-rw-r--r--doc/images/modify-printer.gifbin0 -> 277 bytes
-rw-r--r--doc/images/navbar.gifbin0 -> 2869 bytes
-rw-r--r--doc/images/navbar.xcf.gzbin0 -> 4253 bytes
-rw-r--r--doc/images/print-test-page.gifbin0 -> 288 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/images/reject-jobs.gifbin0 -> 252 bytes
-rw-r--r--doc/images/release-job.gifbin0 -> 255 bytes
-rw-r--r--doc/images/restart-job.gifbin0 -> 249 bytes
-rw-r--r--doc/images/right.gifbin0 -> 145 bytes
-rw-r--r--doc/images/show-active.gifbin0 -> 303 bytes
-rw-r--r--doc/images/show-completed.gifbin0 -> 337 bytes
-rw-r--r--doc/images/start-class.gifbin0 -> 238 bytes
-rw-r--r--doc/images/start-printer.gifbin0 -> 255 bytes
-rw-r--r--doc/images/stop-class.gifbin0 -> 245 bytes
-rw-r--r--doc/images/stop-printer.gifbin0 -> 252 bytes
-rw-r--r--doc/index.html36
-rw-r--r--doc/ipp.html1301
-rw-r--r--doc/ipp.pdf1380
-rw-r--r--doc/ipp.shtml1838
-rw-r--r--doc/overview.html292
-rw-r--r--doc/overview.pdfbin0 -> 20422 bytes
-rw-r--r--doc/references.shtml40
-rw-r--r--doc/sam.html937
-rw-r--r--doc/sam.pdf841
-rw-r--r--doc/sam.shtml960
-rw-r--r--doc/sdd.html494
-rw-r--r--doc/sdd.pdf769
-rw-r--r--doc/sdd.shtml435
-rw-r--r--doc/spm.html3709
-rw-r--r--doc/spm.pdf7676
-rw-r--r--doc/spm.shtml4202
-rw-r--r--doc/ssr.html202
-rw-r--r--doc/ssr.pdfbin0 -> 23173 bytes
-rw-r--r--doc/ssr.shtml165
-rw-r--r--doc/stp.html172
-rw-r--r--doc/stp.pdfbin0 -> 21743 bytes
-rw-r--r--doc/stp.shtml47
-rw-r--r--doc/sum.html543
-rw-r--r--doc/sum.pdf512
-rw-r--r--doc/sum.shtml465
-rw-r--r--doc/svd.shtml156
-rw-r--r--doc/system-overview.shtml20
-rw-r--r--filter/Makefile191
-rw-r--r--filter/common.c255
-rw-r--r--filter/common.h67
-rw-r--r--filter/form-main.c60
-rw-r--r--filter/form-ps.c47
-rw-r--r--filter/form-tree.c622
-rw-r--r--filter/form.h175
-rw-r--r--filter/hpgl-attr.c442
-rw-r--r--filter/hpgl-char.c498
-rw-r--r--filter/hpgl-config.c638
-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.c375
-rw-r--r--filter/hpgl-vector.c707
-rw-r--r--filter/hpgltops.h232
-rw-r--r--filter/image-colorspace.c882
-rw-r--r--filter/image-gif.c644
-rw-r--r--filter/image-jpeg.c190
-rw-r--r--filter/image-photocd.c323
-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.c377
-rw-r--r--filter/image-tiff.c1621
-rw-r--r--filter/image-zoom.c310
-rw-r--r--filter/image.c780
-rw-r--r--filter/image.h225
-rw-r--r--filter/imagetops.c564
-rw-r--r--filter/imagetoraster.c3975
-rw-r--r--filter/pstops.c870
-rw-r--r--filter/raster.c252
-rw-r--r--filter/raster.h233
-rw-r--r--filter/rastertoepson.c596
-rw-r--r--filter/rastertohp.c503
-rw-r--r--filter/textcommon.c746
-rw-r--r--filter/textcommon.h93
-rw-r--r--filter/texttops.c1205
-rw-r--r--fonts/AvantGarde-Bookbin0 -> 34871 bytes
-rw-r--r--fonts/AvantGarde-BookObliquebin0 -> 35156 bytes
-rw-r--r--fonts/AvantGarde-Demibin0 -> 36354 bytes
-rw-r--r--fonts/AvantGarde-DemiObliquebin0 -> 36128 bytes
-rw-r--r--fonts/Bookman-Demibin0 -> 44768 bytes
-rw-r--r--fonts/Bookman-DemiItalicbin0 -> 44950 bytes
-rw-r--r--fonts/Bookman-Lightbin0 -> 44934 bytes
-rw-r--r--fonts/Bookman-LightItalicbin0 -> 44162 bytes
-rw-r--r--fonts/Charter-Boldbin0 -> 33799 bytes
-rw-r--r--fonts/Charter-BoldItalicbin0 -> 35229 bytes
-rw-r--r--fonts/Charter-Italicbin0 -> 35118 bytes
-rw-r--r--fonts/Charter-Romanbin0 -> 34869 bytes
-rw-r--r--fonts/Courierbin0 -> 45758 bytes
-rw-r--r--fonts/Courier-Boldbin0 -> 50493 bytes
-rw-r--r--fonts/Courier-BoldObliquebin0 -> 51527 bytes
-rw-r--r--fonts/Courier-Obliquebin0 -> 44404 bytes
-rw-r--r--fonts/Helveticabin0 -> 36026 bytes
-rw-r--r--fonts/Helvetica-Boldbin0 -> 35941 bytes
-rw-r--r--fonts/Helvetica-BoldObliquebin0 -> 39013 bytes
-rw-r--r--fonts/Helvetica-Narrowbin0 -> 36615 bytes
-rw-r--r--fonts/Helvetica-Narrow-Boldbin0 -> 37240 bytes
-rw-r--r--fonts/Helvetica-Narrow-BoldObliquebin0 -> 38310 bytes
-rw-r--r--fonts/Helvetica-Narrow-Obliquebin0 -> 37247 bytes
-rw-r--r--fonts/Helvetica-Obliquebin0 -> 38314 bytes
-rw-r--r--fonts/Makefile73
-rw-r--r--fonts/NewCenturySchlbk-Boldbin0 -> 48864 bytes
-rw-r--r--fonts/NewCenturySchlbk-BoldItalicbin0 -> 47083 bytes
-rw-r--r--fonts/NewCenturySchlbk-Italicbin0 -> 45832 bytes
-rw-r--r--fonts/NewCenturySchlbk-Romanbin0 -> 46830 bytes
-rw-r--r--fonts/Palatino-Boldbin0 -> 52406 bytes
-rw-r--r--fonts/Palatino-BoldItalicbin0 -> 51285 bytes
-rw-r--r--fonts/Palatino-Italicbin0 -> 50022 bytes
-rw-r--r--fonts/Palatino-Romanbin0 -> 52665 bytes
-rw-r--r--fonts/Symbolbin0 -> 33709 bytes
-rw-r--r--fonts/Times-Boldbin0 -> 44729 bytes
-rw-r--r--fonts/Times-BoldItalicbin0 -> 44656 bytes
-rw-r--r--fonts/Times-Italicbin0 -> 45458 bytes
-rw-r--r--fonts/Times-Romanbin0 -> 46026 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 -> 49289 bytes
-rw-r--r--fonts/ZapfDingbatsbin0 -> 45955 bytes
-rw-r--r--locale/C/cups_C132
-rw-r--r--locale/Makefile78
-rw-r--r--locale/de/cups_de133
-rw-r--r--locale/en/cups_en132
-rw-r--r--locale/es/cups_es132
-rw-r--r--locale/fr/cups_fr132
-rw-r--r--locale/it/cups_it132
-rw-r--r--locale/locale.txt32
-rw-r--r--locale/translate.c259
-rw-r--r--man/Makefile118
-rw-r--r--man/accept.866
-rw-r--r--man/accept.man57
-rw-r--r--man/backend.1132
-rw-r--r--man/backend.man102
-rw-r--r--man/classes.conf.566
-rw-r--r--man/classes.conf.man39
-rw-r--r--man/cupsd.866
-rw-r--r--man/cupsd.conf.566
-rw-r--r--man/cupsd.conf.man37
-rw-r--r--man/cupsd.man48
-rw-r--r--man/enable.866
-rw-r--r--man/enable.man64
-rw-r--r--man/filter.1132
-rw-r--r--man/filter.man108
-rw-r--r--man/lp.1132
-rw-r--r--man/lp.man111
-rw-r--r--man/lpadmin.8132
-rw-r--r--man/lpadmin.man124
-rw-r--r--man/lpc.866
-rw-r--r--man/lpc.man79
-rw-r--r--man/lpq.166
-rw-r--r--man/lpq.man52
-rw-r--r--man/lpr.1132
-rw-r--r--man/lpr.man96
-rw-r--r--man/lprm.166
-rw-r--r--man/lprm.man51
-rw-r--r--man/lpstat.1132
-rw-r--r--man/lpstat.man115
-rw-r--r--man/mime.convs.566
-rw-r--r--man/mime.convs.man43
-rw-r--r--man/mime.types.566
-rw-r--r--man/mime.types.man40
-rw-r--r--man/printers.conf.566
-rw-r--r--man/printers.conf.man39
-rw-r--r--ppd/Makefile55
-rw-r--r--ppd/deskjet.ppd186
-rw-r--r--ppd/laserjet.ppd172
-rw-r--r--ppd/stcolor.ppd131
-rw-r--r--ppd/stphoto.ppd131
-rw-r--r--pstoraster/Fontmap98
-rw-r--r--pstoraster/Makefile435
-rw-r--r--pstoraster/bfont.h76
-rw-r--r--pstoraster/bseq.h66
-rw-r--r--pstoraster/btoken.h41
-rw-r--r--pstoraster/ctype_.h37
-rw-r--r--pstoraster/dirent_.h60
-rw-r--r--pstoraster/dstack.h236
-rw-r--r--pstoraster/errno_.h42
-rw-r--r--pstoraster/errors.h148
-rw-r--r--pstoraster/estack.h139
-rw-r--r--pstoraster/files.h157
-rw-r--r--pstoraster/fname.h54
-rw-r--r--pstoraster/gconf.h43
-rw-r--r--pstoraster/gconfig.c127
-rw-r--r--pstoraster/gconfig.h257
-rw-r--r--pstoraster/gconfig_.h5
-rw-r--r--pstoraster/gconfigv.h4
-rw-r--r--pstoraster/gdebug.h132
-rw-r--r--pstoraster/gdev8bcm.h78
-rw-r--r--pstoraster/gdevabuf.c404
-rw-r--r--pstoraster/gdevbbox.c1069
-rw-r--r--pstoraster/gdevbbox.h100
-rw-r--r--pstoraster/gdevcmap.h74
-rw-r--r--pstoraster/gdevcups.c2426
-rw-r--r--pstoraster/gdevdbit.c706
-rw-r--r--pstoraster/gdevddrw.c635
-rw-r--r--pstoraster/gdevdflt.c270
-rw-r--r--pstoraster/gdevdgbr.c586
-rw-r--r--pstoraster/gdevhit.c98
-rw-r--r--pstoraster/gdevht.h51
-rw-r--r--pstoraster/gdevm1.c759
-rw-r--r--pstoraster/gdevm16.c168
-rw-r--r--pstoraster/gdevm2.c259
-rw-r--r--pstoraster/gdevm24.c526
-rw-r--r--pstoraster/gdevm32.c249
-rw-r--r--pstoraster/gdevm4.c319
-rw-r--r--pstoraster/gdevm8.c247
-rw-r--r--pstoraster/gdevmem.c498
-rw-r--r--pstoraster/gdevmem.h233
-rw-r--r--pstoraster/gdevmgr.h127
-rw-r--r--pstoraster/gdevmpla.c200
-rw-r--r--pstoraster/gdevmrop.h97
-rw-r--r--pstoraster/gdevnfwd.c797
-rw-r--r--pstoraster/gdevpccm.h44
-rw-r--r--pstoraster/gdevpcfb.h209
-rw-r--r--pstoraster/gdevpcl.h52
-rw-r--r--pstoraster/gdevpdfx.h555
-rw-r--r--pstoraster/gdevpipe.c72
-rw-r--r--pstoraster/gdevpm.h46
-rw-r--r--pstoraster/gdevprn.c923
-rw-r--r--pstoraster/gdevprn.h560
-rw-r--r--pstoraster/gdevprna.h190
-rw-r--r--pstoraster/gdevps.c1119
-rw-r--r--pstoraster/gdevpsde.c282
-rw-r--r--pstoraster/gdevpsdf.c514
-rw-r--r--pstoraster/gdevpsdf.h292
-rw-r--r--pstoraster/gdevpsdi.c349
-rw-r--r--pstoraster/gdevpsdp.c703
-rw-r--r--pstoraster/gdevpsds.c470
-rw-r--r--pstoraster/gdevpsds.h110
-rw-r--r--pstoraster/gdevpstr.c197
-rw-r--r--pstoraster/gdevpstr.h90
-rw-r--r--pstoraster/gdevpxat.h149
-rw-r--r--pstoraster/gdevpxen.h269
-rw-r--r--pstoraster/gdevpxop.h114
-rw-r--r--pstoraster/gdevvec.c905
-rw-r--r--pstoraster/gdevvec.h349
-rw-r--r--pstoraster/genarch.c163
-rw-r--r--pstoraster/ghost.h34
-rw-r--r--pstoraster/gp.h233
-rw-r--r--pstoraster/gp_getnv.c60
-rw-r--r--pstoraster/gp_nofb.c58
-rw-r--r--pstoraster/gp_nsync.c120
-rw-r--r--pstoraster/gp_unifn.c61
-rw-r--r--pstoraster/gp_unifs.c440
-rw-r--r--pstoraster/gp_unix.c173
-rw-r--r--pstoraster/gpcheck.h65
-rw-r--r--pstoraster/gpgetenv.h50
-rw-r--r--pstoraster/gpsync.h81
-rw-r--r--pstoraster/gs_btokn.ps313
-rw-r--r--pstoraster/gs_ccfnt.ps100
-rw-r--r--pstoraster/gs_cff.ps614
-rw-r--r--pstoraster/gs_cidfn.ps466
-rw-r--r--pstoraster/gs_cmap.ps256
-rw-r--r--pstoraster/gs_cmdl.ps188
-rw-r--r--pstoraster/gs_dbt_e.ps67
-rw-r--r--pstoraster/gs_diskf.ps232
-rw-r--r--pstoraster/gs_dpnxt.ps120
-rw-r--r--pstoraster/gs_dps.ps205
-rw-r--r--pstoraster/gs_dps1.ps147
-rw-r--r--pstoraster/gs_dps2.ps200
-rw-r--r--pstoraster/gs_epsf.ps67
-rw-r--r--pstoraster/gs_fform.ps100
-rw-r--r--pstoraster/gs_fonts.ps934
-rw-r--r--pstoraster/gs_init.ps1521
-rw-r--r--pstoraster/gs_iso_e.ps74
-rw-r--r--pstoraster/gs_kanji.ps166
-rw-r--r--pstoraster/gs_ksb_e.ps72
-rw-r--r--pstoraster/gs_l2img.ps192
-rw-r--r--pstoraster/gs_lev2.ps717
-rw-r--r--pstoraster/gs_ll3.ps387
-rw-r--r--pstoraster/gs_mex_e.ps72
-rw-r--r--pstoraster/gs_mro_e.ps65
-rw-r--r--pstoraster/gs_pdf_e.ps52
-rw-r--r--pstoraster/gs_pdfwr.ps411
-rw-r--r--pstoraster/gs_pfile.ps135
-rw-r--r--pstoraster/gs_res.ps726
-rw-r--r--pstoraster/gs_setpd.ps712
-rw-r--r--pstoraster/gs_statd.ps296
-rw-r--r--pstoraster/gs_std_e.ps81
-rw-r--r--pstoraster/gs_sym_e.ps91
-rw-r--r--pstoraster/gs_ttf.ps694
-rw-r--r--pstoraster/gs_typ32.ps134
-rw-r--r--pstoraster/gs_typ42.ps52
-rw-r--r--pstoraster/gs_type1.ps145
-rw-r--r--pstoraster/gs_wan_e.ps52
-rw-r--r--pstoraster/gs_wl1_e.ps74
-rw-r--r--pstoraster/gs_wl2_e.ps74
-rw-r--r--pstoraster/gs_wl5_e.ps74
-rw-r--r--pstoraster/gsalloc.c1621
-rw-r--r--pstoraster/gsalloc.h87
-rw-r--r--pstoraster/gsalpha.c48
-rw-r--r--pstoraster/gsalpha.h41
-rw-r--r--pstoraster/gsalphac.h71
-rw-r--r--pstoraster/gsargs.c225
-rw-r--r--pstoraster/gsargs.h89
-rw-r--r--pstoraster/gsbitmap.h193
-rw-r--r--pstoraster/gsbitops.c666
-rw-r--r--pstoraster/gsbitops.h222
-rw-r--r--pstoraster/gsbittab.c144
-rw-r--r--pstoraster/gsbittab.h83
-rw-r--r--pstoraster/gsccode.h72
-rw-r--r--pstoraster/gsccolor.h58
-rw-r--r--pstoraster/gscdefs.c81
-rw-r--r--pstoraster/gscdefs.h81
-rw-r--r--pstoraster/gscdevn.c184
-rw-r--r--pstoraster/gschar.c1492
-rw-r--r--pstoraster/gschar.h130
-rw-r--r--pstoraster/gschar0.c407
-rw-r--r--pstoraster/gscie.c1357
-rw-r--r--pstoraster/gscie.h688
-rw-r--r--pstoraster/gsclipsr.c45
-rw-r--r--pstoraster/gsclipsr.h34
-rw-r--r--pstoraster/gscolor.c357
-rw-r--r--pstoraster/gscolor.h43
-rw-r--r--pstoraster/gscolor1.c271
-rw-r--r--pstoraster/gscolor1.h47
-rw-r--r--pstoraster/gscolor2.c455
-rw-r--r--pstoraster/gscolor2.h106
-rw-r--r--pstoraster/gscolor3.c70
-rw-r--r--pstoraster/gscolor3.h41
-rw-r--r--pstoraster/gscompt.h59
-rw-r--r--pstoraster/gscoord.c507
-rw-r--r--pstoraster/gscoord.h55
-rw-r--r--pstoraster/gscparam.c480
-rw-r--r--pstoraster/gscpixel.c89
-rw-r--r--pstoraster/gscpixel.h34
-rw-r--r--pstoraster/gscpm.h39
-rw-r--r--pstoraster/gscrd.c350
-rw-r--r--pstoraster/gscrd.h75
-rw-r--r--pstoraster/gscrdp.c629
-rw-r--r--pstoraster/gscrdp.h104
-rw-r--r--pstoraster/gscrypt1.h56
-rw-r--r--pstoraster/gscscie.c374
-rw-r--r--pstoraster/gscsel.h44
-rw-r--r--pstoraster/gscsepnm.h54
-rw-r--r--pstoraster/gscsepr.c352
-rw-r--r--pstoraster/gscsepr.h74
-rw-r--r--pstoraster/gscspace.c237
-rw-r--r--pstoraster/gscspace.h400
-rw-r--r--pstoraster/gsdcolor.h332
-rw-r--r--pstoraster/gsdevice.c570
-rw-r--r--pstoraster/gsdevice.h103
-rw-r--r--pstoraster/gsdevmem.c236
-rw-r--r--pstoraster/gsdll.h145
-rw-r--r--pstoraster/gsdparam.c807
-rw-r--r--pstoraster/gsdpnext.h34
-rw-r--r--pstoraster/gsdps.h40
-rw-r--r--pstoraster/gsdps1.c242
-rw-r--r--pstoraster/gsdsrc.c121
-rw-r--r--pstoraster/gsdsrc.h133
-rw-r--r--pstoraster/gserror.h42
-rw-r--r--pstoraster/gserrors.h56
-rw-r--r--pstoraster/gsexit.h41
-rw-r--r--pstoraster/gsfcmap.c167
-rw-r--r--pstoraster/gsfcmap.h69
-rw-r--r--pstoraster/gsflip.h46
-rw-r--r--pstoraster/gsfont.c558
-rw-r--r--pstoraster/gsfont.h87
-rw-r--r--pstoraster/gsfont0.c136
-rw-r--r--pstoraster/gsfunc.c78
-rw-r--r--pstoraster/gsfunc.h136
-rw-r--r--pstoraster/gsfunc0.c347
-rw-r--r--pstoraster/gsfunc0.h66
-rw-r--r--pstoraster/gsfunc3.c361
-rw-r--r--pstoraster/gsfunc3.h114
-rw-r--r--pstoraster/gsgc.h91
-rw-r--r--pstoraster/gshsb.c171
-rw-r--r--pstoraster/gshsb.h33
-rw-r--r--pstoraster/gsht.c651
-rw-r--r--pstoraster/gsht.h78
-rw-r--r--pstoraster/gsht1.c449
-rw-r--r--pstoraster/gsht1.h60
-rw-r--r--pstoraster/gshtscr.c574
-rw-r--r--pstoraster/gshtx.h158
-rw-r--r--pstoraster/gsimage.c326
-rw-r--r--pstoraster/gsimage.h84
-rw-r--r--pstoraster/gsimpath.c191
-rw-r--r--pstoraster/gsinit.c79
-rw-r--r--pstoraster/gsio.h66
-rw-r--r--pstoraster/gsiodev.c318
-rw-r--r--pstoraster/gsiparam.h289
-rw-r--r--pstoraster/gsiparm2.h63
-rw-r--r--pstoraster/gsiparm3.h67
-rw-r--r--pstoraster/gsiparm4.h59
-rw-r--r--pstoraster/gsjconf.h81
-rw-r--r--pstoraster/gsjmorec.h59
-rw-r--r--pstoraster/gslib.h44
-rw-r--r--pstoraster/gsline.c340
-rw-r--r--pstoraster/gsline.h75
-rw-r--r--pstoraster/gslparam.h52
-rw-r--r--pstoraster/gsmalloc.c398
-rw-r--r--pstoraster/gsmalloc.h78
-rw-r--r--pstoraster/gsmatrix.c455
-rw-r--r--pstoraster/gsmatrix.h80
-rw-r--r--pstoraster/gsmdebug.h54
-rw-r--r--pstoraster/gsmemlok.h64
-rw-r--r--pstoraster/gsmemory.c196
-rw-r--r--pstoraster/gsmemory.h277
-rw-r--r--pstoraster/gsmemraw.h181
-rw-r--r--pstoraster/gsmisc.c942
-rw-r--r--pstoraster/gsnorop.c119
-rw-r--r--pstoraster/gspaint.c356
-rw-r--r--pstoraster/gspaint.h38
-rw-r--r--pstoraster/gsparam.c382
-rw-r--r--pstoraster/gsparam.h505
-rw-r--r--pstoraster/gsparams.c417
-rw-r--r--pstoraster/gsparams.h77
-rw-r--r--pstoraster/gspath.c518
-rw-r--r--pstoraster/gspath.h98
-rw-r--r--pstoraster/gspath1.c480
-rw-r--r--pstoraster/gspath2.h40
-rw-r--r--pstoraster/gspcolor.c958
-rw-r--r--pstoraster/gspcolor.h87
-rw-r--r--pstoraster/gspenum.h40
-rw-r--r--pstoraster/gspmdrv.h40
-rw-r--r--pstoraster/gsptype1.h143
-rw-r--r--pstoraster/gsptype2.h52
-rw-r--r--pstoraster/gsrect.h61
-rw-r--r--pstoraster/gsrefct.h148
-rw-r--r--pstoraster/gsrop.h46
-rw-r--r--pstoraster/gsropc.h60
-rw-r--r--pstoraster/gsropt.h195
-rw-r--r--pstoraster/gsshade.c451
-rw-r--r--pstoraster/gsshade.h255
-rw-r--r--pstoraster/gsstate.c1025
-rw-r--r--pstoraster/gsstate.h81
-rw-r--r--pstoraster/gsstruct.h970
-rw-r--r--pstoraster/gstext.c344
-rw-r--r--pstoraster/gstext.h231
-rw-r--r--pstoraster/gstrap.c190
-rw-r--r--pstoraster/gstrap.h81
-rw-r--r--pstoraster/gstype1.c562
-rw-r--r--pstoraster/gstype1.h265
-rw-r--r--pstoraster/gstype2.c772
-rw-r--r--pstoraster/gstype42.c480
-rw-r--r--pstoraster/gstypes.h86
-rw-r--r--pstoraster/gsuid.h78
-rw-r--r--pstoraster/gsutil.c285
-rw-r--r--pstoraster/gsutil.h67
-rw-r--r--pstoraster/gsxfont.h45
-rw-r--r--pstoraster/gx.h52
-rw-r--r--pstoraster/gxacpath.c479
-rw-r--r--pstoraster/gxalloc.h403
-rw-r--r--pstoraster/gxalpha.h74
-rw-r--r--pstoraster/gxarith.h84
-rw-r--r--pstoraster/gxband.h70
-rw-r--r--pstoraster/gxbcache.c153
-rw-r--r--pstoraster/gxbcache.h130
-rw-r--r--pstoraster/gxbitfmt.h195
-rw-r--r--pstoraster/gxbitmap.h133
-rw-r--r--pstoraster/gxbitops.h142
-rw-r--r--pstoraster/gxccache.c456
-rw-r--r--pstoraster/gxccman.c797
-rw-r--r--pstoraster/gxchar.h190
-rw-r--r--pstoraster/gxcht.c710
-rw-r--r--pstoraster/gxcindex.h91
-rw-r--r--pstoraster/gxclbits.c740
-rw-r--r--pstoraster/gxcldev.h708
-rw-r--r--pstoraster/gxclimag.c1320
-rw-r--r--pstoraster/gxclio.h104
-rw-r--r--pstoraster/gxclip.c582
-rw-r--r--pstoraster/gxclip.h75
-rw-r--r--pstoraster/gxclip2.c306
-rw-r--r--pstoraster/gxclip2.h55
-rw-r--r--pstoraster/gxclipm.c309
-rw-r--r--pstoraster/gxclipm.h35
-rw-r--r--pstoraster/gxclist.c728
-rw-r--r--pstoraster/gxclist.h311
-rw-r--r--pstoraster/gxclmem.c1136
-rw-r--r--pstoraster/gxclmem.h155
-rw-r--r--pstoraster/gxclpage.c127
-rw-r--r--pstoraster/gxclpage.h63
-rw-r--r--pstoraster/gxclpath.c1198
-rw-r--r--pstoraster/gxclpath.h207
-rw-r--r--pstoraster/gxclrast.c2331
-rw-r--r--pstoraster/gxclread.c517
-rw-r--r--pstoraster/gxclrect.c656
-rw-r--r--pstoraster/gxclutil.c616
-rw-r--r--pstoraster/gxclzlib.c64
-rw-r--r--pstoraster/gxcmap.c890
-rw-r--r--pstoraster/gxcmap.h101
-rw-r--r--pstoraster/gxcolor2.h98
-rw-r--r--pstoraster/gxcomp.h113
-rw-r--r--pstoraster/gxcoord.h44
-rw-r--r--pstoraster/gxcpath.c962
-rw-r--r--pstoraster/gxcpath.h134
-rw-r--r--pstoraster/gxcspace.h237
-rw-r--r--pstoraster/gxctable.c146
-rw-r--r--pstoraster/gxctable.h70
-rw-r--r--pstoraster/gxcvalue.h48
-rw-r--r--pstoraster/gxdcconv.c162
-rw-r--r--pstoraster/gxdcconv.h43
-rw-r--r--pstoraster/gxdcolor.c339
-rw-r--r--pstoraster/gxdcolor.h199
-rw-r--r--pstoraster/gxdda.h158
-rw-r--r--pstoraster/gxdevcli.h877
-rw-r--r--pstoraster/gxdevice.h453
-rw-r--r--pstoraster/gxdevmem.h170
-rw-r--r--pstoraster/gxdevrop.h35
-rw-r--r--pstoraster/gxdht.h272
-rw-r--r--pstoraster/gxdither.c502
-rw-r--r--pstoraster/gxdither.h76
-rw-r--r--pstoraster/gxfarith.h144
-rw-r--r--pstoraster/gxfcache.h270
-rw-r--r--pstoraster/gxfcmap.h101
-rw-r--r--pstoraster/gxfill.c1543
-rw-r--r--pstoraster/gxfixed.h248
-rw-r--r--pstoraster/gxfmap.h105
-rw-r--r--pstoraster/gxfont.h224
-rw-r--r--pstoraster/gxfont0.h81
-rw-r--r--pstoraster/gxfont1.h158
-rw-r--r--pstoraster/gxfont42.h72
-rw-r--r--pstoraster/gxfrac.h98
-rw-r--r--pstoraster/gxftype.h57
-rw-r--r--pstoraster/gxfunc.h56
-rw-r--r--pstoraster/gxgetbit.h101
-rw-r--r--pstoraster/gxhint1.c275
-rw-r--r--pstoraster/gxhint2.c397
-rw-r--r--pstoraster/gxhint3.c560
-rw-r--r--pstoraster/gxht.c532
-rw-r--r--pstoraster/gxht.h222
-rw-r--r--pstoraster/gxhttile.h54
-rw-r--r--pstoraster/gxhttype.h45
-rw-r--r--pstoraster/gxi12bit.c300
-rw-r--r--pstoraster/gxicolor.c307
-rw-r--r--pstoraster/gxidata.c255
-rw-r--r--pstoraster/gxifast.c716
-rw-r--r--pstoraster/gxiinit.c921
-rw-r--r--pstoraster/gximage.h283
-rw-r--r--pstoraster/gximage3.c429
-rw-r--r--pstoraster/gximage4.c297
-rw-r--r--pstoraster/gximono.c586
-rw-r--r--pstoraster/gxiodev.h190
-rw-r--r--pstoraster/gxiparam.h172
-rw-r--r--pstoraster/gxiscale.c256
-rw-r--r--pstoraster/gxistate.h251
-rw-r--r--pstoraster/gxline.h81
-rw-r--r--pstoraster/gxlum.h50
-rw-r--r--pstoraster/gxmatrix.h83
-rw-r--r--pstoraster/gxmclip.c116
-rw-r--r--pstoraster/gxmclip.h111
-rw-r--r--pstoraster/gxobj.h230
-rw-r--r--pstoraster/gxop1.h81
-rw-r--r--pstoraster/gxp1fill.c374
-rw-r--r--pstoraster/gxp1fill.h40
-rw-r--r--pstoraster/gxpageq.h192
-rw-r--r--pstoraster/gxpaint.c81
-rw-r--r--pstoraster/gxpaint.h125
-rw-r--r--pstoraster/gxpath.c829
-rw-r--r--pstoraster/gxpath.h317
-rw-r--r--pstoraster/gxpath2.c487
-rw-r--r--pstoraster/gxpcache.h61
-rw-r--r--pstoraster/gxpcmap.c666
-rw-r--r--pstoraster/gxpcolor.h138
-rw-r--r--pstoraster/gxpcopy.c824
-rw-r--r--pstoraster/gxpdash.c189
-rw-r--r--pstoraster/gxpflat.c455
-rw-r--r--pstoraster/gxropc.h53
-rw-r--r--pstoraster/gxsample.c211
-rw-r--r--pstoraster/gxsample.h87
-rw-r--r--pstoraster/gxshade.c348
-rw-r--r--pstoraster/gxshade.h247
-rw-r--r--pstoraster/gxshade1.c527
-rw-r--r--pstoraster/gxshade4.c285
-rw-r--r--pstoraster/gxshade4.h58
-rw-r--r--pstoraster/gxshade6.c566
-rw-r--r--pstoraster/gxstate.h86
-rw-r--r--pstoraster/gxstroke.c1319
-rw-r--r--pstoraster/gxsync.h81
-rw-r--r--pstoraster/gxtext.h121
-rw-r--r--pstoraster/gxtmap.h58
-rw-r--r--pstoraster/gxtype1.c518
-rw-r--r--pstoraster/gxtype1.h346
-rw-r--r--pstoraster/gxxfont.h180
-rw-r--r--pstoraster/gzacpath.h60
-rw-r--r--pstoraster/gzcpath.h106
-rw-r--r--pstoraster/gzht.h203
-rw-r--r--pstoraster/gzline.h41
-rw-r--r--pstoraster/gzpath.h385
-rw-r--r--pstoraster/gzstate.h137
-rw-r--r--pstoraster/ialloc.c314
-rw-r--r--pstoraster/ialloc.h125
-rw-r--r--pstoraster/iastate.h35
-rw-r--r--pstoraster/iastruct.h34
-rw-r--r--pstoraster/ibnum.c222
-rw-r--r--pstoraster/ibnum.h71
-rw-r--r--pstoraster/iccinit0.c31
-rw-r--r--pstoraster/ichar.h72
-rw-r--r--pstoraster/icharout.h59
-rw-r--r--pstoraster/icie.h96
-rw-r--r--pstoraster/icolor.h54
-rw-r--r--pstoraster/iconfig.c74
-rw-r--r--pstoraster/icontext.c275
-rw-r--r--pstoraster/icontext.h59
-rw-r--r--pstoraster/icsmap.h45
-rw-r--r--pstoraster/icstate.h74
-rw-r--r--pstoraster/idebug.c299
-rw-r--r--pstoraster/idebug.h48
-rw-r--r--pstoraster/idict.c780
-rw-r--r--pstoraster/idict.h268
-rw-r--r--pstoraster/idictdef.h128
-rw-r--r--pstoraster/idparam.c371
-rw-r--r--pstoraster/idparam.h90
-rw-r--r--pstoraster/idstack.c249
-rw-r--r--pstoraster/idstack.h125
-rw-r--r--pstoraster/iestack.h56
-rw-r--r--pstoraster/ifilter.h89
-rw-r--r--pstoraster/ifont.h97
-rw-r--r--pstoraster/ifunc.h58
-rw-r--r--pstoraster/igc.c1316
-rw-r--r--pstoraster/igc.h102
-rw-r--r--pstoraster/igcref.c680
-rw-r--r--pstoraster/igcstr.c393
-rw-r--r--pstoraster/igcstr.h41
-rw-r--r--pstoraster/igstate.h181
-rw-r--r--pstoraster/iht.h37
-rw-r--r--pstoraster/iimage.h46
-rw-r--r--pstoraster/iimage2.h50
-rw-r--r--pstoraster/iinit.c511
-rw-r--r--pstoraster/ilevel.h39
-rw-r--r--pstoraster/ilocate.c439
-rw-r--r--pstoraster/imain.c664
-rw-r--r--pstoraster/imain.h276
-rw-r--r--pstoraster/imainarg.c849
-rw-r--r--pstoraster/imainarg.h54
-rw-r--r--pstoraster/imemory.h99
-rw-r--r--pstoraster/iminst.h92
-rw-r--r--pstoraster/iname.c637
-rw-r--r--pstoraster/iname.h105
-rw-r--r--pstoraster/inamedef.h230
-rw-r--r--pstoraster/inames.h114
-rw-r--r--pstoraster/interp.c1554
-rw-r--r--pstoraster/interp.h95
-rw-r--r--pstoraster/iostack.h45
-rw-r--r--pstoraster/ipacked.h136
-rw-r--r--pstoraster/iparam.c1080
-rw-r--r--pstoraster/iparam.h118
-rw-r--r--pstoraster/iparray.h42
-rw-r--r--pstoraster/ireclaim.c163
-rw-r--r--pstoraster/iref.h437
-rw-r--r--pstoraster/isave.c1068
-rw-r--r--pstoraster/isave.h133
-rw-r--r--pstoraster/iscan.c1131
-rw-r--r--pstoraster/iscan.h161
-rw-r--r--pstoraster/iscanbin.c768
-rw-r--r--pstoraster/iscannum.c398
-rw-r--r--pstoraster/iscannum.h36
-rw-r--r--pstoraster/isstate.h46
-rw-r--r--pstoraster/istack.c588
-rw-r--r--pstoraster/istack.h256
-rw-r--r--pstoraster/istream.h43
-rw-r--r--pstoraster/istruct.h98
-rw-r--r--pstoraster/iutil.c676
-rw-r--r--pstoraster/iutil.h121
-rw-r--r--pstoraster/iutil2.c154
-rw-r--r--pstoraster/iutil2.h56
-rw-r--r--pstoraster/ivmspace.h111
-rw-r--r--pstoraster/main.h101
-rw-r--r--pstoraster/malloc_.h63
-rw-r--r--pstoraster/math_.h97
-rw-r--r--pstoraster/memory_.h107
-rw-r--r--pstoraster/opcheck.h86
-rw-r--r--pstoraster/opdef.h164
-rw-r--r--pstoraster/oper.h108
-rw-r--r--pstoraster/opextern.h115
-rw-r--r--pstoraster/ostack.h94
-rw-r--r--pstoraster/pdf2dsc.ps127
-rw-r--r--pstoraster/pdf_base.ps589
-rw-r--r--pstoraster/pdf_draw.ps626
-rw-r--r--pstoraster/pdf_font.ps601
-rw-r--r--pstoraster/pdf_main.ps523
-rw-r--r--pstoraster/pdf_ops.ps406
-rw-r--r--pstoraster/pdf_sec.ps65
-rw-r--r--pstoraster/pipe_.h42
-rw-r--r--pstoraster/pstoraster.c230
-rw-r--r--pstoraster/sa85x.h51
-rw-r--r--pstoraster/sbcp.c259
-rw-r--r--pstoraster/sbhc.c291
-rw-r--r--pstoraster/sbhc.h98
-rw-r--r--pstoraster/sbtx.h45
-rw-r--r--pstoraster/sbwbs.c535
-rw-r--r--pstoraster/sbwbs.h78
-rw-r--r--pstoraster/scanchar.h75
-rw-r--r--pstoraster/scantab.c112
-rw-r--r--pstoraster/scf.h213
-rw-r--r--pstoraster/scfd.c809
-rw-r--r--pstoraster/scfdtab.c942
-rw-r--r--pstoraster/scfe.c536
-rw-r--r--pstoraster/scfetab.c170
-rw-r--r--pstoraster/scfparam.c99
-rw-r--r--pstoraster/scfx.h132
-rw-r--r--pstoraster/scommon.h173
-rw-r--r--pstoraster/sdcparam.c630
-rw-r--r--pstoraster/sdcparam.h57
-rw-r--r--pstoraster/sdct.h121
-rw-r--r--pstoraster/sdctc.c55
-rw-r--r--pstoraster/sdctd.c303
-rw-r--r--pstoraster/sdcte.c203
-rw-r--r--pstoraster/sddparam.c83
-rw-r--r--pstoraster/sdeparam.c328
-rw-r--r--pstoraster/seexec.c192
-rw-r--r--pstoraster/sfilter.h139
-rw-r--r--pstoraster/sfilter1.c301
-rw-r--r--pstoraster/sfilter2.c339
-rw-r--r--pstoraster/sfxstdio.c269
-rw-r--r--pstoraster/shc.c74
-rw-r--r--pstoraster/shc.h254
-rw-r--r--pstoraster/shcgen.c491
-rw-r--r--pstoraster/shcgen.h60
-rw-r--r--pstoraster/siscale.c498
-rw-r--r--pstoraster/siscale.h148
-rw-r--r--pstoraster/sjpeg.h80
-rw-r--r--pstoraster/sjpegc.c305
-rw-r--r--pstoraster/sjpegd.c100
-rw-r--r--pstoraster/sjpege.c129
-rw-r--r--pstoraster/sjpegerr.c102
-rw-r--r--pstoraster/slzwc.c50
-rw-r--r--pstoraster/slzwce.c167
-rw-r--r--pstoraster/slzwd.c372
-rw-r--r--pstoraster/slzwx.h80
-rw-r--r--pstoraster/smtf.c184
-rw-r--r--pstoraster/smtf.h49
-rw-r--r--pstoraster/spcxd.c75
-rw-r--r--pstoraster/spcxx.h35
-rw-r--r--pstoraster/spdiff.c334
-rw-r--r--pstoraster/spdiffx.h55
-rw-r--r--pstoraster/spngp.c364
-rw-r--r--pstoraster/spngpx.h61
-rw-r--r--pstoraster/srld.c133
-rw-r--r--pstoraster/srle.c203
-rw-r--r--pstoraster/srlx.h77
-rw-r--r--pstoraster/sstring.c464
-rw-r--r--pstoraster/sstring.h79
-rw-r--r--pstoraster/stat_.h62
-rw-r--r--pstoraster/std.h261
-rw-r--r--pstoraster/stdio_.h74
-rw-r--r--pstoraster/stdpre.h413
-rw-r--r--pstoraster/store.h250
-rw-r--r--pstoraster/stream.c911
-rw-r--r--pstoraster/stream.h334
-rw-r--r--pstoraster/strimpl.h154
-rw-r--r--pstoraster/string_.h59
-rw-r--r--pstoraster/szlibc.c140
-rw-r--r--pstoraster/szlibd.c115
-rw-r--r--pstoraster/szlibe.c112
-rw-r--r--pstoraster/szlibx.h64
-rw-r--r--pstoraster/szlibxx.h73
-rw-r--r--pstoraster/time_.h70
-rw-r--r--pstoraster/vmsmath.h49
-rw-r--r--pstoraster/zarith.c373
-rw-r--r--pstoraster/zarray.c131
-rw-r--r--pstoraster/zbseq.c136
-rw-r--r--pstoraster/zcfont.c164
-rw-r--r--pstoraster/zchar.c710
-rw-r--r--pstoraster/zchar1.c763
-rw-r--r--pstoraster/zchar2.c228
-rw-r--r--pstoraster/zchar32.c217
-rw-r--r--pstoraster/zchar42.c184
-rw-r--r--pstoraster/zcharout.c241
-rw-r--r--pstoraster/zcid.c102
-rw-r--r--pstoraster/zcie.c731
-rw-r--r--pstoraster/zcolor.c241
-rw-r--r--pstoraster/zcolor1.c247
-rw-r--r--pstoraster/zcolor2.c191
-rw-r--r--pstoraster/zcontrol.c897
-rw-r--r--pstoraster/zcrd.c438
-rw-r--r--pstoraster/zcsdevn.c119
-rw-r--r--pstoraster/zcsindex.c239
-rw-r--r--pstoraster/zcspixel.c73
-rw-r--r--pstoraster/zcssepr.c186
-rw-r--r--pstoraster/zdevcal.c79
-rw-r--r--pstoraster/zdevice.c445
-rw-r--r--pstoraster/zdevice2.c373
-rw-r--r--pstoraster/zdict.c515
-rw-r--r--pstoraster/zdps1.c459
-rw-r--r--pstoraster/zfbcp.c99
-rw-r--r--pstoraster/zfcmap.c355
-rw-r--r--pstoraster/zfdctd.c110
-rw-r--r--pstoraster/zfdcte.c157
-rw-r--r--pstoraster/zfdecode.c363
-rw-r--r--pstoraster/zfile.c915
-rw-r--r--pstoraster/zfileio.c842
-rw-r--r--pstoraster/zfilter.c418
-rw-r--r--pstoraster/zfilter2.c165
-rw-r--r--pstoraster/zfilterx.c336
-rw-r--r--pstoraster/zfname.c116
-rw-r--r--pstoraster/zfont.c471
-rw-r--r--pstoraster/zfont0.c346
-rw-r--r--pstoraster/zfont1.c291
-rw-r--r--pstoraster/zfont2.c553
-rw-r--r--pstoraster/zfont32.c79
-rw-r--r--pstoraster/zfont42.c189
-rw-r--r--pstoraster/zfproc.c363
-rw-r--r--pstoraster/zfreuse.c206
-rw-r--r--pstoraster/zfunc.c235
-rw-r--r--pstoraster/zfunc0.c113
-rw-r--r--pstoraster/zfunc3.c136
-rw-r--r--pstoraster/zfzlib.c105
-rw-r--r--pstoraster/zgeneric.c528
-rw-r--r--pstoraster/zgstate.c450
-rw-r--r--pstoraster/zhsb.c68
-rw-r--r--pstoraster/zht.c265
-rw-r--r--pstoraster/zht1.c156
-rw-r--r--pstoraster/zht2.c357
-rw-r--r--pstoraster/zimage.c490
-rw-r--r--pstoraster/zimage2.c158
-rw-r--r--pstoraster/zimage3.c139
-rw-r--r--pstoraster/ziodev.c475
-rw-r--r--pstoraster/ziodev2.c148
-rw-r--r--pstoraster/zmath.c277
-rw-r--r--pstoraster/zmatrix.c358
-rw-r--r--pstoraster/zmedia2.c468
-rw-r--r--pstoraster/zmisc.c345
-rw-r--r--pstoraster/zmisc1.c157
-rw-r--r--pstoraster/zmisc2.c322
-rw-r--r--pstoraster/zmisc3.c129
-rw-r--r--pstoraster/zpacked.c258
-rw-r--r--pstoraster/zpaint.c92
-rw-r--r--pstoraster/zpath.c205
-rw-r--r--pstoraster/zpath1.c279
-rw-r--r--pstoraster/zpcolor.c264
-rw-r--r--pstoraster/zrelbit.c342
-rw-r--r--pstoraster/zshade.c599
-rw-r--r--pstoraster/zstack.c295
-rw-r--r--pstoraster/zstring.c172
-rw-r--r--pstoraster/zsysvm.c164
-rw-r--r--pstoraster/ztoken.c241
-rw-r--r--pstoraster/ztrap.c72
-rw-r--r--pstoraster/ztype.c510
-rw-r--r--pstoraster/zupath.c673
-rw-r--r--pstoraster/zusparam.c655
-rw-r--r--pstoraster/zvmem.c404
-rw-r--r--pstoraster/zvmem2.c153
-rw-r--r--scheduler/Makefile143
-rw-r--r--scheduler/auth.c745
-rw-r--r--scheduler/auth.h115
-rw-r--r--scheduler/banners.c219
-rw-r--r--scheduler/banners.h58
-rw-r--r--scheduler/cert.c250
-rw-r--r--scheduler/cert.h60
-rw-r--r--scheduler/classes.c553
-rw-r--r--scheduler/classes.h43
-rw-r--r--scheduler/client.c1697
-rw-r--r--scheduler/client.h98
-rw-r--r--scheduler/conf.c1391
-rw-r--r--scheduler/conf.h118
-rw-r--r--scheduler/cups-lpd.c526
-rw-r--r--scheduler/cups-polld.c308
-rw-r--r--scheduler/cups.pam2
-rw-r--r--scheduler/cupsd.dsp173
-rw-r--r--scheduler/cupsd.h176
-rw-r--r--scheduler/devices.c372
-rw-r--r--scheduler/dirsvc.c727
-rw-r--r--scheduler/dirsvc.h94
-rw-r--r--scheduler/filter.c301
-rw-r--r--scheduler/ipp.c4676
-rw-r--r--scheduler/job.c2317
-rw-r--r--scheduler/job.h88
-rw-r--r--scheduler/listen.c142
-rw-r--r--scheduler/log.c436
-rw-r--r--scheduler/main.c604
-rw-r--r--scheduler/mime.c553
-rw-r--r--scheduler/mime.h139
-rw-r--r--scheduler/ppds.c641
-rw-r--r--scheduler/printers.c1380
-rw-r--r--scheduler/printers.h84
-rw-r--r--scheduler/testmime.c199
-rw-r--r--scheduler/testspeed.c126
-rw-r--r--scheduler/type.c1065
-rw-r--r--standards/draft-ietf-ipp-model-v11-04.txt11876
-rw-r--r--standards/draft-ietf-ipp-protocol-v11-03.txt2540
-rw-r--r--standards/rfc1179.txt787
-rw-r--r--standards/rfc1321.txt1179
-rw-r--r--standards/rfc2565.txt2075
-rw-r--r--standards/rfc2566.txt9691
-rw-r--r--standards/rfc2567.txt2411
-rw-r--r--standards/rfc2568.txt563
-rw-r--r--standards/rfc2569.txt1571
-rw-r--r--standards/rfc2616.txt9859
-rw-r--r--standards/rfc2617.txt1907
-rw-r--r--standards/rfc2639.txt3587
-rw-r--r--systemv/Makefile151
-rw-r--r--systemv/accept.c293
-rw-r--r--systemv/cancel.c236
-rw-r--r--systemv/lp.c381
-rw-r--r--systemv/lpadmin.c1331
-rw-r--r--systemv/lpoptions.c294
-rw-r--r--systemv/lppasswd.c449
-rw-r--r--systemv/lpstat.c1494
-rw-r--r--templates/Makefile100
-rw-r--r--templates/add-class.tmpl33
-rw-r--r--templates/add-printer.tmpl33
-rw-r--r--templates/admin-op.tmpl1
-rw-r--r--templates/admin.tmpl57
-rw-r--r--templates/choose-device.tmpl32
-rw-r--r--templates/choose-make.tmpl35
-rw-r--r--templates/choose-members.tmpl30
-rw-r--r--templates/choose-model.tmpl35
-rw-r--r--templates/choose-serial.tmpl55
-rw-r--r--templates/choose-uri.tmpl41
-rw-r--r--templates/class-added.tmpl2
-rw-r--r--templates/class-confirm.tmpl6
-rw-r--r--templates/class-deleted.tmpl1
-rw-r--r--templates/class-modified.tmpl2
-rw-r--r--templates/classes.tmpl50
-rw-r--r--templates/config-printer.tmpl6
-rw-r--r--templates/config-printer2.tmpl2
-rw-r--r--templates/error.tmpl3
-rw-r--r--templates/header.tmpl20
-rw-r--r--templates/job-cancel.tmpl1
-rw-r--r--templates/job-hold.tmpl1
-rw-r--r--templates/job-release.tmpl1
-rw-r--r--templates/jobs.tmpl55
-rw-r--r--templates/modify-class.tmpl34
-rw-r--r--templates/modify-printer.tmpl36
-rw-r--r--templates/option-boolean.tmpl7
-rw-r--r--templates/option-header.tmpl8
-rw-r--r--templates/option-pickmany.tmpl7
-rw-r--r--templates/option-pickone.tmpl7
-rw-r--r--templates/option-trailer.tmpl8
-rw-r--r--templates/printer-accept.tmpl1
-rw-r--r--templates/printer-added.tmpl2
-rw-r--r--templates/printer-configured.tmpl2
-rw-r--r--templates/printer-confirm.tmpl6
-rw-r--r--templates/printer-deleted.tmpl1
-rw-r--r--templates/printer-modified.tmpl2
-rw-r--r--templates/printer-reject.tmpl1
-rw-r--r--templates/printer-start.tmpl2
-rw-r--r--templates/printer-stop.tmpl2
-rw-r--r--templates/printers.tmpl56
-rw-r--r--templates/test-page.tmpl2
-rw-r--r--templates/trailer.tmpl7
-rw-r--r--visualc/config.h114
1090 files changed, 349244 insertions, 0 deletions
diff --git a/CHANGES.txt b/CHANGES.txt
new file mode 100644
index 000000000..260611995
--- /dev/null
+++ b/CHANGES.txt
@@ -0,0 +1,299 @@
+CHANGES.txt - 03/13/2000
+------------------------
+
+CHANGES IN CUPS v1.1b2
+
+ - Documentation updates.
+ - The lp command didn't always load the user-defined
+ destinations, preventing it from seeing the default
+ printer.
+ - Many configure script and makefile fixes.
+ - The Microsoft code page files were missing from the
+ distribution.
+ - Added a workaround for the HP IPP client (which is sending
+ an invalid printer-uri in requests)
+ - Fixed the encoding of text-with-language and name-with-language
+ to match the IPP spec.
+ - Added support for unknown value tags in the IPP routines
+ (previously they would be ignored)
+ - Integrated GNU GhostScript 5.50 into the pstoraster filter.
+ - Client hostname resolution was broken on little-endian
+ machines.
+ - Now look at client.conf file for client's default server
+ and printer.
+ - The cupsServer() function did not close the client.conf file
+ if it contained a ServerName directive.
+ - Added BrowseAllow, BrowseDeny, BrowseOrder, BrowsePoll, and
+ BrowseRelay directives.
+ - BrowseInterval 0 disables advertising of local printers, but
+ still receives information on remote printers.
+ - New browse polling daemon (for polling servers on different
+ networks)
+ - New PPD cache file for faster startup times with large numbers
+ of PPD files.
+ - The Host: field was incorrectly required for HTTP/1.0 clients.
+ - New set-job-attributes operation now supported.
+ - The mime_load_types() and mime_load_convs() functions did not
+ close their input files.
+
+
+CHANGES IN CUPS v1.1b1
+
+ - NEW web-based administration interface.
+ - NEW EPSON printer drivers.
+ - NEW user-defined printers and options.
+ - NEW persistent jobs and job history
+ - NEW IPP/1.1 support
+ - NEW template-based web interfaces.
+ - NEW CUPS-get-devices and CUPS-get-ppds operations.
+ - NEW support for create-job and send-file operations.
+ - NEW certificate-based authentication for local
+ administration.
+ - NEW USB backend.
+ - The lpr command now produces human-readable error messages.
+ - The lpq command now produces BSD standard format output
+ instead of OSF/1 output. This should resolve the SAMBA
+ print queue problems that have been reported.
+ - The IPP backend did not always detect when the "raw" option
+ was being used.
+ - The "lpstat -p" command would stop after the first active
+ printer.
+ - The "lpstat -v" command would stop before the first remote
+ printer.
+
+
+CHANGES IN CUPS v1.0.5
+
+ - The HP-GL/2 filter did not correctly set the pen color
+ for pens other than #1.
+ - The scheduler would only accept 26 simultaneous jobs
+ under some OS releases (mkstemp() limitation.) It now
+ handles up to 2^32 simultaneous jobs.
+ - The PostScript filter loaded the printer's PPD file
+ twice.
+ - The PAM authentication code now uses pam_strerror() to
+ provide a textual error message in the error_log file.
+ - The scheduler now copies PPD and interface script
+ files instead of moving them; this fixes installations
+ with a separate requests directory.
+ - The PostScript RIP did not generate correct 6-color
+ output.
+ - Several filters were marking PPD options twice when
+ they didn't need to.
+ - The scheduler did not save the printer or class state
+ after an accept-jobs or reject-jobs operation.
+ - The cupsGetDefault() function now ignores the PRINTER
+ environment variable if it is set to "lp".
+ - New ippErrorString() function to get textual error
+ messages.
+ - Better error reporting in the System V commands.
+ - The lpadmin and lpstat commands always tried to
+ connect to the default server.
+ - The text filter didn't load the charset files from the
+ correct location.
+ - Wasn't sending a WWW-Authenticate: field to HTTP
+ clients when authentication was required.
+ - httpSeparate() didn't always set the default port
+ number for known methods.
+ - The HP-GL/2 filter now looks for "PSwidth,length"
+ instead of (the correct) "PSlength,width" as
+ documented by HP. It appears that many major CAD
+ applications are broken and this change allows the
+ auto-rotation to work with them.
+ - The IPP "printer-resolution" option was not being
+ translated.
+ - The charset files did not include the Microsoft
+ "standard" characters from 128 to 159 (unused by the
+ ISO-8859-x charsets)
+ - The scheduler was chunking the Content-Type field from
+ CGI programs; this problem was most noticeable with
+ Microsoft Internet Explorer 5.
+ - By popular demand, the printers, jobs, and classes
+ CGIs no longer force a reload of the page every 10/30
+ seconds.
+ - The scheduler incorrectly required that the IPP client
+ provide a document-format attribute for the
+ validate-job operation.
+ - Clients that sent bad IPP requests without the
+ required attributes-natural-language and
+ attributes-charset attributes would crash the
+ scheduler.
+
+
+CHANGES IN CUPS v1.0.4
+
+ - Documentation updates.
+ - Jobs would get stuck in the queue and wouldn't print
+ until you enabled the queue.
+ - The lp and lpr commands now catch SIGHUP and SIGINTR.
+ - The lp and lpr commands now use sigaction or sigset
+ when available.
+ - CUPS library updates for WIN32/OS-2
+
+
+CHANGES IN CUPS v1.0.3
+
+ - Documentation updates.
+ - The lpq man page was missing.
+ - The configure script was not properly detecting the
+ image libraries.
+ - The top-level makefile was calling "make" instead of
+ "$(MAKE)".
+ - PostScript filter fixes for number-up, OutputOrder,
+ and %Trailer.
+ - The imagetops filter didn't end the base-85 encoding
+ properly if the image data was not a multiple of 4
+ bytes in length.
+ - The imagetoraster filter didn't generate good banded
+ RGB or CMY data (was dividing the line width by 4
+ instead of 3...)
+ - The imagetoraster filter now records the bounding
+ box of the image on the page.
+ - The CUPS image library cache code wasn't working as
+ designed; images larger than the maximum RIP cache
+ would eventually thrash using the same cache tile.
+ - The CUPS image library TIFF loading code didn't
+ handle unknown resolution units properly; the fixed
+ code uses a default resolution of 128 PPI.
+ - cupsGetClasses() and cupsGetPrinters() did not free
+ existing strings if they ran out of memory.
+ - The scheduler logs incorrectly contained 3 digits for
+ the timezone offset instead of 4.
+ - The scheduler now does a lookup for the default user
+ and group ID; the previous hardcoded values caused
+ problems with the LPD backend.
+ - The cancel-job operation now allows any user in the
+ system group to cancel any job.
+ - The cancel-job operation stopped the print queue if
+ the job was being printed.
+ - Now only stop printers if the backend fails. If the
+ filter fails then the failure is noted in the
+ error_log and printing continues with the next file in
+ the queue.
+ - Now log whether a filter fails because of a signal
+ or because it returned a non-zero exit status.
+ - The root user now always passes the system group test.
+ - Printers with an interface script and remote printers
+ and classes didn't have a printer-make-and-model
+ attribute.
+ - Added logging of lost/timed-out remote printers.
+ - The HP-GL/2 filter was scaling the pen width twice.
+ - Updated the HP-GL/2 filter to use a single SP (Set
+ Pen) procedure. This makes the output smaller and is
+ more appropriate since the filter keeps track of the
+ pen states already.
+ - The scheduler didn't handle passwords with spaces.
+ - The IPP backend now does multiple copies and retries
+ if the destination server requires it (e.g. HP
+ JetDirect.)
+ - The disable command didn't implement the "-c" option
+ (cancel all jobs.)
+ - Changed the CMYK generation function for the image file
+ and PostScript RIPs.
+ - The lp command didn't support the "-h" option as
+ documented.
+ - The AppSocket, IPP, and LPD backends now retry on all
+ network errors. This should prevent stopped queues
+ caused by a printer being disconnected from the
+ network or powered off.
+ - The scheduler now restarts a job if the corresponding
+ printer is modified.
+ - The image RIPs now rotate the image if needed to fit
+ on the page.
+
+
+CHANGES IN CUPS v1.0.2
+
+ - The HP-GL/2 filter didn't always scale the output
+ correctly.
+ - The HP-GL/2 filter now supports changing the page size
+ automatically when the "fitplot" option is not used.
+ - The cancel-job operation was expecting a resource name
+ of the form "/job/#" instead of "/jobs/#"; this
+ prevented the cancel and lprm commands from working.
+ - The backends didn't log pages when files were printed
+ using the "-oraw" option.
+ - The authorization code did not work with the Slackware
+ long shadow password package because its crypt() can
+ return NULL.
+ - The chunking code didn't work for reading the response
+ of a POST request.
+ - cupsGetPPD() now does authentication as needed.
+ - The N-up code in the PostScript filter didn't work
+ with some printers (grestoreall would restore the
+ default blank page and device settings).
+ - The N-up code in the PostScript filter didn't scale
+ the pages to fit within the imageable area of the
+ page.
+ - Wasn't doing an fchown() on the request files. This
+ caused problems when the default root account group
+ and CUPS group were not the same.
+
+
+CHANGES IN CUPS v1.0.1
+
+ - Documentation updates.
+
+ - Fixed a bunch of possible buffer-overflow conditions.
+
+ - The scheduler now supports authentication using PAM.
+
+ - Updated the Italian message file.
+
+ - httpEncode64() didn't add an extra "=" if there was
+ only one byte in the last three-byte group.
+
+ - Now drop any trailing character set from the locale
+ string (e.g. "en_US.ISO_8859-1" becomes "en_US")
+
+ - Fixed "timezone" vs "tm_gmtoff" usage for BSD-based
+ operating systems.
+
+ - Updated IPP security so that "get" operations can be
+ done from any resource name; this allows the CGIs to
+ work with printer authentication enabled so long as
+ authentication isn't turned on for the whole "site".
+
+ - The IPP code didn't properly handle the "unsupported"
+ group; this caused problems with the HP JetDirect since
+ it doesn't seem to support the "copies" attribute.
+
+ - The HTTP chunking code was missing a CR LF pair at the
+ end of a 0-length chunk.
+
+ - The httpSeparate() function didn't handle embedded
+ usernames and passwords in the URI properly.
+
+ - Doing "lpadmin -p printer -E" didn't restart printing
+ if there were pending jobs.
+
+ - The cancel-job operation now requires either a
+ requesting-user-name attribute or an authenticated
+ username.
+
+ - The add-printer code did not report errors if the
+ interface script or PPD file could not be renamed.
+
+ - Request files are now created without world read
+ permissions.
+
+ - Added a cupsLastError() function to the CUPS API to
+ retrieve the IPP error code from the last request.
+
+ - Options are now case-insensitive.
+
+ - The lpq command now provides 10 characters for the
+ username instead of the original (Berkeley standard)
+ 7.
+
+ - The cancel command needed a local CUPS server to work
+ (or the appropriate ServerName in cupsd.conf)
+
+ - The cancel and lprm commands didn't report the IPP
+ error if the job could not be cancelled.
+
+ - The lp and lpr commands didn't intercept SIGTERM to
+ remove temporary files when printing from stdin.
+
+ - The lp and lpr commands didn't report the IPP error if
+ the job could not be printed.
diff --git a/CREDITS.txt b/CREDITS.txt
new file mode 100644
index 000000000..71bad1486
--- /dev/null
+++ b/CREDITS.txt
@@ -0,0 +1,26 @@
+CREDITS.txt - 01/27/2000
+------------------------
+
+Few projects are completed by one person, and CUPS is no exception. We'd
+like to thank the following individuals for their contributions:
+
+ Nathaniel Barbour - Lots of testing and feedback.
+ N. Becker - setsid().
+ Jean-Eric Cuendet - GhostScript filters for CUPS.
+ Van Dang - HTTP and IPP policeman.
+ Dr. ZP Han - setgid()/setuid().
+ Guy Harris - *BSD shared libraries and lots of other fixes.
+ Wang Jian - CUPS RPM corrections.
+ Roderick Johnstone - Beta tester of the millenium.
+ Sergey V. Kovalyov - ESP Print Pro and CUPS beta tester.
+ Mark Lawrence - Microsoft interoperability testing.
+ Jason McMullan - Original CUPS RPM distributions.
+ Wes Morgan - *BSD fixes.
+ Ulrich Oldendorf - German locale.
+ Petter Reinholdtsen - HP-UX compiler stuff.
+ Stuart Stevens - HP JetDirect IPP information.
+ Kiko - Bug fixes.
+ L. Peter Deutsch - MD5 code.
+
+If I've missed someone, please let me know by sending an email to
+"mike@easysw.com".
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..999ed1b7c
--- /dev/null
+++ b/Makedefs.in
@@ -0,0 +1,147 @@
+#
+# "$Id$"
+#
+# Common makefile definitions for the Common UNIX Printing System (CUPS).
+#
+# Copyright 1997-2000 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 = @LIBTOOL@ @CC@
+CHMOD = @CHMOD@
+CP = @CP@
+DSO = @DSO@
+HTMLDOC = @HTMLDOC@
+LIBTOOL = @LIBTOOL@
+LN = /bin/ln -sf
+MKDIR = @MKDIR@ -p
+MV = @MV@
+NROFF = @NROFF@
+RANLIB = @RANLIB@
+RM = @RM@ -f
+SED = @SED@
+SHELL = /bin/sh
+
+#
+# Installation programs...
+#
+
+INSTALL_CAT = @INSTALL_CAT@
+INSTALL_DATA = $(CP)
+INSTALL_LIB = $(LIBTOOL) $(CP)
+INSTALL_MAN = $(CP)
+INSTALL_PROGRAM = $(LIBTOOL) $(CP)
+INSTALL_SCRIPT = $(CP)
+
+#
+# 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)
+LINKCUPS = @LINKCUPS@
+LINKCUPSIMAGE = @LINKCUPSIMAGE@
+LIBS = $(LINKCUPS) $(NETLIBS) @LIBS@
+NETLIBS = @NETLIBS@
+OPTIM = @OPTIM@
+OPTIONS =
+
+#
+# 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@
+DOCDIR = @CUPS_DOCDIR@
+INCLUDEDIR = $(includedir)
+LIBDIR = $(libdir)
+LOCALEDIR = @CUPS_LOCALEDIR@
+LOGDIR = @CUPS_LOGDIR@
+MANDIR = @mandir@
+PAMDIR = @PAMDIR@
+REQUESTS = @CUPS_REQUESTS@
+SBINDIR = @sbindir@
+SERVERBIN = @CUPS_SERVERBIN@
+SERVERROOT = @CUPS_SERVERROOT@
+
+#
+# Rules...
+#
+
+.SILENT:
+.SUFFIXES: .a .c .h .man .o .1 .5 .8
+.c.o:
+ echo Compiling $<...
+ $(CC) $(CFLAGS) -c $<
+.man.1 .man.5 .man.8:
+ echo Formatting $<...
+ $(RM) $@
+ $(NROFF) -man $< >$@
+
+#
+# End of "$Id$"
+#
diff --git a/Makefile b/Makefile
new file mode 100644
index 000000000..d4aaafcc6
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,94 @@
+#
+# "$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) -$(MAKEFLAGS)) || break;\
+ done
+
+#
+# Remove object and target files...
+#
+
+clean:
+ for dir in $(DIRS); do\
+ echo Cleaning in $$dir... ;\
+ (cd $$dir; $(MAKE) -$(MAKEFLAGS) clean) || break;\
+ done
+
+#
+# Install object and target files...
+#
+
+install:
+ for dir in $(DIRS); do\
+ echo Installing in $$dir... ;\
+ (cd $$dir; $(MAKE) -$(MAKEFLAGS) install) || break;\
+ done
+ echo Installing in conf...
+ (cd conf; $(MAKE) -$(MAKEFLAGS) install)
+ echo Installing in data...
+ (cd data; $(MAKE) -$(MAKEFLAGS) install)
+ echo Installing in doc...
+ (cd doc; $(MAKE) -$(MAKEFLAGS) install)
+ echo Installing in fonts...
+ (cd fonts; $(MAKE) -$(MAKEFLAGS) install)
+ echo Installing in ppd...
+ (cd ppd; $(MAKE) -$(MAKEFLAGS) install)
+ echo Installing in templates...
+ (cd templates; $(MAKE) -$(MAKEFLAGS) install)
+
+#
+# Make a software distribution...
+#
+
+epm:
+ epm -v cups
+
+rpm:
+ epm -v -f rpm cups
+
+deb:
+ epm -v -f deb cups
+
+tardist:
+ epm -v -f tardist cups
+
+#
+# End of "$Id$".
+#
diff --git a/README.txt b/README.txt
new file mode 100644
index 000000000..f40e12842
--- /dev/null
+++ b/README.txt
@@ -0,0 +1,242 @@
+README - CUPS v1.1b3 - 04/19/2000
+---------------------------------
+
+************************************************************************
+************************************************************************
+**** ****
+**** BETA SOFTWARE BETA SOFTWARE BETA SOFTWARE BETA SOFTWARE ****
+**** ****
+************************************************************************
+************************************************************************
+
+This is an official public beta release for the Common UNIX Printing
+System. Since this is a beta release, we do not recommend that you
+use this software on a production system. Instead, please use the
+current 1.0.x release for your production systems.
+
+Also, currently we are only providing source code for the beta releases.
+As we approach a final production ("gold") release of CUPS 1.1 we will
+provide binary distributions as well.
+
+Please report all problems in the CUPS 1.1 beta releases to
+"cups-beta@cups.org" or to the CUPS mailing list.
+
+Thanks for using CUPS!
+
+
+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)
+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 5.50) and an image file RIP that can be used
+to support non-PostScript printers.
+
+CUPS is Copyright 1993-2000 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 with glibc2 or higher
+ - 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 available from:
+
+ http://www.easysw.com/epm
+
+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.
+
+
+READING THE DOCUMENTATION
+
+Once you have installed the software you can access the documentation
+(and a bunch of other stuff) on-line at:
+
+ http://localhost:631
+
+If you're having trouble getting that far, the documentation is located
+in the "/usr/share/cups/doc" directory in the binary distributions, and
+under the "doc" directory in the source archives.
+
+Please read the documentation before asking questions.
+
+
+SETTING UP PRINTER QUEUES USING YOUR WEB BROWSER
+
+CUPS 1.1 includes a new web-based administration tool that allows you
+to manage printers, classes, and jobs on your server. To access the
+printer administration tools open the following URL in your browser:
+
+ http://localhost:631/admin
+
+You will be asked for the administration password (root or any other
+user in the sys/system/root group on your system) and then shown a
+menu of available functions.
+
+
+SETTING UP PRINTER QUEUES FROM THE COMMAND-LINE
+
+CUPS works best with PPD (PostScript Printer Description) files. In a
+pinch you can also use System V style printer interface scripts.
+
+Six sample PPD files are provided with this distribution that utilize
+the PostScript and image file RIPs and the sample EPSON and HP printer
+drivers. 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/lp0 -E
+ /usr/lib/lpadmin -p DeskJet -m deskjet.ppd -v parallel:/dev/lp1 -E
+ /usr/lib/lpadmin -p DeskJet -m deskjet.ppd -v parallel:/dev/lp2 -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 other sample drivers you can use:
+
+ Driver PPD File
+ -------------------------- ------------
+ HP DeskJet Series deskjet.ppd
+ HP LaserJet Series laserjet.ppd
+ EPSON Stylus Color Series stcolor.ppd
+ EPSON Stylus Photo Series stphoto.ppd
+ EPSON 9-pin Series epson9.ppd [not in beta 3]
+ EPSON 24-pin Series epson24.ppd [not in beta 3]
+
+These sample drivers provide basic printing capabilities, but generally
+do not exercise the full potential of the printers or CUPS. For
+commercial printer drivers check out our ESP Print Pro software at:
+
+ http://www.easysw.com/printpro
+
+
+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. If you are running a version
+of Linux, be sure to provide the Linux distribution you have, too.
+
+
+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-2000 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
diff --git a/backend/Makefile b/backend/Makefile
new file mode 100644
index 000000000..c6a533b3a
--- /dev/null
+++ b/backend/Makefile
@@ -0,0 +1,139 @@
+#
+# "$Id$"
+#
+# Backend makefile for the Common UNIX Printing System (CUPS).
+#
+# Copyright 1997-2000 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
+
+BACKENDS = ipp lpd parallel serial socket usb
+TARGETS = betest $(BACKENDS)
+OBJS = betest.o ipp.o lpd.o parallel.o serial.o socket.o usb.o
+
+
+#
+# Make all targets...
+#
+
+all: $(TARGETS)
+
+
+#
+# Clean all object files...
+#
+
+clean:
+ $(RM) $(OBJS) $(TARGETS)
+
+
+#
+# Install all targets...
+#
+
+install:
+ -$(MKDIR) $(SERVERBIN)/backend
+ $(INSTALL_BIN) $(BACKENDS) $(SERVERBIN)/backend
+ $(RM) $(SERVERBIN)/backend/http
+ $(LN) ipp $(SERVERBIN)/backend/http
+
+
+#
+# betest
+#
+
+betest: betest.o ../cups/$(LIBCUPS)
+ echo Linking $@...
+ $(CC) $(LDFLAGS) -o betest betest.o $(LIBS)
+
+betest.o: ../cups/string.h ../Makedefs
+
+
+#
+# ipp
+#
+
+ipp: ipp.o ../cups/$(LIBCUPS)
+ echo Linking $@...
+ $(CC) $(LDFLAGS) -o ipp ipp.o $(LIBS)
+ $(RM) http
+ $(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
+
+
+#
+# socket
+#
+
+socket: socket.o ../cups/$(LIBCUPS)
+ echo Linking $@...
+ $(CC) $(LDFLAGS) -o socket socket.o $(LIBS)
+
+socket.o: ../cups/cups.h ../Makedefs
+
+
+#
+# usb
+#
+
+usb: usb.o ../cups/$(LIBCUPS)
+ echo Linking $@...
+ $(CC) $(LDFLAGS) -o usb usb.o $(LIBS)
+
+usb.o: ../cups/cups.h ../Makedefs
+
+
+#
+# End of "$Id$".
+#
diff --git a/backend/betest.c b/backend/betest.c
new file mode 100644
index 000000000..8c68284e1
--- /dev/null
+++ b/backend/betest.c
@@ -0,0 +1,85 @@
+/*
+ * "$Id$"
+ *
+ * Backend test program for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-2000 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() - Run the named backend.
+ */
+
+/*
+ * Include necessary headers.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <cups/string.h>
+#include <unistd.h>
+
+
+/*
+ * 'main()' - Run the named backend.
+ *
+ * Usage:
+ *
+ * betest device-uri job-id user title copies options [file]
+ */
+
+int /* O - Exit status */
+main(int argc, /* I - Number of command-line arguments (7 or 8) */
+ char *argv[]) /* I - Command-line arguments */
+{
+ char backend[255]; /* Method in URI */
+
+
+ if (argc < 7 || argc > 8)
+ {
+ fputs("Usage: betest device-uri job-id user title copies options [file]\n",
+ stderr);
+ return (1);
+ }
+
+ /*
+ * Extract the method from the device-uri - that's the program we want to
+ * execute.
+ */
+
+ if (sscanf(argv[1], "%254[^:]", backend) != 1)
+ {
+ fputs("betest: Bad device-uri - no colon!\n", stderr);
+ return (1);
+ }
+
+ /*
+ * Execute and return
+ */
+
+ execl(backend, argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7],
+ NULL);
+
+ return (1);
+}
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/backend/ipp.c b/backend/ipp.c
new file mode 100644
index 000000000..4026c4898
--- /dev/null
+++ b/backend/ipp.c
@@ -0,0 +1,675 @@
+/*
+ * "$Id$"
+ *
+ * IPP backend for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-2000 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 */
+ ipp_status_t ipp_status; /* Status of IPP request */
+ 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 */
+ ipp_attribute_t *copies_sup; /* copies-supported 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 */
+ int copies; /* Number of copies remaining */
+ const char *content_type; /* CONTENT_TYPE environment variable */
+
+
+ if (argc == 1)
+ {
+ if ((s = strrchr(argv[0], '/')) != NULL)
+ s ++;
+ else
+ s = argv[0];
+
+ printf("network %s \"Unknown\" \"Internet Printing Protocol\"\n", s);
+ return (0);
+ }
+ else 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
+ {
+ strncpy(filename, argv[6], sizeof(filename) - 1);
+ filename[sizeof(filename) - 1] = '\0';
+ }
+
+ /*
+ * Open the print file...
+ */
+
+ if ((fp = fopen(filename, "rb")) == NULL)
+ {
+ perror("ERROR: Unable to open print file");
+ return (1);
+ }
+ else
+ stat(filename, &fileinfo);
+
+ /*
+ * Extract the hostname and printer name from the URI...
+ */
+
+ httpSeparate(argv[0], method, username, hostname, &port, resource);
+
+ /*
+ * Try connecting to the remote server...
+ */
+
+ do
+ {
+ fprintf(stderr, "INFO: Connecting to %s...\n", hostname);
+
+ if ((http = httpConnect(hostname, port)) == NULL)
+ {
+ if (errno == ECONNREFUSED)
+ {
+ fprintf(stderr, "INFO: Network host \'%s\' is busy; will retry in 30 seconds...",
+ hostname);
+ sleep(30);
+ }
+ else
+ {
+ perror("ERROR: Unable to connect to IPP host");
+ sleep(30);
+ }
+ }
+ }
+ while (http == NULL);
+
+ /*
+ * 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...
+ */
+
+ snprintf(uri, sizeof(uri), "%s://%s:%d%s", method, hostname, port, resource);
+
+ /*
+ * First validate the destination and see if the device supports multiple
+ * copies. We have to do this because some IPP servers (e.g. HP JetDirect)
+ * don't support the copies attribute...
+ */
+
+ language = cupsLangDefault();
+ copies_sup = NULL;
+
+ do
+ {
+ /*
+ * Build the IPP request...
+ */
+
+ 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 != NULL ? language->language : "C");
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
+ NULL, uri);
+
+ /*
+ * Now fill in the HTTP request stuff...
+ */
+
+ httpClearFields(http);
+ httpSetField(http, HTTP_FIELD_CONTENT_TYPE, "application/ipp");
+ if (username[0])
+ {
+ httpEncode64(password, username);
+ httpSetField(http, HTTP_FIELD_AUTHORIZATION, password);
+ }
+
+ sprintf(buffer, "%u", ippLength(request));
+ httpSetField(http, HTTP_FIELD_CONTENT_LENGTH, buffer);
+
+ /*
+ * Do the request...
+ */
+
+ for (;;)
+ {
+ /*
+ * POST the request, retrying as needed...
+ */
+
+ if (httpPost(http, resource))
+ {
+ fputs("INFO: Unable to POST get-printer-attributes 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, getting 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);
+
+ ipp_status = response->request.status.status_code;
+
+ if (ipp_status > IPP_OK_CONFLICT)
+ {
+ if (ipp_status == IPP_PRINTER_BUSY ||
+ ipp_status == IPP_SERVICE_UNAVAILABLE)
+ {
+ fputs("INFO: Printer busy; will retry in 10 seconds...\n", stderr);
+ sleep(10);
+ }
+ else
+ {
+ fprintf(stderr, "ERROR: Printer will not accept print file (%x)!\n",
+ ipp_status);
+ status = HTTP_ERROR;
+ }
+ }
+ else if ((copies_sup = ippFindAttribute(response, "copies-supported",
+ IPP_TAG_RANGE)) != NULL)
+ {
+ /*
+ * Has the "copies-supported" attribute - does it have an upper
+ * bound > 1?
+ */
+
+ if (copies_sup->values[0].range.upper <= 1)
+ copies_sup = NULL; /* No */
+ }
+
+ ippDelete(response);
+ }
+ else
+ {
+ response = NULL;
+
+ if (status == HTTP_ERROR)
+ {
+ fprintf(stderr, "WARNING: Did not receive the IPP response (%d)\n",
+ errno);
+ status = HTTP_OK;
+ ipp_status = IPP_PRINTER_BUSY;
+ }
+ else
+ {
+ fprintf(stderr, "ERROR: Validate request was not accepted (%d)!\n", status);
+ ipp_status = IPP_FORBIDDEN;
+ }
+ }
+
+ httpFlush(http);
+
+ break;
+ }
+
+ if (status != HTTP_OK)
+ {
+ if (fp != stdin)
+ fclose(fp);
+
+ httpClose(http);
+
+ return (1);
+ }
+ }
+ while (ipp_status > IPP_OK_CONFLICT);
+
+ /*
+ * See if the printer supports multiple copies...
+ */
+
+ if (copies_sup)
+ copies = 1;
+ else
+ copies = atoi(argv[4]);
+
+ /*
+ * Then issue the print-job request...
+ */
+
+ while (copies > 0)
+ {
+ /*
+ * Build the IPP request...
+ */
+
+ request = ippNew();
+ request->request.op.operation_id = IPP_PRINT_JOB;
+ 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 != NULL ? language->language : "C");
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
+ NULL, uri);
+
+ fprintf(stderr, "DEBUG: printer-uri = \"%s\"\n", uri);
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
+ NULL, argv[2]);
+
+ fprintf(stderr, "DEBUG: requesting-user-name = \"%s\"\n", argv[2]);
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "job-name", NULL,
+ argv[3]);
+
+ fprintf(stderr, "DEBUG: job-name = \"%s\"\n", argv[3]);
+
+ /*
+ * Handle options on the command-line...
+ */
+
+ options = NULL;
+ num_options = cupsParseOptions(argv[5], 0, &options);
+
+ if (cupsGetOption("raw", num_options, options) ||
+ ((content_type = getenv("CONTENT_TYPE")) != NULL &&
+ strcasecmp(content_type, "application/vnd.cups-raw") == 0))
+ 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");
+
+ if (copies_sup)
+ 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 (strcasecmp(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 (strncasecmp(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 (strcasecmp(s, "dpc") == 0)
+ ippAddResolution(request, IPP_TAG_JOB, option, IPP_RES_PER_CM, n, n2);
+ else if (strcasecmp(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");
+ if (username[0])
+ {
+ httpEncode64(password, username);
+ httpSetField(http, HTTP_FIELD_AUTHORIZATION, password);
+ }
+
+ sprintf(buffer, "%u", ippLength(request) + (size_t)fileinfo.st_size);
+ httpSetField(http, HTTP_FIELD_CONTENT_LENGTH, buffer);
+
+ /*
+ * Do the request...
+ */
+
+ for (;;)
+ {
+ /*
+ * POST the request, retrying as needed...
+ */
+
+ httpReconnect(http);
+
+ if (httpPost(http, resource))
+ {
+ fputs("INFO: Unable to POST print request; retrying...\n", stderr);
+ sleep(10);
+ 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...
+ */
+
+ rewind(fp);
+
+ 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 ((ipp_status = response->request.status.status_code) > IPP_OK_CONFLICT)
+ {
+ if (ipp_status == IPP_SERVICE_UNAVAILABLE ||
+ ipp_status == IPP_PRINTER_BUSY)
+ {
+ fputs("INFO: Printer is busy; retrying print job...\n", stderr);
+ sleep(10);
+ }
+ else
+ 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;
+ ipp_status = IPP_PRINTER_BUSY;
+
+ if (status == HTTP_ERROR)
+ {
+ fprintf(stderr, "WARNING: Did not receive the IPP response (%d)\n",
+ errno);
+ status = HTTP_OK;
+ }
+ else
+ fprintf(stderr, "ERROR: Print request was not accepted (%d)!\n", status);
+ }
+
+ httpFlush(http);
+
+ break;
+ }
+
+ if (request != NULL)
+ ippDelete(request);
+ if (response != NULL)
+ ippDelete(response);
+
+ if (ipp_status <= IPP_OK_CONFLICT)
+ {
+ fprintf(stderr, "PAGE: 1 %d\n", copies_sup ? atoi(argv[4]) : 1);
+ copies --;
+ }
+ else if (ipp_status != IPP_SERVICE_UNAVAILABLE &&
+ ipp_status != IPP_PRINTER_BUSY)
+ break;
+ }
+
+ /*
+ * Free memory...
+ */
+
+ httpClose(http);
+
+ /*
+ * Close and remove the temporary file if necessary...
+ */
+
+ fclose(fp);
+
+ if (argc < 7)
+ unlink(filename);
+
+ /*
+ * 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..bbe5c17ea
--- /dev/null
+++ b/backend/lpd.c
@@ -0,0 +1,420 @@
+/*
+ * "$Id$"
+ *
+ * Line Printer Daemon backend for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-2000 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 == 1)
+ {
+ puts("network lpd \"Unknown\" \"LPD/LPR Host or Printer\"");
+ return (0);
+ }
+ else 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
+ {
+ strncpy(filename, argv[6], sizeof(filename) - 1);
+ filename[sizeof(filename) - 1] = '\0';
+ }
+
+ /*
+ * Extract the hostname and printer name from the URI...
+ */
+
+ httpSeparate(argv[0], method, username, hostname, &port, resource);
+
+ /*
+ * Queue the job...
+ */
+
+ if (argc > 6)
+ {
+ status = lpd_queue(hostname, resource + 1, filename,
+ argv[2] /* user */, atoi(argv[4]) /* copies */);
+
+ if (!status)
+ fprintf(stderr, "PAGE: 1 %d\n", atoi(argv[4]));
+ }
+ else
+ status = lpd_queue(hostname, resource + 1, filename,
+ argv[2] /* user */, 1);
+
+ /*
+ * 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 = vsnprintf(buf, sizeof(buf), format, ap);
+ va_end(ap);
+
+ fprintf(stderr, "DEBUG: lpd_command %2.2x %s", buf[0], buf + 1);
+
+ /*
+ * Send the command...
+ */
+
+ fprintf(stderr, "DEBUG: Sending command string (%d bytes)...\n", bytes);
+
+ if (send(fd, buf, bytes, 0) < bytes)
+ return (-1);
+
+ /*
+ * Read back the status from the command and return it...
+ */
+
+ fprintf(stderr, "DEBUG: Reading command status...\n");
+
+ 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 reserve port");
+ sleep(30);
+ continue;
+ }
+
+ 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");
+ sleep(30);
+ }
+ }
+ else
+ break;
+ }
+
+ fprintf(stderr, "INFO: Connected on port %d...\n", port);
+
+ /*
+ * 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 */
+
+ snprintf(control, sizeof(control), "H%s\nP%s\n", localhost, user);
+ cptr = control + strlen(control);
+
+ while (copies > 0)
+ {
+
+ snprintf(cptr, sizeof(control) - (cptr - control), "ldfA%03d%s\n",
+ getpid() % 1000, localhost);
+ cptr += strlen(cptr);
+ copies --;
+ }
+
+ snprintf(cptr, sizeof(control) - (cptr - control),
+ "UdfA%03d%s\nNdfA%03d%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..ea61efd86
--- /dev/null
+++ b/backend/parallel.c
@@ -0,0 +1,528 @@
+/*
+ * "$Id$"
+ *
+ * Parallel port backend for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-2000 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.
+ * list_devices() - List all parallel devices.
+ */
+
+/*
+ * Include necessary headers.
+ */
+
+#include <cups/cups.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.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__ */
+
+#ifdef __sgi
+# include <invent.h>
+# ifndef INV_EPP_ECP_PLP
+# define INV_EPP_ECP_PLP 6 /* From 6.3/6.4/6.5 sys/invent.h */
+# define INV_ASO_SERIAL 14 /* serial portion of SGI ASO board */
+# define INV_IOC3_DMA 16 /* DMA mode IOC3 serial */
+# define INV_IOC3_PIO 17 /* PIO mode IOC3 serial */
+# define INV_ISA_DMA 19 /* DMA mode ISA serial -- O2 */
+# endif /* !INV_EPP_ECP_PLP */
+#endif /* __sgi */
+
+
+/*
+ * Local functions...
+ */
+
+void list_devices(void);
+
+
+/*
+ * '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 copies; /* Number of copies to print */
+ int fd; /* Parallel device */
+ 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 == 1)
+ {
+ list_devices();
+ return (0);
+ }
+ else 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;
+ copies = 1;
+ }
+ else
+ {
+ /*
+ * Try to open the print file...
+ */
+
+ if ((fp = fopen(argv[6], "rb")) == NULL)
+ {
+ perror("ERROR: unable to open print file");
+ return (1);
+ }
+
+ copies = atoi(argv[4]);
+ }
+
+ /*
+ * 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...
+ */
+
+ do
+ {
+ if ((fd = open(resource, O_WRONLY | O_EXCL)) == -1)
+ {
+ if (errno == EBUSY)
+ {
+ fputs("INFO: Parallel port busy; will retry in 30 seconds...\n", stderr);
+ sleep(30);
+ }
+ else
+ {
+ perror("ERROR: Unable to open parallel port device file");
+ return (1);
+ }
+ }
+ }
+ while (fd < 0);
+
+ /*
+ * 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...
+ */
+
+ while (copies > 0)
+ {
+ copies --;
+
+ if (fp != stdin)
+ {
+ fputs("PAGE: 1 1\n", stderr);
+ rewind(fp);
+ }
+
+ 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);
+}
+
+
+/*
+ * 'list_devices()' - List all parallel devices.
+ */
+
+void
+list_devices(void)
+{
+ static char *funky_hex = "0123456789abcdefghijklmnopqrstuvwxyz";
+ /* Funky hex numbering used for some devices */
+
+#ifdef __linux
+ int i; /* Looping var */
+ int fd; /* File descriptor */
+ char device[255]; /* Device filename */
+ FILE *probe; /* /proc/parport/n/autoprobe file */
+ char line[1024], /* Line from file */
+ *delim, /* Delimiter in file */
+ make[IPP_MAX_NAME], /* Make from file */
+ model[IPP_MAX_NAME]; /* Model from file */
+
+
+ for (i = 0; i < 4; i ++)
+ {
+ sprintf(device, "/proc/parport/%d/autoprobe", i);
+ if ((probe = fopen(device, "r")) != NULL)
+ {
+ memset(make, 0, sizeof(make));
+ memset(model, 0, sizeof(model));
+ strcpy(model, "Unknown");
+
+ while (fgets(line, sizeof(line), probe) != NULL)
+ {
+ /*
+ * Strip trailing ; and/or newline.
+ */
+
+ if ((delim = strrchr(line, ';')) != NULL)
+ *delim = '\0';
+ else if ((delim = strrchr(line, '\n')) != NULL)
+ *delim = '\0';
+
+ /*
+ * Look for MODEL and MANUFACTURER lines...
+ */
+
+ if (strncmp(line, "MODEL:", 6) == 0 &&
+ strncmp(line, "MODEL:Unknown", 13) != 0)
+ strncpy(model, line + 6, sizeof(model) - 1);
+ else if (strncmp(line, "MANUFACTURER:", 13) == 0 &&
+ strncmp(line, "MANUFACTURER:Unknown", 20) != 0)
+ strncpy(make, line + 13, sizeof(make) - 1);
+ }
+
+ fclose(probe);
+
+ if (make[0])
+ printf("direct parallel:/dev/lp%d \"%s %s\" \"Parallel Port #%d\"\n",
+ i, make, model, i + 1);
+ else
+ printf("direct parallel:/dev/lp%d \"%s\" \"Parallel Port #%d\"\n",
+ i, model, i + 1);
+ }
+ else
+ {
+ sprintf(device, "/dev/lp%d", i);
+ if ((fd = open(device, O_WRONLY)) >= 0)
+ {
+ close(fd);
+ printf("direct parallel:%s \"Unknown\" \"Parallel Port #%d\"\n", device, i + 1);
+ }
+ else
+ {
+ sprintf(device, "/dev/par%d", i);
+ if ((fd = open(device, O_WRONLY)) >= 0)
+ {
+ close(fd);
+ printf("direct parallel:%s \"Unknown\" \"Parallel Port #%d\"\n", device, i + 1);
+ }
+ }
+ }
+ }
+#elif defined(__sgi)
+ int i, j, n; /* Looping vars */
+ char device[255]; /* Device filename */
+ inventory_t *inv; /* Hardware inventory info */
+
+
+ /*
+ * IRIX maintains a hardware inventory of most devices...
+ */
+
+ setinvent();
+
+ while ((inv = getinvent()) != NULL)
+ {
+ if (inv->inv_class == INV_PARALLEL &&
+ (inv->inv_type == INV_ONBOARD_PLP ||
+ inv->inv_type == INV_EPP_ECP_PLP))
+ {
+ /*
+ * Standard parallel port...
+ */
+
+ puts("direct parallel:/dev/plp \"Unknown\" \"Onboard Parallel Port\"");
+ }
+ else if (inv->inv_class == INV_PARALLEL &&
+ inv->inv_type == INV_EPC_PLP)
+ {
+ /*
+ * EPC parallel port...
+ */
+
+ printf("direct parallel:/dev/plp%d \"Unknown\" \"Integral EPC parallel port, Ebus slot %d\"\n",
+ inv->inv_controller, inv->inv_controller);
+ }
+ }
+
+ endinvent();
+
+ /*
+ * Central Data makes serial and parallel "servers" that can be
+ * connected in a number of ways. Look for ports...
+ */
+
+ for (i = 0; i < 10; i ++)
+ for (j = 0; j < 8; j ++)
+ for (n = 0; n < 32; n ++)
+ {
+ if (i == 8) /* EtherLite */
+ sprintf(device, "/dev/lpn%d%c", j, funky_hex[n]);
+ else if (i == 9) /* PCI */
+ sprintf(device, "/dev/lpp%d%c", j, funky_hex[n]);
+ else /* SCSI */
+ sprintf(device, "/dev/lp%d%d%c", i, j, funky_hex[n]);
+
+ if (access(device, 0) == 0)
+ {
+ if (i == 8)
+ printf("direct parallel:%s \"Unknown\" \"Central Data EtherLite Parallel Port, ID %d, port %d\"\n",
+ device, j, n);
+ else if (i == 9)
+ printf("direct parallel:%s \"Unknown\" \"Central Data PCI Parallel Port, ID %d, port %d\"\n",
+ device, j, n);
+ else
+ printf("direct parallel:%s \"Unknown\" \"Central Data SCSI Parallel Port, logical bus %d, ID %d, port %d\"\n",
+ device, i, j, n);
+ }
+ }
+#elif defined(__sun)
+ int i, j, n; /* Looping vars */
+ char device[255]; /* Device filename */
+
+
+ /*
+ * Standard parallel ports...
+ */
+
+ for (i = 0; i < 10; i ++)
+ {
+ sprintf(device, "/dev/ecpp%d", i);
+ if (access(device, 0) == 0)
+ printf("direct parallel:%s \"Unknown\" \"Sun IEEE-1284 Parallel Port #%d\"\n",
+ device, i + 1);
+ }
+
+ for (i = 0; i < 10; i ++)
+ {
+ sprintf(device, "/dev/bpp%d", i);
+ if (access(device, 0) == 0)
+ printf("direct parallel:%s \"Unknown\" \"Sun Standard Parallel Port #%d\"\n",
+ device, i + 1);
+ }
+
+ for (i = 0; i < 3; i ++)
+ {
+ sprintf(device, "/dev/lp%d", i);
+
+ if (access(device, 0) == 0)
+ printf("direct parallel:%s \"Unknown\" \"PC Parallel Port #%d\"\n",
+ device, i + 1);
+ }
+
+ /*
+ * MAGMA parallel ports...
+ */
+
+ for (i = 0; i < 40; i ++)
+ {
+ sprintf(device, "/dev/pm%02d", i);
+ if (access(device, 0) == 0)
+ printf("direct parallel:%s \"Unknown\" \"MAGMA Parallel Board #%d Port #%d\"\n",
+ device, (i / 10) + 1, (i % 10) + 1);
+ }
+
+ /*
+ * Central Data parallel ports...
+ */
+
+ for (i = 0; i < 9; i ++)
+ for (j = 0; j < 8; j ++)
+ for (n = 0; n < 32; n ++)
+ {
+ if (i == 8) /* EtherLite */
+ sprintf(device, "/dev/sts/lpN%d%c", j, funky_hex[n]);
+ else
+ sprintf(device, "/dev/sts/lp%c%d%c", i + 'C', j,
+ funky_hex[n]);
+
+ if (access(device, 0) == 0)
+ {
+ if (i == 8)
+ printf("direct parallel:%s \"Unknown\" \"Central Data EtherLite Parallel Port, ID %d, port %d\"\n",
+ device, j, n);
+ else
+ printf("direct parallel:%s \"Unknown\" \"Central Data SCSI Parallel Port, logical bus %d, ID %d, port %d\"\n",
+ device, i, j, n);
+ }
+ }
+#elif defined(__hpux)
+ int i, j, n; /* Looping vars */
+ char device[255]; /* Device filename */
+
+
+ /*
+ * Standard parallel ports...
+ */
+
+ if (access("/dev/rlp", 0) == 0)
+ puts("direct parallel:/dev/rlp \"Unknown\" \"Standard Parallel Port (/dev/rlp)\"");
+
+ for (i = 0; i < 7; i ++)
+ for (j = 0; j < 7; j ++)
+ {
+ sprintf(device, "/dev/c%dt%dd0_lp", i, j);
+ if (access(device, 0) == 0)
+ printf("direct parallel:%s \"Unknown\" \"Parallel Port #%d,%d\"\n", i, j);
+ }
+
+ /*
+ * Central Data parallel ports...
+ */
+
+ for (i = 0; i < 9; i ++)
+ for (j = 0; j < 8; j ++)
+ for (n = 0; n < 32; n ++)
+ {
+ if (i == 8) /* EtherLite */
+ sprintf(device, "/dev/lpN%d%c", j, funky_hex[n]);
+ else
+ sprintf(device, "/dev/lp%c%d%c", i + 'C', j,
+ funky_hex[n]);
+
+ if (access(device, 0) == 0)
+ {
+ if (i == 8)
+ printf("direct parallel:%s \"Unknown\" \"Central Data EtherLite Parallel Port, ID %d, port %d\"\n",
+ device, j, n);
+ else
+ printf("direct parallel:%s \"Unknown\" \"Central Data SCSI Parallel Port, logical bus %d, ID %d, port %d\"\n",
+ device, i, j, n);
+ }
+ }
+#elif defined(__osf__)
+ int i; /* Looping var */
+ int fd; /* File descriptor */
+ char device[255]; /* Device filename */
+
+
+ for (i = 0; i < 3; i ++)
+ {
+ sprintf(device, "/dev/lp%d", i);
+ if ((fd = open(device, O_WRONLY)) >= 0)
+ {
+ close(fd);
+ printf("direct parallel:%s \"Unknown\" \"Parallel Port #%d\"\n", device, i + 1);
+ }
+ }
+#elif defined(FreeBSD) || defined(OpenBSD) || defined(NetBSD)
+ int i; /* Looping var */
+ int fd; /* File descriptor */
+ char device[255]; /* Device filename */
+
+
+ for (i = 0; i < 3; i ++)
+ {
+ sprintf(device, "/dev/lpt%d", i);
+ if ((fd = open(device, O_WRONLY)) >= 0)
+ {
+ close(fd);
+ printf("direct parallel:%s \"Unknown\" \"Parallel Port #%d\"\n", device, i + 1);
+ }
+ }
+#endif
+}
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/backend/serial.c b/backend/serial.c
new file mode 100644
index 000000000..6c2e02ecd
--- /dev/null
+++ b/backend/serial.c
@@ -0,0 +1,670 @@
+/*
+ * "$Id$"
+ *
+ * Serial port backend for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-2000 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.
+ * list_devices() - List all serial devices.
+ */
+
+/*
+ * Include necessary headers.
+ */
+
+#include <cups/cups.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.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__ */
+
+#ifdef __sgi
+# include <invent.h>
+# ifndef INV_EPP_ECP_PLP
+# define INV_EPP_ECP_PLP 6 /* From 6.3/6.4/6.5 sys/invent.h */
+# define INV_ASO_SERIAL 14 /* serial portion of SGI ASO board */
+# define INV_IOC3_DMA 16 /* DMA mode IOC3 serial */
+# define INV_IOC3_PIO 17 /* PIO mode IOC3 serial */
+# define INV_ISA_DMA 19 /* DMA mode ISA serial -- O2 */
+# endif /* !INV_EPP_ECP_PLP */
+#endif /* __sgi */
+
+
+/*
+ * Local functions...
+ */
+
+void list_devices(void);
+
+
+/*
+ * '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 copies; /* Number of copies to print */
+ int fd; /* Parallel device */
+ 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 == 1)
+ {
+ list_devices();
+ return (0);
+ }
+ else 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;
+ copies = 1;
+ }
+ else
+ {
+ /*
+ * Try to open the print file...
+ */
+
+ if ((fp = fopen(argv[6], "rb")) == NULL)
+ {
+ perror("ERROR: unable to open print file");
+ return (1);
+ }
+
+ copies = atoi(argv[4]);
+ }
+
+ /*
+ * 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 serial port device...
+ */
+
+ do
+ {
+ if ((fd = open(resource, O_WRONLY | O_NOCTTY | O_EXCL)) == -1)
+ {
+ if (errno == EBUSY)
+ {
+ fputs("INFO: Serial port busy; will retry in 30 seconds...\n", stderr);
+ sleep(30);
+ }
+ else
+ {
+ perror("ERROR: Unable to open serial port device file");
+ return (1);
+ }
+ }
+ }
+ while (fd < 0);
+
+ /*
+ * 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...
+ */
+
+ while (copies > 0)
+ {
+ copies --;
+
+ if (fp != stdin)
+ {
+ fputs("PAGE: 1 1\n", stderr);
+ rewind(fp);
+ }
+
+ 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);
+}
+
+
+/*
+ * 'list_devices()' - List all serial devices.
+ */
+
+void
+list_devices(void)
+{
+ static char *funky_hex = "0123456789abcdefghijklmnopqrstuvwxyz";
+ /* Funky hex numbering used for some devices */
+
+#ifdef __linux
+ int i; /* Looping var */
+ int fd; /* File descriptor */
+ char device[255]; /* Device filename */
+
+
+ for (i = 0; i < 4; i ++)
+ {
+ sprintf(device, "/dev/ttyS%d", i);
+ if ((fd = open(device, O_WRONLY | O_NOCTTY | O_NDELAY)) >= 0)
+ {
+ close(fd);
+ printf("serial serial:%s?baud=115200 \"Unknown\" \"Serial Port #%d\"\n",
+ device, i + 1);
+ }
+ }
+#elif defined(__sgi)
+ int i, j, n; /* Looping vars */
+ char device[255]; /* Device filename */
+ inventory_t *inv; /* Hardware inventory info */
+
+
+ /*
+ * IRIX maintains a hardware inventory of most devices...
+ */
+
+ setinvent();
+
+ while ((inv = getinvent()) != NULL)
+ {
+ if (inv->inv_class == INV_SERIAL)
+ {
+ /*
+ * Some sort of serial port...
+ */
+
+ if (inv->inv_type == INV_CDSIO || inv->inv_type == INV_CDSIO_E)
+ {
+ /*
+ * CDSIO port...
+ */
+
+ for (n = 0; n < 6; n ++)
+ printf("serial serial:/dev/ttyd%d?baud=19200 \"Unknown\" \"CDSIO Board %d Serial Port #%d\"\n",
+ n + 5 + 8 * inv->inv_controller, inv->inv_controller, n + 1);
+ }
+ else if (inv->inv_type == INV_EPC_SERIAL)
+ {
+ /*
+ * Everest serial port...
+ */
+
+ if (inv->inv_unit == 0)
+ i = 1;
+ else
+ i = 41 + 4 * (int)inv->inv_controller;
+
+ for (n = 0; n < (int)inv->inv_state; n ++)
+ printf("serial serial:/dev/ttyd%d?baud=19200 \"Unknown\" \"EPC Serial Port %d, Ebus slot %d\"\n",
+ n + i, n + 1, (int)inv->inv_controller);
+ }
+ else if (inv->inv_state > 1)
+ {
+ /*
+ * Standard serial port under IRIX 6.4 and earlier...
+ */
+
+ for (n = 0; n < (int)inv->inv_state; n ++)
+ printf("serial serial:/dev/ttyd%d?baud=19200 \"Unknown\" \"Onboard Serial Port %d\"\n",
+ n + (int)inv->inv_unit + 1, n + (int)inv->inv_unit + 1);
+ }
+ else
+ {
+ /*
+ * Standard serial port under IRIX 6.5 and beyond...
+ */
+
+ printf("serial serial:/dev/ttyd%d?baud=115200 \"Unknown\" \"Onboard Serial Port %d\"\n",
+ (int)inv->inv_controller, (int)inv->inv_controller);
+ }
+ }
+ }
+
+ endinvent();
+
+ /*
+ * Central Data makes serial and parallel "servers" that can be
+ * connected in a number of ways. Look for ports...
+ */
+
+ for (i = 0; i < 10; i ++)
+ for (j = 0; j < 8; j ++)
+ for (n = 0; n < 32; n ++)
+ {
+ if (i == 8) /* EtherLite */
+ sprintf(device, "/dev/ttydn%d%c", j, funky_hex[n]);
+ else if (i == 9) /* PCI */
+ sprintf(device, "/dev/ttydp%d%c", j, funky_hex[n]);
+ else /* SCSI */
+ sprintf(device, "/dev/ttyd%d%d%c", i, j, funky_hex[n]);
+
+ if (access(device, 0) == 0)
+ {
+ if (i == 8)
+ printf("serial serial:%s?baud=38400 \"Unknown\" \"Central Data EtherLite Serial Port, ID %d, port %d\"\n",
+ device, j, n);
+ else if (i == 9)
+ printf("serial serial:%s?baud=38400 \"Unknown\" \"Central Data PCI Serial Port, ID %d, port %d\"\n",
+ device, j, n);
+ else
+ printf("serial serial:%s?baud=38400 \"Unknown\" \"Central Data SCSI Serial Port, logical bus %d, ID %d, port %d\"\n",
+ device, i, j, n);
+ }
+ }
+#elif defined(__sun)
+ int i, j, n; /* Looping vars */
+ char device[255]; /* Device filename */
+
+
+ /*
+ * Standard serial ports...
+ */
+
+ for (i = 0; i < 26; i ++)
+ {
+ sprintf(device, "/dev/cua/%c", 'a' + i);
+ if (access(device, 0) == 0)
+ printf("serial serial:%s?baud=38400 \"Unknown\" \"Serial Port #%d\"\n",
+ device, i + 1);
+ }
+
+ /*
+ * MAGMA serial ports...
+ */
+
+ for (i = 0; i < 40; i ++)
+ {
+ sprintf(device, "/dev/term/%02d", i);
+ if (access(device, 0) == 0)
+ printf("serial serial:%s?baud=38400 \"Unknown\" \"MAGMA Serial Board #%d Port #%d\"\n",
+ device, (i / 10) + 1, (i % 10) + 1);
+ }
+
+ /*
+ * Central Data serial ports...
+ */
+
+ for (i = 0; i < 9; i ++)
+ for (j = 0; j < 8; j ++)
+ for (n = 0; n < 32; n ++)
+ {
+ if (i == 8) /* EtherLite */
+ sprintf(device, "/dev/sts/ttyN%d%c", j, funky_hex[n]);
+ else
+ sprintf(device, "/dev/sts/tty%c%d%c", i + 'C', j,
+ funky_hex[n]);
+
+ if (access(device, 0) == 0)
+ {
+ if (i == 8)
+ printf("serial serial:%s?baud=38400 \"Unknown\" \"Central Data EtherLite Serial Port, ID %d, port %d\"\n",
+ device, j, n);
+ else
+ printf("serial serial:%s?baud=38400 \"Unknown\" \"Central Data SCSI Serial Port, logical bus %d, ID %d, port %d\"\n",
+ device, i, j, n);
+ }
+ }
+#elif defined(__hpux)
+ int i, j, n; /* Looping vars */
+ char device[255]; /* Device filename */
+
+
+ /*
+ * Standard serial ports...
+ */
+
+ for (i = 0; i < 10; i ++)
+ {
+ sprintf(device, "/dev/tty%dp0", i);
+ if (access(device, 0) == 0)
+ printf("serial serial:%s?baud=38400 \"Unknown\" \"Serial Port #%d\"\n",
+ device, i + 1);
+ }
+
+ /*
+ * Central Data serial ports...
+ */
+
+ for (i = 0; i < 9; i ++)
+ for (j = 0; j < 8; j ++)
+ for (n = 0; n < 32; n ++)
+ {
+ if (i == 8) /* EtherLite */
+ sprintf(device, "/dev/ttyN%d%c", j, funky_hex[n]);
+ else
+ sprintf(device, "/dev/tty%c%d%c", i + 'C', j,
+ funky_hex[n]);
+
+ if (access(device, 0) == 0)
+ {
+ if (i == 8)
+ printf("serial serial:%s?baud=38400 \"Unknown\" \"Central Data EtherLite Serial Port, ID %d, port %d\"\n",
+ device, j, n);
+ else
+ printf("serial serial:%s?baud=38400 \"Unknown\" \"Central Data SCSI Serial Port, logical bus %d, ID %d, port %d\"\n",
+ device, i, j, n);
+ }
+ }
+#elif defined(__osf__)
+ int i; /* Looping var */
+ char device[255]; /* Device filename */
+
+
+ /*
+ * Standard serial ports...
+ */
+
+ for (i = 0; i < 100; i ++)
+ {
+ sprintf(device, "/dev/tty%02d", i);
+ if (access(device, 0) == 0)
+ printf("serial serial:%s?baud=38400 \"Unknown\" \"Serial Port #%d\"\n",
+ device, i + 1);
+ }
+#elif defined(FreeBSD) || defined(OpenBSD) || defined(NetBSD)
+ int i, j; /* Looping vars */
+ int fd; /* File descriptor */
+ char device[255]; /* Device filename */
+
+
+ /*
+ * SIO ports...
+ */
+
+ for (i = 0; i < 32; i ++)
+ {
+ sprintf(device, "/dev/ttyd%c", funky_hex[i]);
+ if ((fd = open(device, O_WRONLY | O_NOCTTY | O_NDELAY)) >= 0)
+ {
+ close(fd);
+ printf("serial serial:%s?baud=115200 \"Unknown\" \"Standard Serial Port #%d\"\n",
+ device, i + 1);
+ }
+ }
+
+ /*
+ * Cyclades ports...
+ */
+
+ for (i = 0; i < 16; i ++) /* Should be up to 65536 boards... */
+ for (j = 0; j < 32; j ++)
+ {
+ sprintf(device, "/dev/ttyc%d%c", i, funky_hex[j]);
+ if ((fd = open(device, O_WRONLY | O_NOCTTY | O_NDELAY)) >= 0)
+ {
+ close(fd);
+ printf("serial serial:%s?baud=115200 \"Unknown\" \"Cyclades #%d Serial Port #%d\"\n",
+ device, i, j + 1);
+ }
+ }
+
+ /*
+ * Digiboard ports...
+ */
+
+ for (i = 0; i < 16; i ++) /* Should be up to 65536 boards... */
+ for (j = 0; j < 32; j ++)
+ {
+ sprintf(device, "/dev/ttyD%d%c", i, funky_hex[j]);
+ if ((fd = open(device, O_WRONLY | O_NOCTTY | O_NDELAY)) >= 0)
+ {
+ close(fd);
+ printf("serial serial:%s?baud=115200 \"Unknown\" \"Digiboard #%d Serial Port #%d\"\n",
+ device, i, j + 1);
+ }
+ }
+
+ /*
+ * Stallion ports...
+ */
+
+ for (i = 0; i < 32; i ++)
+ {
+ sprintf(device, "/dev/ttyE%c", funky_hex[i]);
+ if ((fd = open(device, O_WRONLY | O_NOCTTY | O_NDELAY)) >= 0)
+ {
+ close(fd);
+ printf("serial serial:%s?baud=115200 \"Unknown\" \"Stallion Serial Port #%d\"\n",
+ device, i + 1);
+ }
+ }
+
+ /*
+ * SX ports...
+ */
+
+ for (i = 0; i < 128; i ++)
+ {
+ sprintf(device, "/dev/ttyA%d", i + 1);
+ if ((fd = open(device, O_WRONLY | O_NOCTTY | O_NDELAY)) >= 0)
+ {
+ close(fd);
+ printf("serial serial:%s?baud=115200 \"Unknown\" \"SX Serial Port #%d\"\n",
+ device, i + 1);
+ }
+ }
+#endif
+}
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/backend/socket.c b/backend/socket.c
new file mode 100644
index 000000000..a3b130384
--- /dev/null
+++ b/backend/socket.c
@@ -0,0 +1,259 @@
+/*
+ * "$Id$"
+ *
+ * AppSocket backend for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-2000 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 copies; /* Number of copies to print */
+ 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 == 1)
+ {
+ puts("network socket \"Unknown\" \"AppSocket/HP JetDirect\"");
+ return (0);
+ }
+ else 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;
+ copies = 1;
+ }
+ else
+ {
+ /*
+ * Try to open the print file...
+ */
+
+ if ((fp = fopen(argv[6], "rb")) == NULL)
+ {
+ perror("ERROR: unable to open print file");
+ return (1);
+ }
+
+ copies = atoi(argv[4]);
+ }
+
+ /*
+ * 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);
+
+ while (copies > 0)
+ {
+ for (;;)
+ {
+ if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
+ {
+ perror("ERROR: Unable to create socket");
+ 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");
+ sleep(30);
+ }
+ }
+ else
+ break;
+ }
+
+ /*
+ * Finally, send the print file...
+ */
+
+ copies --;
+
+ if (fp != stdin)
+ {
+ fputs("PAGE: 1 1\n", stderr);
+ rewind(fp);
+ }
+
+ 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...
+ */
+
+ close(fd);
+ }
+
+ /*
+ * Close the input file and return...
+ */
+
+ if (fp != stdin)
+ fclose(fp);
+
+ return (0);
+}
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/backend/usb.c b/backend/usb.c
new file mode 100644
index 000000000..f464ac19f
--- /dev/null
+++ b/backend/usb.c
@@ -0,0 +1,328 @@
+/*
+ * "$Id$"
+ *
+ * USB port backend for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-2000 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 USB port.
+ * list_devices() - List all USB devices.
+ */
+
+/*
+ * Include necessary headers.
+ */
+
+#include <cups/cups.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.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__ */
+
+
+/*
+ * Local functions...
+ */
+
+void list_devices(void);
+
+
+/*
+ * 'main()' - Send a file to the specified USB 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 copies; /* Number of copies to print */
+ int fd; /* Parallel device */
+ 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 == 1)
+ {
+ list_devices();
+ return (0);
+ }
+ else if (argc < 6 || argc > 7)
+ {
+ fputs("Usage: USB 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;
+ copies = 1;
+ }
+ else
+ {
+ /*
+ * Try to open the print file...
+ */
+
+ if ((fp = fopen(argv[6], "rb")) == NULL)
+ {
+ perror("ERROR: unable to open print file");
+ return (1);
+ }
+
+ copies = atoi(argv[4]);
+ }
+
+ /*
+ * 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 USB port device...
+ */
+
+ do
+ {
+ if ((fd = open(resource, O_WRONLY | O_EXCL)) == -1)
+ {
+ if (errno == EBUSY)
+ {
+ fputs("INFO: USB port busy; will retry in 30 seconds...\n", stderr);
+ sleep(30);
+ }
+ else
+ {
+ perror("ERROR: Unable to open USB port device file");
+ return (1);
+ }
+ }
+ }
+ while (fd < 0);
+
+ /*
+ * 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...
+ */
+
+ while (copies > 0)
+ {
+ copies --;
+
+ if (fp != stdin)
+ {
+ fputs("PAGE: 1 1\n", stderr);
+ rewind(fp);
+ }
+
+ 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);
+}
+
+
+/*
+ * 'list_devices()' - List all USB devices.
+ */
+
+void
+list_devices(void)
+{
+#ifdef __linux
+ int i; /* Looping var */
+ int fd; /* File descriptor */
+ char device[255]; /* Device filename */
+ FILE *probe; /* /proc/parport/n/autoprobe file */
+ char line[1024], /* Line from file */
+ *delim, /* Delimiter in file */
+ make[IPP_MAX_NAME], /* Make from file */
+ model[IPP_MAX_NAME]; /* Model from file */
+
+
+ if ((probe = fopen("/proc/bus/usb/devices", "r")) != NULL)
+ {
+ i = 0;
+
+ memset(make, 0, sizeof(make));
+ memset(model, 0, sizeof(model));
+
+ while (fgets(line, sizeof(line), probe) != NULL)
+ {
+ /*
+ * Strip trailing newline.
+ */
+
+ if ((delim = strrchr(line, '\n')) != NULL)
+ *delim = '\0';
+
+ /*
+ * See if it is a printer device ("P: ...")
+ */
+
+ if (strncmp(line, "S:", 2) == 0)
+ {
+ /*
+ * String attribute...
+ */
+
+ if (strncmp(line, "S: Manufacturer=", 17) == 0)
+ {
+ strncpy(make, line + 17, sizeof(make) - 1);
+ if (strcmp(make, "Hewlett-Packard") == 0)
+ strcpy(make, "HP");
+ }
+ else if (strncmp(line, "S: Product=", 12) == 0)
+ strncpy(model, line + 12, sizeof(model) - 1);
+ }
+ else if (strncmp(line, "I:", 2) == 0 &&
+ strstr(line, "Driver=printer") != NULL &&
+ make[0] && model[0])
+ {
+ /*
+ * We were processing a printer device; send the info out...
+ */
+
+ printf("direct usb:/dev/usb/usblp%d \"%s %s\" \"USB Printer #%d\"\n",
+ i, make, model, i + 1);
+
+ i ++;
+
+ memset(make, 0, sizeof(make));
+ memset(model, 0, sizeof(model));
+ }
+ }
+
+ fclose(probe);
+ }
+ else
+ {
+ for (i = 0; i < 8; i ++)
+ {
+ sprintf(device, "/dev/usb/usblp%d", i);
+ if ((fd = open(device, O_WRONLY)) >= 0)
+ {
+ close(fd);
+ printf("direct usb:%s \"Unknown\" \"USB Printer #%d\"\n", device, i + 1);
+ }
+ }
+ }
+#elif defined(__sgi)
+#elif defined(__sun)
+#elif defined(__hpux)
+#elif defined(__osf)
+#elif defined(FreeBSD) || defined(OpenBSD) || defined(NetBSD)
+ int i; /* Looping var */
+ int fd; /* File descriptor */
+ char device[255]; /* Device filename */
+
+ for (i = 0; i < 3; i ++)
+ {
+ sprintf(device, "/dev/ulpt%d", i);
+ if ((fd = open(device, O_WRONLY)) >= 0)
+ {
+ close(fd);
+ printf("direct usb:%s \"Unknown\" \"USB Port #%d\"\n", device, i + 1);
+ }
+ }
+#endif
+}
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/berkeley/Makefile b/berkeley/Makefile
new file mode 100644
index 000000000..a7ecbc399
--- /dev/null
+++ b/berkeley/Makefile
@@ -0,0 +1,103 @@
+#
+# "$Id$"
+#
+# Berkeley commands makefile for the Common UNIX Printing System (CUPS).
+#
+# Copyright 1997-2000 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) $(OBJS) $(TARGETS)
+
+
+#
+# Install all targets...
+#
+
+install:
+ -$(MKDIR) $(BINDIR)
+ $(INSTALL_BIN) lpq lpr lprm $(BINDIR)
+ -$(MKDIR) $(SBINDIR)
+ $(INSTALL_BIN) 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..c89b16dd9
--- /dev/null
+++ b/berkeley/lpc.c
@@ -0,0 +1,466 @@
+/*
+ * "$Id$"
+ *
+ * "lpc" command for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-2000 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 */
+{
+ 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);
+
+ snprintf(printer_uri, sizeof(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
+ {
+ /*
+ * Just show the method...
+ */
+
+ *strchr(device, ':') = '\0';
+ 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..0cce2aa09
--- /dev/null
+++ b/berkeley/lpq.c
@@ -0,0 +1,488 @@
+/*
+ * "$Id$"
+ *
+ * "lpq" command for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-2000 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);
+static void show_printer(http_t *, const 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 */
+ 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 (;;)
+ {
+ if (dest)
+ show_printer(http, dest);
+
+ i = show_jobs(http, dest, user, id, longstatus);
+
+ if (i && interval)
+ {
+ fflush(stdout);
+ 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 */
+#ifdef __osf__
+ jobpriority, /* job-priority */
+#endif /* __osf__ */
+ jobcount, /* Number of jobs */
+ jobcopies, /* Number of copies */
+ rank; /* Rank of job */
+ char resource[1024]; /* Resource string */
+ char rankstr[255]; /* Rank string */
+ char namestr[1024]; /* Job name 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
+ {
+ snprintf(resource, sizeof(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...
+ */
+
+ jobcount = 0;
+
+ if ((response = cupsDoRequest(http, request, "/jobs/")) != NULL)
+ {
+ if (response->request.status.status_code > IPP_OK_CONFLICT)
+ {
+ fprintf(stderr, "lpq: get-jobs failed: %s\n",
+ ippErrorString(response->request.status.status_code));
+ ippDelete(response);
+ return (0);
+ }
+
+ 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;
+#ifdef __osf__
+ jobpriority = 50;
+#endif /* __osf__ */
+ jobstate = IPP_JOB_PENDING;
+ jobname = "untitled";
+ jobuser = NULL;
+ jobdest = NULL;
+ jobcopies = 1;
+
+ 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;
+
+#ifdef __osf__
+ if (strcmp(attr->name, "job-priority") == 0 &&
+ attr->value_tag == IPP_TAG_INTEGER)
+ jobpriority = attr->values[0].integer;
+#endif /* __osf__ */
+
+ 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;
+
+ if (strcmp(attr->name, "copies") == 0 &&
+ attr->value_tag == IPP_TAG_INTEGER)
+ jobcopies = attr->values[0].integer;
+
+ attr = attr->next;
+ }
+
+ /*
+ * See if we have everything needed...
+ */
+
+ if (jobdest == NULL || jobid == 0)
+ {
+ if (attr == NULL)
+ break;
+ else
+ continue;
+ }
+
+ /**** TODO - support OSF/1 and Berkeley formats ****/
+ if (!longstatus && jobcount == 0)
+ puts("Rank Owner Job Files Total Size");
+
+ jobcount ++;
+
+ /*
+ * Display the job...
+ */
+
+ if (jobstate == IPP_JOB_PROCESSING)
+ strcpy(rankstr, "active");
+ else
+ {
+ sprintf(rankstr, "%d%s\t", rank, ranks[rank % 10]);
+ rank ++;
+ }
+
+ if (longstatus)
+ {
+ puts("");
+
+ if (jobcopies > 1)
+ sprintf(namestr, "%d copies of %s", jobcopies, jobname);
+ else
+ strcpy(namestr, jobname);
+
+ printf("%s: %-31s [job %d localhost]\n", jobuser, rankstr, jobid);
+ printf(" %-31.31s %d bytes\n", namestr, jobsize);
+ }
+ else
+ printf("%-6s %-10.10s %-15d %-27.27s %d bytes\n", rankstr, jobuser,
+ jobid, jobname, jobsize);
+
+ if (attr == NULL)
+ break;
+ }
+
+ ippDelete(response);
+ }
+ else
+ {
+ fprintf(stderr, "lpq: get-jobs failed: %s\n", ippErrorString(cupsLastError()));
+ return (0);
+ }
+
+ if (jobcount == 0)
+ puts("no entries");
+
+ return (jobcount);
+}
+
+
+/*
+ * 'show_printer()' - Show printer status.
+ */
+
+static void
+show_printer(http_t *http, /* I - HTTP connection to server */
+ const char *dest) /* I - Destination */
+{
+ ipp_t *request, /* IPP Request */
+ *response; /* IPP Response */
+ ipp_attribute_t *attr; /* Current attribute */
+ cups_lang_t *language; /* Default language */
+ ipp_pstate_t state; /* Printer state */
+ char uri[HTTP_MAX_URI];
+ /* Printer URI */
+
+
+ if (http == NULL)
+ return;
+
+ /*
+ * Build an 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;
+
+ 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://localhost/printers/%s", dest);
+ 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, "/")) != NULL)
+ {
+ if (response->request.status.status_code > IPP_OK_CONFLICT)
+ {
+ fprintf(stderr, "lpq: get-printer-attributes failed: %s\n",
+ ippErrorString(response->request.status.status_code));
+ ippDelete(response);
+ return;
+ }
+
+ if ((attr = ippFindAttribute(response, "printer-state", IPP_TAG_ENUM)) != NULL)
+ state = (ipp_pstate_t)attr->values[0].integer;
+ else
+ state = IPP_PRINTER_STOPPED;
+
+ switch (state)
+ {
+ case IPP_PRINTER_IDLE :
+ printf("%s is ready\n", dest);
+ break;
+ case IPP_PRINTER_PROCESSING :
+ printf("%s is ready and printing\n", dest);
+ break;
+ case IPP_PRINTER_STOPPED :
+ printf("%s is not ready\n", dest);
+ break;
+ }
+
+ ippDelete(response);
+ }
+ else
+ fprintf(stderr, "lpq: get-printer-attributes failed: %s\n",
+ ippErrorString(cupsLastError()));
+}
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/berkeley/lpr.c b/berkeley/lpr.c
new file mode 100644
index 000000000..554df26f5
--- /dev/null
+++ b/berkeley/lpr.c
@@ -0,0 +1,315 @@
+/*
+ * "$Id$"
+ *
+ * "lpr" command for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-2000 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.
+ * sighandler() - Signal catcher for when we print from stdin...
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <cups/cups.h>
+
+
+#ifndef WIN32
+# include <signal.h>
+
+
+/*
+ * Local functions.
+ */
+
+void sighandler(int);
+#endif /* !WIN32 */
+
+
+/*
+ * Globals...
+ */
+
+char tempfile[1024]; /* Temporary file for printing from stdin */
+
+
+/*
+ * '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 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 deletefile; /* Delete file after print? */
+ char buffer[8192]; /* Copy buffer */
+ FILE *temp; /* Temporary file pointer */
+#ifdef HAVE_SIGACTION
+ struct sigaction action; /* Signal action */
+#endif /* HAVE_SIGACTION */
+
+
+ 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\' - %s.\n",
+ argv[i], ippErrorString(cupsLastError()));
+ 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);
+ }
+
+#ifndef WIN32
+# if defined(HAVE_SIGSET)
+ sigset(SIGHUP, sighandler);
+ sigset(SIGINT, sighandler);
+ sigset(SIGTERM, sighandler);
+# elif defined(HAVE_SIGACTION)
+ memset(&action, 0, sizeof(action));
+ action.sa_handler = sighandler;
+
+ sigaction(SIGHUP, &action, NULL);
+ sigaction(SIGINT, &action, NULL);
+ sigaction(SIGTERM, &action, NULL);
+# else
+ signal(SIGHUP, sighandler);
+ signal(SIGINT, sighandler);
+ signal(SIGTERM, sighandler);
+# endif
+#endif /* !WIN32 */
+
+ 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)
+ {
+ fprintf(stderr, "lpr: unable to print standard input - %s.\n",
+ ippErrorString(cupsLastError()));
+ return (1);
+ }
+ }
+
+ return (0);
+}
+
+
+#ifndef WIN32
+/*
+ * 'sighandler()' - Signal catcher for when we print from stdin...
+ */
+
+void
+sighandler(int s) /* I - Signal number */
+{
+ /*
+ * Remove the temporary file we're using to print from stdin...
+ */
+
+ unlink(tempfile);
+
+ /*
+ * Exit...
+ */
+
+ exit(s);
+}
+#endif /* !WIN32 */
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/berkeley/lprm.c b/berkeley/lprm.c
new file mode 100644
index 000000000..4b931a683
--- /dev/null
+++ b/berkeley/lprm.c
@@ -0,0 +1,221 @@
+/*
+ * "$Id$"
+ *
+ * "lprm" command for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-2000 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;
+ http = 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)
+ {
+ snprintf(uri, sizeof(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);
+ }
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
+ "requesting-user-name", NULL, cupsUser());
+
+ /*
+ * 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)
+ {
+ switch (response->request.status.status_code)
+ {
+ case IPP_NOT_FOUND :
+ fputs("lprm: Job or printer not found!\n", stderr);
+ break;
+ case IPP_NOT_AUTHORIZED :
+ fputs("lprm: Not authorized to lprm job(s)!\n", stderr);
+ break;
+ case IPP_FORBIDDEN :
+ fprintf(stderr, "lprm: You don't own job ID %d!\n", job_id);
+ break;
+ default :
+ if (response->request.status.status_code > IPP_OK_CONFLICT)
+ fputs("lprm: Unable to lprm job(s)!\n", stderr);
+ break;
+ }
+
+ 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..73419a810
--- /dev/null
+++ b/cgi-bin/Makefile
@@ -0,0 +1,117 @@
+#
+# "$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 = admin.cgi classes.cgi jobs.cgi printers.cgi
+LIBOBJS = abort.o email.o html.o ipp-var.o template.o var.o
+OBJS = $(LIBOBJS) admin.o classes.o jobs.o printers.o
+
+
+#
+# Make all targets...
+#
+
+all: $(TARGETS)
+
+
+#
+# Clean all object files...
+#
+
+clean:
+ $(RM) $(OBJS) libcgi.a $(TARGETS)
+
+
+#
+# Install all targets...
+#
+
+install:
+ -$(MKDIR) $(SERVERBIN)/cgi-bin
+ $(INSTALL_BIN) $(TARGETS) $(SERVERBIN)/cgi-bin
+
+
+#
+# libcgi.a
+#
+
+libcgi.a: $(LIBOBJS)
+ echo Archiving $@...
+ $(RM) $@
+ $(AR) $(ARFLAGS) $@ $(LIBOBJS)
+ $(RANLIB) $@
+
+$(LIBOBJS): cgi.h
+ipp-var.o: ipp-var.h
+
+
+#
+# admin.cgi
+#
+
+admin.cgi: admin.o ../Makedefs ../cups/$(LIBCUPS) libcgi.a
+ echo Linking $@...
+ $(CC) $(LDFLAGS) -o $@ admin.o libcgi.a $(LIBS)
+
+admin.o: cgi.h ipp-var.h ../cups/cups.h ../cups/ipp.h ../cups/language.h
+
+
+#
+# classes.cgi
+#
+
+classes.cgi: classes.o ../Makedefs ../cups/$(LIBCUPS) libcgi.a
+ echo Linking $@...
+ $(CC) $(LDFLAGS) -o $@ classes.o libcgi.a $(LIBS)
+
+classes.o: cgi.h ipp-var.h ../cups/cups.h ../cups/ipp.h ../cups/language.h
+
+
+#
+# jobs.cgi
+#
+
+jobs.cgi: jobs.o ../Makedefs ../cups/$(LIBCUPS) libcgi.a
+ echo Linking $@...
+ $(CC) $(LDFLAGS) -o $@ jobs.o libcgi.a $(LIBS)
+
+jobs.o: cgi.h ipp-var.h ../cups/cups.h ../cups/ipp.h ../cups/language.h
+
+
+#
+# printers.cgi
+#
+
+printers.cgi: printers.o ../Makedefs ../cups/$(LIBCUPS) libcgi.a
+ echo Linking $@...
+ $(CC) $(LDFLAGS) -o $@ printers.o libcgi.a $(LIBS)
+
+printers.o: cgi.h ipp-var.h ../cups/cups.h ../cups/ipp.h ../cups/language.h
+
+$(OBJS): ../Makedefs
+
+#
+# End of "$Id$".
+#
diff --git a/cgi-bin/admin.c b/cgi-bin/admin.c
new file mode 100644
index 000000000..fc7347132
--- /dev/null
+++ b/cgi-bin/admin.c
@@ -0,0 +1,1357 @@
+/*
+ * "$Id$"
+ *
+ * Administration CGI for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-2000 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 "ipp-var.h"
+#include <ctype.h>
+
+
+/*
+ * Local functions...
+ */
+
+static void do_am_class(http_t *http, cups_lang_t *language, int modify);
+static void do_am_printer(http_t *http, cups_lang_t *language, int modify);
+static void do_config_printer(http_t *http, cups_lang_t *language);
+static void do_delete_class(http_t *http, cups_lang_t *language);
+static void do_delete_printer(http_t *http, cups_lang_t *language);
+static void do_job_op(http_t *http, cups_lang_t *language, ipp_op_t op);
+static void do_printer_op(http_t *http, cups_lang_t *language, ipp_op_t op);
+static char *get_line(char *buf, int length, FILE *fp);
+
+
+/*
+ * '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 */
+ http_t *http; /* Connection to the server */
+ const char *op; /* Operation name */
+
+
+ /*
+ * Get the request language...
+ */
+
+ language = cupsLangDefault();
+
+ /*
+ * Send a standard header...
+ */
+
+ printf("Content-Type: text/html;charset=%s\n\n", cupsLangEncoding(language));
+
+ cgiSetVariable("TITLE", "Admin");
+ ippSetServerVersion();
+
+ cgiCopyTemplateLang(stdout, TEMPLATES, "header.tmpl", getenv("LANG"));
+
+ /*
+ * See if we have form data...
+ */
+
+ if (!cgiInitialize())
+ {
+ /*
+ * Nope, send the administration menu...
+ */
+
+ cgiCopyTemplateLang(stdout, TEMPLATES, "admin.tmpl", getenv("LANG"));
+ }
+ else if ((op = cgiGetVariable("OP")) != NULL)
+ {
+ /*
+ * Connect to the HTTP server...
+ */
+
+ http = httpConnect("localhost", ippPort());
+
+ /*
+ * Do the operation...
+ */
+
+ if (strcmp(op, "cancel-job") == 0)
+ do_job_op(http, language, IPP_CANCEL_JOB);
+ else if (strcmp(op, "hold-job") == 0)
+ do_job_op(http, language, IPP_HOLD_JOB);
+ else if (strcmp(op, "release-job") == 0)
+ do_job_op(http, language, IPP_RELEASE_JOB);
+ else if (strcmp(op, "restart-job") == 0)
+ do_job_op(http, language, IPP_RESTART_JOB);
+ else if (strcmp(op, "start-printer") == 0)
+ do_printer_op(http, language, IPP_RESUME_PRINTER);
+ else if (strcmp(op, "stop-printer") == 0)
+ do_printer_op(http, language, IPP_PAUSE_PRINTER);
+ else if (strcmp(op, "accept-jobs") == 0)
+ do_printer_op(http, language, CUPS_ACCEPT_JOBS);
+ else if (strcmp(op, "reject-jobs") == 0)
+ do_printer_op(http, language, CUPS_REJECT_JOBS);
+ else if (strcmp(op, "add-class") == 0)
+ do_am_class(http, language, 0);
+ else if (strcmp(op, "add-printer") == 0)
+ do_am_printer(http, language, 0);
+ else if (strcmp(op, "modify-class") == 0)
+ do_am_class(http, language, 1);
+ else if (strcmp(op, "modify-printer") == 0)
+ do_am_printer(http, language, 1);
+ else if (strcmp(op, "delete-class") == 0)
+ do_delete_class(http, language);
+ else if (strcmp(op, "delete-printer") == 0)
+ do_delete_printer(http, language);
+ else if (strcmp(op, "config-printer") == 0)
+ do_config_printer(http, language);
+ else
+ {
+ /*
+ * Bad operation code... Display an error...
+ */
+
+ cgiCopyTemplateLang(stdout, TEMPLATES, "admin-op.tmpl", getenv("LANG"));
+ }
+
+ /*
+ * Close the HTTP server connection...
+ */
+
+ httpClose(http);
+ }
+ else
+ {
+ /*
+ * Form data but no operation code... Display an error...
+ */
+
+ cgiCopyTemplateLang(stdout, TEMPLATES, "admin-op.tmpl", getenv("LANG"));
+ }
+
+ /*
+ * Send the standard trailer...
+ */
+
+ cgiCopyTemplateLang(stdout, TEMPLATES, "trailer.tmpl", getenv("LANG"));
+
+ /*
+ * Free the request language...
+ */
+
+ cupsLangFree(language);
+
+ /*
+ * Return with no errors...
+ */
+
+ return (0);
+}
+
+
+/*
+ * 'do_am_class()' - Add or modify a class.
+ */
+
+static void
+do_am_class(http_t *http, /* I - HTTP connection */
+ cups_lang_t *language, /* I - Client's language */
+ int modify) /* I - Modify the printer? */
+{
+ int i, j; /* Looping vars */
+ int element; /* Element number */
+ int num_printers; /* Number of printers */
+ ipp_t *request, /* IPP request */
+ *response; /* IPP response */
+ ipp_attribute_t *attr; /* member-uris attribute */
+ ipp_status_t status; /* Request status */
+ char uri[HTTP_MAX_URI]; /* Device or printer URI */
+
+
+ if (cgiGetVariable("PRINTER_LOCATION") == NULL)
+ {
+ if (modify)
+ {
+ /*
+ * Build an 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);
+
+ snprintf(uri, sizeof(uri), "ipp://localhost/classes/%s",
+ cgiGetVariable("PRINTER_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, "/")) != NULL)
+ {
+ ippSetCGIVars(response, NULL, NULL);
+ ippDelete(response);
+ }
+
+ /*
+ * Update the location and description of an existing printer...
+ */
+
+ cgiCopyTemplateLang(stdout, TEMPLATES, "modify-class.tmpl", getenv("LANG"));
+ }
+ else
+ {
+ /*
+ * Get the name, location, and description for a new printer...
+ */
+
+ cgiCopyTemplateLang(stdout, TEMPLATES, "add-class.tmpl", getenv("LANG"));
+ }
+ }
+ else if (cgiGetVariable("MEMBER_URIS") == NULL)
+ {
+ /*
+ * Build a CUPS_GET_PRINTERS request, which requires the
+ * following attributes:
+ *
+ * attributes-charset
+ * attributes-natural-language
+ * printer-uri
+ */
+
+ 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);
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
+ NULL, "ipp://localhost/printers");
+
+ /*
+ * Do the request and get back a response...
+ */
+
+ if ((response = cupsDoRequest(http, request, "/")) != NULL)
+ {
+ /*
+ * Create MEMBER_URIS and MEMBER_NAMES arrays...
+ */
+
+ for (element = 0, attr = response->attrs;
+ attr != NULL;
+ attr = attr->next)
+ if (attr->name && strcmp(attr->name, "printer-uri-supported") == 0)
+ {
+ cgiSetArray("MEMBER_URIS", element, attr->values[0].string.text);
+ element ++;
+ }
+
+ for (element = 0, attr = response->attrs;
+ attr != NULL;
+ attr = attr->next)
+ if (attr->name && strcmp(attr->name, "printer-name") == 0)
+ {
+ cgiSetArray("MEMBER_NAMES", element, attr->values[0].string.text);
+ element ++;
+ }
+
+ num_printers = cgiGetSize("MEMBER_URIS");
+
+ ippDelete(response);
+ }
+ else
+ num_printers = 0;
+
+ /*
+ * Build an 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);
+
+ snprintf(uri, sizeof(uri), "ipp://localhost/classes/%s",
+ cgiGetVariable("PRINTER_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, "/")) != NULL)
+ {
+ if ((attr = ippFindAttribute(response, "member-uris", IPP_TAG_URI)) != NULL)
+ {
+ /*
+ * Mark any current members in the class...
+ */
+
+ for (j = 0; j < num_printers; j ++)
+ cgiSetArray("MEMBER_SELECTED", j, "");
+
+ for (i = 0; i < attr->num_values; i ++)
+ for (j = 0; j < num_printers; j ++)
+ if (strcmp(attr->values[i].string.text, cgiGetArray("MEMBER_URIS", j)) == 0)
+ {
+ cgiSetArray("MEMBER_SELECTED", j, "SELECTED");
+ break;
+ }
+ }
+
+ ippDelete(response);
+ }
+
+ /*
+ * Let the user choose...
+ */
+
+ cgiCopyTemplateLang(stdout, TEMPLATES, "choose-members.tmpl", getenv("LANG"));
+ }
+ else
+ {
+ /*
+ * Build a CUPS_ADD_CLASS request, which requires the following
+ * attributes:
+ *
+ * attributes-charset
+ * attributes-natural-language
+ * printer-uri
+ * printer-location
+ * printer-info
+ * printer-is-accepting-jobs
+ * printer-state
+ * member-uris
+ */
+
+ request = ippNew();
+
+ request->request.op.operation_id = CUPS_ADD_CLASS;
+ 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);
+
+ snprintf(uri, sizeof(uri), "ipp://localhost/classes/%s",
+ cgiGetVariable("PRINTER_NAME"));
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
+ NULL, uri);
+
+ ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-location",
+ NULL, cgiGetVariable("PRINTER_LOCATION"));
+
+ ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-info",
+ NULL, cgiGetVariable("PRINTER_INFO"));
+
+ ippAddBoolean(request, IPP_TAG_PRINTER, "printer-is-accepting-jobs", 1);
+
+ ippAddInteger(request, IPP_TAG_PRINTER, IPP_TAG_ENUM, "printer-state",
+ IPP_PRINTER_IDLE);
+
+ if ((num_printers = cgiGetSize("MEMBER_URIS")) > 0)
+ {
+ attr = ippAddStrings(request, IPP_TAG_PRINTER, IPP_TAG_URI, "member-uris",
+ num_printers, NULL, NULL);
+ for (i = 0; i < num_printers; i ++)
+ attr->values[i].string.text = strdup(cgiGetArray("MEMBER_URIS", i));
+ }
+
+ /*
+ * Do the request and get back a response...
+ */
+
+ if ((response = cupsDoRequest(http, request, "/admin/")) != NULL)
+ {
+ status = response->request.status.status_code;
+ ippDelete(response);
+ }
+ else
+ status = IPP_NOT_AUTHORIZED;
+
+ if (status > IPP_OK_CONFLICT)
+ {
+ cgiSetVariable("ERROR", ippErrorString(status));
+ cgiCopyTemplateLang(stdout, TEMPLATES, "error.tmpl", getenv("LANG"));
+ }
+ else if (modify)
+ cgiCopyTemplateLang(stdout, TEMPLATES, "class-modified.tmpl", getenv("LANG"));
+ else
+ cgiCopyTemplateLang(stdout, TEMPLATES, "class-added.tmpl", getenv("LANG"));
+ }
+}
+
+
+/*
+ * 'do_am_printer()' - Add or modify a printer.
+ */
+
+static void
+do_am_printer(http_t *http, /* I - HTTP connection */
+ cups_lang_t *language, /* I - Client's language */
+ int modify) /* I - Modify the printer? */
+{
+ int i; /* Looping var */
+ int element; /* Element number */
+ ipp_attribute_t *attr, /* Current attribute */
+ *last; /* Last attribute */
+ ipp_t *request, /* IPP request */
+ *response; /* IPP response */
+ ipp_status_t status; /* Request status */
+ const char *var; /* CGI variable */
+ char uri[HTTP_MAX_URI], /* Device or printer URI */
+ *uriptr; /* Pointer into URI */
+ int maxrate; /* Maximum baud rate */
+ char baudrate[255]; /* Baud rate string */
+ static int baudrates[] = /* Baud rates */
+ {
+ 1200,
+ 2400,
+ 4800,
+ 9600,
+ 19200,
+ 38400,
+ 57600,
+ 115200,
+ 230400,
+ 460800
+ };
+
+
+ if (cgiGetVariable("PRINTER_LOCATION") == NULL)
+ {
+ if (modify)
+ {
+ /*
+ * Build an 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);
+
+ snprintf(uri, sizeof(uri), "ipp://localhost/printers/%s",
+ cgiGetVariable("PRINTER_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, "/")) != NULL)
+ {
+ ippSetCGIVars(response, NULL, NULL);
+ ippDelete(response);
+ }
+
+ /*
+ * Update the location and description of an existing printer...
+ */
+
+ cgiCopyTemplateLang(stdout, TEMPLATES, "modify-printer.tmpl", getenv("LANG"));
+ }
+ else
+ {
+ /*
+ * Get the name, location, and description for a new printer...
+ */
+
+ cgiCopyTemplateLang(stdout, TEMPLATES, "add-printer.tmpl", getenv("LANG"));
+ }
+ }
+ else if ((var = cgiGetVariable("DEVICE_URI")) == NULL)
+ {
+ /*
+ * Build a CUPS_GET_DEVICES request, which requires the following
+ * attributes:
+ *
+ * attributes-charset
+ * attributes-natural-language
+ * printer-uri
+ */
+
+ request = ippNew();
+
+ request->request.op.operation_id = CUPS_GET_DEVICES;
+ 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, "printer-uri",
+ NULL, "ipp://localhost/printers/");
+
+ /*
+ * Do the request and get back a response...
+ */
+
+ if ((response = cupsDoRequest(http, request, "/")) != NULL)
+ {
+ ippSetCGIVars(response, NULL, NULL);
+ ippDelete(response);
+ }
+
+ /*
+ * Let the user choose...
+ */
+
+ cgiCopyTemplateLang(stdout, TEMPLATES, "choose-device.tmpl", getenv("LANG"));
+ }
+ else if (strchr(var, '/') == NULL)
+ {
+ /*
+ * User needs to set the full URI...
+ */
+
+ cgiCopyTemplateLang(stdout, TEMPLATES, "choose-uri.tmpl", getenv("LANG"));
+ }
+ else if (strncmp(var, "serial:", 7) == 0 && cgiGetVariable("BAUDRATE") == NULL)
+ {
+ /*
+ * Need baud rate, parity, etc.
+ */
+
+ if ((var = strchr(var, '?')) != NULL &&
+ strncmp(var, "?baud=", 6) == 0)
+ maxrate = atoi(var + 6);
+ else
+ maxrate = 19200;
+
+ for (i = 0; i < 10; i ++)
+ if (baudrates[i] > maxrate)
+ break;
+ else
+ {
+ sprintf(baudrate, "%d", baudrates[i]);
+ cgiSetArray("BAUDRATES", i, baudrate);
+ }
+
+ cgiCopyTemplateLang(stdout, TEMPLATES, "choose-serial.tmpl", getenv("LANG"));
+ }
+ else if ((var = cgiGetVariable("PPD_NAME")) == NULL)
+ {
+ /*
+ * Build a CUPS_GET_PPDS request, which requires the following
+ * attributes:
+ *
+ * attributes-charset
+ * attributes-natural-language
+ * printer-uri
+ */
+
+ request = ippNew();
+
+ request->request.op.operation_id = CUPS_GET_PPDS;
+ 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, "printer-uri",
+ NULL, "ipp://localhost/printers/");
+
+ /*
+ * Do the request and get back a response...
+ */
+
+ if ((response = cupsDoRequest(http, request, "/")) != NULL)
+ {
+ if ((var = cgiGetVariable("PPD_MAKE")) == NULL)
+ {
+ /*
+ * Let the user choose a make...
+ */
+
+ for (element = 0, attr = response->attrs, last = NULL;
+ attr != NULL;
+ attr = attr->next)
+ if (attr->name && strcmp(attr->name, "ppd-make") == 0)
+ if (last == NULL ||
+ strcasecmp(last->values[0].string.text,
+ attr->values[0].string.text) != 0)
+ {
+ cgiSetArray("PPD_MAKE", element, attr->values[0].string.text);
+ element ++;
+ last = attr;
+ }
+
+ cgiCopyTemplateLang(stdout, TEMPLATES, "choose-make.tmpl",
+ getenv("LANG"));
+ }
+ else
+ {
+ /*
+ * Let the user choose a model...
+ */
+
+ ippSetCGIVars(response, "ppd-make", var);
+ cgiCopyTemplateLang(stdout, TEMPLATES, "choose-model.tmpl",
+ getenv("LANG"));
+ }
+
+ ippDelete(response);
+ }
+
+ }
+ else
+ {
+ /*
+ * Build a CUPS_ADD_PRINTER request, which requires the following
+ * attributes:
+ *
+ * attributes-charset
+ * attributes-natural-language
+ * printer-uri
+ * printer-location
+ * printer-info
+ * ppd-name
+ * device-uri
+ * printer-is-accepting-jobs
+ * printer-state
+ */
+
+ request = ippNew();
+
+ request->request.op.operation_id = CUPS_ADD_PRINTER;
+ 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);
+
+ snprintf(uri, sizeof(uri), "ipp://localhost/printers/%s",
+ cgiGetVariable("PRINTER_NAME"));
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
+ NULL, uri);
+
+ ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-location",
+ NULL, cgiGetVariable("PRINTER_LOCATION"));
+
+ ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-info",
+ NULL, cgiGetVariable("PRINTER_INFO"));
+
+ ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_NAME, "ppd-name",
+ NULL, cgiGetVariable("PPD_NAME"));
+
+ strcpy(uri, cgiGetVariable("DEVICE_URI"));
+ if (strncmp(uri, "serial:", 7) == 0)
+ {
+ /*
+ * Update serial port URI to include baud rate, etc.
+ */
+
+ if ((uriptr = strchr(uri, '?')) == NULL)
+ uriptr = uri + strlen(uri);
+
+ sprintf(uriptr, "?baud=%s+bits=%s+parity=%s+flow=%s",
+ cgiGetVariable("BAUDRATE"), cgiGetVariable("BITS"),
+ cgiGetVariable("PARITY"), cgiGetVariable("FLOW"));
+ }
+
+ ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_URI, "device-uri",
+ NULL, uri);
+
+ ippAddBoolean(request, IPP_TAG_PRINTER, "printer-is-accepting-jobs", 1);
+
+ ippAddInteger(request, IPP_TAG_PRINTER, IPP_TAG_ENUM, "printer-state",
+ IPP_PRINTER_IDLE);
+
+ /*
+ * Do the request and get back a response...
+ */
+
+ if ((response = cupsDoRequest(http, request, "/admin/")) != NULL)
+ {
+ status = response->request.status.status_code;
+ ippDelete(response);
+ }
+ else
+ status = IPP_NOT_AUTHORIZED;
+
+ if (status > IPP_OK_CONFLICT)
+ {
+ cgiSetVariable("ERROR", ippErrorString(status));
+ cgiCopyTemplateLang(stdout, TEMPLATES, "error.tmpl", getenv("LANG"));
+ }
+ else if (modify)
+ cgiCopyTemplateLang(stdout, TEMPLATES, "printer-modified.tmpl", getenv("LANG"));
+ else
+ cgiCopyTemplateLang(stdout, TEMPLATES, "printer-added.tmpl", getenv("LANG"));
+ }
+}
+
+
+/*
+ * 'do_config_printer()' - Configure the default options for a printer.
+ */
+
+static void
+do_config_printer(http_t *http, /* I - HTTP connection */
+ cups_lang_t *language)/* I - Client's language */
+{
+ int i, j, k; /* Looping vars */
+ int have_options; /* Have options? */
+ ipp_t *request, /* IPP request */
+ *response; /* IPP response */
+ char uri[HTTP_MAX_URI]; /* Job URI */
+ const char *var; /* Variable value */
+ const char *printer; /* Printer printer name */
+ ipp_status_t status; /* Operation status... */
+ const char *filename; /* PPD filename */
+ char tempfile[1024]; /* Temporary filename */
+ FILE *in, /* Input file */
+ *out; /* Output file */
+ char line[1024]; /* Line from PPD file */
+ char keyword[1024], /* Keyword from Default line */
+ *keyptr; /* Pointer into keyword... */
+ ppd_file_t *ppd; /* PPD file */
+ ppd_group_t *group; /* Option group */
+ ppd_option_t *option; /* Option */
+
+
+ /*
+ * Get the printer name...
+ */
+
+ if ((printer = cgiGetVariable("PRINTER_NAME")) != NULL)
+ snprintf(uri, sizeof(uri), "ipp://localhost/printers/%s", printer);
+ else
+ {
+ cgiSetVariable("ERROR", ippErrorString(IPP_NOT_FOUND));
+ cgiCopyTemplateLang(stdout, TEMPLATES, "error.tmpl", getenv("LANG"));
+ return;
+ }
+
+ /*
+ * Get the PPD file...
+ */
+
+ if ((filename = cupsGetPPD(printer)) == NULL)
+ {
+ cgiSetVariable("ERROR", ippErrorString(IPP_NOT_FOUND));
+ cgiCopyTemplateLang(stdout, TEMPLATES, "error.tmpl", getenv("LANG"));
+ return;
+ }
+
+ ppd = ppdOpenFile(filename);
+
+ for (have_options = 0, i = ppd->num_groups, group = ppd->groups;
+ i > 0;
+ i --, group ++)
+ for (j = group->num_options, option = group->options;
+ j > 0;
+ j --, option ++)
+ if ((var = cgiGetVariable(option->keyword)) != NULL)
+ {
+ have_options = 1;
+ break;
+ }
+
+ if (!have_options)
+ {
+ /*
+ * Show the options to the user...
+ */
+
+ cgiCopyTemplateLang(stdout, TEMPLATES, "config-printer.tmpl",
+ getenv("LANG"));
+
+ for (i = ppd->num_groups, group = ppd->groups;
+ i > 0;
+ i --, group ++)
+ {
+ cgiSetVariable("GROUP", group->text);
+ cgiCopyTemplateLang(stdout, TEMPLATES, "option-header.tmpl",
+ getenv("LANG"));
+
+ for (j = group->num_options, option = group->options;
+ j > 0;
+ j --, option ++)
+ {
+ if (strcmp(option->keyword, "PageRegion") == 0)
+ continue;
+
+ cgiSetVariable("KEYWORD", option->keyword);
+ cgiSetVariable("KEYTEXT", option->text);
+ cgiSetVariable("DEFCHOICE", option->defchoice);
+
+ cgiSetSize("CHOICES", option->num_choices);
+ cgiSetSize("TEXT", option->num_choices);
+ for (k = 0; k < option->num_choices; k ++)
+ {
+ cgiSetArray("CHOICES", k, option->choices[k].choice);
+ cgiSetArray("TEXT", k, option->choices[k].text);
+ }
+
+ switch (option->ui)
+ {
+ case PPD_UI_BOOLEAN :
+ cgiCopyTemplateLang(stdout, TEMPLATES, "option-boolean.tmpl",
+ getenv("LANG"));
+ break;
+ case PPD_UI_PICKONE :
+ cgiCopyTemplateLang(stdout, TEMPLATES, "option-pickone.tmpl",
+ getenv("LANG"));
+ break;
+ case PPD_UI_PICKMANY :
+ cgiCopyTemplateLang(stdout, TEMPLATES, "option-pickmany.tmpl",
+ getenv("LANG"));
+ break;
+ }
+ }
+
+ cgiCopyTemplateLang(stdout, TEMPLATES, "option-trailer.tmpl",
+ getenv("LANG"));
+ }
+
+ cgiCopyTemplateLang(stdout, TEMPLATES, "config-printer2.tmpl",
+ getenv("LANG"));
+ }
+ else
+ {
+ /*
+ * Set default options...
+ */
+
+ cupsTempFile(tempfile, sizeof(tempfile));
+
+ in = fopen(filename, "rb");
+ out = fopen(tempfile, "wb");
+
+ while (get_line(line, sizeof(line), in) != NULL)
+ {
+ if (strncmp(line, "*Default", 8) != 0)
+ fprintf(out, "%s\n", line);
+ else
+ {
+ /*
+ * Get default option name...
+ */
+
+ strcpy(keyword, line + 8);
+
+ for (keyptr = keyword; *keyptr; keyptr ++)
+ if (*keyptr == ':' || isspace(*keyptr))
+ break;
+
+ *keyptr = '\0';
+
+ if (strcmp(keyword, "PageRegion") == 0)
+ var = cgiGetVariable("PageSize");
+ else
+ var = cgiGetVariable(keyword);
+
+ if (var != NULL)
+ fprintf(out, "*Default%s: %s\n", keyword, var);
+ else
+ fprintf(out, "%s\n", line);
+ }
+ }
+
+ fclose(in);
+ fclose(out);
+
+ /*
+ * Build a CUPS_ADD_PRINTER request, which requires the following
+ * attributes:
+ *
+ * attributes-charset
+ * attributes-natural-language
+ * printer-uri
+ * [ppd file]
+ */
+
+ request = ippNew();
+
+ request->request.op.operation_id = CUPS_ADD_PRINTER;
+ 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);
+
+ snprintf(uri, sizeof(uri), "ipp://localhost/printers/%s",
+ cgiGetVariable("PRINTER_NAME"));
+ 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/", tempfile)) != NULL)
+ {
+ status = response->request.status.status_code;
+ ippDelete(response);
+ }
+ else
+ status = IPP_NOT_AUTHORIZED;
+
+ if (status > IPP_OK_CONFLICT)
+ {
+ cgiSetVariable("ERROR", ippErrorString(status));
+ cgiCopyTemplateLang(stdout, TEMPLATES, "error.tmpl", getenv("LANG"));
+ }
+ else
+ cgiCopyTemplateLang(stdout, TEMPLATES, "printer-configured.tmpl", getenv("LANG"));
+
+ unlink(tempfile);
+ }
+
+ unlink(filename);
+}
+
+
+/*
+ * 'do_delete_class()' - Delete a class...
+ */
+
+static void
+do_delete_class(http_t *http, /* I - HTTP connection */
+ cups_lang_t *language) /* I - Client's language */
+{
+ ipp_t *request, /* IPP request */
+ *response; /* IPP response */
+ char uri[HTTP_MAX_URI]; /* Job URI */
+ const char *pclass; /* Printer class name */
+ ipp_status_t status; /* Operation status... */
+
+
+ if (cgiGetVariable("CONFIRM") == NULL)
+ {
+ cgiCopyTemplateLang(stdout, TEMPLATES, "class-confirm.tmpl", getenv("LANG"));
+ return;
+ }
+
+ if ((pclass = cgiGetVariable("PRINTER_NAME")) != NULL)
+ snprintf(uri, sizeof(uri), "ipp://localhost/classes/%s", pclass);
+ else
+ {
+ cgiSetVariable("ERROR", ippErrorString(IPP_NOT_FOUND));
+ cgiCopyTemplateLang(stdout, TEMPLATES, "error.tmpl", getenv("LANG"));
+ return;
+ }
+
+ /*
+ * 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;
+
+ 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)
+ {
+ status = response->request.status.status_code;
+
+ ippDelete(response);
+ }
+ else
+ status = IPP_GONE;
+
+ if (status > IPP_OK_CONFLICT)
+ {
+ cgiSetVariable("ERROR", ippErrorString(status));
+ cgiCopyTemplateLang(stdout, TEMPLATES, "error.tmpl", getenv("LANG"));
+ }
+ else
+ cgiCopyTemplateLang(stdout, TEMPLATES, "class-deleted.tmpl", getenv("LANG"));
+}
+
+
+/*
+ * 'do_delete_printer()' - Delete a printer...
+ */
+
+static void
+do_delete_printer(http_t *http, /* I - HTTP connection */
+ cups_lang_t *language)/* I - Client's language */
+{
+ ipp_t *request, /* IPP request */
+ *response; /* IPP response */
+ char uri[HTTP_MAX_URI]; /* Job URI */
+ const char *printer; /* Printer printer name */
+ ipp_status_t status; /* Operation status... */
+
+
+ if (cgiGetVariable("CONFIRM") == NULL)
+ {
+ cgiCopyTemplateLang(stdout, TEMPLATES, "printer-confirm.tmpl", getenv("LANG"));
+ return;
+ }
+
+ if ((printer = cgiGetVariable("PRINTER_NAME")) != NULL)
+ snprintf(uri, sizeof(uri), "ipp://localhost/printers/%s", printer);
+ else
+ {
+ cgiSetVariable("ERROR", ippErrorString(IPP_NOT_FOUND));
+ cgiCopyTemplateLang(stdout, TEMPLATES, "error.tmpl", getenv("LANG"));
+ return;
+ }
+
+ /*
+ * Build a CUPS_DELETE_PRINTER request, which requires the following
+ * attributes:
+ *
+ * attributes-charset
+ * attributes-natural-language
+ * printer-uri
+ */
+
+ request = ippNew();
+
+ request->request.op.operation_id = CUPS_DELETE_PRINTER;
+ 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, "printer-uri",
+ NULL, uri);
+
+ /*
+ * Do the request and get back a response...
+ */
+
+ if ((response = cupsDoRequest(http, request, "/admin/")) != NULL)
+ {
+ status = response->request.status.status_code;
+
+ ippDelete(response);
+ }
+ else
+ status = IPP_GONE;
+
+ if (status > IPP_OK_CONFLICT)
+ {
+ cgiSetVariable("ERROR", ippErrorString(status));
+ cgiCopyTemplateLang(stdout, TEMPLATES, "error.tmpl", getenv("LANG"));
+ }
+ else
+ cgiCopyTemplateLang(stdout, TEMPLATES, "printer-deleted.tmpl", getenv("LANG"));
+}
+
+
+/*
+ * 'do_job_op()' - Do a job operation.
+ */
+
+static void
+do_job_op(http_t *http, /* I - HTTP connection */
+ cups_lang_t *language, /* I - Client's language */
+ ipp_op_t op) /* I - Operation to perform */
+{
+ ipp_t *request, /* IPP request */
+ *response; /* IPP response */
+ char uri[HTTP_MAX_URI]; /* Job URI */
+ const char *job; /* Job ID */
+ const char *printer; /* Printer name (purge-jobs) */
+ ipp_status_t status; /* Operation status... */
+
+
+ if ((job = cgiGetVariable("JOB_ID")) != NULL)
+ snprintf(uri, sizeof(uri), "ipp://localhost/jobs/%s", job);
+ else if ((printer = cgiGetVariable("PRINTER_NAME")) != NULL)
+ snprintf(uri, sizeof(uri), "ipp://localhost/printers/%s", printer);
+ else
+ {
+ cgiSetVariable("ERROR", ippErrorString(IPP_NOT_FOUND));
+ cgiCopyTemplateLang(stdout, TEMPLATES, "error.tmpl", getenv("LANG"));
+ return;
+ }
+
+ /*
+ * Build a job request, which requires the following
+ * attributes:
+ *
+ * attributes-charset
+ * attributes-natural-language
+ * job-uri or printer-uri (purge-jobs)
+ * requesting-user-name
+ */
+
+ request = ippNew();
+
+ request->request.op.operation_id = op;
+ 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);
+
+ if (job)
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "job-uri",
+ NULL, uri);
+ else
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
+ NULL, uri);
+
+ if (getenv("REMOTE_USER") != NULL)
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
+ NULL, getenv("REMOTE_USER"));
+ else
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
+ NULL, "root");
+
+ /*
+ * Do the request and get back a response...
+ */
+
+ if ((response = cupsDoRequest(http, request, "/jobs")) != NULL)
+ {
+ status = response->request.status.status_code;
+
+ ippDelete(response);
+ }
+ else
+ status = IPP_GONE;
+
+ if (status > IPP_OK_CONFLICT)
+ {
+ cgiSetVariable("ERROR", ippErrorString(status));
+ cgiCopyTemplateLang(stdout, TEMPLATES, "error.tmpl", getenv("LANG"));
+ }
+ else if (op == IPP_CANCEL_JOB)
+ cgiCopyTemplateLang(stdout, TEMPLATES, "job-cancel.tmpl", getenv("LANG"));
+ else if (op == IPP_HOLD_JOB)
+ cgiCopyTemplateLang(stdout, TEMPLATES, "job-hold.tmpl", getenv("LANG"));
+ else if (op == IPP_RELEASE_JOB)
+ cgiCopyTemplateLang(stdout, TEMPLATES, "job-release.tmpl", getenv("LANG"));
+}
+
+
+/*
+ * 'do_printer_op()' - Do a printer operation.
+ */
+
+static void
+do_printer_op(http_t *http, /* I - HTTP connection */
+ cups_lang_t *language, /* I - Client's language */
+ ipp_op_t op) /* I - Operation to perform */
+{
+ ipp_t *request, /* IPP request */
+ *response; /* IPP response */
+ char uri[HTTP_MAX_URI]; /* Printer URI */
+ const char *printer; /* Printer name (purge-jobs) */
+ ipp_status_t status; /* Operation status... */
+
+
+ if ((printer = cgiGetVariable("PRINTER_NAME")) != NULL)
+ snprintf(uri, sizeof(uri), "ipp://localhost/printers/%s", printer);
+ else
+ {
+ cgiSetVariable("ERROR", ippErrorString(IPP_NOT_FOUND));
+ cgiCopyTemplateLang(stdout, TEMPLATES, "error.tmpl", getenv("LANG"));
+ return;
+ }
+
+ /*
+ * Build a printer request, which requires the following
+ * attributes:
+ *
+ * attributes-charset
+ * attributes-natural-language
+ * printer-uri
+ */
+
+ request = ippNew();
+
+ request->request.op.operation_id = op;
+ 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, "printer-uri",
+ NULL, uri);
+
+ /*
+ * Do the request and get back a response...
+ */
+
+ if ((response = cupsDoRequest(http, request, "/admin/")) != NULL)
+ {
+ status = response->request.status.status_code;
+
+ ippDelete(response);
+ }
+ else
+ status = IPP_GONE;
+
+ if (status > IPP_OK_CONFLICT)
+ {
+ cgiSetVariable("ERROR", ippErrorString(status));
+ cgiCopyTemplateLang(stdout, TEMPLATES, "error.tmpl", getenv("LANG"));
+ }
+ else if (op == IPP_PAUSE_PRINTER)
+ cgiCopyTemplateLang(stdout, TEMPLATES, "printer-stop.tmpl", getenv("LANG"));
+ else if (op == IPP_RESUME_PRINTER)
+ cgiCopyTemplateLang(stdout, TEMPLATES, "printer-start.tmpl", getenv("LANG"));
+ else if (op == CUPS_ACCEPT_JOBS)
+ cgiCopyTemplateLang(stdout, TEMPLATES, "printer-accept.tmpl", getenv("LANG"));
+ else if (op == CUPS_REJECT_JOBS)
+ cgiCopyTemplateLang(stdout, TEMPLATES, "printer-reject.tmpl", getenv("LANG"));
+}
+
+
+/*
+ * 'get_line()' - Get a line that is terminated by a LF, CR, or CR LF.
+ */
+
+static char * /* O - Pointer to buf or NULL on EOF */
+get_line(char *buf, /* I - Line buffer */
+ int length, /* I - Length of buffer */
+ FILE *fp) /* I - File to read from */
+{
+ char *bufptr; /* Pointer into buffer */
+ int ch; /* Character from file */
+
+
+ length --;
+ bufptr = buf;
+
+ while ((ch = getc(fp)) != EOF)
+ {
+ if (ch == '\n')
+ break;
+ else if (ch == '\r')
+ {
+ /*
+ * Look for LF...
+ */
+
+ ch = getc(fp);
+ if (ch != '\n' && ch != EOF)
+ ungetc(ch, fp);
+
+ break;
+ }
+
+ *bufptr++ = ch;
+ length --;
+ if (length == 0)
+ break;
+ }
+
+ *bufptr = '\0';
+
+ if (ch == EOF)
+ return (NULL);
+ else
+ return (buf);
+}
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/cgi-bin/cgi.h b/cgi-bin/cgi.h
new file mode 100644
index 000000000..7bfb5ae53
--- /dev/null
+++ b/cgi-bin/cgi.h
@@ -0,0 +1,83 @@
+/*
+ * "$Id$"
+ *
+ * CGI support library definitions.
+ *
+ * Copyright 1997-2000 by Easy Software Products.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _CGI_H_
+# define _CGI_H_
+
+# include <stdio.h>
+# include <stdlib.h>
+# include <string.h>
+# include <ctype.h>
+
+# ifdef WIN32
+# include <direct.h>
+# include <io.h>
+# include <malloc.h>
+# define strcasecmp(s,t) stricmp((s),(t))
+# define strncasecmp(s,t,n) strnicmp((s),(t),(n))
+# else
+# include <unistd.h>
+# endif /* WIN32 */
+
+
+/*
+ * Prototypes...
+ */
+
+extern int cgiInitialize(void);
+extern void cgiAbort(const char *title, const char *stylesheet,
+ const char *format, ...);
+extern int cgiCheckVariables(const char *names);
+extern const char *cgiGetArray(const char *name, int element);
+extern int cgiGetSize(const char *name);
+extern void cgiSetSize(const char *name, int size);
+extern const char *cgiGetVariable(const char *name);
+extern void cgiSetArray(const char *name, int element,
+ const char *value);
+extern void cgiSetVariable(const char *name, const char *value);
+extern void cgiCopyTemplateFile(FILE *out, const char *tmpl);
+extern void cgiCopyTemplateLang(FILE *out, const char *directory,
+ const char *tmpl, const char *lang);
+
+extern void cgiStartHTML(FILE *out, const char *author,
+ const char *stylesheet,
+ const char *keywords,
+ const char *description,
+ const char *title, ...);
+extern void cgiEndHTML(FILE *out);
+
+extern FILE *cgiEMailOpen(const char *from, const char *to,
+ const char *cc, const char *subject,
+ int multipart);
+extern void cgiEMailPart(FILE *mail, const char *type,
+ const char *charset, const char *encoding);
+extern void cgiEMailClose(FILE *mail);
+
+
+# define cgiGetUser() getenv("REMOTE_USER")
+# define cgiGetHost() (getenv("REMOTE_HOST") == NULL ? getenv("REMOTE_ADDR") : getenv("REMOTE_HOST"))
+
+#endif /* !_CGI_H_ */
+
+/*
+ * End of "$Id$".
+ */
diff --git a/cgi-bin/classes.c b/cgi-bin/classes.c
new file mode 100644
index 000000000..9e38f1294
--- /dev/null
+++ b/cgi-bin/classes.c
@@ -0,0 +1,301 @@
+/*
+ * "$Id$"
+ *
+ * Class status CGI for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-2000 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.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "ipp-var.h"
+
+
+/*
+ * '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 *pclass; /* Printer class name */
+ http_t *http; /* Connection to the server */
+ ipp_t *request, /* IPP request */
+ *response; /* IPP response */
+ ipp_status_t status; /* Operation status... */
+ char uri[HTTP_MAX_URI];
+ /* Printer URI */
+ const char *which_jobs; /* Which jobs to show */
+ const char *op; /* Operation to perform, if any */
+
+
+ /*
+ * Get any form variables...
+ */
+
+ cgiInitialize();
+ op = cgiGetVariable("OP");
+
+ /*
+ * 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...
+ */
+
+ ippSetServerVersion();
+
+ pclass = argv[0];
+ if (strcmp(pclass, "/") == 0 || strcmp(pclass, "classes.cgi") == 0)
+ {
+ pclass = NULL;
+ cgiSetVariable("TITLE", cupsLangString(language, CUPS_MSG_CLASS));
+ }
+ else
+ cgiSetVariable("TITLE", pclass);
+
+ cgiCopyTemplateLang(stdout, TEMPLATES, "header.tmpl", getenv("LANG"));
+
+ if (op == NULL || strcasecmp(op, "print-test-page") != 0)
+ {
+ /*
+ * Get the class info...
+ */
+
+ request = ippNew();
+
+ 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 (pclass == NULL)
+ {
+ /*
+ * Build a CUPS_GET_CLASSES request, which requires the following
+ * attributes:
+ *
+ * attributes-charset
+ * attributes-natural-language
+ */
+
+ request->request.op.operation_id = CUPS_GET_CLASSES;
+ request->request.op.request_id = 1;
+ }
+ else
+ {
+ /*
+ * Build an IPP_GET_PRINTER_ATTRIBUTES request, which requires the following
+ * attributes:
+ *
+ * attributes-charset
+ * attributes-natural-language
+ * printer-uri
+ */
+
+ request->request.op.operation_id = IPP_GET_PRINTER_ATTRIBUTES;
+ request->request.op.request_id = 1;
+
+ snprintf(uri, sizeof(uri), "ipp://%s/classes/%s", getenv("SERVER_NAME"),
+ pclass);
+ 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, "/")) != NULL)
+ {
+ ippSetCGIVars(response, NULL, NULL);
+ ippDelete(response);
+ }
+
+ /*
+ * Write the report...
+ */
+
+ cgiCopyTemplateLang(stdout, TEMPLATES, "classes.tmpl", getenv("LANG"));
+
+ /*
+ * Get jobs for the specified class if a class has been chosen...
+ */
+
+ if (pclass != NULL)
+ {
+ /*
+ * Build an IPP_GET_JOBS request, which requires the following
+ * attributes:
+ *
+ * attributes-charset
+ * attributes-natural-language
+ * printer-uri
+ */
+
+ request = ippNew();
+
+ 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);
+
+ request->request.op.operation_id = IPP_GET_JOBS;
+ request->request.op.request_id = 1;
+
+ snprintf(uri, sizeof(uri), "ipp://%s/classes/%s", getenv("SERVER_NAME"),
+ pclass);
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL,
+ uri);
+
+ if ((which_jobs = cgiGetVariable("which_jobs")) != NULL)
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, "which-jobs",
+ NULL, which_jobs);
+
+ /*
+ * Do the request and get back a response...
+ */
+
+ if ((response = cupsDoRequest(http, request, "/")) != NULL)
+ {
+ ippSetCGIVars(response, NULL, NULL);
+ ippDelete(response);
+
+ cgiCopyTemplateLang(stdout, TEMPLATES, "jobs.tmpl", getenv("LANG"));
+ }
+ }
+ }
+ else
+ {
+ /*
+ * Print a test page...
+ */
+
+ snprintf(uri, sizeof(uri), "ipp://localhost/classes/%s", pclass);
+
+ /*
+ * Build an IPP_PRINT_JOB request, which requires the following
+ * attributes:
+ *
+ * attributes-charset
+ * attributes-natural-language
+ * printer-uri
+ * requesting-user-name
+ * document-format
+ */
+
+ request = ippNew();
+
+ request->request.op.operation_id = IPP_PRINT_JOB;
+ 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, "printer-uri",
+ NULL, uri);
+
+ if (getenv("REMOTE_USER") != NULL)
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
+ NULL, getenv("REMOTE_USER"));
+ else
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
+ NULL, "root");
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "job-name",
+ NULL, "Test Page");
+
+ ippAddString(request, IPP_TAG_JOB, IPP_TAG_MIMETYPE, "document-format",
+ NULL, "application/postscript");
+
+ /*
+ * Do the request and get back a response...
+ */
+
+ if ((response = cupsDoFileRequest(http, request, uri + 15,
+ CUPS_DATADIR "/data/testprint.ps")) != NULL)
+ {
+ status = response->request.status.status_code;
+ ippSetCGIVars(response, NULL, NULL);
+
+ ippDelete(response);
+ }
+ else
+ status = IPP_GONE;
+
+ cgiSetVariable("PRINTER_NAME", pclass);
+
+ if (status > IPP_OK_CONFLICT)
+ {
+ cgiSetVariable("ERROR", ippErrorString(status));
+ cgiCopyTemplateLang(stdout, TEMPLATES, "error.tmpl", getenv("LANG"));
+ }
+ else
+ cgiCopyTemplateLang(stdout, TEMPLATES, "test-page.tmpl", getenv("LANG"));
+ }
+
+ cgiCopyTemplateLang(stdout, TEMPLATES, "trailer.tmpl", getenv("LANG"));
+
+ /*
+ * Close the HTTP server connection...
+ */
+
+ httpClose(http);
+ cupsLangFree(language);
+
+ /*
+ * Return with no errors...
+ */
+
+ return (0);
+}
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/cgi-bin/html.c b/cgi-bin/html.c
new file mode 100644
index 000000000..c2b47c4c0
--- /dev/null
+++ b/cgi-bin/html.c
@@ -0,0 +1,89 @@
+/*
+ * "$Id$"
+ *
+ * CGI HTML functions.
+ *
+ * Copyright 1997-2000 by Easy Software Products.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Contents:
+ *
+ * cgiStartHTML() - Start an HTML document stream.
+ * cgiEndHTML() - End an HTML document stream.
+ */
+
+#include "cgi.h"
+#include <stdarg.h>
+
+
+/*
+ * 'cgiStartHTML()' - Start an HTML document stream.
+ */
+
+void
+cgiStartHTML(FILE *out, /* I - Output file to use */
+ const char *stylesheet, /* I - Stylesheet to use */
+ const char *author, /* I - Author name */
+ const char *keywords, /* I - Search keywords */
+ const char *description, /* I - Description of document */
+ const char *title, /* I - Title for page */
+ ...) /* I - Any addition args for title */
+{
+ va_list ap; /* Argument pointer */
+
+
+ fputs("Content-type: text/html\n\n", out);
+ fputs("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\" "
+ "\"http://www.w3.org/TR/REC-html40/loose.dtd\">\n", out);
+ fputs("<HTML>\n", out);
+ fputs("<HEAD>\n", out);
+
+ fputs("\t<TITLE>\n", out);
+ va_start(ap, title);
+ vfprintf(out, title, ap);
+ va_end(ap);
+ fputs("</TITLE>\n", out);
+
+ if (stylesheet)
+ fprintf(out, "\t<LINK REL=\"STYLESHEET\" TYPE=\"text/css\" HREF=\"%s\">\n",
+ stylesheet);
+ if (author)
+ fprintf(out, "\t<META NAME=\"AUTHOR\" CONTENT=\"%s\">\n", author);
+ if (keywords)
+ fprintf(out, "\t<META NAME=\"KEYWORDS\" CONTENT=\"%s\">\n", keywords);
+ if (description)
+ fprintf(out, "\t<META NAME=\"DESCRIPTION\" CONTENT=\"%s\">\n", description);
+
+ fputs("</HEAD>\n", out);
+ fputs("<BODY>\n", out);
+}
+
+
+/*
+ * 'cgiEndHTML()' - End an HTML document stream.
+ */
+
+void
+cgiEndHTML(FILE *out) /* I - Output file to use */
+{
+ fputs("</BODY>\n", out);
+ fputs("</HTML>\n", out);
+}
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/cgi-bin/ipp-var.c b/cgi-bin/ipp-var.c
new file mode 100644
index 000000000..0cc4c3a89
--- /dev/null
+++ b/cgi-bin/ipp-var.c
@@ -0,0 +1,211 @@
+/*
+ * "$Id$"
+ *
+ * IPP variable routines for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-2000 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:
+ *
+ * ippSetServerVersion() - Set the server name and CUPS version...
+ * ippSetCGIVars() - Set CGI variables from an IPP response.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "ipp-var.h"
+
+
+/*
+ * 'ippSetServerVersion()' - Set the server name and CUPS version...
+ */
+
+void
+ippSetServerVersion(void)
+{
+ cgiSetVariable("SERVER_NAME", getenv("SERVER_NAME"));
+ cgiSetVariable("REMOTE_USER", getenv("REMOTE_USER"));
+ cgiSetVariable("CUPS_VERSION", CUPS_SVERSION);
+}
+
+
+/*
+ * 'ippSetCGIVars()' - Set CGI variables from an IPP response.
+ */
+
+void
+ippSetCGIVars(ipp_t *response, /* I - Response data to be copied... */
+ const char *filter_name, /* I - Filter name */
+ const char *filter_value) /* I - Filter value */
+{
+ int element; /* Element in CGI array */
+ ipp_attribute_t *attr, /* Attribute in response... */
+ *filter; /* Filtering attribute */
+ int i; /* Looping var */
+ char name[1024], /* Name of attribute */
+ value[16384], /* Value(s) */
+ *valptr; /* Pointer into value */
+ char method[HTTP_MAX_URI],
+ username[HTTP_MAX_URI],
+ hostname[HTTP_MAX_URI],
+ resource[HTTP_MAX_URI],
+ uri[HTTP_MAX_URI];
+ int port;
+
+
+ ippSetServerVersion();
+
+ for (attr = response->attrs;
+ attr && attr->group_tag == IPP_TAG_OPERATION;
+ attr = attr->next);
+
+ for (element = 0; attr != NULL; attr = attr->next, element ++)
+ {
+ /*
+ * Copy attributes to a separator...
+ */
+
+ if (filter_name)
+ {
+ printf("filtering on %s = \"%s\"\n", filter_name, filter_value);
+
+ for (filter = attr;
+ filter != NULL && filter->group_tag != IPP_TAG_ZERO;
+ filter = filter->next)
+ if (filter->name && strcmp(filter->name, filter_name) == 0 &&
+ strcasecmp(filter->values[0].string.text, filter_value) == 0)
+ break;
+
+ if (!filter)
+ return;
+
+ if (filter->group_tag == IPP_TAG_ZERO)
+ {
+ attr = filter;
+ element --;
+ continue;
+ }
+ }
+
+ for (; attr != NULL && attr->group_tag != IPP_TAG_ZERO; attr = attr->next)
+ {
+ /*
+ * Copy the attribute name, substituting "_" for "-"...
+ */
+
+ if (attr->name == NULL)
+ continue;
+
+ for (i = 0; attr->name[i]; i ++)
+ if (attr->name[i] == '-')
+ name[i] = '_';
+ else
+ name[i] = attr->name[i];
+
+ name[i] = '\0';
+
+ /*
+ * Copy values...
+ */
+
+ value[0] = '\0';
+ valptr = value;
+
+ for (i = 0; i < attr->num_values; i ++)
+ {
+ if (i)
+ strcat(valptr, ",");
+
+ valptr += strlen(valptr);
+
+ switch (attr->value_tag)
+ {
+ case IPP_TAG_INTEGER :
+ case IPP_TAG_ENUM :
+ sprintf(valptr, "%d", attr->values[i].integer);
+ break;
+
+ case IPP_TAG_BOOLEAN :
+ sprintf(valptr, "%d", attr->values[i].boolean);
+ break;
+
+ case IPP_TAG_NOVALUE :
+ strcat(valptr, "novalue");
+ break;
+
+ case IPP_TAG_RANGE :
+ sprintf(valptr, "%d-%d", attr->values[i].range.lower,
+ attr->values[i].range.upper);
+ break;
+
+ case IPP_TAG_RESOLUTION :
+ sprintf(valptr, "%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_URI :
+ if (strncmp(attr->values[i].string.text, "ipp:", 4) == 0)
+ {
+ httpSeparate(attr->values[i].string.text, method, username,
+ hostname, &port, resource);
+ if (username[0])
+ snprintf(uri, sizeof(uri), "http://%s@%s:%d%s", username,
+ hostname, port, resource);
+ else
+ snprintf(uri, sizeof(uri), "http://%s:%d%s", hostname, port,
+ resource);
+
+ strcat(valptr, uri);
+ 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 :
+ strcat(valptr, attr->values[i].string.text);
+ break;
+
+ default :
+ break; /* anti-compiler-warning-code */
+ }
+ }
+
+ /*
+ * Add the element...
+ */
+
+ cgiSetArray(name, element, value);
+ }
+
+ if (attr == NULL)
+ break;
+ }
+}
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/cgi-bin/ipp-var.h b/cgi-bin/ipp-var.h
new file mode 100644
index 000000000..ffed6427c
--- /dev/null
+++ b/cgi-bin/ipp-var.h
@@ -0,0 +1,55 @@
+/*
+ * "$Id$"
+ *
+ * IPP variable definitions for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-2000 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 <ctype.h>
+#include <cups/cups.h>
+#include <cups/debug.h>
+#include <cups/language.h>
+#include <cups/string.h>
+#include "cgi.h"
+
+
+/*
+ * Definitions...
+ */
+
+/*#define TEMPLATES "/home/mike/c/cups/templates"*/
+#define TEMPLATES CUPS_DATADIR "/templates"
+
+
+/*
+ * Prototype...
+ */
+
+extern void ippSetServerVersion(void);
+extern void ippSetCGIVars(ipp_t *, const char *, const char *);
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/cgi-bin/jobs.c b/cgi-bin/jobs.c
new file mode 100644
index 000000000..3e6d4ab5d
--- /dev/null
+++ b/cgi-bin/jobs.c
@@ -0,0 +1,139 @@
+/*
+ * "$Id$"
+ *
+ * Job status CGI for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-2000 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.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "ipp-var.h"
+
+
+/*
+ * '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 */
+ http_t *http; /* Connection to the server */
+ const char *which_jobs; /* Which jobs to show */
+ ipp_t *request, /* IPP request */
+ *response; /* IPP response */
+
+
+ /*
+ * Get any form variables...
+ */
+
+ cgiInitialize();
+
+ /*
+ * 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));
+
+ cgiSetVariable("TITLE", "Jobs");
+
+ ippSetServerVersion();
+
+ cgiCopyTemplateLang(stdout, TEMPLATES, "header.tmpl", getenv("LANG"));
+
+ /*
+ * Build an IPP_GET_JOBS request, which requires the following
+ * attributes:
+ *
+ * attributes-charset
+ * attributes-natural-language
+ * printer-uri
+ */
+
+ request = ippNew();
+
+ 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);
+
+ request->request.op.operation_id = IPP_GET_JOBS;
+ request->request.op.request_id = 1;
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "job-uri", NULL,
+ "ipp://localhost/jobs");
+
+ if ((which_jobs = cgiGetVariable("which_jobs")) != NULL)
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, "which-jobs",
+ NULL, which_jobs);
+
+ /*
+ * Do the request and get back a response...
+ */
+
+ if ((response = cupsDoRequest(http, request, "/")) != NULL)
+ {
+ ippSetCGIVars(response, NULL, NULL);
+ ippDelete(response);
+
+ cgiCopyTemplateLang(stdout, TEMPLATES, "jobs.tmpl", getenv("LANG"));
+ }
+
+ cgiCopyTemplateLang(stdout, TEMPLATES, "trailer.tmpl", getenv("LANG"));
+
+ /*
+ * Close the HTTP server connection...
+ */
+
+ httpClose(http);
+ cupsLangFree(language);
+
+ /*
+ * Return with no errors...
+ */
+
+ return (0);
+}
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/cgi-bin/printers.c b/cgi-bin/printers.c
new file mode 100644
index 000000000..4c61acc34
--- /dev/null
+++ b/cgi-bin/printers.c
@@ -0,0 +1,301 @@
+/*
+ * "$Id$"
+ *
+ * Printer status CGI for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-2000 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.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "ipp-var.h"
+
+
+/*
+ * '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 */
+ ipp_t *request, /* IPP request */
+ *response; /* IPP response */
+ ipp_status_t status; /* Operation status... */
+ char uri[HTTP_MAX_URI];
+ /* Printer URI */
+ const char *which_jobs; /* Which jobs to show */
+ const char *op; /* Operation to perform, if any */
+
+
+ /*
+ * Get any form variables...
+ */
+
+ cgiInitialize();
+ op = cgiGetVariable("OP");
+
+ /*
+ * 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...
+ */
+
+ ippSetServerVersion();
+
+ printer = argv[0];
+ if (strcmp(printer, "/") == 0 || strcmp(printer, "printers.cgi") == 0)
+ {
+ printer = NULL;
+ cgiSetVariable("TITLE", cupsLangString(language, CUPS_MSG_PRINTER));
+ }
+ else
+ cgiSetVariable("TITLE", printer);
+
+ cgiCopyTemplateLang(stdout, TEMPLATES, "header.tmpl", getenv("LANG"));
+
+ if (op == NULL || strcasecmp(op, "print-test-page") != 0)
+ {
+ /*
+ * Get the printer info...
+ */
+
+ request = ippNew();
+
+ 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 (printer == NULL)
+ {
+ /*
+ * Build a CUPS_GET_PRINTERS request, which requires the following
+ * attributes:
+ *
+ * attributes-charset
+ * attributes-natural-language
+ */
+
+ request->request.op.operation_id = CUPS_GET_PRINTERS;
+ request->request.op.request_id = 1;
+ }
+ else
+ {
+ /*
+ * Build an IPP_GET_PRINTER_ATTRIBUTES request, which requires the following
+ * attributes:
+ *
+ * attributes-charset
+ * attributes-natural-language
+ * printer-uri
+ */
+
+ request->request.op.operation_id = IPP_GET_PRINTER_ATTRIBUTES;
+ request->request.op.request_id = 1;
+
+ snprintf(uri, sizeof(uri), "ipp://%s/printers/%s", getenv("SERVER_NAME"),
+ printer);
+ 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, "/")) != NULL)
+ {
+ ippSetCGIVars(response, NULL, NULL);
+ ippDelete(response);
+ }
+
+ /*
+ * Write the report...
+ */
+
+ cgiCopyTemplateLang(stdout, TEMPLATES, "printers.tmpl", getenv("LANG"));
+
+ /*
+ * Get jobs for the specified printer if a printer has been chosen...
+ */
+
+ if (printer != NULL)
+ {
+ /*
+ * Build an IPP_GET_JOBS request, which requires the following
+ * attributes:
+ *
+ * attributes-charset
+ * attributes-natural-language
+ * printer-uri
+ */
+
+ request = ippNew();
+
+ 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);
+
+ request->request.op.operation_id = IPP_GET_JOBS;
+ request->request.op.request_id = 1;
+
+ snprintf(uri, sizeof(uri), "ipp://%s/printers/%s", getenv("SERVER_NAME"),
+ printer);
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL,
+ uri);
+
+ if ((which_jobs = cgiGetVariable("which_jobs")) != NULL)
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, "which-jobs",
+ NULL, which_jobs);
+
+ /*
+ * Do the request and get back a response...
+ */
+
+ if ((response = cupsDoRequest(http, request, "/")) != NULL)
+ {
+ ippSetCGIVars(response, NULL, NULL);
+ ippDelete(response);
+
+ cgiCopyTemplateLang(stdout, TEMPLATES, "jobs.tmpl", getenv("LANG"));
+ }
+ }
+ }
+ else
+ {
+ /*
+ * Print a test page...
+ */
+
+ snprintf(uri, sizeof(uri), "ipp://localhost/printers/%s", printer);
+
+ /*
+ * Build an IPP_PRINT_JOB request, which requires the following
+ * attributes:
+ *
+ * attributes-charset
+ * attributes-natural-language
+ * printer-uri
+ * requesting-user-name
+ * document-format
+ */
+
+ request = ippNew();
+
+ request->request.op.operation_id = IPP_PRINT_JOB;
+ 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, "printer-uri",
+ NULL, uri);
+
+ if (getenv("REMOTE_USER") != NULL)
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
+ NULL, getenv("REMOTE_USER"));
+ else
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
+ NULL, "root");
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "job-name",
+ NULL, "Test Page");
+
+ ippAddString(request, IPP_TAG_JOB, IPP_TAG_MIMETYPE, "document-format",
+ NULL, "application/postscript");
+
+ /*
+ * Do the request and get back a response...
+ */
+
+ if ((response = cupsDoFileRequest(http, request, uri + 15,
+ CUPS_DATADIR "/data/testprint.ps")) != NULL)
+ {
+ status = response->request.status.status_code;
+ ippSetCGIVars(response, NULL, NULL);
+
+ ippDelete(response);
+ }
+ else
+ status = IPP_GONE;
+
+ cgiSetVariable("PRINTER_NAME", printer);
+
+ if (status > IPP_OK_CONFLICT)
+ {
+ cgiSetVariable("ERROR", ippErrorString(status));
+ cgiCopyTemplateLang(stdout, TEMPLATES, "error.tmpl", getenv("LANG"));
+ }
+ else
+ cgiCopyTemplateLang(stdout, TEMPLATES, "test-page.tmpl", getenv("LANG"));
+ }
+
+ cgiCopyTemplateLang(stdout, TEMPLATES, "trailer.tmpl", getenv("LANG"));
+
+ /*
+ * Close the HTTP server connection...
+ */
+
+ httpClose(http);
+ cupsLangFree(language);
+
+ /*
+ * Return with no errors...
+ */
+
+ return (0);
+}
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/cgi-bin/template.c b/cgi-bin/template.c
new file mode 100644
index 000000000..faf096842
--- /dev/null
+++ b/cgi-bin/template.c
@@ -0,0 +1,454 @@
+/*
+ * "$Id$"
+ *
+ * CGI template function.
+ *
+ * Copyright 1997-2000 by Easy Software Products.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Contents:
+ *
+ * cgiCopyTemplateFile() - Copy a template file and replace all the
+ * '{variable}' strings with the variable value.
+ * cgiCopyTemplateLang() - Copy a template file using a language...
+ * cgi_copy() - Copy the template file, substituting as needed...
+ * cgi_puts() - Put a string to the output file, quoting as
+ * needed...
+ */
+
+#include "cgi.h"
+
+
+/*
+ * Local functions...
+ */
+
+static void cgi_copy(FILE *out, FILE *in, int element, char term);
+static void cgi_puts(const char *s, FILE *out);
+
+
+/*
+ * 'cgiCopyTemplateFile()' - Copy a template file and replace all the
+ * '{variable}' strings with the variable value.
+ */
+
+void
+cgiCopyTemplateFile(FILE *out, /* I - Output file */
+ const char *tmpl) /* I - Template file to read */
+{
+ FILE *in; /* Input file */
+
+
+ /*
+ * Open the template file...
+ */
+
+ if ((in = fopen(tmpl, "r")) == NULL)
+ return;
+
+ /*
+ * Parse the file to the end...
+ */
+
+ cgi_copy(out, in, 0, 0);
+
+ /*
+ * Close the template file and return...
+ */
+
+ fclose(in);
+}
+
+
+/*
+ * 'cgiCopyTemplateLang()' - Copy a template file using a language...
+ */
+
+void
+cgiCopyTemplateLang(FILE *out, /* I - Output file */
+ const char *directory, /* I - Directory */
+ const char *tmpl, /* I - Base filename */
+ const char *lang) /* I - Language */
+{
+ int i; /* Looping var */
+ char filename[1024], /* Filename */
+ locale[16]; /* Locale name */
+ FILE *in; /* Input file */
+
+
+ /*
+ * Convert the language to a locale name...
+ */
+
+ if (lang != NULL)
+ {
+ for (i = 0; lang[i] && i < 15; i ++)
+ if (isalnum(lang[i]))
+ locale[i] = tolower(lang[i]);
+ else
+ locale[i] = '_';
+
+ locale[i] = '\0';
+ }
+ else
+ locale[0] = '\0';
+
+ /*
+ * See if we have a template file for this language...
+ */
+
+ sprintf(filename, "%s/%s/%s", directory, locale, tmpl);
+ if (access(filename, 0))
+ {
+ locale[2] = '\0';
+
+ sprintf(filename, "%s/%s/%s", directory, locale, tmpl);
+ if (access(filename, 0))
+ sprintf(filename, "%s/%s", directory, tmpl);
+ }
+
+ /*
+ * Open the template file...
+ */
+
+ if ((in = fopen(filename, "r")) == NULL)
+ return;
+
+ /*
+ * Parse the file to the end...
+ */
+
+ cgi_copy(out, in, 0, 0);
+
+ /*
+ * Close the template file and return...
+ */
+
+ fclose(in);
+}
+
+
+/*
+ * 'cgi_copy()' - Copy the template file, substituting as needed...
+ */
+
+static void
+cgi_copy(FILE *out, /* I - Output file */
+ FILE *in, /* I - Input file */
+ int element, /* I - Element number (0 to N) */
+ char term) /* I - Terminating character */
+{
+ int ch; /* Character from file */
+ char op; /* Operation */
+ char name[255], /* Name of variable */
+ *nameptr, /* Pointer into name */
+ innername[255], /* Inner comparison name */
+ *innerptr, /* Pointer into inner name */
+ *s; /* String pointer */
+ const char *value; /* Value of variable */
+ const char *innerval; /* Inner value */
+ char outval[1024], /* Output string */
+ compare[1024]; /* Comparison string */
+ int result; /* Result of comparison */
+
+
+ /*
+ * Parse the file to the end...
+ */
+
+ while ((ch = getc(in)) != EOF)
+ if (ch == term)
+ break;
+ else if (ch == '{')
+ {
+ /*
+ * Get a variable name...
+ */
+
+ for (s = name; (ch = getc(in)) != EOF;)
+ if (strchr("}]<>=!", ch))
+ break;
+ else if (s > name && ch == '?')
+ break;
+ else
+ *s++ = ch;
+
+ *s = '\0';
+
+ /*
+ * See if it has a value...
+ */
+
+ if (name[0] == '?')
+ {
+ /*
+ * Insert value only if it exists...
+ */
+
+ if ((nameptr = strrchr(name, '-')) != NULL && isdigit(nameptr[1]))
+ {
+ *nameptr++ = '\0';
+
+ if ((value = cgiGetArray(name + 1, atoi(nameptr) - 1)) != NULL)
+ strcpy(outval, value);
+ else
+ outval[0] = '\0';
+ }
+ if ((value = cgiGetArray(name + 1, element)) != NULL)
+ strcpy(outval, value);
+ else
+ outval[0] = '\0';
+ }
+ else if (name[0] == '#')
+ {
+ /*
+ * Insert count...
+ */
+
+ if (name[1])
+ sprintf(outval, "%d", cgiGetSize(name + 1));
+ else
+ sprintf(outval, "%d", element + 1);
+ }
+ else if (name[0] == '[')
+ {
+ /*
+ * Loop for # of elements...
+ */
+
+ int i; /* Looping var */
+ long pos; /* File position */
+ int count; /* Number of elements */
+
+
+ if (isdigit(name[1]))
+ count = atoi(name + 1);
+ else
+ count = cgiGetSize(name + 1);
+
+ pos = ftell(in);
+
+ if (count > 0)
+ {
+ for (i = 0; i < count; i ++)
+ {
+ fseek(in, pos, SEEK_SET);
+ cgi_copy(out, in, i, '}');
+ }
+ }
+ else
+ cgi_copy(NULL, in, 0, '}');
+
+ continue;
+ }
+ else
+ {
+ /*
+ * Insert variable or variable name (if element is NULL)...
+ */
+
+ if ((nameptr = strrchr(name, '-')) != NULL && isdigit(nameptr[1]))
+ {
+ *nameptr++ = '\0';
+ if ((value = cgiGetArray(name, atoi(nameptr) - 1)) == NULL)
+ sprintf(outval, "{%s}", name);
+ else
+ strcpy(outval, value);
+ }
+ else if ((value = cgiGetArray(name, element)) == NULL)
+ sprintf(outval, "{%s}", name);
+ else
+ strcpy(outval, value);
+ }
+
+ /*
+ * See if the terminating character requires another test...
+ */
+
+ if (ch == '}')
+ {
+ /*
+ * End of substitution...
+ */
+
+ if (out)
+ cgi_puts(outval, out);
+
+ continue;
+ }
+
+ /*
+ * OK, process one of the following checks:
+ *
+ * {name?exist:not-exist} Exists?
+ * {name=value?true:false} Equal
+ * {name<value?true:false} Less than
+ * {name>value?true:false} Greater than
+ * {name!value?true:false} Not equal
+ */
+
+ if (ch == '?')
+ {
+ /*
+ * Test for existance...
+ */
+
+ result = cgiGetArray(name, element) != NULL && outval[0];
+ }
+ else
+ {
+ /*
+ * Compare to a string...
+ */
+
+ op = ch;
+
+ for (s = compare; (ch = getc(in)) != EOF;)
+ if (ch == '?')
+ break;
+ else if (ch == '#')
+ {
+ sprintf(s, "%d", element + 1);
+ s += strlen(s);
+ }
+ else if (ch == '{')
+ {
+ /*
+ * Grab the value of a variable...
+ */
+
+ innerptr = innername;
+ while ((ch = getc(in)) != EOF && ch != '}')
+ *innerptr++ = ch;
+ *innerptr = '\0';
+
+ if (innername[0] == '#')
+ sprintf(s, "%d", cgiGetSize(innername + 1));
+ else if ((innerptr = strrchr(innername, '-')) != NULL &&
+ isdigit(innerptr[1]))
+ {
+ *innerptr++ = '\0';
+ if ((innerval = cgiGetArray(innername, atoi(innerptr) - 1)) == NULL)
+ *s = '\0';
+ else
+ strcpy(s, innerval);
+ }
+ else if (innername[0] == '?')
+ {
+ if ((innerval = cgiGetArray(innername + 1, element)) == NULL)
+ *s = '\0';
+ else
+ strcpy(s, innerval);
+ }
+ else if ((innerval = cgiGetArray(innername, element)) == NULL)
+ sprintf(s, "{%s}", innername);
+ else
+ strcpy(s, innerval);
+
+ s += strlen(s);
+ }
+ else if (ch == '\\')
+ *s++ = getc(in);
+ else
+ *s++ = ch;
+
+ *s = '\0';
+
+ if (ch != '?')
+ return;
+
+ /*
+ * Do the comparison...
+ */
+
+ switch (op)
+ {
+ case '<' :
+ result = strcasecmp(outval, compare) < 0;
+ break;
+ case '>' :
+ result = strcasecmp(outval, compare) > 0;
+ break;
+ case '=' :
+ result = strcasecmp(outval, compare) == 0;
+ break;
+ case '!' :
+ result = strcasecmp(outval, compare) != 0;
+ break;
+ default :
+ result = 1;
+ break;
+ }
+ }
+
+ if (result)
+ {
+ /*
+ * Comparison true; output first part and ignore second...
+ */
+
+ cgi_copy(out, in, element, ':');
+ cgi_copy(NULL, in, element, '}');
+ }
+ else
+ {
+ /*
+ * Comparison false; ignore first part and output second...
+ */
+
+ cgi_copy(NULL, in, element, ':');
+ cgi_copy(out, in, element, '}');
+ }
+ }
+ else if (ch == '\\') /* Quoted char */
+ {
+ if (out)
+ putc(getc(in), out);
+ else
+ getc(in);
+ }
+ else if (out)
+ putc(ch, out);
+}
+
+
+/*
+ * 'cgi_puts()' - Put a string to the output file, quoting as needed...
+ */
+
+static void
+cgi_puts(const char *s,
+ FILE *out)
+{
+ while (*s)
+ {
+ if (s[0] == '<' && s[1] != '/' && !isalpha(s[1]))
+ fputs("&lt;", out);
+ else if (*s == '\"')
+ fputs("&quot;", out);
+ else if (s[0] == '&' && isspace(s[1]))
+ fputs("&amp;", out);
+ else
+ putc(*s, out);
+
+ s ++;
+ }
+}
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/cgi-bin/var.c b/cgi-bin/var.c
new file mode 100644
index 000000000..b2ec5087e
--- /dev/null
+++ b/cgi-bin/var.c
@@ -0,0 +1,656 @@
+/*
+ * "$Id$"
+ *
+ * CGI form variable and array functions.
+ *
+ * Copyright 1997-2000 by Easy Software Products.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Contents:
+ *
+ * cgiInitialize() - Initialize the CGI variable "database"...
+ * cgiCheckVariables() - Check for the presence of "required" variables.
+ * cgiGetArray() - Get an element from a form array...
+ * cgiGetSize() - Get the size of a form array value.
+ * cgiGetVariable() - Get a CGI variable from the database...
+ * cgiSetArray() - Set array element N to the specified string.
+ * cgiSetVariable() - Set a CGI variable in the database...
+ * cgi_add_variable() - Add a form variable.
+ * cgi_compare_variables() - Compare two variables.
+ * cgi_find_variable() - Find a variable...
+ * cgi_initialize_get() - Initialize form variables using the GET method.
+ * cgi_initialize_post() - Initialize variables using the POST method.
+ * cgi_initialize_string() - Initialize form variables from a string.
+ * cgi_sort_variables() - Sort all form variables for faster lookup.
+ */
+
+/*#define DEBUG*/
+#include "cgi.h"
+
+
+/*
+ * Data structure to hold all the CGI form variables and arrays...
+ */
+
+typedef struct
+{
+ const char *name; /* Name of variable */
+ int nvalues, /* Number of values */
+ avalues; /* Number of values allocated */
+ const char **values; /* Value(s) of variable */
+} var_t;
+
+
+/*
+ * Local globals...
+ */
+
+static int form_count = 0, /* Form variable count */
+ form_alloc = 0; /* Number of variables allocated */
+static var_t *form_vars = NULL; /* Form variables */
+
+
+/*
+ * Local functions...
+ */
+
+static void cgi_add_variable(const char *name, int element,
+ const char *value);
+static int cgi_compare_variables(const var_t *v1, const var_t *v2);
+static var_t *cgi_find_variable(const char *name);
+static int cgi_initialize_get(void);
+static int cgi_initialize_post(void);
+static int cgi_initialize_string(const char *data);
+static void cgi_sort_variables(void);
+
+
+/*
+ * 'cgiInitialize()' - Initialize the CGI variable "database"...
+ */
+
+int /* O - Non-zero if there was form data */
+cgiInitialize(void)
+{
+ char *method; /* Form posting method */
+
+
+#ifdef DEBUG
+ setbuf(stdout, NULL);
+ puts("Content-type: text/plain\n");
+#endif /* DEBUG */
+
+ method = getenv("REQUEST_METHOD");
+
+ if (method == NULL)
+ return (0);
+
+ if (strcasecmp(method, "GET") == 0)
+ return (cgi_initialize_get());
+ else if (strcasecmp(method, "POST") == 0)
+ return (cgi_initialize_post());
+ else
+ return (0);
+}
+
+
+/*
+ * 'cgiCheckVariables()' - Check for the presence of "required" variables.
+ *
+ * Names may be separated by spaces and/or commas.
+ */
+
+int /* O - 1 if all variables present, 0 otherwise */
+cgiCheckVariables(const char *names) /* I - Variables to look for */
+{
+ char name[255], /* Current variable name */
+ *s; /* Pointer in string */
+ const char *val; /* Value of variable */
+ int element; /* Array element number */
+
+
+ if (names == NULL)
+ return (1);
+
+ while (*names != '\0')
+ {
+ while (*names == ' ' || *names == ',')
+ names ++;
+
+ for (s = name; *names != '\0' && *names != ' ' && *names != ','; s ++, names ++)
+ *s = *names;
+
+ *s = 0;
+ if (name[0] == '\0')
+ break;
+
+ if ((s = strrchr(name, '-')) != NULL)
+ {
+ *s = '\0';
+ element = atoi(s + 1) - 1;
+ val = cgiGetArray(name, element);
+ }
+ else
+ val = cgiGetVariable(name);
+
+ if (val == NULL)
+ return (0);
+
+ if (*val == '\0')
+ return (0); /* Can't be blank, either! */
+ }
+
+ return (1);
+}
+
+
+/*
+ * 'cgiGetArray()' - Get an element from a form array...
+ */
+
+const char * /* O - Element value or NULL */
+cgiGetArray(const char *name, /* I - Name of array variable */
+ int element) /* I - Element number (0 to N) */
+{
+ var_t *var; /* Pointer to variable */
+
+
+ if ((var = cgi_find_variable(name)) == NULL)
+ return (NULL);
+
+ if (var->nvalues == 1)
+ return (var->values[0]);
+
+ if (element < 0 || element >= var->nvalues)
+ return (NULL);
+
+ return (var->values[element]);
+}
+
+
+/*
+ * 'cgiGetSize()' - Get the size of a form array value.
+ */
+
+int /* O - Number of elements */
+cgiGetSize(const char *name) /* I - Name of variable */
+{
+ var_t *var; /* Pointer to variable */
+
+
+ if ((var = cgi_find_variable(name)) == NULL)
+ return (0);
+
+ return (var->nvalues);
+}
+
+
+/*
+ * 'cgiGetVariable()' - Get a CGI variable from the database...
+ *
+ * Returns NULL if the variable doesn't exist... If the variable is an
+ * array of values, returns the last element...
+ */
+
+const char * /* O - Value of variable */
+cgiGetVariable(const char *name)/* I - Name of variable */
+{
+ const var_t *var; /* Returned variable */
+
+
+ var = cgi_find_variable(name);
+
+#ifdef DEBUG
+ if (var == NULL)
+ printf("cgiGetVariable(\"%s\") is returning NULL...\n", name);
+ else
+ printf("cgiGetVariable(\"%s\") is returning \"%s\"...\n", name,
+ var->values[var->nvalues - 1]);
+#endif /* DEBUG */
+
+ return ((var == NULL) ? NULL : var->values[var->nvalues - 1]);
+}
+
+
+/*
+ * 'cgiSetArray()' - Set array element N to the specified string.
+ *
+ * If the variable array is smaller than (element + 1), the intervening
+ * elements are set to NULL.
+ */
+
+void
+cgiSetArray(const char *name, /* I - Name of variable */
+ int element, /* I - Element number (0 to N) */
+ const char *value) /* I - Value of variable */
+{
+ int i; /* Looping var */
+ var_t *var; /* Returned variable */
+
+
+ if (name == NULL || value == NULL || element < 0)
+ return;
+
+ if ((var = cgi_find_variable(name)) == NULL)
+ {
+ cgi_add_variable(name, element, value);
+ cgi_sort_variables();
+ }
+ else
+ {
+ if (element >= var->avalues)
+ {
+ var->avalues = element + 16;
+ var->values = (const char **)realloc((void *)(var->values),
+ sizeof(char *) * var->avalues);
+ }
+
+ if (element >= var->nvalues)
+ {
+ for (i = var->nvalues; i < element; i ++)
+ var->values[i] = NULL;
+
+ var->nvalues = element + 1;
+ }
+ else if (var->values[element])
+ free((char *)var->values[element]);
+
+ var->values[element] = strdup(value);
+ }
+}
+
+
+/*
+ * 'cgiSetSize()' - Set the array size.
+ */
+
+void
+cgiSetSize(const char *name, /* I - Name of variable */
+ int size) /* I - Number of elements (0 to N) */
+{
+ int i; /* Looping var */
+ var_t *var; /* Returned variable */
+
+
+ if (name == NULL || size < 0)
+ return;
+
+ if ((var = cgi_find_variable(name)) == NULL)
+ return;
+
+ if (size >= var->avalues)
+ {
+ var->avalues = size + 16;
+ var->values = (const char **)realloc((void *)(var->values),
+ sizeof(char *) * var->avalues);
+ }
+
+ if (size > var->nvalues)
+ {
+ for (i = var->nvalues; i < size; i ++)
+ var->values[i] = NULL;
+ }
+ else if (size < var->nvalues)
+ {
+ for (i = size; i < var->nvalues; i ++)
+ if (var->values[i])
+ free((void *)(var->values[i]));
+ }
+
+ var->nvalues = size;
+}
+
+
+/*
+ * 'cgiSetVariable()' - Set a CGI variable in the database...
+ *
+ * If the variable is an array, this truncates the array to a single element.
+ */
+
+void
+cgiSetVariable(const char *name, /* I - Name of variable */
+ const char *value) /* I - Value of variable */
+{
+ int i; /* Looping var */
+ var_t *var; /* Returned variable */
+
+
+ if (name == NULL || value == NULL)
+ return;
+
+ if ((var = cgi_find_variable(name)) == NULL)
+ {
+ cgi_add_variable(name, 0, value);
+ cgi_sort_variables();
+ }
+ else
+ {
+ for (i = 0; i < var->nvalues; i ++)
+ if (var->values[i])
+ free((char *)var->values[i]);
+
+ var->values[0] = strdup(value);
+ var->nvalues = 1;
+ }
+}
+
+
+/*
+ * 'cgi_add_variable()' - Add a form variable.
+ */
+
+static void
+cgi_add_variable(const char *name, /* I - Variable name */
+ int element, /* I - Array element number */
+ const char *value) /* I - Variable value */
+{
+ var_t *var; /* New variable */
+
+
+ if (name == NULL || value == NULL)
+ return;
+
+#ifdef DEBUG
+ printf("Adding variable \'%s\' with value \'%s\'...\n", name, value);
+#endif /* DEBUG */
+
+ if (form_count >= form_alloc)
+ {
+ if (form_alloc == 0)
+ form_vars = malloc(sizeof(var_t) * 16);
+ else
+ form_vars = realloc(form_vars, (form_alloc + 16) * sizeof(var_t));
+
+ form_alloc += 16;
+ }
+
+ var = form_vars + form_count;
+ var->name = strdup(name);
+ var->nvalues = element + 1;
+ var->avalues = element + 1;
+ var->values = calloc(element + 1, sizeof(char *));
+ var->values[element] = strdup(value);
+
+ form_count ++;
+}
+
+
+/*
+ * 'cgi_compare_variables()' - Compare two variables.
+ */
+
+static int /* O - Result of comparison */
+cgi_compare_variables(const var_t *v1, /* I - First variable */
+ const var_t *v2) /* I - Second variable */
+{
+ return (strcasecmp(v1->name, v2->name));
+}
+
+
+/*
+ * 'cgi_find_variable()' - Find a variable...
+ */
+
+static var_t * /* O - Variable pointer or NULL */
+cgi_find_variable(const char *name) /* I - Name of variable */
+{
+ var_t key; /* Search key */
+
+
+ if (form_count < 1 || name == NULL)
+ return (NULL);
+
+ key.name = name;
+
+ return ((var_t *)bsearch(&key, form_vars, form_count, sizeof(var_t),
+ (int (*)(const void *, const void *))cgi_compare_variables));
+}
+
+
+/*
+ * 'cgi_initialize_get()' - Initialize form variables using the GET method.
+ */
+
+static int /* O - 1 if form data read */
+cgi_initialize_get(void)
+{
+ char *data; /* Pointer to form data string */
+
+
+#ifdef DEBUG
+ puts("Initializing variables using GET method...");
+#endif /* DEBUG */
+
+ /*
+ * Check to see if there is anything for us to read...
+ */
+
+ data = getenv("QUERY_STRING");
+ if (data == NULL || strlen(data) == 0)
+ return (0);
+
+ /*
+ * Parse it out and return...
+ */
+
+ return (cgi_initialize_string(data));
+}
+
+
+/*
+ * 'cgi_initialize_post()' - Initialize variables using the POST method.
+ */
+
+static int /* O - 1 if form data was read */
+cgi_initialize_post(void)
+{
+ char *content_length, /* Length of input data (string) */
+ *data; /* Pointer to form data string */
+ int length, /* Length of input data */
+ nbytes, /* Number of bytes read this read() */
+ tbytes, /* Total number of bytes read */
+ status; /* Return status */
+
+
+#ifdef DEBUG
+ puts("Initializing variables using POST method...");
+#endif /* DEBUG */
+
+ /*
+ * Check to see if there is anything for us to read...
+ */
+
+ content_length = getenv("CONTENT_LENGTH");
+ if (content_length == NULL || atoi(content_length) == 0)
+ return (0);
+
+ /*
+ * Get the length of the input stream and allocate a buffer for it...
+ */
+
+ length = atoi(content_length);
+ data = malloc(length + 1);
+
+ /*
+ * Read the data into the buffer...
+ */
+
+ for (tbytes = 0; tbytes < length; tbytes += nbytes)
+ if ((nbytes = read(0, data + tbytes, length - tbytes)) < 0)
+ {
+ free(data);
+ return (0);
+ }
+
+ data[length] = '\0';
+
+ /*
+ * Parse it out...
+ */
+
+ status = cgi_initialize_string(data);
+
+ /*
+ * Free the data and return...
+ */
+
+ free(data);
+
+ return (status);
+}
+
+
+/*
+ * 'cgi_initialize_string()' - Initialize form variables from a string.
+ */
+
+static int
+cgi_initialize_string(const char *data) /* I - Form data string */
+{
+ int done; /* True if we're done reading a form variable */
+ char *s, /* Pointer to current form string */
+ ch, /* Temporary character */
+ name[255], /* Name of form variable */
+ value[65536]; /* Variable value... */
+
+
+ /*
+ * Check input...
+ */
+
+ if (data == NULL)
+ return (0);
+
+ /*
+ * Loop until we've read all the form data...
+ */
+
+ while (*data != '\0')
+ {
+ /*
+ * Get the variable name...
+ */
+
+ for (s = name; *data != '\0'; data ++, s ++)
+ if (*data == '=')
+ break;
+ else
+ *s = *data;
+
+ *s = '\0';
+ if (*data == '=')
+ data ++;
+ else
+ return (0);
+
+ /*
+ * Read the variable value...
+ */
+
+ for (s = value, done = 0; !done && *data != '\0'; data ++, s ++)
+ switch (*data)
+ {
+ case '&' : /* End of data... */
+ done = 1;
+ s --;
+ break;
+
+ case '+' : /* Escaped space character */
+ *s = ' ';
+ break;
+
+ case '%' : /* Escaped control character */
+ /*
+ * Read the hex code from stdin...
+ */
+
+ data ++;
+ ch = *data - '0';
+ if (ch > 9)
+ ch -= 7;
+ *s = ch << 4;
+
+ data ++;
+ ch = *data - '0';
+ if (ch > 9)
+ ch -= 7;
+ *s |= ch;
+ break;
+
+ default : /* Other characters come straight through */
+ *s = *data;
+ break;
+ }
+
+ *s = '\0'; /* nul terminate the string */
+
+ /*
+ * Remove trailing whitespace...
+ */
+
+ s --;
+ while (s >= value && *s == ' ')
+ *s-- = '\0';
+
+ /*
+ * Add the string to the variable "database"...
+ */
+
+ if ((s = strrchr(name, '-')) != NULL && isdigit(s[1]))
+ {
+ *s++ = '\0';
+ cgiSetArray(name, atoi(s) - 1, value);
+ }
+ else if (cgiGetVariable(name) != NULL)
+ cgiSetArray(name, cgiGetSize(name), value);
+ else
+ cgiSetVariable(name, value);
+ }
+
+ return (1);
+}
+
+
+/*
+ * 'cgi_sort_variables()' - Sort all form variables for faster lookup.
+ */
+
+static void
+cgi_sort_variables(void)
+{
+#ifdef DEBUG
+ int i;
+
+
+ puts("Sorting variables...");
+#endif /* DEBUG */
+
+ if (form_count < 2)
+ return;
+
+ qsort(form_vars, form_count, sizeof(var_t),
+ (int (*)(const void *, const void *))cgi_compare_variables);
+
+#ifdef DEBUG
+ puts("New variable list is:");
+ for (i = 0; i < form_count; i ++)
+ printf("%s = %s\n", form_vars[i].name, form_vars[i].value);
+#endif /* DEBUG */
+}
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/conf/Makefile b/conf/Makefile
new file mode 100644
index 000000000..ddcc56ffb
--- /dev/null
+++ b/conf/Makefile
@@ -0,0 +1,72 @@
+#
+# "$Id$"
+#
+# Configuration file makefile for the Common UNIX Printing System (CUPS).
+#
+# Copyright 1993-2000 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 client.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)
+ for file in $(KEEP); do \
+ if test -r $(SERVERROOT)/$$file ; then \
+ $(INSTALL_DATA) $$file $(SERVERROOT)/$$file.N ; \
+ else \
+ $(INSTALL_DATA) $$file $(SERVERROOT) ; \
+ fi ; \
+ done
+ for file in $(REPLACE); do \
+ if test -r $(SERVERROOT)/$$file ; then \
+ $(MV) $(SERVERROOT)/$$file $(SERVERROOT)/$$file.O ; \
+ fi ; \
+ $(INSTALL_DATA) $$file $(SERVERROOT) ; \
+ done
+
+
+#
+# End of "$Id$".
+#
diff --git a/conf/classes.conf b/conf/classes.conf
new file mode 100644
index 000000000..7d1fc3c48
--- /dev/null
+++ b/conf/classes.conf
@@ -0,0 +1,95 @@
+#
+# "$Id: classes.conf 969 2000-03-10 16:56:46Z mike $"
+#
+# Sample class configuration file for the Common UNIX Printing System
+# (CUPS) scheduler.
+#
+# Copyright 1997-2000 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
+#
+
+########################################################################
+# #
+# 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
+
+#
+# State: sets the initial state of the class. Can be one of the
+# following:
+#
+# Idle - Class is available to print new jobs.
+# Stopped - Class is disabled but accepting new jobs.
+#
+
+#State Idle
+
+#
+# StateMessage: sets the printer-state-message attribute for the class.
+#
+
+#StateMessage Class is idle.
+
+#
+# Accepting: is the class accepting jobs?
+#
+#Accepting Yes
+#Accepting No
+#
+
+#
+# Printer: adds a printer to the class.
+#
+
+#Printer sample
+#Printer sample@host2
+#</Class>
+
+#
+# End of "$Id: classes.conf 969 2000-03-10 16:56:46Z mike $".
+#
diff --git a/conf/client.conf b/conf/client.conf
new file mode 100644
index 000000000..26d45e8d5
--- /dev/null
+++ b/conf/client.conf
@@ -0,0 +1,50 @@
+#
+# "$Id: client.conf 969 2000-03-10 16:56:46Z mike $"
+#
+# Sample client configuration file for the Common UNIX Printing System
+# (CUPS).
+#
+# Copyright 1997-2000 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
+#
+
+########################################################################
+# #
+# This is the CUPS client configuration file. This file is used to #
+# define client-specific parameters, such as the default server or #
+# default printer. #
+# #
+########################################################################
+
+#
+# ServerName: the hostname of your server. By default CUPS will use the
+# hostname of the system.
+#
+
+#ServerName myhost.domain.com
+
+#
+# DefaultPrinter: the default printer (or class) that clients should use.
+#
+
+#DefaultPrinter myprinter
+#DefaultPrinter myprinter@host
+
+#
+# End of "$Id: client.conf 969 2000-03-10 16:56:46Z mike $".
+#
diff --git a/conf/cupsd.conf b/conf/cupsd.conf
new file mode 100644
index 000000000..7ebc526f1
--- /dev/null
+++ b/conf/cupsd.conf
@@ -0,0 +1,482 @@
+#
+# "$Id: cupsd.conf 1002 2000-03-21 18:35:38Z mike $"
+#
+# Sample configuration file for the Common UNIX Printing System (CUPS)
+# scheduler.
+#
+# Copyright 1997-2000 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
+#
+
+########################################################################
+# #
+# 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.
+#
+# To set the default server name used by clients, see the client.conf file.
+#
+
+#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 /etc/cups.
+#
+
+#ServerRoot /etc/cups
+
+#
+# ServerBin: the root directory for the scheduler executables.
+# By default /usr/lib/cups.
+#
+
+#ServerBin /usr/lib/cups
+
+#
+# DocumentRoot: the root directory for the HTTP server.
+# By default /usr/share/doc/cups.
+#
+
+#DocumentRoot /usr/share/doc/cups
+
+#
+# RequestRoot: the directory where request files are stored.
+# By default /var/spool/cups.
+#
+
+#RequestRoot /var/spool/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
+# "/var/log/cups/access_log"
+#
+# You can also use the special name "syslog" to send the output to the
+# syslog file or daemon.
+#
+
+#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"
+#
+# You can also use the special name "syslog" to send the output to the
+# syslog file or daemon.
+#
+
+#ErrorLog /var/log/cups/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"
+#
+# You can also use the special name "syslog" to send the output to the
+# syslog file or daemon.
+#
+
+#PageLog /var/log/cups/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 HTTP requests and 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.
+#
+# Set this to 0 to disable outgoing broadcasts so your local printers are
+# not advertised but you can still see printers on other hosts.
+#
+
+#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
+
+#
+# BrowseOrder: specifies the order of
+#
+
+#BrowseOrder allow,deny
+#BrowseOrder deny,allow
+
+#
+# BrowseAllow: specifies an address mask to allow for incoming browser
+# packets. The default is to allow packets from all addresses.
+#
+# BrowseDeny: specifies an address mask to deny for incoming browser
+# packets. The default is to deny packets from no addresses.
+#
+# Both "BrowseAllow" and "BrowseDeny" 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 hostname/domainname restrictions only work if you have turned hostname
+# lookups on!
+#
+
+#BrowseAllow address
+#BrowseDeny address
+
+#
+# BrowseRelay: relay browser packets from one address/network to another.
+#
+
+#BrowseRelay source-address destination-address
+
+#
+# BrowsePoll: poll the named server for printers
+#
+
+#BrowsePoll address:port
+
+#
+# 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 utf-8. Note that this can also be overridden in
+# HTML documents...
+#
+
+#DefaultCharset utf-8
+
+#
+# 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
+
+#
+# PreserveJobHistory: whether or not to preserve the job history after a
+# job is completed, cancelled, or stopped. Default is Yes.
+#
+
+#PreserveJobHistory Yes
+
+#
+# PreserveJobFiles: whether or not to preserve the job files after a
+# job is completed, cancelled, or stopped. Default is No.
+#
+
+#PreserveJobFiles No
+
+#
+# Printcap: the name of the printcap file. Default is no filename.
+# Leave blank to disable printcap file generation.
+#
+
+#Printcap /etc/printcap
+
+#
+# Access permissions for each directory served by the scheduler.
+# Locations are relative to DocumentRoot...
+#
+# AuthType: the authorization to use:
+#
+# None - Perform no authentication
+# Basic - Perform authentication using the HTTP Basic method.
+# Digest - Perform authentication using the HTTP Digest method.
+#
+# (Note: local certificate authentication can be substituted by
+# the client for Basic or Digest)
+#
+# 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.
+#
+
+## Anonymous access (default)
+#AuthType None
+
+## Require a username and password (Basic authentication)
+#AuthType Basic
+#AuthClass User
+
+## Require a username and password (Digest/MD5 authentication)
+#AuthType Digest
+#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 1002 2000-03-21 18:35:38Z mike $".
+#
diff --git a/conf/mime.convs b/conf/mime.convs
new file mode 100644
index 000000000..e8bde17fb
--- /dev/null
+++ b/conf/mime.convs
@@ -0,0 +1,64 @@
+#
+# "$Id: mime.convs 1019 2000-04-18 19:41:12Z mike $"
+#
+# MIME converts file for the Common UNIX Printing System (CUPS).
+#
+# Copyright 1997-2000 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/html application/postscript 50 texttops
+text/plain application/postscript 50 texttops
+application/vnd.cups-form application/vnd.cups-postscript 50 formtops
+
+########################################################################
+#
+# Raster filters...
+#
+
+image/* application/vnd.cups-raster 50 imagetoraster
+application/vnd.cups-postscript application/vnd.cups-raster 50 pstoraster
+
+#
+# End of "$Id: mime.convs 1019 2000-04-18 19:41:12Z mike $".
+#
diff --git a/conf/mime.types b/conf/mime.types
new file mode 100644
index 000000000..36f510eba
--- /dev/null
+++ b/conf/mime.types
@@ -0,0 +1,124 @@
+#
+# "$Id: mime.types 1019 2000-04-18 19:41:12Z mike $"
+#
+# MIME types file for the Common UNIX Printing System (CUPS).
+#
+# Copyright 1997-2000 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
+# contains(offset,range,"string") True if the range contains the 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 128-255.
+#
+# 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-form string(0,"<CUPSFORM>")
+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 1019 2000-04-18 19:41:12Z mike $".
+#
diff --git a/conf/printcap b/conf/printcap
new file mode 100644
index 000000000..230c3017d
--- /dev/null
+++ b/conf/printcap
@@ -0,0 +1,2 @@
+# This is a dummy printcap file that is automatically generated by the
+# CUPS software for old applications that rely on it.
diff --git a/conf/printers.conf b/conf/printers.conf
new file mode 100644
index 000000000..280ac7c72
--- /dev/null
+++ b/conf/printers.conf
@@ -0,0 +1,102 @@
+#
+# "$Id: printers.conf 969 2000-03-10 16:56:46Z mike $"
+#
+# Sample printer configuration file for the Common UNIX Printing System
+# (CUPS) scheduler.
+#
+# Copyright 1997-2000 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
+#
+
+########################################################################
+# #
+# 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 print new jobs.
+# Stopped - Printer is disabled but accepting new jobs.
+#
+
+#State Idle
+
+#
+# StateMessage: sets the printer-state-message attribute for the printer.
+#
+
+#StateMessage Printer is idle.
+
+#
+# Accepting: is the printer accepting jobs?
+#
+#Accepting Yes
+#Accepting No
+
+#</Printer>
+
+#
+# End of "$Id: printers.conf 969 2000-03-10 16:56:46Z mike $".
+#
diff --git a/config.h.in b/config.h.in
new file mode 100644
index 000000000..efdf979bf
--- /dev/null
+++ b/config.h.in
@@ -0,0 +1,127 @@
+/*
+ * "$Id$"
+ *
+ * Configuration file for the Common UNIX Printing System (CUPS).
+ *
+ * @configure_input@
+ *
+ * Copyright 1997-2000 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.1b3"
+
+/*
+ * Where are files stored?
+ */
+
+#define CUPS_LOCALEDIR "/usr/share/locale"
+#define CUPS_SERVERROOT "/etc/cups"
+#define CUPS_SERVERBIN "/usr/lib/cups"
+#define CUPS_DOCROOT "/usr/share/doc/cups"
+#define CUPS_REQUESTS "/var/spool/cups"
+#define CUPS_LOGDIR "/var/logs/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 PAM stuff?
+ */
+
+#ifndef HAVE_LIBPAM
+#define HAVE_LIBPAM 0
+#endif /* !HAVE_LIBPAM */
+
+/*
+ * 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
+
+/*
+ * Do we have the vsyslog() function?
+ */
+
+#undef HAVE_VSYSLOG
+
+/*
+ * Do we have the (v)snprintf() functions?
+ */
+
+#undef HAVE_SNPRINTF
+#undef HAVE_VSNPRINTF
+
+/*
+ * 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..a64247b10
--- /dev/null
+++ b/configure.in
@@ -0,0 +1,418 @@
+dnl
+dnl "$Id$"
+dnl
+dnl Configuration script for the Common UNIX Printing System (CUPS).
+dnl
+dnl Copyright 1997-2000 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/[[^0-9]]//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, [ --enable-shared turn on shared libraries [default=yes]])
+AC_ARG_ENABLE(libtool_unsupported, [ --enable-libtool-unsupported=LIBTOOL_PATH
+ turn on building with libtool (UNSUPPORTED!) [default=no]],[if eval "test x$enable_libtool != xno"; then
+ LIBTOOL="$enable_libtool"
+ enable_shared=no
+ echo "WARNING: libtool is not supported or endorsed by Easy Software Products."
+ echo " WE DO NOT PROVIDE TECHNICAL SUPPORT FOR LIBTOOL PROBLEMS."
+ echo " (even if you have a support contract)"
+else
+ LIBTOOL=""
+fi])
+
+if test "$enable_shared" != "no"; then
+ case "$uname" in
+ SunOS* | UNIX_S*)
+ LIBCUPS="libcups.so.2"
+ LIBCUPSIMAGE="libcupsimage.so.2"
+ DSO="\$(CC) -Wl,-h,\$@ -G \$(OPTIM) -o"
+ ;;
+ HP-UX*)
+ LIBCUPS="libcups.sl.2"
+ LIBCUPSIMAGE="libcupsimage.sl.2"
+ DSO="ld -b -z +h \$@ -o"
+ ;;
+ FreeBSD* | NetBSD* | OpenBSD*)
+ LIBCUPS="libcups.so.2"
+ LIBCUPSIMAGE="libcupsimage.so.2"
+ DSO="\$(CC) -Wl,-soname,\$@ -shared \$(OPTIM) -o"
+ ;;
+ OSF1* | Linux*)
+ LIBCUPS="libcups.so.2"
+ LIBCUPSIMAGE="libcupsimage.so.2"
+ DSO="\$(CC) -Wl,-soname,\$@ -shared \$(OPTIM) -o"
+ ;;
+ IRIX*)
+ LIBCUPS="libcups.so.2"
+ LIBCUPSIMAGE="libcupsimage.so.2"
+ DSO="\$(CC) -soname \$@ -shared \$(OPTIM) -o"
+ ;;
+ *)
+ echo "Warning: shared libraries may not be supported. Trying -shared"
+ echo " option with compiler."
+ LIBCUPS="libcups.so.2"
+ LIBCUPSIMAGE="libcupsimage.so.2"
+ DSO="\$(CC) -Wl,-soname,\$@ -shared \$(OPTIM) -o"
+ ;;
+ esac
+else
+ PICFLAG=0
+ LIBCUPS="libcups.a"
+ LIBCUPSIMAGE="libcupsimage.a"
+ DSO=":"
+fi
+
+if test "$LIBTOOL" != ""; then
+ LIBCUPS="libcups.la"
+ LIBCUPSIMAGE="libcupsimage.la"
+ LINKCUPS="../cups/\$(LIBCUPS)"
+ LINKCUPSIMAGE="../filter/\$(LIBCUPSIMAGE)"
+ DSO=":"
+else
+ LINKCUPS="-L../cups -lcups"
+ LINKCUPSIMAGE="-L../filter -lcupsimage"
+fi
+
+AC_ARG_ENABLE(pam, [ --enable-pam turn on PAM support [default=yes]])
+
+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(MV,mv)
+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(RM,rm)
+AC_PATH_PROG(SED,sed)
+
+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)
+if test "$enable_pam" != "no"; then
+ OLDLIBS="$LIBS"
+ AC_CHECK_LIB(dl,dlopen)
+ AC_CHECK_LIB(pam,pam_start)
+ if test "$ac_cv_lib_pam_pam_start" != "no"; then
+ PAMDIR="/etc/pam.d"
+ else
+ PAMDIR=""
+ LIBS="$OLDLIBS"
+ fi
+ AC_SUBST(PAMDIR)
+fi
+
+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)
+
+dnl Save the current libraries since we don't want the image libraries
+dnl included with every program...
+SAVELIBS="$LIBS"
+
+dnl Check for image libraries...
+LIBJPEG=""
+LIBPNG=""
+LIBTIFF=""
+LIBZ=""
+
+AC_SUBST(LIBJPEG)
+AC_SUBST(LIBPNG)
+AC_SUBST(LIBTIFF)
+AC_SUBST(LIBZ)
+
+AC_CHECK_LIB(jpeg, jpeg_destroy_decompress,
+ AC_DEFINE(HAVE_LIBJPEG)
+ LIBJPEG="-ljpeg"
+ LIBS="$LIBS -ljpeg")
+
+AC_CHECK_LIB(z, deflate,
+ AC_DEFINE(HAVE_LIBZ)
+ LIBZ="-lz"
+ LIBS="$LIBS -lz")
+
+dnl PNG library uses math library functions...
+AC_CHECK_LIB(m, pow)
+
+AC_CHECK_LIB(png, png_read_info,
+ AC_DEFINE(HAVE_LIBPNG)
+ LIBPNG="-lpng")
+
+AC_CHECK_LIB(tiff, TIFFReadScanline,
+ AC_DEFINE(HAVE_LIBTIFF)
+ LIBTIFF="-ltiff")
+
+dnl Restore original LIBS settings...
+LIBS="$SAVELIBS"
+
+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)
+AC_CHECK_FUNCS(snprintf)
+AC_CHECK_FUNCS(vsnprintf)
+
+dnl Checks for vsyslog function.
+AC_CHECK_FUNCS(vsyslog)
+
+dnl Checks for signal functions.
+if test "$uname" != "Linux"; then
+ AC_CHECK_FUNCS(sigset)
+fi
+
+AC_CHECK_FUNCS(sigaction)
+
+dnl Checks for wait functions.
+AC_CHECK_FUNCS(waitpid)
+AC_CHECK_FUNCS(wait3)
+
+dnl Update compiler options...
+if test -n "$GCC"; then
+ if test -z "$OPTIM"; then
+ OPTIM="-O2 -g3"
+ fi
+ if test $PICFLAG = 1; then
+ OPTIM="-fPIC $OPTIM"
+ fi
+ OPTIM="-Wall $OPTIM"
+else
+ case $uname in
+ IRIX*)
+ if test -z "$OPTIM"; then
+ OPTIM="-O2 -g3"
+ fi
+ if test $uversion -ge 62; then
+ OPTIM="$OPTIM -n32 -mips3"
+ fi
+ OPTIM="-fullwarn $OPTIM"
+ ;;
+ HP-UX*)
+ if test -z "$OPTIM"; then
+ OPTIM="+O2 -g3"
+ fi
+ OPTIM="-Ae $OPTIM"
+ ;;
+ SunOS*)
+ # Solaris
+ if test -z "$OPTIM"; then
+ OPTIM="-O -g3"
+ 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 *BSD, HP-UX, and Solaris run-time linkers need help when
+ # 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="$LDFLAGS -R$libdir"
+ ;;
+ FreeBSD* | NetBSD* | OpenBSD*)
+ # *BSD
+ LDFLAGS="$LDFLAGS -Wl,-R$libdir"
+ ;;
+ esac
+else
+ DSOLIBS=""
+ IMGLIBS="\$(LIBJPEG) \$(LIBPNG) \$(LIBTIFF) \$(LIBZ)"
+fi
+
+# Figure out how to install "cat" pages...
+case "$uname" in
+ FreeBSD* | NetBSD* | OpenBSD*)
+ # *BSD
+ INSTALL_CAT="install-bsdcat"
+ ;;
+ *)
+ # All others
+ INSTALL_CAT="install-cat"
+ ;;
+esac
+
+AC_SUBST(DSO)
+AC_SUBST(DSOLIBS)
+AC_SUBST(IMGLIBS)
+AC_SUBST(LIBCUPS)
+AC_SUBST(LIBCUPSIMAGE)
+AC_SUBST(LIBTOOL)
+AC_SUBST(LINKCUPS)
+AC_SUBST(LINKCUPSIMAGE)
+
+dnl Fix prefix as needed..
+if test "$prefix" = "NONE" ; then
+ prefix="/usr"
+fi
+
+dnl Fix "libdir" variable for IRIX 6.x...
+if test "$uname" = "IRIX" -a $uversion -ge 65; then
+ libdir="${prefix}/lib32"
+fi
+
+dnl Need special attention for the default location...
+if test "$prefix" = "/usr" ; then
+ CUPS_SERVERROOT="/etc/cups"
+ CUPS_LOGDIR="/var/log/cups"
+ CUPS_REQUESTS="/var/spool/cups"
+
+ AC_DEFINE_UNQUOTED(CUPS_SERVERROOT, "$CUPS_SERVERROOT")
+ AC_DEFINE_UNQUOTED(CUPS_LOGDIR, "$CUPS_LOGDIR")
+ AC_DEFINE_UNQUOTED(CUPS_REQUESTS, "$CUPS_REQUESTS")
+else
+ CUPS_SERVERROOT='${prefix}/etc/cups'
+ CUPS_LOGDIR='${prefix}/log/cups'
+ CUPS_REQUESTS='${prefix}/spool/cups'
+
+ AC_DEFINE_UNQUOTED(CUPS_SERVERROOT, "$prefix/etc/cups")
+ AC_DEFINE_UNQUOTED(CUPS_LOGDIR, "$prefix/log/cups")
+ AC_DEFINE_UNQUOTED(CUPS_REQUESTS, "$prefix/spool/cups")
+fi
+
+dnl See what directory to put server executables...
+case "$uname" in
+ FreeBSD* | NetBSD* | OpenBSD*)
+ # *BSD
+ CUPS_SERVERBIN='${prefix}/libexec/cups'
+ AC_DEFINE_UNQUOTED(CUPS_SERVERBIN, "$prefix/libexec/cups")
+ ;;
+ *)
+ # All others
+ CUPS_SERVERBIN='${prefix}/lib/cups'
+ AC_DEFINE_UNQUOTED(CUPS_SERVERBIN, "$prefix/lib/cups")
+ ;;
+esac
+
+AC_SUBST(CUPS_SERVERROOT)
+AC_SUBST(CUPS_SERVERBIN)
+AC_SUBST(CUPS_LOGDIR)
+AC_SUBST(CUPS_REQUESTS)
+
+dnl Set the CUPS_LOCALE directory...
+case "$uname" in
+ Linux* | FreeBSD* | NetBSD* | OpenBSD*)
+ CUPS_LOCALEDIR='${prefix}/share/locale'
+ AC_DEFINE_UNQUOTED(CUPS_LOCALEDIR, "$prefix/share/locale")
+ ;;
+
+ OSF1*)
+ CUPS_LOCALEDIR='${prefix}/lib/nls/msg'
+ AC_DEFINE_UNQUOTED(CUPS_LOCALEDIR, "$prefix/lib/nls/msg")
+ ;;
+
+ *)
+ # This is the standard System V location...
+ CUPS_LOCALEDIR='${prefix}/lib/locale'
+ AC_DEFINE_UNQUOTED(CUPS_LOCALEDIR, "$prefix/lib/locale")
+ ;;
+esac
+
+AC_SUBST(CUPS_LOCALEDIR)
+
+dnl Set the CUPS_DATADIR directory...
+CUPS_DATADIR='${datadir}/cups'
+AC_DEFINE_UNQUOTED(CUPS_DATADIR, "$prefix/share/cups")
+AC_SUBST(CUPS_DATADIR)
+
+dnl Set the CUPS_DOCDIR directory...
+CUPS_DOCDIR='${datadir}/doc/cups'
+AC_DEFINE_UNQUOTED(CUPS_DOCDIR, "$prefix/share/doc/cups")
+AC_SUBST(CUPS_DOCDIR)
+
+AC_OUTPUT(Makedefs)
+
+dnl
+dnl End of "$Id$".
+dnl
diff --git a/cups.dsw b/cups.dsw
new file mode 100644
index 000000000..2fc669868
--- /dev/null
+++ b/cups.dsw
@@ -0,0 +1,128 @@
+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: "cupsd"=.\scheduler\cupsd.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+ Begin Project Dependency
+ Project_Dep_Name cups
+ End Project Dependency
+}}}
+
+###############################################################################
+
+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.list b/cups.list
new file mode 100644
index 000000000..9c2520526
--- /dev/null
+++ b/cups.list
@@ -0,0 +1,441 @@
+#
+# "$Id: cups.list 1023 2000-04-19 21:28:50Z mike $"
+#
+# ESP Package Manager (EPM) file list for the Common UNIX Printing
+# System (CUPS).
+#
+# Copyright 1997-2000 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
+#
+
+%product Common UNIX Printing System
+%copyright 1993-2000 by Easy Software Products, All Rights Reserved.
+%vendor Easy Software Products
+%license LICENSE.txt
+%readme README.txt
+%version 1.1b3
+%incompat printpro
+
+%system all
+# Server files
+f 0500 root sys /usr/sbin/cupsd scheduler/cupsd
+f 0555 root sys /usr/lib/cups/backend/ipp backend/ipp
+l 0555 root sys /usr/lib/cups/backend/http ipp
+f 0555 root sys /usr/lib/cups/backend/lpd backend/lpd
+f 0555 root sys /usr/lib/cups/backend/parallel backend/parallel
+f 0555 root sys /usr/lib/cups/backend/serial backend/serial
+f 0555 root sys /usr/lib/cups/backend/socket backend/socket
+f 0555 root sys /usr/lib/cups/backend/usb backend/usb
+f 0555 root sys /usr/lib/cups/cgi-bin/admin.cgi cgi-bin/admin.cgi
+f 0555 root sys /usr/lib/cups/cgi-bin/classes.cgi cgi-bin/classes.cgi
+f 0555 root sys /usr/lib/cups/cgi-bin/jobs.cgi cgi-bin/jobs.cgi
+f 0555 root sys /usr/lib/cups/cgi-bin/printers.cgi cgi-bin/printers.cgi
+f 0555 root sys /usr/lib/cups/daemon/cups-lpd scheduler/cups-lpd
+f 0555 root sys /usr/lib/cups/daemon/cups-polld scheduler/cups-polld
+f 0555 root sys /usr/lib/cups/filter/pstoraster pstoraster/pstoraster
+l 0555 root sys /usr/lib/cups/filter/pdftops pstoraster
+f 0555 root sys /usr/lib/cups/filter/imagetops filter/imagetops
+f 0555 root sys /usr/lib/cups/filter/pstops filter/pstops
+f 0555 root sys /usr/lib/cups/filter/texttops filter/texttops
+f 0555 root sys /usr/lib/cups/filter/rastertoepson filter/rastertoepson
+f 0555 root sys /usr/lib/cups/filter/rastertohp filter/rastertohp
+f 0555 root sys /usr/lib/cups/filter/hpgltops filter/hpgltops
+f 0555 root sys /usr/lib/cups/filter/imagetoraster filter/imagetoraster
+
+# Admin commands
+l 0555 root sys /usr/bin/disable /usr/sbin/accept
+l 0555 root sys /usr/bin/enable /usr/sbin/accept
+l 0555 root sys /usr/lib/accept /usr/sbin/accept
+l 0555 root sys /usr/lib/lpadmin /usr/sbin/lpadmin
+l 0555 root sys /usr/lib/reject accept
+f 0555 root sys /usr/sbin/accept systemv/accept
+f 0555 root sys /usr/sbin/lpadmin systemv/lpadmin
+f 0555 root sys /usr/sbin/lpc berkeley/lpc
+l 0555 root sys /usr/sbin/reject accept
+
+# User commands
+f 0555 root sys /usr/bin/cancel systemv/cancel
+f 0555 root sys /usr/bin/lp systemv/lp
+f 0555 root sys /usr/bin/lpoptions systemv/lpoptions
+f 4555 root sys /usr/bin/lppasswd systemv/lppasswd
+f 0555 root sys /usr/bin/lpq berkeley/lpq
+f 0555 root sys /usr/bin/lpr berkeley/lpr
+f 0555 root sys /usr/bin/lprm berkeley/lprm
+f 0555 root sys /usr/bin/lpstat systemv/lpstat
+
+# DSOs
+%system hpux
+f 0555 root sys /usr/lib/libcups.sl.2 cups/libcups.sl.2
+l 0555 root sys /usr/lib/libcups.sl libcups.sl.2
+f 0555 root sys /usr/lib/libcupsimage.sl.2 filter/libcupsimage.sl.2
+l 0555 root sys /usr/lib/libcupsimage.sl libcupsimage.sl.2
+%system irix-6.5
+f 0555 root sys /usr/lib32/libcups.so.2 cups/libcups.so.2
+l 0555 root sys /usr/lib32/libcups.so libcups.so.2
+f 0555 root sys /usr/lib32/libcupsimage.so.2 filter/libcupsimage.so.2
+l 0555 root sys /usr/lib32/libcupsimage.so libcupsimage.so.2
+%system !irix-6.5 !hpux
+f 0555 root sys /usr/lib/libcups.so.2 cups/libcups.so.2
+l 0555 root sys /usr/lib/libcups.so libcups.so.2
+f 0555 root sys /usr/lib/libcupsimage.so.2 filter/libcupsimage.so.2
+l 0555 root sys /usr/lib/libcupsimage.so libcupsimage.so.2
+%system all
+
+# Directories
+d 0711 root sys /etc/cups/certs
+d 0755 root sys /etc/cups/interfaces
+d 0755 root sys /etc/cups/ppd
+d 0755 root sys /var/log/cups
+d 0755 root sys /var/spool/cups
+
+# Data files
+%system linux
+f 0444 root sys /usr/share/locale/C/cups_C locale/C/cups_C
+f 0444 root sys /usr/share/locale/de/cups_de locale/de/cups_de
+f 0444 root sys /usr/share/locale/en/cups_en locale/en/cups_en
+f 0444 root sys /usr/share/locale/es/cups_es locale/es/cups_es
+f 0444 root sys /usr/share/locale/fr/cups_fr locale/fr/cups_fr
+f 0444 root sys /usr/share/locale/it/cups_it locale/it/cups_it
+
+%system dunix
+f 0444 root sys /usr/lib/nls/msg/C/cups_C locale/C/cups_C
+f 0444 root sys /usr/lib/nls/msg/de/cups_de locale/de/cups_de
+f 0444 root sys /usr/lib/nls/msg/en/cups_en locale/en/cups_en
+f 0444 root sys /usr/lib/nls/msg/es/cups_es locale/es/cups_es
+f 0444 root sys /usr/lib/nls/msg/fr/cups_fr locale/fr/cups_fr
+f 0444 root sys /usr/lib/nls/msg/it/cups_it locale/it/cups_it
+
+%system !linux dunix
+f 0444 root sys /usr/lib/locale/C/cups_C locale/C/cups_C
+f 0444 root sys /usr/lib/locale/de/cups_de locale/de/cups_de
+f 0444 root sys /usr/lib/locale/en/cups_en locale/en/cups_en
+f 0444 root sys /usr/lib/locale/es/cups_es locale/es/cups_es
+f 0444 root sys /usr/lib/locale/fr/cups_fr locale/fr/cups_fr
+f 0444 root sys /usr/lib/locale/it/cups_it locale/it/cups_it
+
+%system all
+f 0444 root sys /usr/share/cups/charsets/cp874 data/cp874
+f 0444 root sys /usr/share/cups/charsets/cp1250 data/cp1250
+f 0444 root sys /usr/share/cups/charsets/cp1251 data/cp1251
+f 0444 root sys /usr/share/cups/charsets/cp1252 data/cp1252
+f 0444 root sys /usr/share/cups/charsets/cp1253 data/cp1253
+f 0444 root sys /usr/share/cups/charsets/cp1254 data/cp1254
+f 0444 root sys /usr/share/cups/charsets/cp1255 data/cp1255
+f 0444 root sys /usr/share/cups/charsets/cp1256 data/cp1256
+f 0444 root sys /usr/share/cups/charsets/cp1257 data/cp1257
+f 0444 root sys /usr/share/cups/charsets/cp1258 data/cp1258
+f 0444 root sys /usr/share/cups/charsets/iso-8859-1 data/iso-8859-1
+f 0444 root sys /usr/share/cups/charsets/iso-8859-14 data/iso-8859-14
+f 0444 root sys /usr/share/cups/charsets/iso-8859-15 data/iso-8859-15
+f 0444 root sys /usr/share/cups/charsets/iso-8859-2 data/iso-8859-2
+f 0444 root sys /usr/share/cups/charsets/iso-8859-3 data/iso-8859-3
+f 0444 root sys /usr/share/cups/charsets/iso-8859-4 data/iso-8859-4
+f 0444 root sys /usr/share/cups/charsets/iso-8859-5 data/iso-8859-5
+f 0444 root sys /usr/share/cups/charsets/iso-8859-6 data/iso-8859-6
+f 0444 root sys /usr/share/cups/charsets/iso-8859-7 data/iso-8859-7
+f 0444 root sys /usr/share/cups/charsets/iso-8859-8 data/iso-8859-8
+f 0444 root sys /usr/share/cups/charsets/iso-8859-9 data/iso-8859-9
+f 0444 root sys /usr/share/cups/charsets/utf-8 data/utf-8
+
+f 0444 root sys /usr/share/cups/data/HPGLprolog data/HPGLprolog
+f 0444 root sys /usr/share/cups/data/psglyphs data/psglyphs
+f 0444 root sys /usr/share/cups/data/testprint.ps data/testprint.ps
+f 0444 root sys /usr/share/cups/fonts/AvantGarde-Book fonts/AvantGarde-Book
+f 0444 root sys /usr/share/cups/fonts/AvantGarde-BookOblique fonts/AvantGarde-BookOblique
+f 0444 root sys /usr/share/cups/fonts/AvantGarde-Demi fonts/AvantGarde-Demi
+f 0444 root sys /usr/share/cups/fonts/AvantGarde-DemiOblique fonts/AvantGarde-DemiOblique
+f 0444 root sys /usr/share/cups/fonts/Bookman-Demi fonts/Bookman-Demi
+f 0444 root sys /usr/share/cups/fonts/Bookman-DemiItalic fonts/Bookman-DemiItalic
+f 0444 root sys /usr/share/cups/fonts/Bookman-Light fonts/Bookman-Light
+f 0444 root sys /usr/share/cups/fonts/Bookman-LightItalic fonts/Bookman-LightItalic
+f 0444 root sys /usr/share/cups/fonts/Charter-Bold fonts/Charter-Bold
+f 0444 root sys /usr/share/cups/fonts/Charter-BoldItalic fonts/Charter-BoldItalic
+f 0444 root sys /usr/share/cups/fonts/Charter-Italic fonts/Charter-Italic
+f 0444 root sys /usr/share/cups/fonts/Charter-Roman fonts/Charter-Roman
+f 0444 root sys /usr/share/cups/fonts/Courier fonts/Courier
+f 0444 root sys /usr/share/cups/fonts/Courier-Bold fonts/Courier-Bold
+f 0444 root sys /usr/share/cups/fonts/Courier-BoldOblique fonts/Courier-BoldOblique
+f 0444 root sys /usr/share/cups/fonts/Courier-Oblique fonts/Courier-Oblique
+f 0444 root sys /usr/share/cups/fonts/Helvetica fonts/Helvetica
+f 0444 root sys /usr/share/cups/fonts/Helvetica-Bold fonts/Helvetica-Bold
+f 0444 root sys /usr/share/cups/fonts/Helvetica-BoldOblique fonts/Helvetica-BoldOblique
+f 0444 root sys /usr/share/cups/fonts/Helvetica-Narrow fonts/Helvetica-Narrow
+f 0444 root sys /usr/share/cups/fonts/Helvetica-Narrow-Bold fonts/Helvetica-Narrow-Bold
+f 0444 root sys /usr/share/cups/fonts/Helvetica-Narrow-BoldOblique fonts/Helvetica-Narrow-BoldOblique
+f 0444 root sys /usr/share/cups/fonts/Helvetica-Narrow-Oblique fonts/Helvetica-Narrow-Oblique
+f 0444 root sys /usr/share/cups/fonts/Helvetica-Oblique fonts/Helvetica-Oblique
+f 0444 root sys /usr/share/cups/fonts/NewCenturySchlbk-Bold fonts/NewCenturySchlbk-Bold
+f 0444 root sys /usr/share/cups/fonts/NewCenturySchlbk-BoldItalic fonts/NewCenturySchlbk-BoldItalic
+f 0444 root sys /usr/share/cups/fonts/NewCenturySchlbk-Italic fonts/NewCenturySchlbk-Italic
+f 0444 root sys /usr/share/cups/fonts/NewCenturySchlbk-Roman fonts/NewCenturySchlbk-Roman
+f 0444 root sys /usr/share/cups/fonts/Palatino-Bold fonts/Palatino-Bold
+f 0444 root sys /usr/share/cups/fonts/Palatino-BoldItalic fonts/Palatino-BoldItalic
+f 0444 root sys /usr/share/cups/fonts/Palatino-Italic fonts/Palatino-Italic
+f 0444 root sys /usr/share/cups/fonts/Palatino-Roman fonts/Palatino-Roman
+f 0444 root sys /usr/share/cups/fonts/Symbol fonts/Symbol
+f 0444 root sys /usr/share/cups/fonts/Times-Bold fonts/Times-Bold
+f 0444 root sys /usr/share/cups/fonts/Times-BoldItalic fonts/Times-BoldItalic
+f 0444 root sys /usr/share/cups/fonts/Times-Italic fonts/Times-Italic
+f 0444 root sys /usr/share/cups/fonts/Times-Roman fonts/Times-Roman
+f 0444 root sys /usr/share/cups/fonts/Utopia-Bold fonts/Utopia-Bold
+f 0444 root sys /usr/share/cups/fonts/Utopia-BoldItalic fonts/Utopia-BoldItalic
+f 0444 root sys /usr/share/cups/fonts/Utopia-Italic fonts/Utopia-Italic
+f 0444 root sys /usr/share/cups/fonts/Utopia-Regular fonts/Utopia-Regular
+f 0444 root sys /usr/share/cups/fonts/ZapfChancery-MediumItalic fonts/ZapfChancery-MediumItalic
+f 0444 root sys /usr/share/cups/fonts/ZapfDingbats fonts/ZapfDingbats
+f 0444 root sys /usr/share/cups/pstoraster/Fontmap pstoraster/Fontmap
+f 0444 root sys /usr/share/cups/pstoraster/gs_btokn.ps pstoraster/gs_btokn.ps
+f 0444 root sys /usr/share/cups/pstoraster/gs_ccfnt.ps pstoraster/gs_ccfnt.ps
+f 0444 root sys /usr/share/cups/pstoraster/gs_cff.ps pstoraster/gs_cff.ps
+f 0444 root sys /usr/share/cups/pstoraster/gs_cidfn.ps pstoraster/gs_cidfn.ps
+f 0444 root sys /usr/share/cups/pstoraster/gs_cmap.ps pstoraster/gs_cmap.ps
+f 0444 root sys /usr/share/cups/pstoraster/gs_cmdl.ps pstoraster/gs_cmdl.ps
+f 0444 root sys /usr/share/cups/pstoraster/gs_dbt_e.ps pstoraster/gs_dbt_e.ps
+f 0444 root sys /usr/share/cups/pstoraster/gs_diskf.ps pstoraster/gs_diskf.ps
+f 0444 root sys /usr/share/cups/pstoraster/gs_dpnxt.ps pstoraster/gs_dpnxt.ps
+f 0444 root sys /usr/share/cups/pstoraster/gs_dps1.ps pstoraster/gs_dps1.ps
+f 0444 root sys /usr/share/cups/pstoraster/gs_dps2.ps pstoraster/gs_dps2.ps
+f 0444 root sys /usr/share/cups/pstoraster/gs_dps.ps pstoraster/gs_dps.ps
+f 0444 root sys /usr/share/cups/pstoraster/gs_epsf.ps pstoraster/gs_epsf.ps
+f 0444 root sys /usr/share/cups/pstoraster/gs_fform.ps pstoraster/gs_fform.ps
+f 0444 root sys /usr/share/cups/pstoraster/gs_fonts.ps pstoraster/gs_fonts.ps
+f 0444 root sys /usr/share/cups/pstoraster/gs_init.ps pstoraster/gs_init.ps
+f 0444 root sys /usr/share/cups/pstoraster/gs_iso_e.ps pstoraster/gs_iso_e.ps
+f 0444 root sys /usr/share/cups/pstoraster/gs_kanji.ps pstoraster/gs_kanji.ps
+f 0444 root sys /usr/share/cups/pstoraster/gs_ksb_e.ps pstoraster/gs_ksb_e.ps
+f 0444 root sys /usr/share/cups/pstoraster/gs_l2img.ps pstoraster/gs_l2img.ps
+f 0444 root sys /usr/share/cups/pstoraster/gs_lev2.ps pstoraster/gs_lev2.ps
+f 0444 root sys /usr/share/cups/pstoraster/gs_ll3.ps pstoraster/gs_ll3.ps
+f 0444 root sys /usr/share/cups/pstoraster/gs_mex_e.ps pstoraster/gs_mex_e.ps
+f 0444 root sys /usr/share/cups/pstoraster/gs_mro_e.ps pstoraster/gs_mro_e.ps
+f 0444 root sys /usr/share/cups/pstoraster/gs_pdfwr.ps pstoraster/gs_pdfwr.ps
+f 0444 root sys /usr/share/cups/pstoraster/gs_pdf_e.ps pstoraster/gs_pdf_e.ps
+f 0444 root sys /usr/share/cups/pstoraster/gs_pfile.ps pstoraster/gs_pfile.ps
+f 0444 root sys /usr/share/cups/pstoraster/gs_res.ps pstoraster/gs_res.ps
+f 0444 root sys /usr/share/cups/pstoraster/gs_setpd.ps pstoraster/gs_setpd.ps
+f 0444 root sys /usr/share/cups/pstoraster/gs_statd.ps pstoraster/gs_statd.ps
+f 0444 root sys /usr/share/cups/pstoraster/gs_std_e.ps pstoraster/gs_std_e.ps
+f 0444 root sys /usr/share/cups/pstoraster/gs_sym_e.ps pstoraster/gs_sym_e.ps
+f 0444 root sys /usr/share/cups/pstoraster/gs_ttf.ps pstoraster/gs_ttf.ps
+f 0444 root sys /usr/share/cups/pstoraster/gs_typ32.ps pstoraster/gs_typ32.ps
+f 0444 root sys /usr/share/cups/pstoraster/gs_typ42.ps pstoraster/gs_typ42.ps
+f 0444 root sys /usr/share/cups/pstoraster/gs_type1.ps pstoraster/gs_type1.ps
+f 0444 root sys /usr/share/cups/pstoraster/gs_wan_e.ps pstoraster/gs_wan_e.ps
+f 0444 root sys /usr/share/cups/pstoraster/gs_wl1_e.ps pstoraster/gs_wl1_e.ps
+f 0444 root sys /usr/share/cups/pstoraster/gs_wl2_e.ps pstoraster/gs_wl2_e.ps
+f 0444 root sys /usr/share/cups/pstoraster/gs_wl5_e.ps pstoraster/gs_wl5_e.ps
+f 0444 root sys /usr/share/cups/pstoraster/pdf2dsc.ps pstoraster/pdf2dsc.ps
+f 0444 root sys /usr/share/cups/pstoraster/pdf_base.ps pstoraster/pdf_base.ps
+f 0444 root sys /usr/share/cups/pstoraster/pdf_draw.ps pstoraster/pdf_draw.ps
+f 0444 root sys /usr/share/cups/pstoraster/pdf_font.ps pstoraster/pdf_font.ps
+f 0444 root sys /usr/share/cups/pstoraster/pdf_main.ps pstoraster/pdf_main.ps
+f 0444 root sys /usr/share/cups/pstoraster/pdf_ops.ps pstoraster/pdf_ops.ps
+f 0444 root sys /usr/share/cups/pstoraster/pdf_sec.ps pstoraster/pdf_sec.ps
+f 0444 root sys /usr/share/cups/model/deskjet.ppd ppd/deskjet.ppd
+f 0444 root sys /usr/share/cups/model/laserjet.ppd ppd/laserjet.ppd
+f 0444 root sys /usr/share/cups/model/stcolor.ppd ppd/stcolor.ppd
+f 0444 root sys /usr/share/cups/model/stphoto.ppd ppd/stphoto.ppd
+
+f 0444 root sys /usr/share/cups/templates/add-class.tmpl templates/add-class.tmpl
+f 0444 root sys /usr/share/cups/templates/add-printer.tmpl templates/add-printer.tmpl
+f 0444 root sys /usr/share/cups/templates/admin-op.tmpl templates/admin-op.tmpl
+f 0444 root sys /usr/share/cups/templates/admin.tmpl templates/admin.tmpl
+f 0444 root sys /usr/share/cups/templates/choose-device.tmpl templates/choose-device.tmpl
+f 0444 root sys /usr/share/cups/templates/choose-make.tmpl templates/choose-make.tmpl
+f 0444 root sys /usr/share/cups/templates/choose-members.tmpl templates/choose-members.tmpl
+f 0444 root sys /usr/share/cups/templates/choose-model.tmpl templates/choose-model.tmpl
+f 0444 root sys /usr/share/cups/templates/choose-serial.tmpl templates/choose-serial.tmpl
+f 0444 root sys /usr/share/cups/templates/choose-uri.tmpl templates/choose-uri.tmpl
+f 0444 root sys /usr/share/cups/templates/class-added.tmpl templates/class-added.tmpl
+f 0444 root sys /usr/share/cups/templates/class-confirm.tmpl templates/class-confirm.tmpl
+f 0444 root sys /usr/share/cups/templates/class-deleted.tmpl templates/class-deleted.tmpl
+f 0444 root sys /usr/share/cups/templates/class-modified.tmpl templates/class-modified.tmpl
+f 0444 root sys /usr/share/cups/templates/classes.tmpl templates/classes.tmpl
+f 0444 root sys /usr/share/cups/templates/config-printer.tmpl templates/config-printer.tmpl
+f 0444 root sys /usr/share/cups/templates/config-printer2.tmpl templates/config-printer2.tmpl
+f 0444 root sys /usr/share/cups/templates/error.tmpl templates/error.tmpl
+f 0444 root sys /usr/share/cups/templates/header.tmpl templates/header.tmpl
+f 0444 root sys /usr/share/cups/templates/job-cancel.tmpl templates/job-cancel.tmpl
+f 0444 root sys /usr/share/cups/templates/job-hold.tmpl templates/job-hold.tmpl
+f 0444 root sys /usr/share/cups/templates/job-release.tmpl templates/job-release.tmpl
+f 0444 root sys /usr/share/cups/templates/jobs.tmpl templates/jobs.tmpl
+f 0444 root sys /usr/share/cups/templates/modify-class.tmpl templates/modify-class.tmpl
+f 0444 root sys /usr/share/cups/templates/modify-printer.tmpl templates/modify-printer.tmpl
+f 0444 root sys /usr/share/cups/templates/option-boolean.tmpl templates/option-boolean.tmpl
+f 0444 root sys /usr/share/cups/templates/option-header.tmpl templates/option-header.tmpl
+f 0444 root sys /usr/share/cups/templates/option-pickmany.tmpl templates/option-pickmany.tmpl
+f 0444 root sys /usr/share/cups/templates/option-pickone.tmpl templates/option-pickone.tmpl
+f 0444 root sys /usr/share/cups/templates/option-trailer.tmpl templates/option-trailer.tmpl
+f 0444 root sys /usr/share/cups/templates/printer-accept.tmpl templates/printer-accept.tmpl
+f 0444 root sys /usr/share/cups/templates/printer-added.tmpl templates/printer-added.tmpl
+f 0444 root sys /usr/share/cups/templates/printer-configured.tmpl templates/printer-configured.tmpl
+f 0444 root sys /usr/share/cups/templates/printer-confirm.tmpl templates/printer-confirm.tmpl
+f 0444 root sys /usr/share/cups/templates/printer-deleted.tmpl templates/printer-deleted.tmpl
+f 0444 root sys /usr/share/cups/templates/printer-modified.tmpl templates/printer-modified.tmpl
+f 0444 root sys /usr/share/cups/templates/printer-reject.tmpl templates/printer-reject.tmpl
+f 0444 root sys /usr/share/cups/templates/printer-start.tmpl templates/printer-start.tmpl
+f 0444 root sys /usr/share/cups/templates/printer-stop.tmpl templates/printer-stop.tmpl
+f 0444 root sys /usr/share/cups/templates/printers.tmpl templates/printers.tmpl
+f 0444 root sys /usr/share/cups/templates/test-page.tmpl templates/test-page.tmpl
+f 0444 root sys /usr/share/cups/templates/trailer.tmpl templates/trailer.tmpl
+
+# Config files
+c 0644 root sys /etc/cups/classes.conf conf/classes.conf
+c 0644 root sys /etc/cups/cupsd.conf conf/cupsd.conf
+f 0644 root sys /etc/cups/mime.convs conf/mime.convs
+f 0644 root sys /etc/cups/mime.types conf/mime.types
+c 0644 root sys /etc/cups/printers.conf conf/printers.conf
+
+# Dummy printcap file for Digital UNIX and Linux...
+%system dunix linux
+%format !rpm
+f 0644 root sys /etc/printcap conf/printcap
+%system all
+%format all
+
+# Developer files
+f 0444 root sys /usr/include/cups/cups.h cups/cups.h
+f 0444 root sys /usr/include/cups/http.h cups/http.h
+f 0444 root sys /usr/include/cups/image.h filter/image.h
+f 0444 root sys /usr/include/cups/ipp.h cups/ipp.h
+f 0444 root sys /usr/include/cups/language.h cups/language.h
+f 0444 root sys /usr/include/cups/md5.h cups/md5.h
+f 0444 root sys /usr/include/cups/ppd.h cups/ppd.h
+f 0444 root sys /usr/include/cups/raster.h filter/raster.h
+
+%system irix-6.5
+f 0444 root sys /usr/lib32/libcups.a cups/libcups.a
+%system !irix-6.5
+f 0444 root sys /usr/lib/libcups.a cups/libcups.a
+%system all
+
+# Documentation files
+f 0444 root sys /usr/share/doc/cups/cups.css doc/cups.css
+f 0444 root sys /usr/share/doc/cups/documentation.html doc/documentation.html
+f 0444 root sys /usr/share/doc/cups/index.html doc/index.html
+
+f 0444 root sys /usr/share/doc/cups/images/accept-jobs.gif doc/images/accept-jobs.gif
+f 0444 root sys /usr/share/doc/cups/images/add-class.gif doc/images/add-class.gif
+f 0444 root sys /usr/share/doc/cups/images/add-printer.gif doc/images/add-printer.gif
+f 0444 root sys /usr/share/doc/cups/images/cancel-job.gif doc/images/cancel-job.gif
+f 0444 root sys /usr/share/doc/cups/images/cancel-jobs.gif doc/images/cancel-jobs.gif
+f 0444 root sys /usr/share/doc/cups/images/cancel.gif doc/images/cancel.gif
+f 0444 root sys /usr/share/doc/cups/images/classes.gif doc/images/classes.gif
+f 0444 root sys /usr/share/doc/cups/images/continue.gif doc/images/continue.gif
+f 0444 root sys /usr/share/doc/cups/images/config-printer.gif doc/images/config-printer.gif
+f 0444 root sys /usr/share/doc/cups/images/cups-bar.gif doc/images/cups-bar.gif
+f 0444 root sys /usr/share/doc/cups/images/cups-block-diagram.gif doc/images/cups-block-diagram.gif
+f 0444 root sys /usr/share/doc/cups/images/cups-large.gif doc/images/cups-large.gif
+f 0444 root sys /usr/share/doc/cups/images/cups-medium.gif doc/images/cups-medium.gif
+f 0444 root sys /usr/share/doc/cups/images/cups-small.gif doc/images/cups-small.gif
+f 0444 root sys /usr/share/doc/cups/images/delete-class.gif doc/images/delete-class.gif
+f 0444 root sys /usr/share/doc/cups/images/delete-printer.gif doc/images/delete-printer.gif
+f 0444 root sys /usr/share/doc/cups/images/hold-job.gif doc/images/hold-job.gif
+f 0444 root sys /usr/share/doc/cups/images/left.gif doc/images/left.gif
+f 0444 root sys /usr/share/doc/cups/images/logo.gif doc/images/logo.gif
+f 0444 root sys /usr/share/doc/cups/images/manage-classes.gif doc/images/manage-classes.gif
+f 0444 root sys /usr/share/doc/cups/images/manage-jobs.gif doc/images/manage-jobs.gif
+f 0444 root sys /usr/share/doc/cups/images/manage-printers.gif doc/images/manage-printers.gif
+f 0444 root sys /usr/share/doc/cups/images/modify-class.gif doc/images/modify-class.gif
+f 0444 root sys /usr/share/doc/cups/images/modify-printer.gif doc/images/modify-printer.gif
+f 0444 root sys /usr/share/doc/cups/images/navbar.gif doc/images/navbar.gif
+f 0444 root sys /usr/share/doc/cups/images/print-test-page.gif doc/images/print-test-page.gif
+f 0444 root sys /usr/share/doc/cups/images/printer-idle.gif doc/images/printer-idle.gif
+f 0444 root sys /usr/share/doc/cups/images/printer-processing.gif doc/images/printer-processing.gif
+f 0444 root sys /usr/share/doc/cups/images/printer-stopped.gif doc/images/printer-stopped.gif
+f 0444 root sys /usr/share/doc/cups/images/reject-jobs.gif doc/images/reject-jobs.gif
+f 0444 root sys /usr/share/doc/cups/images/release-job.gif doc/images/release-job.gif
+f 0444 root sys /usr/share/doc/cups/images/restart-job.gif doc/images/restart-job.gif
+f 0444 root sys /usr/share/doc/cups/images/right.gif doc/images/right.gif
+f 0444 root sys /usr/share/doc/cups/images/show-active.gif doc/images/show-active.gif
+f 0444 root sys /usr/share/doc/cups/images/show-completed.gif doc/images/show-completed.gif
+f 0444 root sys /usr/share/doc/cups/images/start-class.gif doc/images/start-class.gif
+f 0444 root sys /usr/share/doc/cups/images/start-printer.gif doc/images/start-printer.gif
+f 0444 root sys /usr/share/doc/cups/images/stop-class.gif doc/images/stop-class.gif
+f 0444 root sys /usr/share/doc/cups/images/stop-printer.gif doc/images/stop-printer.gif
+
+f 0444 root sys /usr/share/doc/cups/cmp.html doc/cmp.html
+f 0444 root sys /usr/share/doc/cups/cmp.pdf doc/cmp.pdf
+f 0444 root sys /usr/share/doc/cups/cupsdoc.css doc/cupsdoc.css
+f 0444 root sys /usr/share/doc/cups/idd.html doc/idd.html
+f 0444 root sys /usr/share/doc/cups/idd.pdf doc/idd.pdf
+f 0444 root sys /usr/share/doc/cups/overview.html doc/overview.html
+f 0444 root sys /usr/share/doc/cups/overview.pdf doc/overview.pdf
+f 0444 root sys /usr/share/doc/cups/sam.html doc/sam.html
+f 0444 root sys /usr/share/doc/cups/sam.pdf doc/sam.pdf
+f 0444 root sys /usr/share/doc/cups/sdd.html doc/sdd.html
+f 0444 root sys /usr/share/doc/cups/sdd.pdf doc/sdd.pdf
+f 0444 root sys /usr/share/doc/cups/spm.html doc/spm.html
+f 0444 root sys /usr/share/doc/cups/spm.pdf doc/spm.pdf
+f 0444 root sys /usr/share/doc/cups/ssr.html doc/ssr.html
+f 0444 root sys /usr/share/doc/cups/ssr.pdf doc/ssr.pdf
+f 0444 root sys /usr/share/doc/cups/sum.html doc/sum.html
+f 0444 root sys /usr/share/doc/cups/sum.pdf doc/sum.pdf
+
+# Man pages
+%system irix
+f 0444 root sys /usr/share/catman/a_man/cat1/accept.1 man/accept.8
+l 0444 root sys /usr/share/catman/a_man/cat1/reject.1 accept.1
+f 0444 root sys /usr/share/catman/u_man/cat1/backend.1 man/backend.1
+f 0444 root sys /usr/share/catman/u_man/cat5/classes.conf.5 man/classes.conf.5
+f 0444 root sys /usr/share/catman/u_man/cat5/cupsd.conf.5 man/cupsd.conf.5
+f 0444 root sys /usr/share/catman/a_man/cat1/cupsd.1 man/cupsd.8
+f 0444 root sys /usr/share/catman/a_man/cat1/enable.1 man/enable.8
+l 0444 root sys /usr/share/catman/a_man/cat1/disable.1 enable.1
+f 0444 root sys /usr/share/catman/u_man/cat1/filter.1 man/filter.1
+f 0444 root sys /usr/share/catman/a_man/cat1/lpadmin.1 man/lpadmin.8
+f 0444 root sys /usr/share/catman/a_man/cat1/lpc.1 man/lpc.8
+f 0444 root sys /usr/share/catman/u_man/cat1/lpq.1 man/lpq.1
+f 0444 root sys /usr/share/catman/u_man/cat1/lprm.1 man/lprm.1
+f 0444 root sys /usr/share/catman/u_man/cat1/lpr.1 man/lpr.1
+f 0444 root sys /usr/share/catman/u_man/cat1/lpstat.1 man/lpstat.1
+f 0444 root sys /usr/share/catman/u_man/cat1/lp.1 man/lp.1
+l 0444 root sys /usr/share/catman/u_man/cat1/cancel.1 lp.1
+f 0444 root sys /usr/share/catman/u_man/cat5/mime.convs.5 man/mime.convs.5
+f 0444 root sys /usr/share/catman/u_man/cat5/mime.types.5 man/mime.types.5
+f 0444 root sys /usr/share/catman/u_man/cat5/printers.conf.5 man/printers.conf.5
+%system !irix
+f 0444 root sys /usr/man/man8/accept.8 man/accept.man
+l 0444 root sys /usr/man/man8/reject.8 accept.man
+f 0444 root sys /usr/man/man1/backend.1 man/backend.man
+f 0444 root sys /usr/man/man1/classes.conf.5 man/classes.conf.man
+f 0444 root sys /usr/man/man8/cupsd.8 man/cupsd.man
+f 0444 root sys /usr/man/man5/cupsd.conf.5 man/cupsd.conf.man
+f 0444 root sys /usr/man/man8/enable.8 man/enable.man
+f 0444 root sys /usr/man/man8/esplicense.8 man/esplicense.man
+l 0444 root sys /usr/man/man8/disable.8 enable.man
+f 0444 root sys /usr/man/man1/filter.1 man/filter.man
+f 0444 root sys /usr/man/man8/lpadmin.8 man/lpadmin.man
+f 0444 root sys /usr/man/man8/lpc.8 man/lpc.man
+f 0444 root sys /usr/man/man1/lpq.1 man/lpq.man
+f 0444 root sys /usr/man/man1/lprm.1 man/lprm.man
+f 0444 root sys /usr/man/man1/lpr.1 man/lpr.man
+f 0444 root sys /usr/man/man1/lpstat.1 man/lpstat.man
+f 0444 root sys /usr/man/man1/lp.1 man/lp.man
+l 0444 root sys /usr/man/man1/cancel.1 lp.man
+f 0444 root sys /usr/man/man5/mime.convs.5 man/mime.convs.man
+f 0444 root sys /usr/man/man5/mime.types.5 man/mime.types.man
+f 0444 root sys /usr/man/man5/printers.conf.5 man/printers.conf.man
+
+# Startup script
+%system all
+i 0555 root sys cups cups.sh
+
+#
+# End of "$Id: cups.list 1023 2000-04-19 21:28:50Z mike $".
+#
diff --git a/cups.sh b/cups.sh
new file mode 100755
index 000000000..25edd65fc
--- /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: 02345 99 00
+# description: Startup/shutdown script for the Common UNIX \
+# Printing System (CUPS).
+#
+# Copyright 1997-2000 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
+ $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.spec b/cups.spec
new file mode 100644
index 000000000..d15d13204
--- /dev/null
+++ b/cups.spec
@@ -0,0 +1,138 @@
+#
+# "$Id: cups.spec 1007 2000-04-09 23:09:10Z mike $"
+#
+# RPM "spec" file for the Common UNIX Printing System (CUPS).
+#
+# Original version by Jason McMullan <jmcc@ontv.com>.
+#
+# Copyright 1999-2000 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
+#
+
+Summary: Common Unix Printing System
+Name: cups
+Version: 1.1b3
+Release: 0
+Copyright: GPL
+Group: System Environment/Daemons
+Source: ftp://ftp.easysw.com/pub/cups/beta/cups-1.1b3-source.tar.gz
+Url: http://www.cups.org
+Packager: Michael Sweet <mike@easysw.com>
+Vendor: Easy Software Products
+# use buildroot so as not to disturb the version already installed
+BuildRoot: /tmp/rpmbuild
+Conflicts: lpr
+
+%package devel
+Summary: Common Unix Printing System - development environment
+Group: Development/Libraries
+
+%description
+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.
+
+%description devel
+The Common UNIX Printing System provides a portable printing layer for
+UNIX® operating systems. This is the development package for creating
+additional printer drivers, and other CUPS services.
+
+%prep
+%setup
+
+%build
+./configure
+
+# If we got this far, all prerequisite libraries must be here.
+make
+
+%install
+# these lines just make sure the directory structure in the
+# RPM_BUILD_ROOT exists
+rm -rf $RPM_BUILD_ROOT
+mkdir -p $RPM_BUILD_ROOT/etc/rc.d/init.d
+
+make prefix=$RPM_BUILD_ROOT/usr LOGDIR=$RPM_BUILD_ROOT/var/log/cups \
+ REQUESTS=$RPM_BUILD_ROOT/var/spool/cups \
+ SERVERROOT=$RPM_BUILD_ROOT/etc/cups install
+
+install -m 755 -o root -g root cups.sh $RPM_BUILD_ROOT/etc/rc.d/init.d/cups
+
+%post
+/sbin/chkconfig --add cups
+/sbin/chkconfig cups on
+/etc/rc.d/init.d/cups start
+
+%preun
+/etc/rc.d/init.d/cups stop
+/sbin/chkconfig --del cups
+
+%clean
+rm -rf $RPM_BUILD_ROOT
+
+%files
+%defattr(-,root,root)
+/etc/rc.d/init.d/cups
+%config /etc/cups/*
+%dir /etc/cups/certs
+%dir /etc/cups/interfaces
+%dir /etc/cups/ppd
+/usr/bin/*
+%%attr(4555,root,root) /usr/bin/lppasswd
+/usr/lib/*.so*
+/usr/man/*
+/usr/sbin/*
+%dir /usr/share/cups
+/usr/share/cups/*
+%dir /usr/share/doc/cups
+/usr/share/doc/cups/*
+%dir /usr/share/locale
+%dir /usr/share/locale/C
+%dir /usr/share/locale/de
+%dir /usr/share/locale/en
+%dir /usr/share/locale/es
+%dir /usr/share/locale/fr
+%dir /usr/share/locale/it
+/usr/share/locale/C/*
+/usr/share/locale/de/*
+/usr/share/locale/en/*
+/usr/share/locale/es/*
+/usr/share/locale/fr/*
+/usr/share/locale/it/*
+%dir /usr/lib/cups
+%dir /usr/lib/cups/backend
+%dir /usr/lib/cups/cgi-bin
+%dir /usr/lib/cups/daemon
+%dir /usr/lib/cups/filter
+/usr/lib/cups/backend/*
+/usr/lib/cups/cgi-bin/*
+/usr/lib/cups/daemon/*
+/usr/lib/cups/filter/*
+%dir /var/spool/cups
+%dir /var/log/cups
+
+%files devel
+%dir /usr/include/cups
+/usr/include/cups/*
+%dir /usr/lib
+/usr/lib/*.a
+
+#
+# End of "$Id: cups.spec 1007 2000-04-09 23:09:10Z mike $".
+#
diff --git a/cups/Makefile b/cups/Makefile
new file mode 100644
index 000000000..9e95a1faf
--- /dev/null
+++ b/cups/Makefile
@@ -0,0 +1,153 @@
+#
+# "$Id$"
+#
+# Support library Makefile for the Common UNIX Printing System (CUPS).
+#
+# Copyright 1997-2000 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 = dest.o emit.o http.o ipp.o language.o mark.o md5.o options.o \
+ page.o ppd.o snprintf.o string.o usersys.o util.o
+OBJS = $(LIBOBJS) testhttp.o testppd.o
+
+
+#
+# Header files to install...
+#
+
+HEADERS = cups.h http.h ipp.h language.h ppd.h
+
+
+#
+# Targets in this directory...
+#
+
+TARGETS = $(LIBCUPS) testhttp testppd
+
+
+#
+# Make all targets...
+#
+
+all: $(TARGETS)
+
+
+#
+# Remove object and target files...
+#
+
+clean:
+ $(RM) $(OBJS) $(TARGETS)
+
+
+#
+# Install object and target files...
+#
+
+install: all
+ -$(MKDIR) $(INCLUDEDIR)/cups
+ $(INSTALL_DATA) $(HEADERS) $(INCLUDEDIR)/cups
+ -$(MKDIR) $(LIBDIR)
+ $(INSTALL_LIB) $(LIBCUPS) $(LIBDIR)
+ if test $(LIBCUPS) != "libcups.a"; then \
+ $(RM) `basename $(LIBCUPS) .2`; \
+ $(LN) $(LIBCUPS) `basename $(LIBCUPS) .2`; \
+ fi
+
+
+#
+# libcups.so.2, libcups.sl.1
+#
+
+libcups.so.2 libcups.sl.2: $(LIBOBJS) ../Makedefs
+ echo Linking $@...
+ $(DSO) $@ $(LIBOBJS)
+ $(RM) `basename $@ .2`
+ $(LN) $@ `basename $@ .2`
+
+
+#
+# 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
+
+dest.o: cups.h http.h ipp.h language.h string.h
+emit.o: ppd.h
+http.o: http.h ipp.h md5.h string.h
+ipp.o: http.h ipp.h string.h language.h
+language.o: cups_C.h language.h string.h
+mark.o: ppd.h
+md5.o: md5.h
+options.o: cups.h
+page.o: ppd.h
+ppd.o: language.h ppd.h
+snprintf.o: string.h
+string.o: string.h
+usersys.o: cups.h
+util.o: cups.h http.h ipp.h
+
+
+#
+# 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
+
+
+#
+# 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
+
+$(OBJS): ../Makedefs ../config.h
+
+
+#
+# End of "$Id$".
+#
diff --git a/cups/cups.dsp b/cups/cups.dsp
new file mode 100644
index 000000000..cef288765
--- /dev/null
+++ b/cups/cups.dsp
@@ -0,0 +1,180 @@
+# 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 ".." /I "../visualc" /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=.\snprintf.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..b71796136
--- /dev/null
+++ b/cups/cups.h
@@ -0,0 +1,145 @@
+/*
+ * "$Id$"
+ *
+ * API definitions for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-2000 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/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) */
+};
+
+typedef struct /**** Printer Options ****/
+{
+ char *name; /* Name of option */
+ char *value; /* Value of option */
+} cups_option_t;
+
+typedef struct /**** Destination ****/
+{
+ char *name, /* Printer or class name */
+ *instance; /* Local instance name */
+ int is_default; /* Is this printer the default? */
+ int num_options; /* Number of options */
+ cups_option_t *options; /* Options */
+} cups_dest_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 ipp_status_t cupsLastError(void);
+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 cupsAddDest(const char *name, const char *instance,
+ int num_dests, cups_dest_t **dests);
+extern void cupsFreeDests(int num_dests, cups_dest_t *dests);
+extern cups_dest_t *cupsGetDest(const char *name, const char *instance,
+ int num_dests, cups_dest_t *dests);
+extern int cupsGetDests(cups_dest_t **dests);
+extern void cupsSetDests(int num_dests, cups_dest_t *dests);
+
+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..033dcd80c
--- /dev/null
+++ b/cups/cups_C.h
@@ -0,0 +1,132 @@
+"iso-8859-1",
+"OK",
+"Cancel",
+"Help",
+"Quit",
+"Close",
+"Yes",
+"No",
+"On",
+"Off",
+"Save",
+"Discard",
+"Default",
+"Options",
+"More Info",
+"Black",
+"Color",
+"Cyan",
+"Magenta",
+"Yellow",
+"Copyright 1993-2000 by Easy Software Products, All Rights Reserved.",
+"General",
+"Printer",
+"Image",
+"HP-GL/2",
+"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",
+"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",
+"Text",
+"Pretty Print",
+"Margins",
+"Left",
+"Right",
+"Bottom",
+"Top",
+"Filename(s)",
+"Print",
+"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..c7918b9bf
--- /dev/null
+++ b/cups/debug.h
@@ -0,0 +1,57 @@
+/*
+ * "$Id$"
+ *
+ * Debugging macros for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-2000 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/dest.c b/cups/dest.c
new file mode 100644
index 000000000..913cac717
--- /dev/null
+++ b/cups/dest.c
@@ -0,0 +1,472 @@
+/*
+ * "$Id$"
+ *
+ * User-defined destination (and option) support for the Common UNIX
+ * Printing System (CUPS).
+ *
+ * Copyright 1997-2000 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:
+ *
+ * cupsAddDest() - Add a destination to the list of destinations.
+ * cupsFreeDests() - Free the memory used by the list of destinations.
+ * cupsGetDest() - Get the named destination from the list.
+ * cupsGetDests() - Get the list of destinations.
+ * cupsSetDests() - Set the list of destinations.
+ * cups_get_dests() - Get destinations from a file.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "cups.h"
+#include "string.h"
+#include <stdlib.h>
+#include <ctype.h>
+
+
+/*
+ * Local functions...
+ */
+
+static int cups_get_dests(const char *filename, int num_dests,
+ cups_dest_t **dests);
+
+
+/*
+ * 'cupsAddDest()' - Add a destination to the list of destinations.
+ */
+
+int /* O - New number of destinations */
+cupsAddDest(const char *name, /* I - Name of destination */
+ const char *instance, /* I - Instance of destination */
+ int num_dests, /* I - Number of destinations */
+ cups_dest_t **dests) /* IO - Destinations */
+{
+ int i; /* Looping var */
+ cups_dest_t *dest; /* Destination pointer */
+
+
+ if (name == NULL || dests == NULL)
+ return (0);
+
+ if ((dest = cupsGetDest(name, instance, num_dests, *dests)) != NULL)
+ return (num_dests);
+
+ /*
+ * Add new destination...
+ */
+
+ if (num_dests == 0)
+ dest = malloc(sizeof(cups_dest_t));
+ else
+ dest = realloc(*dests, sizeof(cups_dest_t) * (num_dests + 1));
+
+ if (dest == NULL)
+ return (num_dests);
+
+ *dests = dest;
+
+ for (i = num_dests; i > 0; i --, dest ++)
+ if (strcasecmp(name, dest->name) < 0)
+ break;
+ else if (instance == NULL && dest->instance != NULL)
+ break;
+ else if (instance != NULL && dest->instance != NULL &&
+ strcasecmp(instance, dest->instance) < 0)
+ break;
+
+ if (i > 0)
+ memmove(dest + 1, dest, i * sizeof(cups_dest_t));
+
+ dest->name = strdup(name);
+ dest->is_default = 0;
+ dest->num_options = 0;
+ dest->options = (cups_option_t *)0;
+
+ if (instance == NULL)
+ dest->instance = NULL;
+ else
+ dest->instance = strdup(instance);
+
+ return (num_dests + 1);
+}
+
+
+/*
+ * 'cupsFreeDests()' - Free the memory used by the list of destinations.
+ */
+
+void
+cupsFreeDests(int num_dests, /* I - Number of destinations */
+ cups_dest_t *dests) /* I - Destinations */
+{
+ int i; /* Looping var */
+ cups_dest_t *dest; /* Current destination */
+
+
+ if (num_dests == 0 || dests == NULL)
+ return;
+
+ for (i = num_dests, dest = dests; i > 0; i --, dest ++)
+ {
+ free(dest->name);
+
+ if (dest->instance)
+ free(dest->instance);
+
+ cupsFreeOptions(dest->num_options, dest->options);
+ }
+
+ free(dests);
+}
+
+
+/*
+ * 'cupsGetDest()' - Get the named destination from the list.
+ */
+
+cups_dest_t * /* O - Destination pointer or NULL */
+cupsGetDest(const char *name, /* I - Name of destination */
+ const char *instance, /* I - Instance of destination */
+ int num_dests, /* I - Number of destinations */
+ cups_dest_t *dests) /* I - Destinations */
+{
+ int comp; /* Result of comparison */
+
+
+ if (name == NULL || num_dests == 0 || dests == NULL)
+ return (NULL);
+
+ while (num_dests > 0)
+ {
+ if ((comp = strcasecmp(name, dests->name)) < 0)
+ return (NULL);
+ else if (comp == 0)
+ {
+ if ((instance == NULL && dests->instance == NULL) ||
+ (instance != NULL && dests->instance != NULL &&
+ strcasecmp(instance, dests->instance) == 0))
+ return (dests);
+ }
+
+ num_dests --;
+ dests ++;
+ }
+
+ return (NULL);
+}
+
+
+/*
+ * 'cupsGetDests()' - Get the list of destinations.
+ */
+
+int /* O - Number of destinations */
+cupsGetDests(cups_dest_t **dests) /* O - Destinations */
+{
+ int i; /* Looping var */
+ int num_dests; /* Number of destinations */
+ int count; /* Number of printers/classes */
+ char **names; /* Printer/class names */
+ cups_dest_t *dest; /* Destination pointer */
+ const char *home; /* HOME environment variable */
+ char filename[1024]; /* Local ~/.lpoptions file */
+
+
+ /*
+ * Initialize destination array...
+ */
+
+ num_dests = 0;
+ *dests = (cups_dest_t *)0;
+
+ /*
+ * Grab all available printers...
+ */
+
+ if ((count = cupsGetPrinters(&names)) > 0)
+ {
+ for (i = 0; i < count; i ++)
+ {
+ num_dests = cupsAddDest(names[i], NULL, num_dests, dests);
+ free(names[i]);
+ }
+
+ free(names);
+ }
+
+ /*
+ * Grab all available classes...
+ */
+
+ if ((count = cupsGetClasses(&names)) > 0)
+ {
+ for (i = 0; i < count; i ++)
+ {
+ num_dests = cupsAddDest(names[i], NULL, num_dests, dests);
+ free(names[i]);
+ }
+
+ free(names);
+ }
+
+ /*
+ * Grab the default destination...
+ */
+
+ if ((dest = cupsGetDest(cupsGetDefault(), NULL, num_dests, *dests)) != NULL)
+ dest->is_default = 1;
+
+ /*
+ * Load the /etc/cups/lpoptions and ~/.lpoptions files...
+ */
+
+ num_dests = cups_get_dests(CUPS_SERVERROOT "/lpoptions", num_dests, dests);
+
+ if ((home = getenv("HOME")) != NULL)
+ {
+ snprintf(filename, sizeof(filename), "%s/.lpoptions", home);
+ num_dests = cups_get_dests(filename, num_dests, dests);
+ }
+
+ /*
+ * Return the number of destinations...
+ */
+
+ return (num_dests);
+}
+
+
+/*
+ * 'cupsSetDests()' - Set the list of destinations.
+ */
+
+void
+cupsSetDests(int num_dests, /* I - Number of destinations */
+ cups_dest_t *dests) /* I - Destinations */
+{
+ int i, j; /* Looping vars */
+ cups_dest_t *dest; /* Current destination */
+ cups_option_t *option; /* Current option */
+ FILE *fp; /* File pointer */
+ const char *home; /* HOME environment variable */
+ char filename[1024]; /* lpoptions file */
+
+
+ /*
+ * Figure out which file to write to...
+ */
+
+ if (getuid() == 0)
+ strcpy(filename, CUPS_SERVERROOT "/lpoptions");
+ else if ((home = getenv("HOME")) != NULL)
+ snprintf(filename, sizeof(filename), "%s/.lpoptions", home);
+ else
+ return;
+
+ /*
+ * Try to open the file...
+ */
+
+ if ((fp = fopen(filename, "w")) == NULL)
+ return;
+
+ /*
+ * Write each printer; each line looks like:
+ *
+ * Dest name[/instance] options
+ * Default name[/instance] options
+ */
+
+ for (i = num_dests, dest = dests; i > 0; i --, dest ++)
+ if (dest->instance != NULL || dest->num_options != 0 || dest->is_default)
+ {
+ fprintf(fp, "%s %s", dest->is_default ? "Default" : "Dest",
+ dest->name);
+ if (dest->instance)
+ fprintf(fp, "/%s", dest->instance);
+
+ for (j = dest->num_options, option = dest->options; j > 0; j --, option ++)
+ if (option->value[0])
+ fprintf(fp, " %s=%s", option->name, option->value);
+ else
+ fprintf(fp, " %s", option->name);
+
+ fputs("\n", fp);
+ }
+
+ /*
+ * Close the file and return...
+ */
+
+ fclose(fp);
+}
+
+
+/*
+ * 'cups_get_dests()' - Get destinations from a file.
+ */
+
+static int /* O - Number of destinations */
+cups_get_dests(const char *filename, /* I - File to read from */
+ int num_dests, /* I - Number of destinations */
+ cups_dest_t **dests) /* IO - Destinations */
+{
+ int i; /* Looping var */
+ cups_dest_t *dest; /* Current destination */
+ FILE *fp; /* File pointer */
+ char line[8192], /* Line from file */
+ *lineptr, /* Pointer into line */
+ *name, /* Name of destination/option */
+ *instance; /* Instance of destination */
+
+
+ /*
+ * Try to open the file...
+ */
+
+ if ((fp = fopen(filename, "r")) == NULL)
+ return (num_dests);
+
+ /*
+ * Read each printer; each line looks like:
+ *
+ * Dest name[/instance] options
+ * Default name[/instance] options
+ */
+
+ while (fgets(line, sizeof(line), fp) != NULL)
+ {
+ /*
+ * See what type of line it is...
+ */
+
+ if (strncasecmp(line, "dest", 4) == 0 && isspace(line[4]))
+ lineptr = line + 4;
+ else if (strncasecmp(line, "default", 7) == 0 && isspace(line[7]))
+ lineptr = line + 7;
+ else
+ continue;
+
+ /*
+ * Skip leading whitespace...
+ */
+
+ while (isspace(*lineptr))
+ lineptr ++;
+
+ if (!*lineptr)
+ continue;
+
+ name = lineptr;
+
+ /*
+ * Search for an instance...
+ */
+
+ while (!isspace(*lineptr) && *lineptr && *lineptr != '/')
+ lineptr ++;
+
+ if (!*lineptr)
+ continue;
+
+ if (*lineptr == '/')
+ {
+ /*
+ * Found an instance...
+ */
+
+ *lineptr++ = '\0';
+ instance = lineptr;
+
+ /*
+ * Search for an instance...
+ */
+
+ while (!isspace(*lineptr) && *lineptr)
+ lineptr ++;
+ }
+ else
+ instance = NULL;
+
+ *lineptr++ = '\0';
+
+ /*
+ * Add the destination...
+ */
+
+ num_dests = cupsAddDest(name, instance, num_dests, dests);
+
+ if ((dest = cupsGetDest(name, instance, num_dests, *dests)) == NULL)
+ {
+ /*
+ * Out of memory!
+ */
+
+ fclose(fp);
+ return (num_dests);
+ }
+
+ /*
+ * Add options until we hit the end of the line...
+ */
+
+ if (dest->num_options)
+ {
+ /*
+ * Free old options...
+ */
+
+ cupsFreeOptions(dest->num_options, dest->options);
+
+ dest->num_options = 0;
+ dest->options = (cups_option_t *)0;
+ }
+
+ dest->num_options = cupsParseOptions(lineptr, dest->num_options,
+ &(dest->options));
+
+ /*
+ * Set this as default if needed...
+ */
+
+ if (strncasecmp(line, "default", 7) == 0)
+ {
+ for (i = 0; i < num_dests; i ++)
+ (*dests)[i].is_default = 0;
+
+ dest->is_default = 1;
+ }
+ }
+
+ /*
+ * Close the file and return...
+ */
+
+ fclose(fp);
+
+ return (num_dests);
+}
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/cups/emit.c b/cups/emit.c
new file mode 100644
index 000000000..be56f5b97
--- /dev/null
+++ b/cups/emit.c
@@ -0,0 +1,301 @@
+/*
+ * "$Id$"
+ *
+ * PPD code emission routines for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-2000 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 (strcasecmp(((ppd_option_t *)choices[i]->option)->keyword, "PageSize") == 0 &&
+ strcasecmp(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[5 -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/http.c b/cups/http.c
new file mode 100644
index 000000000..6ba2710b2
--- /dev/null
+++ b/cups/http.c
@@ -0,0 +1,1546 @@
+/*
+ * "$Id$"
+ *
+ * HTTP routines for the Common UNIX Printing System (CUPS) scheduler.
+ *
+ * Copyright 1997-2000 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 */
+
+
+ if (host == NULL)
+ return (NULL);
+
+ 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"...
+ */
+
+ strncpy(http->hostname, host, sizeof(http->hostname) - 1);
+ 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)
+ {
+#if defined(WIN32) || defined(__EMX__)
+ http->error = WSAGetLastError();
+#else
+ http->error = errno;
+#endif /* WIN32 || __EMX__ */
+ http->status = HTTP_ERROR;
+ 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)
+ {
+#if defined(WIN32) || defined(__EMX__)
+ http->error = WSAGetLastError();
+#else
+ http->error = errno;
+#endif /* WIN32 || __EMX__ */
+ http->status = HTTP_ERROR;
+
+#ifdef WIN32
+ closesocket(http->fd);
+#else
+ close(http->fd);
+#endif
+
+ return (-1);
+ }
+
+ http->error = 0;
+ http->status = HTTP_CONTINUE;
+
+ return (0);
+}
+
+
+/*
+ * 'httpSeparate()' - Separate a Universal Resource Identifier into its
+ * components.
+ */
+
+void
+httpSeparate(const char *uri, /* I - Universal Resource Identifier */
+ char *method, /* O - Method [32] (http, https, etc.) */
+ char *username, /* O - Username [32] */
+ char *host, /* O - Hostname [32] */
+ int *port, /* O - Port number to use */
+ char *resource) /* O - Resource/filename [1024] */
+{
+ 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...
+ */
+
+ if (strncmp(uri, "//", 2) == 0)
+ {
+ /*
+ * Workaround for HP IPP client bug...
+ */
+
+ strcpy(method, "ipp");
+ }
+ else
+ {
+ /*
+ * Standard URI with method...
+ */
+
+ 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)
+ {
+ strncpy(resource, ptr, HTTP_MAX_URI - 1);
+ resource[HTTP_MAX_URI - 1] = '\0';
+ *ptr = '\0';
+ }
+ else
+ resource[0] = '\0';
+
+ if (isdigit(*uri))
+ {
+ /*
+ * OK, we have "hostname:port[/resource]"...
+ */
+
+ *port = strtol(uri, (char **)&uri, 10);
+
+ if (*uri == '/')
+ {
+ strncpy(resource, uri, HTTP_MAX_URI - 1);
+ resource[HTTP_MAX_URI - 1] = '\0';
+ }
+ }
+ else
+ *port = 631;
+
+ strcpy(method, "http");
+ username[0] = '\0';
+ return;
+ }
+ else
+ {
+ strncpy(method, host, 31);
+ method[31] = '\0';
+ }
+ }
+
+ /*
+ * If the method starts with less than 2 slashes then it is a local resource...
+ */
+
+ if (strncmp(uri, "//", 2) != 0)
+ {
+ strncpy(resource, uri, 1023);
+ resource[1023] = '\0';
+
+ username[0] = '\0';
+ host[0] = '\0';
+ *port = 0;
+ return;
+ }
+
+ /*
+ * Grab the hostname...
+ */
+
+ while (*uri == '/')
+ uri ++;
+
+ ptr = host;
+ while (!(*uri == ':' && isdigit(uri[1])) && *uri != '@' && *uri != '/' && *uri != '\0')
+ *ptr ++ = *uri ++;
+
+ *ptr = '\0';
+
+ if (*uri == '@')
+ {
+ /*
+ * Got a username...
+ */
+
+ strncpy(username, host, 31);
+ username[31] = '\0';
+
+ ptr = host;
+ uri ++;
+ while (*uri != ':' && *uri != '/' && *uri != '\0')
+ *ptr ++ = *uri ++;
+
+ *ptr = '\0';
+ }
+ else
+ username[0] = '\0';
+
+ if (*uri != ':')
+ {
+ 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;
+ }
+ else
+ {
+ /*
+ * Parse port number...
+ */
+
+ *port = 0;
+ uri ++;
+ while (isdigit(*uri))
+ {
+ *port = (*port * 10) + *uri - '0';
+ uri ++;
+ }
+ }
+
+ if (*uri == '\0')
+ {
+ /*
+ * Hostname but no port or path...
+ */
+
+ resource[0] = '/';
+ resource[1] = '\0';
+ return;
+ }
+
+ /*
+ * The remaining portion is the resource string...
+ */
+
+ strncpy(resource, uri, HTTP_MAX_URI - 1);
+ resource[HTTP_MAX_URI - 1] = '\0';
+}
+
+
+/*
+ * '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)
+ {
+ DEBUG_puts("httpRead: Getting chunk length...");
+
+ if (httpGets(len, sizeof(len), http) == NULL)
+ {
+ DEBUG_puts("httpRead: Could not get length!");
+ 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->data_encoding == HTTP_ENCODE_CHUNKED)
+ httpGets(len, sizeof(len), http);
+
+ 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;
+ else if (bytes < 0)
+#if defined(WIN32) || defined(__EMX__)
+ http->error = WSAGetLastError();
+#else
+ http->error = errno;
+#endif /* WIN32 || __EMX__ */
+
+ if (http->data_remaining == 0)
+ {
+ if (http->data_encoding == HTTP_ENCODE_CHUNKED)
+ httpGets(len, sizeof(len), http);
+
+ if (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 */
+
+
+ if (http == NULL || buffer == NULL)
+ return (-1);
+
+ http->activity = time(NULL);
+
+ if (http->data_encoding == HTTP_ENCODE_CHUNKED)
+ {
+ if (httpPrintf(http, "%x\r\n", length) < 0)
+ return (-1);
+
+ if (length == 0)
+ {
+ /*
+ * A zero-length chunk ends a transfer; unless we are sending POST
+ * data, go idle...
+ */
+
+ DEBUG_puts("httpWrite: changing states...");
+
+ if (http->state == HTTP_POST_RECV)
+ http->state ++;
+ else
+ http->state = HTTP_WAITING;
+
+ if (httpPrintf(http, "\r\n") < 0)
+ return (-1);
+
+ 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_encoding == HTTP_ENCODE_CHUNKED)
+ if (httpPrintf(http, "\r\n") < 0)
+ return (-1);
+
+ if (http->data_remaining == 0 && http->data_encoding == HTTP_ENCODE_LENGTH)
+ {
+ /*
+ * Finished with the transfer; unless we are sending POST data, go idle...
+ */
+
+ DEBUG_puts("httpWrite: changing states...");
+
+ 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 */
+
+
+ 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...
+ */
+
+#if defined(WIN32) || defined(__EMX__)
+ WSASetLastError(0);
+#else
+ errno = 0;
+#endif /* WIN32 || __EMX__ */
+
+ 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 defined(WIN32) || defined(__EMX__)
+ if (WSAGetLastError() != http->error)
+ {
+ http->error = WSAGetLastError();
+ continue;
+ }
+
+ DEBUG_printf(("httpGets(): recv() error %d!\n", WSAGetLastError()));
+#else
+ if (errno != http->error)
+ {
+ http->error = errno;
+ continue;
+ }
+
+ DEBUG_printf(("httpGets(): recv() error %d!\n", errno));
+#endif /* WIN32 || __EMX__ */
+
+ return (NULL);
+ }
+ else if (bytes == 0)
+ {
+ if (http->blocking)
+ http->error = EPIPE;
+
+ 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 = vsnprintf(buf, sizeof(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%15s%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, (int *)&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
+ {
+ http->status = HTTP_ERROR;
+ return (HTTP_ERROR);
+ }
+ }
+
+ /*
+ * See if there was an error...
+ */
+
+ if (http->error)
+ {
+ http->status = HTTP_ERROR;
+ 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')
+ {
+ *outptr ++ = '=';
+ 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 */
+{
+ DEBUG_printf(("httpGetLength(%08x)\n", http));
+
+ if (strcasecmp(http->fields[HTTP_FIELD_TRANSFER_ENCODING], "chunked") == 0)
+ {
+ DEBUG_puts("httpGetLength: chunked request!");
+
+ 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]);
+
+ DEBUG_printf(("httpGetLength: content_length = %d\n", http->data_remaining));
+ }
+
+ 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';
+
+ /*
+ * See if we had an error the last time around; if so, reconnect...
+ */
+
+ if (http->status == HTTP_ERROR || http->status >= HTTP_BAD_REQUEST)
+ httpReconnect(http);
+
+ /*
+ * 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)
+ {
+ http->status = HTTP_ERROR;
+ 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)
+ {
+ http->status = HTTP_ERROR;
+ return (-1);
+ }
+ }
+
+ if (httpPrintf(http, "\r\n") < 1)
+ {
+ http->status = HTTP_ERROR;
+ 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..59206ca8b
--- /dev/null
+++ b/cups/http.h
@@ -0,0 +1,315 @@
+/*
+ * "$Id$"
+ *
+ * Hyper-Text Transport Protocol definitions for the Common UNIX Printing
+ * System (CUPS).
+ *
+ * Copyright 1997-2000 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__ */
+
+# include "md5.h"
+
+
+/*
+ * 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 authentication types...
+ */
+
+typedef enum
+{
+ HTTP_AUTH_NONE, /* No authentication in use */
+ HTTP_AUTH_BASIC, /* Basic authentication in use */
+ HTTP_AUTH_MD5, /* Digest authentication in use */
+ HTTP_AUTH_MD5_SESS, /* MD5-session authentication in use */
+ HTTP_AUTH_MD5_INT, /* Digest authentication in use for body */
+ HTTP_AUTH_MD5_SESS_INT /* MD5-session authentication in use for body */
+} http_auth_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 */
+ int error; /* Last error on read */
+ 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 */
+ int auth_type; /* Authentication in use */
+ md5_state_t md5_state; /* MD5 state */
+ char nonce[HTTP_MAX_VALUE];
+ /* Nonce value */
+ int nonce_count; /* Nonce count */
+} 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);
+# define httpError(http) ((http)->error)
+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..2ca2663c9
--- /dev/null
+++ b/cups/ipp.c
@@ -0,0 +1,1683 @@
+/*
+ * "$Id$"
+ *
+ * Internet Printing Protocol support functions for the Common UNIX
+ * Printing System (CUPS).
+ *
+ * Copyright 1997-2000 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.
+ * ippErrorString() - Return a textual message for the given error message.
+ * 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.
+ * _ipp_add_attr() - Add a new attribute to the request.
+ * _ipp_free_attr() - Free an attribute.
+ * ipp_read() - Semi-blocking read on a HTTP connection...
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "string.h"
+#include "language.h"
+
+#include "ipp.h"
+#include "debug.h"
+
+
+/*
+ * Local functions...
+ */
+
+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 = _ipp_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 = _ipp_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 = _ipp_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 = _ipp_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 = _ipp_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 = _ipp_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 = _ipp_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 = _ipp_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 = _ipp_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 = _ipp_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 = _ipp_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 = _ipp_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;
+
+ return (t);
+}
+
+
+/*
+ * 'ippDelete()' - Delete an IPP request.
+ */
+
+void
+ippDelete(ipp_t *ipp) /* I - IPP request */
+{
+ ipp_attribute_t *attr, /* Current attribute */
+ *next; /* Next attribute */
+
+
+ if (ipp == NULL)
+ return;
+
+ for (attr = ipp->attrs; attr != NULL; attr = next)
+ {
+ next = attr->next;
+ _ipp_free_attr(attr);
+ }
+
+ free(ipp);
+}
+
+
+/*
+ * 'ippErrorString()' - Return a textual message for the given error message.
+ */
+
+const char * /* O - Text string */
+ippErrorString(ipp_status_t error) /* I - Error status */
+{
+ static cups_lang_t *language = 0; /* Language info */
+
+
+ /*
+ * Load the localized message file as needed...
+ */
+
+ if (!language)
+ language = cupsLangDefault();
+
+ /*
+ * Return the appropriate message...
+ */
+
+ switch (error)
+ {
+ case IPP_OK :
+ case IPP_OK_SUBST :
+ case IPP_OK_CONFLICT :
+ return ("OK");
+
+ case IPP_BAD_REQUEST :
+ return (cupsLangString(language, HTTP_BAD_REQUEST));
+
+ case IPP_FORBIDDEN :
+ return (cupsLangString(language, HTTP_FORBIDDEN));
+
+ case IPP_NOT_AUTHENTICATED :
+ case IPP_NOT_AUTHORIZED :
+ return (cupsLangString(language, HTTP_UNAUTHORIZED));
+
+ case IPP_NOT_POSSIBLE :
+ return (cupsLangString(language, HTTP_METHOD_NOT_ALLOWED));
+
+ case IPP_TIMEOUT :
+ return (cupsLangString(language, HTTP_REQUEST_TIMEOUT));
+
+ case IPP_NOT_FOUND :
+ return (cupsLangString(language, HTTP_NOT_FOUND));
+
+ case IPP_GONE :
+ return (cupsLangString(language, HTTP_GONE));
+
+ case IPP_DOCUMENT_FORMAT :
+ return (cupsLangString(language, HTTP_UNSUPPORTED_MEDIATYPE));
+
+ case IPP_CONFLICT :
+ return (cupsLangString(language, HTTP_CONFLICT));
+
+ case IPP_INTERNAL_ERROR :
+ return (cupsLangString(language, HTTP_SERVER_ERROR));
+
+ case IPP_OPERATION_NOT_SUPPORTED :
+ case IPP_VERSION_NOT_SUPPORTED :
+ return (cupsLangString(language, HTTP_NOT_SUPPORTED));
+
+ case IPP_SERVICE_UNAVAILABLE :
+ case IPP_DEVICE_UNAVAILABLE :
+ case IPP_TEMPORARY_ERROR :
+ case IPP_PRINTER_BUSY :
+ return (cupsLangString(language, HTTP_SERVICE_UNAVAILABLE));
+
+ case IPP_NOT_ACCEPTING :
+ return (cupsLangString(language, CUPS_MSG_NOT_ACCEPTING_JOBS));
+
+ default :
+ return ("ERROR");
+ }
+
+}
+
+
+/*
+ * '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 && strcasecmp(attr->name, name) == 0 &&
+ (attr->value_tag == type || type == IPP_TAG_ZERO ||
+ (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 += 4 * attr->num_values;/* Charset + text length */
+ for (i = 0; i < attr->num_values; i ++)
+ bytes += strlen(attr->values[i].string.charset) +
+ strlen(attr->values[i].string.text);
+ break;
+
+ default :
+ for (i = 0; i < attr->num_values; i ++)
+ bytes += attr->values[0].unknown.length;
+ break;
+ }
+ }
+
+ /*
+ * Finally, add 1 byte for the "end of attributes" tag and return...
+ */
+
+ DEBUG_printf(("bytes = %d\n", bytes + 1));
+
+ return (bytes + 1);
+}
+
+
+/*
+ * 'ippNew()' - Allocate a new IPP request.
+ */
+
+ipp_t * /* O - New IPP request */
+ippNew(void)
+{
+ ipp_t *temp; /* New IPP request */
+
+
+ if ((temp = (ipp_t *)calloc(sizeof(ipp_t), 1)) != NULL)
+ {
+ /*
+ * Default to IPP 1.1...
+ */
+
+ temp->request.any.version[0] = 1;
+ temp->request.any.version[1] = 1;
+ }
+
+ return (temp);
+}
+
+
+/*
+ * '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 */
+ *bufptr; /* Pointer into 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_VALUE)
+ {
+ /*
+ * 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 = _ipp_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);
+
+ bufptr = buffer;
+
+ /*
+ * text-with-language and name-with-language are composite
+ * values:
+ *
+ * charset-length
+ * charset
+ * text-length
+ * text
+ */
+
+ n = (bufptr[0] << 8) | bufptr[1];
+
+ attr->values[attr->num_values].string.charset = calloc(n + 1, 1);
+
+ memcpy(attr->values[attr->num_values].string.charset,
+ bufptr + 2, n);
+
+ bufptr += 2 + n;
+ n = (bufptr[0] << 8) | bufptr[1];
+
+ attr->values[attr->num_values].string.text = calloc(n + 1, 1);
+
+ memcpy(attr->values[attr->num_values].string.text,
+ bufptr + 2, n);
+
+ break;
+
+ default : /* Other unsupported values */
+ attr->values[attr->num_values].unknown.length = n;
+ if (n > 0)
+ {
+ attr->values[attr->num_values].unknown.data = malloc(n);
+ if (ipp_read(http, attr->values[attr->num_values].unknown.data, n) < n)
+ return (IPP_ERROR);
+ }
+ else
+ attr->values[attr->num_values].unknown.data = NULL;
+ break;
+ }
+
+ attr->num_values ++;
+
+ /*
+ * If blocking is disabled, stop here...
+ */
+
+ if (!http->blocking && http->used == 0)
+ break;
+ }
+ break;
+
+ case IPP_DATA :
+ break;
+
+ default :
+ break; /* anti-compiler-warning-code */
+ }
+
+ 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++ = ipp->request.any.version[0];
+ *bufptr++ = ipp->request.any.version[1];
+ *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));
+
+ if ((sizeof(buffer) - (bufptr - buffer)) < (n + 2))
+ {
+ if (httpWrite(http, (char *)buffer, bufptr - buffer) < 0)
+ {
+ DEBUG_puts("ippWrite: Could not write IPP attribute...");
+ return (IPP_ERROR);
+ }
+
+ bufptr = buffer;
+ }
+
+ *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) +
+ strlen(attr->values[i].string.text) +
+ 2;
+
+ if ((sizeof(buffer) - (bufptr - buffer)) < (n + 2))
+ {
+ if (httpWrite(http, (char *)buffer, bufptr - buffer) < 0)
+ {
+ DEBUG_puts("ippWrite: Could not write IPP attribute...");
+ return (IPP_ERROR);
+ }
+
+ bufptr = buffer;
+ }
+
+ /* Length of entire value */
+ *bufptr++ = n >> 8;
+ *bufptr++ = n;
+
+ /* Length of charset */
+ n = strlen(attr->values[i].string.charset);
+ *bufptr++ = n >> 8;
+ *bufptr++ = n;
+
+ /* Charset */
+ memcpy(bufptr, attr->values[i].string.charset, n);
+ bufptr += n;
+
+ /* Length of text */
+ n = strlen(attr->values[i].string.text);
+ *bufptr++ = n >> 8;
+ *bufptr++ = n;
+
+ /* Text */
+ memcpy(bufptr, attr->values[i].string.text, n);
+ bufptr += n;
+ }
+ break;
+
+ default :
+ 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 = attr->values[i].unknown.length;
+
+ if ((sizeof(buffer) - (bufptr - buffer)) < (n + 2))
+ {
+ if (httpWrite(http, (char *)buffer, bufptr - buffer) < 0)
+ {
+ DEBUG_puts("ippWrite: Could not write IPP attribute...");
+ return (IPP_ERROR);
+ }
+
+ bufptr = buffer;
+ }
+
+ /* Length of unknown value */
+ *bufptr++ = n >> 8;
+ *bufptr++ = n;
+
+ /* Value */
+ if (n > 0)
+ {
+ memcpy(bufptr, attr->values[i].unknown.data, 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;
+
+ default :
+ break; /* anti-compiler-warning-code */
+ }
+
+ 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));
+}
+
+
+/*
+ * '_ipp_add_attr()' - Add a new attribute to the request.
+ */
+
+ipp_attribute_t * /* O - New attribute */
+_ipp_add_attr(ipp_t *ipp, /* I - IPP request */
+ int num_values) /* I - Number of values */
+{
+ ipp_attribute_t *attr; /* New attribute */
+
+
+ DEBUG_printf(("_ipp_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_free_attr()' - Free an attribute.
+ */
+
+void
+_ipp_free_attr(ipp_attribute_t *attr) /* I - Attribute to free */
+{
+ int i; /* Looping var */
+
+
+ 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;
+
+ default :
+ break; /* anti-compiler-warning-code */
+ }
+
+ if (attr->name != NULL)
+ free(attr->name);
+
+ free(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..85e806531
--- /dev/null
+++ b/cups/ipp.h
@@ -0,0 +1,363 @@
+/*
+ * "$Id$"
+ *
+ * Internet Printing Protocol definitions for the Common UNIX Printing
+ * System (CUPS).
+ *
+ * Copyright 1997-2000 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_UNSUPPORTED_GROUP,
+ IPP_TAG_UNSUPPORTED_VALUE = 0x10,
+ IPP_TAG_DEFAULT,
+ IPP_TAG_UNKNOWN,
+ IPP_TAG_NOVALUE,
+ IPP_TAG_NOTSETTABLE = 0x15,
+ IPP_TAG_DELETEATTR,
+ IPP_TAG_ANYVALUE,
+ 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_CANCELLED,
+ 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,
+ IPP_RELEASE_JOB,
+ IPP_RESTART_JOB,
+ IPP_PAUSE_PRINTER = 0x0010,
+ IPP_RESUME_PRINTER,
+ IPP_PURGE_JOBS,
+ IPP_SET_PRINTER_ATTRIBUTES,
+ IPP_SET_JOB_ATTRIBUTES,
+ IPP_GET_PRINTER_SUPPORTED_VALUES,
+ 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,
+ CUPS_GET_DEVICES,
+ CUPS_GET_PPDS,
+ CUPS_MOVE_JOB
+} 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 */
+
+ struct
+ {
+ int length; /* Length of attribute */
+ void *data; /* Data in attribute */
+ } unknown; /* Unknown attribute type */
+} 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 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 time_t ippDateToTime(const ipp_uchar_t *date);
+extern void ippDelete(ipp_t *ipp);
+extern const char *ippErrorString(ipp_status_t error);
+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);
+
+extern ipp_attribute_t *_ipp_add_attr(ipp_t *, int);
+extern void _ipp_free_attr(ipp_attribute_t *);
+
+
+/*
+ * 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..4e2308cc6
--- /dev/null
+++ b/cups/language.c
@@ -0,0 +1,403 @@
+/*
+ * "$Id$"
+ *
+ * I18N/language support for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-2000 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",
+ "iso8859-13",
+ "iso8859-14",
+ "iso8859-15",
+ "cp874",
+ "cp1250",
+ "cp1251",
+ "cp1252",
+ "cp1253",
+ "cp1254",
+ "cp1255",
+ "cp1256",
+ "cp1257",
+ "cp1258"
+ };
+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. Any trailing character set specification is
+ * dropped.
+ */
+
+ if (language == NULL || language[0] == '\0' ||
+ strcmp(language, "POSIX") == 0)
+ strcpy(langname, "C");
+ else
+ {
+ /*
+ * Copy the locale string over safely...
+ */
+
+ strncpy(langname, language, sizeof(langname) - 1);
+ langname[sizeof(langname) - 1] = '\0';
+
+ /*
+ * Strip charset from "locale.charset"...
+ */
+
+ if ((text = strchr(langname, '.')) != NULL)
+ *text = '\0';
+ }
+
+ 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;
+
+ snprintf(filename, sizeof(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';
+ snprintf(filename, sizeof(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..4d8290b3e
--- /dev/null
+++ b/cups/language.h
@@ -0,0 +1,222 @@
+/*
+ * "$Id$"
+ *
+ * Multi-language support for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-2000 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_TEXT,
+ CUPS_MSG_PRETTYPRINT,
+ CUPS_MSG_MARGINS,
+ CUPS_MSG_LEFT,
+ CUPS_MSG_RIGHT,
+ CUPS_MSG_BOTTOM,
+ CUPS_MSG_TOP,
+ CUPS_MSG_FILENAME,
+ CUPS_MSG_PRINT,
+ 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_ISO8859_13,
+ CUPS_ISO8859_14,
+ CUPS_ISO8859_15,
+ CUPS_CP874,
+ CUPS_CP1250,
+ CUPS_CP1251,
+ CUPS_CP1252,
+ CUPS_CP1253,
+ CUPS_CP1254,
+ CUPS_CP1255,
+ CUPS_CP1256,
+ CUPS_CP1257,
+ CUPS_CP1258
+} 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...
+ */
+
+# ifdef WIN32
+# define cupsLangDefault() cupsLangGet(setlocale(LC_ALL, ""))
+# else /* This fix works around bugs in the Linux and HP-UX setlocale() */
+# define cupsLangDefault() cupsLangGet(getenv("LANG"))
+# endif /* WIN32 */
+
+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..d6b79424e
--- /dev/null
+++ b/cups/mark.c
@@ -0,0 +1,431 @@
+/*
+ * "$Id$"
+ *
+ * Option marking routines for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-2000 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 || strcasecmp(c1->choice, "None") == 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 || strcasecmp(c2->choice, "None") == 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 (strcasecmp(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 (strcasecmp(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 (strcasecmp(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 */
+
+
+ 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 (strcasecmp(option, "PageSize") == 0 && strncasecmp(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 (strcasecmp(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 (strcasecmp(c->choice, choice) != 0)
+ c->marked = 0;
+
+ if (strcasecmp(option, "PageSize") == 0 || strcasecmp(option, "PageRegion") == 0)
+ {
+ /*
+ * Mark current page size...
+ */
+
+ for (i = 0; i < ppd->num_sizes; i ++)
+ ppd->sizes[i].marked = strcasecmp(ppd->sizes[i].name, choice) == 0;
+
+ /*
+ * Unmark the current PageSize or PageRegion setting, as appropriate...
+ */
+
+ if (strcasecmp(option, "PageSize") == 0)
+ {
+ if ((o = ppdFindOption(ppd, "PageRegion")) != NULL)
+ for (i = 0; i < o->num_choices; i ++)
+ o->choices[i].marked = 0;
+ }
+ else
+ {
+ if ((o = ppdFindOption(ppd, "PageSize")) != NULL)
+ for (i = 0; i < o->num_choices; i ++)
+ o->choices[i].marked = 0;
+ }
+ }
+ else if (strcasecmp(option, "InputSlot") == 0)
+ {
+ /*
+ * Unmark ManualFeed option...
+ */
+
+ if ((o = ppdFindOption(ppd, "ManualFeed")) != NULL)
+ for (i = 0; i < o->num_choices; i ++)
+ o->choices[i].marked = 0;
+ }
+ else if (strcasecmp(option, "ManualFeed") == 0)
+ {
+ /*
+ * Unmark InputSlot option...
+ */
+
+ if ((o = ppdFindOption(ppd, "InputSlot")) != NULL)
+ 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 (strcasecmp(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/md5.c b/cups/md5.c
new file mode 100644
index 000000000..5db868858
--- /dev/null
+++ b/cups/md5.c
@@ -0,0 +1,392 @@
+/*
+ Copyright (C) 1999 Aladdin Enterprises. All rights reserved.
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+
+ L. Peter Deutsch
+ ghost@aladdin.com
+
+ */
+/*$Id$ */
+/*
+ Independent implementation of MD5 (RFC 1321).
+
+ This code implements the MD5 Algorithm defined in RFC 1321.
+ It is derived directly from the text of the RFC and not from the
+ reference implementation.
+
+ The original and principal author of md5.c is L. Peter Deutsch
+ <ghost@aladdin.com>. Other authors are noted in the change history
+ that follows (in reverse chronological order):
+
+ 1999-11-04 lpd Edited comments slightly for automatic TOC extraction.
+ 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5).
+ 1999-05-03 lpd Original version.
+ */
+
+#include "md5.h"
+#include "string.h"
+
+#ifdef TEST
+/*
+ * Compile with -DTEST to create a self-contained executable test program.
+ * The test program should print out the same values as given in section
+ * A.5 of RFC 1321, reproduced below.
+ */
+main()
+{
+ static const char *const test[7] = {
+ "", /*d41d8cd98f00b204e9800998ecf8427e*/
+ "a", /*0cc175b9c0f1b6a831c399e269772661*/
+ "abc", /*900150983cd24fb0d6963f7d28e17f72*/
+ "message digest", /*f96b697d7cb7938d525a2f31aaf161d0*/
+ "abcdefghijklmnopqrstuvwxyz", /*c3fcd3d76192e4007dfb496cca67e13b*/
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
+ /*d174ab98d277d9f5a5611c2c9f419d9f*/
+ "12345678901234567890123456789012345678901234567890123456789012345678901234567890" /*57edf4a22be3c955ac49da2e2107b67a*/
+ };
+ int i;
+
+ for (i = 0; i < 7; ++i) {
+ md5_state_t state;
+ md5_byte_t digest[16];
+ int di;
+
+ md5_init(&state);
+ md5_append(&state, (const md5_byte_t *)test[i], strlen(test[i]));
+ md5_finish(&state, digest);
+ printf("MD5 (\"%s\") = ", test[i]);
+ for (di = 0; di < 16; ++di)
+ printf("%02x", digest[di]);
+ printf("\n");
+ }
+ return 0;
+}
+#endif /* TEST */
+
+
+/*
+ * For reference, here is the program that computed the T values.
+ */
+#if 0
+#include <math.h>
+main()
+{
+ int i;
+ for (i = 1; i <= 64; ++i) {
+ unsigned long v = (unsigned long)(4294967296.0 * fabs(sin((double)i)));
+ printf("#define T%d 0x%08lx\n", i, v);
+ }
+ return 0;
+}
+#endif
+/*
+ * End of T computation program.
+ */
+#define T1 0xd76aa478
+#define T2 0xe8c7b756
+#define T3 0x242070db
+#define T4 0xc1bdceee
+#define T5 0xf57c0faf
+#define T6 0x4787c62a
+#define T7 0xa8304613
+#define T8 0xfd469501
+#define T9 0x698098d8
+#define T10 0x8b44f7af
+#define T11 0xffff5bb1
+#define T12 0x895cd7be
+#define T13 0x6b901122
+#define T14 0xfd987193
+#define T15 0xa679438e
+#define T16 0x49b40821
+#define T17 0xf61e2562
+#define T18 0xc040b340
+#define T19 0x265e5a51
+#define T20 0xe9b6c7aa
+#define T21 0xd62f105d
+#define T22 0x02441453
+#define T23 0xd8a1e681
+#define T24 0xe7d3fbc8
+#define T25 0x21e1cde6
+#define T26 0xc33707d6
+#define T27 0xf4d50d87
+#define T28 0x455a14ed
+#define T29 0xa9e3e905
+#define T30 0xfcefa3f8
+#define T31 0x676f02d9
+#define T32 0x8d2a4c8a
+#define T33 0xfffa3942
+#define T34 0x8771f681
+#define T35 0x6d9d6122
+#define T36 0xfde5380c
+#define T37 0xa4beea44
+#define T38 0x4bdecfa9
+#define T39 0xf6bb4b60
+#define T40 0xbebfbc70
+#define T41 0x289b7ec6
+#define T42 0xeaa127fa
+#define T43 0xd4ef3085
+#define T44 0x04881d05
+#define T45 0xd9d4d039
+#define T46 0xe6db99e5
+#define T47 0x1fa27cf8
+#define T48 0xc4ac5665
+#define T49 0xf4292244
+#define T50 0x432aff97
+#define T51 0xab9423a7
+#define T52 0xfc93a039
+#define T53 0x655b59c3
+#define T54 0x8f0ccc92
+#define T55 0xffeff47d
+#define T56 0x85845dd1
+#define T57 0x6fa87e4f
+#define T58 0xfe2ce6e0
+#define T59 0xa3014314
+#define T60 0x4e0811a1
+#define T61 0xf7537e82
+#define T62 0xbd3af235
+#define T63 0x2ad7d2bb
+#define T64 0xeb86d391
+
+static void
+md5_process(md5_state_t *pms, const md5_byte_t *data /*[64]*/)
+{
+ md5_word_t
+ a = pms->abcd[0], b = pms->abcd[1],
+ c = pms->abcd[2], d = pms->abcd[3];
+ md5_word_t t;
+
+#ifndef ARCH_IS_BIG_ENDIAN
+# define ARCH_IS_BIG_ENDIAN 1 /* slower, default implementation */
+#endif
+#if ARCH_IS_BIG_ENDIAN
+
+ /*
+ * On big-endian machines, we must arrange the bytes in the right
+ * order. (This also works on machines of unknown byte order.)
+ */
+ md5_word_t X[16];
+ const md5_byte_t *xp = data;
+ int i;
+
+ for (i = 0; i < 16; ++i, xp += 4)
+ X[i] = xp[0] + (xp[1] << 8) + (xp[2] << 16) + (xp[3] << 24);
+
+#else /* !ARCH_IS_BIG_ENDIAN */
+
+ /*
+ * On little-endian machines, we can process properly aligned data
+ * without copying it.
+ */
+ md5_word_t xbuf[16];
+ const md5_word_t *X;
+
+ if (!((data - (const md5_byte_t *)0) & 3)) {
+ /* data are properly aligned */
+ X = (const md5_word_t *)data;
+ } else {
+ /* not aligned */
+ memcpy(xbuf, data, 64);
+ X = xbuf;
+ }
+#endif
+
+#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n))))
+
+ /* Round 1. */
+ /* Let [abcd k s i] denote the operation
+ a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */
+#define F(x, y, z) (((x) & (y)) | (~(x) & (z)))
+#define SET(a, b, c, d, k, s, Ti)\
+ t = a + F(b,c,d) + X[k] + Ti;\
+ a = ROTATE_LEFT(t, s) + b
+ /* Do the following 16 operations. */
+ SET(a, b, c, d, 0, 7, T1);
+ SET(d, a, b, c, 1, 12, T2);
+ SET(c, d, a, b, 2, 17, T3);
+ SET(b, c, d, a, 3, 22, T4);
+ SET(a, b, c, d, 4, 7, T5);
+ SET(d, a, b, c, 5, 12, T6);
+ SET(c, d, a, b, 6, 17, T7);
+ SET(b, c, d, a, 7, 22, T8);
+ SET(a, b, c, d, 8, 7, T9);
+ SET(d, a, b, c, 9, 12, T10);
+ SET(c, d, a, b, 10, 17, T11);
+ SET(b, c, d, a, 11, 22, T12);
+ SET(a, b, c, d, 12, 7, T13);
+ SET(d, a, b, c, 13, 12, T14);
+ SET(c, d, a, b, 14, 17, T15);
+ SET(b, c, d, a, 15, 22, T16);
+#undef SET
+
+ /* Round 2. */
+ /* Let [abcd k s i] denote the operation
+ a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */
+#define G(x, y, z) (((x) & (z)) | ((y) & ~(z)))
+#define SET(a, b, c, d, k, s, Ti)\
+ t = a + G(b,c,d) + X[k] + Ti;\
+ a = ROTATE_LEFT(t, s) + b
+ /* Do the following 16 operations. */
+ SET(a, b, c, d, 1, 5, T17);
+ SET(d, a, b, c, 6, 9, T18);
+ SET(c, d, a, b, 11, 14, T19);
+ SET(b, c, d, a, 0, 20, T20);
+ SET(a, b, c, d, 5, 5, T21);
+ SET(d, a, b, c, 10, 9, T22);
+ SET(c, d, a, b, 15, 14, T23);
+ SET(b, c, d, a, 4, 20, T24);
+ SET(a, b, c, d, 9, 5, T25);
+ SET(d, a, b, c, 14, 9, T26);
+ SET(c, d, a, b, 3, 14, T27);
+ SET(b, c, d, a, 8, 20, T28);
+ SET(a, b, c, d, 13, 5, T29);
+ SET(d, a, b, c, 2, 9, T30);
+ SET(c, d, a, b, 7, 14, T31);
+ SET(b, c, d, a, 12, 20, T32);
+#undef SET
+
+ /* Round 3. */
+ /* Let [abcd k s t] denote the operation
+ a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */
+#define H(x, y, z) ((x) ^ (y) ^ (z))
+#define SET(a, b, c, d, k, s, Ti)\
+ t = a + H(b,c,d) + X[k] + Ti;\
+ a = ROTATE_LEFT(t, s) + b
+ /* Do the following 16 operations. */
+ SET(a, b, c, d, 5, 4, T33);
+ SET(d, a, b, c, 8, 11, T34);
+ SET(c, d, a, b, 11, 16, T35);
+ SET(b, c, d, a, 14, 23, T36);
+ SET(a, b, c, d, 1, 4, T37);
+ SET(d, a, b, c, 4, 11, T38);
+ SET(c, d, a, b, 7, 16, T39);
+ SET(b, c, d, a, 10, 23, T40);
+ SET(a, b, c, d, 13, 4, T41);
+ SET(d, a, b, c, 0, 11, T42);
+ SET(c, d, a, b, 3, 16, T43);
+ SET(b, c, d, a, 6, 23, T44);
+ SET(a, b, c, d, 9, 4, T45);
+ SET(d, a, b, c, 12, 11, T46);
+ SET(c, d, a, b, 15, 16, T47);
+ SET(b, c, d, a, 2, 23, T48);
+#undef SET
+
+ /* Round 4. */
+ /* Let [abcd k s t] denote the operation
+ a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */
+#define I(x, y, z) ((y) ^ ((x) | ~(z)))
+#define SET(a, b, c, d, k, s, Ti)\
+ t = a + I(b,c,d) + X[k] + Ti;\
+ a = ROTATE_LEFT(t, s) + b
+ /* Do the following 16 operations. */
+ SET(a, b, c, d, 0, 6, T49);
+ SET(d, a, b, c, 7, 10, T50);
+ SET(c, d, a, b, 14, 15, T51);
+ SET(b, c, d, a, 5, 21, T52);
+ SET(a, b, c, d, 12, 6, T53);
+ SET(d, a, b, c, 3, 10, T54);
+ SET(c, d, a, b, 10, 15, T55);
+ SET(b, c, d, a, 1, 21, T56);
+ SET(a, b, c, d, 8, 6, T57);
+ SET(d, a, b, c, 15, 10, T58);
+ SET(c, d, a, b, 6, 15, T59);
+ SET(b, c, d, a, 13, 21, T60);
+ SET(a, b, c, d, 4, 6, T61);
+ SET(d, a, b, c, 11, 10, T62);
+ SET(c, d, a, b, 2, 15, T63);
+ SET(b, c, d, a, 9, 21, T64);
+#undef SET
+
+ /* Then perform the following additions. (That is increment each
+ of the four registers by the value it had before this block
+ was started.) */
+ pms->abcd[0] += a;
+ pms->abcd[1] += b;
+ pms->abcd[2] += c;
+ pms->abcd[3] += d;
+}
+
+void
+md5_init(md5_state_t *pms)
+{
+ pms->count[0] = pms->count[1] = 0;
+ pms->abcd[0] = 0x67452301;
+ pms->abcd[1] = 0xefcdab89;
+ pms->abcd[2] = 0x98badcfe;
+ pms->abcd[3] = 0x10325476;
+}
+
+void
+md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes)
+{
+ const md5_byte_t *p = data;
+ int left = nbytes;
+ int offset = (pms->count[0] >> 3) & 63;
+ md5_word_t nbits = (md5_word_t)(nbytes << 3);
+
+ if (nbytes <= 0)
+ return;
+
+ /* Update the message length. */
+ pms->count[1] += nbytes >> 29;
+ pms->count[0] += nbits;
+ if (pms->count[0] < nbits)
+ pms->count[1]++;
+
+ /* Process an initial partial block. */
+ if (offset) {
+ int copy = (offset + nbytes > 64 ? 64 - offset : nbytes);
+
+ memcpy(pms->buf + offset, p, copy);
+ if (offset + copy < 64)
+ return;
+ p += copy;
+ left -= copy;
+ md5_process(pms, pms->buf);
+ }
+
+ /* Process full blocks. */
+ for (; left >= 64; p += 64, left -= 64)
+ md5_process(pms, p);
+
+ /* Process a final partial block. */
+ if (left)
+ memcpy(pms->buf, p, left);
+}
+
+void
+md5_finish(md5_state_t *pms, md5_byte_t digest[16])
+{
+ static const md5_byte_t pad[64] = {
+ 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ };
+ md5_byte_t data[8];
+ int i;
+
+ /* Save the length before padding. */
+ for (i = 0; i < 8; ++i)
+ data[i] = (md5_byte_t)(pms->count[i >> 2] >> ((i & 3) << 3));
+ /* Pad to 56 bytes mod 64. */
+ md5_append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1);
+ /* Append the length. */
+ md5_append(pms, data, 8);
+ for (i = 0; i < 16; ++i)
+ digest[i] = (md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3));
+}
diff --git a/cups/md5.h b/cups/md5.h
new file mode 100644
index 000000000..a2d7b3415
--- /dev/null
+++ b/cups/md5.h
@@ -0,0 +1,94 @@
+/*
+ Copyright (C) 1999 Aladdin Enterprises. All rights reserved.
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+
+ L. Peter Deutsch
+ ghost@aladdin.com
+
+ */
+/*$Id$ */
+/*
+ Independent implementation of MD5 (RFC 1321).
+
+ This code implements the MD5 Algorithm defined in RFC 1321.
+ It is derived directly from the text of the RFC and not from the
+ reference implementation.
+
+ The original and principal author of md5.h is L. Peter Deutsch
+ <ghost@aladdin.com>. Other authors are noted in the change history
+ that follows (in reverse chronological order):
+
+ 1999-11-04 lpd Edited comments slightly for automatic TOC extraction.
+ 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5);
+ added conditionalization for C++ compilation from Martin
+ Purschke <purschke@bnl.gov>.
+ 1999-05-03 lpd Original version.
+ */
+
+#ifndef md5_INCLUDED
+# define md5_INCLUDED
+
+/*
+ * This code has some adaptations for the Ghostscript environment, but it
+ * will compile and run correctly in any environment with 8-bit chars and
+ * 32-bit ints. Specifically, it assumes that if the following are
+ * defined, they have the same meaning as in Ghostscript: P1, P2, P3,
+ * ARCH_IS_BIG_ENDIAN.
+ */
+
+typedef unsigned char md5_byte_t; /* 8-bit byte */
+typedef unsigned int md5_word_t; /* 32-bit word */
+
+/* Define the state of the MD5 Algorithm. */
+typedef struct md5_state_s {
+ md5_word_t count[2]; /* message length in bits, lsw first */
+ md5_word_t abcd[4]; /* digest buffer */
+ md5_byte_t buf[64]; /* accumulate block */
+} md5_state_t;
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/* Initialize the algorithm. */
+#ifdef P1
+void md5_init(P1(md5_state_t *pms));
+#else
+void md5_init(md5_state_t *pms);
+#endif
+
+/* Append a string to the message. */
+#ifdef P3
+void md5_append(P3(md5_state_t *pms, const md5_byte_t *data, int nbytes));
+#else
+void md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes);
+#endif
+
+/* Finish the message and return the digest. */
+#ifdef P2
+void md5_finish(P2(md5_state_t *pms, md5_byte_t digest[16]));
+#else
+void md5_finish(md5_state_t *pms, md5_byte_t digest[16]);
+#endif
+
+#ifdef __cplusplus
+} /* end extern "C" */
+#endif
+
+#endif /* md5_INCLUDED */
diff --git a/cups/options.c b/cups/options.c
new file mode 100644
index 000000000..d8170ca5b
--- /dev/null
+++ b/cups/options.c
@@ -0,0 +1,379 @@
+/*
+ * "$Id$"
+ *
+ * Option routines for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-2000 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 (strcasecmp(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 (strcasecmp(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 (strcasecmp(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 (strcasecmp(options->name, "sides") == 0)
+ {
+ if (strcasecmp(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 (strcasecmp(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 (strcasecmp(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 (strcasecmp(options->name, "resolution") == 0 ||
+ strcasecmp(options->name, "printer-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..b9a206948
--- /dev/null
+++ b/cups/page.c
@@ -0,0 +1,189 @@
+/*
+ * "$Id$"
+ *
+ * Page size functions for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-2000 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%254s", &w, &l, units) < 2)
+ return (NULL);
+
+ if (strcasecmp(units, "in") == 0)
+ {
+ ppd->sizes[i].width = w * 72.0f;
+ ppd->sizes[i].length = l * 72.0f;
+ ppd->sizes[i].left = ppd->custom_margins[0];
+ ppd->sizes[i].bottom = ppd->custom_margins[1];
+ ppd->sizes[i].right = w * 72.0f - ppd->custom_margins[2];
+ ppd->sizes[i].top = l * 72.0f - ppd->custom_margins[3];
+ }
+ else if (strcasecmp(units, "cm") == 0)
+ {
+ ppd->sizes[i].width = w / 2.54f * 72.0f;
+ ppd->sizes[i].length = l / 2.54f * 72.0f;
+ ppd->sizes[i].left = ppd->custom_margins[0];
+ ppd->sizes[i].bottom = ppd->custom_margins[1];
+ ppd->sizes[i].right = w / 2.54f * 72.0f - ppd->custom_margins[2];
+ ppd->sizes[i].top = l / 2.54f * 72.0f - ppd->custom_margins[3];
+ }
+ else if (strcasecmp(units, "mm") == 0)
+ {
+ ppd->sizes[i].width = w / 25.4f * 72.0f;
+ ppd->sizes[i].length = l / 25.4f * 72.0f;
+ ppd->sizes[i].left = ppd->custom_margins[0];
+ ppd->sizes[i].bottom = ppd->custom_margins[1];
+ ppd->sizes[i].right = w / 25.4f * 72.0f - ppd->custom_margins[2];
+ ppd->sizes[i].top = l / 25.4f * 72.0f - 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..6d193625b
--- /dev/null
+++ b/cups/ppd.c
@@ -0,0 +1,1818 @@
+/*
+ * "$Id$"
+ *
+ * PPD file routines for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-2000 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);
+ }
+
+ DEBUG_printf(("ppdOpen: keyword = %s, string = %08x\n", keyword, string));
+
+ 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, "cupsFlipDuplex") == 0)
+ ppd->flip_duplex = strcmp(string, "True") == 0;
+ 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, "Throughput") == 0)
+ ppd->throughput = atoi(string);
+ else if (strcmp(keyword, "VariablePaperSize") == 0 &&
+ strcmp(string, "True") == 0 &&
+ !ppd->variable_sizes)
+ {
+ 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] = (float)atof(string);
+ else if (strcmp(keyword, "MaxMediaHeight") == 0)
+ ppd->custom_max[1] = (float)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)
+ {
+ if (!ppd->variable_sizes)
+ {
+ 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;
+ }
+
+ 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%40s%40s", &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..622866b10
--- /dev/null
+++ b/cups/ppd.h
@@ -0,0 +1,241 @@
+/*
+ * "$Id$"
+ *
+ * PostScript Printer Description definitions for the Common UNIX Printing
+ * System (CUPS).
+ *
+ * Copyright 1997-2000 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 */
+ throughput; /* Pages per minute */
+ 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... */
+ int flip_duplex; /* 1 = Flip page for back sides */
+} 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/snprintf.c b/cups/snprintf.c
new file mode 100644
index 000000000..2cb180ed9
--- /dev/null
+++ b/cups/snprintf.c
@@ -0,0 +1,287 @@
+/*
+ * "$Id$"
+ *
+ * snprintf functions for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-2000 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:
+ *
+ * vsnprintf() - Format a string into a fixed size buffer.
+ * snprintf() - Format a string into a fixed size buffer.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include "string.h"
+
+
+#ifndef HAVE_VSNPRINTF
+/*
+ * 'vsnprintf()' - Format a string into a fixed size buffer.
+ */
+
+int /* O - Number of bytes formatted */
+vsnprintf(char *buffer, /* O - Output buffer */
+ size_t bufsize, /* O - Size of output buffer */
+ const char *format, /* I - printf-style format string */
+ va_list ap) /* I - Pointer to additional arguments */
+{
+ char *bufptr, /* Pointer to position in buffer */
+ *bufend, /* Pointer to end of buffer */
+ sign, /* Sign of format width */
+ size, /* Size character (h, l, L) */
+ type; /* Format type character */
+ const char *bufformat; /* Start of format */
+ int width, /* Width of field */
+ prec; /* Number of characters of precision */
+ char tformat[100], /* Temporary format string for sprintf() */
+ temp[1024]; /* Buffer for formatted numbers */
+ int *chars; /* Pointer to integer for %p */
+ char *s; /* Pointer to string */
+ int slen; /* Length of string */
+
+
+ /*
+ * Loop through the format string, formatting as needed...
+ */
+
+ bufptr = buffer;
+ bufend = buffer + bufsize - 1;
+
+ while (*format && bufptr < bufend)
+ {
+ if (*format == '%')
+ {
+ bufformat = format;
+ format ++;
+
+ if (*format == '%')
+ {
+ *bufptr++ = *format++;
+ continue;
+ }
+ else if (strchr(" -+#\'", *format))
+ sign = *format++;
+ else
+ sign = 0;
+
+ width = 0;
+ while (isdigit(*format))
+ width = width * 10 + *format++ - '0';
+
+ if (*format == '.')
+ {
+ format ++;
+ prec = 0;
+
+ while (isdigit(*format))
+ prec = prec * 10 + *format++ - '0';
+ }
+ else
+ prec = -1;
+
+ if (*format == 'l' && format[1] == 'l')
+ {
+ size = 'L';
+ format += 2;
+ }
+ else if (*format == 'h' || *format == 'l' || *format == 'L')
+ size = *format++;
+
+ if (!*format)
+ break;
+
+ type = *format++;
+
+ switch (type)
+ {
+ case 'E' : /* Floating point formats */
+ case 'G' :
+ case 'e' :
+ case 'f' :
+ case 'g' :
+ if ((format - bufformat + 1) > sizeof(tformat) ||
+ (width + 2) > sizeof(temp))
+ break;
+
+ strncpy(tformat, bufformat, format - bufformat);
+ tformat[format - bufformat] = '\0';
+
+ sprintf(temp, tformat, va_arg(ap, double));
+
+ if ((bufptr + strlen(temp)) > bufend)
+ {
+ strncpy(bufptr, temp, bufend - bufptr);
+ bufptr = bufend;
+ break;
+ }
+ else
+ {
+ strcpy(bufptr, temp);
+ bufptr += strlen(temp);
+ }
+ break;
+
+ case 'B' : /* Integer formats */
+ case 'X' :
+ case 'b' :
+ case 'd' :
+ case 'i' :
+ case 'o' :
+ case 'u' :
+ case 'x' :
+ if ((format - bufformat + 1) > sizeof(tformat) ||
+ (width + 2) > sizeof(temp))
+ break;
+
+ strncpy(tformat, bufformat, format - bufformat);
+ tformat[format - bufformat] = '\0';
+
+ sprintf(temp, tformat, va_arg(ap, int));
+
+ if ((bufptr + strlen(temp)) > bufend)
+ {
+ strncpy(bufptr, temp, bufend - bufptr);
+ bufptr = bufend;
+ break;
+ }
+ else
+ {
+ strcpy(bufptr, temp);
+ bufptr += strlen(temp);
+ }
+ break;
+
+ case 'p' : /* Pointer value */
+ if ((chars = va_arg(ap, int *)) != NULL)
+ *chars = bufptr - buffer;
+ break;
+
+ case 'c' : /* Character or character array */
+ if (width <= 1)
+ *bufptr++ = va_arg(ap, int);
+ else
+ {
+ if ((bufptr + width) > bufend)
+ width = bufend - bufptr;
+
+ memcpy(bufptr, va_arg(ap, char *), width);
+ bufptr += width;
+ }
+ break;
+
+ case 's' : /* String */
+ if ((s = va_arg(ap, char *)) == NULL)
+ s = "(null)";
+
+ slen = strlen(s);
+ if (slen > width && prec != width)
+ width = slen;
+
+ if ((bufptr + width) > bufend)
+ width = bufend - bufptr;
+
+ if (slen > width)
+ slen = width;
+
+ if (sign == '-')
+ {
+ strncpy(bufptr, s, slen);
+ memset(bufptr + slen, ' ', width - slen);
+ }
+ else
+ {
+ memset(bufptr, ' ', width - slen);
+ strncpy(bufptr + width - slen, s, slen);
+ }
+
+ bufptr += width;
+ break;
+
+ case 'n' : /* Output number of chars so far */
+ if ((format - bufformat + 1) > sizeof(tformat) ||
+ (width + 2) > sizeof(temp))
+ break;
+
+ strncpy(tformat, bufformat, format - bufformat);
+ tformat[format - bufformat] = '\0';
+
+ sprintf(temp, tformat, va_arg(ap, int));
+
+ if ((bufptr + strlen(temp)) > bufend)
+ {
+ strncpy(bufptr, temp, bufend - bufptr);
+ bufptr = bufend;
+ break;
+ }
+ else
+ {
+ strcpy(bufptr, temp);
+ bufptr += strlen(temp);
+ }
+ break;
+ }
+ }
+ else
+ *bufptr++ = *format++;
+ }
+
+ /*
+ * Nul-terminate the string and return the number of characters in it.
+ */
+
+ *bufptr = '\0';
+ return (bufptr - buffer);
+}
+#endif /* !HAVE_VSNPRINT */
+
+
+#ifndef HAVE_SNPRINTF
+/*
+ * 'snprintf()' - Format a string into a fixed size buffer.
+ */
+
+int /* O - Number of bytes formatted */
+snprintf(char *buffer, /* O - Output buffer */
+ size_t bufsize, /* O - Size of output buffer */
+ const char *format, /* I - printf-style format string */
+ ...) /* I - Additional arguments as needed */
+{
+ int bytes; /* Number of bytes formatted */
+ va_list ap; /* Pointer to additional arguments */
+
+
+ va_start(ap, format);
+ bytes = vsnprintf(buffer, bufsize, format, ap);
+ va_end(ap);
+
+ return (bytes);
+}
+#endif /* !HAVE_SNPRINTF */
+
+
+/*
+ * End of "$Id$".
+ */
+
diff --git a/cups/string.c b/cups/string.c
new file mode 100644
index 000000000..b295162c7
--- /dev/null
+++ b/cups/string.c
@@ -0,0 +1,125 @@
+/*
+ * "$Id$"
+ *
+ * String functions for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-2000 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..5c3464ae7
--- /dev/null
+++ b/cups/string.h
@@ -0,0 +1,94 @@
+/*
+ * "$Id$"
+ *
+ * String definitions for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-2000 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 <stdio.h>
+# include <stdarg.h>
+# 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__ */
+
+
+/*
+ * C++ magic...
+ */
+
+# ifdef __cplusplus
+extern "C" {
+# endif /* __cplusplus */
+
+
+/*
+ * 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 */
+
+# ifndef HAVE_SNPRINTF
+extern int snprintf(char *, size_t, const char *, ...);
+# endif /* !HAVE_SNPRINTF */
+
+# ifndef HAVE_VSNPRINTF
+extern int vsnprintf(char *, size_t, const char *, va_list);
+# endif /* !HAVE_VSNPRINTF */
+
+
+/*
+ * C++ magic...
+ */
+
+# ifdef __cplusplus
+}
+# endif /* __cplusplus */
+
+#endif /* !_CUPS_STRING_H_ */
+
+/*
+ * End of "$Id$".
+ */
diff --git a/cups/testhttp.c b/cups/testhttp.c
new file mode 100644
index 000000000..a800feb51
--- /dev/null
+++ b/cups/testhttp.c
@@ -0,0 +1,109 @@
+/*
+ * "$Id$"
+ *
+ * HTTP test program for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-2000 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 */
+ long 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 %ld bytes, %ld 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.dsp b/cups/testmime.dsp
new file mode 100644
index 000000000..33fbd301a
--- /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 ".." /I "../visualc" /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..bf9e5b21c
--- /dev/null
+++ b/cups/testppd.c
@@ -0,0 +1,183 @@
+/*
+ * "$Id$"
+ *
+ * PPD test program for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-2000 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; /* 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..27d4f035c
--- /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 ".." /I "../visualc" /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/usersys.c b/cups/usersys.c
new file mode 100644
index 000000000..5837d7609
--- /dev/null
+++ b/cups/usersys.c
@@ -0,0 +1,193 @@
+/*
+ * "$Id$"
+ *
+ * User, system, and password routines for the Common UNIX Printing
+ * System (CUPS).
+ *
+ * Copyright 1997-2000 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:
+ *
+ * cupsUser() - Return the current users name.
+ * cupsGetPassword() - Get a password from the user...
+ * cupsServer() - Return the hostname of the default server...
+ */
+
+/*
+ * 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; /* client.conf file */
+ char *server; /* Pointer to server name */
+ const char *home; /* Home directory of user */
+ 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 $HOME/.cupsrc or client.conf file...
+ */
+
+ if ((home = getenv("HOME")) != NULL)
+ {
+ snprintf(line, sizeof(line), "%s/.cupsrc", home);
+ fp = fopen(line, "r");
+ }
+ else
+ fp = NULL;
+
+ if (fp == NULL)
+ fp = fopen(CUPS_SERVERROOT "/client.conf", "r");
+
+ if (fp == NULL)
+ return ("localhost");
+
+ /*
+ * Read the config 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)
+ {
+ fclose(fp);
+ 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..04f97f47a
--- /dev/null
+++ b/cups/util.c
@@ -0,0 +1,1273 @@
+/*
+ * "$Id$"
+ *
+ * Printing utilities for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-2000 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...
+ * cups_local_auth() - Get the local authorization certificate if
+ * available/applicable...
+ */
+
+/*
+ * 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; /* Current server connection */
+static ipp_status_t last_error = IPP_OK; /* Last IPP error */
+static char authstring[255] = ""; /* Authorization string */
+
+
+/*
+ * Local functions...
+ */
+
+static char *cups_connect(const char *name, char *printer, char *hostname);
+static int cups_local_auth(http_t *http);
+
+
+/*
+ * '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))
+ {
+ last_error = IPP_SERVICE_UNAVAILABLE;
+ return (0);
+ }
+
+ /*
+ * Build an IPP_CANCEL_JOB request, which requires the following
+ * attributes:
+ *
+ * attributes-charset
+ * attributes-natural-language
+ * printer-uri
+ * job-id
+ * [requesting-user-name]
+ */
+
+ 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");
+
+ snprintf(uri, sizeof(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);
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
+ NULL, cupsUser());
+
+ /*
+ * Do the request...
+ */
+
+ if ((response = cupsDoRequest(cups_server, request, "/jobs/")) == NULL)
+ {
+ last_error = IPP_BAD_REQUEST;
+ return (0);
+ }
+ else
+ {
+ last_error = response->request.status.status_code;
+ 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 */
+
+
+ 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);
+ last_error = IPP_NOT_FOUND;
+ return (NULL);
+ }
+
+ if ((file = fopen(filename, "rb")) == NULL)
+ {
+ /*
+ * Can't open file!
+ */
+
+ ippDelete(request);
+ last_error = IPP_NOT_FOUND;
+ return (NULL);
+ }
+ }
+ else
+ file = NULL;
+
+ /*
+ * Loop until we can send the request without authorization problems.
+ */
+
+ response = NULL;
+ status = HTTP_ERROR;
+
+ 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))
+ 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);
+
+ /*
+ * See if we can do local authentication...
+ */
+
+ if (cups_local_auth(http))
+ continue;
+
+ /*
+ * Nope - get a password from the user...
+ */
+
+ printf("Authentication required for %s on %s...\n", cupsUser(),
+ http->hostname);
+
+ if ((password = cupsGetPassword("UNIX Password: ")) != NULL)
+ {
+ /*
+ * Got a password; send it to the server...
+ */
+
+ if (!password[0])
+ break;
+ snprintf(plain, sizeof(plain), "%s:%s", cupsUser(), password);
+ httpEncode64(encode, plain);
+ snprintf(authstring, sizeof(authstring), "Basic %s", encode);
+
+ continue;
+ }
+ else
+ break;
+ }
+ else if (status == HTTP_ERROR)
+ {
+#if defined(WIN32) || defined(__EMX__)
+ if (http->error != WSAENETDOWN && http->error != WSAENETUNREACH)
+#else
+ if (http->error != ENETDOWN && http->error != ENETUNREACH)
+#endif /* WIN32 || __EMX__ */
+ continue;
+ else
+ break;
+ }
+ else 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;
+
+ last_error = IPP_SERVICE_UNAVAILABLE;
+
+ /*
+ * 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);
+
+ if (response)
+ last_error = response->request.status.status_code;
+ else if (status == HTTP_NOT_FOUND)
+ last_error = IPP_NOT_FOUND;
+ else if (status == HTTP_UNAUTHORIZED)
+ last_error = IPP_NOT_AUTHORIZED;
+ else if (status != HTTP_OK)
+ last_error = IPP_SERVICE_UNAVAILABLE;
+
+ 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 */
+ char **temp; /* Temporary pointer */
+
+
+ /*
+ * Try to connect to the server...
+ */
+
+ if (!cups_connect("default", NULL, NULL))
+ {
+ last_error = IPP_SERVICE_UNAVAILABLE;
+ 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, "/")) != NULL)
+ {
+ last_error = response->request.status.status_code;
+
+ for (attr = response->attrs; attr != NULL; attr = attr->next)
+ if (attr->name != NULL &&
+ strcasecmp(attr->name, "printer-name") == 0 &&
+ attr->value_tag == IPP_TAG_NAME)
+ {
+ if (n == 0)
+ temp = malloc(sizeof(char *));
+ else
+ temp = realloc(*classes, sizeof(char *) * (n + 1));
+
+ if (temp == NULL)
+ {
+ /*
+ * Ran out of memory!
+ */
+
+ while (n > 0)
+ {
+ n --;
+ free((*classes)[n]);
+ }
+
+ free(*classes);
+ ippDelete(response);
+ return (0);
+ }
+
+ *classes = temp;
+ temp[n] = strdup(attr->values[0].string.text);
+ n ++;
+ }
+
+ ippDelete(response);
+ }
+ else
+ last_error = IPP_BAD_REQUEST;
+
+ return (n);
+}
+
+
+/*
+ * 'cupsGetDefault()' - Get the default printer or class.
+ */
+
+const char * /* O - Default printer or NULL */
+cupsGetDefault(void)
+{
+ FILE *fp; /* cupsd.conf file */
+ char *printer; /* Pointer to server name */
+ char line[1024]; /* Line from file */
+ ipp_t *request, /* IPP Request */
+ *response; /* IPP Response */
+ ipp_attribute_t *attr; /* Current attribute */
+ cups_lang_t *language; /* Default language */
+ const char *var; /* Environment variable */
+ static char def_printer[256];/* Default printer */
+
+
+ /*
+ * First see if the LPDEST or PRINTER environment variables are
+ * set... However, if PRINTER is set to "lp", ignore it to work
+ * around a "feature" in most Linux distributions - the default
+ * user login scripts set PRINTER to "lp"...
+ */
+
+ if ((var = getenv("LPDEST")) != NULL)
+ return (var);
+ else if ((var = getenv("PRINTER")) != NULL && strcmp(var, "lp") != 0)
+ return (var);
+
+ /*
+ * Next check to see if we have a client.conf file...
+ */
+
+ if ((fp = fopen(CUPS_SERVERROOT "/client.conf", "r")) != NULL)
+ {
+ /*
+ * Read the client.conf file and look for a DefaultPrinter line...
+ */
+
+ while (fgets(line, sizeof(line), fp) != NULL)
+ if (strncmp(line, "DefaultPrinter ", 15) == 0)
+ {
+ /*
+ * Got it! Drop any trailing newline and find the name...
+ */
+
+ printer = line + strlen(line) - 1;
+ if (*printer == '\n')
+ *printer = '\0';
+
+ for (printer = line + 15; isspace(*printer); printer ++);
+
+ if (*printer)
+ {
+ strncpy(def_printer, printer, sizeof(def_printer) - 1);
+ def_printer[sizeof(def_printer) - 1] = '\0';
+
+ fclose(fp);
+
+ return (def_printer);
+ }
+ }
+
+ fclose(fp);
+ }
+
+ /*
+ * Try to connect to the server...
+ */
+
+ if (!cups_connect("default", NULL, NULL))
+ {
+ last_error = IPP_SERVICE_UNAVAILABLE;
+ 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, "/")) != NULL)
+ {
+ last_error = response->request.status.status_code;
+
+ if ((attr = ippFindAttribute(response, "printer-name", IPP_TAG_NAME)) != NULL)
+ {
+ strncpy(def_printer, attr->values[0].string.text, sizeof(def_printer) - 1);
+ def_printer[sizeof(def_printer) - 1] = '\0';
+ ippDelete(response);
+ return (def_printer);
+ }
+
+ ippDelete(response);
+ }
+ else
+ last_error = IPP_BAD_REQUEST;
+
+ 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 */
+ char *tempdir; /* Temporary file directory */
+ const char *password; /* Password string */
+ char plain[255], /* Plaintext username:password */
+ encode[255]; /* Encoded username:password */
+ http_status_t status; /* HTTP status from server */
+ static char filename[HTTP_MAX_URI]; /* Local filename */
+
+
+ /*
+ * See if we can connect to the server...
+ */
+
+ if (!cups_connect(name, printer, hostname))
+ {
+ last_error = IPP_SERVICE_UNAVAILABLE;
+ return (NULL);
+ }
+
+ /*
+ * Then check for the cache file...
+ */
+
+#if defined(WIN32) || defined(__EMX__)
+ tempdir = "C:/WINDOWS/TEMP";
+
+ snprintf(filename, sizeof(filename), "%s/%s.ppd", tempdir, printer);
+#else
+ if ((tempdir = getenv("TMPDIR")) == NULL)
+ tempdir = "/tmp";
+
+ snprintf(filename, sizeof(filename), "%s/%d.%s.ppd", tempdir, getuid(), printer);
+#endif /* WIN32 || __EMX__ */
+
+ /*
+ * And send a request to the HTTP server...
+ */
+
+ snprintf(resource, sizeof(resource), "/printers/%s.ppd", printer);
+
+ do
+ {
+ httpClearFields(cups_server);
+ httpSetField(cups_server, HTTP_FIELD_HOST, hostname);
+ httpSetField(cups_server, HTTP_FIELD_AUTHORIZATION, authstring);
+
+ if (httpGet(cups_server, resource))
+ {
+ status = HTTP_UNAUTHORIZED;
+ continue;
+ }
+
+ while ((status = httpUpdate(cups_server)) == HTTP_CONTINUE);
+
+ if (status == HTTP_UNAUTHORIZED)
+ {
+ DEBUG_puts("cupsGetPPD: unauthorized...");
+
+ /*
+ * Flush any error message...
+ */
+
+ httpFlush(cups_server);
+
+ /*
+ * See if we can do local authentication...
+ */
+
+ if (cups_local_auth(cups_server))
+ continue;
+
+ /*
+ * Nope, get a password from the user...
+ */
+
+ printf("Authentication required for %s on %s...\n", cupsUser(),
+ cups_server->hostname);
+
+ if ((password = cupsGetPassword("UNIX Password: ")) != NULL)
+ {
+ /*
+ * Got a password; send it to the server...
+ */
+
+ if (!password[0])
+ break;
+ snprintf(plain, sizeof(plain), "%s:%s", cupsUser(), password);
+ httpEncode64(encode, plain);
+ snprintf(authstring, sizeof(authstring), "Basic %s", encode);
+
+ continue;
+ }
+ else
+ break;
+ }
+ }
+ while (status == HTTP_UNAUTHORIZED);
+
+ /*
+ * 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 */
+ char **temp; /* Temporary pointer */
+
+
+ /*
+ * Try to connect to the server...
+ */
+
+ if (!cups_connect("default", NULL, NULL))
+ {
+ last_error = IPP_SERVICE_UNAVAILABLE;
+ 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, "/")) != NULL)
+ {
+ last_error = response->request.status.status_code;
+
+ for (attr = response->attrs; attr != NULL; attr = attr->next)
+ if (attr->name != NULL &&
+ strcasecmp(attr->name, "printer-name") == 0 &&
+ attr->value_tag == IPP_TAG_NAME)
+ {
+ if (n == 0)
+ temp = malloc(sizeof(char *));
+ else
+ temp = realloc(*printers, sizeof(char *) * (n + 1));
+
+ if (temp == NULL)
+ {
+ /*
+ * Ran out of memory!
+ */
+
+ while (n > 0)
+ {
+ n --;
+ free((*printers)[n]);
+ }
+
+ free(*printers);
+ ippDelete(response);
+ return (0);
+ }
+
+ *printers = temp;
+ temp[n] = strdup(attr->values[0].string.text);
+ n ++;
+ }
+
+ ippDelete(response);
+ }
+ else
+ last_error = IPP_BAD_REQUEST;
+
+ return (n);
+}
+
+
+/*
+ * 'cupsLastError()' - Return the last IPP error that occurred.
+ */
+
+ipp_status_t /* O - IPP error code */
+cupsLastError(void)
+{
+ return (last_error);
+}
+
+
+/*
+ * '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)));
+ last_error = IPP_SERVICE_UNAVAILABLE;
+ 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;
+
+ snprintf(uri, sizeof(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 (strcasecmp(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 (strncasecmp(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 (strcasecmp(s, "dpc") == 0)
+ ippAddResolution(request, IPP_TAG_JOB, option, IPP_RES_PER_CM, n, n2);
+ else if (strcasecmp(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...
+ */
+
+ snprintf(uri, sizeof(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 ((int)(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 [HTTP_MAX_URI] */
+ char *hostname) /* O - Hostname [HTTP_MAX_URI] */
+{
+ char hostbuf[HTTP_MAX_URI];
+ /* Name of host */
+ static char printerbuf[HTTP_MAX_URI];
+ /* Name of printer or class */
+
+
+ if (name == NULL)
+ {
+ last_error = IPP_BAD_REQUEST;
+ return (NULL);
+ }
+
+ if (sscanf(name, "%1023[^@]@%1023s", printerbuf, hostbuf) == 1)
+ {
+ strncpy(hostbuf, cupsServer(), sizeof(hostbuf) - 1);
+ hostbuf[sizeof(hostbuf) - 1] = '\0';
+ }
+
+ if (hostname != NULL)
+ {
+ strncpy(hostname, hostbuf, HTTP_MAX_URI - 1);
+ hostname[HTTP_MAX_URI - 1] = '\0';
+ }
+ else
+ hostname = hostbuf;
+
+ if (printer != NULL)
+ {
+ strncpy(printer, printerbuf, HTTP_MAX_URI - 1);
+ printer[HTTP_MAX_URI - 1] = '\0';
+ }
+ 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)
+ {
+ last_error = IPP_SERVICE_UNAVAILABLE;
+ return (NULL);
+ }
+ else
+ return (printer);
+}
+
+
+/*
+ * 'cups_local_auth()' - Get the local authorization certificate if
+ * available/applicable...
+ */
+
+static int /* O - 1 if available, 0 if not */
+cups_local_auth(http_t *http) /* I - Connection */
+{
+#if defined(WIN32) || defined(__EMX__)
+ /*
+ * Currently WIN32 and OS-2 do not support the CUPS server...
+ */
+
+ return (0);
+#else
+ int pid; /* Current process ID */
+ FILE *fp; /* Certificate file */
+ char filename[1024], /* Certificate filename */
+ certificate[33]; /* Certificate string */
+
+
+ /*
+ * See if we are accessing localhost...
+ */
+
+ if (ntohl(http->hostaddr.sin_addr.s_addr) != 0x7f000001 &&
+ strcasecmp(http->hostname, "localhost") != 0)
+ return (0);
+
+ /*
+ * Try opening a certificate file for this PID. If that fails,
+ * try the root certificate...
+ */
+
+ pid = getpid();
+ sprintf(filename, CUPS_SERVERROOT "/certs/%d", pid);
+ if ((fp = fopen(filename, "r")) == NULL && pid > 0)
+ fp = fopen(CUPS_SERVERROOT "/certs/0", "r");
+
+ if (fp == NULL)
+ return (0);
+
+ /*
+ * Read the certificate from the file...
+ */
+
+ fgets(certificate, sizeof(certificate), fp);
+ fclose(fp);
+
+ /*
+ * Set the authorization string and return...
+ */
+
+ sprintf(authstring, "Local %s", certificate);
+
+ return (1);
+#endif /* WIN32 || __EMX__ */
+}
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/data/8859-1 b/data/8859-1
new file mode 100644
index 000000000..b680efc7b
--- /dev/null
+++ b/data/8859-1
@@ -0,0 +1,216 @@
+0x20 0x0020
+0x21 0x0021
+0x22 0x0022
+0x23 0x0023
+0x24 0x0024
+0x25 0x0025
+0x26 0x0026
+0x27 0x0027
+0x28 0x0028
+0x29 0x0029
+0x2a 0x002a
+0x2b 0x002b
+0x2c 0x002c
+0x2d 0x002d
+0x2e 0x002e
+0x2f 0x002f
+0x30 0x0030
+0x31 0x0031
+0x32 0x0032
+0x33 0x0033
+0x34 0x0034
+0x35 0x0035
+0x36 0x0036
+0x37 0x0037
+0x38 0x0038
+0x39 0x0039
+0x3a 0x003a
+0x3b 0x003b
+0x3c 0x003c
+0x3d 0x003d
+0x3e 0x003e
+0x3f 0x003f
+0x40 0x0040
+0x41 0x0041
+0x42 0x0042
+0x43 0x0043
+0x44 0x0044
+0x45 0x0045
+0x46 0x0046
+0x47 0x0047
+0x48 0x0048
+0x49 0x0049
+0x4a 0x004a
+0x4b 0x004b
+0x4c 0x004c
+0x4d 0x004d
+0x4e 0x004e
+0x4f 0x004f
+0x50 0x0050
+0x51 0x0051
+0x52 0x0052
+0x53 0x0053
+0x54 0x0054
+0x55 0x0055
+0x56 0x0056
+0x57 0x0057
+0x58 0x0058
+0x59 0x0059
+0x5a 0x005a
+0x5b 0x005b
+0x5c 0x005c
+0x5d 0x005d
+0x5e 0x005e
+0x5f 0x005f
+0x60 0x0060
+0x61 0x0061
+0x62 0x0062
+0x63 0x0063
+0x64 0x0064
+0x65 0x0065
+0x66 0x0066
+0x67 0x0067
+0x68 0x0068
+0x69 0x0069
+0x6a 0x006a
+0x6b 0x006b
+0x6c 0x006c
+0x6d 0x006d
+0x6e 0x006e
+0x6f 0x006f
+0x70 0x0070
+0x71 0x0071
+0x72 0x0072
+0x73 0x0073
+0x74 0x0074
+0x75 0x0075
+0x76 0x0076
+0x77 0x0077
+0x78 0x0078
+0x79 0x0079
+0x7a 0x007a
+0x7b 0x007b
+0x7c 0x007c
+0x7d 0x007d
+0x7e 0x007e
+0x80 0x20ac
+0x82 0x201a
+0x83 0x0192
+0x84 0x201e
+0x85 0x2026
+0x86 0x2020
+0x87 0x2021
+0x88 0x02c6
+0x89 0x2030
+0x8a 0x0160
+0x8b 0x2039
+0x8c 0x0152
+0x91 0x2018
+0x92 0x2019
+0x93 0x201c
+0x94 0x201d
+0x95 0x2022
+0x96 0x2013
+0x97 0x2014
+0x98 0x02dc
+0x99 0x2122
+0x9a 0x0161
+0x9b 0x203a
+0x9c 0x0153
+0x9f 0x0178
+0xa0 0x00a0
+0xa1 0x00a1
+0xa2 0x00a2
+0xa3 0x00a3
+0xa4 0x00a4
+0xa5 0x00a5
+0xa6 0x00a6
+0xa7 0x00a7
+0xa8 0x00a8
+0xa9 0x00a9
+0xaa 0x00aa
+0xab 0x00ab
+0xac 0x00ac
+0xad 0x00ad
+0xae 0x00ae
+0xaf 0x00af
+0xb0 0x00b0
+0xb1 0x00b1
+0xb2 0x00b2
+0xb3 0x00b3
+0xb4 0x00b4
+0xb5 0x00b5
+0xb6 0x00b6
+0xb7 0x00b7
+0xb8 0x00b8
+0xb9 0x00b9
+0xba 0x00ba
+0xbb 0x00bb
+0xbc 0x00bc
+0xbd 0x00bd
+0xbe 0x00be
+0xbf 0x00bf
+0xc0 0x00c0
+0xc1 0x00c1
+0xc2 0x00c2
+0xc3 0x00c3
+0xc4 0x00c4
+0xc5 0x00c5
+0xc6 0x00c6
+0xc7 0x00c7
+0xc8 0x00c8
+0xc9 0x00c9
+0xca 0x00ca
+0xcb 0x00cb
+0xcc 0x00cc
+0xcd 0x00cd
+0xce 0x00ce
+0xcf 0x00cf
+0xd0 0x00d0
+0xd1 0x00d1
+0xd2 0x00d2
+0xd3 0x00d3
+0xd4 0x00d4
+0xd5 0x00d5
+0xd6 0x00d6
+0xd7 0x00d7
+0xd8 0x00d8
+0xd9 0x00d9
+0xda 0x00da
+0xdb 0x00db
+0xdc 0x00dc
+0xdd 0x00dd
+0xde 0x00de
+0xdf 0x00df
+0xe0 0x00e0
+0xe1 0x00e1
+0xe2 0x00e2
+0xe3 0x00e3
+0xe4 0x00e4
+0xe5 0x00e5
+0xe6 0x00e6
+0xe7 0x00e7
+0xe8 0x00e8
+0xe9 0x00e9
+0xea 0x00ea
+0xeb 0x00eb
+0xec 0x00ec
+0xed 0x00ed
+0xee 0x00ee
+0xef 0x00ef
+0xf0 0x00f0
+0xf1 0x00f1
+0xf2 0x00f2
+0xf3 0x00f3
+0xf4 0x00f4
+0xf5 0x00f5
+0xf6 0x00f6
+0xf7 0x00f7
+0xf8 0x00f8
+0xf9 0x00f9
+0xfa 0x00fa
+0xfb 0x00fb
+0xfc 0x00fc
+0xfd 0x00fd
+0xfe 0x00fe
+0xff 0x00ff
diff --git a/data/8859-14 b/data/8859-14
new file mode 100644
index 000000000..648be60bf
--- /dev/null
+++ b/data/8859-14
@@ -0,0 +1,217 @@
+0x20 0x0020
+0x21 0x0021
+0x22 0x0022
+0x23 0x0023
+0x24 0x0024
+0x25 0x0025
+0x26 0x0026
+0x27 0x0027
+0x28 0x0028
+0x29 0x0029
+0x2a 0x002a
+0x2b 0x002b
+0x2c 0x002c
+0x2d 0x002d
+0x2e 0x002e
+0x2f 0x002f
+0x30 0x0030
+0x31 0x0031
+0x32 0x0032
+0x33 0x0033
+0x34 0x0034
+0x35 0x0035
+0x36 0x0036
+0x37 0x0037
+0x38 0x0038
+0x39 0x0039
+0x3a 0x003a
+0x3b 0x003b
+0x3c 0x003c
+0x3d 0x003d
+0x3e 0x003e
+0x3f 0x003f
+0x40 0x0040
+0x41 0x0041
+0x42 0x0042
+0x43 0x0043
+0x44 0x0044
+0x45 0x0045
+0x46 0x0046
+0x47 0x0047
+0x48 0x0048
+0x49 0x0049
+0x4a 0x004a
+0x4b 0x004b
+0x4c 0x004c
+0x4d 0x004d
+0x4e 0x004e
+0x4f 0x004f
+0x50 0x0050
+0x51 0x0051
+0x52 0x0052
+0x53 0x0053
+0x54 0x0054
+0x55 0x0055
+0x56 0x0056
+0x57 0x0057
+0x58 0x0058
+0x59 0x0059
+0x5a 0x005a
+0x5b 0x005b
+0x5c 0x005c
+0x5d 0x005d
+0x5e 0x005e
+0x5f 0x005f
+0x60 0x0060
+0x61 0x0061
+0x62 0x0062
+0x63 0x0063
+0x64 0x0064
+0x65 0x0065
+0x66 0x0066
+0x67 0x0067
+0x68 0x0068
+0x69 0x0069
+0x6a 0x006a
+0x6b 0x006b
+0x6c 0x006c
+0x6d 0x006d
+0x6e 0x006e
+0x6f 0x006f
+0x70 0x0070
+0x71 0x0071
+0x72 0x0072
+0x73 0x0073
+0x74 0x0074
+0x75 0x0075
+0x76 0x0076
+0x77 0x0077
+0x78 0x0078
+0x79 0x0079
+0x7a 0x007a
+0x7b 0x007b
+0x7c 0x007c
+0x7d 0x007d
+0x7e 0x007e
+0x80 0x20ac
+0x82 0x201a
+0x83 0x0192
+0x84 0x201e
+0x85 0x2026
+0x86 0x2020
+0x87 0x2021
+0x88 0x02c6
+0x89 0x2030
+0x8a 0x0160
+0x8b 0x2039
+0x8c 0x0152
+0x91 0x2018
+0x92 0x2019
+0x93 0x201c
+0x94 0x201d
+0x95 0x2022
+0x96 0x2013
+0x97 0x2014
+0x98 0x02dc
+0x99 0x2122
+0x9a 0x0161
+0x9b 0x203a
+0x9c 0x0153
+0x9f 0x0178
+0xa0 0x00a0
+0xa1 0x1e02
+0xa2 0x1e03
+0xa3 0x00a3
+0xa4 0x010a
+0xa5 0x010b
+0xa6 0x1e0a
+0xa7 0x00a7
+0xa8 0x1e80
+0xa9 0x00a9
+0xaa 0x1e82
+0xab 0x1e0b
+0xac 0x1ef2
+0xad 0x00ad
+0xae 0x00ae
+0xaf 0x0178
+0xb0 0x1e1e
+0xb1 0x1e1f
+0xb2 0x0120
+0xb3 0x0121
+0xb4 0x1e40
+0xb5 0x1e41
+0xb6 0x00b6
+0xb7 0x1e56
+0xb8 0x1e81
+0xb9 0x1e57
+0xba 0x1e83
+0xbb 0x1e60
+0xbc 0x1ef3
+0xbd 0x1e84
+0xbe 0x1e85
+0xbf 0x1e61
+0xc0 0x00c0
+0xc1 0x00c1
+0xc2 0x00c2
+0xc3 0x00c3
+0xc4 0x00c4
+0xc5 0x00c5
+0xc6 0x00c6
+0xc7 0x00c7
+0xc8 0x00c8
+0xc9 0x00c9
+0xca 0x00ca
+0xcb 0x00cb
+0xcc 0x00cc
+0xcd 0x00cd
+0xce 0x00ce
+0xcf 0x00cf
+0xd0 0x0174
+0xd1 0x00d1
+0xd2 0x00d2
+0xd3 0x00d3
+0xd4 0x00d4
+0xd5 0x00d5
+0xd6 0x00d6
+0xd7 0x1e6a
+0xd8 0x00d8
+0xd9 0x00d9
+0xda 0x00da
+0xdb 0x00db
+0xdc 0x00dc
+0xdd 0x00dd
+0xde 0x0176
+0xdf 0x00df
+0xe0 0x00e0
+0xe1 0x00e1
+0xe2 0x00e2
+0xe3 0x00e3
+0xe4 0x00e4
+0xe5 0x00e5
+0xe6 0x00e6
+0xe7 0x00e7
+0xe8 0x00e8
+0xe9 0x00e9
+0xea 0x00ea
+0xeb 0x00eb
+0xec 0x00ec
+0xed 0x00ed
+0xee 0x00ee
+0xef 0x00ef
+0xf0 0x0175
+0xf1 0x00f1
+0xf2 0x00f2
+0xf3 0x00f3
+0xf4 0x00f4
+0xf5 0x00f5
+0xf6 0x00f6
+0xf7 0x1e6b
+0xf8 0x00f8
+0xf9 0x00f9
+0xfa 0x00fa
+0xfb 0x00fb
+0xfc 0x00fc
+0xfd 0x00fd
+0xfe 0x0177
+0xff 0x00ff
+
diff --git a/data/8859-15 b/data/8859-15
new file mode 100644
index 000000000..fa50e1e74
--- /dev/null
+++ b/data/8859-15
@@ -0,0 +1,217 @@
+0x20 0x0020
+0x21 0x0021
+0x22 0x0022
+0x23 0x0023
+0x24 0x0024
+0x25 0x0025
+0x26 0x0026
+0x27 0x0027
+0x28 0x0028
+0x29 0x0029
+0x2a 0x002a
+0x2b 0x002b
+0x2c 0x002c
+0x2d 0x002d
+0x2e 0x002e
+0x2f 0x002f
+0x30 0x0030
+0x31 0x0031
+0x32 0x0032
+0x33 0x0033
+0x34 0x0034
+0x35 0x0035
+0x36 0x0036
+0x37 0x0037
+0x38 0x0038
+0x39 0x0039
+0x3a 0x003a
+0x3b 0x003b
+0x3c 0x003c
+0x3d 0x003d
+0x3e 0x003e
+0x3f 0x003f
+0x40 0x0040
+0x41 0x0041
+0x42 0x0042
+0x43 0x0043
+0x44 0x0044
+0x45 0x0045
+0x46 0x0046
+0x47 0x0047
+0x48 0x0048
+0x49 0x0049
+0x4a 0x004a
+0x4b 0x004b
+0x4c 0x004c
+0x4d 0x004d
+0x4e 0x004e
+0x4f 0x004f
+0x50 0x0050
+0x51 0x0051
+0x52 0x0052
+0x53 0x0053
+0x54 0x0054
+0x55 0x0055
+0x56 0x0056
+0x57 0x0057
+0x58 0x0058
+0x59 0x0059
+0x5a 0x005a
+0x5b 0x005b
+0x5c 0x005c
+0x5d 0x005d
+0x5e 0x005e
+0x5f 0x005f
+0x60 0x0060
+0x61 0x0061
+0x62 0x0062
+0x63 0x0063
+0x64 0x0064
+0x65 0x0065
+0x66 0x0066
+0x67 0x0067
+0x68 0x0068
+0x69 0x0069
+0x6a 0x006a
+0x6b 0x006b
+0x6c 0x006c
+0x6d 0x006d
+0x6e 0x006e
+0x6f 0x006f
+0x70 0x0070
+0x71 0x0071
+0x72 0x0072
+0x73 0x0073
+0x74 0x0074
+0x75 0x0075
+0x76 0x0076
+0x77 0x0077
+0x78 0x0078
+0x79 0x0079
+0x7a 0x007a
+0x7b 0x007b
+0x7c 0x007c
+0x7d 0x007d
+0x7e 0x007e
+0x80 0x20ac
+0x82 0x201a
+0x83 0x0192
+0x84 0x201e
+0x85 0x2026
+0x86 0x2020
+0x87 0x2021
+0x88 0x02c6
+0x89 0x2030
+0x8a 0x0160
+0x8b 0x2039
+0x8c 0x0152
+0x91 0x2018
+0x92 0x2019
+0x93 0x201c
+0x94 0x201d
+0x95 0x2022
+0x96 0x2013
+0x97 0x2014
+0x98 0x02dc
+0x99 0x2122
+0x9a 0x0161
+0x9b 0x203a
+0x9c 0x0153
+0x9f 0x0178
+0xa0 0x00a0
+0xa1 0x00a1
+0xa2 0x00a2
+0xa3 0x00a3
+0xa4 0x20ac
+0xa5 0x00a5
+0xa6 0x0160
+0xa7 0x00a7
+0xa8 0x0161
+0xa9 0x00a9
+0xaa 0x00aa
+0xab 0x00ab
+0xac 0x00ac
+0xad 0x00ad
+0xae 0x00ae
+0xaf 0x00af
+0xb0 0x00b0
+0xb1 0x00b1
+0xb2 0x00b2
+0xb3 0x00b3
+0xb4 0x017d
+0xb5 0x00b5
+0xb6 0x00b6
+0xb7 0x00b7
+0xb8 0x017e
+0xb9 0x00b9
+0xba 0x00ba
+0xbb 0x00bb
+0xbc 0x0152
+0xbd 0x0153
+0xbe 0x0178
+0xbf 0x00bf
+0xc0 0x00c0
+0xc1 0x00c1
+0xc2 0x00c2
+0xc3 0x00c3
+0xc4 0x00c4
+0xc5 0x00c5
+0xc6 0x00c6
+0xc7 0x00c7
+0xc8 0x00c8
+0xc9 0x00c9
+0xca 0x00ca
+0xcb 0x00cb
+0xcc 0x00cc
+0xcd 0x00cd
+0xce 0x00ce
+0xcf 0x00cf
+0xd0 0x00d0
+0xd1 0x00d1
+0xd2 0x00d2
+0xd3 0x00d3
+0xd4 0x00d4
+0xd5 0x00d5
+0xd6 0x00d6
+0xd7 0x00d7
+0xd8 0x00d8
+0xd9 0x00d9
+0xda 0x00da
+0xdb 0x00db
+0xdc 0x00dc
+0xdd 0x00dd
+0xde 0x00de
+0xdf 0x00df
+0xe0 0x00e0
+0xe1 0x00e1
+0xe2 0x00e2
+0xe3 0x00e3
+0xe4 0x00e4
+0xe5 0x00e5
+0xe6 0x00e6
+0xe7 0x00e7
+0xe8 0x00e8
+0xe9 0x00e9
+0xea 0x00ea
+0xeb 0x00eb
+0xec 0x00ec
+0xed 0x00ed
+0xee 0x00ee
+0xef 0x00ef
+0xf0 0x00f0
+0xf1 0x00f1
+0xf2 0x00f2
+0xf3 0x00f3
+0xf4 0x00f4
+0xf5 0x00f5
+0xf6 0x00f6
+0xf7 0x00f7
+0xf8 0x00f8
+0xf9 0x00f9
+0xfa 0x00fa
+0xfb 0x00fb
+0xfc 0x00fc
+0xfd 0x00fd
+0xfe 0x00fe
+0xff 0x00ff
+
diff --git a/data/8859-2 b/data/8859-2
new file mode 100644
index 000000000..29a753b62
--- /dev/null
+++ b/data/8859-2
@@ -0,0 +1,218 @@
+0x20 0x0020
+0x21 0x0021
+0x22 0x0022
+0x23 0x0023
+0x24 0x0024
+0x25 0x0025
+0x26 0x0026
+0x27 0x0027
+0x28 0x0028
+0x29 0x0029
+0x2a 0x002a
+0x2b 0x002b
+0x2c 0x002c
+0x2d 0x002d
+0x2e 0x002e
+0x2f 0x002f
+0x30 0x0030
+0x31 0x0031
+0x32 0x0032
+0x33 0x0033
+0x34 0x0034
+0x35 0x0035
+0x36 0x0036
+0x37 0x0037
+0x38 0x0038
+0x39 0x0039
+0x3a 0x003a
+0x3b 0x003b
+0x3c 0x003c
+0x3d 0x003d
+0x3e 0x003e
+0x3f 0x003f
+0x40 0x0040
+0x41 0x0041
+0x42 0x0042
+0x43 0x0043
+0x44 0x0044
+0x45 0x0045
+0x46 0x0046
+0x47 0x0047
+0x48 0x0048
+0x49 0x0049
+0x4a 0x004a
+0x4b 0x004b
+0x4c 0x004c
+0x4d 0x004d
+0x4e 0x004e
+0x4f 0x004f
+0x50 0x0050
+0x51 0x0051
+0x52 0x0052
+0x53 0x0053
+0x54 0x0054
+0x55 0x0055
+0x56 0x0056
+0x57 0x0057
+0x58 0x0058
+0x59 0x0059
+0x5a 0x005a
+0x5b 0x005b
+0x5c 0x005c
+0x5d 0x005d
+0x5e 0x005e
+0x5f 0x005f
+0x60 0x0060
+0x61 0x0061
+0x62 0x0062
+0x63 0x0063
+0x64 0x0064
+0x65 0x0065
+0x66 0x0066
+0x67 0x0067
+0x68 0x0068
+0x69 0x0069
+0x6a 0x006a
+0x6b 0x006b
+0x6c 0x006c
+0x6d 0x006d
+0x6e 0x006e
+0x6f 0x006f
+0x70 0x0070
+0x71 0x0071
+0x72 0x0072
+0x73 0x0073
+0x74 0x0074
+0x75 0x0075
+0x76 0x0076
+0x77 0x0077
+0x78 0x0078
+0x79 0x0079
+0x7a 0x007a
+0x7b 0x007b
+0x7c 0x007c
+0x7d 0x007d
+0x7e 0x007e
+0x80 0x20ac
+0x82 0x201a
+0x84 0x201e
+0x85 0x2026
+0x86 0x2020
+0x87 0x2021
+0x89 0x2030
+0x8a 0x0160
+0x8b 0x2039
+0x8c 0x015a
+0x8d 0x0164
+0x8e 0x017d
+0x8f 0x0179
+0x91 0x2018
+0x92 0x2019
+0x93 0x201c
+0x94 0x201d
+0x95 0x2022
+0x96 0x2013
+0x97 0x2014
+0x99 0x2122
+0x9a 0x0161
+0x9b 0x203a
+0x8c 0x015b
+0x8d 0x0165
+0x8e 0x017e
+0x8f 0x017a
+0xa0 0x00a0
+0xa1 0x0104
+0xa2 0x02d8
+0xa3 0x0141
+0xa4 0x00a4
+0xa5 0x013d
+0xa6 0x015a
+0xa7 0x00a7
+0xa8 0x00a8
+0xa9 0x0160
+0xaa 0x015e
+0xab 0x0164
+0xac 0x0179
+0xad 0x00ad
+0xae 0x017d
+0xaf 0x017b
+0xb0 0x00b0
+0xb1 0x0105
+0xb2 0x02db
+0xb3 0x0142
+0xb4 0x00b4
+0xb5 0x013e
+0xb6 0x015b
+0xb7 0x02c7
+0xb8 0x00b8
+0xb9 0x0161
+0xba 0x015f
+0xbb 0x0165
+0xbc 0x017a
+0xbd 0x02dd
+0xbe 0x017e
+0xbf 0x017c
+0xc0 0x0154
+0xc1 0x00c1
+0xc2 0x00c2
+0xc3 0x0102
+0xc4 0x00c4
+0xc5 0x0139
+0xc6 0x0106
+0xc7 0x00c7
+0xc8 0x010c
+0xc9 0x00c9
+0xca 0x0118
+0xcb 0x00cb
+0xcc 0x011a
+0xcd 0x00cd
+0xce 0x00ce
+0xcf 0x010e
+0xd0 0x0110
+0xd1 0x0143
+0xd2 0x0147
+0xd3 0x00d3
+0xd4 0x00d4
+0xd5 0x0150
+0xd6 0x00d6
+0xd7 0x00d7
+0xd8 0x0158
+0xd9 0x016e
+0xda 0x00da
+0xdb 0x0170
+0xdc 0x00dc
+0xdd 0x00dd
+0xde 0x0162
+0xdf 0x00df
+0xe0 0x0155
+0xe1 0x00e1
+0xe2 0x00e2
+0xe3 0x0103
+0xe4 0x00e4
+0xe5 0x013a
+0xe6 0x0107
+0xe7 0x00e7
+0xe8 0x010d
+0xe9 0x00e9
+0xea 0x0119
+0xeb 0x00eb
+0xec 0x011b
+0xed 0x00ed
+0xee 0x00ee
+0xef 0x010f
+0xf0 0x0111
+0xf1 0x0144
+0xf2 0x0148
+0xf3 0x00f3
+0xf4 0x00f4
+0xf5 0x0151
+0xf6 0x00f6
+0xf7 0x00f7
+0xf8 0x0159
+0xf9 0x016f
+0xfa 0x00fa
+0xfb 0x0171
+0xfc 0x00fc
+0xfd 0x00fd
+0xfe 0x0163
+0xff 0x02d9
diff --git a/data/8859-3 b/data/8859-3
new file mode 100644
index 000000000..c0c458802
--- /dev/null
+++ b/data/8859-3
@@ -0,0 +1,209 @@
+0x20 0x0020
+0x21 0x0021
+0x22 0x0022
+0x23 0x0023
+0x24 0x0024
+0x25 0x0025
+0x26 0x0026
+0x27 0x0027
+0x28 0x0028
+0x29 0x0029
+0x2a 0x002a
+0x2b 0x002b
+0x2c 0x002c
+0x2d 0x002d
+0x2e 0x002e
+0x2f 0x002f
+0x30 0x0030
+0x31 0x0031
+0x32 0x0032
+0x33 0x0033
+0x34 0x0034
+0x35 0x0035
+0x36 0x0036
+0x37 0x0037
+0x38 0x0038
+0x39 0x0039
+0x3a 0x003a
+0x3b 0x003b
+0x3c 0x003c
+0x3d 0x003d
+0x3e 0x003e
+0x3f 0x003f
+0x40 0x0040
+0x41 0x0041
+0x42 0x0042
+0x43 0x0043
+0x44 0x0044
+0x45 0x0045
+0x46 0x0046
+0x47 0x0047
+0x48 0x0048
+0x49 0x0049
+0x4a 0x004a
+0x4b 0x004b
+0x4c 0x004c
+0x4d 0x004d
+0x4e 0x004e
+0x4f 0x004f
+0x50 0x0050
+0x51 0x0051
+0x52 0x0052
+0x53 0x0053
+0x54 0x0054
+0x55 0x0055
+0x56 0x0056
+0x57 0x0057
+0x58 0x0058
+0x59 0x0059
+0x5a 0x005a
+0x5b 0x005b
+0x5c 0x005c
+0x5d 0x005d
+0x5e 0x005e
+0x5f 0x005f
+0x60 0x0060
+0x61 0x0061
+0x62 0x0062
+0x63 0x0063
+0x64 0x0064
+0x65 0x0065
+0x66 0x0066
+0x67 0x0067
+0x68 0x0068
+0x69 0x0069
+0x6a 0x006a
+0x6b 0x006b
+0x6c 0x006c
+0x6d 0x006d
+0x6e 0x006e
+0x6f 0x006f
+0x70 0x0070
+0x71 0x0071
+0x72 0x0072
+0x73 0x0073
+0x74 0x0074
+0x75 0x0075
+0x76 0x0076
+0x77 0x0077
+0x78 0x0078
+0x79 0x0079
+0x7a 0x007a
+0x7b 0x007b
+0x7c 0x007c
+0x7d 0x007d
+0x7e 0x007e
+0x80 0x20ac
+0x82 0x201a
+0x83 0x0192
+0x84 0x201e
+0x85 0x2026
+0x86 0x2020
+0x87 0x2021
+0x88 0x02c6
+0x89 0x2030
+0x8a 0x0160
+0x8b 0x2039
+0x8c 0x0152
+0x91 0x2018
+0x92 0x2019
+0x93 0x201c
+0x94 0x201d
+0x95 0x2022
+0x96 0x2013
+0x97 0x2014
+0x98 0x02dc
+0x99 0x2122
+0x9a 0x0161
+0x9b 0x203a
+0x9c 0x0153
+0x9f 0x0178
+0xa0 0x00a0
+0xa1 0x0126
+0xa2 0x02d8
+0xa3 0x00a3
+0xa4 0x00a4
+0xa6 0x0124
+0xa7 0x00a7
+0xa8 0x00a8
+0xa9 0x0130
+0xaa 0x015e
+0xab 0x011e
+0xac 0x0134
+0xad 0x00ad
+0xaf 0x017b
+0xb0 0x00b0
+0xb1 0x0127
+0xb2 0x00b2
+0xb3 0x00b3
+0xb4 0x00b4
+0xb5 0x00b5
+0xb6 0x0125
+0xb7 0x00b7
+0xb8 0x00b8
+0xb9 0x0131
+0xba 0x015f
+0xbb 0x011f
+0xbc 0x0135
+0xbd 0x00bd
+0xbf 0x017c
+0xc0 0x00c0
+0xc1 0x00c1
+0xc2 0x00c2
+0xc4 0x00c4
+0xc5 0x010a
+0xc6 0x0108
+0xc7 0x00c7
+0xc8 0x00c8
+0xc9 0x00c9
+0xca 0x00ca
+0xcb 0x00cb
+0xcc 0x00cc
+0xcd 0x00cd
+0xce 0x00ce
+0xcf 0x00cf
+0xd1 0x00d1
+0xd2 0x00d2
+0xd3 0x00d3
+0xd4 0x00d4
+0xd5 0x0120
+0xd6 0x00d6
+0xd7 0x00d7
+0xd8 0x011c
+0xd9 0x00d9
+0xda 0x00da
+0xdb 0x00db
+0xdc 0x00dc
+0xdd 0x016c
+0xde 0x015c
+0xdf 0x00df
+0xe0 0x00e0
+0xe1 0x00e1
+0xe2 0x00e2
+0xe4 0x00e4
+0xe5 0x010b
+0xe6 0x0109
+0xe7 0x00e7
+0xe8 0x00e8
+0xe9 0x00e9
+0xea 0x00ea
+0xeb 0x00eb
+0xec 0x00ec
+0xed 0x00ed
+0xee 0x00ee
+0xef 0x00ef
+0xf1 0x00f1
+0xf2 0x00f2
+0xf3 0x00f3
+0xf4 0x00f4
+0xf5 0x0121
+0xf6 0x00f6
+0xf7 0x00f7
+0xf8 0x011d
+0xf9 0x00f9
+0xfa 0x00fa
+0xfb 0x00fb
+0xfc 0x00fc
+0xfd 0x016d
+0xfe 0x015d
+0xff 0x02d9
diff --git a/data/8859-4 b/data/8859-4
new file mode 100644
index 000000000..b5accea38
--- /dev/null
+++ b/data/8859-4
@@ -0,0 +1,216 @@
+0x20 0x0020
+0x21 0x0021
+0x22 0x0022
+0x23 0x0023
+0x24 0x0024
+0x25 0x0025
+0x26 0x0026
+0x27 0x0027
+0x28 0x0028
+0x29 0x0029
+0x2a 0x002a
+0x2b 0x002b
+0x2c 0x002c
+0x2d 0x002d
+0x2e 0x002e
+0x2f 0x002f
+0x30 0x0030
+0x31 0x0031
+0x32 0x0032
+0x33 0x0033
+0x34 0x0034
+0x35 0x0035
+0x36 0x0036
+0x37 0x0037
+0x38 0x0038
+0x39 0x0039
+0x3a 0x003a
+0x3b 0x003b
+0x3c 0x003c
+0x3d 0x003d
+0x3e 0x003e
+0x3f 0x003f
+0x40 0x0040
+0x41 0x0041
+0x42 0x0042
+0x43 0x0043
+0x44 0x0044
+0x45 0x0045
+0x46 0x0046
+0x47 0x0047
+0x48 0x0048
+0x49 0x0049
+0x4a 0x004a
+0x4b 0x004b
+0x4c 0x004c
+0x4d 0x004d
+0x4e 0x004e
+0x4f 0x004f
+0x50 0x0050
+0x51 0x0051
+0x52 0x0052
+0x53 0x0053
+0x54 0x0054
+0x55 0x0055
+0x56 0x0056
+0x57 0x0057
+0x58 0x0058
+0x59 0x0059
+0x5a 0x005a
+0x5b 0x005b
+0x5c 0x005c
+0x5d 0x005d
+0x5e 0x005e
+0x5f 0x005f
+0x60 0x0060
+0x61 0x0061
+0x62 0x0062
+0x63 0x0063
+0x64 0x0064
+0x65 0x0065
+0x66 0x0066
+0x67 0x0067
+0x68 0x0068
+0x69 0x0069
+0x6a 0x006a
+0x6b 0x006b
+0x6c 0x006c
+0x6d 0x006d
+0x6e 0x006e
+0x6f 0x006f
+0x70 0x0070
+0x71 0x0071
+0x72 0x0072
+0x73 0x0073
+0x74 0x0074
+0x75 0x0075
+0x76 0x0076
+0x77 0x0077
+0x78 0x0078
+0x79 0x0079
+0x7a 0x007a
+0x7b 0x007b
+0x7c 0x007c
+0x7d 0x007d
+0x7e 0x007e
+0x80 0x20ac
+0x82 0x201a
+0x83 0x0192
+0x84 0x201e
+0x85 0x2026
+0x86 0x2020
+0x87 0x2021
+0x88 0x02c6
+0x89 0x2030
+0x8a 0x0160
+0x8b 0x2039
+0x8c 0x0152
+0x91 0x2018
+0x92 0x2019
+0x93 0x201c
+0x94 0x201d
+0x95 0x2022
+0x96 0x2013
+0x97 0x2014
+0x98 0x02dc
+0x99 0x2122
+0x9a 0x0161
+0x9b 0x203a
+0x9c 0x0153
+0x9f 0x0178
+0xa0 0x00a0
+0xa1 0x0104
+0xa2 0x0138
+0xa3 0x0156
+0xa4 0x00a4
+0xa5 0x0128
+0xa6 0x013b
+0xa7 0x00a7
+0xa8 0x00a8
+0xa9 0x0160
+0xaa 0x0112
+0xab 0x0122
+0xac 0x0166
+0xad 0x00ad
+0xae 0x017d
+0xaf 0x00af
+0xb0 0x00b0
+0xb1 0x0105
+0xb2 0x02db
+0xb3 0x0157
+0xb4 0x00b4
+0xb5 0x0129
+0xb6 0x013c
+0xb7 0x02c7
+0xb8 0x00b8
+0xb9 0x0161
+0xba 0x0113
+0xbb 0x0123
+0xbc 0x0167
+0xbd 0x014a
+0xbe 0x017e
+0xbf 0x014b
+0xc0 0x0100
+0xc1 0x00c1
+0xc2 0x00c2
+0xc3 0x00c3
+0xc4 0x00c4
+0xc5 0x00c5
+0xc6 0x00c6
+0xc7 0x012e
+0xc8 0x010c
+0xc9 0x00c9
+0xca 0x0118
+0xcb 0x00cb
+0xcc 0x0116
+0xcd 0x00cd
+0xce 0x00ce
+0xcf 0x012a
+0xd0 0x0110
+0xd1 0x0145
+0xd2 0x014c
+0xd3 0x0136
+0xd4 0x00d4
+0xd5 0x00d5
+0xd6 0x00d6
+0xd7 0x00d7
+0xd8 0x00d8
+0xd9 0x0172
+0xda 0x00da
+0xdb 0x00db
+0xdc 0x00dc
+0xdd 0x0168
+0xde 0x016a
+0xdf 0x00df
+0xe0 0x0101
+0xe1 0x00e1
+0xe2 0x00e2
+0xe3 0x00e3
+0xe4 0x00e4
+0xe5 0x00e5
+0xe6 0x00e6
+0xe7 0x012f
+0xe8 0x010d
+0xe9 0x00e9
+0xea 0x0119
+0xeb 0x00eb
+0xec 0x0117
+0xed 0x00ed
+0xee 0x00ee
+0xef 0x012b
+0xf0 0x0111
+0xf1 0x0146
+0xf2 0x014d
+0xf3 0x0137
+0xf4 0x00f4
+0xf5 0x00f5
+0xf6 0x00f6
+0xf7 0x00f7
+0xf8 0x00f8
+0xf9 0x0173
+0xfa 0x00fa
+0xfb 0x00fb
+0xfc 0x00fc
+0xfd 0x0169
+0xfe 0x016b
+0xff 0x02d9
diff --git a/data/8859-5 b/data/8859-5
new file mode 100644
index 000000000..919b55d09
--- /dev/null
+++ b/data/8859-5
@@ -0,0 +1,216 @@
+0x20 0x0020
+0x21 0x0021
+0x22 0x0022
+0x23 0x0023
+0x24 0x0024
+0x25 0x0025
+0x26 0x0026
+0x27 0x0027
+0x28 0x0028
+0x29 0x0029
+0x2a 0x002a
+0x2b 0x002b
+0x2c 0x002c
+0x2d 0x002d
+0x2e 0x002e
+0x2f 0x002f
+0x30 0x0030
+0x31 0x0031
+0x32 0x0032
+0x33 0x0033
+0x34 0x0034
+0x35 0x0035
+0x36 0x0036
+0x37 0x0037
+0x38 0x0038
+0x39 0x0039
+0x3a 0x003a
+0x3b 0x003b
+0x3c 0x003c
+0x3d 0x003d
+0x3e 0x003e
+0x3f 0x003f
+0x40 0x0040
+0x41 0x0041
+0x42 0x0042
+0x43 0x0043
+0x44 0x0044
+0x45 0x0045
+0x46 0x0046
+0x47 0x0047
+0x48 0x0048
+0x49 0x0049
+0x4a 0x004a
+0x4b 0x004b
+0x4c 0x004c
+0x4d 0x004d
+0x4e 0x004e
+0x4f 0x004f
+0x50 0x0050
+0x51 0x0051
+0x52 0x0052
+0x53 0x0053
+0x54 0x0054
+0x55 0x0055
+0x56 0x0056
+0x57 0x0057
+0x58 0x0058
+0x59 0x0059
+0x5a 0x005a
+0x5b 0x005b
+0x5c 0x005c
+0x5d 0x005d
+0x5e 0x005e
+0x5f 0x005f
+0x60 0x0060
+0x61 0x0061
+0x62 0x0062
+0x63 0x0063
+0x64 0x0064
+0x65 0x0065
+0x66 0x0066
+0x67 0x0067
+0x68 0x0068
+0x69 0x0069
+0x6a 0x006a
+0x6b 0x006b
+0x6c 0x006c
+0x6d 0x006d
+0x6e 0x006e
+0x6f 0x006f
+0x70 0x0070
+0x71 0x0071
+0x72 0x0072
+0x73 0x0073
+0x74 0x0074
+0x75 0x0075
+0x76 0x0076
+0x77 0x0077
+0x78 0x0078
+0x79 0x0079
+0x7a 0x007a
+0x7b 0x007b
+0x7c 0x007c
+0x7d 0x007d
+0x7e 0x007e
+0x80 0x20ac
+0x82 0x201a
+0x83 0x0192
+0x84 0x201e
+0x85 0x2026
+0x86 0x2020
+0x87 0x2021
+0x88 0x02c6
+0x89 0x2030
+0x8a 0x0160
+0x8b 0x2039
+0x8c 0x0152
+0x91 0x2018
+0x92 0x2019
+0x93 0x201c
+0x94 0x201d
+0x95 0x2022
+0x96 0x2013
+0x97 0x2014
+0x98 0x02dc
+0x99 0x2122
+0x9a 0x0161
+0x9b 0x203a
+0x9c 0x0153
+0x9f 0x0178
+0xa0 0x00a0
+0xa1 0x0401
+0xa2 0x0402
+0xa3 0x0403
+0xa4 0x0404
+0xa5 0x0405
+0xa6 0x0406
+0xa7 0x0407
+0xa8 0x0408
+0xa9 0x0409
+0xaa 0x040a
+0xab 0x040b
+0xac 0x040c
+0xad 0x00ad
+0xae 0x040e
+0xaf 0x040f
+0xb0 0x0410
+0xb1 0x0411
+0xb2 0x0412
+0xb3 0x0413
+0xb4 0x0414
+0xb5 0x0415
+0xb6 0x0416
+0xb7 0x0417
+0xb8 0x0418
+0xb9 0x0419
+0xba 0x041a
+0xbb 0x041b
+0xbc 0x041c
+0xbd 0x041d
+0xbe 0x041e
+0xbf 0x041f
+0xc0 0x0420
+0xc1 0x0421
+0xc2 0x0422
+0xc3 0x0423
+0xc4 0x0424
+0xc5 0x0425
+0xc6 0x0426
+0xc7 0x0427
+0xc8 0x0428
+0xc9 0x0429
+0xca 0x042a
+0xcb 0x042b
+0xcc 0x042c
+0xcd 0x042d
+0xce 0x042e
+0xcf 0x042f
+0xd0 0x0430
+0xd1 0x0431
+0xd2 0x0432
+0xd3 0x0433
+0xd4 0x0434
+0xd5 0x0435
+0xd6 0x0436
+0xd7 0x0437
+0xd8 0x0438
+0xd9 0x0439
+0xda 0x043a
+0xdb 0x043b
+0xdc 0x043c
+0xdd 0x043d
+0xde 0x043e
+0xdf 0x043f
+0xe0 0x0440
+0xe1 0x0441
+0xe2 0x0442
+0xe3 0x0443
+0xe4 0x0444
+0xe5 0x0445
+0xe6 0x0446
+0xe7 0x0447
+0xe8 0x0448
+0xe9 0x0449
+0xea 0x044a
+0xeb 0x044b
+0xec 0x044c
+0xed 0x044d
+0xee 0x044e
+0xef 0x044f
+0xf0 0x2116
+0xf1 0x0451
+0xf2 0x0452
+0xf3 0x0453
+0xf4 0x0454
+0xf5 0x0455
+0xf6 0x0456
+0xf7 0x0457
+0xf8 0x0458
+0xf9 0x0459
+0xfa 0x045a
+0xfb 0x045b
+0xfc 0x045c
+0xfd 0x00a7
+0xfe 0x045e
+0xff 0x045f
diff --git a/data/8859-6 b/data/8859-6
new file mode 100644
index 000000000..90b7d4917
--- /dev/null
+++ b/data/8859-6
@@ -0,0 +1,171 @@
+0x20 0x0020
+0x21 0x0021
+0x22 0x0022
+0x23 0x0023
+0x24 0x0024
+0x25 0x0025
+0x26 0x0026
+0x27 0x0027
+0x28 0x0028
+0x29 0x0029
+0x2a 0x002a
+0x2b 0x002b
+0x2c 0x002c
+0x2d 0x002d
+0x2e 0x002e
+0x2f 0x002f
+0x30 0x0660
+0x31 0x0661
+0x32 0x0662
+0x33 0x0663
+0x34 0x0664
+0x35 0x0665
+0x36 0x0666
+0x37 0x0667
+0x38 0x0668
+0x39 0x0669
+0x3a 0x003a
+0x3b 0x003b
+0x3c 0x003c
+0x3d 0x003d
+0x3e 0x003e
+0x3f 0x003f
+0x40 0x0040
+0x41 0x0041
+0x42 0x0042
+0x43 0x0043
+0x44 0x0044
+0x45 0x0045
+0x46 0x0046
+0x47 0x0047
+0x48 0x0048
+0x49 0x0049
+0x4a 0x004a
+0x4b 0x004b
+0x4c 0x004c
+0x4d 0x004d
+0x4e 0x004e
+0x4f 0x004f
+0x50 0x0050
+0x51 0x0051
+0x52 0x0052
+0x53 0x0053
+0x54 0x0054
+0x55 0x0055
+0x56 0x0056
+0x57 0x0057
+0x58 0x0058
+0x59 0x0059
+0x5a 0x005a
+0x5b 0x005b
+0x5c 0x005c
+0x5d 0x005d
+0x5e 0x005e
+0x5f 0x005f
+0x60 0x0060
+0x61 0x0061
+0x62 0x0062
+0x63 0x0063
+0x64 0x0064
+0x65 0x0065
+0x66 0x0066
+0x67 0x0067
+0x68 0x0068
+0x69 0x0069
+0x6a 0x006a
+0x6b 0x006b
+0x6c 0x006c
+0x6d 0x006d
+0x6e 0x006e
+0x6f 0x006f
+0x70 0x0070
+0x71 0x0071
+0x72 0x0072
+0x73 0x0073
+0x74 0x0074
+0x75 0x0075
+0x76 0x0076
+0x77 0x0077
+0x78 0x0078
+0x79 0x0079
+0x7a 0x007a
+0x7b 0x007b
+0x7c 0x007c
+0x7d 0x007d
+0x7e 0x007e
+0x80 0x20ac
+0x82 0x201a
+0x83 0x0192
+0x84 0x201e
+0x85 0x2026
+0x86 0x2020
+0x87 0x2021
+0x88 0x02c6
+0x89 0x2030
+0x8a 0x0160
+0x8b 0x2039
+0x8c 0x0152
+0x91 0x2018
+0x92 0x2019
+0x93 0x201c
+0x94 0x201d
+0x95 0x2022
+0x96 0x2013
+0x97 0x2014
+0x98 0x02dc
+0x99 0x2122
+0x9a 0x0161
+0x9b 0x203a
+0x9c 0x0153
+0x9f 0x0178
+0xa0 0x00a0
+0xa4 0x00a4
+0xac 0x060c
+0xad 0x00ad
+0xbb 0x061b
+0xbf 0x061f
+0xc1 0x0621
+0xc2 0x0622
+0xc3 0x0623
+0xc4 0x0624
+0xc5 0x0625
+0xc6 0x0626
+0xc7 0x0627
+0xc8 0x0628
+0xc9 0x0629
+0xca 0x062a
+0xcb 0x062b
+0xcc 0x062c
+0xcd 0x062d
+0xce 0x062e
+0xcf 0x062f
+0xd0 0x0630
+0xd1 0x0631
+0xd2 0x0632
+0xd3 0x0633
+0xd4 0x0634
+0xd5 0x0635
+0xd6 0x0636
+0xd7 0x0637
+0xd8 0x0638
+0xd9 0x0639
+0xda 0x063a
+0xe0 0x0640
+0xe1 0x0641
+0xe2 0x0642
+0xe3 0x0643
+0xe4 0x0644
+0xe5 0x0645
+0xe6 0x0646
+0xe7 0x0647
+0xe8 0x0648
+0xe9 0x0649
+0xea 0x064a
+0xeb 0x064b
+0xec 0x064c
+0xed 0x064d
+0xee 0x064e
+0xef 0x064f
+0xf0 0x0650
+0xf1 0x0651
+0xf2 0x0652
diff --git a/data/8859-7 b/data/8859-7
new file mode 100644
index 000000000..56502dac7
--- /dev/null
+++ b/data/8859-7
@@ -0,0 +1,210 @@
+0x20 0x0020
+0x21 0x0021
+0x22 0x0022
+0x23 0x0023
+0x24 0x0024
+0x25 0x0025
+0x26 0x0026
+0x27 0x0027
+0x28 0x0028
+0x29 0x0029
+0x2a 0x002a
+0x2b 0x002b
+0x2c 0x002c
+0x2d 0x002d
+0x2e 0x002e
+0x2f 0x002f
+0x30 0x0030
+0x31 0x0031
+0x32 0x0032
+0x33 0x0033
+0x34 0x0034
+0x35 0x0035
+0x36 0x0036
+0x37 0x0037
+0x38 0x0038
+0x39 0x0039
+0x3a 0x003a
+0x3b 0x003b
+0x3c 0x003c
+0x3d 0x003d
+0x3e 0x003e
+0x3f 0x003f
+0x40 0x0040
+0x41 0x0041
+0x42 0x0042
+0x43 0x0043
+0x44 0x0044
+0x45 0x0045
+0x46 0x0046
+0x47 0x0047
+0x48 0x0048
+0x49 0x0049
+0x4a 0x004a
+0x4b 0x004b
+0x4c 0x004c
+0x4d 0x004d
+0x4e 0x004e
+0x4f 0x004f
+0x50 0x0050
+0x51 0x0051
+0x52 0x0052
+0x53 0x0053
+0x54 0x0054
+0x55 0x0055
+0x56 0x0056
+0x57 0x0057
+0x58 0x0058
+0x59 0x0059
+0x5a 0x005a
+0x5b 0x005b
+0x5c 0x005c
+0x5d 0x005d
+0x5e 0x005e
+0x5f 0x005f
+0x60 0x0060
+0x61 0x0061
+0x62 0x0062
+0x63 0x0063
+0x64 0x0064
+0x65 0x0065
+0x66 0x0066
+0x67 0x0067
+0x68 0x0068
+0x69 0x0069
+0x6a 0x006a
+0x6b 0x006b
+0x6c 0x006c
+0x6d 0x006d
+0x6e 0x006e
+0x6f 0x006f
+0x70 0x0070
+0x71 0x0071
+0x72 0x0072
+0x73 0x0073
+0x74 0x0074
+0x75 0x0075
+0x76 0x0076
+0x77 0x0077
+0x78 0x0078
+0x79 0x0079
+0x7a 0x007a
+0x7b 0x007b
+0x7c 0x007c
+0x7d 0x007d
+0x7e 0x007e
+0x80 0x20ac
+0x82 0x201a
+0x83 0x0192
+0x84 0x201e
+0x85 0x2026
+0x86 0x2020
+0x87 0x2021
+0x88 0x02c6
+0x89 0x2030
+0x8a 0x0160
+0x8b 0x2039
+0x8c 0x0152
+0x91 0x2018
+0x92 0x2019
+0x93 0x201c
+0x94 0x201d
+0x95 0x2022
+0x96 0x2013
+0x97 0x2014
+0x98 0x02dc
+0x99 0x2122
+0x9a 0x0161
+0x9b 0x203a
+0x9c 0x0153
+0x9f 0x0178
+0xa0 0x00a0
+0xa1 0x02bd
+0xa2 0x02bc
+0xa3 0x00a3
+0xa6 0x00a6
+0xa7 0x00a7
+0xa8 0x00a8
+0xa9 0x00a9
+0xab 0x00ab
+0xac 0x00ac
+0xad 0x00ad
+0xaf 0x2015
+0xb0 0x00b0
+0xb1 0x00b1
+0xb2 0x00b2
+0xb3 0x00b3
+0xb4 0x0384
+0xb5 0x0385
+0xb6 0x0386
+0xb7 0x00b7
+0xb8 0x0388
+0xb9 0x0389
+0xba 0x038a
+0xbb 0x00bb
+0xbc 0x038c
+0xbd 0x00bd
+0xbe 0x038e
+0xbf 0x038f
+0xc0 0x0390
+0xc1 0x0391
+0xc2 0x0392
+0xc3 0x0393
+0xc4 0x0394
+0xc5 0x0395
+0xc6 0x0396
+0xc7 0x0397
+0xc8 0x0398
+0xc9 0x0399
+0xca 0x039a
+0xcb 0x039b
+0xcc 0x039c
+0xcd 0x039d
+0xce 0x039e
+0xcf 0x039f
+0xd0 0x03a0
+0xd1 0x03a1
+0xd3 0x03a3
+0xd4 0x03a4
+0xd5 0x03a5
+0xd6 0x03a6
+0xd7 0x03a7
+0xd8 0x03a8
+0xd9 0x03a9
+0xda 0x03aa
+0xdb 0x03ab
+0xdc 0x03ac
+0xdd 0x03ad
+0xde 0x03ae
+0xdf 0x03af
+0xe0 0x03b0
+0xe1 0x03b1
+0xe2 0x03b2
+0xe3 0x03b3
+0xe4 0x03b4
+0xe5 0x03b5
+0xe6 0x03b6
+0xe7 0x03b7
+0xe8 0x03b8
+0xe9 0x03b9
+0xea 0x03ba
+0xeb 0x03bb
+0xec 0x03bc
+0xed 0x03bd
+0xee 0x03be
+0xef 0x03bf
+0xf0 0x03c0
+0xf1 0x03c1
+0xf2 0x03c2
+0xf3 0x03c3
+0xf4 0x03c4
+0xf5 0x03c5
+0xf6 0x03c6
+0xf7 0x03c7
+0xf8 0x03c8
+0xf9 0x03c9
+0xfa 0x03ca
+0xfb 0x03cb
+0xfc 0x03cc
+0xfd 0x03cd
+0xfe 0x03ce
diff --git a/data/8859-8 b/data/8859-8
new file mode 100644
index 000000000..c1c0f657c
--- /dev/null
+++ b/data/8859-8
@@ -0,0 +1,178 @@
+0x20 0x0020
+0x21 0x0021
+0x22 0x0022
+0x23 0x0023
+0x24 0x0024
+0x25 0x0025
+0x26 0x0026
+0x27 0x0027
+0x28 0x0028
+0x29 0x0029
+0x2a 0x002a
+0x2b 0x002b
+0x2c 0x002c
+0x2d 0x002d
+0x2e 0x002e
+0x2f 0x002f
+0x30 0x0030
+0x31 0x0031
+0x32 0x0032
+0x33 0x0033
+0x34 0x0034
+0x35 0x0035
+0x36 0x0036
+0x37 0x0037
+0x38 0x0038
+0x39 0x0039
+0x3a 0x003a
+0x3b 0x003b
+0x3c 0x003c
+0x3d 0x003d
+0x3e 0x003e
+0x3f 0x003f
+0x40 0x0040
+0x41 0x0041
+0x42 0x0042
+0x43 0x0043
+0x44 0x0044
+0x45 0x0045
+0x46 0x0046
+0x47 0x0047
+0x48 0x0048
+0x49 0x0049
+0x4a 0x004a
+0x4b 0x004b
+0x4c 0x004c
+0x4d 0x004d
+0x4e 0x004e
+0x4f 0x004f
+0x50 0x0050
+0x51 0x0051
+0x52 0x0052
+0x53 0x0053
+0x54 0x0054
+0x55 0x0055
+0x56 0x0056
+0x57 0x0057
+0x58 0x0058
+0x59 0x0059
+0x5a 0x005a
+0x5b 0x005b
+0x5c 0x005c
+0x5d 0x005d
+0x5e 0x005e
+0x5f 0x005f
+0x60 0x0060
+0x61 0x0061
+0x62 0x0062
+0x63 0x0063
+0x64 0x0064
+0x65 0x0065
+0x66 0x0066
+0x67 0x0067
+0x68 0x0068
+0x69 0x0069
+0x6a 0x006a
+0x6b 0x006b
+0x6c 0x006c
+0x6d 0x006d
+0x6e 0x006e
+0x6f 0x006f
+0x70 0x0070
+0x71 0x0071
+0x72 0x0072
+0x73 0x0073
+0x74 0x0074
+0x75 0x0075
+0x76 0x0076
+0x77 0x0077
+0x78 0x0078
+0x79 0x0079
+0x7a 0x007a
+0x7b 0x007b
+0x7c 0x007c
+0x7d 0x007d
+0x7e 0x007e
+0x80 0x20ac
+0x82 0x201a
+0x83 0x0192
+0x84 0x201e
+0x85 0x2026
+0x86 0x2020
+0x87 0x2021
+0x88 0x02c6
+0x89 0x2030
+0x8a 0x0160
+0x8b 0x2039
+0x8c 0x0152
+0x91 0x2018
+0x92 0x2019
+0x93 0x201c
+0x94 0x201d
+0x95 0x2022
+0x96 0x2013
+0x97 0x2014
+0x98 0x02dc
+0x99 0x2122
+0x9a 0x0161
+0x9b 0x203a
+0x9c 0x0153
+0x9f 0x0178
+0xa0 0x00a0
+0xa2 0x00a2
+0xa3 0x00a3
+0xa4 0x00a4
+0xa5 0x00a5
+0xa6 0x00a6
+0xa7 0x00a7
+0xa8 0x00a8
+0xa9 0x00a9
+0xaa 0x00d7
+0xab 0x00ab
+0xac 0x00ac
+0xad 0x00ad
+0xae 0x00ae
+0xaf 0x203e
+0xb0 0x00b0
+0xb1 0x00b1
+0xb2 0x00b2
+0xb3 0x00b3
+0xb4 0x00b4
+0xb5 0x00b5
+0xb6 0x00b6
+0xb7 0x00b7
+0xb8 0x00b8
+0xb9 0x00b9
+0xba 0x00f7
+0xbb 0x00bb
+0xbc 0x00bc
+0xbd 0x00bd
+0xbe 0x00be
+0xdf 0x2017
+0xe0 0x05d0
+0xe1 0x05d1
+0xe2 0x05d2
+0xe3 0x05d3
+0xe4 0x05d4
+0xe5 0x05d5
+0xe6 0x05d6
+0xe7 0x05d7
+0xe8 0x05d8
+0xe9 0x05d9
+0xea 0x05da
+0xeb 0x05db
+0xec 0x05dc
+0xed 0x05dd
+0xee 0x05de
+0xef 0x05df
+0xf0 0x05e0
+0xf1 0x05e1
+0xf2 0x05e2
+0xf3 0x05e3
+0xf4 0x05e4
+0xf5 0x05e5
+0xf6 0x05e6
+0xf7 0x05e7
+0xf8 0x05e8
+0xf9 0x05e9
+0xfa 0x05ea
diff --git a/data/8859-9 b/data/8859-9
new file mode 100644
index 000000000..1cc08eb44
--- /dev/null
+++ b/data/8859-9
@@ -0,0 +1,218 @@
+0x20 0x0020
+0x21 0x0021
+0x22 0x0022
+0x23 0x0023
+0x24 0x0024
+0x25 0x0025
+0x26 0x0026
+0x27 0x0027
+0x28 0x0028
+0x29 0x0029
+0x2a 0x002a
+0x2b 0x002b
+0x2c 0x002c
+0x2d 0x002d
+0x2e 0x002e
+0x2f 0x002f
+0x30 0x0030
+0x31 0x0031
+0x32 0x0032
+0x33 0x0033
+0x34 0x0034
+0x35 0x0035
+0x36 0x0036
+0x37 0x0037
+0x38 0x0038
+0x39 0x0039
+0x3a 0x003a
+0x3b 0x003b
+0x3c 0x003c
+0x3d 0x003d
+0x3e 0x003e
+0x3f 0x003f
+0x40 0x0040
+0x41 0x0041
+0x42 0x0042
+0x43 0x0043
+0x44 0x0044
+0x45 0x0045
+0x46 0x0046
+0x47 0x0047
+0x48 0x0048
+0x49 0x0049
+0x4a 0x004a
+0x4b 0x004b
+0x4c 0x004c
+0x4d 0x004d
+0x4e 0x004e
+0x4f 0x004f
+0x50 0x0050
+0x51 0x0051
+0x52 0x0052
+0x53 0x0053
+0x54 0x0054
+0x55 0x0055
+0x56 0x0056
+0x57 0x0057
+0x58 0x0058
+0x59 0x0059
+0x5a 0x005a
+0x5b 0x005b
+0x5c 0x005c
+0x5d 0x005d
+0x5e 0x005e
+0x5f 0x005f
+0x60 0x0060
+0x61 0x0061
+0x62 0x0062
+0x63 0x0063
+0x64 0x0064
+0x65 0x0065
+0x66 0x0066
+0x67 0x0067
+0x68 0x0068
+0x69 0x0069
+0x6a 0x006a
+0x6b 0x006b
+0x6c 0x006c
+0x6d 0x006d
+0x6e 0x006e
+0x6f 0x006f
+0x70 0x0070
+0x71 0x0071
+0x72 0x0072
+0x73 0x0073
+0x74 0x0074
+0x75 0x0075
+0x76 0x0076
+0x77 0x0077
+0x78 0x0078
+0x79 0x0079
+0x7a 0x007a
+0x7b 0x007b
+0x7c 0x007c
+0x7d 0x007d
+0x7e 0x007e
+0x80 0x20ac
+0x82 0x201a
+0x83 0x0192
+0x84 0x201e
+0x85 0x2026
+0x86 0x2020
+0x87 0x2021
+0x88 0x02c6
+0x89 0x2030
+0x8a 0x0160
+0x8b 0x2039
+0x8c 0x0152
+0x91 0x2018
+0x92 0x2019
+0x93 0x201c
+0x94 0x201d
+0x95 0x2022
+0x96 0x2013
+0x97 0x2014
+0x98 0x02dc
+0x99 0x2122
+0x9a 0x0161
+0x9b 0x203a
+0x9c 0x0153
+0x9f 0x0178
+0xa0 0x00a0
+0xa1 0x00a1
+0xa2 0x00a2
+0xa3 0x00a3
+0xa4 0x00a4
+0xa5 0x00a5
+0xa6 0x00a6
+0xa7 0x00a7
+0xa8 0x00a8
+0xa9 0x00a9
+0xaa 0x00aa
+0xab 0x00ab
+0xac 0x00ac
+0xad 0x00ad
+0xae 0x00ae
+0xaf 0x00af
+0xb0 0x00b0
+0xb1 0x00b1
+0xb2 0x00b2
+0xb3 0x00b3
+0xb4 0x00b4
+0xb5 0x00b5
+0xb6 0x00b6
+0xb7 0x00b7
+0xb8 0x00b8
+0xb9 0x00b9
+0xba 0x00ba
+0xbb 0x00bb
+0xbc 0x00bc
+0xbd 0x00bd
+0xbe 0x00be
+0xbf 0x00bf
+0xc0 0x00c0
+0xc1 0x00c1
+0xc2 0x00c2
+0xc3 0x00c3
+0xc4 0x00c4
+0xc5 0x00c5
+0xc6 0x00c6
+0xc7 0x00c7
+0xc8 0x00c8
+0xc9 0x00c9
+0xca 0x00ca
+0xcb 0x00cb
+0xcc 0x00cc
+0xcd 0x00cd
+0xce 0x00ce
+0xcf 0x00cf
+0xd0 0x011e
+0xd1 0x00d1
+0xd2 0x00d2
+0xd3 0x00d3
+0xd4 0x00d4
+0xd5 0x00d5
+0xd6 0x00d6
+0xd7 0x00d7
+0xd8 0x00d8
+0xd9 0x00d9
+0xda 0x00da
+0xdb 0x00db
+0xdc 0x00dc
+0xdd 0x0130
+0xde 0x015e
+0xdf 0x00df
+0xe0 0x00e0
+0xe1 0x00e1
+0xe2 0x00e2
+0xe3 0x00e3
+0xe4 0x00e4
+0xe5 0x00e5
+0xe6 0x00e6
+0xe7 0x00e7
+0xe8 0x00e8
+0xe9 0x00e9
+0xea 0x00ea
+0xeb 0x00eb
+0xec 0x00ec
+0xed 0x00ed
+0xee 0x00ee
+0xef 0x00ef
+0xf0 0x011f
+0xf1 0x00f1
+0xf2 0x00f2
+0xf3 0x00f3
+0xf4 0x00f4
+0xf5 0x00f5
+0xf6 0x00f6
+0xf7 0x00f7
+0xf8 0x00f8
+0xf9 0x00f9
+0xfa 0x00fa
+0xfb 0x00fb
+0xfc 0x00fc
+0xfd 0x0131
+0xfe 0x015f
+0xff 0x00ff
+
+
diff --git a/data/HPGLprolog b/data/HPGLprolog
new file mode 100644
index 000000000..21b244ca2
--- /dev/null
+++ b/data/HPGLprolog
@@ -0,0 +1,37 @@
+%%BeginResource: procset hpgltops 1.1 0
+%
+% "$Id: HPGLprolog 932 2000-02-26 20:01:37Z mike $"
+%
+% HP-GL/2 filter procset for the Common UNIX Printing System (CUPS).
+%
+% This procset contains the basic drawing commands that are used to
+% reduce output size. Note the 'MP' (make newpath) definition - this
+% should be called 'NP' (newpath), but GhostScript uses the 'NP' name
+% for 'noaccess put' in some of its font files...
+%
+% Copyright 1993-2000 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
+%
+/MO { moveto } bind def
+/LI { lineto } bind def
+/FI { fill } bind def
+/ST { stroke } bind def
+/CP { closepath } bind def
+/MP { newpath } bind def
+/SP { setlinewidth setrgbcolor } bind def
+%%EndResource
diff --git a/data/Makefile b/data/Makefile
new file mode 100644
index 000000000..f13973041
--- /dev/null
+++ b/data/Makefile
@@ -0,0 +1,86 @@
+#
+# "$Id$"
+#
+# Datafile makefile for the Common UNIX Printing System (CUPS).
+#
+# Copyright 1993-2000 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...
+#
+
+CHARSETS = cp874 \
+ cp1250 \
+ cp1251 \
+ cp1252 \
+ cp1253 \
+ cp1254 \
+ cp1255 \
+ cp1256 \
+ cp1257 \
+ cp1258 \
+ 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 \
+ iso-8859-13 \
+ iso-8859-14 \
+ iso-8859-15 \
+ utf-8
+DATAFILES = HPGLprolog psglyphs testprint.ps
+
+
+#
+# Make everything...
+#
+
+all:
+
+
+#
+# Clean all config and object files...
+#
+
+clean:
+
+
+#
+# Install files...
+#
+
+install:
+ -$(MKDIR) $(DATADIR)
+ -$(MKDIR) $(DATADIR)/data
+ $(INSTALL_DATA) $(DATAFILES) $(DATADIR)/data
+ -$(MKDIR) $(DATADIR)/charsets
+ $(INSTALL_DATA) $(CHARSETS) $(DATADIR)/charsets
+
+
+#
+# End of "$Id$".
+#
diff --git a/data/cp1250 b/data/cp1250
new file mode 100644
index 000000000..afcc62ed5
--- /dev/null
+++ b/data/cp1250
@@ -0,0 +1,254 @@
+charset 8bit
+
+#
+# This file defines the font and character mappings used for Windows
+# Code Page 1250 (WinLatin2) text printing.
+#
+# The first line consists of:
+#
+# direction width normal bold italic bold-italic
+#
+# Direction is the string "ltor" or "rtol", indicating left-to-right or
+# right-to-left text.
+#
+# Width is the string "single" or "double"; double means that the glyphs
+# are twice as wide as ASCII characters in the Courier typeface.
+#
+# "Normal", "bold", "italic", and "bold-italic" are the typefaces to use
+# for each presentation. If characters are only available in a single
+# style then only one typeface should be listed (e.g. "Symbol")
+#
+# Each font that is listed will be used (and downloaded if needed) when
+# printing.
+#
+
+00 ff ltor single Courier Courier-Bold Courier-Italic Courier-BoldItalic
+
+#
+# The following lines define the mapping from the 8-bit character set to
+# the Unicode glyphs for each character:
+#
+# char glyph
+#
+# "Char" and "glyph" are hexadecimal values.
+#
+
+20 0020
+21 0021
+22 0022
+23 0023
+24 0024
+25 0025
+26 0026
+27 0027
+28 0028
+29 0029
+2A 002A
+2B 002B
+2C 002C
+2D 002D
+2E 002E
+2F 002F
+30 0030
+31 0031
+32 0032
+33 0033
+34 0034
+35 0035
+36 0036
+37 0037
+38 0038
+39 0039
+3A 003A
+3B 003B
+3C 003C
+3D 003D
+3E 003E
+3F 003F
+40 0040
+41 0041
+42 0042
+43 0043
+44 0044
+45 0045
+46 0046
+47 0047
+48 0048
+49 0049
+4A 004A
+4B 004B
+4C 004C
+4D 004D
+4E 004E
+4F 004F
+50 0050
+51 0051
+52 0052
+53 0053
+54 0054
+55 0055
+56 0056
+57 0057
+58 0058
+59 0059
+5A 005A
+5B 005B
+5C 005C
+5D 005D
+5E 005E
+5F 005F
+60 0060
+61 0061
+62 0062
+63 0063
+64 0064
+65 0065
+66 0066
+67 0067
+68 0068
+69 0069
+6A 006A
+6B 006B
+6C 006C
+6D 006D
+6E 006E
+6F 006F
+70 0070
+71 0071
+72 0072
+73 0073
+74 0074
+75 0075
+76 0076
+77 0077
+78 0078
+79 0079
+7A 007A
+7B 007B
+7C 007C
+7D 007D
+7E 007E
+7F 007F
+80 20AC
+82 201A
+84 201E
+85 2026
+86 2020
+87 2021
+89 2030
+8A 0160
+8B 2039
+8C 015A
+8D 0164
+8E 017D
+8F 0179
+91 2018
+92 2019
+93 201C
+94 201D
+95 2022
+96 2013
+97 2014
+99 2122
+9A 0161
+9B 203A
+9C 015B
+9D 0165
+9E 017E
+9F 017A
+A0 00A0
+A1 02C7
+A2 02D8
+A3 0141
+A4 00A4
+A5 0104
+A6 00A6
+A7 00A7
+A8 00A8
+A9 00A9
+AA 015E
+AB 00AB
+AC 00AC
+AD 00AD
+AE 00AE
+AF 017B
+B0 00B0
+B1 00B1
+B2 02DB
+B3 0142
+B4 00B4
+B5 00B5
+B6 00B6
+B7 00B7
+B8 00B8
+B9 0105
+BA 015F
+BB 00BB
+BC 013D
+BD 02DD
+BE 013E
+BF 017C
+C0 0154
+C1 00C1
+C2 00C2
+C3 0102
+C4 00C4
+C5 0139
+C6 0106
+C7 00C7
+C8 010C
+C9 00C9
+CA 0118
+CB 00CB
+CC 011A
+CD 00CD
+CE 00CE
+CF 010E
+D0 0110
+D1 0143
+D2 0147
+D3 00D3
+D4 00D4
+D5 0150
+D6 00D6
+D7 00D7
+D8 0158
+D9 016E
+DA 00DA
+DB 0170
+DC 00DC
+DD 00DD
+DE 0162
+DF 00DF
+E0 0155
+E1 00E1
+E2 00E2
+E3 0103
+E4 00E4
+E5 013A
+E6 0107
+E7 00E7
+E8 010D
+E9 00E9
+EA 0119
+EB 00EB
+EC 011B
+ED 00ED
+EE 00EE
+EF 010F
+F0 0111
+F1 0144
+F2 0148
+F3 00F3
+F4 00F4
+F5 0151
+F6 00F6
+F7 00F7
+F8 0159
+F9 016F
+FA 00FA
+FB 0171
+FC 00FC
+FD 00FD
+FE 0163
+FF 02D9
diff --git a/data/cp1251 b/data/cp1251
new file mode 100644
index 000000000..6d31dee09
--- /dev/null
+++ b/data/cp1251
@@ -0,0 +1,258 @@
+charset 8bit
+
+#
+# This file defines the font and character mappings used for Windows
+# Code Page 1251 (WinCyrillic) text printing.
+#
+# The first line consists of:
+#
+# direction width normal bold italic bold-italic
+#
+# Direction is the string "ltor" or "rtol", indicating left-to-right or
+# right-to-left text.
+#
+# Width is the string "single" or "double"; double means that the glyphs
+# are twice as wide as ASCII characters in the Courier typeface.
+#
+# "Normal", "bold", "italic", and "bold-italic" are the typefaces to use
+# for each presentation. If characters are only available in a single
+# style then only one typeface should be listed (e.g. "Symbol")
+#
+# Each font that is listed will be used (and downloaded if needed) when
+# printing.
+#
+
+00 ff ltor single Courier Courier-Bold Courier-Italic Courier-BoldItalic
+
+#
+# The following lines define the mapping from the 8-bit character set to
+# the Unicode glyphs for each character:
+#
+# char glyph
+#
+# "Char" and "glyph" are hexadecimal values.
+#
+
+20 0020
+21 0021
+22 0022
+23 0023
+24 0024
+25 0025
+26 0026
+27 0027
+28 0028
+29 0029
+2A 002A
+2B 002B
+2C 002C
+2D 002D
+2E 002E
+2F 002F
+30 0030
+31 0031
+32 0032
+33 0033
+34 0034
+35 0035
+36 0036
+37 0037
+38 0038
+39 0039
+3A 003A
+3B 003B
+3C 003C
+3D 003D
+3E 003E
+3F 003F
+40 0040
+41 0041
+42 0042
+43 0043
+44 0044
+45 0045
+46 0046
+47 0047
+48 0048
+49 0049
+4A 004A
+4B 004B
+4C 004C
+4D 004D
+4E 004E
+4F 004F
+50 0050
+51 0051
+52 0052
+53 0053
+54 0054
+55 0055
+56 0056
+57 0057
+58 0058
+59 0059
+5A 005A
+5B 005B
+5C 005C
+5D 005D
+5E 005E
+5F 005F
+60 0060
+61 0061
+62 0062
+63 0063
+64 0064
+65 0065
+66 0066
+67 0067
+68 0068
+69 0069
+6A 006A
+6B 006B
+6C 006C
+6D 006D
+6E 006E
+6F 006F
+70 0070
+71 0071
+72 0072
+73 0073
+74 0074
+75 0075
+76 0076
+77 0077
+78 0078
+79 0079
+7A 007A
+7B 007B
+7C 007C
+7D 007D
+7E 007E
+7F 007F
+80 0402
+81 0403
+82 201A
+83 0453
+84 201E
+85 2026
+86 2020
+87 2021
+88 20AC
+89 2030
+8A 0409
+8B 2039
+8C 040A
+8D 040C
+8E 040B
+8F 040F
+90 0452
+91 2018
+92 2019
+93 201C
+94 201D
+95 2022
+96 2013
+97 2014
+99 2122
+9A 0459
+9B 203A
+9C 045A
+9D 045C
+9E 045B
+9F 045F
+A0 00A0
+A1 040E
+A2 045E
+A3 0408
+A4 00A4
+A5 0490
+A6 00A6
+A7 00A7
+A8 0401
+A9 00A9
+AA 0404
+AB 00AB
+AC 00AC
+AD 00AD
+AE 00AE
+AF 0407
+B0 00B0
+B1 00B1
+B2 0406
+B3 0456
+B4 0491
+B5 00B5
+B6 00B6
+B7 00B7
+B8 0451
+B9 2116
+BA 0454
+BB 00BB
+BC 0458
+BD 0405
+BE 0455
+BF 0457
+C0 0410
+C1 0411
+C2 0412
+C3 0413
+C4 0414
+C5 0415
+C6 0416
+C7 0417
+C8 0418
+C9 0419
+CA 041A
+CB 041B
+CC 041C
+CD 041D
+CE 041E
+CF 041F
+D0 0420
+D1 0421
+D2 0422
+D3 0423
+D4 0424
+D5 0425
+D6 0426
+D7 0427
+D8 0428
+D9 0429
+DA 042A
+DB 042B
+DC 042C
+DD 042D
+DE 042E
+DF 042F
+E0 0430
+E1 0431
+E2 0432
+E3 0433
+E4 0434
+E5 0435
+E6 0436
+E7 0437
+E8 0438
+E9 0439
+EA 043A
+EB 043B
+EC 043C
+ED 043D
+EE 043E
+EF 043F
+F0 0440
+F1 0441
+F2 0442
+F3 0443
+F4 0444
+F5 0445
+F6 0446
+F7 0447
+F8 0448
+F9 0449
+FA 044A
+FB 044B
+FC 044C
+FD 044D
+FE 044E
+FF 044F
diff --git a/data/cp1252 b/data/cp1252
new file mode 100644
index 000000000..a98595874
--- /dev/null
+++ b/data/cp1252
@@ -0,0 +1,254 @@
+charset 8bit
+
+#
+# This file defines the font and character mappings used for Windows
+# Code Page 1252 (WinLatin1) text printing.
+#
+# The first line consists of:
+#
+# direction width normal bold italic bold-italic
+#
+# Direction is the string "ltor" or "rtol", indicating left-to-right or
+# right-to-left text.
+#
+# Width is the string "single" or "double"; double means that the glyphs
+# are twice as wide as ASCII characters in the Courier typeface.
+#
+# "Normal", "bold", "italic", and "bold-italic" are the typefaces to use
+# for each presentation. If characters are only available in a single
+# style then only one typeface should be listed (e.g. "Symbol")
+#
+# Each font that is listed will be used (and downloaded if needed) when
+# printing.
+#
+
+00 ff ltor single Courier Courier-Bold Courier-Italic Courier-BoldItalic
+
+#
+# The following lines define the mapping from the 8-bit character set to
+# the Unicode glyphs for each character:
+#
+# char glyph
+#
+# "Char" and "glyph" are hexadecimal values.
+#
+
+20 0020
+21 0021
+22 0022
+23 0023
+24 0024
+25 0025
+26 0026
+27 0027
+28 0028
+29 0029
+2A 002A
+2B 002B
+2C 002C
+2D 002D
+2E 002E
+2F 002F
+30 0030
+31 0031
+32 0032
+33 0033
+34 0034
+35 0035
+36 0036
+37 0037
+38 0038
+39 0039
+3A 003A
+3B 003B
+3C 003C
+3D 003D
+3E 003E
+3F 003F
+40 0040
+41 0041
+42 0042
+43 0043
+44 0044
+45 0045
+46 0046
+47 0047
+48 0048
+49 0049
+4A 004A
+4B 004B
+4C 004C
+4D 004D
+4E 004E
+4F 004F
+50 0050
+51 0051
+52 0052
+53 0053
+54 0054
+55 0055
+56 0056
+57 0057
+58 0058
+59 0059
+5A 005A
+5B 005B
+5C 005C
+5D 005D
+5E 005E
+5F 005F
+60 0060
+61 0061
+62 0062
+63 0063
+64 0064
+65 0065
+66 0066
+67 0067
+68 0068
+69 0069
+6A 006A
+6B 006B
+6C 006C
+6D 006D
+6E 006E
+6F 006F
+70 0070
+71 0071
+72 0072
+73 0073
+74 0074
+75 0075
+76 0076
+77 0077
+78 0078
+79 0079
+7A 007A
+7B 007B
+7C 007C
+7D 007D
+7E 007E
+7F 007F
+80 20AC
+82 201A
+83 0192
+84 201E
+85 2026
+86 2020
+87 2021
+88 02C6
+89 2030
+8A 0160
+8B 2039
+8C 0152
+8E 017D
+91 2018
+92 2019
+93 201C
+94 201D
+95 2022
+96 2013
+97 2014
+98 02DC
+99 2122
+9A 0161
+9B 203A
+9C 0153
+9E 017E
+9F 0178
+A0 00A0
+A1 00A1
+A2 00A2
+A3 00A3
+A4 00A4
+A5 00A5
+A6 00A6
+A7 00A7
+A8 00A8
+A9 00A9
+AA 00AA
+AB 00AB
+AC 00AC
+AD 00AD
+AE 00AE
+AF 00AF
+B0 00B0
+B1 00B1
+B2 00B2
+B3 00B3
+B4 00B4
+B5 00B5
+B6 00B6
+B7 00B7
+B8 00B8
+B9 00B9
+BA 00BA
+BB 00BB
+BC 00BC
+BD 00BD
+BE 00BE
+BF 00BF
+C0 00C0
+C1 00C1
+C2 00C2
+C3 00C3
+C4 00C4
+C5 00C5
+C6 00C6
+C7 00C7
+C8 00C8
+C9 00C9
+CA 00CA
+CB 00CB
+CC 00CC
+CD 00CD
+CE 00CE
+CF 00CF
+D0 00D0
+D1 00D1
+D2 00D2
+D3 00D3
+D4 00D4
+D5 00D5
+D6 00D6
+D7 00D7
+D8 00D8
+D9 00D9
+DA 00DA
+DB 00DB
+DC 00DC
+DD 00DD
+DE 00DE
+DF 00DF
+E0 00E0
+E1 00E1
+E2 00E2
+E3 00E3
+E4 00E4
+E5 00E5
+E6 00E6
+E7 00E7
+E8 00E8
+E9 00E9
+EA 00EA
+EB 00EB
+EC 00EC
+ED 00ED
+EE 00EE
+EF 00EF
+F0 00F0
+F1 00F1
+F2 00F2
+F3 00F3
+F4 00F4
+F5 00F5
+F6 00F6
+F7 00F7
+F8 00F8
+F9 00F9
+FA 00FA
+FB 00FB
+FC 00FC
+FD 00FD
+FE 00FE
+FF 00FF
diff --git a/data/cp1253 b/data/cp1253
new file mode 100644
index 000000000..c736ecd6c
--- /dev/null
+++ b/data/cp1253
@@ -0,0 +1,243 @@
+charset 8bit
+
+#
+# This file defines the font and character mappings used for Windows
+# Code Page 1253 (WinGreek) text printing.
+#
+# The first line consists of:
+#
+# direction width normal bold italic bold-italic
+#
+# Direction is the string "ltor" or "rtol", indicating left-to-right or
+# right-to-left text.
+#
+# Width is the string "single" or "double"; double means that the glyphs
+# are twice as wide as ASCII characters in the Courier typeface.
+#
+# "Normal", "bold", "italic", and "bold-italic" are the typefaces to use
+# for each presentation. If characters are only available in a single
+# style then only one typeface should be listed (e.g. "Symbol")
+#
+# Each font that is listed will be used (and downloaded if needed) when
+# printing.
+#
+
+00 9f ltor single Courier Courier-Bold Courier-Italic Courier-BoldItalic
+a0 ff ltor single Symbol
+
+#
+# The following lines define the mapping from the 8-bit character set to
+# the Unicode glyphs for each character:
+#
+# char glyph
+#
+# "Char" and "glyph" are hexadecimal values.
+#
+
+20 0020
+21 0021
+22 0022
+23 0023
+24 0024
+25 0025
+26 0026
+27 0027
+28 0028
+29 0029
+2A 002A
+2B 002B
+2C 002C
+2D 002D
+2E 002E
+2F 002F
+30 0030
+31 0031
+32 0032
+33 0033
+34 0034
+35 0035
+36 0036
+37 0037
+38 0038
+39 0039
+3A 003A
+3B 003B
+3C 003C
+3D 003D
+3E 003E
+3F 003F
+40 0040
+41 0041
+42 0042
+43 0043
+44 0044
+45 0045
+46 0046
+47 0047
+48 0048
+49 0049
+4A 004A
+4B 004B
+4C 004C
+4D 004D
+4E 004E
+4F 004F
+50 0050
+51 0051
+52 0052
+53 0053
+54 0054
+55 0055
+56 0056
+57 0057
+58 0058
+59 0059
+5A 005A
+5B 005B
+5C 005C
+5D 005D
+5E 005E
+5F 005F
+60 0060
+61 0061
+62 0062
+63 0063
+64 0064
+65 0065
+66 0066
+67 0067
+68 0068
+69 0069
+6A 006A
+6B 006B
+6C 006C
+6D 006D
+6E 006E
+6F 006F
+70 0070
+71 0071
+72 0072
+73 0073
+74 0074
+75 0075
+76 0076
+77 0077
+78 0078
+79 0079
+7A 007A
+7B 007B
+7C 007C
+7D 007D
+7E 007E
+7F 007F
+80 20AC
+82 201A
+83 0192
+84 201E
+85 2026
+86 2020
+87 2021
+89 2030
+8B 2039
+91 2018
+92 2019
+93 201C
+94 201D
+95 2022
+96 2013
+97 2014
+99 2122
+9B 203A
+A0 00A0
+A1 0385
+A2 0386
+A3 00A3
+A4 00A4
+A5 00A5
+A6 00A6
+A7 00A7
+A8 00A8
+A9 00A9
+AB 00AB
+AC 00AC
+AD 00AD
+AE 00AE
+AF 2015
+B0 00B0
+B1 00B1
+B2 00B2
+B3 00B3
+B4 0384
+B5 00B5
+B6 00B6
+B7 00B7
+B8 0388
+B9 0389
+BA 038A
+BB 00BB
+BC 038C
+BD 00BD
+BE 038E
+BF 038F
+C0 0390
+C1 0391
+C2 0392
+C3 0393
+C4 0394
+C5 0395
+C6 0396
+C7 0397
+C8 0398
+C9 0399
+CA 039A
+CB 039B
+CC 039C
+CD 039D
+CE 039E
+CF 039F
+D0 03A0
+D1 03A1
+D3 03A3
+D4 03A4
+D5 03A5
+D6 03A6
+D7 03A7
+D8 03A8
+D9 03A9
+DA 03AA
+DB 03AB
+DC 03AC
+DD 03AD
+DE 03AE
+DF 03AF
+E0 03B0
+E1 03B1
+E2 03B2
+E3 03B3
+E4 03B4
+E5 03B5
+E6 03B6
+E7 03B7
+E8 03B8
+E9 03B9
+EA 03BA
+EB 03BB
+EC 03BC
+ED 03BD
+EE 03BE
+EF 03BF
+F0 03C0
+F1 03C1
+F2 03C2
+F3 03C3
+F4 03C4
+F5 03C5
+F6 03C6
+F7 03C7
+F8 03C8
+F9 03C9
+FA 03CA
+FB 03CB
+FC 03CC
+FD 03CD
+FE 03CE
diff --git a/data/cp1254 b/data/cp1254
new file mode 100644
index 000000000..87fff2db7
--- /dev/null
+++ b/data/cp1254
@@ -0,0 +1,252 @@
+charset 8bit
+
+#
+# This file defines the font and character mappings used for Windows
+# Code Page 1254 (WinTurkish) text printing.
+#
+# The first line consists of:
+#
+# direction width normal bold italic bold-italic
+#
+# Direction is the string "ltor" or "rtol", indicating left-to-right or
+# right-to-left text.
+#
+# Width is the string "single" or "double"; double means that the glyphs
+# are twice as wide as ASCII characters in the Courier typeface.
+#
+# "Normal", "bold", "italic", and "bold-italic" are the typefaces to use
+# for each presentation. If characters are only available in a single
+# style then only one typeface should be listed (e.g. "Symbol")
+#
+# Each font that is listed will be used (and downloaded if needed) when
+# printing.
+#
+
+00 ff ltor single Courier Courier-Bold Courier-Italic Courier-BoldItalic
+
+#
+# The following lines define the mapping from the 8-bit character set to
+# the Unicode glyphs for each character:
+#
+# char glyph
+#
+# "Char" and "glyph" are hexadecimal values.
+#
+
+20 0020
+21 0021
+22 0022
+23 0023
+24 0024
+25 0025
+26 0026
+27 0027
+28 0028
+29 0029
+2A 002A
+2B 002B
+2C 002C
+2D 002D
+2E 002E
+2F 002F
+30 0030
+31 0031
+32 0032
+33 0033
+34 0034
+35 0035
+36 0036
+37 0037
+38 0038
+39 0039
+3A 003A
+3B 003B
+3C 003C
+3D 003D
+3E 003E
+3F 003F
+40 0040
+41 0041
+42 0042
+43 0043
+44 0044
+45 0045
+46 0046
+47 0047
+48 0048
+49 0049
+4A 004A
+4B 004B
+4C 004C
+4D 004D
+4E 004E
+4F 004F
+50 0050
+51 0051
+52 0052
+53 0053
+54 0054
+55 0055
+56 0056
+57 0057
+58 0058
+59 0059
+5A 005A
+5B 005B
+5C 005C
+5D 005D
+5E 005E
+5F 005F
+60 0060
+61 0061
+62 0062
+63 0063
+64 0064
+65 0065
+66 0066
+67 0067
+68 0068
+69 0069
+6A 006A
+6B 006B
+6C 006C
+6D 006D
+6E 006E
+6F 006F
+70 0070
+71 0071
+72 0072
+73 0073
+74 0074
+75 0075
+76 0076
+77 0077
+78 0078
+79 0079
+7A 007A
+7B 007B
+7C 007C
+7D 007D
+7E 007E
+7F 007F
+80 20AC
+82 201A
+83 0192
+84 201E
+85 2026
+86 2020
+87 2021
+88 02C6
+89 2030
+8A 0160
+8B 2039
+8C 0152
+91 2018
+92 2019
+93 201C
+94 201D
+95 2022
+96 2013
+97 2014
+98 02DC
+99 2122
+9A 0161
+9B 203A
+9C 0153
+9F 0178
+A0 00A0
+A1 00A1
+A2 00A2
+A3 00A3
+A4 00A4
+A5 00A5
+A6 00A6
+A7 00A7
+A8 00A8
+A9 00A9
+AA 00AA
+AB 00AB
+AC 00AC
+AD 00AD
+AE 00AE
+AF 00AF
+B0 00B0
+B1 00B1
+B2 00B2
+B3 00B3
+B4 00B4
+B5 00B5
+B6 00B6
+B7 00B7
+B8 00B8
+B9 00B9
+BA 00BA
+BB 00BB
+BC 00BC
+BD 00BD
+BE 00BE
+BF 00BF
+C0 00C0
+C1 00C1
+C2 00C2
+C3 00C3
+C4 00C4
+C5 00C5
+C6 00C6
+C7 00C7
+C8 00C8
+C9 00C9
+CA 00CA
+CB 00CB
+CC 00CC
+CD 00CD
+CE 00CE
+CF 00CF
+D0 011E
+D1 00D1
+D2 00D2
+D3 00D3
+D4 00D4
+D5 00D5
+D6 00D6
+D7 00D7
+D8 00D8
+D9 00D9
+DA 00DA
+DB 00DB
+DC 00DC
+DD 0130
+DE 015E
+DF 00DF
+E0 00E0
+E1 00E1
+E2 00E2
+E3 00E3
+E4 00E4
+E5 00E5
+E6 00E6
+E7 00E7
+E8 00E8
+E9 00E9
+EA 00EA
+EB 00EB
+EC 00EC
+ED 00ED
+EE 00EE
+EF 00EF
+F0 011F
+F1 00F1
+F2 00F2
+F3 00F3
+F4 00F4
+F5 00F5
+F6 00F6
+F7 00F7
+F8 00F8
+F9 00F9
+FA 00FA
+FB 00FB
+FC 00FC
+FD 0131
+FE 015F
+FF 00FF
diff --git a/data/cp1255 b/data/cp1255
new file mode 100644
index 000000000..a8b26a85d
--- /dev/null
+++ b/data/cp1255
@@ -0,0 +1,236 @@
+charset 8bit
+
+#
+# This file defines the font and character mappings used for Windows
+# Code Page 1255 (WinHebrew) text printing.
+#
+# The first line consists of:
+#
+# direction width normal bold italic bold-italic
+#
+# Direction is the string "ltor" or "rtol", indicating left-to-right or
+# right-to-left text.
+#
+# Width is the string "single" or "double"; double means that the glyphs
+# are twice as wide as ASCII characters in the Courier typeface.
+#
+# "Normal", "bold", "italic", and "bold-italic" are the typefaces to use
+# for each presentation. If characters are only available in a single
+# style then only one typeface should be listed (e.g. "Symbol")
+#
+# Each font that is listed will be used (and downloaded if needed) when
+# printing.
+#
+
+00 ff rtol single Courier Courier-Bold Courier-Italic Courier-BoldItalic
+
+#
+# The following lines define the mapping from the 8-bit character set to
+# the Unicode glyphs for each character:
+#
+# char glyph
+#
+# "Char" and "glyph" are hexadecimal values.
+#
+
+20 0020
+21 0021
+22 0022
+23 0023
+24 0024
+25 0025
+26 0026
+27 0027
+28 0028
+29 0029
+2A 002A
+2B 002B
+2C 002C
+2D 002D
+2E 002E
+2F 002F
+30 0030
+31 0031
+32 0032
+33 0033
+34 0034
+35 0035
+36 0036
+37 0037
+38 0038
+39 0039
+3A 003A
+3B 003B
+3C 003C
+3D 003D
+3E 003E
+3F 003F
+40 0040
+41 0041
+42 0042
+43 0043
+44 0044
+45 0045
+46 0046
+47 0047
+48 0048
+49 0049
+4A 004A
+4B 004B
+4C 004C
+4D 004D
+4E 004E
+4F 004F
+50 0050
+51 0051
+52 0052
+53 0053
+54 0054
+55 0055
+56 0056
+57 0057
+58 0058
+59 0059
+5A 005A
+5B 005B
+5C 005C
+5D 005D
+5E 005E
+5F 005F
+60 0060
+61 0061
+62 0062
+63 0063
+64 0064
+65 0065
+66 0066
+67 0067
+68 0068
+69 0069
+6A 006A
+6B 006B
+6C 006C
+6D 006D
+6E 006E
+6F 006F
+70 0070
+71 0071
+72 0072
+73 0073
+74 0074
+75 0075
+76 0076
+77 0077
+78 0078
+79 0079
+7A 007A
+7B 007B
+7C 007C
+7D 007D
+7E 007E
+7F 007F
+80 20AC
+82 201A
+83 0192
+84 201E
+85 2026
+86 2020
+87 2021
+88 02C6
+89 2030
+8B 2039
+91 2018
+92 2019
+93 201C
+94 201D
+95 2022
+96 2013
+97 2014
+98 02DC
+99 2122
+9B 203A
+A0 00A0
+A1 00A1
+A2 00A2
+A3 00A3
+A4 20AA
+A5 00A5
+A6 00A6
+A7 00A7
+A8 00A8
+A9 00A9
+AA 00D7
+AB 00AB
+AC 00AC
+AD 00AD
+AE 00AE
+AF 00AF
+B0 00B0
+B1 00B1
+B2 00B2
+B3 00B3
+B4 00B4
+B5 00B5
+B6 00B6
+B7 00B7
+B8 00B8
+B9 00B9
+BA 00F7
+BB 00BB
+BC 00BC
+BD 00BD
+BE 00BE
+BF 00BF
+C0 05B0
+C1 05B1
+C2 05B2
+C3 05B3
+C4 05B4
+C5 05B5
+C6 05B6
+C7 05B7
+C8 05B8
+C9 05B9
+CB 05BB
+CC 05BC
+CD 05BD
+CE 05BE
+CF 05BF
+D0 05C0
+D1 05C1
+D2 05C2
+D3 05C3
+D4 05F0
+D5 05F1
+D6 05F2
+D7 05F3
+D8 05F4
+E0 05D0
+E1 05D1
+E2 05D2
+E3 05D3
+E4 05D4
+E5 05D5
+E6 05D6
+E7 05D7
+E8 05D8
+E9 05D9
+EA 05DA
+EB 05DB
+EC 05DC
+ED 05DD
+EE 05DE
+EF 05DF
+F0 05E0
+F1 05E1
+F2 05E2
+F3 05E3
+F4 05E4
+F5 05E5
+F6 05E6
+F7 05E7
+F8 05E8
+F9 05E9
+FA 05EA
+FD 200E
+FE 200F
diff --git a/data/cp1256 b/data/cp1256
new file mode 100644
index 000000000..6908f1eb5
--- /dev/null
+++ b/data/cp1256
@@ -0,0 +1,259 @@
+charset 8bit
+
+#
+# This file defines the font and character mappings used for Windows
+# Code Page 1256 (WinArabic) text printing.
+#
+# The first line consists of:
+#
+# direction width normal bold italic bold-italic
+#
+# Direction is the string "ltor" or "rtol", indicating left-to-right or
+# right-to-left text.
+#
+# Width is the string "single" or "double"; double means that the glyphs
+# are twice as wide as ASCII characters in the Courier typeface.
+#
+# "Normal", "bold", "italic", and "bold-italic" are the typefaces to use
+# for each presentation. If characters are only available in a single
+# style then only one typeface should be listed (e.g. "Symbol")
+#
+# Each font that is listed will be used (and downloaded if needed) when
+# printing.
+#
+
+00 ff rtol single Courier Courier-Bold Courier-Italic Courier-BoldItalic
+
+#
+# The following lines define the mapping from the 8-bit character set to
+# the Unicode glyphs for each character:
+#
+# char glyph
+#
+# "Char" and "glyph" are hexadecimal values.
+#
+
+20 0020
+21 0021
+22 0022
+23 0023
+24 0024
+25 0025
+26 0026
+27 0027
+28 0028
+29 0029
+2A 002A
+2B 002B
+2C 002C
+2D 002D
+2E 002E
+2F 002F
+30 0030
+31 0031
+32 0032
+33 0033
+34 0034
+35 0035
+36 0036
+37 0037
+38 0038
+39 0039
+3A 003A
+3B 003B
+3C 003C
+3D 003D
+3E 003E
+3F 003F
+40 0040
+41 0041
+42 0042
+43 0043
+44 0044
+45 0045
+46 0046
+47 0047
+48 0048
+49 0049
+4A 004A
+4B 004B
+4C 004C
+4D 004D
+4E 004E
+4F 004F
+50 0050
+51 0051
+52 0052
+53 0053
+54 0054
+55 0055
+56 0056
+57 0057
+58 0058
+59 0059
+5A 005A
+5B 005B
+5C 005C
+5D 005D
+5E 005E
+5F 005F
+60 0060
+61 0061
+62 0062
+63 0063
+64 0064
+65 0065
+66 0066
+67 0067
+68 0068
+69 0069
+6A 006A
+6B 006B
+6C 006C
+6D 006D
+6E 006E
+6F 006F
+70 0070
+71 0071
+72 0072
+73 0073
+74 0074
+75 0075
+76 0076
+77 0077
+78 0078
+79 0079
+7A 007A
+7B 007B
+7C 007C
+7D 007D
+7E 007E
+7F 007F
+80 20AC
+81 067E
+82 201A
+83 0192
+84 201E
+85 2026
+86 2020
+87 2021
+88 02C6
+89 2030
+8A 0679
+8B 2039
+8C 0152
+8D 0686
+8E 0698
+8F 0688
+90 06AF
+91 2018
+92 2019
+93 201C
+94 201D
+95 2022
+96 2013
+97 2014
+98 06A9
+99 2122
+9A 0691
+9B 203A
+9C 0153
+9D 200C
+9E 200D
+9F 06BA
+A0 00A0
+A1 060C
+A2 00A2
+A3 00A3
+A4 00A4
+A5 00A5
+A6 00A6
+A7 00A7
+A8 00A8
+A9 00A9
+AA 06BE
+AB 00AB
+AC 00AC
+AD 00AD
+AE 00AE
+AF 00AF
+B0 00B0
+B1 00B1
+B2 00B2
+B3 00B3
+B4 00B4
+B5 00B5
+B6 00B6
+B7 00B7
+B8 00B8
+B9 00B9
+BA 061B
+BB 00BB
+BC 00BC
+BD 00BD
+BE 00BE
+BF 061F
+C0 06C1
+C1 0621
+C2 0622
+C3 0623
+C4 0624
+C5 0625
+C6 0626
+C7 0627
+C8 0628
+C9 0629
+CA 062A
+CB 062B
+CC 062C
+CD 062D
+CE 062E
+CF 062F
+D0 0630
+D1 0631
+D2 0632
+D3 0633
+D4 0634
+D5 0635
+D6 0636
+D7 00D7
+D8 0637
+D9 0638
+DA 0639
+DB 063A
+DC 0640
+DD 0641
+DE 0642
+DF 0643
+E0 00E0
+E1 0644
+E2 00E2
+E3 0645
+E4 0646
+E5 0647
+E6 0648
+E7 00E7
+E8 00E8
+E9 00E9
+EA 00EA
+EB 00EB
+EC 0649
+ED 064A
+EE 00EE
+EF 00EF
+F0 064B
+F1 064C
+F2 064D
+F3 064E
+F4 00F4
+F5 064F
+F6 0650
+F7 00F7
+F8 0651
+F9 00F9
+FA 0652
+FB 00FB
+FC 00FC
+FD 200E
+FE 200F
+FF 06D2
diff --git a/data/cp1257 b/data/cp1257
new file mode 100644
index 000000000..ac5f8e0b7
--- /dev/null
+++ b/data/cp1257
@@ -0,0 +1,247 @@
+charset 8bit
+
+#
+# This file defines the font and character mappings used for Windows
+# Code Page 1257 (WinBaltic) text printing.
+#
+# The first line consists of:
+#
+# direction width normal bold italic bold-italic
+#
+# Direction is the string "ltor" or "rtol", indicating left-to-right or
+# right-to-left text.
+#
+# Width is the string "single" or "double"; double means that the glyphs
+# are twice as wide as ASCII characters in the Courier typeface.
+#
+# "Normal", "bold", "italic", and "bold-italic" are the typefaces to use
+# for each presentation. If characters are only available in a single
+# style then only one typeface should be listed (e.g. "Symbol")
+#
+# Each font that is listed will be used (and downloaded if needed) when
+# printing.
+#
+
+00 ff ltor single Courier Courier-Bold Courier-Italic Courier-BoldItalic
+
+#
+# The following lines define the mapping from the 8-bit character set to
+# the Unicode glyphs for each character:
+#
+# char glyph
+#
+# "Char" and "glyph" are hexadecimal values.
+#
+
+20 0020
+21 0021
+22 0022
+23 0023
+24 0024
+25 0025
+26 0026
+27 0027
+28 0028
+29 0029
+2A 002A
+2B 002B
+2C 002C
+2D 002D
+2E 002E
+2F 002F
+30 0030
+31 0031
+32 0032
+33 0033
+34 0034
+35 0035
+36 0036
+37 0037
+38 0038
+39 0039
+3A 003A
+3B 003B
+3C 003C
+3D 003D
+3E 003E
+3F 003F
+40 0040
+41 0041
+42 0042
+43 0043
+44 0044
+45 0045
+46 0046
+47 0047
+48 0048
+49 0049
+4A 004A
+4B 004B
+4C 004C
+4D 004D
+4E 004E
+4F 004F
+50 0050
+51 0051
+52 0052
+53 0053
+54 0054
+55 0055
+56 0056
+57 0057
+58 0058
+59 0059
+5A 005A
+5B 005B
+5C 005C
+5D 005D
+5E 005E
+5F 005F
+60 0060
+61 0061
+62 0062
+63 0063
+64 0064
+65 0065
+66 0066
+67 0067
+68 0068
+69 0069
+6A 006A
+6B 006B
+6C 006C
+6D 006D
+6E 006E
+6F 006F
+70 0070
+71 0071
+72 0072
+73 0073
+74 0074
+75 0075
+76 0076
+77 0077
+78 0078
+79 0079
+7A 007A
+7B 007B
+7C 007C
+7D 007D
+7E 007E
+7F 007F
+80 20AC
+82 201A
+84 201E
+85 2026
+86 2020
+87 2021
+89 2030
+8B 2039
+8D 00A8
+8E 02C7
+8F 00B8
+91 2018
+92 2019
+93 201C
+94 201D
+95 2022
+96 2013
+97 2014
+99 2122
+9B 203A
+9D 00AF
+9E 02DB
+A0 00A0
+A2 00A2
+A3 00A3
+A4 00A4
+A6 00A6
+A7 00A7
+A8 00D8
+A9 00A9
+AA 0156
+AB 00AB
+AC 00AC
+AD 00AD
+AE 00AE
+AF 00C6
+B0 00B0
+B1 00B1
+B2 00B2
+B3 00B3
+B4 00B4
+B5 00B5
+B6 00B6
+B7 00B7
+B8 00F8
+B9 00B9
+BA 0157
+BB 00BB
+BC 00BC
+BD 00BD
+BE 00BE
+BF 00E6
+C0 0104
+C1 012E
+C2 0100
+C3 0106
+C4 00C4
+C5 00C5
+C6 0118
+C7 0112
+C8 010C
+C9 00C9
+CA 0179
+CB 0116
+CC 0122
+CD 0136
+CE 012A
+CF 013B
+D0 0160
+D1 0143
+D2 0145
+D3 00D3
+D4 014C
+D5 00D5
+D6 00D6
+D7 00D7
+D8 0172
+D9 0141
+DA 015A
+DB 016A
+DC 00DC
+DD 017B
+DE 017D
+DF 00DF
+E0 0105
+E1 012F
+E2 0101
+E3 0107
+E4 00E4
+E5 00E5
+E6 0119
+E7 0113
+E8 010D
+E9 00E9
+EA 017A
+EB 0117
+EC 0123
+ED 0137
+EE 012B
+EF 013C
+F0 0161
+F1 0144
+F2 0146
+F3 00F3
+F4 014D
+F5 00F5
+F6 00F6
+F7 00F7
+F8 0173
+F9 0142
+FA 015B
+FB 016B
+FC 00FC
+FD 017C
+FE 017E
+FF 02D9
diff --git a/data/cp1258 b/data/cp1258
new file mode 100644
index 000000000..f7721c51b
--- /dev/null
+++ b/data/cp1258
@@ -0,0 +1,250 @@
+charset 8bit
+
+#
+# This file defines the font and character mappings used for Windows
+# Code Page 1258 (WinVietnamese) text printing.
+#
+# The first line consists of:
+#
+# direction width normal bold italic bold-italic
+#
+# Direction is the string "ltor" or "rtol", indicating left-to-right or
+# right-to-left text.
+#
+# Width is the string "single" or "double"; double means that the glyphs
+# are twice as wide as ASCII characters in the Courier typeface.
+#
+# "Normal", "bold", "italic", and "bold-italic" are the typefaces to use
+# for each presentation. If characters are only available in a single
+# style then only one typeface should be listed (e.g. "Symbol")
+#
+# Each font that is listed will be used (and downloaded if needed) when
+# printing.
+#
+
+00 ff ltor single Courier Courier-Bold Courier-Italic Courier-BoldItalic
+
+#
+# The following lines define the mapping from the 8-bit character set to
+# the Unicode glyphs for each character:
+#
+# char glyph
+#
+# "Char" and "glyph" are hexadecimal values.
+#
+
+20 0020
+21 0021
+22 0022
+23 0023
+24 0024
+25 0025
+26 0026
+27 0027
+28 0028
+29 0029
+2A 002A
+2B 002B
+2C 002C
+2D 002D
+2E 002E
+2F 002F
+30 0030
+31 0031
+32 0032
+33 0033
+34 0034
+35 0035
+36 0036
+37 0037
+38 0038
+39 0039
+3A 003A
+3B 003B
+3C 003C
+3D 003D
+3E 003E
+3F 003F
+40 0040
+41 0041
+42 0042
+43 0043
+44 0044
+45 0045
+46 0046
+47 0047
+48 0048
+49 0049
+4A 004A
+4B 004B
+4C 004C
+4D 004D
+4E 004E
+4F 004F
+50 0050
+51 0051
+52 0052
+53 0053
+54 0054
+55 0055
+56 0056
+57 0057
+58 0058
+59 0059
+5A 005A
+5B 005B
+5C 005C
+5D 005D
+5E 005E
+5F 005F
+60 0060
+61 0061
+62 0062
+63 0063
+64 0064
+65 0065
+66 0066
+67 0067
+68 0068
+69 0069
+6A 006A
+6B 006B
+6C 006C
+6D 006D
+6E 006E
+6F 006F
+70 0070
+71 0071
+72 0072
+73 0073
+74 0074
+75 0075
+76 0076
+77 0077
+78 0078
+79 0079
+7A 007A
+7B 007B
+7C 007C
+7D 007D
+7E 007E
+7F 007F
+80 20AC
+82 201A
+83 0192
+84 201E
+85 2026
+86 2020
+87 2021
+88 02C6
+89 2030
+8B 2039
+8C 0152
+91 2018
+92 2019
+93 201C
+94 201D
+95 2022
+96 2013
+97 2014
+98 02DC
+99 2122
+9B 203A
+9C 0153
+9F 0178
+A0 00A0
+A1 00A1
+A2 00A2
+A3 00A3
+A4 00A4
+A5 00A5
+A6 00A6
+A7 00A7
+A8 00A8
+A9 00A9
+AA 00AA
+AB 00AB
+AC 00AC
+AD 00AD
+AE 00AE
+AF 00AF
+B0 00B0
+B1 00B1
+B2 00B2
+B3 00B3
+B4 00B4
+B5 00B5
+B6 00B6
+B7 00B7
+B8 00B8
+B9 00B9
+BA 00BA
+BB 00BB
+BC 00BC
+BD 00BD
+BE 00BE
+BF 00BF
+C0 00C0
+C1 00C1
+C2 00C2
+C3 0102
+C4 00C4
+C5 00C5
+C6 00C6
+C7 00C7
+C8 00C8
+C9 00C9
+CA 00CA
+CB 00CB
+CC 0300
+CD 00CD
+CE 00CE
+CF 00CF
+D0 0110
+D1 00D1
+D2 0309
+D3 00D3
+D4 00D4
+D5 01A0
+D6 00D6
+D7 00D7
+D8 00D8
+D9 00D9
+DA 00DA
+DB 00DB
+DC 00DC
+DD 01AF
+DE 0303
+DF 00DF
+E0 00E0
+E1 00E1
+E2 00E2
+E3 0103
+E4 00E4
+E5 00E5
+E6 00E6
+E7 00E7
+E8 00E8
+E9 00E9
+EA 00EA
+EB 00EB
+EC 0301
+ED 00ED
+EE 00EE
+EF 00EF
+F0 0111
+F1 00F1
+F2 0323
+F3 00F3
+F4 00F4
+F5 01A1
+F6 00F6
+F7 00F7
+F8 00F8
+F9 00F9
+FA 00FA
+FB 00FB
+FC 00FC
+FD 01B0
+FE 20AB
+FF 00FF
diff --git a/data/cp874 b/data/cp874
new file mode 100644
index 000000000..886e46a4b
--- /dev/null
+++ b/data/cp874
@@ -0,0 +1,228 @@
+charset 8bit
+
+#
+# This file defines the font and character mappings used for Windows
+# Code Page 874 (Thai) text printing.
+#
+# The first line consists of:
+#
+# direction width normal bold italic bold-italic
+#
+# Direction is the string "ltor" or "rtol", indicating left-to-right or
+# right-to-left text.
+#
+# Width is the string "single" or "double"; double means that the glyphs
+# are twice as wide as ASCII characters in the Courier typeface.
+#
+# "Normal", "bold", "italic", and "bold-italic" are the typefaces to use
+# for each presentation. If characters are only available in a single
+# style then only one typeface should be listed (e.g. "Symbol")
+#
+# Each font that is listed will be used (and downloaded if needed) when
+# printing.
+#
+
+00 ff ltor single Courier Courier-Bold Courier-Italic Courier-BoldItalic
+
+#
+# The following lines define the mapping from the 8-bit character set to
+# the Unicode glyphs for each character:
+#
+# char glyph
+#
+# "Char" and "glyph" are hexadecimal values.
+#
+
+20 0020
+21 0021
+22 0022
+23 0023
+24 0024
+25 0025
+26 0026
+27 0027
+28 0028
+29 0029
+2A 002A
+2B 002B
+2C 002C
+2D 002D
+2E 002E
+2F 002F
+30 0030
+31 0031
+32 0032
+33 0033
+34 0034
+35 0035
+36 0036
+37 0037
+38 0038
+39 0039
+3A 003A
+3B 003B
+3C 003C
+3D 003D
+3E 003E
+3F 003F
+40 0040
+41 0041
+42 0042
+43 0043
+44 0044
+45 0045
+46 0046
+47 0047
+48 0048
+49 0049
+4A 004A
+4B 004B
+4C 004C
+4D 004D
+4E 004E
+4F 004F
+50 0050
+51 0051
+52 0052
+53 0053
+54 0054
+55 0055
+56 0056
+57 0057
+58 0058
+59 0059
+5A 005A
+5B 005B
+5C 005C
+5D 005D
+5E 005E
+5F 005F
+60 0060
+61 0061
+62 0062
+63 0063
+64 0064
+65 0065
+66 0066
+67 0067
+68 0068
+69 0069
+6A 006A
+6B 006B
+6C 006C
+6D 006D
+6E 006E
+6F 006F
+70 0070
+71 0071
+72 0072
+73 0073
+74 0074
+75 0075
+76 0076
+77 0077
+78 0078
+79 0079
+7A 007A
+7B 007B
+7C 007C
+7D 007D
+7E 007E
+7F 007F
+80 20AC
+85 2026
+91 2018
+92 2019
+93 201C
+94 201D
+95 2022
+96 2013
+97 2014
+A0 00A0
+A1 0E01
+A2 0E02
+A3 0E03
+A4 0E04
+A5 0E05
+A6 0E06
+A7 0E07
+A8 0E08
+A9 0E09
+AA 0E0A
+AB 0E0B
+AC 0E0C
+AD 0E0D
+AE 0E0E
+AF 0E0F
+B0 0E10
+B1 0E11
+B2 0E12
+B3 0E13
+B4 0E14
+B5 0E15
+B6 0E16
+B7 0E17
+B8 0E18
+B9 0E19
+BA 0E1A
+BB 0E1B
+BC 0E1C
+BD 0E1D
+BE 0E1E
+BF 0E1F
+C0 0E20
+C1 0E21
+C2 0E22
+C3 0E23
+C4 0E24
+C5 0E25
+C6 0E26
+C7 0E27
+C8 0E28
+C9 0E29
+CA 0E2A
+CB 0E2B
+CC 0E2C
+CD 0E2D
+CE 0E2E
+CF 0E2F
+D0 0E30
+D1 0E31
+D2 0E32
+D3 0E33
+D4 0E34
+D5 0E35
+D6 0E36
+D7 0E37
+D8 0E38
+D9 0E39
+DA 0E3A
+DF 0E3F
+E0 0E40
+E1 0E41
+E2 0E42
+E3 0E43
+E4 0E44
+E5 0E45
+E6 0E46
+E7 0E47
+E8 0E48
+E9 0E49
+EA 0E4A
+EB 0E4B
+EC 0E4C
+ED 0E4D
+EE 0E4E
+EF 0E4F
+F0 0E50
+F1 0E51
+F2 0E52
+F3 0E53
+F4 0E54
+F5 0E55
+F6 0E56
+F7 0E57
+F8 0E58
+F9 0E59
+FA 0E5A
+FB 0E5B
diff --git a/data/iso-8859-1 b/data/iso-8859-1
new file mode 100644
index 000000000..057d8aee3
--- /dev/null
+++ b/data/iso-8859-1
@@ -0,0 +1,251 @@
+charset 8bit
+
+#
+# This file defines the font and character mappings used for ISO-8859-1
+# (Latin1/West European) text printing.
+#
+# The first line consists of:
+#
+# direction width normal bold italic bold-italic
+#
+# Direction is the string "ltor" or "rtol", indicating left-to-right or
+# right-to-left text.
+#
+# Width is the string "single" or "double"; double means that the glyphs
+# are twice as wide as ASCII characters in the Courier typeface.
+#
+# "Normal", "bold", "italic", and "bold-italic" are the typefaces to use
+# for each presentation. If characters are only available in a single
+# style then only one typeface should be listed (e.g. "Symbol")
+#
+# Each font that is listed will be used (and downloaded if needed) when
+# printing.
+#
+
+00 ff ltor single Courier Courier-Bold Courier-Italic Courier-BoldItalic
+
+#
+# The following lines define the mapping from the 8-bit character set to
+# the Unicode glyphs for each character:
+#
+# char glyph
+#
+# "Char" and "glyph" are hexadecimal values.
+#
+
+20 0020
+21 0021
+22 0022
+23 0023
+24 0024
+25 0025
+26 0026
+27 0027
+28 0028
+29 0029
+2A 002A
+2B 002B
+2C 002C
+2D 002D
+2E 002E
+2F 002F
+30 0030
+31 0031
+32 0032
+33 0033
+34 0034
+35 0035
+36 0036
+37 0037
+38 0038
+39 0039
+3A 003A
+3B 003B
+3C 003C
+3D 003D
+3E 003E
+3F 003F
+40 0040
+41 0041
+42 0042
+43 0043
+44 0044
+45 0045
+46 0046
+47 0047
+48 0048
+49 0049
+4A 004A
+4B 004B
+4C 004C
+4D 004D
+4E 004E
+4F 004F
+50 0050
+51 0051
+52 0052
+53 0053
+54 0054
+55 0055
+56 0056
+57 0057
+58 0058
+59 0059
+5A 005A
+5B 005B
+5C 005C
+5D 005D
+5E 005E
+5F 005F
+60 0060
+61 0061
+62 0062
+63 0063
+64 0064
+65 0065
+66 0066
+67 0067
+68 0068
+69 0069
+6A 006A
+6B 006B
+6C 006C
+6D 006D
+6E 006E
+6F 006F
+70 0070
+71 0071
+72 0072
+73 0073
+74 0074
+75 0075
+76 0076
+77 0077
+78 0078
+79 0079
+7A 007A
+7B 007B
+7C 007C
+7D 007D
+7E 007E
+80 20AC
+82 201A
+83 0192
+84 201E
+85 2026
+86 2020
+87 2021
+88 02C6
+89 2030
+8A 0160
+8B 2039
+8C 0152
+91 2018
+92 2019
+93 201C
+94 201D
+95 2022
+96 2013
+97 2014
+98 02DC
+99 2122
+9A 0161
+9B 203A
+9C 0153
+9F 0178
+A0 00A0
+A1 00A1
+A2 00A2
+A3 00A3
+A4 00A4
+A5 00A5
+A6 00A6
+A7 00A7
+A8 00A8
+A9 00A9
+AA 00AA
+AB 00AB
+AC 00AC
+AD 00AD
+AE 00AE
+AF 00AF
+B0 00B0
+B1 00B1
+B2 00B2
+B3 00B3
+B4 00B4
+B5 00B5
+B6 00B6
+B7 00B7
+B8 00B8
+B9 00B9
+BA 00BA
+BB 00BB
+BC 00BC
+BD 00BD
+BE 00BE
+BF 00BF
+C0 00C0
+C1 00C1
+C2 00C2
+C3 00C3
+C4 00C4
+C5 00C5
+C6 00C6
+C7 00C7
+C8 00C8
+C9 00C9
+CA 00CA
+CB 00CB
+CC 00CC
+CD 00CD
+CE 00CE
+CF 00CF
+D0 00D0
+D1 00D1
+D2 00D2
+D3 00D3
+D4 00D4
+D5 00D5
+D6 00D6
+D7 00D7
+D8 00D8
+D9 00D9
+DA 00DA
+DB 00DB
+DC 00DC
+DD 00DD
+DE 00DE
+DF 00DF
+E0 00E0
+E1 00E1
+E2 00E2
+E3 00E3
+E4 00E4
+E5 00E5
+E6 00E6
+E7 00E7
+E8 00E8
+E9 00E9
+EA 00EA
+EB 00EB
+EC 00EC
+ED 00ED
+EE 00EE
+EF 00EF
+F0 00F0
+F1 00F1
+F2 00F2
+F3 00F3
+F4 00F4
+F5 00F5
+F6 00F6
+F7 00F7
+F8 00F8
+F9 00F9
+FA 00FA
+FB 00FB
+FC 00FC
+FD 00FD
+FE 00FE
+FF 00FF
diff --git a/data/iso-8859-10 b/data/iso-8859-10
new file mode 100644
index 000000000..31f55552e
--- /dev/null
+++ b/data/iso-8859-10
@@ -0,0 +1,251 @@
+charset 8bit
+
+#
+# This file defines the font and character mappings used for ISO-8859-10
+# (Latin6/Nordic) text printing.
+#
+# The first line consists of:
+#
+# direction width normal bold italic bold-italic
+#
+# Direction is the string "ltor" or "rtol", indicating left-to-right or
+# right-to-left text.
+#
+# Width is the string "single" or "double"; double means that the glyphs
+# are twice as wide as ASCII characters in the Courier typeface.
+#
+# "Normal", "bold", "italic", and "bold-italic" are the typefaces to use
+# for each presentation. If characters are only available in a single
+# style then only one typeface should be listed (e.g. "Symbol")
+#
+# Each font that is listed will be used (and downloaded if needed) when
+# printing.
+#
+
+00 ff ltor single Courier Courier-Bold Courier-Italic Courier-BoldItalic
+
+#
+# The following lines define the mapping from the 8-bit character set to
+# the Unicode glyphs for each character:
+#
+# char glyph
+#
+# "Char" and "glyph" are hexadecimal values.
+#
+
+20 0020
+21 0021
+22 0022
+23 0023
+24 0024
+25 0025
+26 0026
+27 0027
+28 0028
+29 0029
+2A 002A
+2B 002B
+2C 002C
+2D 002D
+2E 002E
+2F 002F
+30 0030
+31 0031
+32 0032
+33 0033
+34 0034
+35 0035
+36 0036
+37 0037
+38 0038
+39 0039
+3A 003A
+3B 003B
+3C 003C
+3D 003D
+3E 003E
+3F 003F
+40 0040
+41 0041
+42 0042
+43 0043
+44 0044
+45 0045
+46 0046
+47 0047
+48 0048
+49 0049
+4A 004A
+4B 004B
+4C 004C
+4D 004D
+4E 004E
+4F 004F
+50 0050
+51 0051
+52 0052
+53 0053
+54 0054
+55 0055
+56 0056
+57 0057
+58 0058
+59 0059
+5A 005A
+5B 005B
+5C 005C
+5D 005D
+5E 005E
+5F 005F
+60 0060
+61 0061
+62 0062
+63 0063
+64 0064
+65 0065
+66 0066
+67 0067
+68 0068
+69 0069
+6A 006A
+6B 006B
+6C 006C
+6D 006D
+6E 006E
+6F 006F
+70 0070
+71 0071
+72 0072
+73 0073
+74 0074
+75 0075
+76 0076
+77 0077
+78 0078
+79 0079
+7A 007A
+7B 007B
+7C 007C
+7D 007D
+7E 007E
+80 20AC
+82 201A
+83 0192
+84 201E
+85 2026
+86 2020
+87 2021
+88 02C6
+89 2030
+8A 0160
+8B 2039
+8C 0152
+91 2018
+92 2019
+93 201C
+94 201D
+95 2022
+96 2013
+97 2014
+98 02DC
+99 2122
+9A 0161
+9B 203A
+9C 0153
+9F 0178
+A0 00A0
+A1 0104
+A2 0112
+A3 0122
+A4 012A
+A5 0128
+A6 0136
+A7 00A7
+A8 013B
+A9 0110
+AA 0160
+AB 0166
+AC 017D
+AD 00AD
+AE 016A
+AF 014A
+B0 00B0
+B1 0105
+B2 0113
+B3 0123
+B4 012B
+B5 0129
+B6 0137
+B7 00B7
+B8 013C
+B9 0111
+BA 0161
+BB 0167
+BC 017E
+BD 2015
+BE 016B
+BF 014B
+C0 0100
+C1 00C1
+C2 00C2
+C3 00C3
+C4 00C4
+C5 00C5
+C6 00C6
+C7 012E
+C8 010C
+C9 00C9
+CA 0118
+CB 00CB
+CC 0116
+CD 00CD
+CE 00CE
+CF 00CF
+D0 0110
+D1 0145
+D2 014C
+D3 00D3
+D4 00D4
+D5 00D5
+D6 00D6
+D7 0168
+D8 00D8
+D9 0172
+DA 00DA
+DB 00DB
+DC 00DC
+DD 00DD
+DE 00DE
+DF 00DF
+E0 0101
+E1 00E1
+E2 00E2
+E3 00E3
+E4 00E4
+E5 00E5
+E6 00E6
+E7 012F
+E8 010D
+E9 00E9
+EA 0119
+EB 00EB
+EC 0117
+ED 00ED
+EE 00EE
+EF 00EF
+F0 00F0
+F1 0146
+F2 014D
+F3 00F3
+F4 00F4
+F5 00F5
+F6 00F6
+F7 0169
+F8 00F8
+F9 0173
+FA 00FA
+FB 00FB
+FC 00FC
+FD 00FD
+FE 00FD
+FF 0138
diff --git a/data/iso-8859-13 b/data/iso-8859-13
new file mode 100644
index 000000000..dcfacca9d
--- /dev/null
+++ b/data/iso-8859-13
@@ -0,0 +1,251 @@
+charset 8bit
+
+#
+# This file defines the font and character mappings used for ISO-8859-13
+# (Latin7/Baltic Rim) text printing.
+#
+# The first line consists of:
+#
+# direction width normal bold italic bold-italic
+#
+# Direction is the string "ltor" or "rtol", indicating left-to-right or
+# right-to-left text.
+#
+# Width is the string "single" or "double"; double means that the glyphs
+# are twice as wide as ASCII characters in the Courier typeface.
+#
+# "Normal", "bold", "italic", and "bold-italic" are the typefaces to use
+# for each presentation. If characters are only available in a single
+# style then only one typeface should be listed (e.g. "Symbol")
+#
+# Each font that is listed will be used (and downloaded if needed) when
+# printing.
+#
+
+00 ff ltor single Courier Courier-Bold Courier-Italic Courier-BoldItalic
+
+#
+# The following lines define the mapping from the 8-bit character set to
+# the Unicode glyphs for each character:
+#
+# char glyph
+#
+# "Char" and "glyph" are hexadecimal values.
+#
+
+20 0020
+21 0021
+22 0022
+23 0023
+24 0024
+25 0025
+26 0026
+27 0027
+28 0028
+29 0029
+2A 002A
+2B 002B
+2C 002C
+2D 002D
+2E 002E
+2F 002F
+30 0030
+31 0031
+32 0032
+33 0033
+34 0034
+35 0035
+36 0036
+37 0037
+38 0038
+39 0039
+3A 003A
+3B 003B
+3C 003C
+3D 003D
+3E 003E
+3F 003F
+40 0040
+41 0041
+42 0042
+43 0043
+44 0044
+45 0045
+46 0046
+47 0047
+48 0048
+49 0049
+4A 004A
+4B 004B
+4C 004C
+4D 004D
+4E 004E
+4F 004F
+50 0050
+51 0051
+52 0052
+53 0053
+54 0054
+55 0055
+56 0056
+57 0057
+58 0058
+59 0059
+5A 005A
+5B 005B
+5C 005C
+5D 005D
+5E 005E
+5F 005F
+60 0060
+61 0061
+62 0062
+63 0063
+64 0064
+65 0065
+66 0066
+67 0067
+68 0068
+69 0069
+6A 006A
+6B 006B
+6C 006C
+6D 006D
+6E 006E
+6F 006F
+70 0070
+71 0071
+72 0072
+73 0073
+74 0074
+75 0075
+76 0076
+77 0077
+78 0078
+79 0079
+7A 007A
+7B 007B
+7C 007C
+7D 007D
+7E 007E
+80 20AC
+82 201A
+83 0192
+84 201E
+85 2026
+86 2020
+87 2021
+88 02C6
+89 2030
+8A 0160
+8B 2039
+8C 0152
+91 2018
+92 2019
+93 201C
+94 201D
+95 2022
+96 2013
+97 2014
+98 02DC
+99 2122
+9A 0161
+9B 203A
+9C 0153
+9F 0178
+A0 00A0
+A1 201D
+A2 00A2
+A3 00A3
+A4 00A4
+A5 201E
+A6 00A6
+A7 00A7
+A8 00D8
+A9 00A9
+AA 0156
+AB 00AB
+AC 00AC
+AD 00AD
+AE 00AE
+AF 00C6
+B0 00B0
+B1 00B1
+B2 00B2
+B3 00B3
+B4 201C
+B5 00B5
+B6 00B6
+B7 00B7
+B8 00F8
+B9 00B9
+BA 0157
+BB 00BB
+BC 00BC
+BD 00BD
+BE 00BE
+BF 00E6
+C0 0104
+C1 012E
+C2 0100
+C3 0106
+C4 00C4
+C5 00C5
+C6 0118
+C7 0112
+C8 010C
+C9 00C9
+CA 0179
+CB 0116
+CC 0122
+CD 0136
+CE 012A
+CF 013B
+D0 0160
+D1 0143
+D2 0145
+D3 00D3
+D4 014C
+D5 00D5
+D6 00D6
+D7 00D7
+D8 0172
+D9 0141
+DA 015A
+DB 016A
+DC 00DC
+DD 017B
+DE 017D
+DF 00DF
+E0 0105
+E1 012F
+E2 0101
+E3 0107
+E4 00E4
+E5 00E5
+E6 0119
+E7 0113
+E8 010D
+E9 00E9
+EA 017A
+EB 0117
+EC 0123
+ED 0137
+EE 012B
+EF 013C
+F0 0161
+F1 0144
+F2 0146
+F3 00F3
+F4 014D
+F5 00F5
+F6 00F6
+F7 00F7
+F8 0173
+F9 0142
+FA 015B
+FB 016B
+FC 00FC
+FD 017C
+FE 017E
+FF 2019
diff --git a/data/iso-8859-14 b/data/iso-8859-14
new file mode 100644
index 000000000..ca0097a47
--- /dev/null
+++ b/data/iso-8859-14
@@ -0,0 +1,251 @@
+charset 8bit
+
+#
+# This file defines the font and character mappings used for ISO-8859-14
+# (Latin8/Celtic) text printing.
+#
+# The first line consists of:
+#
+# direction width normal bold italic bold-italic
+#
+# Direction is the string "ltor" or "rtol", indicating left-to-right or
+# right-to-left text.
+#
+# Width is the string "single" or "double"; double means that the glyphs
+# are twice as wide as ASCII characters in the Courier typeface.
+#
+# "Normal", "bold", "italic", and "bold-italic" are the typefaces to use
+# for each presentation. If characters are only available in a single
+# style then only one typeface should be listed (e.g. "Symbol")
+#
+# Each font that is listed will be used (and downloaded if needed) when
+# printing.
+#
+
+00 ff ltor single Courier Courier-Bold Courier-Italic Courier-BoldItalic
+
+#
+# The following lines define the mapping from the 8-bit character set to
+# the Unicode glyphs for each character:
+#
+# char glyph
+#
+# "Char" and "glyph" are hexadecimal values.
+#
+
+20 0020
+21 0021
+22 0022
+23 0023
+24 0024
+25 0025
+26 0026
+27 0027
+28 0028
+29 0029
+2A 002A
+2B 002B
+2C 002C
+2D 002D
+2E 002E
+2F 002F
+30 0030
+31 0031
+32 0032
+33 0033
+34 0034
+35 0035
+36 0036
+37 0037
+38 0038
+39 0039
+3A 003A
+3B 003B
+3C 003C
+3D 003D
+3E 003E
+3F 003F
+40 0040
+41 0041
+42 0042
+43 0043
+44 0044
+45 0045
+46 0046
+47 0047
+48 0048
+49 0049
+4A 004A
+4B 004B
+4C 004C
+4D 004D
+4E 004E
+4F 004F
+50 0050
+51 0051
+52 0052
+53 0053
+54 0054
+55 0055
+56 0056
+57 0057
+58 0058
+59 0059
+5A 005A
+5B 005B
+5C 005C
+5D 005D
+5E 005E
+5F 005F
+60 0060
+61 0061
+62 0062
+63 0063
+64 0064
+65 0065
+66 0066
+67 0067
+68 0068
+69 0069
+6A 006A
+6B 006B
+6C 006C
+6D 006D
+6E 006E
+6F 006F
+70 0070
+71 0071
+72 0072
+73 0073
+74 0074
+75 0075
+76 0076
+77 0077
+78 0078
+79 0079
+7A 007A
+7B 007B
+7C 007C
+7D 007D
+7E 007E
+80 20AC
+82 201A
+83 0192
+84 201E
+85 2026
+86 2020
+87 2021
+88 02C6
+89 2030
+8A 0160
+8B 2039
+8C 0152
+91 2018
+92 2019
+93 201C
+94 201D
+95 2022
+96 2013
+97 2014
+98 02DC
+99 2122
+9A 0161
+9B 203A
+9C 0153
+9F 0178
+A0 00A0
+A1 1E02
+A2 1E03
+A3 00A3
+A4 010A
+A5 010B
+A6 1E0A
+A7 00A7
+A8 1E80
+A9 00A9
+AA 1E82
+AB 1E0B
+AC 1EF2
+AD 00AD
+AE 00AE
+AF 0178
+B0 1E1E
+B1 1E1F
+B2 0120
+B3 0121
+B4 1E40
+B5 1E41
+B6 00B6
+B7 1E56
+B8 1E81
+B9 1E57
+BA 1E83
+BB 1E60
+BC 1EF3
+BD 1E84
+BE 1E85
+BF 1E61
+C0 00C0
+C1 00C1
+C2 00C2
+C3 00C3
+C4 00C4
+C5 00C5
+C6 00C6
+C7 00C7
+C8 00C8
+C9 00C9
+CA 00CA
+CB 00CB
+CC 00CC
+CD 00CD
+CE 00CE
+CF 00CF
+D0 0174
+D1 00D1
+D2 00D2
+D3 00D3
+D4 00D4
+D5 00D5
+D6 00D6
+D7 1E6A
+D8 00D8
+D9 00D9
+DA 00DA
+DB 00DB
+DC 00DC
+DD 00DD
+DE 0176
+DF 00DF
+E0 00E0
+E1 00E1
+E2 00E2
+E3 00E3
+E4 00E4
+E5 00E5
+E6 00E6
+E7 00E7
+E8 00E8
+E9 00E9
+EA 00EA
+EB 00EB
+EC 00EC
+ED 00ED
+EE 00EE
+EF 00EF
+F0 0175
+F1 00F1
+F2 00F2
+F3 00F3
+F4 00F4
+F5 00F5
+F6 00F6
+F7 1E6B
+F8 00F8
+F9 00F9
+FA 00FA
+FB 00FB
+FC 00FC
+FD 00FD
+FE 0177
+FF 00FF
diff --git a/data/iso-8859-15 b/data/iso-8859-15
new file mode 100644
index 000000000..334160f4f
--- /dev/null
+++ b/data/iso-8859-15
@@ -0,0 +1,251 @@
+charset 8bit
+
+#
+# This file defines the font and character mappings used for ISO-8859-15
+# (Latin9/West Europe + Euro) text printing.
+#
+# The first line consists of:
+#
+# direction width normal bold italic bold-italic
+#
+# Direction is the string "ltor" or "rtol", indicating left-to-right or
+# right-to-left text.
+#
+# Width is the string "single" or "double"; double means that the glyphs
+# are twice as wide as ASCII characters in the Courier typeface.
+#
+# "Normal", "bold", "italic", and "bold-italic" are the typefaces to use
+# for each presentation. If characters are only available in a single
+# style then only one typeface should be listed (e.g. "Symbol")
+#
+# Each font that is listed will be used (and downloaded if needed) when
+# printing.
+#
+
+00 ff ltor single Courier Courier-Bold Courier-Italic Courier-BoldItalic
+
+#
+# The following lines define the mapping from the 8-bit character set to
+# the Unicode glyphs for each character:
+#
+# char glyph
+#
+# "Char" and "glyph" are hexadecimal values.
+#
+
+20 0020
+21 0021
+22 0022
+23 0023
+24 0024
+25 0025
+26 0026
+27 0027
+28 0028
+29 0029
+2A 002A
+2B 002B
+2C 002C
+2D 002D
+2E 002E
+2F 002F
+30 0030
+31 0031
+32 0032
+33 0033
+34 0034
+35 0035
+36 0036
+37 0037
+38 0038
+39 0039
+3A 003A
+3B 003B
+3C 003C
+3D 003D
+3E 003E
+3F 003F
+40 0040
+41 0041
+42 0042
+43 0043
+44 0044
+45 0045
+46 0046
+47 0047
+48 0048
+49 0049
+4A 004A
+4B 004B
+4C 004C
+4D 004D
+4E 004E
+4F 004F
+50 0050
+51 0051
+52 0052
+53 0053
+54 0054
+55 0055
+56 0056
+57 0057
+58 0058
+59 0059
+5A 005A
+5B 005B
+5C 005C
+5D 005D
+5E 005E
+5F 005F
+60 0060
+61 0061
+62 0062
+63 0063
+64 0064
+65 0065
+66 0066
+67 0067
+68 0068
+69 0069
+6A 006A
+6B 006B
+6C 006C
+6D 006D
+6E 006E
+6F 006F
+70 0070
+71 0071
+72 0072
+73 0073
+74 0074
+75 0075
+76 0076
+77 0077
+78 0078
+79 0079
+7A 007A
+7B 007B
+7C 007C
+7D 007D
+7E 007E
+80 20AC
+82 201A
+83 0192
+84 201E
+85 2026
+86 2020
+87 2021
+88 02C6
+89 2030
+8A 0160
+8B 2039
+8C 0152
+91 2018
+92 2019
+93 201C
+94 201D
+95 2022
+96 2013
+97 2014
+98 02DC
+99 2122
+9A 0161
+9B 203A
+9C 0153
+9F 0178
+A0 00A0
+A1 00A1
+A2 00A2
+A3 00A3
+A4 20AC
+A5 00A5
+A6 0160
+A7 00A7
+A8 0161
+A9 00A9
+AA 00AA
+AB 00AB
+AC 00AC
+AD 00AD
+AE 00AE
+AF 00AF
+B0 00B0
+B1 00B1
+B2 00B2
+B3 00B3
+B4 017D
+B5 00B5
+B6 00B6
+B7 00B7
+B8 017E
+B9 00B9
+BA 00BA
+BB 00BB
+BC 0152
+BD 0153
+BE 0178
+BF 00BF
+C0 00C0
+C1 00C1
+C2 00C2
+C3 00C3
+C4 00C4
+C5 00C5
+C6 00C6
+C7 00C7
+C8 00C8
+C9 00C9
+CA 00CA
+CB 00CB
+CC 00CC
+CD 00CD
+CE 00CE
+CF 00CF
+D0 00D0
+D1 00D1
+D2 00D2
+D3 00D3
+D4 00D4
+D5 00D5
+D6 00D6
+D7 00D7
+D8 00D8
+D9 00D9
+DA 00DA
+DB 00DB
+DC 00DC
+DD 00DD
+DE 00DE
+DF 00DF
+E0 00E0
+E1 00E1
+E2 00E2
+E3 00E3
+E4 00E4
+E5 00E5
+E6 00E6
+E7 00E7
+E8 00E8
+E9 00E9
+EA 00EA
+EB 00EB
+EC 00EC
+ED 00ED
+EE 00EE
+EF 00EF
+F0 00F0
+F1 00F1
+F2 00F2
+F3 00F3
+F4 00F4
+F5 00F5
+F6 00F6
+F7 00F7
+F8 00F8
+F9 00F9
+FA 00FA
+FB 00FB
+FC 00FC
+FD 00FD
+FE 00FE
+FF 00FF
diff --git a/data/iso-8859-2 b/data/iso-8859-2
new file mode 100644
index 000000000..19b77878d
--- /dev/null
+++ b/data/iso-8859-2
@@ -0,0 +1,253 @@
+charset 8bit
+
+#
+# This file defines the font and character mappings used for ISO-8859-2
+# (Latin2/East European) text printing.
+#
+# The first line consists of:
+#
+# direction width normal bold italic bold-italic
+#
+# Direction is the string "ltor" or "rtol", indicating left-to-right or
+# right-to-left text.
+#
+# Width is the string "single" or "double"; double means that the glyphs
+# are twice as wide as ASCII characters in the Courier typeface.
+#
+# "Normal", "bold", "italic", and "bold-italic" are the typefaces to use
+# for each presentation. If characters are only available in a single
+# style then only one typeface should be listed (e.g. "Symbol")
+#
+# Each font that is listed will be used (and downloaded if needed) when
+# printing.
+#
+
+00 ff ltor single Courier Courier-Bold Courier-Italic Courier-BoldItalic
+
+#
+# The following lines define the mapping from the 8-bit character set to
+# the Unicode glyphs for each character:
+#
+# char glyph
+#
+# "Char" and "glyph" are hexadecimal values.
+#
+
+20 0020
+21 0021
+22 0022
+23 0023
+24 0024
+25 0025
+26 0026
+27 0027
+28 0028
+29 0029
+2A 002A
+2B 002B
+2C 002C
+2D 002D
+2E 002E
+2F 002F
+30 0030
+31 0031
+32 0032
+33 0033
+34 0034
+35 0035
+36 0036
+37 0037
+38 0038
+39 0039
+3A 003A
+3B 003B
+3C 003C
+3D 003D
+3E 003E
+3F 003F
+40 0040
+41 0041
+42 0042
+43 0043
+44 0044
+45 0045
+46 0046
+47 0047
+48 0048
+49 0049
+4A 004A
+4B 004B
+4C 004C
+4D 004D
+4E 004E
+4F 004F
+50 0050
+51 0051
+52 0052
+53 0053
+54 0054
+55 0055
+56 0056
+57 0057
+58 0058
+59 0059
+5A 005A
+5B 005B
+5C 005C
+5D 005D
+5E 005E
+5F 005F
+60 0060
+61 0061
+62 0062
+63 0063
+64 0064
+65 0065
+66 0066
+67 0067
+68 0068
+69 0069
+6A 006A
+6B 006B
+6C 006C
+6D 006D
+6E 006E
+6F 006F
+70 0070
+71 0071
+72 0072
+73 0073
+74 0074
+75 0075
+76 0076
+77 0077
+78 0078
+79 0079
+7A 007A
+7B 007B
+7C 007C
+7D 007D
+7E 007E
+80 20AC
+82 201A
+84 201E
+85 2026
+86 2020
+87 2021
+89 2030
+8A 0160
+8B 2039
+8C 015A
+8D 0164
+8E 017D
+8F 0179
+91 2018
+92 2019
+93 201C
+94 201D
+95 2022
+96 2013
+97 2014
+99 2122
+9A 0161
+9B 203A
+8C 015B
+8D 0165
+8E 017E
+8F 017A
+A0 00A0
+A1 0104
+A2 02D8
+A3 0141
+A4 00A4
+A5 013D
+A6 015A
+A7 00A7
+A8 00A8
+A9 0160
+AA 015E
+AB 0164
+AC 0179
+AD 00AD
+AE 017D
+AF 017B
+B0 00B0
+B1 0105
+B2 02DB
+B3 0142
+B4 00B4
+B5 013E
+B6 015B
+B7 02C7
+B8 00B8
+B9 0161
+BA 015F
+BB 0165
+BC 017A
+BD 02DD
+BE 017E
+BF 017C
+C0 0154
+C1 00C1
+C2 00C2
+C3 0102
+C4 00C4
+C5 0139
+C6 0106
+C7 00C7
+C8 010C
+C9 00C9
+CA 0118
+CB 00CB
+CC 011A
+CD 00CD
+CE 00CE
+CF 010E
+D0 0110
+D1 0143
+D2 0147
+D3 00D3
+D4 00D4
+D5 0150
+D6 00D6
+D7 00D7
+D8 0158
+D9 016E
+DA 00DA
+DB 0170
+DC 00DC
+DD 00DD
+DE 0162
+DF 00DF
+E0 0155
+E1 00E1
+E2 00E2
+E3 0103
+E4 00E4
+E5 013A
+E6 0107
+E7 00E7
+E8 010D
+E9 00E9
+EA 0119
+EB 00EB
+EC 011B
+ED 00ED
+EE 00EE
+EF 010F
+F0 0111
+F1 0144
+F2 0148
+F3 00F3
+F4 00F4
+F5 0151
+F6 00F6
+F7 00F7
+F8 0159
+F9 016F
+FA 00FA
+FB 0171
+FC 00FC
+FD 00FD
+FE 0163
+FF 02D9
diff --git a/data/iso-8859-3 b/data/iso-8859-3
new file mode 100644
index 000000000..efc4529ed
--- /dev/null
+++ b/data/iso-8859-3
@@ -0,0 +1,244 @@
+charset 8bit
+
+#
+# This file defines the font and character mappings used for ISO-8859-3
+# (Latin3/South European) text printing.
+#
+# The first line consists of:
+#
+# direction width normal bold italic bold-italic
+#
+# Direction is the string "ltor" or "rtol", indicating left-to-right or
+# right-to-left text.
+#
+# Width is the string "single" or "double"; double means that the glyphs
+# are twice as wide as ASCII characters in the Courier typeface.
+#
+# "Normal", "bold", "italic", and "bold-italic" are the typefaces to use
+# for each presentation. If characters are only available in a single
+# style then only one typeface should be listed (e.g. "Symbol")
+#
+# Each font that is listed will be used (and downloaded if needed) when
+# printing.
+#
+
+00 ff ltor single Courier Courier-Bold Courier-Italic Courier-BoldItalic
+
+#
+# The following lines define the mapping from the 8-bit character set to
+# the Unicode glyphs for each character:
+#
+# char glyph
+#
+# "Char" and "glyph" are hexadecimal values.
+#
+
+20 0020
+21 0021
+22 0022
+23 0023
+24 0024
+25 0025
+26 0026
+27 0027
+28 0028
+29 0029
+2A 002A
+2B 002B
+2C 002C
+2D 002D
+2E 002E
+2F 002F
+30 0030
+31 0031
+32 0032
+33 0033
+34 0034
+35 0035
+36 0036
+37 0037
+38 0038
+39 0039
+3A 003A
+3B 003B
+3C 003C
+3D 003D
+3E 003E
+3F 003F
+40 0040
+41 0041
+42 0042
+43 0043
+44 0044
+45 0045
+46 0046
+47 0047
+48 0048
+49 0049
+4A 004A
+4B 004B
+4C 004C
+4D 004D
+4E 004E
+4F 004F
+50 0050
+51 0051
+52 0052
+53 0053
+54 0054
+55 0055
+56 0056
+57 0057
+58 0058
+59 0059
+5A 005A
+5B 005B
+5C 005C
+5D 005D
+5E 005E
+5F 005F
+60 0060
+61 0061
+62 0062
+63 0063
+64 0064
+65 0065
+66 0066
+67 0067
+68 0068
+69 0069
+6A 006A
+6B 006B
+6C 006C
+6D 006D
+6E 006E
+6F 006F
+70 0070
+71 0071
+72 0072
+73 0073
+74 0074
+75 0075
+76 0076
+77 0077
+78 0078
+79 0079
+7A 007A
+7B 007B
+7C 007C
+7D 007D
+7E 007E
+80 20AC
+82 201A
+83 0192
+84 201E
+85 2026
+86 2020
+87 2021
+88 02C6
+89 2030
+8A 0160
+8B 2039
+8C 0152
+91 2018
+92 2019
+93 201C
+94 201D
+95 2022
+96 2013
+97 2014
+98 02DC
+99 2122
+9A 0161
+9B 203A
+9C 0153
+9F 0178
+A0 00A0
+A1 0126
+A2 02D8
+A3 00A3
+A4 00A4
+A6 0124
+A7 00A7
+A8 00A8
+A9 0130
+AA 015E
+AB 011E
+AC 0134
+AD 00AD
+AF 017B
+B0 00B0
+B1 0127
+B2 00B2
+B3 00B3
+B4 00B4
+B5 00B5
+B6 0125
+B7 00B7
+B8 00B8
+B9 0131
+BA 015F
+BB 011F
+BC 0135
+BD 00BD
+BF 017C
+C0 00C0
+C1 00C1
+C2 00C2
+C4 00C4
+C5 010A
+C6 0108
+C7 00C7
+C8 00C8
+C9 00C9
+CA 00CA
+CB 00CB
+CC 00CC
+CD 00CD
+CE 00CE
+CF 00CF
+D1 00D1
+D2 00D2
+D3 00D3
+D4 00D4
+D5 0120
+D6 00D6
+D7 00D7
+D8 011C
+D9 00D9
+DA 00DA
+DB 00DB
+DC 00DC
+DD 016C
+DE 015C
+DF 00DF
+E0 00E0
+E1 00E1
+E2 00E2
+E4 00E4
+E5 010B
+E6 0109
+E7 00E7
+E8 00E8
+E9 00E9
+EA 00EA
+EB 00EB
+EC 00EC
+ED 00ED
+EE 00EE
+EF 00EF
+F1 00F1
+F2 00F2
+F3 00F3
+F4 00F4
+F5 0121
+F6 00F6
+F7 00F7
+F8 011D
+F9 00F9
+FA 00FA
+FB 00FB
+FC 00FC
+FD 016D
+FE 015D
+FF 02D9
diff --git a/data/iso-8859-4 b/data/iso-8859-4
new file mode 100644
index 000000000..5c93156eb
--- /dev/null
+++ b/data/iso-8859-4
@@ -0,0 +1,251 @@
+charset 8bit
+
+#
+# This file defines the font and character mappings used for ISO-8859-4
+# (Latin4/North European) text printing.
+#
+# The first line consists of:
+#
+# direction width normal bold italic bold-italic
+#
+# Direction is the string "ltor" or "rtol", indicating left-to-right or
+# right-to-left text.
+#
+# Width is the string "single" or "double"; double means that the glyphs
+# are twice as wide as ASCII characters in the Courier typeface.
+#
+# "Normal", "bold", "italic", and "bold-italic" are the typefaces to use
+# for each presentation. If characters are only available in a single
+# style then only one typeface should be listed (e.g. "Symbol")
+#
+# Each font that is listed will be used (and downloaded if needed) when
+# printing.
+#
+
+00 ff ltor single Courier Courier-Bold Courier-Italic Courier-BoldItalic
+
+#
+# The following lines define the mapping from the 8-bit character set to
+# the Unicode glyphs for each character:
+#
+# char glyph
+#
+# "Char" and "glyph" are hexadecimal values.
+#
+
+20 0020
+21 0021
+22 0022
+23 0023
+24 0024
+25 0025
+26 0026
+27 0027
+28 0028
+29 0029
+2A 002A
+2B 002B
+2C 002C
+2D 002D
+2E 002E
+2F 002F
+30 0030
+31 0031
+32 0032
+33 0033
+34 0034
+35 0035
+36 0036
+37 0037
+38 0038
+39 0039
+3A 003A
+3B 003B
+3C 003C
+3D 003D
+3E 003E
+3F 003F
+40 0040
+41 0041
+42 0042
+43 0043
+44 0044
+45 0045
+46 0046
+47 0047
+48 0048
+49 0049
+4A 004A
+4B 004B
+4C 004C
+4D 004D
+4E 004E
+4F 004F
+50 0050
+51 0051
+52 0052
+53 0053
+54 0054
+55 0055
+56 0056
+57 0057
+58 0058
+59 0059
+5A 005A
+5B 005B
+5C 005C
+5D 005D
+5E 005E
+5F 005F
+60 0060
+61 0061
+62 0062
+63 0063
+64 0064
+65 0065
+66 0066
+67 0067
+68 0068
+69 0069
+6A 006A
+6B 006B
+6C 006C
+6D 006D
+6E 006E
+6F 006F
+70 0070
+71 0071
+72 0072
+73 0073
+74 0074
+75 0075
+76 0076
+77 0077
+78 0078
+79 0079
+7A 007A
+7B 007B
+7C 007C
+7D 007D
+7E 007E
+80 20AC
+82 201A
+83 0192
+84 201E
+85 2026
+86 2020
+87 2021
+88 02C6
+89 2030
+8A 0160
+8B 2039
+8C 0152
+91 2018
+92 2019
+93 201C
+94 201D
+95 2022
+96 2013
+97 2014
+98 02DC
+99 2122
+9A 0161
+9B 203A
+9C 0153
+9F 0178
+A0 00A0
+A1 0104
+A2 0138
+A3 0156
+A4 00A4
+A5 0128
+A6 013B
+A7 00A7
+A8 00A8
+A9 0160
+AA 0112
+AB 0122
+AC 0166
+AD 00AD
+AE 017D
+AF 00AF
+B0 00B0
+B1 0105
+B2 02DB
+B3 0157
+B4 00B4
+B5 0129
+B6 013C
+B7 02C7
+B8 00B8
+B9 0161
+BA 0113
+BB 0123
+BC 0167
+BD 014A
+BE 017E
+BF 014B
+C0 0100
+C1 00C1
+C2 00C2
+C3 00C3
+C4 00C4
+C5 00C5
+C6 00C6
+C7 012E
+C8 010C
+C9 00C9
+CA 0118
+CB 00CB
+CC 0116
+CD 00CD
+CE 00CE
+CF 012A
+D0 0110
+D1 0145
+D2 014C
+D3 0136
+D4 00D4
+D5 00D5
+D6 00D6
+D7 00D7
+D8 00D8
+D9 0172
+DA 00DA
+DB 00DB
+DC 00DC
+DD 0168
+DE 016A
+DF 00DF
+E0 0101
+E1 00E1
+E2 00E2
+E3 00E3
+E4 00E4
+E5 00E5
+E6 00E6
+E7 012F
+E8 010D
+E9 00E9
+EA 0119
+EB 00EB
+EC 0117
+ED 00ED
+EE 00EE
+EF 012B
+F0 0111
+F1 0146
+F2 014D
+F3 0137
+F4 00F4
+F5 00F5
+F6 00F6
+F7 00F7
+F8 00F8
+F9 0173
+FA 00FA
+FB 00FB
+FC 00FC
+FD 0169
+FE 016B
+FF 02D9
diff --git a/data/iso-8859-5 b/data/iso-8859-5
new file mode 100644
index 000000000..59ee84d57
--- /dev/null
+++ b/data/iso-8859-5
@@ -0,0 +1,251 @@
+charset 8bit
+
+#
+# This file defines the font and character mappings used for ISO-8859-5
+# (Cyrillic) text printing.
+#
+# The first line consists of:
+#
+# direction width normal bold italic bold-italic
+#
+# Direction is the string "ltor" or "rtol", indicating left-to-right or
+# right-to-left text.
+#
+# Width is the string "single" or "double"; double means that the glyphs
+# are twice as wide as ASCII characters in the Courier typeface.
+#
+# "Normal", "bold", "italic", and "bold-italic" are the typefaces to use
+# for each presentation. If characters are only available in a single
+# style then only one typeface should be listed (e.g. "Symbol")
+#
+# Each font that is listed will be used (and downloaded if needed) when
+# printing.
+#
+
+00 ff ltor single Courier Courier-Bold Courier-Italic Courier-BoldItalic
+
+#
+# The following lines define the mapping from the 8-bit character set to
+# the Unicode glyphs for each character:
+#
+# char glyph
+#
+# "Char" and "glyph" are hexadecimal values.
+#
+
+20 0020
+21 0021
+22 0022
+23 0023
+24 0024
+25 0025
+26 0026
+27 0027
+28 0028
+29 0029
+2A 002A
+2B 002B
+2C 002C
+2D 002D
+2E 002E
+2F 002F
+30 0030
+31 0031
+32 0032
+33 0033
+34 0034
+35 0035
+36 0036
+37 0037
+38 0038
+39 0039
+3A 003A
+3B 003B
+3C 003C
+3D 003D
+3E 003E
+3F 003F
+40 0040
+41 0041
+42 0042
+43 0043
+44 0044
+45 0045
+46 0046
+47 0047
+48 0048
+49 0049
+4A 004A
+4B 004B
+4C 004C
+4D 004D
+4E 004E
+4F 004F
+50 0050
+51 0051
+52 0052
+53 0053
+54 0054
+55 0055
+56 0056
+57 0057
+58 0058
+59 0059
+5A 005A
+5B 005B
+5C 005C
+5D 005D
+5E 005E
+5F 005F
+60 0060
+61 0061
+62 0062
+63 0063
+64 0064
+65 0065
+66 0066
+67 0067
+68 0068
+69 0069
+6A 006A
+6B 006B
+6C 006C
+6D 006D
+6E 006E
+6F 006F
+70 0070
+71 0071
+72 0072
+73 0073
+74 0074
+75 0075
+76 0076
+77 0077
+78 0078
+79 0079
+7A 007A
+7B 007B
+7C 007C
+7D 007D
+7E 007E
+80 20AC
+82 201A
+83 0192
+84 201E
+85 2026
+86 2020
+87 2021
+88 02C6
+89 2030
+8A 0160
+8B 2039
+8C 0152
+91 2018
+92 2019
+93 201C
+94 201D
+95 2022
+96 2013
+97 2014
+98 02DC
+99 2122
+9A 0161
+9B 203A
+9C 0153
+9F 0178
+A0 00A0
+A1 0401
+A2 0402
+A3 0403
+A4 0404
+A5 0405
+A6 0406
+A7 0407
+A8 0408
+A9 0409
+AA 040A
+AB 040B
+AC 040C
+AD 00AD
+AE 040E
+AF 040F
+B0 0410
+B1 0411
+B2 0412
+B3 0413
+B4 0414
+B5 0415
+B6 0416
+B7 0417
+B8 0418
+B9 0419
+BA 041A
+BB 041B
+BC 041C
+BD 041D
+BE 041E
+BF 041F
+C0 0420
+C1 0421
+C2 0422
+C3 0423
+C4 0424
+C5 0425
+C6 0426
+C7 0427
+C8 0428
+C9 0429
+CA 042A
+CB 042B
+CC 042C
+CD 042D
+CE 042E
+CF 042F
+D0 0430
+D1 0431
+D2 0432
+D3 0433
+D4 0434
+D5 0435
+D6 0436
+D7 0437
+D8 0438
+D9 0439
+DA 043A
+DB 043B
+DC 043C
+DD 043D
+DE 043E
+DF 043F
+E0 0440
+E1 0441
+E2 0442
+E3 0443
+E4 0444
+E5 0445
+E6 0446
+E7 0447
+E8 0448
+E9 0449
+EA 044A
+EB 044B
+EC 044C
+ED 044D
+EE 044E
+EF 044F
+F0 2116
+F1 0451
+F2 0452
+F3 0453
+F4 0454
+F5 0455
+F6 0456
+F7 0457
+F8 0458
+F9 0459
+FA 045A
+FB 045B
+FC 045C
+FD 00A7
+FE 045E
+FF 045F
diff --git a/data/iso-8859-6 b/data/iso-8859-6
new file mode 100644
index 000000000..356fe72e3
--- /dev/null
+++ b/data/iso-8859-6
@@ -0,0 +1,206 @@
+charset 8bit
+
+#
+# This file defines the font and character mappings used for ISO-8859-6
+# (Arabic) text printing.
+#
+# The first line consists of:
+#
+# direction width normal bold italic bold-italic
+#
+# Direction is the string "ltor" or "rtol", indicating left-to-right or
+# right-to-left text.
+#
+# Width is the string "single" or "double"; double means that the glyphs
+# are twice as wide as ASCII characters in the Courier typeface.
+#
+# "Normal", "bold", "italic", and "bold-italic" are the typefaces to use
+# for each presentation. If characters are only available in a single
+# style then only one typeface should be listed (e.g. "Symbol")
+#
+# Each font that is listed will be used (and downloaded if needed) when
+# printing.
+#
+
+00 ff rtol single Courier Courier-Bold Courier-Italic Courier-BoldItalic
+
+#
+# The following lines define the mapping from the 8-bit character set to
+# the Unicode glyphs for each character:
+#
+# char glyph
+#
+# "Char" and "glyph" are hexadecimal values.
+#
+
+20 0020
+21 0021
+22 0022
+23 0023
+24 0024
+25 0025
+26 0026
+27 0027
+28 0028
+29 0029
+2A 002A
+2B 002B
+2C 002C
+2D 002D
+2E 002E
+2F 002F
+30 0660
+31 0661
+32 0662
+33 0663
+34 0664
+35 0665
+36 0666
+37 0667
+38 0668
+39 0669
+3A 003A
+3B 003B
+3C 003C
+3D 003D
+3E 003E
+3F 003F
+40 0040
+41 0041
+42 0042
+43 0043
+44 0044
+45 0045
+46 0046
+47 0047
+48 0048
+49 0049
+4A 004A
+4B 004B
+4C 004C
+4D 004D
+4E 004E
+4F 004F
+50 0050
+51 0051
+52 0052
+53 0053
+54 0054
+55 0055
+56 0056
+57 0057
+58 0058
+59 0059
+5A 005A
+5B 005B
+5C 005C
+5D 005D
+5E 005E
+5F 005F
+60 0060
+61 0061
+62 0062
+63 0063
+64 0064
+65 0065
+66 0066
+67 0067
+68 0068
+69 0069
+6A 006A
+6B 006B
+6C 006C
+6D 006D
+6E 006E
+6F 006F
+70 0070
+71 0071
+72 0072
+73 0073
+74 0074
+75 0075
+76 0076
+77 0077
+78 0078
+79 0079
+7A 007A
+7B 007B
+7C 007C
+7D 007D
+7E 007E
+80 20AC
+82 201A
+83 0192
+84 201E
+85 2026
+86 2020
+87 2021
+88 02C6
+89 2030
+8A 0160
+8B 2039
+8C 0152
+91 2018
+92 2019
+93 201C
+94 201D
+95 2022
+96 2013
+97 2014
+98 02DC
+99 2122
+9A 0161
+9B 203A
+9C 0153
+9F 0178
+A0 00A0
+A4 00A4
+AC 060C
+AD 00AD
+BB 061B
+BF 061F
+C1 0621
+C2 0622
+C3 0623
+C4 0624
+C5 0625
+C6 0626
+C7 0627
+C8 0628
+C9 0629
+CA 062A
+CB 062B
+CC 062C
+CD 062D
+CE 062E
+CF 062F
+D0 0630
+D1 0631
+D2 0632
+D3 0633
+D4 0634
+D5 0635
+D6 0636
+D7 0637
+D8 0638
+D9 0639
+DA 063A
+E0 0640
+E1 0641
+E2 0642
+E3 0643
+E4 0644
+E5 0645
+E6 0646
+E7 0647
+E8 0648
+E9 0649
+EA 064A
+EB 064B
+EC 064C
+ED 064D
+EE 064E
+EF 064F
+F0 0650
+F1 0651
+F2 0652
diff --git a/data/iso-8859-7 b/data/iso-8859-7
new file mode 100644
index 000000000..57647841c
--- /dev/null
+++ b/data/iso-8859-7
@@ -0,0 +1,246 @@
+charset 8bit
+
+#
+# This file defines the font and character mappings used for ISO-8859-7
+# (Greek) text printing.
+#
+# The first line consists of:
+#
+# direction width normal bold italic bold-italic
+#
+# Direction is the string "ltor" or "rtol", indicating left-to-right or
+# right-to-left text.
+#
+# Width is the string "single" or "double"; double means that the glyphs
+# are twice as wide as ASCII characters in the Courier typeface.
+#
+# "Normal", "bold", "italic", and "bold-italic" are the typefaces to use
+# for each presentation. If characters are only available in a single
+# style then only one typeface should be listed (e.g. "Symbol")
+#
+# Each font that is listed will be used (and downloaded if needed) when
+# printing.
+#
+
+00 9f ltor single Courier Courier-Bold Courier-Italic Courier-BoldItalic
+a0 ff ltor single Symbol
+
+#
+# The following lines define the mapping from the 8-bit character set to
+# the Unicode glyphs for each character:
+#
+# char glyph
+#
+# "Char" and "glyph" are hexadecimal values.
+#
+
+20 0020
+21 0021
+22 0022
+23 0023
+24 0024
+25 0025
+26 0026
+27 0027
+28 0028
+29 0029
+2A 002A
+2B 002B
+2C 002C
+2D 002D
+2E 002E
+2F 002F
+30 0030
+31 0031
+32 0032
+33 0033
+34 0034
+35 0035
+36 0036
+37 0037
+38 0038
+39 0039
+3A 003A
+3B 003B
+3C 003C
+3D 003D
+3E 003E
+3F 003F
+40 0040
+41 0041
+42 0042
+43 0043
+44 0044
+45 0045
+46 0046
+47 0047
+48 0048
+49 0049
+4A 004A
+4B 004B
+4C 004C
+4D 004D
+4E 004E
+4F 004F
+50 0050
+51 0051
+52 0052
+53 0053
+54 0054
+55 0055
+56 0056
+57 0057
+58 0058
+59 0059
+5A 005A
+5B 005B
+5C 005C
+5D 005D
+5E 005E
+5F 005F
+60 0060
+61 0061
+62 0062
+63 0063
+64 0064
+65 0065
+66 0066
+67 0067
+68 0068
+69 0069
+6A 006A
+6B 006B
+6C 006C
+6D 006D
+6E 006E
+6F 006F
+70 0070
+71 0071
+72 0072
+73 0073
+74 0074
+75 0075
+76 0076
+77 0077
+78 0078
+79 0079
+7A 007A
+7B 007B
+7C 007C
+7D 007D
+7E 007E
+80 20AC
+82 201A
+83 0192
+84 201E
+85 2026
+86 2020
+87 2021
+88 02C6
+89 2030
+8A 0160
+8B 2039
+8C 0152
+91 2018
+92 2019
+93 201C
+94 201D
+95 2022
+96 2013
+97 2014
+98 02DC
+99 2122
+9A 0161
+9B 203A
+9C 0153
+9F 0178
+A0 00A0
+A1 02BD
+A2 02BC
+A3 00A3
+A6 00A6
+A7 00A7
+A8 00A8
+A9 00A9
+AB 00AB
+AC 00AC
+AD 00AD
+AF 2015
+B0 00B0
+B1 00B1
+B2 00B2
+B3 00B3
+B4 0384
+B5 0385
+B6 0386
+B7 00B7
+B8 0388
+B9 0389
+BA 038A
+BB 00BB
+BC 038C
+BD 00BD
+BE 038E
+BF 038F
+C0 0390
+C1 0391
+C2 0392
+C3 0393
+C4 0394
+C5 0395
+C6 0396
+C7 0397
+C8 0398
+C9 0399
+CA 039A
+CB 039B
+CC 039C
+CD 039D
+CE 039E
+CF 039F
+D0 03A0
+D1 03A1
+D3 03A3
+D4 03A4
+D5 03A5
+D6 03A6
+D7 03A7
+D8 03A8
+D9 03A9
+DA 03AA
+DB 03AB
+DC 03AC
+DD 03AD
+DE 03AE
+DF 03AF
+E0 03B0
+E1 03B1
+E2 03B2
+E3 03B3
+E4 03B4
+E5 03B5
+E6 03B6
+E7 03B7
+E8 03B8
+E9 03B9
+EA 03BA
+EB 03BB
+EC 03BC
+ED 03BD
+EE 03BE
+EF 03BF
+F0 03C0
+F1 03C1
+F2 03C2
+F3 03C3
+F4 03C4
+F5 03C5
+F6 03C6
+F7 03C7
+F8 03C8
+F9 03C9
+FA 03CA
+FB 03CB
+FC 03CC
+FD 03CD
+FE 03CE
diff --git a/data/iso-8859-8 b/data/iso-8859-8
new file mode 100644
index 000000000..a64c598f1
--- /dev/null
+++ b/data/iso-8859-8
@@ -0,0 +1,214 @@
+charset 8bit
+
+#
+# This file defines the font and character mappings used for ISO-8859-8
+# (Hebrew) text printing.
+#
+# The first line consists of:
+#
+# direction width normal bold italic bold-italic
+#
+# Direction is the string "ltor" or "rtol", indicating left-to-right or
+# right-to-left text.
+#
+# Width is the string "single" or "double"; double means that the glyphs
+# are twice as wide as ASCII characters in the Courier typeface.
+#
+# "Normal", "bold", "italic", and "bold-italic" are the typefaces to use
+# for each presentation. If characters are only available in a single
+# style then only one typeface should be listed (e.g. "Symbol")
+#
+# Each font that is listed will be used (and downloaded if needed) when
+# printing.
+#
+
+00 7f ltor single Courier Courier-Bold Courier-Italic Courier-BoldItalic
+80 ff rtol single Courier-Hebrew
+
+#
+# The following lines define the mapping from the 8-bit character set to
+# the Unicode glyphs for each character:
+#
+# char glyph
+#
+# "Char" and "glyph" are hexadecimal values.
+#
+
+20 0020
+21 0021
+22 0022
+23 0023
+24 0024
+25 0025
+26 0026
+27 0027
+28 0028
+29 0029
+2A 002A
+2B 002B
+2C 002C
+2D 002D
+2E 002E
+2F 002F
+30 0030
+31 0031
+32 0032
+33 0033
+34 0034
+35 0035
+36 0036
+37 0037
+38 0038
+39 0039
+3A 003A
+3B 003B
+3C 003C
+3D 003D
+3E 003E
+3F 003F
+40 0040
+41 0041
+42 0042
+43 0043
+44 0044
+45 0045
+46 0046
+47 0047
+48 0048
+49 0049
+4A 004A
+4B 004B
+4C 004C
+4D 004D
+4E 004E
+4F 004F
+50 0050
+51 0051
+52 0052
+53 0053
+54 0054
+55 0055
+56 0056
+57 0057
+58 0058
+59 0059
+5A 005A
+5B 005B
+5C 005C
+5D 005D
+5E 005E
+5F 005F
+60 0060
+61 0061
+62 0062
+63 0063
+64 0064
+65 0065
+66 0066
+67 0067
+68 0068
+69 0069
+6A 006A
+6B 006B
+6C 006C
+6D 006D
+6E 006E
+6F 006F
+70 0070
+71 0071
+72 0072
+73 0073
+74 0074
+75 0075
+76 0076
+77 0077
+78 0078
+79 0079
+7A 007A
+7B 007B
+7C 007C
+7D 007D
+7E 007E
+80 20AC
+82 201A
+83 0192
+84 201E
+85 2026
+86 2020
+87 2021
+88 02C6
+89 2030
+8A 0160
+8B 2039
+8C 0152
+91 2018
+92 2019
+93 201C
+94 201D
+95 2022
+96 2013
+97 2014
+98 02DC
+99 2122
+9A 0161
+9B 203A
+9C 0153
+9F 0178
+A0 00A0
+A2 00A2
+A3 00A3
+A4 00A4
+A5 00A5
+A6 00A6
+A7 00A7
+A8 00A8
+A9 00A9
+AA 00D7
+AB 00AB
+AC 00AC
+AD 00AD
+AE 00AE
+AF 203E
+B0 00B0
+B1 00B1
+B2 00B2
+B3 00B3
+B4 00B4
+B5 00B5
+B6 00B6
+B7 00B7
+B8 00B8
+B9 00B9
+BA 00F7
+BB 00BB
+BC 00BC
+BD 00BD
+BE 00BE
+DF 2017
+E0 05D0
+E1 05D1
+E2 05D2
+E3 05D3
+E4 05D4
+E5 05D5
+E6 05D6
+E7 05D7
+E8 05D8
+E9 05D9
+EA 05DA
+EB 05DB
+EC 05DC
+ED 05DD
+EE 05DE
+EF 05DF
+F0 05E0
+F1 05E1
+F2 05E2
+F3 05E3
+F4 05E4
+F5 05E5
+F6 05E6
+F7 05E7
+F8 05E8
+F9 05E9
+FA 05EA
diff --git a/data/iso-8859-9 b/data/iso-8859-9
new file mode 100644
index 000000000..83a661edc
--- /dev/null
+++ b/data/iso-8859-9
@@ -0,0 +1,251 @@
+charset 8bit
+
+#
+# This file defines the font and character mappings used for ISO-8859-9
+# (Latin5/Turkish) text printing.
+#
+# The first line consists of:
+#
+# direction width normal bold italic bold-italic
+#
+# Direction is the string "ltor" or "rtol", indicating left-to-right or
+# right-to-left text.
+#
+# Width is the string "single" or "double"; double means that the glyphs
+# are twice as wide as ASCII characters in the Courier typeface.
+#
+# "Normal", "bold", "italic", and "bold-italic" are the typefaces to use
+# for each presentation. If characters are only available in a single
+# style then only one typeface should be listed (e.g. "Symbol")
+#
+# Each font that is listed will be used (and downloaded if needed) when
+# printing.
+#
+
+00 ff ltor single Courier Courier-Bold Courier-Italic Courier-BoldItalic
+
+#
+# The following lines define the mapping from the 8-bit character set to
+# the Unicode glyphs for each character:
+#
+# char glyph
+#
+# "Char" and "glyph" are hexadecimal values.
+#
+
+20 0020
+21 0021
+22 0022
+23 0023
+24 0024
+25 0025
+26 0026
+27 0027
+28 0028
+29 0029
+2A 002A
+2B 002B
+2C 002C
+2D 002D
+2E 002E
+2F 002F
+30 0030
+31 0031
+32 0032
+33 0033
+34 0034
+35 0035
+36 0036
+37 0037
+38 0038
+39 0039
+3A 003A
+3B 003B
+3C 003C
+3D 003D
+3E 003E
+3F 003F
+40 0040
+41 0041
+42 0042
+43 0043
+44 0044
+45 0045
+46 0046
+47 0047
+48 0048
+49 0049
+4A 004A
+4B 004B
+4C 004C
+4D 004D
+4E 004E
+4F 004F
+50 0050
+51 0051
+52 0052
+53 0053
+54 0054
+55 0055
+56 0056
+57 0057
+58 0058
+59 0059
+5A 005A
+5B 005B
+5C 005C
+5D 005D
+5E 005E
+5F 005F
+60 0060
+61 0061
+62 0062
+63 0063
+64 0064
+65 0065
+66 0066
+67 0067
+68 0068
+69 0069
+6A 006A
+6B 006B
+6C 006C
+6D 006D
+6E 006E
+6F 006F
+70 0070
+71 0071
+72 0072
+73 0073
+74 0074
+75 0075
+76 0076
+77 0077
+78 0078
+79 0079
+7A 007A
+7B 007B
+7C 007C
+7D 007D
+7E 007E
+80 20AC
+82 201A
+83 0192
+84 201E
+85 2026
+86 2020
+87 2021
+88 02C6
+89 2030
+8A 0160
+8B 2039
+8C 0152
+91 2018
+92 2019
+93 201C
+94 201D
+95 2022
+96 2013
+97 2014
+98 02DC
+99 2122
+9A 0161
+9B 203A
+9C 0153
+9F 0178
+A0 00A0
+A1 00A1
+A2 00A2
+A3 00A3
+A4 00A4
+A5 00A5
+A6 00A6
+A7 00A7
+A8 00A8
+A9 00A9
+AA 00AA
+AB 00AB
+AC 00AC
+AD 00AD
+AE 00AE
+AF 00AF
+B0 00B0
+B1 00B1
+B2 00B2
+B3 00B3
+B4 00B4
+B5 00B5
+B6 00B6
+B7 00B7
+B8 00B8
+B9 00B9
+BA 00BA
+BB 00BB
+BC 00BC
+BD 00BD
+BE 00BE
+BF 00BF
+C0 00C0
+C1 00C1
+C2 00C2
+C3 00C3
+C4 00C4
+C5 00C5
+C6 00C6
+C7 00C7
+C8 00C8
+C9 00C9
+CA 00CA
+CB 00CB
+CC 00CC
+CD 00CD
+CE 00CE
+CF 00CF
+D0 011E
+D1 00D1
+D2 00D2
+D3 00D3
+D4 00D4
+D5 00D5
+D6 00D6
+D7 00D7
+D8 00D8
+D9 00D9
+DA 00DA
+DB 00DB
+DC 00DC
+DD 0130
+DE 015E
+DF 00DF
+E0 00E0
+E1 00E1
+E2 00E2
+E3 00E3
+E4 00E4
+E5 00E5
+E6 00E6
+E7 00E7
+E8 00E8
+E9 00E9
+EA 00EA
+EB 00EB
+EC 00EC
+ED 00ED
+EE 00EE
+EF 00EF
+F0 011F
+F1 00F1
+F2 00F2
+F3 00F3
+F4 00F4
+F5 00F5
+F6 00F6
+F7 00F7
+F8 00F8
+F9 00F9
+FA 00FA
+FB 00FB
+FC 00FC
+FD 0131
+FE 015F
+FF 00FF
diff --git a/data/psglyphs b/data/psglyphs
new file mode 100644
index 000000000..c4a902c66
--- /dev/null
+++ b/data/psglyphs
@@ -0,0 +1,1051 @@
+0020 space
+0021 exclam
+0022 quotedbl
+0023 numbersign
+0024 dollar
+0025 percent
+0026 ampersand
+0027 quotesingle
+0028 parenleft
+0029 parenright
+002a asterisk
+002b plus
+002c comma
+002d minus
+002e period
+002f slash
+0030 zero
+0031 one
+0032 two
+0033 three
+0034 four
+0035 five
+0036 six
+0037 seven
+0038 eight
+0039 nine
+003a colon
+003b semicolon
+003c less
+003d equal
+003e greater
+003f question
+0040 at
+0041 A
+0042 B
+0043 C
+0044 D
+0045 E
+0046 F
+0047 G
+0048 H
+0049 I
+004a J
+004b K
+004c L
+004d M
+004e N
+004f O
+0050 P
+0051 Q
+0052 R
+0053 S
+0054 T
+0055 U
+0056 V
+0057 W
+0058 X
+0059 Y
+005a Z
+005b bracketleft
+005c backslash
+005d bracketright
+005e asciicircum
+005f underscore
+0060 grave
+0061 a
+0062 b
+0063 c
+0064 d
+0065 e
+0066 f
+0067 g
+0068 h
+0069 i
+006a j
+006b k
+006c l
+006d m
+006e n
+006f o
+0070 p
+0071 q
+0072 r
+0073 s
+0074 t
+0075 u
+0076 v
+0077 w
+0078 x
+0079 y
+007a z
+007b braceleft
+007c bar
+007d braceright
+007e asciitilde
+00a0 space
+00a1 exclamdown
+00a2 cent
+00a3 sterling
+00a4 currency
+00a5 yen
+00a6 brokenbar
+00a7 section
+00a8 dieresis
+00a9 copyright
+00aa ordfeminine
+00ab guillemotleft
+00ac logicalnot
+00ad hyphen
+00ae registered
+00af macron
+00b0 degree
+00b1 plusminus
+00b2 twosuperior
+00b3 threesuperior
+00b4 acute
+00b5 mu
+00b6 paragraph
+00b7 periodcentered
+00b8 cedilla
+00b9 onesuperior
+00ba ordmasculine
+00bb guillemotright
+00bc onequarter
+00bd onehalf
+00be threequarters
+00bf questiondown
+00c0 Agrave
+00c1 Aacute
+00c2 Acircumflex
+00c3 Atilde
+00c4 Adieresis
+00c5 Aring
+00c6 AE
+00c7 Ccedilla
+00c8 Egrave
+00c9 Eacute
+00ca Ecircumflex
+00cb Edieresis
+00cc Igrave
+00cd Iacute
+00ce Icircumflex
+00cf Idieresis
+00d0 Eth
+00d1 Ntilde
+00d2 Ograve
+00d3 Oacute
+00d4 Ocircumflex
+00d5 Otilde
+00d6 Odieresis
+00d7 multiply
+00d8 Oslash
+00d9 Ugrave
+00da Uacute
+00db Ucircumflex
+00dc Udieresis
+00dd Yacute
+00de Thorn
+00df germandbls
+00e0 agrave
+00e1 aacute
+00e2 acircumflex
+00e3 atilde
+00e4 adieresis
+00e5 aring
+00e6 ae
+00e7 ccedilla
+00e8 egrave
+00e9 eacute
+00ea ecircumflex
+00eb edieresis
+00ec igrave
+00ed iacute
+00ee icircumflex
+00ef idieresis
+00f0 eth
+00f1 ntilde
+00f2 ograve
+00f3 oacute
+00f4 ocircumflex
+00f5 otilde
+00f6 odieresis
+00f7 divide
+00f8 oslash
+00f9 ugrave
+00fa uacute
+00fb ucircumflex
+00fc udieresis
+00fd yacute
+00fe thorn
+00ff ydieresis
+0100 Amacron
+0101 amacron
+0102 Abreve
+0103 abreve
+0104 Aogonek
+0105 aogonek
+0106 Cacute
+0107 cacute
+0108 Ccircumflex
+0109 ccircumflex
+010a Cdotaccent
+010b cdotaccent
+010c Ccaron
+010d ccaron
+010e Dcaron
+010f dcaron
+0110 Dcroat
+0111 dcroat
+0112 Emacron
+0113 emacron
+0114 Ebreve
+0115 ebreve
+0116 Edotaccent
+0117 edotaccent
+0118 Eogonek
+0119 eogonek
+011a Ecaron
+011b ecaron
+011c Gcircumflex
+011d gcircumflex
+011e Gbreve
+011f gbreve
+0120 Gdotaccent
+0121 gdotaccent
+0122 Gcommaaccent
+0123 gcommaaccent
+0124 Hcircumflex
+0125 hcircumflex
+0126 Hbar
+0127 hbar
+0128 Itilde
+0129 itilde
+012a Imacron
+012b imacron
+012c Ibreve
+012d ibreve
+012e Iogonek
+012f iogonek
+0130 Idotaccent
+0131 dotlessi
+0132 IJ
+0133 ij
+0134 Jcircumflex
+0135 jcircumflex
+0136 Kcommaaccent
+0137 kcommaaccent
+0138 kgreenlandic
+0139 Lacute
+013a lacute
+013b Lcommaaccent
+013c lcommaaccent
+013d Lcaron
+013e lcaron
+013f Ldot
+0140 ldot
+0141 Lslash
+0142 lslash
+0143 Nacute
+0144 nacute
+0145 Ncommaaccent
+0146 ncommaaccent
+0147 Ncaron
+0148 ncaron
+0149 napostrophe
+014a Eng
+014b eng
+014c Omacron
+014d omacron
+014e Obreve
+014f obreve
+0150 Ohungarumlaut
+0151 ohungarumlaut
+0152 OE
+0153 oe
+0154 Racute
+0155 racute
+0156 Rcommaaccent
+0157 rcommaaccent
+0158 Rcaron
+0159 rcaron
+015a Sacute
+015b sacute
+015c Scircumflex
+015d scircumflex
+015e Scedilla
+015f scedilla
+0160 Scaron
+0161 scaron
+0162 Tcommaaccent
+0163 tcommaaccent
+0164 Tcaron
+0165 tcaron
+0166 Tbar
+0167 tbar
+0168 Utilde
+0169 utilde
+016a Umacron
+016b umacron
+016c Ubreve
+016d ubreve
+016e Uring
+016f uring
+0170 Uhungarumlaut
+0171 uhungarumlaut
+0172 Uogonek
+0173 uogonek
+0174 Wcircumflex
+0175 wcircumflex
+0176 Ycircumflex
+0177 ycircumflex
+0178 Ydieresis
+0179 Zacute
+017a zacute
+017b Zdotaccent
+017c zdotaccent
+017d Zcaron
+017e zcaron
+017f longs
+0192 florin
+01a0 Ohorn
+01a1 ohorn
+01af Uhorn
+01b0 uhorn
+01e6 Gcaron
+01e7 gcaron
+01fa Aringacute
+01fb aringacute
+01fc AEacute
+01fd aeacute
+01fe Oslashacute
+01ff oslashacute
+0218 Scommaaccent
+0219 scommaaccent
+021a Tcommaaccent
+021b tcommaaccent
+02bc afii57929
+02bd afii64937
+02c6 circumflex
+02c7 caron
+02c9 macron
+02d8 breve
+02d9 dotaccent
+02da ring
+02db ogonek
+02dc tilde
+02dd hungarumlaut
+0300 gravecomb
+0301 acutecomb
+0303 tildecomb
+0309 hookabovecomb
+0323 dotbelowcomb
+0384 tonos
+0385 dieresistonos
+0386 Alphatonos
+0387 anoteleia
+0388 Epsilontonos
+0389 Etatonos
+038a Iotatonos
+038c Omicrontonos
+038e Upsilontonos
+038f Omegatonos
+0390 iotadieresistonos
+0391 Alpha
+0392 Beta
+0393 Gamma
+0394 Delta
+0395 Epsilon
+0396 Zeta
+0397 Eta
+0398 Theta
+0399 Iota
+039a Kappa
+039b Lambda
+039c Mu
+039d Nu
+039e Xi
+039f Omicron
+03a0 Pi
+03a1 Rho
+03a3 Sigma
+03a4 Tau
+03a5 Upsilon
+03a6 Phi
+03a7 Chi
+03a8 Psi
+03a9 Omega
+03aa Iotadieresis
+03ab Upsilondieresis
+03ac alphatonos
+03ad epsilontonos
+03ae etatonos
+03af iotatonos
+03b0 upsilondieresistonos
+03b1 alpha
+03b2 beta
+03b3 gamma
+03b4 delta
+03b5 epsilon
+03b6 zeta
+03b7 eta
+03b8 theta
+03b9 iota
+03ba kappa
+03bb lambda
+03bc mu
+03bd nu
+03be xi
+03bf omicron
+03c0 pi
+03c1 rho
+03c2 sigma1
+03c3 sigma
+03c4 tau
+03c5 upsilon
+03c6 phi
+03c7 chi
+03c8 psi
+03c9 omega
+03ca iotadieresis
+03cb upsilondieresis
+03cc omicrontonos
+03cd upsilontonos
+03ce omegatonos
+03d1 theta1
+03d2 Upsilon1
+03d5 phi1
+03d6 omega1
+0401 afii10023
+0402 afii10051
+0403 afii10052
+0404 afii10053
+0405 afii10054
+0406 afii10055
+0407 afii10056
+0408 afii10057
+0409 afii10058
+040a afii10059
+040b afii10060
+040c afii10061
+040e afii10062
+040f afii10145
+0410 afii10017
+0411 afii10018
+0412 afii10019
+0413 afii10020
+0414 afii10021
+0415 afii10022
+0416 afii10024
+0417 afii10025
+0418 afii10026
+0419 afii10027
+041a afii10028
+041b afii10029
+041c afii10030
+041d afii10031
+041e afii10032
+041f afii10033
+0420 afii10034
+0421 afii10035
+0422 afii10036
+0423 afii10037
+0424 afii10038
+0425 afii10039
+0426 afii10040
+0427 afii10041
+0428 afii10042
+0429 afii10043
+042a afii10044
+042b afii10045
+042c afii10046
+042d afii10047
+042e afii10048
+042f afii10049
+0430 afii10065
+0431 afii10066
+0432 afii10067
+0433 afii10068
+0434 afii10069
+0435 afii10070
+0436 afii10072
+0437 afii10073
+0438 afii10074
+0439 afii10075
+043a afii10076
+043b afii10077
+043c afii10078
+043d afii10079
+043e afii10080
+043f afii10081
+0440 afii10082
+0441 afii10083
+0442 afii10084
+0443 afii10085
+0444 afii10086
+0445 afii10087
+0446 afii10088
+0447 afii10089
+0448 afii10090
+0449 afii10091
+044a afii10092
+044b afii10093
+044c afii10094
+044d afii10095
+044e afii10096
+044f afii10097
+0451 afii10071
+0452 afii10099
+0453 afii10100
+0454 afii10101
+0455 afii10102
+0456 afii10103
+0457 afii10104
+0458 afii10105
+0459 afii10106
+045a afii10107
+045b afii10108
+045c afii10109
+045e afii10110
+045f afii10193
+0462 afii10146
+0463 afii10194
+0472 afii10147
+0473 afii10195
+0474 afii10148
+0475 afii10196
+0490 afii10050
+0491 afii10098
+04d9 afii10846
+05b0 afii57799
+05b1 afii57801
+05b2 afii57800
+05b3 afii57802
+05b4 afii57793
+05b5 afii57794
+05b6 afii57795
+05b7 afii57798
+05b8 afii57797
+05b9 afii57806
+05bb afii57796
+05bc afii57807
+05bd afii57839
+05be afii57645
+05bf afii57841
+05c0 afii57842
+05c1 afii57804
+05c2 afii57803
+05c3 afii57658
+05d0 afii57664
+05d1 afii57665
+05d2 afii57666
+05d3 afii57667
+05d4 afii57668
+05d5 afii57669
+05d6 afii57670
+05d7 afii57671
+05d8 afii57672
+05d9 afii57673
+05da afii57674
+05db afii57675
+05dc afii57676
+05dd afii57677
+05de afii57678
+05df afii57679
+05e0 afii57680
+05e1 afii57681
+05e2 afii57682
+05e3 afii57683
+05e4 afii57684
+05e5 afii57685
+05e6 afii57686
+05e7 afii57687
+05e8 afii57688
+05e9 afii57689
+05ea afii57690
+05f0 afii57716
+05f1 afii57717
+05f2 afii57718
+060c afii57388
+061b afii57403
+061f afii57407
+0621 afii57409
+0622 afii57410
+0623 afii57411
+0624 afii57412
+0625 afii57413
+0626 afii57414
+0627 afii57415
+0628 afii57416
+0629 afii57417
+062a afii57418
+062b afii57419
+062c afii57420
+062d afii57421
+062e afii57422
+062f afii57423
+0630 afii57424
+0631 afii57425
+0632 afii57426
+0633 afii57427
+0634 afii57428
+0635 afii57429
+0636 afii57430
+0637 afii57431
+0638 afii57432
+0639 afii57433
+063a afii57434
+0640 afii57440
+0641 afii57441
+0642 afii57442
+0643 afii57443
+0644 afii57444
+0645 afii57445
+0646 afii57446
+0647 afii57470
+0648 afii57448
+0649 afii57449
+064a afii57450
+064b afii57451
+064c afii57452
+064d afii57453
+064e afii57454
+064f afii57455
+0650 afii57456
+0651 afii57457
+0652 afii57458
+0660 afii57392
+0661 afii57393
+0662 afii57394
+0663 afii57395
+0664 afii57396
+0665 afii57397
+0666 afii57398
+0667 afii57399
+0668 afii57400
+0669 afii57401
+066a afii57381
+066d afii63167
+0679 afii57511
+067e afii57506
+0686 afii57507
+0688 afii57512
+0691 afii57513
+0698 afii57508
+06a4 afii57505
+06af afii57509
+06ba afii57514
+06d2 afii57519
+06d5 afii57534
+1e80 Wgrave
+1e81 wgrave
+1e82 Wacute
+1e83 wacute
+1e84 Wdieresis
+1e85 wdieresis
+1ef2 Ygrave
+1ef3 ygrave
+200c afii61664
+200d afii301
+200e afii299
+200f afii300
+2012 figuredash
+2013 endash
+2014 emdash
+2015 afii00208
+2017 underscoredbl
+2018 quoteleft
+2019 quoteright
+201a quotesinglbase
+201b quotereversed
+201c quotedblleft
+201d quotedblright
+201e quotedblbase
+2020 dagger
+2021 daggerdbl
+2022 bullet
+2024 onedotenleader
+2025 twodotenleader
+2026 ellipsis
+202c afii61573
+202d afii61574
+202e afii61575
+2030 perthousand
+2032 minute
+2033 second
+2039 guilsinglleft
+203a guilsinglright
+203c exclamdbl
+2044 fraction
+2070 zerosuperior
+2074 foursuperior
+2075 fivesuperior
+2076 sixsuperior
+2077 sevensuperior
+2078 eightsuperior
+2079 ninesuperior
+207d parenleftsuperior
+207e parenrightsuperior
+207f nsuperior
+2080 zeroinferior
+2081 oneinferior
+2082 twoinferior
+2083 threeinferior
+2084 fourinferior
+2085 fiveinferior
+2086 sixinferior
+2087 seveninferior
+2088 eightinferior
+2089 nineinferior
+208d parenleftinferior
+208e parenrightinferior
+20a1 colonmonetary
+20a3 franc
+20a4 lira
+20a7 peseta
+20aa afii57636
+20ab dong
+20ac Euro
+2105 afii61248
+2111 Ifraktur
+2113 afii61289
+2116 afii61352
+2118 weierstrass
+211c Rfraktur
+211e prescription
+2122 trademark
+2126 Omega
+212e estimated
+2135 aleph
+2153 onethird
+2154 twothirds
+215b oneeighth
+215c threeeighths
+215d fiveeighths
+215e seveneighths
+2190 arrowleft
+2191 arrowup
+2192 arrowright
+2193 arrowdown
+2194 arrowboth
+2195 arrowupdn
+21a8 arrowupdnbse
+21b5 carriagereturn
+21d0 arrowdblleft
+21d1 arrowdblup
+21d2 arrowdblright
+21d3 arrowdbldown
+21d4 arrowdblboth
+2200 universal
+2202 partialdiff
+2203 existential
+2205 emptyset
+2206 Delta
+2207 gradient
+2208 element
+2209 notelement
+220b suchthat
+220f product
+2211 summation
+2212 minus
+2215 fraction
+2217 asteriskmath
+2219 periodcentered
+221a radical
+221d proportional
+221e infinity
+221f orthogonal
+2220 angle
+2227 logicaland
+2228 logicalor
+2229 intersection
+222a union
+222b integral
+2234 therefore
+223c similar
+2245 congruent
+2248 approxequal
+2260 notequal
+2261 equivalence
+2264 lessequal
+2265 greaterequal
+2282 propersubset
+2283 propersuperset
+2284 notsubset
+2286 reflexsubset
+2287 reflexsuperset
+2295 circleplus
+2297 circlemultiply
+22a5 perpendicular
+22c5 dotmath
+2302 house
+2310 revlogicalnot
+2320 integraltp
+2321 integralbt
+2329 angleleft
+232a angleright
+2500 SF100000
+2502 SF110000
+250c SF010000
+2510 SF030000
+2514 SF020000
+2518 SF040000
+251c SF080000
+2524 SF090000
+252c SF060000
+2534 SF070000
+253c SF050000
+2550 SF430000
+2551 SF240000
+2552 SF510000
+2553 SF520000
+2554 SF390000
+2555 SF220000
+2556 SF210000
+2557 SF250000
+2558 SF500000
+2559 SF490000
+255a SF380000
+255b SF280000
+255c SF270000
+255d SF260000
+255e SF360000
+255f SF370000
+2560 SF420000
+2561 SF190000
+2562 SF200000
+2563 SF230000
+2564 SF470000
+2565 SF480000
+2566 SF410000
+2567 SF450000
+2568 SF460000
+2569 SF400000
+256a SF540000
+256b SF530000
+256c SF440000
+2580 upblock
+2584 dnblock
+2588 block
+258c lfblock
+2590 rtblock
+2591 ltshade
+2592 shade
+2593 dkshade
+25a0 filledbox
+25a1 H22073
+25aa H18543
+25ab H18551
+25ac filledrect
+25b2 triagup
+25ba triagrt
+25bc triagdn
+25c4 triaglf
+25ca lozenge
+25cb circle
+25cf H18533
+25d8 invbullet
+25d9 invcircle
+25e6 openbullet
+263a smileface
+263b invsmileface
+263c sun
+2640 female
+2642 male
+2660 spade
+2663 club
+2665 heart
+2666 diamond
+266a musicalnote
+266b musicalnotedbl
+f6be dotlessj
+f6bf LL
+f6c0 ll
+f6c1 Scedilla
+f6c2 scedilla
+f6c3 commaaccent
+f6c4 afii10063
+f6c5 afii10064
+f6c6 afii10192
+f6c7 afii10831
+f6c8 afii10832
+f6c9 Acute
+f6ca Caron
+f6cb Dieresis
+f6cc DieresisAcute
+f6cd DieresisGrave
+f6ce Grave
+f6cf Hungarumlaut
+f6d0 Macron
+f6d1 cyrBreve
+f6d2 cyrFlex
+f6d3 dblGrave
+f6d4 cyrbreve
+f6d5 cyrflex
+f6d6 dblgrave
+f6d7 dieresisacute
+f6d8 dieresisgrave
+f6d9 copyrightserif
+f6da registerserif
+f6db trademarkserif
+f6dc onefitted
+f6dd rupiah
+f6de threequartersemdash
+f6df centinferior
+f6e0 centsuperior
+f6e1 commainferior
+f6e2 commasuperior
+f6e3 dollarinferior
+f6e4 dollarsuperior
+f6e5 hypheninferior
+f6e6 hyphensuperior
+f6e7 periodinferior
+f6e8 periodsuperior
+f6e9 asuperior
+f6ea bsuperior
+f6eb dsuperior
+f6ec esuperior
+f6ed isuperior
+f6ee lsuperior
+f6ef msuperior
+f6f0 osuperior
+f6f1 rsuperior
+f6f2 ssuperior
+f6f3 tsuperior
+f6f4 Brevesmall
+f6f5 Caronsmall
+f6f6 Circumflexsmall
+f6f7 Dotaccentsmall
+f6f8 Hungarumlautsmall
+f6f9 Lslashsmall
+f6fa OEsmall
+f6fb Ogoneksmall
+f6fc Ringsmall
+f6fd Scaronsmall
+f6fe Tildesmall
+f6ff Zcaronsmall
+f721 exclamsmall
+f724 dollaroldstyle
+f726 ampersandsmall
+f730 zerooldstyle
+f731 oneoldstyle
+f732 twooldstyle
+f733 threeoldstyle
+f734 fouroldstyle
+f735 fiveoldstyle
+f736 sixoldstyle
+f737 sevenoldstyle
+f738 eightoldstyle
+f739 nineoldstyle
+f73f questionsmall
+f760 Gravesmall
+f761 Asmall
+f762 Bsmall
+f763 Csmall
+f764 Dsmall
+f765 Esmall
+f766 Fsmall
+f767 Gsmall
+f768 Hsmall
+f769 Ismall
+f76a Jsmall
+f76b Ksmall
+f76c Lsmall
+f76d Msmall
+f76e Nsmall
+f76f Osmall
+f770 Psmall
+f771 Qsmall
+f772 Rsmall
+f773 Ssmall
+f774 Tsmall
+f775 Usmall
+f776 Vsmall
+f777 Wsmall
+f778 Xsmall
+f779 Ysmall
+f77a Zsmall
+f7a1 exclamdownsmall
+f7a2 centoldstyle
+f7a8 Dieresissmall
+f7af Macronsmall
+f7b4 Acutesmall
+f7b8 Cedillasmall
+f7bf questiondownsmall
+f7e0 Agravesmall
+f7e1 Aacutesmall
+f7e2 Acircumflexsmall
+f7e3 Atildesmall
+f7e4 Adieresissmall
+f7e5 Aringsmall
+f7e6 AEsmall
+f7e7 Ccedillasmall
+f7e8 Egravesmall
+f7e9 Eacutesmall
+f7ea Ecircumflexsmall
+f7eb Edieresissmall
+f7ec Igravesmall
+f7ed Iacutesmall
+f7ee Icircumflexsmall
+f7ef Idieresissmall
+f7f0 Ethsmall
+f7f1 Ntildesmall
+f7f2 Ogravesmall
+f7f3 Oacutesmall
+f7f4 Ocircumflexsmall
+f7f5 Otildesmall
+f7f6 Odieresissmall
+f7f8 Oslashsmall
+f7f9 Ugravesmall
+f7fa Uacutesmall
+f7fb Ucircumflexsmall
+f7fc Udieresissmall
+f7fd Yacutesmall
+f7fe Thornsmall
+f7ff Ydieresissmall
+f8e5 radicalex
+f8e6 arrowvertex
+f8e7 arrowhorizex
+f8e8 registersans
+f8e9 copyrightsans
+f8ea trademarksans
+f8eb parenlefttp
+f8ec parenleftex
+f8ed parenleftbt
+f8ee bracketlefttp
+f8ef bracketleftex
+f8f0 bracketleftbt
+f8f1 bracelefttp
+f8f2 braceleftmid
+f8f3 braceleftbt
+f8f4 braceex
+f8f5 integralex
+f8f6 parenrighttp
+f8f7 parenrightex
+f8f8 parenrightbt
+f8f9 bracketrighttp
+f8fa bracketrightex
+f8fb bracketrightbt
+f8fc bracerighttp
+f8fd bracerightmid
+f8fe bracerightbt
+fb00 ff
+fb01 fi
+fb02 fl
+fb03 ffi
+fb04 ffl
+fb1f afii57705
+fb2a afii57694
+fb2b afii57695
+fb35 afii57723
+fb4b afii57700
diff --git a/data/testprint.ps b/data/testprint.ps
new file mode 100644
index 000000000..9ebcff3b6
--- /dev/null
+++ b/data/testprint.ps
@@ -0,0 +1,505 @@
+%!PS-Adobe-3.0
+%%BoundingBox: 0 0 612 792
+%%Pages: 1
+%%LanguageLevel: 1
+%%DocumentData: Clean7Bit
+%%DocumentSuppliedResources: procset testprint/1.0
+%%DocumentNeededResources: font Helvetica Helvetica-Bold Times-Roman
+%%Creator: Michael Sweet, Easy Software Products
+%%CreationDate: May 11, 1999
+%%Title: Test Page
+%%EndComments
+%%BeginProlog
+%%BeginResource procset testprint 1.1 0
+%
+% PostScript test page for the Common UNIX Printing System ("CUPS").
+%
+% Copyright 1993-2000 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.
+%
+/OCTANT { % Draw a color wheel OCTANT...
+ % (name) radius r g b OCTANT -
+ % Loop through 100 shades...
+ 0 0.010101 0.98 {
+ % Set the color...
+ 3 index 1 eq % R == 1?
+ 3 index 1 eq % G == 1?
+ 3 index 1 eq % B == 1?
+ and and {
+ 0 index 4 index mul % R * val
+ 1 index 4 index mul % G * val
+ 2 index 4 index mul % B * val
+ } {
+ 0 index 4 index mul % R * val
+ 1 index neg 1 add add % + (1 - val)
+ 1 index 4 index mul % G * val
+ 2 index neg 1 add add % + (1 - val)
+ 2 index 4 index mul % B * val
+ 3 index neg 1 add add % + (1 - val)
+ } ifelse
+ setrgbcolor
+
+ % Draw a polygon...
+ dup 5 index mul dup 0 % x1, y1
+ moveto
+ 0.707106781 mul dup lineto % x2, y2
+
+ 0.010101 add 4 index mul dup % x3
+ 0.707106781 mul dup lineto % x3, y3
+ 0 lineto % x4, y4
+ closepath
+ fill
+ } for
+
+ % Draw a line around the polygons...
+ pop pop pop dup
+ 0 setgray
+ 0 0 moveto
+ dup 0 lineto
+ 0.707106781 mul dup lineto
+ closepath
+ stroke
+
+ % Draw the label...
+ 0 exch dup -9 div exch % text offset = 0, -radius/9
+ dup 0.923879532 mul % x = radius * cos(22.5)
+ exch 0.382683432 mul % y = radius * cos(22.5)
+ moveto % position label
+ gsave
+ 22.5 rotate % rotate label
+ rmoveto % offset label
+ show % show label
+ grestore
+} bind def
+/CENTER { % Draw centered text
+ % (name) CENTER -
+ dup stringwidth pop % Get the width of the string
+ 0.5 mul neg 0 rmoveto % Shift left 1/2 of the distance
+ show % Show the string
+} bind def
+/RIGHT { % Draw right-justified text
+ % (name) RIGHT -
+ dup stringwidth pop % Get the width of the string
+ neg 0 rmoveto % Shift left the entire distance
+ show % Show the string
+} bind def
+/NUMBER { % Draw a number
+ % power n NUMBER -
+ 1 index 1 eq { % power == 1?
+ round cvi exch pop % Convert "n" to integer
+ } {
+ 1 index mul round exch div % Truncate extra decimal places
+ } ifelse
+ 100 string cvs show % Convert to a string and show it...
+} bind def
+/CUPSLOGO { % Draw the CUPS logo
+ % height CUPSLOGO
+ % Start with a big C...
+ /Helvetica findfont 1 index scalefont setfont
+ 0 setgray
+ 0 0 moveto
+ (C) show
+
+ % Then "UNIX Printing System" much smaller...
+ /Helvetica-Bold findfont 1 index 9 div scalefont setfont
+ 0.25 mul
+ dup dup 2.0 mul moveto
+ (UNIX) show
+ dup dup 1.6 mul moveto
+ (Printing) show
+ dup 1.2 mul moveto
+ (System) show
+} bind def
+/ESPLOGO { % Draw the ESP logo
+ % height ESPLOGO
+ % Compute the size of the logo...
+ 0 0
+ 2 index 1.5 mul 3 index
+
+ % Do the "metallic" fill from 10% black to 40% black...
+ 1 -0.001 0 {
+ dup % loopval
+ -0.15 mul % loopval * -0.15
+ 0.9 add % 0.9 - loopval * 0.15
+ setgray % set gray shade
+
+ 0 % x
+ 1 index neg % loopval
+ 1 add % 1 - loopval
+ 3 index % height
+ mul % height * (1 - loopval)
+ moveto % starting point
+
+ dup % loopval
+ 3 index % width
+ mul % loopval * width
+ 2 index % height
+ lineto % Next point
+
+ 0 % x
+ 2 index % height
+ lineto % Next point
+
+ closepath
+ fill
+
+ dup % loopval
+ 0.15 mul % loopval * 0.15
+ 0.6 add % 0.6 + loopval * 0.15
+ setgray
+
+ dup % loopval
+ neg 1 add % 1 - loopval
+ 3 index % width
+ mul % (1 - loopval) * width
+ 0 % y
+ moveto % Starting point
+
+ 2 index % width
+ exch % loopval
+ 2 index % height
+ mul % loopval * height
+ lineto % Next point
+
+ 1 index % width
+ 0 % y
+ lineto % Next point
+
+ closepath
+ fill
+ } for
+
+ 0 setgray rectstroke
+
+ /Helvetica-BoldOblique findfont 1 index 3 div scalefont setfont
+ dup 40 div
+
+ dup 4 mul 1 index 25 mul moveto (E) show
+ dup 10 mul 1 index 15 mul moveto (S) show
+ dup 16 mul 1 index 5 mul moveto (P) show
+
+ /Helvetica-BoldOblique findfont 2 index 5 div scalefont setfont
+ dup 14 mul 1 index 29 mul moveto (asy) show
+ dup 20 mul 1 index 19 mul moveto (oftware) show
+ dup 26 mul 1 index 9 mul moveto (roducts) show
+
+ pop
+} bind def
+%%EndResource
+%%EndProlog
+%%Page: 1 1
+gsave
+
+ % Determine the imageable area and device resolution...
+ initclip newpath clippath pathbbox % Get bounding rectangle
+ 72 div /pageTop exch def % Get top margin in inches
+ 72 div /pageRight exch def % Get right margin in inches
+ 72 div /pageBottom exch def % Get bottom margin in inches
+ 72 div /pageLeft exch def % Get left margin in inches
+
+ 4 setlinewidth % Draw wide lines
+ 0 setgray closepath stroke % Draw a clipping rectangle
+ 1 setlinewidth % Draw normal lines
+
+ /pageWidth pageRight pageLeft sub def % pageWidth = pageRight - pageLeft
+ /pageHeight pageTop pageBottom sub def% pageHeight = pageTop - pageBottom
+
+ 72 72 dtransform % Get device resolution per inch
+ /yResolution exch abs def % yResolution = abs(yres)
+ /xResolution exch abs def % xResolution = abs(xres)
+
+ % Create fonts...
+ /bigFont /Helvetica-Bold findfont % bigFont = Helvetica-Bold
+ pageWidth 4 mul scalefont def % size = pageWidth * 4 (nominally 34)
+
+ /mediumFont /Helvetica findfont % mediumFont = Helvetica
+ pageWidth 2 mul scalefont def % size = pageWidth * 2 (nominally 17)
+
+ /smallFont /Times-Roman findfont % smallFont = Times-Roman
+ pageWidth 1.5 mul scalefont def % size = pageWidth * 1.5 (nominally 13)
+
+ % Offset page to account for lower-left margin...
+ pageLeft 72 mul
+ pageBottom 72 mul
+ translate
+
+ % Draw the color wheel...
+ mediumFont setfont % Font
+ 0 setgray % Color
+
+ gsave
+ % Position the wheel on the left side...
+ pageWidth 18 mul % x = pageWidth * 1/4 * 72
+ pageHeight 54 mul % y = pageHeight * 3/4 * 72
+ translate
+
+ % Size the wheel...
+ pageWidth 9 mul % radius = pageWidth * 1/8 * 72
+
+ % Draw the colors...
+ dup (C) exch 0 1 1 OCTANT 45 rotate
+ dup (M) exch 1 0 1 OCTANT 45 rotate
+ dup (Y) exch 1 1 0 OCTANT 45 rotate
+ dup (K) exch 0 0 0 OCTANT 45 rotate
+ dup (R) exch 1 0 0 OCTANT 45 rotate
+ dup (G) exch 0 1 0 OCTANT 45 rotate
+ dup (B) exch 0 0 1 OCTANT 45 rotate
+ (W) exch 1 1 1 OCTANT 45 rotate
+ grestore
+
+ % Label the color wheel...
+ pageWidth 18 mul % x = pageWidth * 1/4 * 72
+ pageHeight 44 mul % y = pageHeight * 19/32 * 72
+ moveto % Position the text
+ (Color Wheel) CENTER % Show the text centered
+
+ % Draw radial lines...
+ gsave
+ 0 setlinewidth % 1 pixel lines
+
+ % Position the lines on the left side...
+ pageWidth 54 mul % x = pageWidth * 3/4 * 72
+ pageHeight 54 mul % y = pageHeight * 3/4 * 72
+ translate
+
+ % Size the wheel...
+ pageWidth 9 mul % radius = pageWidth * 1/8 * 72
+
+ % Loop at 1 degree increments
+ 0 1 359 {
+ pop % Discard angle - not used
+ 0 0 moveto % Start line at the center
+ dup 0 lineto % Draw to the radius
+ 1 rotate % Rotate 1 degree
+ } for
+
+ pop % Discard radius - not needed anymore
+ stroke % Draw lines...
+
+ grestore
+
+ % Label the lines...
+ pageWidth 54 mul % x = pageWidth * 3/4 * 72
+ pageHeight 44 mul % y = pageHeight * 19/32 * 72
+ moveto % Position the text
+ (1 Degree Radial Lines) CENTER % Show the text centered
+
+ % Imageable area...
+ pageWidth 19.5 mul % Height of imageable area
+
+ pageWidth 4.5 mul % x = pageWidth * 1/16 * 72
+ pageHeight 35.5 mul % y = pageHeight * 1/2 * 72
+ 2 index sub % y -= height
+ pageWidth 28 mul % width = pageWidth * 1/4 * 72
+ 3 index % height
+ 0.5 setgray rectfill % Draw a shadow
+
+ pageWidth 4 mul % x = pageWidth * 1/16 * 72
+ pageHeight 36 mul % y = pageHeight * 1/2 * 72
+ 2 index sub % y -= height
+ pageWidth 28 mul % width = pageWidth * 3/8 * 72
+ 3 index % height
+ 4 copy 1 setgray rectfill % Clear the box to white
+ 0 setgray rectstroke % Draw a black box around it...
+
+ pop % Discard height
+
+ % Label the imageable area...
+ pageWidth 4 mul % x = pageWidth * 1/16 * 72
+ pageHeight 37 mul % y = pageHeight * 1/2 * 72
+ moveto % Position the text
+ mediumFont setfont % Font
+ (Imageable Area) show % Show the text
+
+ smallFont setfont % Font
+ pageWidth 14 mul % x = pageWidth * 3/16 * 72
+ pageHeight 36 mul % y = pageWidth * 1/2 * 72
+ pageWidth -3 mul add % y -= 2 * smallFont height
+
+ % Page Size inches
+ 2 copy moveto % Move to x & y
+ (Page Size: ) RIGHT % Label
+ 100 pageWidth NUMBER % pageWidth
+ (x) show % "x"
+ 100 pageHeight NUMBER % pageHeight
+ (in) show % "in"
+
+ % Page Size millimeters
+ pageWidth -1.5 mul add % Move down...
+
+ 2 copy moveto % Move to x & y
+ 10 pageWidth 25.4 mul NUMBER % pageWidth
+ (x) show % "x"
+ 10 pageHeight 25.4 mul NUMBER % pageHeight
+ (mm) show % "mm"
+
+ % Lower-left inches
+ pageWidth -3 mul add % Move down...
+
+ 2 copy moveto % Move to x & y
+ (Lower-Left: ) RIGHT % Label
+ 100 pageLeft NUMBER % pageLeft
+ (x) show % "x"
+ 100 pageBottom NUMBER % pageBottom
+ (in) show % "in"
+
+ % Lower-left millimeters
+ pageWidth -1.5 mul add % Move down...
+
+ 2 copy moveto % Move to x & y
+ 10 pageLeft 25.4 mul NUMBER % pageLeft
+ (x) show % "x"
+ 10 pageBottom 25.4 mul NUMBER % pageBottom
+ (mm) show % "mm"
+
+ % Upper-right inches
+ pageWidth -3 mul add % Move down...
+
+ 2 copy moveto % Move to x & y
+ (Upper-Right: ) RIGHT % Label
+ 100 pageRight NUMBER % pageRight
+ (x) show % "x"
+ 100 pageTop NUMBER % pageTop
+ (in) show % "in"
+
+ % Upper-right millimeters
+ pageWidth -1.5 mul add % Move down...
+
+ 2 copy moveto % Move to x & y
+ 10 pageRight 25.4 mul NUMBER % pageRight
+ (x) show % "x"
+ 10 pageTop 25.4 mul NUMBER % pageTop
+ (mm) show % "mm"
+
+ % Resolution dots-per-inch
+ pageWidth -3 mul add % Move down...
+
+ 2 copy moveto % Move to x & y
+ (Resolution: ) RIGHT % Label
+ 1 xResolution NUMBER % xResolution
+ (x) show % "x"
+ 1 yResolution NUMBER % yResolution
+ (dpi) show % "dpi"
+
+ % Resolution dots-per-meter
+ pageWidth -1.5 mul add % Move down...
+
+ moveto % Move to x & y
+ 1 xResolution 39.27 mul NUMBER % xResolution
+ (x) show % "x"
+ 1 yResolution 39.27 mul NUMBER % yResolution
+ (dpm) show % "dpm"
+
+ % Interpreter Information...
+ pageWidth 19.5 mul % Height of interpreter info
+
+ pageWidth 40.5 mul % x = pageWidth * 9/16 * 72
+ pageHeight 35.5 mul % y = pageHeight * 1/2 * 72
+ 2 index sub % y -= height
+ pageWidth 28 mul % width = pageWidth * 1/4 * 72
+ 3 index % height
+ 0.5 setgray rectfill % Draw a shadow
+
+ pageWidth 40 mul % x = pageWidth * 9/16 * 72
+ pageHeight 36 mul % y = pageHeight * 1/2 * 72
+ 2 index sub % y -= height
+ pageWidth 28 mul % width = pageWidth * 3/8 * 72
+ 3 index % height
+ 4 copy 1 setgray rectfill % Clear the box to white
+ 0 setgray rectstroke % Draw a black box around it...
+
+ pop % Discard height
+
+ % Label the interpreter info...
+ pageWidth 40 mul % x = pageWidth * 9/16 * 72
+ pageHeight 37 mul % y = pageHeight * 1/2 * 72
+ moveto % Position the text
+ mediumFont setfont % Font
+ (Interpreter Information) show % Show the text
+
+ smallFont setfont % Font
+ pageWidth 49 mul % x = pageWidth * 11/16 * 72
+ pageHeight 36 mul % y = pageWidth * 1/2 * 72
+ pageWidth -3 mul add % y -= 2 * smallFont height
+
+ % Language level
+ 2 copy moveto % Move to x & y
+ (PostScript: ) RIGHT % Label
+ (Level ) show % "Level "
+ 1 languagelevel NUMBER % Language level
+
+ % Version
+ pageWidth -3 mul add % Move down...
+ 2 copy moveto % Move to x & y
+ (Version: ) RIGHT % Label
+ version show % Version
+ ( \() show % " ("
+ 1 revision NUMBER % Revision
+ (\)) show % ")"
+
+ % Product
+ pageWidth -3 mul add % Move down...
+ 2 copy moveto % Move to x & y
+ (Product: ) RIGHT % Label
+ product show % Product name
+
+ % Serial Number
+ pageWidth -3 mul add % Move down...
+ 2 copy moveto % Move to x & y
+ (Serial #: ) RIGHT % Label
+ 1 serialnumber NUMBER % S/N
+
+ % Draw the label at the top...
+ pageWidth 36 mul % Center of page
+ pageHeight 68 mul % Top of page (15/16ths)
+ 2 copy moveto % Position text
+ bigFont setfont % Font
+ (Printer Test Page) CENTER % Show text centered
+
+ % Draw the copyright notice at the bottom...
+ pageWidth 36 mul % Center of page
+ pageWidth 14 mul % Bottom of page
+ 2 copy moveto % Position text
+ (Printed Using CUPS v1.1) CENTER % Show text centered
+
+ pageWidth 3 mul sub % Move down...
+ 2 copy moveto % Position text
+ smallFont setfont % Font
+ (Copyright 1993-2000 Easy Software Products, All Rights Reserved.) CENTER
+ pageWidth 1.5 mul sub % Move down...
+ 2 copy moveto % Position text
+ (CUPS, and the CUPS logo are the trademark property of) CENTER
+ pageWidth 1.5 mul sub % Move down...
+ 2 copy moveto % Position text
+ (Easy Software Products, 44141 Airport View Drive, Suite 204,) CENTER
+ pageWidth 1.5 mul sub % Move down...
+ 2 copy moveto % Position text
+ (Hollywood, Maryland, 20636-3111, USA.) CENTER
+
+ % Then the CUPS logo....
+ gsave
+ pageWidth 4 mul
+ pageWidth 6 mul
+ translate
+ pageWidth 9 mul CUPSLOGO
+ grestore
+
+ % And the ESP logo....
+ gsave
+ pageWidth 59 mul
+ pageWidth 6 mul
+ translate
+ pageWidth 6 mul ESPLOGO
+ grestore
+% Show the page...
+grestore
+showpage
+%
+% End of "$Id: testprint.ps 985 2000-03-13 18:55:00Z mike $".
+%
+%%EOF
diff --git a/data/utf-8 b/data/utf-8
new file mode 100644
index 000000000..e808223a0
--- /dev/null
+++ b/data/utf-8
@@ -0,0 +1,38 @@
+charset utf8
+
+#
+# This file defines the font mappings used for Unicode/UTF-8 text printing.
+#
+# Each line consists of:
+#
+# first last direction width normal bold italic bold-italic
+#
+# First and last are the first and last glyphs in the font mapping
+# that correspond to that font; a maximum of 256 characters can be
+# mapped within each group, with a maximum of 256 mappings (this is a
+# PostScript limitation.) The glyph values are hexadecimal.
+#
+# Direction is the string "ltor" or "rtol", indicating left-to-right or
+# right-to-left text.
+#
+# Width is the string "single" or "double"; double means that the glyphs
+# are twice as wide as ASCII characters in the Courier typeface.
+#
+# "Normal", "bold", "italic", and "bold-italic" are the typefaces to use
+# for each presentation. If characters are only available in a single
+# style then only one typeface should be listed (e.g. "Symbol")
+#
+# Each font that is listed will be used (and downloaded if needed) when
+# printing.
+#
+
+0000 00FF ltor single Courier Courier-Bold Courier-Italic Courier-Bold-Italic
+0100 01FF ltor single Courier Courier-Bold Courier-Italic Courier-Bold-Italic
+0200 02FF ltor single Courier Courier-Bold Courier-Italic Courier-Bold-Italic
+0300 03FF ltor single Symbol
+0400 04FF ltor single Courier-Cyrillic
+1E00 1EFF ltor single Courier Courier-Bold Courier-Italic Courier-Bold-Italic
+2000 20FF ltor single Courier Courier-Bold Courier-Italic Courier-Bold-Italic
+2100 21FF ltor single Courier Courier-Bold Courier-Italic Courier-Bold-Italic
+2200 22FF ltor single Symbol
+2300 23FF ltor single Symbol
diff --git a/doc/Makefile b/doc/Makefile
new file mode 100644
index 000000000..dd475141a
--- /dev/null
+++ b/doc/Makefile
@@ -0,0 +1,158 @@
+#
+# "$Id$"
+#
+# Documentation makefile for the Common UNIX Printing System (CUPS).
+#
+# Copyright 1993-2000 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 .ps .shtml
+.shtml.html:
+ echo Formatting $@...
+ htmldoc --titleimage images/cups-large.gif --numbered -f $@ $<
+.shtml.pdf:
+ echo Formatting $@...
+ htmldoc --titleimage images/cups-large.gif --duplex --compression=9 \
+ --numbered --jpeg -f $@ $<
+.shtml.ps:
+ echo Formatting $@...
+ htmldoc --titleimage images/cups-large.gif --duplex --numbered \
+ --jpeg -f $@ $<
+
+
+#
+# Document files...
+#
+
+DOCUMENTS = cmp.shtml idd.shtml ipp.shtml sam.shtml sdd.shtml \
+ spm.shtml ssr.shtml stp.shtml sum.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/accept-jobs.gif \
+ images/add-class.gif \
+ images/add-printer.gif \
+ images/cancel-job.gif \
+ images/cancel-jobs.gif \
+ images/cancel.gif \
+ images/classes.gif \
+ images/config-printer.gif \
+ images/continue.gif \
+ images/delete-class.gif \
+ images/delete-printer.gif \
+ images/draft.gif \
+ images/hold-job.gif \
+ images/left.gif \
+ images/logo.gif \
+ images/manage-classes.gif \
+ images/manage-jobs.gif \
+ images/manage-printers.gif \
+ images/modify-class.gif \
+ images/modify-printer.gif \
+ images/navbar.gif \
+ images/print-test-page.gif \
+ images/printer-idle.gif \
+ images/printer-processing.gif \
+ images/printer-stopped.gif \
+ images/reject-jobs.gif \
+ images/release-job.gif \
+ images/right.gif \
+ images/show-active.gif \
+ images/show-completed.gif \
+ images/start-class.gif \
+ images/start-printer.gif \
+ images/stop-class.gif \
+ images/stop-printer.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) $(DOCDIR)
+ $(INSTALL_MAN) $(WEBPAGES) $(DOCDIR)
+ $(INSTALL_MAN) overview.html overview.pdf $(DOCDIR)
+ $(INSTALL_MAN) $(DOCUMENTS:.shtml=.html) $(DOCDIR)
+ $(INSTALL_MAN) $(DOCUMENTS:.shtml=.pdf) $(DOCDIR)
+ -$(MKDIR) $(DOCDIR)/images
+ $(INSTALL_MAN) $(WEBIMAGES) $(DOCDIR)/images
+ $(INSTALL_MAN) $(DOCIMAGES) $(DOCDIR)/images
+
+
+#
+# The overview, admin manual, programmers manual, and users manual get
+# special attention...
+#
+
+overview.pdf: overview.html
+ echo Formatting $@...
+ htmldoc --duplex --compression=9 --jpeg --webpage -f overview.pdf overview.html
+
+sam.html: sam.shtml
+ echo Formatting $@...
+ htmldoc --titleimage images/cups-large.gif -f $@ $<
+sam.pdf: sam.shtml
+ echo Formatting $@...
+ htmldoc --titleimage images/cups-large.gif --duplex --compression=9 \
+ --jpeg -f $@ $<
+
+spm.html: spm.shtml
+ echo Formatting $@...
+ htmldoc --titleimage images/cups-large.gif -f $@ $<
+spm.pdf: spm.shtml
+ echo Formatting $@...
+ htmldoc --titleimage images/cups-large.gif --duplex --compression=9 \
+ --jpeg -f $@ $<
+
+sum.html: sum.shtml
+ echo Formatting $@...
+ htmldoc --titleimage images/cups-large.gif -f $@ $<
+sum.pdf: sum.shtml
+ echo Formatting $@...
+ htmldoc --titleimage 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..1c33532d1
--- /dev/null
+++ b/doc/cmp.html
@@ -0,0 +1,676 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
+<HTML>
+<HEAD>
+<TITLE>CUPS Configuration Management Plan</TITLE>
+<META NAME="AUTHOR" CONTENT="Easy Software Products">
+<META NAME="COPYRIGHT" CONTENT="Copyright 1997-2000, All Rights Reserved">
+<META NAME="DOCNUMBER" CONTENT="CUPS-CMP-1.1">
+<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
+<STYLE>
+BODY { font-family: serif; font-size: 11.0pt }
+H1 { font-family: sans-serif; font-size: 20.0pt }
+H2 { font-family: sans-serif; font-size: 17.0pt }
+H3 { font-family: sans-serif; font-size: 14.0pt }
+H4 { font-family: sans-serif; font-size: 11.0pt }
+H5 { font-family: sans-serif; font-size: 9.0pt }
+H6 { font-family: sans-serif; font-size: 8.0pt }
+SUB { font-size: 8.0pt }
+SUP { font-size: 8.0pt }
+PRE { font-size: 9.0pt }
+A:link { text-decoration: underline }
+A:visited { text-decoration: underline }
+A:active { text-decoration: underline }
+</STYLE>
+</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.1<BR>
+Easy Software Products<BR>
+Copyright 1997-2000, 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.1 software.
+<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 <A HREF="http://www.easysw.com">
+Easy Software Products</A> 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 5.50) and an image file RIP that
+is 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>
+<P>The following CUPS documentation is referenced by this document: </P>
+<UL>
+<LI>CUPS-CMP-1.1: CUPS Configuration Management Plan </LI>
+<LI>CUPS-IDD-1.1: CUPS System Interface Design Description </LI>
+<LI>CUPS-IPP-1.1: CUPS Implmentation of IPP </LI>
+<LI>CUPS-SAM-1.1.x: CUPS Software Administrators Manual </LI>
+<LI>CUPS-SDD-1.1: CUPS Software Design Description </LI>
+<LI>CUPS-SPM-1.1: CUPS Software Programming Manual </LI>
+<LI>CUPS-SSR-1.1: CUPS Software Security Report </LI>
+<LI>CUPS-STP-1.1: CUPS Software Test Plan </LI>
+<LI>CUPS-SUM-1.1.x: CUPS Software Users Manual </LI>
+<LI>CUPS-SVD-1.1.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>Adobe PostScript Printer Description File Format Specification,
+ Version 4.3. </LI>
+<LI>Adobe PostScript Language Reference, Third Edition. </LI>
+<LI>IPP: Job and Printer Set Operations </LI>
+<LI>IPP/1.1: Encoding and Transport </LI>
+<LI>IPP/1.1: Implementers Guide </LI>
+<LI>IPP/1.1: Model and Semantics </LI>
+<LI>RFC 1179, Line Printer Daemon Protocol </LI>
+<LI>RFC 2567, Design Goals for an Internet Printing Protocol </LI>
+<LI>RFC 2568, Rationale for the Structure of the Model and Protocol
+ for the Internet Printing Protocol </LI>
+<LI>RFC 2569, Mapping between LPD and IPP Protocols </LI>
+<LI>RFC 2616, Hypertext Transfer Protocol -- HTTP/1.1 </LI>
+<LI>RFC 2617, HTTP Authentication: Basic and Digest Access
+ Authentication </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.1.0
+</PRE>
+</UL>
+ Beta-test releases are indentified by appending the letter B followed
+by the build number:
+<UL>
+<PRE>
+major.minor.patchbbuild
+1.1.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.1.0b1 First beta release
+1.1.0b2 Second beta release
+1.1.0 First production release
+1.1.1b1 First beta of 1.1.1
+1.1.1 Production release of 1.1.1
+1.1.1b1 First beta of 1.1.1
+1.1.1 Production release of 1.1.1
+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.1. 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;.cxx&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$&quot; tag:
+<UL>
+<PRE>
+/*
+ * &quot;36;Id$&quot;
+ *
+ * Description of file contents.
+ *
+ * Copyright 1997-2000 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
+ * 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:
+ *
+ * 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$&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;36;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.1 */
+do_this(float x) /* I - Power value (0.0 &lt;= x &lt;= 1.1) */
+{
+ ...
+ 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..3b7b93abb
--- /dev/null
+++ b/doc/cmp.pdf
@@ -0,0 +1,765 @@
+%PDF-1.2
+%âãÏÓ
+1 0 obj<</Producer(htmldoc 1.8.5 Copyright 1997-2000 Easy Software Products, All Rights Reserved.)/CreationDate(D:20000312215227Z)/Title(CUPS Configuration Management Plan)/Author(Easy Software Products)>>endobj
+2 0 obj<</Type/Encoding/Differences[ 32/space/exclam/quotedbl/numbersign/dollar/percent/ampersand/quotesingle/parenleft/parenright/asterisk/plus/comma/minus/period/slash/zero/one/two/three/four/five/six/seven/eight/nine/colon/semicolon/less/equal/greater/question/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/grave/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 128/Euro 130/quotesinglbase/florin/quotedblbase/ellipsis/dagger/daggerdbl/circumflex/perthousand/Scaron/guilsinglleft/OE 145/quoteleft/quoteright/quotedblleft/quotedblright/bullet/endash/emdash/tilde/trademark/scaron/guilsinglright/oe 159/Ydieresis/space/exclamdown/cent/sterling/currency/yen/brokenbar/section/dieresis/copyright/ordfeminine/guillemotleft/logicalnot/hyphen/registered/macron/degree/plusminus/twosuperior/threesuperior/acute/mu/paragraph/periodcentered/cedilla/onesuperior/ordmasculine/guillemotright/onequarter/onehalf/threequarters/questiondown/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]>>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://www.easysw.com)>>endobj
+11 0 obj<</Subtype/Link/Rect[157.2 547.2 262.3 560.2]/Border[0 0 0]/A 10 0 R>>endobj
+12 0 obj[11 0 R
+]endobj
+13 0 obj<</Subtype/Link/Rect[72.0 670.8 107.8 683.8]/Border[0 0 0]/Dest[132 0 R/XYZ null 818 0]>>endobj
+14 0 obj<</Subtype/Link/Rect[108.0 657.6 183.8 670.6]/Border[0 0 0]/Dest[132 0 R/XYZ null 735 0]>>endobj
+15 0 obj<</Subtype/Link/Rect[108.0 644.4 203.0 657.4]/Border[0 0 0]/Dest[132 0 R/XYZ null 675 0]>>endobj
+16 0 obj<</Subtype/Link/Rect[108.0 631.2 216.4 644.2]/Border[0 0 0]/Dest[132 0 R/XYZ null 449 0]>>endobj
+17 0 obj<</Subtype/Link/Rect[72.0 604.8 131.6 617.8]/Border[0 0 0]/Dest[138 0 R/XYZ null 818 0]>>endobj
+18 0 obj<</Subtype/Link/Rect[108.0 591.6 222.6 604.6]/Border[0 0 0]/Dest[138 0 R/XYZ null 735 0]>>endobj
+19 0 obj<</Subtype/Link/Rect[108.0 578.4 202.4 591.4]/Border[0 0 0]/Dest[138 0 R/XYZ null 535 0]>>endobj
+20 0 obj<</Subtype/Link/Rect[72.0 552.0 162.4 565.0]/Border[0 0 0]/Dest[144 0 R/XYZ null 818 0]>>endobj
+21 0 obj<</Subtype/Link/Rect[108.0 538.8 209.7 551.8]/Border[0 0 0]/Dest[144 0 R/XYZ null 735 0]>>endobj
+22 0 obj<</Subtype/Link/Rect[108.0 525.6 179.2 538.6]/Border[0 0 0]/Dest[144 0 R/XYZ null 641 0]>>endobj
+23 0 obj<</Subtype/Link/Rect[108.0 512.4 246.4 525.4]/Border[0 0 0]/Dest[144 0 R/XYZ null 574 0]>>endobj
+24 0 obj<</Subtype/Link/Rect[72.0 486.0 206.7 499.0]/Border[0 0 0]/Dest[150 0 R/XYZ null 818 0]>>endobj
+25 0 obj<</Subtype/Link/Rect[108.0 472.8 185.0 485.8]/Border[0 0 0]/Dest[150 0 R/XYZ null 623 0]>>endobj
+26 0 obj<</Subtype/Link/Rect[108.0 459.6 183.8 472.6]/Border[0 0 0]/Dest[150 0 R/XYZ null 397 0]>>endobj
+27 0 obj<</Subtype/Link/Rect[108.0 446.4 171.5 459.4]/Border[0 0 0]/Dest[150 0 R/XYZ null 264 0]>>endobj
+28 0 obj<</Subtype/Link/Rect[108.0 433.2 177.7 446.2]/Border[0 0 0]/Dest[153 0 R/XYZ null 800 0]>>endobj
+29 0 obj<</Subtype/Link/Rect[72.0 406.8 164.9 419.8]/Border[0 0 0]/Dest[156 0 R/XYZ null 818 0]>>endobj
+30 0 obj<</Subtype/Link/Rect[108.0 393.6 212.2 406.6]/Border[0 0 0]/Dest[156 0 R/XYZ null 735 0]>>endobj
+31 0 obj<</Subtype/Link/Rect[108.0 380.4 173.4 393.4]/Border[0 0 0]/Dest[156 0 R/XYZ null 307 0]>>endobj
+32 0 obj<</Subtype/Link/Rect[108.0 367.2 157.5 380.2]/Border[0 0 0]/Dest[156 0 R/XYZ null 227 0]>>endobj
+33 0 obj<</Subtype/Link/Rect[108.0 354.0 158.7 367.0]/Border[0 0 0]/Dest[159 0 R/XYZ null 800 0]>>endobj
+34 0 obj<</Subtype/Link/Rect[72.0 327.6 124.2 340.6]/Border[0 0 0]/Dest[162 0 R/XYZ null 818 0]>>endobj
+35 0 obj<</Subtype/Link/Rect[108.0 314.4 155.0 327.4]/Border[0 0 0]/Dest[162 0 R/XYZ null 735 0]>>endobj
+36 0 obj<</Subtype/Link/Rect[108.0 301.2 172.8 314.2]/Border[0 0 0]/Dest[162 0 R/XYZ null 549 0]>>endobj
+37 0 obj<</Subtype/Link/Rect[72.0 274.8 185.1 287.8]/Border[0 0 0]/Dest[168 0 R/XYZ null 818 0]>>endobj
+38 0 obj<</Subtype/Link/Rect[108.0 261.6 181.0 274.6]/Border[0 0 0]/Dest[168 0 R/XYZ null 715 0]>>endobj
+39 0 obj<</Subtype/Link/Rect[144.0 248.4 206.0 261.4]/Border[0 0 0]/Dest[168 0 R/XYZ null 643 0]>>endobj
+40 0 obj<</Subtype/Link/Rect[144.0 235.2 238.4 248.2]/Border[0 0 0]/Dest[168 0 R/XYZ null 557 0]>>endobj
+41 0 obj<</Subtype/Link/Rect[108.0 222.0 169.7 235.0]/Border[0 0 0]/Dest[171 0 R/XYZ null 720 0]>>endobj
+42 0 obj<</Subtype/Link/Rect[144.0 208.8 206.0 221.8]/Border[0 0 0]/Dest[171 0 R/XYZ null 648 0]>>endobj
+43 0 obj<</Subtype/Link/Rect[144.0 195.6 238.4 208.6]/Border[0 0 0]/Dest[171 0 R/XYZ null 522 0]>>endobj
+44 0 obj<</Subtype/Link/Rect[108.0 182.4 164.8 195.4]/Border[0 0 0]/Dest[171 0 R/XYZ null 321 0]>>endobj
+45 0 obj<</Subtype/Link/Rect[144.0 169.2 206.0 182.2]/Border[0 0 0]/Dest[171 0 R/XYZ null 249 0]>>endobj
+46 0 obj<</Subtype/Link/Rect[144.0 156.0 238.4 169.0]/Border[0 0 0]/Dest[171 0 R/XYZ null 176 0]>>endobj
+47 0 obj<</Subtype/Link/Rect[108.0 142.8 168.5 155.8]/Border[0 0 0]/Dest[174 0 R/XYZ null 650 0]>>endobj
+48 0 obj<</Subtype/Link/Rect[144.0 129.6 206.0 142.6]/Border[0 0 0]/Dest[174 0 R/XYZ null 578 0]>>endobj
+49 0 obj<</Subtype/Link/Rect[144.0 116.4 238.4 129.4]/Border[0 0 0]/Dest[174 0 R/XYZ null 438 0]>>endobj
+50 0 obj<</Subtype/Link/Rect[108.0 103.2 153.2 116.2]/Border[0 0 0]/Dest[174 0 R/XYZ null 345 0]>>endobj
+51 0 obj<</Subtype/Link/Rect[144.0 90.0 206.0 103.0]/Border[0 0 0]/Dest[174 0 R/XYZ null 273 0]>>endobj
+52 0 obj<</Subtype/Link/Rect[144.0 76.8 238.4 89.8]/Border[0 0 0]/Dest[174 0 R/XYZ null 200 0]>>endobj
+53 0 obj<</Subtype/Link/Rect[108.0 63.6 170.9 76.6]/Border[0 0 0]/Dest[177 0 R/XYZ null 768 0]>>endobj
+54 0 obj[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
+]endobj
+55 0 obj<</Subtype/Link/Rect[108.0 670.8 170.0 683.8]/Border[0 0 0]/Dest[177 0 R/XYZ null 696 0]>>endobj
+56 0 obj<</Subtype/Link/Rect[108.0 657.6 202.4 670.6]/Border[0 0 0]/Dest[177 0 R/XYZ null 623 0]>>endobj
+57 0 obj<</Subtype/Link/Rect[72.0 644.4 123.3 657.4]/Border[0 0 0]/Dest[177 0 R/XYZ null 465 0]>>endobj
+58 0 obj<</Subtype/Link/Rect[108.0 631.2 170.0 644.2]/Border[0 0 0]/Dest[177 0 R/XYZ null 393 0]>>endobj
+59 0 obj<</Subtype/Link/Rect[108.0 618.0 202.4 631.0]/Border[0 0 0]/Dest[177 0 R/XYZ null 333 0]>>endobj
+60 0 obj<</Subtype/Link/Rect[72.0 604.8 133.7 617.8]/Border[0 0 0]/Dest[180 0 R/XYZ null 800 0]>>endobj
+61 0 obj<</Subtype/Link/Rect[108.0 591.6 170.0 604.6]/Border[0 0 0]/Dest[180 0 R/XYZ null 728 0]>>endobj
+62 0 obj<</Subtype/Link/Rect[108.0 578.4 202.4 591.4]/Border[0 0 0]/Dest[180 0 R/XYZ null 615 0]>>endobj
+63 0 obj<</Subtype/Link/Rect[72.0 565.2 113.6 578.2]/Border[0 0 0]/Dest[180 0 R/XYZ null 502 0]>>endobj
+64 0 obj<</Subtype/Link/Rect[108.0 552.0 202.4 565.0]/Border[0 0 0]/Dest[180 0 R/XYZ null 430 0]>>endobj
+65 0 obj<</Subtype/Link/Rect[108.0 538.8 157.2 551.8]/Border[0 0 0]/Dest[183 0 R/XYZ null 782 0]>>endobj
+66 0 obj<</Subtype/Link/Rect[36.0 512.4 194.2 525.4]/Border[0 0 0]/Dest[186 0 R/XYZ null 818 0]>>endobj
+67 0 obj[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
+]endobj
+68 0 obj<</Dests 69 0 R>>endobj
+69 0 obj<</Kids[70 0 R]>>endobj
+70 0 obj<</Limits[(1)(cmp.shtml)]/Names[(1)71 0 R(1_1)72 0 R(1_2)73 0 R(1_3)74 0 R(2)75 0 R(2_1)76 0 R(2_2)77 0 R(3)78 0 R(3_1)79 0 R(3_2)80 0 R(3_3)81 0 R(4)82 0 R(4_1)83 0 R(4_2)84 0 R(4_3)85 0 R(4_4)86 0 R(5)87 0 R(5_1)88 0 R(5_2)89 0 R(5_3)90 0 R(5_4)91 0 R(6)92 0 R(6_1)93 0 R(6_2)94 0 R(7)95 0 R(7_1)96 0 R(7_1_1)97 0 R(7_1_2)98 0 R(7_2)99 0 R(7_2_1)100 0 R(7_2_2)101 0 R(7_3)102 0 R(7_3_1)103 0 R(7_3_2)104 0 R(7_4)105 0 R(7_4_1)106 0 R(7_4_2)107 0 R(7_5)108 0 R(7_5_1)109 0 R(7_5_2)110 0 R(7_6)111 0 R(7_6_1)112 0 R(7_6_2)113 0 R(7_7)114 0 R(7_7_1)115 0 R(7_7_2)116 0 R(7_8)117 0 R(7_8_1)118 0 R(7_8_2)119 0 R(7_9)120 0 R(7_9_1)121 0 R(7_9_2)122 0 R(8)123 0 R(cmp.shtml)124 0 R]>>endobj
+71 0 obj<</D[132 0 R/XYZ null 818 null]>>endobj
+72 0 obj<</D[132 0 R/XYZ null 735 null]>>endobj
+73 0 obj<</D[132 0 R/XYZ null 675 null]>>endobj
+74 0 obj<</D[132 0 R/XYZ null 449 null]>>endobj
+75 0 obj<</D[138 0 R/XYZ null 818 null]>>endobj
+76 0 obj<</D[138 0 R/XYZ null 735 null]>>endobj
+77 0 obj<</D[138 0 R/XYZ null 535 null]>>endobj
+78 0 obj<</D[144 0 R/XYZ null 818 null]>>endobj
+79 0 obj<</D[144 0 R/XYZ null 735 null]>>endobj
+80 0 obj<</D[144 0 R/XYZ null 641 null]>>endobj
+81 0 obj<</D[144 0 R/XYZ null 574 null]>>endobj
+82 0 obj<</D[150 0 R/XYZ null 818 null]>>endobj
+83 0 obj<</D[150 0 R/XYZ null 623 null]>>endobj
+84 0 obj<</D[150 0 R/XYZ null 397 null]>>endobj
+85 0 obj<</D[150 0 R/XYZ null 264 null]>>endobj
+86 0 obj<</D[153 0 R/XYZ null 800 null]>>endobj
+87 0 obj<</D[156 0 R/XYZ null 818 null]>>endobj
+88 0 obj<</D[156 0 R/XYZ null 735 null]>>endobj
+89 0 obj<</D[156 0 R/XYZ null 307 null]>>endobj
+90 0 obj<</D[156 0 R/XYZ null 227 null]>>endobj
+91 0 obj<</D[159 0 R/XYZ null 800 null]>>endobj
+92 0 obj<</D[162 0 R/XYZ null 818 null]>>endobj
+93 0 obj<</D[162 0 R/XYZ null 735 null]>>endobj
+94 0 obj<</D[162 0 R/XYZ null 549 null]>>endobj
+95 0 obj<</D[168 0 R/XYZ null 818 null]>>endobj
+96 0 obj<</D[168 0 R/XYZ null 715 null]>>endobj
+97 0 obj<</D[168 0 R/XYZ null 643 null]>>endobj
+98 0 obj<</D[168 0 R/XYZ null 557 null]>>endobj
+99 0 obj<</D[171 0 R/XYZ null 720 null]>>endobj
+100 0 obj<</D[171 0 R/XYZ null 648 null]>>endobj
+101 0 obj<</D[171 0 R/XYZ null 522 null]>>endobj
+102 0 obj<</D[171 0 R/XYZ null 321 null]>>endobj
+103 0 obj<</D[171 0 R/XYZ null 249 null]>>endobj
+104 0 obj<</D[171 0 R/XYZ null 176 null]>>endobj
+105 0 obj<</D[174 0 R/XYZ null 650 null]>>endobj
+106 0 obj<</D[174 0 R/XYZ null 578 null]>>endobj
+107 0 obj<</D[174 0 R/XYZ null 438 null]>>endobj
+108 0 obj<</D[174 0 R/XYZ null 345 null]>>endobj
+109 0 obj<</D[174 0 R/XYZ null 273 null]>>endobj
+110 0 obj<</D[174 0 R/XYZ null 200 null]>>endobj
+111 0 obj<</D[177 0 R/XYZ null 768 null]>>endobj
+112 0 obj<</D[177 0 R/XYZ null 696 null]>>endobj
+113 0 obj<</D[177 0 R/XYZ null 623 null]>>endobj
+114 0 obj<</D[177 0 R/XYZ null 465 null]>>endobj
+115 0 obj<</D[177 0 R/XYZ null 393 null]>>endobj
+116 0 obj<</D[177 0 R/XYZ null 333 null]>>endobj
+117 0 obj<</D[180 0 R/XYZ null 800 null]>>endobj
+118 0 obj<</D[180 0 R/XYZ null 728 null]>>endobj
+119 0 obj<</D[180 0 R/XYZ null 615 null]>>endobj
+120 0 obj<</D[180 0 R/XYZ null 502 null]>>endobj
+121 0 obj<</D[180 0 R/XYZ null 430 null]>>endobj
+122 0 obj<</D[183 0 R/XYZ null 782 null]>>endobj
+123 0 obj<</D[186 0 R/XYZ null 818 null]>>endobj
+124 0 obj<</D[132 0 R/XYZ null 698 null]>>endobj
+125 0 obj<</Type/Pages/MediaBox[0 0 595 792]/Count 24/Kids[126 0 R
+129 0 R
+192 0 R
+195 0 R
+132 0 R
+135 0 R
+138 0 R
+141 0 R
+144 0 R
+147 0 R
+150 0 R
+153 0 R
+156 0 R
+159 0 R
+162 0 R
+165 0 R
+168 0 R
+171 0 R
+174 0 R
+177 0 R
+180 0 R
+183 0 R
+186 0 R
+189 0 R
+]>>endobj
+126 0 obj<</Type/Page/Parent 125 0 R/Contents 127 0 R/Resources<</ProcSet[/PDF/Text/ImageB/ImageC/ImageI]/Font<</F4 4 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+127 0 obj<</Length 128 0 R/Filter/FlateDecode>>stream
+xÚìÏsë8rÇIŠºÌ‰öŒî´üt§çyšÝ­ÚJñÙfvçÖ$ÁÊ!•S*Ç6•üÿ±~X"H€èn
+ ‚B¹ü29×$X,0X
+Ú Ñ ê£s5Ѭåà‹+›— 9Ä`™ãÅ`ž‹,öD.1?V{àÊàŠRç\uD98æY\‰c®}HÙ8媣`Jé++D91WTθš°ŒÒË TÔ  LÔ‹Š4ðZåªBÃ2t,OãQ ÔxŒsÆq‰±FãzX¢Æ#F,Ï%£FL×]w÷Ûí—×cy>|ðÛa0IwÅ÷_†ƒüxÂ[‡ÿîŠG’þÅ˃ŸßÝuoœ*=[ í,¸ö~ºªÛk[ça=xì.Bú±àæØ\ìPƒ–.Î$[q¹¸¡==·aMJ&³»XéÔœ.K™\¬@ž› É貘ÇUùÑv‡]¶cqeSÙàÕ›eN¤ܯjؤ½s’  ®|²¡ÅϼJé\Í Xd°˜ÎEùØÍÉÜZ9ƹfÂ"‚­¨\Õ FÈ1Å’È•Ï…EÛи'’ –Y¹0p7ñú¥uZj+Cg‘¼c,Òé« …«rÎX•½ÅÍÁ‘jÄ¥{.B J<—°ÒlKÃ7D=Ì:¸ÎåÛÁÉ %i=•œkˆàBf½ .Ú`Ø`¹Š¹2O”,Á W­Ç’ó ìÍПR4q‡ãÚa…„–¤8®
+¢6(®–KH·Kh%ºÖRÕÓbQ®•–ër›®Îa‡Àà ¯Çé¹ £^ >ÜÌÑ–œáµCrí´\Mçù
+.A5C#WªçÚŸòáªÕr[¬àª©fhäŠõ\盽§5Rì•K,×êZ[Éu6Ž;êÊ12BªWNZ,×:{—'%WG¤(³ÔgO@• ž«x j®Š5K5†ˆëWC6Ãq®ZÂp ÖbI…Ó5 ÊFÒâ¹Ä»Y¨¹®&EZ,ip-â\9%pžê¹*ÖšNè(;
+\n£á¬5;c+\ŒôÅq®æ"
+®K‰kvª‰@“¤¥pµ¥×qgéB™ÐdcMãº(½Ž«àôWòD@“ë¢ô:®Œ3¾êÙwCÅuQz WÍÛÃÈ0’ ¤h#!r]”^ÃUðûMÏ%s Æð2r•^Õñ6g
+ÌÃ’lì¨\g¥Ws5Ì]Œ
+㘴¶ÑR¹ÎJ¯æ½ØER2WÁ^f®“Ò«¹rfæŽÀ8f DQ)ëdÝ…~^¹e¢IàkSYŽ­\§Öç*®³Ý FÈ‘#š ”(ªT?º²k ±ÌukDŸ+?krN7Ä="à
+vt6מÍ•4™¿peM/\œ¥ùp‰'óg®úÃÉ<–k2ßÎ-‡Í‹ó·•Ü:\[æ»uîªßFwÒ²ßmµwpœOm›.Ò,²
+£®[‘"K~­lâŽÃÕ¿(—óM
+-áðì8—Ðpz®”ÃÕ¿¨7RNf™
+E2þW=/WÁá’.ªn»Ê·EÞÙ¹j—t‘趲¾Æ6ÓJ\‚Ã%_”ßö¿ž#Wé“«{œÍ%_T ö+Càª8\òE½H0?qÙL—Q\Ù8WÃá’/’gÆé>®Îq><Wï"©ÃÊbB®ƒ¾‰‡K¾HN#‚—qy#¶äÊ£îíZðS¹zýèN«i¸®oÐ4Qp¸ú=w–"p…S^Nù‡?ßÞ?oäZJ¬èžó2Ûâr{Ï\ù'×'׸V”+]&WöÉõÉõÉõÉõÉ…ãZr}rùçŠì¹Þf>ès¥”ºór=ŸÞa?bšK©;3×Cÿøšü²¡ÎæuÕ»êªHqPJÛ:k®|¾!çéw·§†u\y1Ô×áŒF~™WsÛìø9QeäÚÉÔ—Pì(É/4¬®KüuòÈȵ™‚ TSë¼;Àz齺 ®u+ M?\™j« ºã “Òdúu\©l)^¸š÷§»nÝî«ko»õʺ ®Dºµ®êºQ(¾unÚ‘À[v…².Ÿ«öÉUtv¬÷7ïÒqY·äsM]Õ2¿ú®òöNå“+ª›v^Ȭ©K㊯ۧoƒ8ñÆ•u—áòžÒߌ§©KãJ®£0qÙÄóÒŸá¦×VËIÚªº$®Õõq¼=™t®êöÓ5ä$mU]WÚ}tk;.CúáõÏÍÝ[ÙtĪ윑Ò×%qÏ]ÓÆ+W¢ûѧ“T<¶ÔçÊ/ƒô8„wÞ¸2]:ÎeO¹éÀd#©;x®ýåóŽŸ|ðÆuŽš¸1‘†‘¶.‰«¸ÈÐÑÒKo\—½Îûƒ:À:eR¼Ë¹¶.‰ nÙ¯qëë¢Z{þ⇬ã‹õu•\ÃÄÎEu¯G÷å«“Òkmñîm®¾X_—ÂU_ `ü`;®±}½n=é½ÄÕ0‹V[—ÂÕ\~<ºeK®±}ØByŽCš—ƺ.]_ˆ¾ç²Ú_ÚÌÒ|Õ¢¯Kàj/Žùô[\½,Ú¸ôdŠ¨Ká:;æ“[öÉÕyÑ–L¡ú’9]]
+×Ù1×'ï“ëý ì —J•l:ZÇuvÌõ©¾—1ïë9Sš\ùPÔuñ~ùäËãÿc—‹|¶—‡á¤¦ÒLrTu \ÕÉOnÙ?×­µ«^(u@Õ%póÉ-OÁõ.
+‰vÐŒÕ%póÉ-[raß-Z‹ÁX]—x÷b멸n‰×f®^]×Ù1ŸEr”Ë&ûø*™ìŠµ\cu)\o&˜4·õS/\²{ãb§®.…ëM2âúì-¹JÜú¡k¬.…ëØÓUt}Q7®D²­x”KW—Âçð±‰ËæÜM.ý9Õù±º®ê|²&±çÚO¿6ÝyÈJË5V—ÂUßœº7®ªÛŸûÞmz«Kájn³Q.›sˆçkãï·M­–k¬®!ž—Þ¶%ä]A ®qö¶ ©Ÿ,ŽÕ¥puÞ ë«™«õ¹Fê’¸r—Õ¹ìf¤jŸk¤.ƒ«´çZãÒmú¡ÿ VÑ×%qƒßxàjô
+3àj¸ëQ2\ŸÊ(—Õû7䵘_ÚQ.}]—îý¬n¹:}l \Úº$®úêÓǹ¬²Þè!Ón”ØÔµ*–ïí¹Ê×ëMóU|–ïY
+¶X¾ë“kz.óë–—ÉUüÓr•‹ä2/pÉe3aþäšžËjb¹h®õ"¹Ä?-WºH.ólõA¹’ere0ðµü>ŽO®¸ö0ðÅ}Ïn‘\ðA¹ª Z~¯Ù\Åô²×å÷ÐÍULMÞá¾70¸@ªrÃ\ e’ºÒò{9ç*Š+[\Àa
+%°ß{ZÀ‘›ì ù=Å¡­´e&=@~¯thG„â‚¥Âèo‘ßoXÀQã¸÷}ô•±
+-V裥ÈaÜ媗#ˆ(Â-ôDˆ *âƒ-ôéBäpÝåB} Â(»‚-ôÇ¥€VÏP„#C̾:\ÕB")£œ· Å }ºÙHd.„Ð'‹TæB,ÝÄ‹u+_†pd8
+h)‚¸xí\Åú,(ÖCéÑg­¡% ÇÜ ýŽhIÂ1ó
+œéöÔ1GNÀY™8樼A:©Ôï˜gB©œÇ ÜÏ™.H,8Ž¸Ãtºc wàÊêd†kÍÁ…Tú‰:,c=càÔ4Æ €?R§é°ŒÖÛNÓaÓ™_‚& :C¢Åqí±`»0ºk…äªé0‘qƒph­ ±÷UKó„ªY.´v†èuâ,øV­!zÕzìÓÝ ¹ð†èQ:j ›ÖÖãrfWŠØ ¬OKüñÍPË%¢¹-±±’.h­ Ñ“&æV>¬­'BõÊXZ?Þ¹±´°öõ^†˜È,e \<1÷3–½í#×½{&ܸ¥rU¤s©ÍJÉ\"š Ldö
+\EiW°/)áøf08Ô&[õ cœFwžÿf<[l2êR6W›qÀúßtŽ*OôÛø\‹+Š¿S;‹añcq€‰‹ÙaQt_zî¬ñ¹‘‘«ˆ¸ÿ¥ó/9k·6\"â—{”56¼OßYqYtØqœ=šÌñ…Ie˜>˜¹l:ìŒöªÿ짌ý¹K.ê4Li¯ƒ~/ÛÌæqµ¶\Mä¦ÜÝo·Û/¯oåe»}È,?mcÍå¢ÃœSþ#†KȵqÀe'‰³tŽ+¼Û9á
+®ÃÌK_8.±´îBrvm¦(ˆ=$W›…ÄupÇU/«»Ð\¬•O_ºäj‚áÚ´.¹‚ÑzÜöžKdKÑxw gÑ q!Øý^
+—XŒÒ¸°DôÖ!‰kvKÄgиš…X!•kæø—°MäšÕ)¹/T®9½3eû‰Ê5c`OJš#sÍ6Ähi/t®™†1±ŒÁ5Ï#îí2¸fbÔ,%=pÍ`sM®ôT9×ÄÚÁÈ
+·¶øhß"7\o¶è®Ë’CW+Âé,—\ŽºìþІÆÕŠ'küâª-.¹ø©ÕªGw-qËe‘^HÝž“‹Kæ–ʇìþ‹ë6øà"æÅÇ[ï¹ðÃõV^qÙññýw/·÷Æuê5ƒAÞm_}ÝÛ'ש۞¶wê~z|õy_ß\çŽ;žpØ>ÜËývûøúêý–“pÍPàÿ
+ä
+Õ |†\òT=*Õ¥³M×Þ½³Øj«[s56 êµ«ËG Š2‰‚`”ó짳r[1âAF‚X6ã"Ök=¼ppMxjoPyWßOaø‚"ÿ¼øï¸ÛËwí9€¤œ³,MÓ –}} ìÍ`üÃÔQ]«d—¼ˆñÏendstream
+endobj
+128 0 obj
+6288
+endobj
+129 0 obj<</Type/Page/Parent 125 0 R/Contents 130 0 R/Resources<</ProcSet[/PDF/Text]>>>>endobj
+130 0 obj<</Length 131 0 R/Filter/FlateDecode>>stream
+xÚ+ä2T0
+ár á
+ä
+endobj
+131 0 obj
+31
+endobj
+132 0 obj<</Type/Page/Parent 125 0 R/Contents 133 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 4 0 R/F8 7 0 R/F9 8 0 R/Fc 9 0 R>>>>/Annots 12 0 R>>endobj
+133 0 obj<</Length 134 0 R/Filter/FlateDecode>>stream
+xÚ•VÛnâH}ç+JyJ¤Áksgކ܄4™eŒö—Æ.ƒ'm7énƒØ¯ßÓmÈe¢AQ¬´».§N*ç¹QˆŸˆú-j÷(Σy㯻!µBš§¸éõ;4O.#šÅjÃWó_þ6êW·Í^´ü}Ñ8áÂfi ›©¢2íPÕ¦­^àCÍ×™¡Xi¶*µ7¥\bÅ9Üi#EA‰ŠËê¤Õ6KØ]3­Jü)³ÇTiJxËRm¼(É
+‹_QÄL*uù‘5jW
+5vvÛ£LP¨U±’Pëøv~×O&Ы¨Ì—ÂdÕdøqrö¾Dú¥–¾ z.¹DR‚ ßLÇw‡Ë§Än;…..¿On¾Ðôî:ŠúÃÅÕšAÞ¸`c0®4’*~‚Ùìaän]†o›Í /ÙO®‡ŠÌèF½IiÊÓ8”µËìš4£…8¤e»f™Ùý‡üˆ$1ZvJ?UÓR«q%;eì,ÖÙÆkbã_d¾°‹Éä“Þe|"åïÁæo°¤„¼2!±ÙÈzÓ*‹„«±ü1ªG×cYV³—ƪ<û¹·õ®Q)Ýÿx¤ûõúâ2.µÆv“{ª€ªô½U7膋«7x X£YîÚ”fØ"Óñ"– ‘ÒÅÂLª*TÑ<¡¬¦Ô¼_ySëW^›n+ú–ÞgûþeÕÃHé•(<-@ ¼¨S%¥Úù)e¯ óµJ׉Z7–‡\‹V¯O¯‘àÓÕ<ýxÅG„º>òBPšrÊh
+endobj
+134 0 obj
+954
+endobj
+135 0 obj<</Type/Page/Parent 125 0 R/Contents 136 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F8 7 0 R>>>>>>endobj
+136 0 obj<</Length 137 0 R/Filter/FlateDecode>>stream
+xÚ+ä2T0
+áÒw³P04TIS041׳P072PIÑp VpÎÏKËL/-J,ÉÌÏSðMÌKLOÍMÍ+QÈIÌÓ ÉâÒ…èÑ…j2‰™˜˜é™)€ù†
+ÁÉù© Q×®@.
+endobj
+137 0 obj
+117
+endobj
+138 0 obj<</Type/Page/Parent 125 0 R/Contents 139 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 4 0 R/F8 7 0 R/F9 8 0 R/Fc 9 0 R>>>>>>endobj
+139 0 obj<</Length 140 0 R/Filter/FlateDecode>>stream
+xÚ­–Moâ0†ïüŠ9v%HI r£PZVEÍ’´§½g^%6k;jù÷µ>Ê–JZq
+äN5…ÅôÊ\ÚT`º}Äžý;{p¬|aZ£YÔò=PéÏز”•ÞŒp²Dˆ('ü˜o÷;UžŸÂ§ãñ;x¼Q ˜r2#aŒŠ-¹ý¢’­µ©#gVÂ{݉ˆL¿‰0L Æ™Ò&!•Í¥$ùüãvôo =šBGR,%)
+k‚³£Žç§Ð1ÒR2½1æ] ©Ïà&Ñ)n‚ê\‹ÄOŸôïIáWÚö<>}6TëíÛÍo«œnÛëW3À£^¡Ü±úÏæ‚·Ž†X à;'x˜Š…1ŒP:vÉï0;]ﳃ Ë&BDC¼FÊ2FÝ|7÷Åèz¯V??¾,Ͳ8¬Å&$+&S¸M™«ÇŸFÑ
+endobj
+140 0 obj
+693
+endobj
+141 0 obj<</Type/Page/Parent 125 0 R/Contents 142 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F8 7 0 R>>>>>>endobj
+142 0 obj<</Length 143 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
+143 0 obj
+122
+endobj
+144 0 obj<</Type/Page/Parent 125 0 R/Contents 145 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 4 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+145 0 obj<</Length 146 0 R/Filter/FlateDecode>>stream
+xÚ“ÍnÛ0„ï~Š…N ਖìÚNoMœ
+‰ð‰)Ö`‡Ê—?b]¶ê.–YšÇÊ4ƒ0È6OP8ã¹ó‡údÙXŸ/Óxó ã-Xí G¨ƒ†m™”°Gè%ãXë÷ÕŸ+¹6m¯U%TNƒk©I×îÀ ÆZûdv Ý"µj,U}RÍæÔ÷g‰å-V^¢I¦H±ç¾·ôOïÏ¡Ô`°cB‘^×3'ö4ÚA¸´¬ÐÀ÷ÏÛ»8ï f§pœP±í¸‡ÒîD9ÒZÑn™aÜ¡±@:UãÚôØÅ,]`s(NÁ û&ÑâÓiVšû`\ ª*¨µé˜‹' ZnÄž4ÇǾGbûWS¸Öò7|ð´]h·ÿpN ª7L«WqùïaGë½
+¨ƒ¿\+g´]Ç#ÉpO1Pv„´,ƒëäìõ®HÈ—L¤#¶N$Šÿ$ƒÑ’I©&ä‹t™ƒŽ€Ô‚Çõ,p¦B«#©3^VÛŒÄôÄÉwÅ1¬"Ï¥¯H6J†ŠC+ö¬GC¹ åÛíøBI‘¥x~ã'mð彚ÉhÓúH|¾ÊGŸþö-/Ö³ô’Êèýûp¾)'_'¿{žMCendstream
+endobj
+146 0 obj
+544
+endobj
+147 0 obj<</Type/Page/Parent 125 0 R/Contents 148 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F8 7 0 R>>>>>>endobj
+148 0 obj<</Length 149 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
+149 0 obj
+118
+endobj
+150 0 obj<</Type/Page/Parent 125 0 R/Contents 151 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 4 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+151 0 obj<</Length 152 0 R/Filter/FlateDecode>>stream
+xÚVMsÓ0½çWìpJg¨iÒÐn¥À '  Ã%Å^×bdÉHrBÿ=oe'i\\˜~%Îêí¾·oWý5™Ñ¾ft9§ó ÊëÉûåäͧkšŸÑ²Ä'— ZSüò®]¦;nœôÕ»œCÐöádù'4›u'NçY:sC÷®Œ[åyxx5}u¿¼{µ:¡P)chÍÚu­cä‚xÃþ‘¢®™µ=9O¶þðf‹Ôø°ñ µ€´³ól.i·:V+¦Ûï_ï)ô%dû|ªçQW­´øAZmX¨¨Ö*0%g™\™°JgŒÛ‚-…¨"‡w’tþö@v–‘<š‚é@¹q  &wuc8JæàLµ³ƒ’磇]ÇSyÔÄ,Žcè(,VSË[’‡PU…xe Bé|­ä©ÒFA²Õ‰@œ>¥;sßǦóÊoÅÏÄã&dc®ivÙ;fq–]uÅÎèÖ(تԹÚ1þ«¹~T, ‹GuCÏ9C44R݆(Eå=&žªø÷¶ØÎŒ·õŽµ °RLh+ÖlãH?¸¼•;Ù{çGZ÷ÝŠÀ„ƒÁ¨Ôf¬‡Ã`¼PÝKâ¿ÃÇ·1ÏÛ -Bîš½0ýh¡¯®5PÍ'B°±¯Ó¼¨f á\:ØX«¼Â‰1Ë?µ„<T«<Â,õˆl7Mc4v€‚ïú[<ƒÇvK¼†›Óçú·á¾ åIšä””éH±çN|"–ì6‘¬ºÓ®´Õ4`j»qf³ÛÞõBÎ0±Ð*$©ÕãàécçÔ­í9í‡ëòiì,´Ò¦&†]›rŒ¬=ÆÄqŒZ¥Pßš¦{¹¹x- gû
+endobj
+152 0 obj
+870
+endobj
+153 0 obj<</Type/Page/Parent 125 0 R/Contents 154 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 4 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+154 0 obj<</Length 155 0 R/Filter/FlateDecode>>stream
+xÚuASà …ïüŠw´‡`ˆ ¤W;õ¦SGü4'%i«ÿÞ%ñêÀ°3û¾·oùb5Un7²GÃÆ“¢´æ
+¦¿“\â%ÍÁ‡ÎÎ!Åù PBˆ¬Eæìp™\FʸºØS âbu=ng1ãÃwìÕ†ÁWøàé3'jŒ¸Ù ¥a=Ž?ëP—sÊ| oÿÂ…Ô¼…jé¡üÝûá »}8]ò²*žm´'7º8ã0Øeõj5Uº©‹©-=)$oPÿ÷Û½a¯ì¤ŠYƒendstream
+endobj
+155 0 obj
+214
+endobj
+156 0 obj<</Type/Page/Parent 125 0 R/Contents 157 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F0 3 0 R/F4 4 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+157 0 obj<</Length 158 0 R/Filter/FlateDecode>>stream
+xÚµVMSÛ0½çWìpjg°k‡HoÀ@§—NJ\®Œ,¯1¶äJ24ÿ¾+ɉ‰óÑöÐpHlí¾}ûöCü¥Ð_
+Wc¸˜¯G·ÙèÓÃ Æ d%L¯&.a¡JûÆ4Â#VÈ šÙ‹·L¯‚e4Mã±·SxBm„’ð­­sÔB.ƒõÒ´³Oc|÷c¾€–ð€]iĨaÚÂk =
+䢦ÒWñŠñ
+Óæ‘Y‹5p5:²ŽÿN vRÄðµ$ï€?Е¥ã|·ð±” JðóBg2t¼«ö
+endobj
+158 0 obj
+837
+endobj
+159 0 obj<</Type/Page/Parent 125 0 R/Contents 160 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 4 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+160 0 obj<</Length 161 0 R/Filter/FlateDecode>>stream
+xÚmP½R„0îyŠ«´8$¶Þh§sz¨õ,Gœ`6Ìoï,,œ¤Úý~÷+QÉS°+ãoÇä¾Inï@UÐô²)«*-¡é®nÓ^É2]7Ÿ‚)@©³ÍKY
+æc 8h{†N$ƒÖ“¡@ðܶÄÜÏÆ|‚¥ tšƒ×§9hgAx&Ð ­'Œ„Þ»Â@ÐÎÞ“ °?Š^·DcµKóh¼a‹.lRxv0y×Íí"é×ÈÀ#TP[`ׇ zm KRëLÈ,¦Ñ'ј¼–ÀҌӵ}ýÛ^UZC½¤Ø¿Ž°w¶×çÙãbÿ„V*1úÁ ÜíJÙVyI*‹Ã"¯Ódÿú¡I^’Zƒµendstream
+endobj
+161 0 obj
+285
+endobj
+162 0 obj<</Type/Page/Parent 125 0 R/Contents 163 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 4 0 R/F6 6 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+163 0 obj<</Length 164 0 R/Filter/FlateDecode>>stream
+xÚ•ÁrÚ0†ï<ÅÛƒ Ò#q q‚:vfzòâ¨È’+‰Rúô• ¥0S+ÁãíúÛýW¿ÌN=÷ a܇ÁXÙyÈ:wÓ/ÐïA¶v‘ÑxYþi3¡Œ¡zÿ9ûÞ$„ãCB0
+»ý&¥B†º4‡Œ„aá¨AÔm(Ñ!4< …ƒãÓÀTYm-jT[Z`÷œ§WTS!P´S”9—( òŸõœZ
+¥ÒöJ'{Å-Pw奿 ¯Ð×½’ìè¾VQn%gÔr% 0WF¢€Ú¢»SPiUhZ_9ƒšÓ©»A“QlƒÖ§ÊµÛ¨’®}¥7-꺗žœ—‚a¯{pE&L+¹÷c’FIâi¤tã`nÃRKeNu‘ÊÖN}"ݽlúqß}êž
+ô¨Ž^IÚ^*r
+êõ%ùDsiëÁ¦{c±ô0ã4º#íИ¤Ë—ÿ4ß@m<äiæáN¹@È4•f5MYÅ”ðОH0›·óžp'ÐÚ€P¶©iZ½qf`~<’~4‰n`µ»aH«•¸¶ÄóJ<«Õ5ø$ΦíðÆ\î@, .ÑÒ#£fS¥™—KÈؓٮØÆ$]¾GlN'ÓXê‚Jþ» xØsòØΞ;姱>R,½¨E²ˆÛY‹­¨_P9§p‚rñ¯wÏ°×iÄmó-¶">ÅD›2Í+ûO7šfÁ?ÇtñpˆÞŸG_‹ÿ§ÃñÐ-÷ê@Ö qÖùÚù÷ªªendstream
+endobj
+164 0 obj
+573
+endobj
+165 0 obj<</Type/Page/Parent 125 0 R/Contents 166 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 4 0 R/F6 6 0 R/F8 7 0 R>>>>>>endobj
+166 0 obj<</Length 167 0 R/Filter/FlateDecode>>stream
+xÚUÁ‚0DïýŠ=ꤥ®BÄ Æú R-m,HâßÛŠÌîigÞtúD?R¶PÁÑ®¢€1ð2,§qü¶¹H7KµGÑK(´m[~÷nösG>!ÂiL‚W¼Yä5ìOujVBC¥´î„;ŸÝ8;ÙÖê…Ë×{_€á˜°¼6(­éTÿrbRÖ@-Œ/4H3A£… p„iæ¡(#I€0 Gš†Ç¿‡œ´õÿpï 9:£bîGàendstream
+endobj
+167 0 obj
+194
+endobj
+168 0 obj<</Type/Page/Parent 125 0 R/Contents 169 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F0 3 0 R/F4 4 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+169 0 obj<</Length 170 0 R/Filter/FlateDecode>>stream
+xÚV[sÚ8~çWœñô!mÁØØ …¾,%É”™6Ó]H²y¶ÀÚµ-W’!üû=’ 1Áf
+aß|ƒY¾¿éï’ šÑ\ÉËQ6ß·²½ÁÐ5ÒË„J
+‘Õ5 (ß²˜BLa)åk.2¢Ï’—"¢°Æ3°JÛ y 1JmÄÊF<Wøàj èÙÜÁ›çM‰>R–S 2!i
++
+¤(R†½336gŸ?×]ñÅ°c*Ù㯅k£ƒ?ª¢ =÷‹áÄõaa•ïµòQ0¬ÿð,(ˆ¢$ÃPZ9›"¨(9Éêø}ÌNB‰¸
+ŽÕˆÑçDéÌX‘v¬^Ä‹½`›DSé€ÑSή»f×x:¦¥ÂfÚéÂ…eµ²øPígA·ÌPYoBVk‚ÕÞX›=-Àù0?8 ÈfbIô`¬9Ä¡Ñ
+ûŸæ«þ€OàïZïìä\à–ÊH°BUÉ5È«6—îUfGæüñxÔxž§c¸#rŽV;‚Iø%x\FJvÍ0â²É˜ÀI"¶4¾Îõqä™$H%J››.H,=;ýlêt{–ØÄšæ ™DÕdÒÔàØÆHHsƦÞ@QEq:è´5™»§Xµ$­•WJv.ÒÎ-[•†wm¬Ä@,-Æ./•¥q“ÍZ¹óc>»{XܹêU9°K˜n£„—il›wE©®4ÓÛv¾6hËæë·G]—“¦°¡b’‘ š)RJ¤-œŒMÛXS“«òª?S¥ò‰¹ àDZço=Ó¦×ìºM: ýЇ)á!<1ºƒ[Á¶Øð‹’)Š7rئú§é~ÇyÜ…ŸDìSÅ7 †½ÀÇQ÷¸˜^ëÇ'ðrxþËGFAo<ô‚Öâ5>¨,dOO‘?õÊåbÓ¦
+endobj
+170 0 obj
+1054
+endobj
+171 0 obj<</Type/Page/Parent 125 0 R/Contents 172 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F0 3 0 R/F4 4 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+172 0 obj<</Length 173 0 R/Filter/FlateDecode>>stream
+xÚÍU]oÚ0}çW\¡I- dÐõ¥“úЮSÙReœâɱYìØ´ÿ¾ë$|Œ¬íº§BŽís|Ϲ÷:ßZ!ô a0t?žµ.Æ­ÞÇÂÆ ­ #ÿÆñaÊ LHm,(d9ØU :)‡‰xÚ%|
+-÷'“=ZÉÕZ•§“qÉqÉBa /äΑÎÕË/÷ ”Å<aAŠiÎò$Û SÁSÈØŠ3¡Ö"æ9&b Çß;XêæœtJx17[5ë§RQ~ ~Ö/©yÓ®¹d9yÕ6–YÁÛÀ(ß´°=»„*Æœ°9ºú² Wb.h³2ëG[GY+ÓihÖ®?â®í² üáº,úp¥y‘¡rÑiõlu\3rwíöFÞŽÑ\gŽRd¤„tž‹)C™a¶êŒ5<ÖhºåÔ\#¦”l¡æ…¥´fÂ6%‹„\[M:ÝÒ¼ßpº°øÄdAÎío¥!˜9rA‰*ÑyVª¤¾Åã·téAíúäpÒ9
+åú…‘›ŒÂÁ_’×ãÖçÖ/x\¡endstream
+endobj
+173 0 obj
+751
+endobj
+174 0 obj<</Type/Page/Parent 125 0 R/Contents 175 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F0 3 0 R/F4 4 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+175 0 obj<</Length 176 0 R/Filter/FlateDecode>>stream
+xÚ•UÛrÛ8 }÷W`üÒ8SË–ãK’¶m¶ÙÉöéÔ»OžÉPd³¥H-I9ñîôß Š–/RÜ‹=c‹x€BÿvbÒ7†Ù.¦Àóλygp;„+˜g~gz9‹F0OÏç½ù—Îúñ0ºôp/¸dÖ^_§úÁ­„]œ-z/ 7:/J‡°7öŸ½¨u¼÷A;´× ´Â¶û ±ÓXfR3íÏà>RNwjÆ"ú ¬™,ñ% £!¼~CyÒOLÏ­M~!±ñ°w{¿‡ƒÅÙïi‹Gn-Äÿ›¼ ŠZ\Á +"ÄÍ¢÷ªaýæ׃Û+ˆg¾G}jR¼µ½‹Æð3‚%íÎoìýa2$sðŠbøÀr¡–Ái q¼uMƒÓ…[ƒ¥Ô “`¹.ìŠI g…pLŠÿ0¥t»s*S}²ûhÍÜñZØÏŽ9<ØDÇ£E¯fyä6_!h%7€O '´§+5)åAŽoþ¾ÿ B94ãR$†™MñzÏd%ø
+òÒ::¹*P#„FðÂ`&ž ËËÂv=-ÿФöŽè&ðg3î.Yƒ…¤ SH6•Š{^ØÌ,Ë•óÙ¢BR-iÖZA@µNN¶HjÞîô匴_y•*¥‹Àµ¡c ºGD*ƒ6©õ<}}Ö;BÔÈÌܹæûVm¶¡(›cž!K*:«KC}ÊÄa#S¤ÛæÏt- DðnÔðÅE4­<‚?4¯*Åê*>«ã÷ŒZ^çõL4]%„3¤J’ŽB`*Ý;Š<ÇT´˜ié«YÑbÀuîÃCBÜ¿6J•¢åF$tÁ*Öñ¯CšûñÚUI’vá¨ö¯Âäò·€—Æø0¾*t'²p Zƒ%@ôçÇ4ÆÄÉI2ù¦øá™ü|Š¼¥":‚ÅòËðË¢ô½è>¸.°¢@òJÃ@ E`°õ¯µìcÓiº®UmvËýÌ9!°Éï ¬âØ­Ø›ê8RQ‚­2÷ÇRÌNÉâ¹w®C!ýP~™¦‘c@W“Òžxyn/·éÇqL–éÕ(Ø+ˆ­2±,MÅþbŠ-±bp/Yžgäߟ†Ï¾kƳ1™+[<ñïçOï«„€Âendstream
+endobj
+176 0 obj
+847
+endobj
+177 0 obj<</Type/Page/Parent 125 0 R/Contents 178 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F0 3 0 R/F4 4 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+178 0 obj<</Length 179 0 R/Filter/FlateDecode>>stream
+xÚÕUÑn›0}ÏWå©Á ƒt}Z³îmS§²·J‘1N`ÌÀ$Š¦ý{¯ Iºdm×Mš4!|}Ïõ=ç+ß = ÓÀüD1¸Šã÷.-M$˜…îQr¦·•LäY©!ÚªYè4£Eúò<úB¨ °ÐÀÂ9¾çÎ îÊ p«ëV趖Í>Ñ7‰œ×žë÷i.ÃG^dåªKòÁXŸ4 º¤·yŽfW %/dƒ&å%rµ‘µàÄ&Ó)Ú2‘u#J»z#e‰ª“¼L0\P!xUIÊK t*A ¨¥ù4=ÐÉlÚ±7gáîlhIw-Ø
+¯@!®I-Ü»óªÓ)IÑSàm!KÍu¦ÊG_s‘> Ü‘MùZ‚C¨Â@œ+ñ-’Œk™o‰ï’x[FÖr–¦V!‹XÖ{ÕŽh&}S¤HV‚ ’/á¥è%5õÖ¼Îxœ[õiT¨Tž‰-x¬ÖòMGã`gbIŒG»czO`„ˆ„|@ŒÔ2æŸonéCż†ªŒ2{‚EzŠS†q”ùý¸–u³Åtº\0oëÚ(ÛÐt¤mÌ䜞¼Ã“žÅs}ŠÿñôÅ 1ÏyÓ<ykÂß»5Âúã³3¾­²÷ünõ¬Ý×ٽëõåVïp9o™þ_8½ëío\ÞU8˜Ü®ÿÏúI3Æh# iÛm«Ã\•ËlÕÖÖøÀK¾’vÖ79·Fq˜R¾N<b ú,t§ð~ýs > îÄendstream
+endobj
+179 0 obj
+572
+endobj
+180 0 obj<</Type/Page/Parent 125 0 R/Contents 181 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F0 3 0 R/F4 4 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+181 0 obj<</Length 182 0 R/Filter/FlateDecode>>stream
+xÚµUÛnÓ@}ÏW }JÚƉ“4n(}HC• j„©ªµ=Ž—Ú^³»&Ä¿3k;ܤ$”§ë™3sœ/ ºô³ÁéA~Ò8s#°pCò Ç‚4Ϭc˜ˆTi–jÕr?—Qƒ2ª}ÔµU”eÃ%Kx:+ƒ`ÛUPoXãüª¤,A*bdôò,Cé3…0ç:‚< P*_H È«çˆ)Ì… L›{îË‹ëÛÉ›Ëkw|éîš~ÔÅî[=Ó…Ücwà ¨}kÚZ†<¥Â¡ #„Éû«kà©F2!æždrI®45Ÿñ´ÄÄÒ5ÊZÏLRÉo›©u[Kë6\U¥%Gî"#d˜æ J¦9aÞàH‘kaŠ_QB&”â^Œ P€˜—óP>øúw´ ðÅ€¾H2£´îí¯ß_m™&y.|ê›ê¢ñÎ5NDb‚À‹…·„ÇÉp¦1^Ž2?ZíûiY® #SN®Ý+Š™I—dvéÔÈô£öX“ê¾<€Î>¸W %[À~gWn2`™ÁôŽŒŸ'+j
+´ ÂAå<³Ft=>pý#ºþ?cψ@‰\úf-VôåšÇü;–œš}™Bª¸;:¿0Oýòh×*_r:³Y‘­ÎÄC³qzÓ$F6‹Ú‘2˜I‘g B •h,ZìÚKg¿Îé>Lbd¥bŠ|`Òpé!5$Ùb©˲îeÖÙ®=ÍÝN›N¡{žÑC"œ6‹$Þ,-¿y>uo¦­iËd¬´´>ƒ2ˆßPiR{~{ñüÕùÉÃh¶Mþq]È«ÜX:Cõ£âÑcÿl
+endobj
+182 0 obj
+746
+endobj
+183 0 obj<</Type/Page/Parent 125 0 R/Contents 184 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F0 3 0 R/F4 4 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+184 0 obj<</Length 185 0 R/Filter/FlateDecode>>stream
+xÚ•V]oÓ0}ﯸÊ$MÚª[ 1Hƒx`¹ÉMkæÚÅv ´ÿε®´Y˜èª&±ï÷9>ÙÏ^)ýe0»o¾êÏzý7ÈF0+ig|š%˜Γ ÝLíÀ£Ù`“›x8IÆw6 ƒ·²@i™åJÛÑÎv0NFÎö¥«a.T~m
+²ó ׂžŠýÀÙCMÎÕnke64¦95ÊD“ÒÏïi`
+7?‚(øñýÞFM“S÷ ädŸAÿÞ)µvñ7LÃqÿÀðЯÜŠt —ZQ£†æ§ædÃD…ô¤ÕŠ ´
+&“I’$mLJ’Q@ W8<‡ô 8<ƒ,MýÝãÇWG-ëv“
+,Y%ln­‹V! AÄ´f7_ù·*mL\<˜Î^Î.¾¿}ýîžÞƒÊø=„œûÌ5²ëöVÓYwb©(µ\üoäÛ}¡Û#Gº9Liæº[æ* ^;õ†€«•.hâ8 iž
+endobj
+185 0 obj
+857
+endobj
+186 0 obj<</Type/Page/Parent 125 0 R/Contents 187 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 4 0 R/F5 5 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+187 0 obj<</Length 188 0 R/Filter/FlateDecode>>stream
+xÚíS=OÃ0Üó+ÞƒMì¸ù@Ê
+endobj
+188 0 obj
+344
+endobj
+189 0 obj<</Type/Page/Parent 125 0 R/Contents 190 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F8 7 0 R>>>>>>endobj
+190 0 obj<</Length 191 0 R/Filter/FlateDecode>>stream
+xÚ-ŒA D÷œb–ºhªÒµÝ™Tû=
+endobj
+191 0 obj
+135
+endobj
+192 0 obj<</Type/Page/Parent 125 0 R/Contents 193 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 4 0 R/F5 5 0 R/F8 7 0 R/F9 8 0 R>>>>/Annots 54 0 R>>endobj
+193 0 obj<</Length 194 0 R/Filter/FlateDecode>>stream
+xÚÕ›MsÛ6†ïþ<¶‡2Ä óQgzh’ÆjîŠB;êXbBIÉøßw]€Fr3ìÌØ‚W|,¾Ä¾ b™ïW¬éàkÞˆ¾Ùì®^­®^\›†wÍê¶aÊ4ý ›Õ—ßVëÏ÷c3Ý6¯§ýqÜ¿¯þ»úsuÕµì7f¿}|kM/T«›]£[N¯ï›Ë>{û|×¼¸V cö¯ÙÃzx+ü5Ö<ŠÈ]ã~$|Œåf3}-ˆ+€Ã/Úê¿Ø£ç 'qÞöp⊷Štâ/®%É×±ç"VÇ6£dÙÁ±Ìj#)N¨¿¾ÀUßÞn7ëãvÚ[¨àˆŠt-i×™V.Ú¹¨UŒu­DÅxI1âè„I8ö¤›‡ÃqÜe&r8älùˆ~*»¸§½ÿ1Î?¶ãO‹“¬5U_P\™Ô$ÅqWf€±S\÷ÖÙùœ€(N¨7Óæ´ƒiú8/F
+éõ¿n2—$ Wuö¢Ä,ŠËgÂbÖ}ÿ´—¥¤ºÁœqY…KS§uÉœ=Gwà* ‡ÂDzü:αÃáÖ›­'0ŠÌ‹í3€W‘"ol€ïdîõ%«`.ÅEÁ(Å5ä~¨c\oaùø(&:t×'wÌ¡°ý½Þ¯ïF+·[ƒ˜¶Êµ*L÷N ·ºIéM¦“h²¢l2D„Â%S"…‰ôf;›ã4?<΋vòVûHqÞçÓæxšÝB[XûyúËQP_š.X<‚ÙHÃÐBEÑl<GÛ„Ca"ÝL§y“™ýÑñv‘c— âD³™äœ†™ºf~IõaƒHõA£—ŠâjÐs˜sˆDqBÁêíöî4»˜YF ÖÔ*¼!@Ïì§"ûQYs—Š-掯/˜»Tn…+óæîQRÃ=G¨c¬æéô9çïÑÑð©$LäEaŽëÉoÓœYÁGG[w’àNéǸc}˜'X—¶û;W™ë0§v(ä‰BmÀÁ¥Ú …F–ÅÚà‰Œ‰…ŸdŒG½¾_ƒZñ=~ßUU_KÒq»Dè,;qpS$Q\=—M‘’bp†Á”Ý RŒq4=Y6eâsΡ0÷ž<Ûõ‰%ûºÄúÁ„ÑaOA0a nØ’ÌsX§ÀÁÅ õn:›`.FUšél-[ö$ðõ…Z$Ž¤òÕÈÔ8§a”(7ÓíñçzΤ
+çíýt8¬ç'›iYííHVèìðzi*'.Õ®©{õ2[OfŸXð~ö¹°'­Æyç
+1¯³—¥ë©)æN«O›b¼§¾ÓËl=‡Ú™l1 ˆâµ™§ýŠ&em‹G–ïhqM(fW8¸”¯’šQ¯²ùʬÎpöœ×Ó¨™ýŒ!‚ÙÛê3ÆåÒ"û~ÚÎãÒ"èU[ÏÖ+tf8§~˜:8¸˜êœ:O¯
+©.¢kp'D
+¿¸mŽ·Ä,6n /´hÛ´­iã4£?ÜÀ)1ìB»&ÆßÀaL-(P=§{t0nšu)Šâ{·Þ-ë¡VØE‰ºÎY!ž…Du }ÐJÄK‰õv1 (ÎÒ' ¢þêS)UJ`¦±­æ’ ¡à0¾Y(7b¡{Ï•P(Œ]¾ÓÞmîjy^â\+™ŸIl .š½Ø83ɶ¼ZÅdó$L¶Eq‚U—l%‰zìSáYôQ›ÊJÔ3_x1Ù< “-AQœ=ú0Ó%›,$›üÂXÐ J6©½[‹RºÇØ-¨„Caߎ_§/¸3ÕWøèMq6 ¬ÑlÊ[·('‘(ábÅ öìŽc¯ Ï‚G­.+_*¦q(ÝbÅùsK¶Ž:e.%º¨­e“­ÓÞ­e)Ùˆcl77áP˜HŸÖóÖ>} ÛxY[º1•ŸJFë62qn£¼qËbª3-æ`”HÕåYAÍÂ:[³d™­¹÷mYL3Â`–ÅŒòô‘±²¬ðÜF¯ý~…À×ÑSÓÆû³*¤A´]>Ç fõð s‹«JŸ"-Í!¥‚U»×a)J/UL/‚`zÅŒ>Ëä’,¬«%K–Õ’{ŸVÅä" &WÌÁ(‘žQrqm®èäyn¼#÷¥ ímÝ$ fyN¯Ú{³Þ~°pŠÝò„4m3)æÌ‚v‚ÝãÒé3^áá¬æÃýzéa¯ýv/¶$ä?Wÿýž¡endstream
+endobj
+194 0 obj
+1953
+endobj
+195 0 obj<</Type/Page/Parent 125 0 R/Contents 196 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 4 0 R/F5 5 0 R/F8 7 0 R/F9 8 0 R>>>>/Annots 67 0 R>>endobj
+196 0 obj<</Length 197 0 R/Filter/FlateDecode>>stream
+xÚÕ—ÁŽ›0†ï<…í!Ä66¶¯›6=µÚnèÐ]¥
+¡%DUß¾c{L¤Äí)Éî
+ivàƒù=ÿ~eŒPøe¤(íßk›=UÙ|i§¤Z& )• ÕÛ»ªþ¾kH·&‹n?4ûáð¾ú‘}¬2šS س‡—ODñœ’²¹&-1:/1Ø‘•¥Ÿ]ÐoÈ|)cö~ŠÃ¹%œ 7|ÊËœ‘‹w‰À-a…8Ýoƒy„}©Ûí~caE‘+¨þ—ßð‡• ù8‡Çò¹
+êPµ´DŠ¼À
+ó[ v®Ÿ˜¹›~búbqqS„Â:ú
+¦¸«k»EÁ¾fˆvrendstream
+endobj
+197 0 obj
+700
+endobj
+198 0 obj<</Count 9/First 199 0 R/Last 252 0 R>>endobj
+199 0 obj<</Parent 198 0 R/Title(Table of Contents)/Dest[192 0 R/XYZ null 756 null]/Next 200 0 R>>endobj
+200 0 obj<</Parent 198 0 R/Count -3/First 201 0 R/Last 203 0 R/Title(1 Scope)/Dest[132 0 R/XYZ null 746 null]/Prev 199 0 R/Next 204 0 R>>endobj
+201 0 obj<</Parent 200 0 R/Title(1.1 Identification)/Dest[132 0 R/XYZ null 694 null]/Next 202 0 R>>endobj
+202 0 obj<</Parent 200 0 R/Title(1.2 System Overview)/Dest[132 0 R/XYZ null 613 null]/Prev 201 0 R/Next 203 0 R>>endobj
+203 0 obj<</Parent 200 0 R/Title(1.3 Document Overview)/Dest[132 0 R/XYZ null 408 null]/Prev 202 0 R>>endobj
+204 0 obj<</Parent 198 0 R/Count -2/First 205 0 R/Last 206 0 R/Title(2 References)/Dest[138 0 R/XYZ null 746 null]/Prev 200 0 R/Next 207 0 R>>endobj
+205 0 obj<</Parent 204 0 R/Title(2.1 CUPS Documentation)/Dest[138 0 R/XYZ null 694 null]/Next 206 0 R>>endobj
+206 0 obj<</Parent 204 0 R/Title(2.2 Other Documents)/Dest[138 0 R/XYZ null 495 null]/Prev 205 0 R>>endobj
+207 0 obj<</Parent 198 0 R/Count -3/First 208 0 R/Last 210 0 R/Title(3 File Management)/Dest[144 0 R/XYZ null 746 null]/Prev 204 0 R/Next 211 0 R>>endobj
+208 0 obj<</Parent 207 0 R/Title(3.1 Directory Structure)/Dest[144 0 R/XYZ null 694 null]/Next 209 0 R>>endobj
+209 0 obj<</Parent 207 0 R/Title(3.2 Source Files)/Dest[144 0 R/XYZ null 600 null]/Prev 208 0 R/Next 210 0 R>>endobj
+210 0 obj<</Parent 207 0 R/Title(3.3 Configuration Management)/Dest[144 0 R/XYZ null 533 null]/Prev 209 0 R>>endobj
+211 0 obj<</Parent 198 0 R/Count -4/First 212 0 R/Last 215 0 R/Title(4 Trouble Report Processing)/Dest[150 0 R/XYZ null 746 null]/Prev 207 0 R/Next 216 0 R>>endobj
+212 0 obj<</Parent 211 0 R/Title(4.1 Classification)/Dest[150 0 R/XYZ null 582 null]/Next 213 0 R>>endobj
+213 0 obj<</Parent 211 0 R/Title(4.2 Identification)/Dest[150 0 R/XYZ null 357 null]/Prev 212 0 R/Next 214 0 R>>endobj
+214 0 obj<</Parent 211 0 R/Title(4.3 Correction)/Dest[150 0 R/XYZ null 223 null]/Prev 213 0 R/Next 215 0 R>>endobj
+215 0 obj<</Parent 211 0 R/Title(4.4 Notification)/Dest[153 0 R/XYZ null 738 null]/Prev 214 0 R>>endobj
+216 0 obj<</Parent 198 0 R/Count -4/First 217 0 R/Last 220 0 R/Title(5 Software Releases)/Dest[156 0 R/XYZ null 746 null]/Prev 211 0 R/Next 221 0 R>>endobj
+217 0 obj<</Parent 216 0 R/Title(5.1 Version Numbering)/Dest[156 0 R/XYZ null 694 null]/Next 218 0 R>>endobj
+218 0 obj<</Parent 216 0 R/Title(5.2 Generation)/Dest[156 0 R/XYZ null 267 null]/Prev 217 0 R/Next 219 0 R>>endobj
+219 0 obj<</Parent 216 0 R/Title(5.3 Testing)/Dest[156 0 R/XYZ null 186 null]/Prev 218 0 R/Next 220 0 R>>endobj
+220 0 obj<</Parent 216 0 R/Title(5.4 Release)/Dest[159 0 R/XYZ null 738 null]/Prev 219 0 R>>endobj
+221 0 obj<</Parent 198 0 R/Count -2/First 222 0 R/Last 223 0 R/Title(A Glossary)/Dest[162 0 R/XYZ null 746 null]/Prev 216 0 R/Next 224 0 R>>endobj
+222 0 obj<</Parent 221 0 R/Title(A.1 Terms)/Dest[162 0 R/XYZ null 694 null]/Next 223 0 R>>endobj
+223 0 obj<</Parent 221 0 R/Title(A.2 Acronyms)/Dest[162 0 R/XYZ null 508 null]/Prev 222 0 R>>endobj
+224 0 obj<</Parent 198 0 R/Count -9/First 225 0 R/Last 249 0 R/Title(B Coding Requirements)/Dest[168 0 R/XYZ null 746 null]/Prev 221 0 R/Next 252 0 R>>endobj
+225 0 obj<</Parent 224 0 R/Count -2/First 226 0 R/Last 227 0 R/Title(B.1 Source Files)/Dest[168 0 R/XYZ null 675 null]/Next 228 0 R>>endobj
+226 0 obj<</Parent 225 0 R/Title(B.1.1 Naming)/Dest[168 0 R/XYZ null 610 null]/Next 227 0 R>>endobj
+227 0 obj<</Parent 225 0 R/Title(B.1.2 Documentation)/Dest[168 0 R/XYZ null 523 null]/Prev 226 0 R>>endobj
+228 0 obj<</Parent 224 0 R/Count -2/First 229 0 R/Last 230 0 R/Title(B.2 Functions)/Dest[171 0 R/XYZ null 680 null]/Prev 225 0 R/Next 231 0 R>>endobj
+229 0 obj<</Parent 228 0 R/Title(B.2.1 Naming)/Dest[171 0 R/XYZ null 615 null]/Next 230 0 R>>endobj
+230 0 obj<</Parent 228 0 R/Title(B.2.2 Documentation)/Dest[171 0 R/XYZ null 489 null]/Prev 229 0 R>>endobj
+231 0 obj<</Parent 224 0 R/Count -2/First 232 0 R/Last 233 0 R/Title(B.3 Methods)/Dest[171 0 R/XYZ null 280 null]/Prev 228 0 R/Next 234 0 R>>endobj
+232 0 obj<</Parent 231 0 R/Title(B.3.1 Naming)/Dest[171 0 R/XYZ null 215 null]/Next 233 0 R>>endobj
+233 0 obj<</Parent 231 0 R/Title(B.3.2 Documentation)/Dest[171 0 R/XYZ null 142 null]/Prev 232 0 R>>endobj
+234 0 obj<</Parent 224 0 R/Count -2/First 235 0 R/Last 236 0 R/Title(B.4 Variables)/Dest[174 0 R/XYZ null 609 null]/Prev 231 0 R/Next 237 0 R>>endobj
+235 0 obj<</Parent 234 0 R/Title(B.4.1 Naming)/Dest[174 0 R/XYZ null 544 null]/Next 236 0 R>>endobj
+236 0 obj<</Parent 234 0 R/Title(B.4.2 Documentation)/Dest[174 0 R/XYZ null 405 null]/Prev 235 0 R>>endobj
+237 0 obj<</Parent 224 0 R/Count -2/First 238 0 R/Last 239 0 R/Title(B.5 Types)/Dest[174 0 R/XYZ null 304 null]/Prev 234 0 R/Next 240 0 R>>endobj
+238 0 obj<</Parent 237 0 R/Title(B.5.1 Naming)/Dest[174 0 R/XYZ null 239 null]/Next 239 0 R>>endobj
+239 0 obj<</Parent 237 0 R/Title(B.5.2 Documentation)/Dest[174 0 R/XYZ null 166 null]/Prev 238 0 R>>endobj
+240 0 obj<</Parent 224 0 R/Count -2/First 241 0 R/Last 242 0 R/Title(B.6 Structures)/Dest[177 0 R/XYZ null 728 null]/Prev 237 0 R/Next 243 0 R>>endobj
+241 0 obj<</Parent 240 0 R/Title(B.6.1 Naming)/Dest[177 0 R/XYZ null 663 null]/Next 242 0 R>>endobj
+242 0 obj<</Parent 240 0 R/Title(B.6.2 Documentation)/Dest[177 0 R/XYZ null 589 null]/Prev 241 0 R>>endobj
+243 0 obj<</Parent 224 0 R/Count -2/First 244 0 R/Last 245 0 R/Title(B.7 Classes)/Dest[177 0 R/XYZ null 424 null]/Prev 240 0 R/Next 246 0 R>>endobj
+244 0 obj<</Parent 243 0 R/Title(B.7.1 Naming)/Dest[177 0 R/XYZ null 359 null]/Next 245 0 R>>endobj
+245 0 obj<</Parent 243 0 R/Title(B.7.2 Documentation)/Dest[177 0 R/XYZ null 299 null]/Prev 244 0 R>>endobj
+246 0 obj<</Parent 224 0 R/Count -2/First 247 0 R/Last 248 0 R/Title(B.8 Constants)/Dest[180 0 R/XYZ null 738 null]/Prev 243 0 R/Next 249 0 R>>endobj
+247 0 obj<</Parent 246 0 R/Title(B.8.1 Naming)/Dest[180 0 R/XYZ null 694 null]/Next 248 0 R>>endobj
+248 0 obj<</Parent 246 0 R/Title(B.8.2 Documentation)/Dest[180 0 R/XYZ null 581 null]/Prev 247 0 R>>endobj
+249 0 obj<</Parent 224 0 R/Count -2/First 250 0 R/Last 251 0 R/Title(B.9 Code)/Dest[180 0 R/XYZ null 461 null]/Prev 246 0 R>>endobj
+250 0 obj<</Parent 249 0 R/Title(B.9.1 Documentation)/Dest[180 0 R/XYZ null 396 null]/Next 251 0 R>>endobj
+251 0 obj<</Parent 249 0 R/Title(B.9.2 Style)/Dest[183 0 R/XYZ null 731 null]/Prev 250 0 R>>endobj
+252 0 obj<</Parent 198 0 R/Title(C Software Trouble Report Form)/Dest[186 0 R/XYZ null 746 null]/Prev 224 0 R>>endobj
+253 0 obj<</Type/Catalog/Pages 125 0 R/Names 68 0 R/PageLayout/SinglePage/Outlines 198 0 R/OpenAction[132 0 R/XYZ null null null]/PageMode/UseOutlines/PageLabels<</Nums[0<</P(title)>>1<</P(eltit)>>2<</S/r>>4<</S/D>>]>>>>endobj
+xref
+0 254
+0000000000 65535 f
+0000000015 00000 n
+0000000227 00000 n
+0000001793 00000 n
+0000001867 00000 n
+0000001945 00000 n
+0000002022 00000 n
+0000002101 00000 n
+0000002177 00000 n
+0000002258 00000 n
+0000002316 00000 n
+0000002368 00000 n
+0000002453 00000 n
+0000002477 00000 n
+0000002581 00000 n
+0000002686 00000 n
+0000002791 00000 n
+0000002896 00000 n
+0000003000 00000 n
+0000003105 00000 n
+0000003210 00000 n
+0000003314 00000 n
+0000003419 00000 n
+0000003524 00000 n
+0000003629 00000 n
+0000003733 00000 n
+0000003838 00000 n
+0000003943 00000 n
+0000004048 00000 n
+0000004153 00000 n
+0000004257 00000 n
+0000004362 00000 n
+0000004467 00000 n
+0000004572 00000 n
+0000004677 00000 n
+0000004781 00000 n
+0000004886 00000 n
+0000004991 00000 n
+0000005095 00000 n
+0000005200 00000 n
+0000005305 00000 n
+0000005410 00000 n
+0000005515 00000 n
+0000005620 00000 n
+0000005725 00000 n
+0000005830 00000 n
+0000005935 00000 n
+0000006040 00000 n
+0000006145 00000 n
+0000006250 00000 n
+0000006355 00000 n
+0000006460 00000 n
+0000006564 00000 n
+0000006667 00000 n
+0000006770 00000 n
+0000007074 00000 n
+0000007179 00000 n
+0000007284 00000 n
+0000007388 00000 n
+0000007493 00000 n
+0000007598 00000 n
+0000007702 00000 n
+0000007807 00000 n
+0000007912 00000 n
+0000008016 00000 n
+0000008121 00000 n
+0000008226 00000 n
+0000008330 00000 n
+0000008431 00000 n
+0000008463 00000 n
+0000008495 00000 n
+0000009190 00000 n
+0000009238 00000 n
+0000009286 00000 n
+0000009334 00000 n
+0000009382 00000 n
+0000009430 00000 n
+0000009478 00000 n
+0000009526 00000 n
+0000009574 00000 n
+0000009622 00000 n
+0000009670 00000 n
+0000009718 00000 n
+0000009766 00000 n
+0000009814 00000 n
+0000009862 00000 n
+0000009910 00000 n
+0000009958 00000 n
+0000010006 00000 n
+0000010054 00000 n
+0000010102 00000 n
+0000010150 00000 n
+0000010198 00000 n
+0000010246 00000 n
+0000010294 00000 n
+0000010342 00000 n
+0000010390 00000 n
+0000010438 00000 n
+0000010486 00000 n
+0000010534 00000 n
+0000010582 00000 n
+0000010631 00000 n
+0000010680 00000 n
+0000010729 00000 n
+0000010778 00000 n
+0000010827 00000 n
+0000010876 00000 n
+0000010925 00000 n
+0000010974 00000 n
+0000011023 00000 n
+0000011072 00000 n
+0000011121 00000 n
+0000011170 00000 n
+0000011219 00000 n
+0000011268 00000 n
+0000011317 00000 n
+0000011366 00000 n
+0000011415 00000 n
+0000011464 00000 n
+0000011513 00000 n
+0000011562 00000 n
+0000011611 00000 n
+0000011660 00000 n
+0000011709 00000 n
+0000011758 00000 n
+0000011807 00000 n
+0000012068 00000 n
+0000012220 00000 n
+0000018579 00000 n
+0000018601 00000 n
+0000018696 00000 n
+0000018798 00000 n
+0000018818 00000 n
+0000018972 00000 n
+0000019997 00000 n
+0000020018 00000 n
+0000020131 00000 n
+0000020319 00000 n
+0000020340 00000 n
+0000020480 00000 n
+0000021244 00000 n
+0000021265 00000 n
+0000021378 00000 n
+0000021571 00000 n
+0000021592 00000 n
+0000021723 00000 n
+0000022338 00000 n
+0000022359 00000 n
+0000022472 00000 n
+0000022661 00000 n
+0000022682 00000 n
+0000022813 00000 n
+0000023754 00000 n
+0000023775 00000 n
+0000023906 00000 n
+0000024191 00000 n
+0000024212 00000 n
+0000024352 00000 n
+0000025260 00000 n
+0000025281 00000 n
+0000025412 00000 n
+0000025768 00000 n
+0000025789 00000 n
+0000025929 00000 n
+0000026573 00000 n
+0000026594 00000 n
+0000026725 00000 n
+0000026990 00000 n
+0000027011 00000 n
+0000027151 00000 n
+0000028276 00000 n
+0000028298 00000 n
+0000028438 00000 n
+0000029260 00000 n
+0000029281 00000 n
+0000029421 00000 n
+0000030339 00000 n
+0000030360 00000 n
+0000030500 00000 n
+0000031143 00000 n
+0000031164 00000 n
+0000031304 00000 n
+0000032121 00000 n
+0000032142 00000 n
+0000032282 00000 n
+0000033210 00000 n
+0000033231 00000 n
+0000033371 00000 n
+0000033786 00000 n
+0000033807 00000 n
+0000033920 00000 n
+0000034126 00000 n
+0000034147 00000 n
+0000034301 00000 n
+0000036325 00000 n
+0000036347 00000 n
+0000036501 00000 n
+0000037272 00000 n
+0000037293 00000 n
+0000037348 00000 n
+0000037453 00000 n
+0000037597 00000 n
+0000037703 00000 n
+0000037823 00000 n
+0000037932 00000 n
+0000038081 00000 n
+0000038191 00000 n
+0000038298 00000 n
+0000038452 00000 n
+0000038563 00000 n
+0000038680 00000 n
+0000038796 00000 n
+0000038960 00000 n
+0000039066 00000 n
+0000039185 00000 n
+0000039300 00000 n
+0000039404 00000 n
+0000039560 00000 n
+0000039669 00000 n
+0000039784 00000 n
+0000039896 00000 n
+0000039995 00000 n
+0000040142 00000 n
+0000040239 00000 n
+0000040339 00000 n
+0000040497 00000 n
+0000040637 00000 n
+0000040737 00000 n
+0000040844 00000 n
+0000040994 00000 n
+0000041094 00000 n
+0000041201 00000 n
+0000041349 00000 n
+0000041449 00000 n
+0000041556 00000 n
+0000041706 00000 n
+0000041806 00000 n
+0000041913 00000 n
+0000042059 00000 n
+0000042159 00000 n
+0000042266 00000 n
+0000042417 00000 n
+0000042517 00000 n
+0000042624 00000 n
+0000042772 00000 n
+0000042872 00000 n
+0000042979 00000 n
+0000043129 00000 n
+0000043229 00000 n
+0000043336 00000 n
+0000043468 00000 n
+0000043575 00000 n
+0000043674 00000 n
+0000043792 00000 n
+trailer
+<</Size 254/Root 253 0 R/Info 1 0 R>>
+startxref
+44019
+%%EOF
diff --git a/doc/cmp.shtml b/doc/cmp.shtml
new file mode 100644
index 000000000..898b3f8c9
--- /dev/null
+++ b/doc/cmp.shtml
@@ -0,0 +1,595 @@
+<HTML>
+<HEAD>
+ <META NAME="DOCNUMBER" CONTENT="CUPS-CMP-1.1">
+ <META NAME="COPYRIGHT" CONTENT="Copyright 1997-2000, 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.1 software.
+
+<EMBED SRC="system-overview.shtml">
+
+<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>
+
+<EMBED SRC="references.shtml">
+
+<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.1.0
+</PRE>
+</UL>
+
+Beta-test releases are indentified by appending the letter B followed by
+the build number:
+
+<UL>
+<PRE>
+major.minor.patchbbuild
+1.1.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.1.0b1 First beta release
+1.1.0b2 Second beta release
+1.1.0 First production release
+1.1.1b1 First beta of 1.1.1
+1.1.1 Production release of 1.1.1
+1.1.1b1 First beta of 1.1.1
+1.1.1 Production release of 1.1.1
+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.1. 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.
+
+<EMBED SRC="glossary.shtml">
+
+<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
+".cxx" 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 "&#36;Id$" tag:
+
+<UL>
+<PRE>
+/*
+ * "&#36;Id$"
+ *
+ * Description of file contents.
+ *
+ * Copyright 1997-2000 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:
+ *
+ * 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 "&#36;Id$" 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 "&#36;Id$".
+ */
+</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.1 */
+do_this(float x) /* I - Power value (0.0 &lt;= x &lt;= 1.1) */
+{
+ ...
+ 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..9285a5b5e
--- /dev/null
+++ b/doc/cups.css
@@ -0,0 +1,4 @@
+H1 { font-family: sans-serif }
+H2 { font-family: sans-serif }
+TH { text-align: left }
+
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..e69721336
--- /dev/null
+++ b/doc/documentation.html
@@ -0,0 +1,77 @@
+<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="12,10,50,20" HREF="http://www.easysw.com" ALT="Easy Software Products Home Page">
+ <AREA SHAPE="RECT" COORDS="82,10,196,20" HREF="/admin" ALT="Do Administration Tasks">
+ <AREA SHAPE="RECT" COORDS="216,10,280,20" HREF="/classes" ALT="Manage Printer Classes Status">
+ <AREA SHAPE="RECT" COORDS="300,10,336,20" HREF="/documentation.html" ALT="On-Line Help">
+ <AREA SHAPE="RECT" COORDS="356,10,394,20" HREF="/jobs" ALT="Manage Jobs">
+ <AREA SHAPE="RECT" COORDS="414,10,476,20" HREF="/printers" ALT="Manage Printers">
+ <AREA SHAPE="RECT" COORDS="496,10,568,20" HREF="http://www.cups.org" ALT="Download the Current CUPS Software">
+ </MAP>
+</HEAD>
+
+<BODY BGCOLOR="#cccc99" TEXT="#000000" LINK="#0000FF" VLINK="#FF00FF">
+<CENTER>
+<IMG SRC="/images/navbar.gif" WIDTH="583" HEIGHT="30" USEMAP="#navbar" BORDER="0" ALT="Common UNIX Printing System">
+</CENTER>
+
+<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>Software Programmers Manual (
+ <A HREF="spm.html">HTML</A> |
+ <A HREF="spm.pdf">PDF</A> )
+
+ <LI>Configuration Management Plan (
+ <A HREF="cmp.html">HTML</A> |
+ <A HREF="cmp.pdf">PDF</A> )
+
+ <LI>CUPS Implementation of IPP (
+ <A HREF="ipp.html">HTML</A> |
+ <A HREF="ipp.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> )
+
+</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-2000 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/glossary.shtml b/doc/glossary.shtml
new file mode 100644
index 000000000..f13ac3d37
--- /dev/null
+++ b/doc/glossary.shtml
@@ -0,0 +1,76 @@
+<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>
diff --git a/doc/idd.html b/doc/idd.html
new file mode 100644
index 000000000..c341ba2dc
--- /dev/null
+++ b/doc/idd.html
@@ -0,0 +1,1058 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
+<HTML>
+<HEAD>
+<TITLE>CUPS Interface Design Description</TITLE>
+<META NAME="AUTHOR" CONTENT="Easy Software Products">
+<META NAME="COPYRIGHT" CONTENT="Copyright 1997-2000, All Rights Reserved">
+<META NAME="DOCNUMBER" CONTENT="CUPS-IDD-1.1">
+<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
+<STYLE>
+BODY { font-family: serif; font-size: 11.0pt }
+H1 { font-family: sans-serif; font-size: 20.0pt }
+H2 { font-family: sans-serif; font-size: 17.0pt }
+H3 { font-family: sans-serif; font-size: 14.0pt }
+H4 { font-family: sans-serif; font-size: 11.0pt }
+H5 { font-family: sans-serif; font-size: 9.0pt }
+H6 { font-family: sans-serif; font-size: 8.0pt }
+SUB { font-size: 8.0pt }
+SUP { font-size: 8.0pt }
+PRE { font-size: 9.0pt }
+A:link { text-decoration: underline }
+A:visited { text-decoration: underline }
+A:active { text-decoration: underline }
+</STYLE>
+</HEAD>
+<BODY>
+<CENTER><A HREF="#CONTENTS"><IMG SRC="images/cups-large.gif" BORDER="0"><BR>
+<H1>CUPS Interface Design Description</H1></A><BR>
+CUPS-IDD-1.1<BR>
+Easy Software Products<BR>
+Copyright 1997-2000, 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 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>
+<UL>
+<LI><A HREF="#3_1_1">3.1.1 8-Bit Character Set Files</A></LI>
+<LI><A HREF="#3_1_2">3.1.2 Unicode Character Set Files</A></LI>
+</UL>
+<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 Option Files</A></LI>
+<LI><A HREF="#3_5">3.5 PostScript Printer Description Files</A></LI>
+<UL>
+<LI><A HREF="#3_5_1">3.5.1 PPD Specification</A></LI>
+<LI><A HREF="#3_5_2">3.5.2 CUPS Extensions to PPD Files</A></LI>
+</UL>
+<LI><A HREF="#3_6">3.6 Scheduler Configuration Files</A></LI>
+<UL>
+<LI><A HREF="#3_6_1">3.6.1 classes.conf</A></LI>
+<LI><A HREF="#3_6_2">3.6.2 cupsd.conf</A></LI>
+<LI><A HREF="#3_6_3">3.6.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 Form File</A></LI>
+<UL>
+<LI><A HREF="#4_3_1">4.3.1 CUPS Form DTD</A></LI>
+</UL>
+<LI><A HREF="#4_4">4.4 CUPS PostScript File</A></LI>
+<LI><A HREF="#4_5">4.5 CUPS Raster File</A></LI>
+<LI><A HREF="#4_6">4.6 CUPS Raw Files</A></LI>
+<LI><A HREF="#4_7">4.7 Internet Printing Protocol</A></LI>
+<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>
+</UL>
+<B><A HREF="#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.1. </P>
+<H2><A NAME="1_2">1.2 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.1: CUPS Configuration Management Plan </LI>
+<LI>CUPS-IDD-1.1: CUPS System Interface Design Description </LI>
+<LI>CUPS-IPP-1.1: CUPS Implmentation of IPP </LI>
+<LI>CUPS-SAM-1.1.x: CUPS Software Administrators Manual </LI>
+<LI>CUPS-SDD-1.1: CUPS Software Design Description </LI>
+<LI>CUPS-SPM-1.1: CUPS Software Programming Manual </LI>
+<LI>CUPS-SSR-1.1: CUPS Software Security Report </LI>
+<LI>CUPS-STP-1.1: CUPS Software Test Plan </LI>
+<LI>CUPS-SUM-1.1.x: CUPS Software Users Manual </LI>
+<LI>CUPS-SVD-1.1.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>Adobe PostScript Printer Description File Format Specification,
+ Version 4.3. </LI>
+<LI>Adobe PostScript Language Reference, Third Edition. </LI>
+<LI>IPP: Job and Printer Set Operations </LI>
+<LI>IPP/1.1: Encoding and Transport </LI>
+<LI>IPP/1.1: Implementers Guide </LI>
+<LI>IPP/1.1: Model and Semantics </LI>
+<LI>RFC 1179, Line Printer Daemon Protocol </LI>
+<LI>RFC 2567, Design Goals for an Internet Printing Protocol </LI>
+<LI>RFC 2568, Rationale for the Structure of the Model and Protocol
+ for the Internet Printing Protocol </LI>
+<LI>RFC 2569, Mapping between LPD and IPP Protocols </LI>
+<LI>RFC 2616, Hypertext Transfer Protocol -- HTTP/1.1 </LI>
+<LI>RFC 2617, HTTP Authentication: Basic and Digest Access
+ Authentication </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, or between Unicode and printer fonts.
+They are named using the IETF charset names defined in RFCnnnn. These
+files are ASCII text, the content of which is described below. Comments
+can be included by using the <TT>#</TT> character in the first column
+of a line. </P>
+<H3><A NAME="3_1_1">3.1.1 8-Bit Character Set Files</A></H3>
+<P>8-bit character set files start with a line reading: </P>
+<UL>
+<PRE>
+charset 8bit
+</PRE>
+</UL>
+<P>Following this are lines that define the font information: </P>
+<UL>
+<PRE>
+first last direction width normal bold italic bold-italic
+</PRE>
+</UL>
+<P><VAR>First</VAR> and <VAR>last</VAR> are the first and last glyphs
+in the font mapping that correspond to that font; a maximum of 256
+characters can be mapped within each group, with a maximum of 256
+mappings (this is a PostScript limitation.) The glyph values are
+hexadecimal. </P>
+<P><VAR>Direction</VAR> is the string &quot;ltor&quot;, &quot;rtol&quot;, or &quot;rtola&quot;
+indicating left-to-right, right-to-left, or right-to-left Arabic text. </P>
+<P><VAR>Width</VAR> is the string &quot;single&quot; or &quot;double&quot;; double means
+that the glyphs are twice as wide as ASCII characters in the Courier
+typeface. </P>
+<P><VAR>Normal, bold, italic</VAR>, and <VAR>bold-italic</VAR> are the
+typefaces to use for each presentation. If characters are only
+available in a single style then only one typeface should be listed
+(e.g. &quot;Symbol&quot;.) Each font that is listed will be used (and downloaded
+if needed) when printing. </P>
+<P>The remaining lines define a character to Unicode glyph mapping for
+the character set. The character and glyph values are hexadecimal: </P>
+<UL>
+<PRE>
+xx yyyy
+</PRE>
+</UL>
+<H3><A NAME="3_1_2">3.1.2 Unicode Character Set Files</A></H3>
+<P>Unicode character set files start with a line reading: </P>
+<UL>
+<PRE>
+charset encoding
+</PRE>
+</UL>
+<P><VAR>Encoding</VAR> is the encoding to use for the text; currently
+only the string &quot;utf8&quot; is supported. </P>
+<P>Following this are lines defining the font information: </P>
+<UL>
+<PRE>
+first last direction width normal bold italic bold-italic
+</PRE>
+</UL>
+<P><VAR>First</VAR> and <VAR>last</VAR> are the first and last glyphs
+in the font mapping that correspond to that font; a maximum of 256
+characters can be mapped within each group, with a maximum of 256
+mappings (this is a PostScript limitation.) The glyph values are
+hexadecimal. </P>
+<P><VAR>Direction</VAR> is the string &quot;ltor&quot;, &quot;rtol&quot;, or &quot;rtola&quot;
+indicating left-to-right, right-to-left, or right-to-left Arabic text. </P>
+<P><VAR>Width</VAR> is the string &quot;single&quot; or &quot;double&quot;; double means
+that the glyphs are twice as wide as ASCII characters in the Courier
+typeface. </P>
+<P><VAR>Normal, bold, italic</VAR>, and <VAR>bold-italic</VAR> are the
+typefaces to use for each presentation. If characters are only
+available in a single style then only one typeface should be listed
+(e.g. &quot;Symbol&quot;.) Each font that is listed will be used (and downloaded
+if needed) when printing. </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>cp874 </LI>
+<LI>cp1250 </LI>
+<LI>cp1251 </LI>
+<LI>cp1252 </LI>
+<LI>cp1253 </LI>
+<LI>cp1254 </LI>
+<LI>cp1255 </LI>
+<LI>cp1256 </LI>
+<LI>cp1257 </LI>
+<LI>cp1258 </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-10 </LI>
+<LI>iso-8859-13 </LI>
+<LI>iso-8859-14 </LI>
+<LI>iso-8859-15 </LI>
+<LI>us-ascii </LI>
+<LI>utf-8 </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;contains(&quot; offset &quot;,&quot; length &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 Option Files</A></H2>
+<P>CUPS maintains user-defined printer and option files for each
+printer and user on the system. The printers and options defined in the
+system option file (<CODE>/etc/cups/lpoptions</CODE>) are loaded first,
+followed by the user option file (<CODE>$HOME/.lpoptions</CODE>).
+Options in the user file replace those defined in the system file for
+the same destination. Each line in the files can be one of the
+following: </P>
+<UL>
+<PRE>
+Dest name option=value option=value ... option=value
+Dest name/instance option=value option=value ... option=value
+Default name option=value option=value ... option=value
+Default name/instance option=value option=value ... option=value
+</PRE>
+</UL>
+<P>The line beginning with &quot;Default&quot; indicates the default destination
+for print jobs; a default line in the user option file overrides the
+default defined in the system option file. </P>
+<P><VAR>Name</VAR> is the name of a printer known to the local server. </P>
+<P><VAR>Instance</VAR> can be any string of letters, numbers, and the
+underscore up to 127 characters in length. </P>
+<P>The remainder of the line contains a list of space-separated options
+and their values. </P>
+<H2><A NAME="3_5">3.5 PostScript Printer Description Files</A></H2>
+<P>PostScript Printer Description (&quot;PPD&quot;) files describe the
+capabilities of each printer and are used by CUPS to support
+printer-specific features and intelligent filtering. </P>
+<H3><A NAME="3_5_1">3.5.1 PPD Specification</A></H3>
+<P>The PPD file format is described in <A HREF="http://partners.adobe.com/asn/developer/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_5_2">3.5.2 CUPS Extensions to PPD Files</A></H3>
+<P>CUPS adds several new attributes that are described below. </P>
+<H4>3.5.2.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.5.2.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.5.2.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.5.2.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.5.2.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;
+or &quot;1.1&quot;. </P>
+<H2><A NAME="3_6">3.6 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_6_1">3.6.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>Surrounds a class definition.</TD></TR>
+<TR><TD>&lt;DefaultClass name&gt;
+<BR> &lt;/Class&gt;</TD><TD>Surrounds a class definition for the default
+destination.</TD></TR>
+<TR><TD>Accepting</TD><TD>Specifies whether the class is accepting new
+jobs. May be the names &quot;Yes&quot; or &quot;No&quot;.</TD></TR>
+<TR><TD>AllowUsers</TD><TD>Specifies a list of users that are allowed
+to access the class.</TD></TR>
+<TR><TD>BannerStart</TD><TD>Specifies the banner that is printed before
+other files in a job.</TD></TR>
+<TR><TD>BannerEnd</TD><TD>Specifies the banner that is printed after
+other files in a job.</TD></TR>
+<TR><TD>DenyUsers</TD><TD>Specifies a list of users that are not
+allowed to access the class.</TD></TR>
+<TR><TD>Info</TD><TD>A textual description of the class.</TD></TR>
+<TR><TD>Location</TD><TD>A textual location of the class.</TD></TR>
+<TR><TD>MoreInfo</TD><TD>A URL pointing to additional information on
+the class.</TD></TR>
+<TR><TD>Printer</TD><TD>Specifies a printer that is a member of the
+class.</TD></TR>
+<TR><TD>State</TD><TD>Specifies the initial state of the class; can be
+&quot;Idle&quot; or &quot;Stopped&quot;.</TD></TR>
+<TR><TD>StateMessage</TD><TD>Specifies a textual message for the
+current class state.</TD></TR>
+</TABLE>
+</CENTER>
+</P>
+<H3><A NAME="3_6_2">3.6.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>access_log</TD><TD>Specifies the location of
+the access log file. The special name &quot;syslog&quot; can be used to send
+access log information to the system log.</TD></TR>
+<TR><TD>Allow</TD><TD>-</TD><TD>Allows connections from the specified
+host, network, or domain.</TD></TR>
+<TR><TD>AuthClass</TD><TD>-</TD><TD>Specifies what level of
+authentication is required; may be &quot;User&quot;, &quot;System&quot;, or &quot;Group&quot;.</TD></TR>
+<TR><TD>AuthType</TD><TD>None</TD><TD>Specifies the type of
+authentication to perform; may be &quot;None&quot;, &quot;Basic&quot;, or &quot;Digest&quot;.</TD></TR>
+<TR><TD>BrowseAddress</TD><TD>255.255.255.255</TD><TD>Specifies a
+broadcast address to send CUPS browsing packets to.</TD></TR>
+<TR><TD>BrowseAllow</TD><TD>-</TD><TD>Specifies hosts or addresses from
+which browsing information should be used.</TD></TR>
+<TR><TD>BrowseDeny</TD><TD>-</TD><TD>Specifies hosts or addresses from
+which browsing information should not be used.</TD></TR>
+<TR><TD>BrowseInterval</TD><TD>30</TD><TD>Specifies the number of
+seconds between browsing updates. A browse interval of 0 seconds
+disables outgoing packets.</TD></TR>
+<TR><TD>BrowseOrder</TD><TD>Allow,Deny</TD><TD>Specifies the order of
+BrowseAllow and BrowseDeny directive processing; can be &quot;Deny,Allow&quot;
+to implicitly deny hosts unless they are allowed by a BrowseAllow
+line, or &quot;Allow,Deny&quot; to implicitly allow hosts unless they are denied
+by a BrowseDeny line.</TD></TR>
+<TR><TD>BrowsePoll</TD><TD>-</TD><TD>Specifies a server to poll for
+available printers and classes.</TD></TR>
+<TR><TD>BrowsePort</TD><TD>631</TD><TD>Specifies the UDP port number to
+use for browse packets.</TD></TR>
+<TR><TD>BrowseRelay</TD><TD>-</TD><TD>Specifies a source and
+destination address for relaying browser information from one subnet
+to another.</TD></TR>
+<TR><TD>BrowseTimeout</TD><TD>300</TD><TD>Specifies the number of
+seconds to wait until remote destinations are removed from the local
+destination list.</TD></TR>
+<TR><TD>Browsing</TD><TD>On</TD><TD>Specifies whether or not printer
+and class browsing is enabled; can be &quot;On&quot; or &quot;Off&quot;.</TD></TR>
+<TR><TD>DefaultCharset</TD><TD>iso-8859-1</TD><TD>Specifies the default
+character set.</TD></TR>
+<TR><TD>DefaultLanguage</TD><TD>current locale</TD><TD>Specifies the
+default language.</TD></TR>
+<TR><TD>Deny</TD><TD>-</TD><TD>Refuses connections from the specified
+host, network, or domain.</TD></TR>
+<TR><TD>DocumentRoot</TD><TD>/usr/share/doc/cups</TD><TD>Specifies the
+document data root directory.</TD></TR>
+<TR><TD>ErrorLog</TD><TD>error_log</TD><TD>Specifies the error log file
+location. The special name &quot;syslog&quot; can be used to send error log
+information to the system log.</TD></TR>
+<TR><TD>Group</TD><TD>root, sys, system</TD><TD>Specifies the group
+name or ID that is used when running external programs.</TD></TR>
+<TR><TD>HostNameLookups</TD><TD>Off</TD><TD>Specifies whether or not to
+perform reverse IP address lookups to get the actual hostname; may be
+&quot;On&quot; or &quot;Off&quot;. Hostname lookups can significantly degrade the
+performance of the CUPS server if one or more DNS servers is not
+functioning properly.</TD></TR>
+<TR><TD>ImplicitClasses</TD><TD>On</TD><TD>Specifies whether or not to
+automatically create printer classes when more than one printer or
+class of the same name is detected on the network; may be &quot;On&quot; or
+&quot;Off&quot;.</TD></TR>
+<TR><TD>KeepAlive</TD><TD>On</TD><TD>Specifies whether or not to use
+the HTTP Keep-Alive feature; may be &quot;On&quot; or &quot;Off&quot;.</TD></TR>
+<TR><TD>KeepAliveTimeout</TD><TD>30</TD><TD>Specifies the amount of
+time to keep the HTTP connection alive before closing it.</TD></TR>
+<TR><TD>&lt;Location path&gt;
+<BR> &lt;/Location&gt;</TD><TD>-</TD><TD>Specifies a location to restrict
+access to.</TD></TR>
+<TR><TD>LogLevel</TD><TD>info</TD><TD>Controls the amount of
+information that is logged in the error log file. Can be one of
+&quot;debug&quot;, &quot;info&quot;, &quot;warn&quot;, &quot;error&quot;, or &quot;none&quot;, in decreasing order or
+verbosity.</TD></TR>
+<TR><TD>MaxClients</TD><TD>100</TD><TD>Specifies the maximum number of
+simultaneous active clients. This value is internally limited to 1/3
+of the total number of available file descriptors.</TD></TR>
+<TR><TD>MaxLogSize</TD><TD>0</TD><TD>Specifies the maximum size of the
+access, error, and page log files in bytes. If set to 0 then no
+maximum size is set. Log files are rotated automatically when this
+size is exceeded.</TD></TR>
+<TR><TD>MaxRequestSize</TD><TD>0</TD><TD>Specifies the maximum size of
+HTTP requests in bytes. If set to 0 then there is no maximum.</TD></TR>
+<TR><TD>Order</TD><TD>Allow,Deny</TD><TD>Specifies the order of Allow
+and Deny directive processing; can be &quot;Deny,Allow&quot; to implicitly deny
+hosts unless they are allowed by an Allow line, or &quot;Allow,Deny&quot; to
+implicitly allow hosts unless they are denied by a Deny line.</TD></TR>
+<TR><TD>PageLog</TD><TD>page_log</TD><TD>Specifies the location of the
+page log file. The special name &quot;syslog&quot; can be used to send page log
+information to the system log.</TD></TR>
+<TR><TD>Port</TD><TD>631</TD><TD>Specifies a port number to listen to
+for HTTP connections.</TD></TR>
+<TR><TD>Printcap</TD><TD>/etc/printcap</TD><TD>Specifies the location
+of a Berkeley printcap file to update with a list of current printers
+and classes. If no filename is supplied then this automatic generation
+is disabled.</TD></TR>
+<TR><TD>RequestRoot</TD><TD>/var/spool/cups</TD><TD>Specifies the
+location of request files.</TD></TR>
+<TR><TD>RIPCache</TD><TD>8m</TD><TD>Specifies the size of the memory
+cache in bytes that is used by RIP filters.</TD></TR>
+<TR><TD>ServerAdmin</TD><TD>root@ServerName</TD><TD>Specifies the
+person to contact with problems.</TD></TR>
+<TR><TD>ServerName</TD><TD>hostname</TD><TD>Specifies the hostname that
+is supplied to HTTP clients. This is also used to determine the
+default CUPS server for the CUPS IPP client applications.</TD></TR>
+<TR><TD>ServerRoot</TD><TD>/etc/cups</TD><TD>Specifies the root
+directory for server configuration files.</TD></TR>
+<TR><TD>SystemGroup</TD><TD>root, sys, system</TD><TD>Specifies the
+group name used for System class authentication.</TD></TR>
+<TR><TD>TempDir</TD><TD>/var/tmp</TD><TD>Specifies the temporary
+directory to use.</TD></TR>
+<TR><TD>Timeout</TD><TD>300</TD><TD>The timeout in seconds before
+client connections are closed in the middle of a request.</TD></TR>
+<TR><TD>User</TD><TD>lp</TD><TD>Specifies the user that is used when
+running external programs.</TD></TR>
+</TABLE>
+</CENTER>
+</P>
+<H3><A NAME="3_6_3">3.6.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>Accepting</TD><TD>Specifies whether the printer is accepting
+new jobs. May be the names &quot;Yes&quot; or &quot;No&quot;.</TD></TR>
+<TR><TD>&lt;DefaultPrinter name&gt;
+<BR> &lt;/Printer&gt;</TD><TD>Surrounds the printer definition for a default
+destination.</TD></TR>
+<TR><TD>AllowUsers</TD><TD>Specifies a list of users that are allowed
+to access the printer.</TD></TR>
+<TR><TD>BannerStart</TD><TD>Specifies the banner that is printed before
+other files in a job.</TD></TR>
+<TR><TD>BannerEnd</TD><TD>Specifies the banner that is printed after
+other files in a job.</TD></TR>
+<TR><TD>DenyUsers</TD><TD>Specifies a list of users that are not
+allowed to access the printer.</TD></TR>
+<TR><TD>DeviceURI</TD><TD>Specifies the device-uri attribute for the
+printer.</TD></TR>
+<TR><TD>Info</TD><TD>A textual description of the printer.</TD></TR>
+<TR><TD>Location</TD><TD>A textual location of the printer.</TD></TR>
+<TR><TD>MoreInfo</TD><TD>A URL pointing to additional information on
+the printer.</TD></TR>
+<TR><TD>&lt;Printer name&gt;
+<BR> &lt;/Printer&gt;</TD><TD>Surrounds the printer definition.</TD></TR>
+<TR><TD>State</TD><TD>Specifies the initial state of the printer; can
+be &quot;Idle&quot; or &quot;Stopped&quot;.</TD></TR>
+<TR><TD>StateMessage</TD><TD>Specifies a textual message for the
+current printer state.</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. The URI method name is &quot;socket&quot;. </P>
+<P>The AppSocket protocol is used by the Hewlett Packard JetDirect
+network interfaces and print servers, as well as many other vendors'
+products. See the CUPS Software Administrators Manual for a list of
+supported products. </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 SP &quot;location&quot; SP &quot;info&quot; SP &quot;make-and-model&quot; NL
+</PRE>
+</UL>
+<P><VAR>State, uri, location, info</VAR>, and <VAR>make-and-model</VAR>
+, correspond to the IPP <CODE>printer-state</CODE>, <CODE>
+printer-uri-supported</CODE>, <CODE>printer-location</CODE>, <CODE>
+printer-info</CODE>, and <CODE>printer-make-and-model</CODE>
+ attributes. </P>
+<P><VAR>Type</VAR> 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 Form File</A></H2>
+<P>CUPS Form files are XML files used by the CUPS <CODE>formtops</CODE>
+ filter to produce dynamic banner pages and support preprinted forms. </P>
+<P>The MIME type for CUPS Form files is <CODE>application/vnd.cups-form</CODE>
+. </P>
+<H3><A NAME="4_3_1">4.3.1 CUPS Form DTD</A></H3>
+<P>The following DTD describes the available elements and attributes in
+a CUPS Form file:
+<CENTER>
+<TABLE BORDER>
+<TR><TD>
+<PRE>
+&lt;!ENTITY % Angle &quot;CDATA&quot; -- angle in degrees --&gt;
+
+&lt;!ENTITY % Color &quot;CDATA&quot; -- a color using sRGB: #RRGGBB as Hex values --&gt;
+
+&lt;!ENTITY % Length &quot;CDATA&quot; -- nn for pixels or nn% for percentage length --&gt;
+
+&lt;!ENTITY % Lengths &quot;CDATA&quot; -- comma-separated Length values --&gt;
+
+&lt;!ENTITY % Text &quot;CDATA&quot;&gt;
+
+&lt;!ENTITY % heading &quot;H1|H2|H3|H4|H5|H6&quot;&gt;
+
+&lt;!ENTITY % preformatted &quot;PRE&quot;&gt;
+
+&lt;!ENTITY % i18n
+ &quot;lang %LanguageCode; #IMPLIED -- language code --
+ dir (ltr|rtl) #IMPLIED -- direction for weak/neutral text --&quot;
+ &gt;
+
+&lt;!ENTITY % attrs &quot;%i18n;&quot;&gt;
+
+&lt;!ENTITY % fontstyle
+ &quot;B | FONT | I | TT&quot;&gt;
+
+&lt;!ENTITY % graphics
+ &quot;BOX | RECT | LINE | POLY | ARC | PIE | TEXT&quot;&gt;
+
+&lt;!ENTITY % insert
+ &quot;IMG | VAR&quot;&gt;
+
+&lt;!-- %inline; covers inline or &quot;text-level&quot; elements --&gt;
+&lt;!ENTITY % inline &quot;#PCDATA | %fontstyle; | %graphics; | %insert;&quot;&gt;
+
+&lt;!ELEMENT (%fontstyle;) - - (%inline;)*&gt;
+&lt;!ATTLIST (%fontstyle;)
+ %attrs; -- %i18n --
+ &gt;
+
+&lt;!ELEMENT BR - O EMPTY -- forced line break --&gt;
+&lt;!ATTLIST BR
+ %attrs; -- %i18n --
+ &gt;
+
+&lt;!ENTITY % block
+ &quot;P | %heading; | %preformatted;&quot;&gt;
+
+&lt;!ENTITY % flow &quot;%block; | %inline;&quot;&gt;
+
+&lt;!ELEMENT PAGE O O (%flow;)+ -- document body --&gt;
+&lt;!ATTLIST PAGE
+ %attrs; -- %i18n --
+ align (left|center|right) #IMPLIED -- horizontal alignment --
+ valign (top|middle|center|bottom) #IMPLIED -- vertical alignment --
+ &gt;
+
+&lt;!ELEMENT P - O (%inline;)* -- paragraph --&gt;
+&lt;!ATTLIST P
+ %attrs; -- %i18n --
+ align (left|center|right) #IMPLIED -- horizontal alignment --
+ &gt;
+
+&lt;!ELEMENT (%heading;) - - (%inline;)* -- heading --&gt;
+&lt;!ATTLIST (%heading;)
+ %attrs; -- %i18n --
+ align (left|center|right) #IMPLIED -- horizontal alignment --
+ &gt;
+
+&lt;!ELEMENT PRE - - (%inline;)* -- preformatted text --&gt;
+&lt;!ATTLIST PRE
+ %attrs; -- %i18n --
+ align (left|center|right) #IMPLIED -- horizontal alignment --
+ &gt;
+
+&lt;!ELEMENT BOX - O EMPTY -- unfilled box --&gt;
+&lt;!ATTLIST BOX
+ color %Color; #IMPLIED -- override color --
+ height %Length; #REQUIRED -- height of box --
+ thickness %Length; #IMPLIED -- override line thickness --
+ width %Length; #REQUIRED -- width of box --
+ x %Length; #REQUIRED -- horizontal position --
+ y %Length; #REQUIRED -- vertical position --
+ &gt;
+
+&lt;!ELEMENT RECT - O EMPTY -- filled box --&gt;
+&lt;!ATTLIST RECT
+ color %Color; #IMPLIED -- override color --
+ height %Length; #REQUIRED -- height of box --
+ width %Length; #REQUIRED -- width of box --
+ x %Length; #REQUIRED -- horizontal position --
+ y %Length; #REQUIRED -- vertical position --
+ &gt;
+
+&lt;!ELEMENT LINE - O EMPTY -- polyline --&gt;
+&lt;!ATTLIST LINE
+ color %Color; #IMPLIED -- override color --
+ thickness %Length; #IMPLIED -- override line thickness --
+ x %Lengths; #REQUIRED -- horizontal positions --
+ y %Lengths; #REQUIRED -- vertical positions --
+ &gt;
+
+&lt;!ELEMENT POLY - O EMPTY -- polygon (filled) --&gt;
+&lt;!ATTLIST POLY
+ color %Color; #IMPLIED -- override color --
+ x %Lengths; #REQUIRED -- horizontal positions --
+ y %Lengths; #REQUIRED -- vertical positions --
+ &gt;
+
+&lt;!ELEMENT ARC - O EMPTY -- unfilled arc --&gt;
+&lt;!ATTLIST ARC
+ color %Color; #IMPLIED -- override color --
+ end %Angle; #IMPLIED -- override end angle --
+ height %Length; #REQUIRED -- height of arc --
+ start %Angle; #IMPLIED -- override start angle --
+ thickness %Length; #IMPLIED -- override line thickness --
+ width %Length; #REQUIRED -- width of arc --
+ x %Length; #REQUIRED -- horizontal position --
+ y %Length; #REQUIRED -- vertical position --
+ &gt;
+
+&lt;!ELEMENT PIE - O EMPTY -- filled arc --&gt;
+&lt;!ATTLIST PIE
+ color %Color; #IMPLIED -- override color --
+ end %Angle; #IMPLIED -- override end angle --
+ height %Length; #REQUIRED -- height of arc --
+ start %Angle; #IMPLIED -- override start angle --
+ width %Length; #REQUIRED -- width of arc --
+ x %Length; #REQUIRED -- horizontal position --
+ y %Length; #REQUIRED -- vertical position --
+ &gt;
+
+&lt;!ELEMENT TEXT - - (%flow;)* -- text box --&gt;
+&lt;!ATTLIST RECT
+ align (left|center|right) #IMPLIED -- horizontal alignment --
+ height %Length; #REQUIRED -- height of box --
+ valign (top|middle|center|bottom) #IMPLIED -- vertical alignment --
+ width %Length; #REQUIRED -- width of box --
+ x %Length; #REQUIRED -- horizontal position --
+ y %Length; #REQUIRED -- vertical position --
+ &gt;
+
+
+&lt;!ELEMENT IMG - O EMPTY -- Embedded image --&gt;
+&lt;!ATTLIST IMG
+ %attrs; -- %coreattrs, %i18n, %events --
+ src %URI; #REQUIRED -- URI of image to embed --
+ height %Length; #IMPLIED -- override height --
+ width %Length; #IMPLIED -- override width --
+ &gt;
+
+&lt;!ELEMENT HEAD O O (DEFVAR)* -- document head --&gt;
+&lt;!ATTLIST HEAD
+ %i18n; -- lang, dir --
+ &gt;
+
+&lt;!ELEMENT DEFVAR - O EMPTY -- variable definition --&gt;
+&lt;!ATTLIST DEFVAR
+ name CDATA #REQUIRED -- name
+ value CDATA #REQUIRED -- value
+ &gt;
+
+
+&lt;!ENTITY % html.content &quot;HEAD, PAGE&quot;&gt;
+
+&lt;!ELEMENT CUPSFORM - - (HEAD) (PAGE)+ -- document root element --&gt;
+&lt;!ATTLIST CUPSFORM
+ %i18n; -- lang, dir --
+ &gt;
+</PRE>
+</TD></TR>
+</TABLE>
+</CENTER>
+</P>
+<H2><A NAME="4_4">4.4 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/asn/developer/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_5">4.5 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 Image 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_6">4.6 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_7">4.7 Internet Printing Protocol</A></H2>
+<P>The Internet Printing Protocol and the CUPS extensions to it are
+described in the CUPS Implementation of IPP document. </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>
+<P>The URI method name for LPD is &quot;lpd&quot;. </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>
+<P>The URI method name for SMB is &quot;smb&quot;. </P>
+<H1><A NAME="5">5 Directories</A></H1>
+<DL>
+<DT>/etc/cups </DT>
+<DD>The scheduler configuration and MIME files reside here. </DD>
+<DT>/etc/cups/certs </DT>
+<DD>The authentication certificates reside here. </DD>
+<DT>/etc/cups/interfaces </DT>
+<DD>System V interface scripts reside here. </DD>
+<DT>/etc/cups/ppd </DT>
+<DD>This directory contains PPD files for each printer. </DD>
+<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/cups/backend </DT>
+<DD>The backend filters reside here. </DD>
+<DT>/usr/lib/cups/cgi-bin </DT>
+<DD>The CGI programs reside here. </DD>
+<DT>/usr/lib/cups/daemon </DT>
+<DD>The polling and LPD daemons reside here. </DD>
+<DT>/usr/lib/cups/filter </DT>
+<DD>The file filters reside here. </DD>
+<DT>/usr/sbin </DT>
+<DD>The <CODE>accept</CODE>, <CODE>cupsd</CODE>, <CODE>lpadmin</CODE>, <CODE>
+lpc</CODE>, and <CODE>reject</CODE> commands reside here. </DD>
+<DT>/usr/share/cups </DT>
+<DD>This is the root directory of the CUPS static data. </DD>
+<DT>/usr/share/cups/charsets </DT>
+<DD>The character set files reside here. </DD>
+<DT>/usr/share/cups/data </DT>
+<DD>The 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>/usr/share/doc/cups </DT>
+<DD>The scheduler documentation files reside here. </DD>
+<DT>/var/log/cups </DT>
+<DD>The <CODE>access_log</CODE>, <CODE>error_log</CODE>, and <CODE>
+page_log</CODE> files reside here. </DD>
+<DT>/var/spool/cups </DT>
+<DD>This directory contains 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..fc7c1f8f3
--- /dev/null
+++ b/doc/idd.pdf
@@ -0,0 +1,814 @@
+%PDF-1.2
+%âãÏÓ
+1 0 obj<</Producer(htmldoc 1.8.6 Copyright 1997-2000 Easy Software Products, All Rights Reserved.)/CreationDate(D:20000407154508Z)/Title(CUPS Interface Design Description)/Author(Easy Software Products)>>endobj
+2 0 obj<</Type/Encoding/Differences[ 32/space/exclam/quotedbl/numbersign/dollar/percent/ampersand/quotesingle/parenleft/parenright/asterisk/plus/comma/minus/period/slash/zero/one/two/three/four/five/six/seven/eight/nine/colon/semicolon/less/equal/greater/question/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/grave/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 160/space/exclamdown/cent/sterling/currency/yen/brokenbar/section/dieresis/copyright/ordfeminine/guillemotleft/logicalnot/hyphen/registered/macron/degree/plusminus/twosuperior/threesuperior/acute/mu/paragraph/periodcentered/cedilla/onesuperior/ordmasculine/guillemotright/onequarter/onehalf/threequarters/questiondown/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]>>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/Helvetica-Oblique/Encoding 2 0 R>>endobj
+10 0 obj<</Type/Font/Subtype/Type1/BaseFont/Symbol>>endobj
+11 0 obj<</S/URI/URI(http://partners.adobe.com/asn/developer/PDFS/TN/5003.PPD_Spec_v4.3.pdf)>>endobj
+12 0 obj<</Subtype/Link/Rect[194.6 332.0 229.4 343.0]/Border[0 0 0]/A 11 0 R>>endobj
+13 0 obj<</S/URI/URI(http://partners.adobe.com/asn/developer/PDFS/TN/5003.PPD_Spec_v4.3.pdf)>>endobj
+14 0 obj<</Subtype/Link/Rect[226.6 332.0 492.5 343.0]/Border[0 0 0]/A 13 0 R>>endobj
+15 0 obj<</S/URI/URI(http://partners.adobe.com/asn/developer/PDFS/TN/5003.PPD_Spec_v4.3.pdf)>>endobj
+16 0 obj<</Subtype/Link/Rect[36.0 318.8 148.1 329.8]/Border[0 0 0]/A 15 0 R>>endobj
+17 0 obj[12 0 R
+14 0 R
+16 0 R
+]endobj
+18 0 obj<</S/URI/URI(http://partners.adobe.com/asn/developer/PDFS/TN/PLRM.pdf)>>endobj
+19 0 obj<</Subtype/Link/Rect[144.4 510.6 179.2 523.6]/Border[0 0 0]/A 18 0 R>>endobj
+20 0 obj<</S/URI/URI(http://partners.adobe.com/asn/developer/PDFS/TN/PLRM.pdf)>>endobj
+21 0 obj<</Subtype/Link/Rect[176.5 510.6 416.6 523.6]/Border[0 0 0]/A 20 0 R>>endobj
+22 0 obj[19 0 R
+21 0 R
+]endobj
+23 0 obj<</S/URI/URI(http://www.ietf.org/rfc/rfc1179.txt)>>endobj
+24 0 obj<</Subtype/Link/Rect[290.5 606.8 474.7 617.8]/Border[0 0 0]/A 23 0 R>>endobj
+25 0 obj<</S/URI/URI(http://anu.samba.org/cifs)>>endobj
+26 0 obj<</Subtype/Link/Rect[46.7 500.0 156.1 511.0]/Border[0 0 0]/A 25 0 R>>endobj
+27 0 obj[24 0 R
+26 0 R
+]endobj
+28 0 obj<</Subtype/Link/Rect[72.0 670.8 107.8 683.8]/Border[0 0 0]/Dest[114 0 R/XYZ null 818 0]>>endobj
+29 0 obj<</Subtype/Link/Rect[108.0 657.6 183.8 670.6]/Border[0 0 0]/Dest[114 0 R/XYZ null 735 0]>>endobj
+30 0 obj<</Subtype/Link/Rect[108.0 644.4 216.4 657.4]/Border[0 0 0]/Dest[114 0 R/XYZ null 654 0]>>endobj
+31 0 obj<</Subtype/Link/Rect[72.0 618.0 131.6 631.0]/Border[0 0 0]/Dest[120 0 R/XYZ null 818 0]>>endobj
+32 0 obj<</Subtype/Link/Rect[108.0 604.8 222.6 617.8]/Border[0 0 0]/Dest[120 0 R/XYZ null 735 0]>>endobj
+33 0 obj<</Subtype/Link/Rect[108.0 591.6 202.4 604.6]/Border[0 0 0]/Dest[120 0 R/XYZ null 522 0]>>endobj
+34 0 obj<</Subtype/Link/Rect[72.0 565.2 168.5 578.2]/Border[0 0 0]/Dest[126 0 R/XYZ null 818 0]>>endobj
+35 0 obj<</Subtype/Link/Rect[108.0 552.0 208.2 565.0]/Border[0 0 0]/Dest[126 0 R/XYZ null 735 0]>>endobj
+36 0 obj<</Subtype/Link/Rect[144.0 538.8 280.4 551.8]/Border[0 0 0]/Dest[126 0 R/XYZ null 613 0]>>endobj
+37 0 obj<</Subtype/Link/Rect[144.0 525.6 292.5 538.6]/Border[0 0 0]/Dest[126 0 R/XYZ null 231 0]>>endobj
+38 0 obj<</Subtype/Link/Rect[108.0 512.4 192.0 525.4]/Border[0 0 0]/Dest[129 0 R/XYZ null 583 0]>>endobj
+39 0 obj<</Subtype/Link/Rect[108.0 499.2 178.6 512.2]/Border[0 0 0]/Dest[132 0 R/XYZ null 753 0]>>endobj
+40 0 obj<</Subtype/Link/Rect[144.0 486.0 219.8 499.0]/Border[0 0 0]/Dest[132 0 R/XYZ null 671 0]>>endobj
+41 0 obj<</Subtype/Link/Rect[144.0 472.8 222.2 485.8]/Border[0 0 0]/Dest[132 0 R/XYZ null 309 0]>>endobj
+42 0 obj<</Subtype/Link/Rect[108.0 459.6 179.2 472.6]/Border[0 0 0]/Dest[135 0 R/XYZ null 799 0]>>endobj
+43 0 obj<</Subtype/Link/Rect[108.0 446.4 280.6 459.4]/Border[0 0 0]/Dest[135 0 R/XYZ null 517 0]>>endobj
+44 0 obj<</Subtype/Link/Rect[144.0 433.2 249.7 446.2]/Border[0 0 0]/Dest[135 0 R/XYZ null 423 0]>>endobj
+45 0 obj<</Subtype/Link/Rect[144.0 420.0 305.7 433.0]/Border[0 0 0]/Dest[135 0 R/XYZ null 349 0]>>endobj
+46 0 obj<</Subtype/Link/Rect[108.0 406.8 257.1 419.8]/Border[0 0 0]/Dest[138 0 R/XYZ null 454 0]>>endobj
+47 0 obj<</Subtype/Link/Rect[144.0 393.6 221.6 406.6]/Border[0 0 0]/Dest[138 0 R/XYZ null 280 0]>>endobj
+48 0 obj<</Subtype/Link/Rect[144.0 380.4 216.7 393.4]/Border[0 0 0]/Dest[141 0 R/XYZ null 483 0]>>endobj
+49 0 obj<</Subtype/Link/Rect[144.0 367.2 224.7 380.2]/Border[0 0 0]/Dest[150 0 R/XYZ null 631 0]>>endobj
+50 0 obj<</Subtype/Link/Rect[72.0 340.8 171.0 353.8]/Border[0 0 0]/Dest[156 0 R/XYZ null 818 0]>>endobj
+51 0 obj<</Subtype/Link/Rect[108.0 327.6 213.4 340.6]/Border[0 0 0]/Dest[156 0 R/XYZ null 735 0]>>endobj
+52 0 obj<</Subtype/Link/Rect[108.0 314.4 237.6 327.4]/Border[0 0 0]/Dest[156 0 R/XYZ null 601 0]>>endobj
+53 0 obj<</Subtype/Link/Rect[108.0 301.2 198.5 314.2]/Border[0 0 0]/Dest[159 0 R/XYZ null 645 0]>>endobj
+54 0 obj<</Subtype/Link/Rect[144.0 288.0 248.2 301.0]/Border[0 0 0]/Dest[159 0 R/XYZ null 523 0]>>endobj
+55 0 obj<</Subtype/Link/Rect[108.0 274.8 219.9 287.8]/Border[0 0 0]/Dest[168 0 R/XYZ null 638 0]>>endobj
+56 0 obj<</Subtype/Link/Rect[108.0 261.6 202.7 274.6]/Border[0 0 0]/Dest[168 0 R/XYZ null 531 0]>>endobj
+57 0 obj<</Subtype/Link/Rect[108.0 248.4 199.1 261.4]/Border[0 0 0]/Dest[174 0 R/XYZ null 285 0]>>endobj
+58 0 obj<</Subtype/Link/Rect[108.0 235.2 236.9 248.2]/Border[0 0 0]/Dest[177 0 R/XYZ null 799 0]>>endobj
+59 0 obj<</Subtype/Link/Rect[108.0 222.0 257.4 235.0]/Border[0 0 0]/Dest[177 0 R/XYZ null 719 0]>>endobj
+60 0 obj<</Subtype/Link/Rect[108.0 208.8 263.5 221.8]/Border[0 0 0]/Dest[177 0 R/XYZ null 625 0]>>endobj
+61 0 obj<</Subtype/Link/Rect[72.0 182.4 132.2 195.4]/Border[0 0 0]/Dest[180 0 R/XYZ null 818 0]>>endobj
+62 0 obj<</Subtype/Link/Rect[72.0 156.0 124.3 169.0]/Border[0 0 0]/Dest[186 0 R/XYZ null 818 0]>>endobj
+63 0 obj<</Subtype/Link/Rect[108.0 142.8 155.0 155.8]/Border[0 0 0]/Dest[186 0 R/XYZ null 735 0]>>endobj
+64 0 obj<</Subtype/Link/Rect[108.0 129.6 172.8 142.6]/Border[0 0 0]/Dest[186 0 R/XYZ null 549 0]>>endobj
+65 0 obj[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
+]endobj
+66 0 obj<</Dests 67 0 R>>endobj
+67 0 obj<</Kids[68 0 R]>>endobj
+68 0 obj<</Limits[(1)(idd.shtml)]/Names[(1)69 0 R(1_1)70 0 R(1_2)71 0 R(2)72 0 R(2_1)73 0 R(2_2)74 0 R(3)75 0 R(3_1)76 0 R(3_1_1)77 0 R(3_1_2)78 0 R(3_2)79 0 R(3_3)80 0 R(3_3_1)81 0 R(3_3_2)82 0 R(3_4)83 0 R(3_5)84 0 R(3_5_1)85 0 R(3_5_2)86 0 R(3_6)87 0 R(3_6_1)88 0 R(3_6_2)89 0 R(3_6_3)90 0 R(4)91 0 R(4_1)92 0 R(4_2)93 0 R(4_3)94 0 R(4_3_1)95 0 R(4_4)96 0 R(4_5)97 0 R(4_6)98 0 R(4_7)99 0 R(4_8)100 0 R(4_9)101 0 R(5)102 0 R(6)103 0 R(6_1)104 0 R(6_2)105 0 R(idd.shtml)106 0 R]>>endobj
+69 0 obj<</D[114 0 R/XYZ null 818 null]>>endobj
+70 0 obj<</D[114 0 R/XYZ null 735 null]>>endobj
+71 0 obj<</D[114 0 R/XYZ null 654 null]>>endobj
+72 0 obj<</D[120 0 R/XYZ null 818 null]>>endobj
+73 0 obj<</D[120 0 R/XYZ null 735 null]>>endobj
+74 0 obj<</D[120 0 R/XYZ null 522 null]>>endobj
+75 0 obj<</D[126 0 R/XYZ null 818 null]>>endobj
+76 0 obj<</D[126 0 R/XYZ null 735 null]>>endobj
+77 0 obj<</D[126 0 R/XYZ null 613 null]>>endobj
+78 0 obj<</D[126 0 R/XYZ null 231 null]>>endobj
+79 0 obj<</D[129 0 R/XYZ null 583 null]>>endobj
+80 0 obj<</D[132 0 R/XYZ null 753 null]>>endobj
+81 0 obj<</D[132 0 R/XYZ null 671 null]>>endobj
+82 0 obj<</D[132 0 R/XYZ null 309 null]>>endobj
+83 0 obj<</D[135 0 R/XYZ null 799 null]>>endobj
+84 0 obj<</D[135 0 R/XYZ null 517 null]>>endobj
+85 0 obj<</D[135 0 R/XYZ null 423 null]>>endobj
+86 0 obj<</D[135 0 R/XYZ null 349 null]>>endobj
+87 0 obj<</D[138 0 R/XYZ null 454 null]>>endobj
+88 0 obj<</D[138 0 R/XYZ null 280 null]>>endobj
+89 0 obj<</D[141 0 R/XYZ null 483 null]>>endobj
+90 0 obj<</D[150 0 R/XYZ null 631 null]>>endobj
+91 0 obj<</D[156 0 R/XYZ null 818 null]>>endobj
+92 0 obj<</D[156 0 R/XYZ null 735 null]>>endobj
+93 0 obj<</D[156 0 R/XYZ null 601 null]>>endobj
+94 0 obj<</D[159 0 R/XYZ null 645 null]>>endobj
+95 0 obj<</D[159 0 R/XYZ null 523 null]>>endobj
+96 0 obj<</D[168 0 R/XYZ null 638 null]>>endobj
+97 0 obj<</D[168 0 R/XYZ null 531 null]>>endobj
+98 0 obj<</D[174 0 R/XYZ null 285 null]>>endobj
+99 0 obj<</D[177 0 R/XYZ null 799 null]>>endobj
+100 0 obj<</D[177 0 R/XYZ null 719 null]>>endobj
+101 0 obj<</D[177 0 R/XYZ null 625 null]>>endobj
+102 0 obj<</D[180 0 R/XYZ null 818 null]>>endobj
+103 0 obj<</D[186 0 R/XYZ null 818 null]>>endobj
+104 0 obj<</D[186 0 R/XYZ null 735 null]>>endobj
+105 0 obj<</D[186 0 R/XYZ null 549 null]>>endobj
+106 0 obj<</D[114 0 R/XYZ null 698 null]>>endobj
+107 0 obj<</Type/Pages/MediaBox[0 0 595 792]/Count 30/Kids[108 0 R
+111 0 R
+192 0 R
+195 0 R
+114 0 R
+117 0 R
+120 0 R
+123 0 R
+126 0 R
+129 0 R
+132 0 R
+135 0 R
+138 0 R
+141 0 R
+144 0 R
+147 0 R
+150 0 R
+153 0 R
+156 0 R
+159 0 R
+162 0 R
+165 0 R
+168 0 R
+171 0 R
+174 0 R
+177 0 R
+180 0 R
+183 0 R
+186 0 R
+189 0 R
+]>>endobj
+108 0 obj<</Type/Page/Parent 107 0 R/Contents 109 0 R/Resources<</ProcSet[/PDF/Text/ImageB/ImageC/ImageI]/Font<</F4 4 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+109 0 obj<</Length 110 0 R/Filter/FlateDecode>>stream
+xÚìÏsë8rÇIŠºÌ‰öŒî´üt§çyšÝ­ÚJñÙfvçÖ$ÁÊ!•S*Ç6•üÿ±~X"H€èn
+ ‚B¹ü29×$X,0X
+Ú Ñ ê£s5Ѭåà‹+›— 9Ä`™ãÅ`ž‹,öD.1?V{àÊàŠRç\uD98æY\‰c®}HÙ8媣`Jé++D91WTθš°ŒÒË TÔ  LÔ‹Š4ðZåªBÃ2t,OãQ ÔxŒsÆq‰±FãzX¢Æ#F,Ï%£FL×]w÷Ûí—×cy>|ðÛa0IwÅ÷_†ƒüxÂ[‡ÿîŠG’þÅ˃ŸßÝuoœ*=[ í,¸ö~ºªÛk[ça=xì.Bú±àæØ\ìPƒ–.Î$[q¹¸¡==·aMJ&³»XéÔœ.K™\¬@ž› É貘ÇUùÑv‡]¶cqeSÙàÕ›eN¤ܯjؤ½s’  ®|²¡ÅϼJé\Í Xd°˜ÎEùØÍÉÜZ9ƹfÂ"‚­¨\Õ FÈ1Å’È•Ï…EÛи'’ –Y¹0p7ñú¥uZj+Cg‘¼c,Òé« …«rÎX•½ÅÍÁ‘jÄ¥{.B J<—°ÒlKÃ7D=Ì:¸ÎåÛÁÉ %i=•œkˆàBf½ .Ú`Ø`¹Š¹2O”,Á W­Ç’ó ìÍПR4q‡ãÚa…„–¤8®
+¢6(®–KH·Kh%ºÖRÕÓbQ®•–ër›®Îa‡Àà ¯Çé¹ £^ >ÜÌÑ–œáµCrí´\Mçù
+.A5C#WªçÚŸòáªÕr[¬àª©fhäŠõ\盽§5Rì•K,×êZ[Éu6Ž;êÊ12BªWNZ,×:{—'%WG¤(³ÔgO@• ž«x j®Š5K5†ˆëWC6Ãq®ZÂp ÖbI…Ó5 ÊFÒâ¹Ä»Y¨¹®&EZ,ip-â\9%pžê¹*ÖšNè(;
+\n£á¬5;c+\ŒôÅq®æ"
+®K‰kvª‰@“¤¥pµ¥×qgéB™ÐdcMãº(½Ž«àôWòD@“ë¢ô:®Œ3¾êÙwCÅuQz WÍÛÃÈ0’ ¤h#!r]”^ÃUðûMÏ%s Æð2r•^Õñ6g
+ÌÃ’lì¨\g¥Ws5Ì]Œ
+㘴¶ÑR¹ÎJ¯æ½ØER2WÁ^f®“Ò«¹rfæŽÀ8f DQ)ëdÝ…~^¹e¢IàkSYŽ­\§Öç*®³Ý FÈ‘#š ”(ªT?º²k ±ÌukDŸ+?krN7Ä="à
+vt6מÍ•4™¿peM/\œ¥ùp‰'óg®úÃÉ<–k2ßÎ-‡Í‹ó·•Ü:\[æ»uîªßFwÒ²ßmµwpœOm›.Ò,²
+£®[‘"K~­lâŽÃÕ¿(—óM
+-áðì8—Ðpz®”ÃÕ¿¨7RNf™
+E2þW=/WÁá’.ªn»Ê·EÞÙ¹j—t‘趲¾Æ6ÓJ\‚Ã%_”ßö¿ž#Wé“«{œÍ%_T ö+Càª8\òE½H0?qÙL—Q\Ù8WÃá’/’gÆé>®Îq><Wï"©ÃÊbB®ƒ¾‰‡K¾HN#‚—qy#¶äÊ£îíZðS¹zýèN«i¸®oÐ4Qp¸ú=w–"p…S^Nù‡?ßÞ?oäZJ¬èžó2Ûâr{Ï\ù'×'׸V”+]&WöÉõÉõÉõÉõÉ…ãZr}rùçŠì¹Þf>ès¥”ºór=ŸÞa?bšK©;3×Cÿøšü²¡ÎæuÕ»êªHqPJÛ:k®|¾!çéw·§†u\y1Ô×áŒF~™WsÛìø9QeäÚÉÔ—Pì(É/4¬®KüuòÈȵ™‚ TSë¼;Àz齺 ®u+ M?\™j« ºã “Òdúu\©l)^¸š÷§»nÝî«ko»õʺ ®Dºµ®êºQ(¾unÚ‘À[v…².Ÿ«öÉUtv¬÷7ïÒqY·äsM]Õ2¿ú®òöNå“+ª›v^Ȭ©K㊯ۧoƒ8ñÆ•u—áòžÒߌ§©KãJ®£0qÙÄóÒŸá¦×VËIÚªº$®Õõq¼=™t®êöÓ5ä$mU]WÚ}tk;.CúáõÏÍÝ[ÙtĪ윑Ò×%qÏ]ÓÆ+W¢ûѧ“T<¶ÔçÊ/ƒô8„wÞ¸2]:ÎeO¹éÀd#©;x®ýåóŽŸ|ðÆuŽš¸1‘†‘¶.‰«¸ÈÐÑÒKo\—½Îûƒ:À:eR¼Ë¹¶.‰ nÙ¯qëë¢Z{þ⇬ã‹õu•\ÃÄÎEu¯G÷å«“Òkmñîm®¾X_—ÂU_ `ü`;®±}½n=é½ÄÕ0‹V[—ÂÕ\~<ºeK®±}ØByŽCš—ƺ.]_ˆ¾ç²Ú_ÚÌÒ|Õ¢¯Kàj/Žùô[\½,Ú¸ôdŠ¨Ká:;æ“[öÉÕyÑ–L¡ú’9]]
+×Ù1×'ï“ëý ì —J•l:ZÇuvÌõ©¾—1ïë9Sš\ùPÔuñ~ùäËãÿc—‹|¶—‡á¤¦ÒLrTu \ÕÉOnÙ?×­µ«^(u@Õ%póÉ-OÁõ.
+‰vÐŒÕ%póÉ-[raß-Z‹ÁX]—x÷b멸n‰×f®^]×Ù1ŸEr”Ë&ûø*™ìŠµ\cu)\o&˜4·õS/\²{ãb§®.…ëM2âúì-¹JÜú¡k¬.…ëØÓUt}Q7®D²­x”KW—Âçð±‰ËæÜM.ý9Õù±º®ê|²&±çÚO¿6ÝyÈJË5V—ÂUßœº7®ªÛŸûÞmz«Kájn³Q.›sˆçkãï·M­–k¬®!ž—Þ¶%ä]A ®qö¶ ©Ÿ,ŽÕ¥puÞ ë«™«õ¹Fê’¸r—Õ¹ìf¤jŸk¤.ƒ«´çZãÒmú¡ÿ VÑ×%qƒßxàjô
+3àj¸ëQ2\ŸÊ(—Õû7䵘_ÚQ.}]—îý¬n¹:}l \Úº$®úêÓǹ¬²Þè!Ón”ØÔµ*–ïí¹Ê×ëMóU|–ïY
+¶X¾ë“kz.óë–—ÉUüÓr•‹ä2/pÉe3aþäšžËjb¹h®õ"¹Ä?-WºH.ólõA¹’ere0ðµü>ŽO®¸ö0ðÅ}Ïn‘\ðA¹ª Z~¯Ù\Åô²×å÷ÐÍULMÞá¾70¸@ªrÃ\ e’ºÒò{9ç*Š+[\Àa
+%°ß{ZÀ‘›ì ù=Å¡­´e&=@~¯thG„â‚¥Âèo‘ßoXÀQã¸÷}ô•±
+-V裥ÈaÜ媗#ˆ(Â-ôDˆ *âƒ-ôéBäpÝåB} Â(»‚-ôÇ¥€VÏP„#C̾:\ÕB")£œ· Å }ºÙHd.„Ð'‹TæB,ÝÄ‹u+_†pd8
+h)‚¸xí\Åú,(ÖCéÑg­¡% ÇÜ ýŽhIÂ1ó
+œéöÔ1GNÀY™8樼A:©Ôï˜gB©œÇ ÜÏ™.H,8Ž¸Ãtºc wàÊêd†kÍÁ…Tú‰:,c=càÔ4Æ €?R§é°ŒÖÛNÓaÓ™_‚& :C¢Åqí±`»0ºk…äªé0‘qƒph­ ±÷UKó„ªY.´v†èuâ,øV­!zÕzìÓÝ ¹ð†èQ:j ›ÖÖãrfWŠØ ¬OKüñÍPË%¢¹-±±’.h­ Ñ“&æV>¬­'BõÊXZ?Þ¹±´°öõ^†˜È,e \<1÷3–½í#×½{&ܸ¥rU¤s©ÍJÉ\"š Ldö
+\EiW°/)áøf08Ô&[õ cœFwžÿf<[l2êR6W›qÀúßtŽ*OôÛø\‹+Š¿S;‹añcq€‰‹ÙaQt_zî¬ñ¹‘‘«ˆ¸ÿ¥ó/9k·6\"â—{”56¼OßYqYtØqœ=šÌñ…Ie˜>˜¹l:ìŒöªÿ짌ý¹K.ê4Li¯ƒ~/ÛÌæqµ¶\Mä¦ÜÝo·Û/¯oåe»}È,?mcÍå¢ÃœSþ#†KȵqÀe'‰³tŽ+¼Û9á
+®ÃÌK_8.±´îBrvm¦(ˆ=$W›…ÄupÇU/«»Ð\¬•O_ºäj‚áÚ´.¹‚ÑzÜöžKdKÑxw gÑ q!Øý^
+—XŒÒ¸°DôÖ!‰kvKÄgиš…X!•kæø—°MäšÕ)¹/T®9½3eû‰Ê5c`OJš#sÍ6Ähi/t®™†1±ŒÁ5Ï#îí2¸fbÔ,%=pÍ`sM®ôT9×ÄÚÁÈ
+·¶øhß"7\o¶è®Ë’CW+Âé,—\ŽºìþІÆÕŠ'küâª-.¹ø©ÕªGw-qËe‘^HÝž“‹Kæ–ʇìþ‹ë6øà"æÅÇ[ï¹ðÃõV^qÙññýw/·÷Æuê5ƒAÞm_}ÝÛ'ש۞¶wê~z|õy_ß\çŽ;žpØ>ÜËývûøúêý–“pÍPàÿ
+ä
+¢Ûœ®e Ål¥î›VB&Í8Ì.Z=.vTóVÜ?rˆNFÆiÊ~6)²Œ EŒ(Âbº9oÌ
+µêí«ÑJ­ºGkÍ ÿý¨eÕãp³€œ'„ù¾¿ƒÃ4AåJ•4R?eçÔ\xï QTñˆendstream
+endobj
+110 0 obj
+6287
+endobj
+111 0 obj<</Type/Page/Parent 107 0 R/Contents 112 0 R/Resources<</ProcSet[/PDF/Text]>>>>endobj
+112 0 obj<</Length 113 0 R/Filter/FlateDecode>>stream
+xÚ+ä2T0
+ár á
+ä
+endobj
+113 0 obj
+31
+endobj
+114 0 obj<</Type/Page/Parent 107 0 R/Contents 115 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 4 0 R/F8 7 0 R/F9 8 0 R/Fc 10 0 R>>>>>>endobj
+115 0 obj<</Length 116 0 R/Filter/FlateDecode>>stream
+xÚ­SMSÂ0½÷Wìp‚©¤ÔÞÐá¢(ÅñÀ%“nKœ6Á¤‚øëÝ´ øqat:Ó¼}oß¾4σ] zt#…w{§W:§„D½â¤É`&ô
+[ñS…²^¶#æî3˜$¨J™JÁK©U]c»Ò ò+©x)-HU¢I¹@HÐÊL¹‡0rå˜hñR¬Œ^KB-¹Ì1”îjSðÒž@Öòì`«Ä‘2à ןº²nmPhµvö´²®Ê%ÂPµ›ßLajÈ’T̶¶ÄÍÆp>5-x@c-šÑÿ@Øñûu
+endobj
+116 0 obj
+384
+endobj
+117 0 obj<</Type/Page/Parent 107 0 R/Contents 118 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F8 7 0 R>>>>>>endobj
+118 0 obj<</Length 119 0 R/Filter/FlateDecode>>stream
+xÚ+ä2T0
+áÒw³P04TIS045×3S072PIÑp VðÌ+I-JKLNUpI-ÎLÏQÉE™%™ùyš!Y\º-ºP=F 13 ˜o¨œœ_
+u á
+ä
+endobj
+119 0 obj
+113
+endobj
+120 0 obj<</Type/Page/Parent 107 0 R/Contents 121 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 4 0 R/F8 7 0 R/F9 8 0 R/Fc 10 0 R>>>>>>endobj
+121 0 obj<</Length 122 0 R/Filter/FlateDecode>>stream
+xÚ­–Ks¢@Çï~Š>f«”ºø¸I¶b…’Ó^Fht¶`Æ*ñÛoøܘªÅ¤<¨Ðóë÷þ4\hÓÇ…žâ¼q5®§ðÚ¥tÇïu!J®<˜cŠ
+EŒú[ô»4q{•IËw¯4r\?!Ld\ä( 3\ŠÊ¼ ®»5÷|§dF+„Tf™|åbYLŽ× v^XlÀ¬èÒÎfXã-ØëS{ö/ÏïÁ©ç+ë¡5ž-×q‡•¿±)_ªò7c‚-Ѳ!Ș8åSàn§ÊóCøÃdr7Ú` JYŒ0AÍ—Â~ÅŠ¯µ©å"8Žÿ!_g‡zÉèv}f8šY¦ó¶ \¦æ•)„Q’sÁµ¡úH¥m}
+–]À?-ËŽþåƒÙ9t äR±<·ƒuqÔáü:ĸPÜlh!ÖR™ ¸QpŽ¡¾tìÂçú÷¬ñ3m{™œÇ¾ÕÎÛ»Îí4¡U-c·íô+]ðàɬPí…Aÿ§()Z' Áð•ª0Jä‚Fj–ÉÐìp»±ÇÙÁ”gS©rf \cÌS—;×Ü£ëtœZ5~çø‘‰eAtÚ&D+®¸M¸uVOR0„rL$û¤B4ð´ÆJðt]Þu9··"–‰íŽGŠ ]{ö,+`¥âÚA½+x‚—qf2Á¬ (Äœ ÃãzÉͧcúÙ4á‘ <Ì
+x &¥gêØ]¿EžïúM¸ßÐ|3ÕôѲjµà>ŠÊ©¸„NݲÇaTPUì•U Ó<.˜ð¥UêQLoBú»ŠÙß2·*èµ=çì;T·ßvÝêØÿ·Qãgã/4s¯6endstream
+endobj
+122 0 obj
+706
+endobj
+123 0 obj<</Type/Page/Parent 107 0 R/Contents 124 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F8 7 0 R>>>>>>endobj
+124 0 obj<</Length 125 0 R/Filter/FlateDecode>>stream
+xÚ+ä2T0
+áÒw³P04TIS045×3S072PIÑp VðÌ+I-JKLNUpI-ÎLÏQÉE™%™ùyš!Y\º-ºP=& 1#C ˜o¤”š–Z”š—œZ ’r á
+ä
+endobj
+125 0 obj
+118
+endobj
+126 0 obj<</Type/Page/Parent 107 0 R/Contents 127 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F0 3 0 R/F4 4 0 R/F8 7 0 R/F9 8 0 R/Fa 9 0 R>>>>>>endobj
+127 0 obj<</Length 128 0 R/Filter/FlateDecode>>stream
+xÚ¥VQâ6~çWŒÒN‚t=–í>ÝÑEâ¥êu©îe_LâWŽÚÎÿ¾3v Iv¹Ó© “Œg>óÍLþÍà¿3x˜Ãb Y5ú¼ýºy„ùì
+|²|¸‡]>^ÀV9n“aQ°ŒÛ»¼íì!ØN—³tî­Ó¬KfX†¦ðÂl„Œö÷0›µöóeê½ïJÙÅÞ¢}AöóB( *V×B`ÏÝ‘s«é^¸ë LåàÐËßJd:x›€6q+aÀȳE@íim
+­œMAŠU<‡Æ
+ǤÈüzÖÁ{$ù¦ŒäÙß3¦¨oÌ ï¤6yly®K 1¿t¸XPþä™6†ÛZS ép‹Œž|áDÕT¤ƒùÇå@0úkInQŽ”:ŒÆêø`tSOb6ûþ" ¯cŸÊ
+ü©­{Aá×nOŠ
+é#’Ó×T_ádðɆ‡|–üÄrž ä>íÖQÞï1WCþ„õ Ygˆ›D:m’ $Æi™ø&â—,A*s‘!´’¼pS§§FJ¬eÿG×tßïéÝO†íCþ;£6pîWÔ RñKžxŒ¹nö¸~‚°€Š3ÕjÜEÎ[î(2ì}–4ëÿCWºæõý±ÖX±î\s·¡ÿá«`â¥?®ö/'™¼'î7¥òVã1¸%Í6Ôiñì^q5Š»d
+l‹ÞœÀÝZÉó°#cB2" È °‰äž¥¦üü¹†[êFRgÆbŠþuÌÓC
+É˹Bø Iô™àøzóìcÎZÛ£r
+ð' Wèí¾Ó¸Ÿ{&o*5zèÊÕëkÿ ²[°r^døÓ-íÆ«„¼Ø¦®µAù s®ùìÇׂCmÕað¡}}»õ²w¿ºKÑm>Òõónôeô7Y7Õendstream
+endobj
+128 0 obj
+1007
+endobj
+129 0 obj<</Type/Page/Parent 107 0 R/Contents 130 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F0 3 0 R/F4 4 0 R/F8 7 0 R/F9 8 0 R/Fa 9 0 R/Fc 10 0 R>>>>>>endobj
+130 0 obj<</Length 131 0 R/Filter/FlateDecode>>stream
+xÚVÛnÛ8}÷W üä¶×’ïÛ§nÚ
+î¥Ñp™/A[1{£2ž)ÉÃz×ïvÿ¢GIB.'ès’®¦ òyO>ãóEû|LgpÑ©¯Ì¬
+ˆq(ÄV¨c]::>4ÚCÅêZê7˜n¬®6hïMÜ"£÷ÀÐîYVM&‡t¹¢óf0Iæ1}^2˸Ögö"¸RàK<M0^BaMSÃÖ+§0<!:´ùbœäVÖþæ<%+¤Hž>½ƒæ2ƒL5Â…ìKñÌ2Á%r?½&lv¦øã©V·üáñÄó–¸*oìp Cë»±qɆHe&9F‚VJä~âÍÄÊ¢ôc7úŸöæj>X¶õ¿HÌ‹gÿv¸ßHP¿ ÕáU‰aˆ13Í×ï!. L»XVâ,²å’ `Ž4îïà\×›8[Ý™ÆJaÁk‘3.Þýsè‚qþ.µÿ’ÉøWâ~Õ*¯5~:Ü‘fGº¶Qq5ŠYèV(ð_äÐF«ãMbì“ŠY˜"ƒÈ&’{Tá00x9 ®4 ö6Š^IçQôO#1-¦0|<Vþ$ú‰Â ýØÇšµ¶©ÔMè³ 7ÄGfZ”2N´¸B‡Š¥Æ’“ôZÚ·¬[Ú³é†|‘Ë¿™.V¸—J¸k/*D]¤N¦9™B&r©#ɸdògÁ‰8XN ¥Ú‘‡ÝLF¥9‡~ÚQÃü‹ç)uë‘Ø¿ÉZ³
+SÜ©h¹|Ž#é,kÞÔîû0N% Ï•tØeЮÆ~ÏI§
+”ÐÔºa™S ϱ$7ÇÇaÖ˜vXÜ 5Re,W‹°[ã¥So(lßĶ–ŠzMfr*4é• N¤Ž€78‹µGµáŒ2…–?Ñô<àþŒ…äm!Ó æñÈSºZÃM¿ðz³^\ƒ.òÿ5"I—³î¤;$í™w‡ôHÙ²êYw‡l:A¤3“Íf¹$ý`i?ؼlѶì[õƒ­ûÁzÖm۳ܳž¸ž•Kz–.éV»ÆM˜ãRvùüÄþ¦ÝKÒ-ÌÕ¦}[Þ}ýòGtx±Nšná´ýLÄX×SüL_§3ÂõÌ·sô3{ûûi7øgð?…uo=endstream
+endobj
+131 0 obj
+951
+endobj
+132 0 obj<</Type/Page/Parent 107 0 R/Contents 133 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F0 3 0 R/F4 4 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+133 0 obj<</Length 134 0 R/Filter/FlateDecode>>stream
+xÚ­VËvÛ6Ýë+¦ìÆiMšzԜӅêØçh'­ÕMã. ¤P“„
+€qœ¸ÿÞ;
+¡9Ý/¾nœjZ¬,Aè0¡ºv‡^+u&²%5º‰•h®ÜoCô"”"•#]U(„…Hm»’æ€8ba!j å¤A¤›ÄgÓ‹iBÓªòšõ"-tUéÞ ½çE22ô)ª‚MdTß/wÅãV!Ó¢^‡>Hé„Û
+…Æö½íZê¸ë”áOGh¨Ãt{ÿš¡µM! ÏÉ]Éo™Q+Ö„¹q ÄG£t÷Õnrœ&'äøûÙ|ðëà_.ëAuendstream
+endobj
+134 0 obj
+1063
+endobj
+135 0 obj<</Type/Page/Parent 107 0 R/Contents 136 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F0 3 0 R/F4 4 0 R/F8 7 0 R/F9 8 0 R/Fa 9 0 R>>>>/Annots 17 0 R>>endobj
+136 0 obj<</Length 137 0 R/Filter/FlateDecode>>stream
+xÚ¥WKsÛ6¾ûW쨩2Q$õN§‡6±[’¨±ÚS. JHA€%@»ú÷Ý@‹¢+)ÓŒ­ˆûÞo¡¿oˆñ/Éœþ³òæ—ÍÍønÉ6îÌ‹h›|8‰¦ð±²B+¸’›W›/œB’xà(#oÿX?@É„²ø2Ð^r^Ås¨j|Ìk`*í…$
+]gÙþAG!vÏÁŒåeü0ÎTœL¢”…hŠîé®Jø<ôÄÁƒá˜Ûlœ5•Ë*ˆ9õqøù°šƒÔ,GÑ…¨}–K©Ÿp½=ªžMÞ…Kj¿ÿíãûÛqt^gÂnZgœL'¬æ•dǧÚð3>; †·g˜C°’N+# ÜR
+$Ji…øìdLÁ–c*ðUøçÎk¡voZwVd.–Ð(uUðÅ‚"Þ¯Ÿ™lz‹(ŠN´&ÆÑòDÄëÈ2•}“¬‚5òÛ-:Jù¿Fs;rÑ
+]Cuí¿å;¡Fž„Ýà è`Fr‘1‹é äÁ”Nú\¹Þ€/zk~öŒê¦”ʧW ÝÕ¼®EþBÍ•~Š¼oì%|À`õŠ„—î³Q ¡mÛÿ¥ôªÐn[êŒI@sѤóòïCú:BÑ2u
+(j’Üs¼Õ”[÷…XÃ…Eå¸Î46xS‘Iº€lÏj–Ñ ò\rµ³û(¯›¹šáåÔë¾?\À38ák÷L…ý:2¼B¹–ù*X!jp…b‚»GMC N¢¬µ±Y-* ë8l÷à:?_9üy8X¯ß ë|ïçnoË[«ØVHaî ;/(›ø ̱¡V÷jÍ4U¥kÛž™Šg¢œÙ¦æ>´'¥Øq,f´˜ÀcP¦Á§É¤N³(´‚8ׄ¿ÝÜÄQŒCŽÞzûô+$³%LW³(†’ 7¿’ðàGà1réjÞæ™4´„Z2KuÜÆÇõÆy…«¸£0Ì1™G…/ñ]â1ºwßA”0I–Wð]âgq´¸Œï J˜ÆÓ+öwˆO§WäwˆŸÍ±ÊOÒ;Rïžsè’ós®±D7<ÛЖÃw³8ž¼ùš>;—ɳ‰Ã¤Ë)aN-èÿíEPÂjIEuÝ` &)%äN.ã2õMRÕð'ò}N£‰k®gÉqÍðr§¤¾5oÿ±\G?HwTÚ_q±cyn’‘‘‘šf‘\·ŸKØÄ
+endobj
+137 0 obj
+1208
+endobj
+138 0 obj<</Type/Page/Parent 107 0 R/Contents 139 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
+139 0 obj<</Length 140 0 R/Filter/FlateDecode>>stream
+xÚ­VßoÛ6~Ï_qp_œ¡b,ùwÑ Øœ´ÈC¶¬v È -Ñ6[‰tIÊŽÿûÝ‘’b+µ»Cà˜wß}÷ÝQß®bèá_ ãú#H‹«ßW7¦Ç°XáÎh2`XdÝ>²„õ!-·öAg"ÿ½,–Â\/¾ ý ±’ýb#-HåÄZàι,
+–J+2XðìJæx]èµá¡ÀØqŸ%ÛiàÙ—Ò:pºtÛÒÁJ#hÅÒ« ™L_g2øÌÆÈârÖ“Uë3I§:×7ù¡ø±ï‚ÓLÉ'#J¼K#¬ÎK'µºq‡­€L(+ÝÖ¼(8½~bü$PĸFHEŒë× ®“älä=7p„íQµ}îô\¸ÊÚƨm¶ãy‰™ü@ëD r¤8¥¿—y–r“1ø7{i1p€‚ÊUp—n@«š˜š’†çºÂŸÎ¢DrOOuD&ùâ;H›Y$u%jK*_ÇÇ[R˜`•°.ðU•ã_å v†­¿ª)­«t\….UJƒ-‘$·áîœdVOÝç§kø¹ñò<צ“sX~$ TZ¼Û]®7¯,Ûšk§É¡ÿÜg¸²¤|î³Â/#Ÿ}›R fW½ÎÒ<hÄßê÷†l>¾ÝæR¼"Ÿ¯\{Xu›8§H~4 †~ü%Œ}%¿ö$0â[) ¢y™™°).‘ŒýFbwÁO= fŸçp{ {l?îjBjM‚xvÄ‚V–µx˜•Æ RòHj黫žH˜õ:Ô!¸ˆ;/yŽ+ìƒ^=ëG0O7"+sœ±3­Vr]šP¦Á^ȃ5'à™%•µ<öB‰X¯áZ“ï¸Ìùs¬†²} iέ¸ ®°Âìd*ì»Óê+·²gï&YÕLùêx6CxÔMž×5¨‚סá«Ò{ESÌSy°Nì@tì›Ô‘ýçøþ VÞB†’I6’~lÃ^› —Â¥¬¹Ý1Ó—àÔTþŒ\¢e4ð±ñM"ê“‚‚X çKÒRαaÀ‚++­³$&åÚÈ=>|ôë|v;‚¡L ?&æuÜ8¼mÜ&$ K”ÏS·ó¦ótÝêštà ÞO‹Gî8ö§Ò*Zæ\}õŽƒ_ûâXñÂ_V¼%ðPC¹£»=Ïõ>MâQ?ÎÂ4 [Ø™-DÍa,¦‹Ð‘{§uVé~XÑ7¢~’q€{[Ÿ"£}õ‘_zî'ζ¾,ïW=†o /ÿ>}„d€>FcF—æk—ô †žÇ6îOÙ4÷“„ŒýËc]\k˜„‘ò~FµõtýÒÊöýßóCôQPÏÇÕÍ¿6ùã^š²ÒçSñ Îp9¸œŠ7©¼ûT&U*ñpŒ&ãÄóF4õʧ^¹VÐb9
+G¢êŒ¿9Âp¿kÆ7õMõf¤&=„ãL«üþ¼úÝneendstream
+endobj
+140 0 obj
+1139
+endobj
+141 0 obj<</Type/Page/Parent 107 0 R/Contents 142 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 4 0 R/F5 5 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+142 0 obj<</Length 143 0 R/Filter/FlateDecode>>stream
+xÚ½XÛnÛ8}ÏWÚ—¨Ýmm äÒÚ¢Û¸ XЫ•DW¤›æïwfHÙŽYrX´¹@:¤çΙæÛ™Ï<øç³0ÁÿY}v¹<;1ßgË‚s7bÉ"tl™?ûãZ|[é«Š+Å^‹?Ÿ/¿œylæ‡n@ˆszG}?v–À¼¸Ý¶­Ü6¹bœe´<EÙ”º” +dËôZà#Ü~*]6ß¹¸Ó«å™çzçîÛÇ7,ˆ0´$‚ýÍ'…!<h»}쇩›Ztîü
+w
+]Ð`‚oYÑÊÚpµg³µTúEO Fè{Ù~}öÎeÍ˱›’ÐúÆïM¡E Ø(òóEÃOR¢ºÕkºŽirxÇ‚vY‰ï¢¢b@©*m.BmÅ·-Ø7ÙÓ¦6W.gNçt%ʇ´rÞÀ5t3Ö |]'‹Eè©bxP¬Ð(×òaÓïdïe#†Ã´Ñ°è ¹À@Ñ¢¡^ö­ÚÉ…{£\—\•™U뺼ƒ»ø¨\` oº\„ž*å·‡Ë|$.ò¼?eX
+OT‰°C"% {?6M©K{M°ö(L+ˆ°ê~]BŸï$è)sX¹ÕZn«¼«úc÷‹ä„¿ox¢@„(´Ó°o«ÿ¿>xѦLÓë8'jDØ'4:½°s…ÏáS牂Ž`7x¯)x&ŒFå]ÃzÒÌ,™Ù5¾‡#/qC£ìSs.Pÿëì_osMUendstream
+endobj
+143 0 obj
+1433
+endobj
+144 0 obj<</Type/Page/Parent 107 0 R/Contents 145 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 4 0 R/F8 7 0 R>>>>>>endobj
+145 0 obj<</Length 146 0 R/Filter/FlateDecode>>stream
+xÚ¥XÛnÛ8}ÏW~ÚbE_ѧ6î¶ÁM6qŸŒD9ÜH¤—¤’õßï IIŽ\Y
+vAѢ렘Ïf.­Ü}ïdQtbbÚ„FÔ›X êBÛÃN’KEè åf<µÍ#ý“ÐH ª5J•98°ò¬$ ,œgÅ¢}Ðöƒ=+ìXiÑ +¨L-'Êt8Y$Q?+˜!?6wÀˆ2µAè
+òã$¦ÃÇ8阭Wt óáÐ#ùpà>>fË#>îYA½ArªÉ%²R)Ã0€ц j¸„f™Â$ÎOÒQá(©Ž,‚  ÒmË•,‰ŒèêQ0ÜvvS!á Ô™ó6¸†uØ¡Gê°÷éð,~“r[^2(!B“ðýe‚ì•r*exÑ)ÅJiØ1ÿÚj®¿€hYVÑn!SWwkÝÑ«\›!jÃVÍFP6j6‚Ú°U³Sj“Õ1µ@VoEÚ¾>1Œ¨ â§/´V­úº® ¨wY]ÐÈäVLÐÎä6Ï'\%Ë÷hœCÌiîËéd¾tlXN«Â\?Q¥Y7¹–ÓÕj¾ž¶‚žÆbæLlÐiKCŽC’,¼ãq8TòÚžì·`çx‹nO–.H¼ãߨØUtÇ:ž§•RL—lŒ÷…74äxÒ6€#Ošp„ãIÛ
+h ¡×ƒ¥î×?îêAŒç¶3†³K Tn¾×Ÿh áÄího^ «íöë %á°bH¥¢¨­ÛãªCU¸oT]ú~åÆÏæ×n†ìoñ¢µ÷sÑ@+#QD Æñ©bÔœ\·‚~hµ©åø…Œ–ðr2ÉØ=þáˆÆ °‘
+endobj
+146 0 obj
+1574
+endobj
+147 0 obj<</Type/Page/Parent 107 0 R/Contents 148 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 4 0 R/F8 7 0 R>>>>>>endobj
+148 0 obj<</Length 149 0 R/Filter/FlateDecode>>stream
+xÚ¥Xmsâ6þž_¡áS;“€_€ÀÜÍMïÒ^›¹´G/ôsGØ ¨gKœ,“£¿¾»’m°°¯7ÉbvWÚçY=»âËUÈü Y<¥ß$¿z·¼½³0dË5‹âñpΦ³p8cËô‡
+†Foþ|ú•Eãá˜Mo'±(FL{|Á4Œç´v/Û“°yË1ûþÐqØÛö4nÞò̾;öx §ÿcÏyË“ö G·„×dR“j9]ŠTiˆ»0œ K@Ç=˜Û€ø Ÿ<î k3[`<W¥4L­™AfûŒíG¿-— r¾ ØMãvÑ9QRBb„’ŒÛBZÁZaI¦
+!7L˜®âGC*OÚ`ãÛË\Xk»ûKÆ`Ö8ŠêhÝÀÍ\
+¯TÂm;n¶oü _êÏß¡¬ »i° ÚHr–ÕAA …Ñ"1Œ' ˆ³ê%˜cIôÅZ÷ÅŸerkkèAm`™W;B®U“q8w"r§¤Ñ*ó«‡lu^A°å†‰1Ùl ¥'ø
+i=Ak”´`k‘ÁÝqI:¤$P´A
+«r3¸ö|´Êàš ž¸–ôj£àR.‰¾øã§hàT>¹J§ Ézz…kÌLÆsôt˜L‹Ì8k ×%cÇŒ3vÌ­fpI»åßù×»L€4…ÇNÏÈiŸíœy™3Yæ+J{Í
+ü?3\¢P¶Oõž²]kÈ–[dpϳˆJ! hɳìÀ2‘ )Vµn8Š­lIG ÏNæ{.2¾ÊÀÒí;¦P$ZìŒÒEãÙ¼‘n>œuO>œñ9>Æ“ þu|àqyÿ‚ÇG_6
+t­arqíNƒ_í\¦(QhN ÑÀVHÏ=’ †Ô& H’Égt´DÑ~ÈšXóÓH“qGôÔ“—FÑYN,áOÞP9Ô‘àkBÚEV4Gpz“e­û’eÏ’ÌHÖ'øR¢_$ìÅÎèóE헱ш ­s¼àmA“ªÙ[<Ÿ6šÓÝ œuÏnàŒÏuƒx2q@|$™ôÐz›eêéúg‡¶hêŽD·Jc×Ìz0ªbòb©Ð`5Æn§TëW,q]`@×Ö@PŠ|—‰D˜ìð\90òV#¥Ìl³ÝÂÁÖ6'wÔ©þ+Ýf¼•3!ÁõŽcrÞz.Š[Á_Û_÷"ªõ\Æ¿‹ë(n¸¾±S_æÚZ[ì/W\[cÇõѺæ:šOÜY Â (xl“îü¹ÇNÓª¤Û\7SO¥g-¹¢òLê ò§ÖÀqÚ‡­5ïeaû
+ž Jú|Â0rkÐPvUŒb §u;ÐŽn§ éíE´µÍþ’±CÛ;´Ö Ú“m¥ý }‡ç…ˆ³zÔóÍæj3G:Fô®fãogÝShœñ9¡‰‚¹ÛëBã`‘ðÉL2Ú|v¡­ž– gï@† díÝÌ'!xå.ÅÆÇž„ÙÒðŽ ’RjÓs]x=ƒ7ãEQ)=J:…·µLí±Ü¡nP ×Óïèu_e Ý¾Ñ1ÍF]ý4œÅß0ü8ëžýÔŸë§á´â«j¦Ÿ”ò«x´çzTì”ÊFI¹+Î]”|ƪ~êF’®ô§Çr‚®k¼³v}*èºk;c—þѺIß^,)ýûÅO¶þ 1Ë/O§_¹Ò< ÓŒs~Õ(«‡ØOp]BˆJ² £ˆÈë{¤uÏ#íŒÏi·$¥/WoÓÜMH'0i¬šŸÜÇà‰¹T";ÌÔé;ª™Á«‰;©8*à1É;AŽsgB š¹³G¡ǹóy¡Ì¢ªI¶’<€æyšûËmµ6knÒGeQæ·4éôÒ†¿<+TÓMSÀÂA:ÀÆNaÍñè—ÜÝ_‹Gì»´oÛYÈÔ>»_,ªK!ã´ wv»(˜ÆÍÝÖ÷ì³Öö\›·ðI¥¨±´ôéÅK•ZÍ«tX »’k±)+!ï£a'7¢·)Ì ÷X—­Ã9z?«¾ '·4dFAC÷=5¹5O
+endobj
+149 0 obj
+1631
+endobj
+150 0 obj<</Type/Page/Parent 107 0 R/Contents 151 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 4 0 R/F5 5 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+151 0 obj<</Length 152 0 R/Filter/FlateDecode>>stream
+xÚ­WÁnÛ8½û+ÞK ¬Q²-{·(Æi Ytç°@/´D;ìZ¤KRIó÷!e»±#[Î.(†ø(½7óf†úÞaãƒ,tyÙù0íœ}ìc0C’E}ŽÒhÓâÍí“u¢üdtµz;ýÖal ¡ÇÒ(¡U£µûì“õfÌ¢1 FÛW"—s),¸{ z (^
+¨¬(`® „@¾äÖÒîxût^á&ådÎÔ*¢ÕËi'Žb¤¿¹Ü|‚¤OŒ‡}|eà—¦xø}ÌÒ1ÑóhÏô8IÙœ¤1†d‹Æ …P b¼"Ý©(Wi¶aŠéîÙ7g®\m"ïÇ°Ò†›'(¤¹ÓøËiŠÑ1Ñ}º†·%1Fí°hö$kÑDoÑÑI<•¥Ð•ÛÆñ®¦¤3€A*°"ת°0hé—˜iÀ{
+õc¶÷¼àAš\ƒ»)h¥,Š¥
+tÏ»wMÖ»”à³Ò`Ɖ°¹‘+¢uØ8}6Â÷·m<&I”=ï%;S'×üÎó\ ‹ 6¼e¯8° 0ª&ä,ØJ„¯·‚ðMÏl×ü Í& »½¥û°]rL÷/Ý=R1é¨l[1„ï·…4‹ƒ¨w1çÕÒ}©eÉ÷Ä£÷ Ëwgõòûý TÆkí³`b.•ô£AËé½ÿ[ P›¡šØf¾´PëÑjÓ,9''S'´;­p›[Žd µ?úù›×‚Ó‘mŸ)>&ûR{Ïô ž bXFŒ?pœWæ–
+½Q œyܦ¾Åzøiïcjb–†'×ÓòË(k¡Å£›´$ãahÂAÍ¥*^£…ÏÉs¯’ŒãME—ÐR²º"&B=½ÒbJ»ÿd³ÄWuk=ݨgPÏ—‰x¹¸»¹:˜šÂ£z•‘À3rV9ákÿöþ4Ûš½G7²O‡¡â¯Ô\ï?÷£¸Â£J±9”¨¦ñ õÐT™·Íg¾5É.kÀ‰LY|BµtS6„j½ÆÖñb`ïn>ã!yÑ$…ŸH_"Þ”µu‚6ŸP¤ý²‚X6¨ÇÞÞ¼ûÆÝ1-¾¨Úµ€nj¬Ÿº8œ8XœžæÀrÇ?BÎQºWøMÎ"·N¯V¢8v a¾ÈZgÆ£½•lµ\c×ã q ®k¢ ÈM£É1=tº^'Æ >¦‚ O¨€~ñ<9ªÏ“l@ŸYâY_Ü}¹…+"3ç¹
+endobj
+152 0 obj
+1248
+endobj
+153 0 obj<</Type/Page/Parent 107 0 R/Contents 154 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F8 7 0 R>>>>>>endobj
+154 0 obj<</Length 155 0 R/Filter/FlateDecode>>stream
+xÚ+ä2T0
+áÒw³P04TIS045×3S072PIÑp VðÌ+I-JKLNUpI-ÎLÏQÉE™%™ùyš!Y\º-ºP=†& AcKK0×XÏLÏX¡ (dJ±^r~^HÞ5„+
+endobj
+155 0 obj
+123
+endobj
+156 0 obj<</Type/Page/Parent 107 0 R/Contents 157 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/Fa 9 0 R>>>>>>endobj
+157 0 obj<</Length 158 0 R/Filter/FlateDecode>>stream
+xÚW]s«6}÷¯ØñKCø2àÎôá:I[ßi;îµó–äš"¶ÿ}Wpc®1Lf G»g÷œ•ð,ü³!pÀõ!Ê&«Ýäñ×%8ìøÄ<ØÅ3^΂òœ¤°Îñâ@"Z>ìþUX;ÐX÷MG¡M¾Å–EïTÀ†3Á"–j¸¶]ÃßTÁwGú ^ÔpHJ 9„Æ>¥¯wO›ÇõJ ŒXžÓH$,7A†ˆéT©
+4/’$â*¥ [JUú§×Ͷì N„#Ý8Kò¤œÄß$¯P¿ã@ ÅûÀPV…l;yÛØ?ˆìYf¨Evt¾g§2Éÿ'ôí%Jlx}–ú{¢:ʉ#RŠFMV—VdqÄ̬ X#v”å¨~§’+/ø®Ýö…DÇOÉ
+¢Žiñd1e_Káe»HÚ)Õº¡ØÌ(%¥S;öËöi½AÏÈ\pY%.¬
+ loauèí/’8J*§D1GŽ.Jr
+o3ël‘·íï:&A 3"P4éŒKSìæϺë,eÓq– G•'.…í¦./*žÈÓ”EDÏT}KòÓWy§Vcd,¦éþúCG&µž†Š]7o+£ÎeÌ94ç ƒ]›`6W ºŽ4»NõʈqNË‚áBìÔa½Ù@Sg «…0T}Ý×Ø+{ëUHܸ+=©šBÇâûÚ1Èîn
+endobj
+158 0 obj
+1087
+endobj
+159 0 obj<</Type/Page/Parent 107 0 R/Contents 160 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F0 3 0 R/F4 4 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+160 0 obj<</Length 161 0 R/Filter/FlateDecode>>stream
+xÚVÛnâH}ç+j‰2v|ÃÀdw¥pG‚„e<£Œ4/ÝÞ5m¯m²âã·ªmÂe¸q3Ý®:uêtu—ÿ-™`àÛÛ¥¿(5½ÒC×Óo¦ÓÐMpë¶^/¸7OÞß%ÛÐÉ‹ÆðøLÂTÈ€ît¼’¡øñ3é!†Kµb(WËÐ-H8|9e\sñfnlºŽ^Û³Ff×Ö]ß<Í'’ìŸj 1n壌ÏóqlÝQñ­Ó|âDÈ –1d4Ö¦Búo<½FÑ®!ì­•ñyŠ–sDɾNѬ¯­[9š5Ľ•£2>ÏÑ°#ç:CÛ];õÛV5ݸ•an|–aµn¡z‰ãŠ%‚MC©øï:? |óÆÈOð{è6Є¶¬U«`éè6´¾Ž¿@7JÐ!'.»Ým€f¹yåîÌfh–Cà×Ñ°-SÀô²7žæ8Fs?CÇ,ŠÓCø{òÎxB 'Q°ô9ï’-„S&%Þ‰Ùœ‚å§Ò1‹*M—qŒ{ݸ’ÃSŒT/춴=$4Œ:½ÇœLà8‘“eq
+Ÿe"’+èþ2N5B?b¯çcÔÕ)IJ‹cuÅÕÚEj{í³ÂÃY†ÑO!çd OýDL‘ÉÉVL„ªXxÈ\fJ`Y†6ËŒ2ÀŽÒú¼M©AÑì*²ÑðW-úï¿už½÷*ð$çˆ[nµŸ¼§2h"Ó|žpÄÖ´?·Ššyn{î­(DA÷ÝÁWSË”rI'½æg¸›Lz½fX
+}¾ÆÒ—×p‡\γ·}`)ÕÚÅbÍÃ🔕|†'>j‚UaîurºíG‹ÓR³„QÁoáéñu¶…º`öÆY@z”ûæ¦omúö¦ïlúÕM߽䅕M5‡ëŒ¤ÊãIç’±0ër»CŒ|™¡âjBñª q°D™ZQÀán06aq ¥8NA ’-ü¸³d“dáOùÄZrŸ6ŽZœŸœýó ù2KX‰¥iå_Ð/¤EEŽ‹U¡ô/å?‹p_dïùùu B6Ð}yöð2À¯ç]‚™',~~ú+ÊË+:O:-ž;x¿ ¿ãåiÒ¢Á€¦¼ÎëE|!Sž?x F=tþö49á‹¢V„ …ÄUó£Oh¿ÓvA™DÕB¾âayw<ìjv r.ßUÝbÜʇx4Új 9áBú“=ʶ¨/YºšÁ¶KÄÏt(§êèm¬]§ótQ8æ£pÛÎêÛ'PõèU³Œ.5ÀvÌ6“6OÅ\ÒÔ˜ê’òÐr­ð1]Õªëuœ3ÎwEÌþ¯ÒÿªwÏcendstream
+endobj
+161 0 obj
+1013
+endobj
+162 0 obj<</Type/Page/Parent 107 0 R/Contents 163 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F0 3 0 R/F8 7 0 R>>>>>>endobj
+163 0 obj<</Length 164 0 R/Filter/FlateDecode>>stream
+xÚåVmo¢@þ³!±w‘µÚÆË%µÅ†ÄVKiÒKü‚°È^W–[·/^üñ7‹hkÚ—KÌA‚¸Ì<ûÌ3Ã0¿*&xšÐi@³ þ¬Òs+_ûœ€BóH7¡Ýiê-pƒÚ·OÖÀº´®\×´Çr.ŒtLJPÇ×hÌh¬>?tV ¨›†~¼r=uÝ}³ãúÆ@ó¤ó.”õ:hÔ<Žñf`½uÃÔÛÛ¬{Ò‚u9räA†\ø$
+ëïkè1:3»q‘P.}¤CÄRÐi$±ZìËÑÀ¶ÎLÄýõ豕cÊ<õñì¸&y²œÑ `d=áRòÙðG"$õ߃.;­×­ë
+‰'¼©ð’¨\è}Q¹X
+a]∽ÛpÒ]V¥Rláì¿,#ÇÊÑb«B^5äY–WŠcí¿(½á]q£Çâ2†rLøsy‹ÞílësÆE†¥©?e6Q¨M8¾ü‚$sÈ "*ú i@â©ŒÖPŽu}k;™ +;f|w€dDýû˜Ìç9@¹”Ò/Ü‹Wä dïs[ÙS{~¥|iŒ/IOøœJÊs+iñA¸Mã-+. Ç:s +H
++³õó'{`_YeÉN8[¤/LYªˆzh¹C7p¢Þ\œ h6pŠn4ôtÇé6^›ÓÖQK?ɵMÇòc0M5—›G £Ó0‹³ÛÑ ØªŸ†žOàœÌU»Å_ÐD顈ÕW.õ̧¥7!õëã·ú”eÔê´0°ÔÀìdá\Wþ
+endobj
+164 0 obj
+693
+endobj
+165 0 obj<</Type/Page/Parent 107 0 R/Contents 166 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F0 3 0 R/F8 7 0 R>>>>>>endobj
+166 0 obj<</Length 167 0 R/Filter/FlateDecode>>stream
+xÚíWmoÚ0þί¸©B¢S“ ”ŠªƒÐE‚•¦é´J| ‰!Þ’˜9îÛÔ¿s’ÒÒ„Ðhí‡j3(Qœ»ÇÏÝ=>+¿*Ôñ§A³-ÿNPùlU> ëpÖšûªíNKm€åÖ
+œ‚>žX—°>Éü» aZ›Sß'îtgŸ-pô¡gY#ã<ˬþz…{¯Yî™ýI–øWa’]°¹S˜_zÃô’Ð}LG/\ød ’t°¥]šGèÂkÉ]í²µb%vlžFŸŠ„ÍEZ‰ÃFbo°ýo¨+<Økb·9Ôçç,+ðê;.£ï’MÄÐ åýBq#Ìqÿ­¸ÿm%Zúw ¥¨àyUûì¦;Ýý¸®EAnÌØm¡M½oeVµ}ºS iÍ'sqïP~Ïeñd\l=/±c€¦¯#¤„|èú ¿iM°å}@]×'$gL<c¹Jw!ÇÒšÚ@ñ=hªÙP[ëš2Æ'…ÝMfÄu±¿ÑÀ^B]!Tfáª-ºP8p™ªÃ8‰m÷ JµNˆ7rË="ÜÕ«Ü\˜ÆüµÜà+Y°„º`@d0eušÛ§R‡’rÊ…JJ5‚/zo€5;Å­0Їßzf¦¸Ì¹ŠïÛ-¬šÄÊ–M–`KÕä2>vê=p)/Ç?!½AwRÓ6§ö
+endobj
+167 0 obj
+757
+endobj
+168 0 obj<</Type/Page/Parent 107 0 R/Contents 169 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 22 0 R>>endobj
+169 0 obj<</Length 170 0 R/Filter/FlateDecode>>stream
+xÚ­WÛn7}÷WL\pPï†äÞ¤6  Ør*ÀNY)P /Ô.%1Ø‹Ê¥ì¨_ß!¹’V×ê!2`{¹‡3sÎÌp¨.(ü¡0bH‹‹£‹·wº0š@ùâNè3eW
+(ÛReßF > ·âÿ°'‘š®to¤ÝKìý*uC1J”©€^.x~I–*ƒ~&µ¬JÛ&Û…‹Æ¯|³îYë:3Åñ0xèƒ^ÎL*‡+OÖàj•4µzÅçó\¦Üø{û\f~º˜×Þ÷Õvßve;ßíÖð¦‰MQXŸC^k¡Îh å€'Z¢AȈåpzÆ5˜‘cò­v1 g™Ôâj¹:j×À¼ÌN»QK«žàéÌYlÚaubÚ>¬E+êEM£+Њ—5æ³1·ã8ãšÃDU…µÖ
+ÛD´!ÃÁcm¬í‰oÑêF€g¡´µÕDoàF¾ãx®$Š5ÎmU\û«ÁѤ¢×ÎŒÅT–5¼H=Cu'ÕBÁx©ÔË2©ª”ÿÚJ—Je¿ù±˜FAâ|¹ò'}‰£Æè7–S—“¤Ò™Ô"Õ %êé ?’0
+Ð 3Fô®ŒäRë\´ãŽÃ%MäÕ¤-„åñ"óÜdƾ(1ægacÞñ+B]ÛX ‚g¦jü¯žWe-Ý0Ƴ,§¨Š¨½Iº1Ö„sjÌÔ/ØPgdé¡"™¿ãÙ„o»``s/Ç
+«uÍaQÚ®a.”Ɇ‡Q­–/t…¹Ä¾ÍóånFïª<¯^L †Ó¡ÌÙºåP %1F”¯qk
+¥í¯‹¿Ö\éu9´:l·º×ý3§¡-kŒØÆb¦JQL£ù¥Ù+\£±­lã£9d¢æ܈ºÈ-qÜ>UÍë˜â1K·ÂTÍI;[ÿËܵ꣋°ã'Àˆ™è¡™îÌÌþ#w…®¹8,%þ>f„øAƒfAgÍmï«ó0¢í'!ñâÀkƒ±Œ`79¯- çؾø´È=T®XØ(mïéf0ÀTa£OOÓ¥öº{]‡=—®C ëH&6î8ôðJvˆd•W꧑ŒC3ßÏ#i±g“´è£$”u<Ú¥xŽp8ÿ4ša€îΤi±gÓ´è£4Y×]kh—y,Š¶i~^èùBÿTž,0®óxZìÙ<-ú(OÒuG‹bäÙÝæÙËž9ÞÛn%žeºK–˜AÌV·ŸŽivf¾nF®Õ¶}1ê€×¼ÁolæNìÿ9¸( Î>¸ölU,ú˜*1s5Îb‚ó&8(Š-õ"«±DàødF'p‡Ã/Ÿy¾3H(‚z­×À'«™¾ƒd‡‘ß«±½·Áad-ôŽÉð0ÐŒ¤ÓÙÈ*I€²J†…:aNa].,Ø¥b¶3¤Ó”¬á„‘õ­zPbÌŽp0Êi ;óÑs[¼fÏ©o´ab¶‰köF/ÿ['‹ˆendstream
+endobj
+170 0 obj
+1477
+endobj
+171 0 obj<</Type/Page/Parent 107 0 R/Contents 172 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 4 0 R/F8 7 0 R>>>>>>endobj
+172 0 obj<</Length 173 0 R/Filter/FlateDecode>>stream
+xÚ˜ÉrÛ8†ï~
+gªB 7æâ$žq*v<±R9ÓD#C‘ —lO? 4IY”]Úðƒ$>vÿÝà·+F(ü1""ý¿;^]o¯^ß„1²=ù1‰á'd»ÿƒGÇ£øÏí׫ ò9ñ˜€WxSæyÚJ=Àhè$ò#ý;%‘}IŠ²%;”À{¥d£•=€ðRñn{E}
+×7¾|ú›‰¾¤(€3˜‹ÎWKò8¡Ýþ¦×âu-‰9¥¾èÕ\$>}¡(ˆ‚"ñxÌFÔ@èÚ;¹Wé‰ü`Àp/¿Ëšìº–‘µ~˜Oj`@ÒC ҃ʥ¥â—ª¯å“ÍR\ªÙZ‡
+.EUšÉeêᆠÔc|–¨£9,‰‘:ª‘úI=PcÚ`Ì{xÎýmWåòçdì=ÔªhI£Š,—^£ör?ÁEû²{z)Z &0Ù-öPë{¨ž‹½0ÀØ‹#'âœÁ?_>ɦ̻V•Å‰•µú]m
+÷¹ØˆÂVíàK=N ª€å·WÉÚSÅîÙ_`.ÐœœSxY`´}:,ˆ{
+šx‚Y5óƒLuÖ¼Ûgršë¶¬ˆ„Q¢ V7íÙO:_/UžEöÚ¤÷âÑ4Ùà—¢y°"‹â*XÔº‚Eõ,X1€eÀZnx—Öà†U ÌÚ´¯¡Õ(  ÒF¦ŽÚÅKˆ1–V
+€`}Hp0..ì%]šßH¬ßs¾u4ªü9€ðÔ‹Ùžu7-›gÂ7cQ\õ+ÔºúªçüŠfÎÁ¯¸åW¦!}è+– ·E `“”¡¦‘C QA¡ì‘û•åBZ'Ž!€Z×@õ\ðH !
+ÈS$˜%éªe¦£tK9#uÍ8#ž}ò3ܶ,8´œçnà£ú-­tû¢öí³é·rYdðÑuc¿ŽGsuØ^Ç£·N4WýÃ*¡D–¡<âÞMo{´ìÊ£é$$¶“³ÏZve®wçÇ\¨6ÂÙbÔ¹Öˆiƒ}}“ôÈLYŽ9>ßûüð¥’ßDé[Ù¨¬Ðo»ZUƒÃx8ÅëçpªQà3ÀyÌ )kÍLrÓïÀ¿Wÿ¤lendstream
+endobj
+173 0 obj
+1373
+endobj
+174 0 obj<</Type/Page/Parent 107 0 R/Contents 175 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F0 3 0 R/F4 4 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+175 0 obj<</Length 176 0 R/Filter/FlateDecode>>stream
+xÚÅ—MoÛ8†ïþsLH%Eêë°‡Ó´Ej4›x±0Ћ"Ñ6·¶¬•è¸ù÷;$%Çq$›»— €Þ!ù>¤‡â?#
+ÿ(Ä°òõèj:úxÃR˜Î!¤~ QÂü¦Å‹¸Ç¢øÃôïü
+Å È94b%r%
+Ù€XWêY·@^š¦˜?vNü4Ÿ £ýåþ3ðD›ˆ8È ›1^-à¡G›r?mµÖÆ)q@ˆÏZuÀŸ¨£…Ç÷ðÅtpÛõãJœ‚VoT¦ˆ'QB•-D»%þ[l«•ø%ËE®{‡” ŦA­3(£Ecƒ)SøS¾­š¿d¡–—‰›læÆ&ȵ¹¢iùK¬š36)Åa˜.‚—“6Öö{JÜÚ4jkóEÝÙ Ó¨]q„FÓ·F¿¹Xª#§6øÿ¬†)Á.ݬZ­«U«´·Ë™%Äc {ku"
+™MŸ+qäöº–O¢öšJär.s ª Z¤KS¢+bÄÑÚEÏö¯lÕJÀk߀ô œ˜tuá<£u†cÔƒpÂ[1p°¨&ñ[8WR5w¢oV›úˆ½„àø%:}DÕ‡!Á<G‡FëìШ²¨ž+_JÞé5|ìPÏ8 \ì1ÒÕ«óöŒÖÙžQÚ£vm§X¯Òžzuõ¬„v÷M–¢ÏÜëå„>u[Îð¨>ÄçâfµÎLŒzˆ O‚öŸbqK{Š›YÍßëB,énïÒ»^¾Ü–?ŸÛ²?.Æ“Ù-ì/?>ôlwYYà·ÀKÊx “Éf³ÜÞ¾Í 0§ZeeV¿Êñ}_§éfê&ãÍæ³æë~^\×»S¬­Öº>%¶¬­Ú²~Qw¬Y’Ú„âqÂX?TY~°þÂÔVÍz·”Jô}?|¾êA†ÑßÂLÓ_eùO÷^pýÑ6™ÉCŒÎ&ã£hdµ·GáØŠÃ †oß6¶á|}z²XÀ»É¢,E®§fËŠ»5zBm§ËÊítÈ÷óEI ‡pœ¯¸o¾ÖU-šFnÊ÷ÛáÎåÔj]K‡U•Ž %NâqÚ³WÜovãͶTïG&H¸sQµZW2V=H&ê~è4@6a/›!ŠwD1ç/L«uFcÔƒhxj÷N#D“ö¢yP¢zG4xþsý¾´Zg4F݃F?š"?]
+˜||…ŸØ0ßÔ0þóîî³F‰ær…GC<=[s¤5w‘UÕJæ™Â2ôñ©,|Ñ«MÎk ¾}NAÝæHëZ»Tqdm_;¸Ñ½NEiÐ~ÿj…J†ªZ–Ø‘WˆJà¶^*ij™)#ÓO¦ ­ñ¹ÙJ•á)ZÏ¡BÇ…h”,Íø»æp‡þÂ?Ú4¾Üywão—€÷û)Þ…Êq·‡Aj»ÿŠl×Ï+iŸi#&} 4 L/_õpç¸eõhä¢Ô·¼–•j7Ï&yq@†9ó˜ã4AÐ-Ï?Fÿ|ü5®endstream
+endobj
+176 0 obj
+1149
+endobj
+177 0 obj<</Type/Page/Parent 107 0 R/Contents 178 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 4 0 R/F8 7 0 R/F9 8 0 R>>>>/Annots 27 0 R>>endobj
+178 0 obj<</Length 179 0 R/Filter/FlateDecode>>stream
+xÚ”Moâ0†ïüŠQOô€±óMÀ²Bj%Ò“p7¶ÙØ]mÿýŽ )A¥´J”àðÎ×3cÿé1 x1ª7Î{ÃÙX
+ùÿIÒ”$—ýˆ¤0×NÔZ8XÔR;©wøÃ8S˜ê>A³kÌAB"o–ïÅ 3ງ’ÉóbâŸÚJ£-8Ò¯”µ܈¤>KçêP %´ãõ`¶> ËBø°óÅJS¼z i’;×4ˆ(Éšš2x”Z4‰‰¦\(t×­êGÞ£„"$ÿ`þ±ü A‘âd„iFèiUÁªAxf§§pžÅµpëþãbº¾‡CKEÚNÙ›7ø<Nd!ñ}Î㣾«@}aSoê;
+Ô' ›zSßQ(ˆ({§t]ßU >¼Ìgü¡äz×¢o™.gŽ¾läÙ=¹ê'cÐx¼˜Øçå”p{S‚æJÀÖÔ€=ò¹«åÝ­‘ÁJÔ1‡'a-ß W¦øý½rÕrÝ_=q<ü^©EÅÅÄ(_ßûΚÉ
+­ß¬
+õ“ùlÕ™'{¹‘>&F”„8R
+X@ ;­Úfàò˜*wpI´mNïÜáa8äú•X®6œ˜z7,äÖÞê
+endobj
+179 0 obj
+539
+endobj
+180 0 obj<</Type/Page/Parent 107 0 R/Contents 181 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
+181 0 obj<</Length 182 0 R/Filter/FlateDecode>>stream
+xÚ­–QoÚ0Çßù÷ȤAPh_×vS¥Ubj¶×ÉØ—Ä,‰]Û™Ôo¿3I;
+YBÂÿíŸ}wþçžG!ÌèÂz‹ð|ô)Ÿo`>ƒ(¦‘Õz ‘_Á4È2í‡hGš„¡×дÉ|5ÝËt<८%ËI¸˜Î½$J,OQ”ઈeRæ¤*€ï!–Z0h¥@HÑàô=sr¸â4àh\š•.ÅÂI^ý ûCx²phbƱúôbæðÞÄt|#µÀÓZ´NZu–^|d“……Í殎g¬ ã)h³ßL+´´&ØÊ¢=šÕè¬sVpÌÞO<Veº[ñÜ-1gHò/±uÌ))~yNâó³ä–Ém•©-ã¿°íÑ«E>;”$žÈIgžn¿<PÒUbX>#檃¢U–É"ÙÇø+U]5g °
+J;Зõ èÙ¾•Í8Gíº*Îo\t—%ù1¾IÆÏ)^ƒ;ºðÿ¥xmÊ vº7™ }ÉDÁ(å GÅû§·ß7Oà/”äT
+¼B³´NeµRÙ9VÑÐì›Ø©mÅ­Yׯ,jD'áÕMµÆIÛ¹\/§×Ô–ÒØ|áÜG£o£?”¶èendstream
+endobj
+182 0 obj
+590
+endobj
+183 0 obj<</Type/Page/Parent 107 0 R/Contents 184 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F8 7 0 R>>>>>>endobj
+184 0 obj<</Length 185 0 R/Filter/FlateDecode>>stream
+xÚ+ä2T0
+áÒw³P04TIS045×3S072PIÑp VðÌ+I-JKLNUpI-ÎLÏQÉE™%™ùyš!Y\º-ºP=F& A#S=c°€©‚KfQjrI~Qfj1HÎ5„+
+endobj
+185 0 obj
+121
+endobj
+186 0 obj<</Type/Page/Parent 107 0 R/Contents 187 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 4 0 R/F6 6 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+187 0 obj<</Length 188 0 R/Filter/FlateDecode>>stream
+xÚ•ÁrÚ0†ï<ÅÛƒ 6Ò#q q‚:vfzòâ¨È•D)}úȆR2S+ÁãíúÛýW¿ÌÏN=÷ aA¬êÜå›ÉWˆz¯\d8@^|ÃT(c¨ÞÎ4 áè ÃnÔ¤tCÈQWæ1„0¬35ˆ†Ý†Bƒ³PØ?> LU›­E ‚ÊrKKì¾ç骩(ÚÊ‚Ë” ù¯úGA-…JiûB¥“½ä¨»ÀòÊ_oÐ×½’ìè¾VQm%gÔr% 0WF¢€%Ú¢»S°ÑªÔ´2¾r5§Rw…&£Ø­O•k·Q%]ûJ¯[Ôußz"p^
+½îíÁŒ™Vrï3Æ8‹ÓÔÓHåÆÁ܆e–Ê‚êbU ¬œúTº{Õôã¾;ûÔ=•èQ?“¬½Tì:ÔóSúˆæÒÖƒÍöÆbåa&Y|CÚ¡ ÉOÿi¾)€ÚxÈ“ÜÃpk*Í
+kš²Š)á¡=`:kç=àN µ¡l]7:Õtó™ÙñHúÑ$¾‚}Ôî†!­VâÒ(ñ¨–—àÓ$Ÿ´Ãs¹s
+endobj
+188 0 obj
+574
+endobj
+189 0 obj<</Type/Page/Parent 107 0 R/Contents 190 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 4 0 R/F6 6 0 R/F8 7 0 R>>>>>>endobj
+190 0 obj<</Length 191 0 R/Filter/FlateDecode>>stream
+xÚUÍ „ï<ÅõÐZúC¹ZµÆƒIø
+endobj
+191 0 obj
+195
+endobj
+192 0 obj<</Type/Page/Parent 107 0 R/Contents 193 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 4 0 R/F5 5 0 R/F8 7 0 R/F9 8 0 R>>>>/Annots 65 0 R>>endobj
+193 0 obj<</Length 194 0 R/Filter/FlateDecode>>stream
+xÚÕ›ÍrÛ8€ï~
+wÃÿà1‰ã©TM*ÞØó
+—œ]™&„;^tv"Zî~$Ä &ÒÇ?¯o2™F»E«0IÌ
+rb‘ŒaCë󚥤u¥§S˜P.æé ëR˜ Žmj‘rPŒ¤¯ÇïÝQö4Ü)[@ H`(G)›<€/ÂD>L¸Iq
+þ÷—„€Àì¦"
+anî*¥)R>»fد¶¯Ã"À®jõœ†âw¿¢ÈcwTaÍ3«_™¾ñb¤aÞ£E9Î ¯µ#<YûóñûjX­¹Õ E‘Xb\k vÓåöÙh$x‘†µs¨ƒG]m¶Á¼Y„½n<ÃA=𑸅¬74pV.­W¬Éh9³Eƒ‚¸¬MŠB9Âìo69uGãÍ€u@Ü !†8¤5ãÄ ‰™_Ñð;ìXª ÌFŸn\ |þ­–CÁè²müŽàí“ÑeËÂv
+Tã‘ҵ¬¤%¥#Lj„ƒb$}ùüåS&Å™FÃòÀ„…rdÅ
+×ËÊIL!¼)0|#
+oŠt^Üш䙃PîQ»Í®«¿»¥äÍ%õ3mù¡©GXÚòE9ú# Žq<E¡a^Eë~ÿëHYA¦à´.¿Ïp¡9-W´;Ê’Ó"ÇB…1á I_áp›u[;ÂXc@Õ1 åj¶ï0» 8é¿lAïŒE;”oDzgœv(UÒ;rXÓÌA(GÔu8Þ¬‡Íc¶ÈÖÄHN‘ ©1‡Í>Ÿ”Dˆ‹Š; ð²;ø˜]"1&ä¤zÅüùfúvÎhóqI´–ÎËS\mK›¥*n"17=“¢PŽ°ëëËܹo 3gQ„Ý<vë´Ä­Ï³q–ôìƸ=†F¤g«IË¥èOÆÍHM´Pùr`<”lç(×HŸþsìö§ÜCî¨gcžmݲOyfüdÇ>çQÓHçQŒ?ã`]\Gñ`‡rîmRu˜Åïÿyô.ÄcaØTA) Ã)OÐ…xLï' Åœ.ܾwwOÛlEgB8ãj¯Éˆr~ì÷÷›‡§aUž– Ä—„Jxý¬Âóö—mÁ¹•Žö¡9·¿ F+Q$1 ƒÊ¶Þ®‡î
+X? ߈4$5¥º
+á÷®DƒO dþÚ`î€Ù¦´ E
+lùk‡àÃm“ÒPŒ¸Ùµƒ\Ú!¿f…Šo£ç ¡ñÒ½oñ ‚,Þ<‘5Ð7A¢Qïoúõ¿²õˆ!’ñ„Hy=ôÇ~Ýo( nœ×Eå›è=DhLÉ·øä@ˉÄñ×¾ 'ˆ‰tÂ¥2¡·..%¬ 'Ö‡¡ÿyØìrYé„€DÎÖ ;11Þ™.ðŠÓzªE†Fd1mB–ÅZ$q‚NcŠ‘tŠÅÅȥʑuÕ»œ«MÃÁüz>Aê`¦Ê¦Ï°aÛ^BÌc:¿s%¦[hLû1Wèjå‚&q¡+¡\žzbQ
+âW‚‚’
+v‹ÆÂÙ,–’°C(„Ü^ž±üÏ
+•~.¢ç6¡y—Äç,²X4$NXÿbþÈf$â]ˆ‚w&e9±NªƒEPoS$v æècÊyÞ9}ŒµÃñèÅNhD†ã†r‡RÕ‘8Aµ|þbg$b8D1æ—ÌB9²¾­ùZc×AŒ£fª£Ñ–P:.šŽ‰(ñÈtLRR*P'(7æ Xžžƒ Š¹¼Û¤,”#ëÛêgnC›F‡hÍRu³r.‡g ØÑxá5-oš(iš4iå"¦d9äØvÎAqòb+—®G„ q“òPŽ<_å/d2H ñ2R‡‘eç;úrž7\xü‚† ÉpÌâ&g f#ŠŠIBA±çü±Ùg^DcÁݬ[ø åö´K™h<ìnÏhÔ!<J]u»lAQ&4ûl–ÔÁ.ààW´¸Sb‘ŤĦ-Ù9VŽÐÄæf|{5üÈ>i•Ñ‡b®)š•ëKw8dŸ Äã!—•su@à‡­;õçZãZ6Wu@\bzõÆNŸ1<T¸ Å“Xá
+¿¿Pá‚…ìw •¯pL7sZ"år3tëc?lþOáó|‘q|?yWhÐdrMößçbÒ8Ê{ŸH(ö”ß·½[èÃ/¿ŽÚe¤
+Šš&ØŒÿcÐâ
+Ru4“û±&^]v‡ÍþŠ^_xçr‡§¶úÍp?Õ jñÿ?ˆendstream
+endobj
+194 0 obj
+2330
+endobj
+195 0 obj<</Type/Page/Parent 107 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
+áÒw³P04TIS045×3S072PIÑp VðÌ+I-JKLNUpI-ÎLÏQÉE™%™ùyš!Y\º-ºP=™™ A×®@.
+endobj
+197 0 obj
+98
+endobj
+198 0 obj<</Count 7/First 199 0 R/Last 234 0 R>>endobj
+199 0 obj<</Parent 198 0 R/Title(Table of Contents)/Dest[192 0 R/XYZ null 756 null]/Next 200 0 R>>endobj
+200 0 obj<</Parent 198 0 R/Count -2/First 201 0 R/Last 202 0 R/Title(1 Scope)/Dest[114 0 R/XYZ null 746 null]/Prev 199 0 R/Next 203 0 R>>endobj
+201 0 obj<</Parent 200 0 R/Title(1.1 Identification)/Dest[114 0 R/XYZ null 694 null]/Next 202 0 R>>endobj
+202 0 obj<</Parent 200 0 R/Title(1.2 Document Overview)/Dest[114 0 R/XYZ null 613 null]/Prev 201 0 R>>endobj
+203 0 obj<</Parent 198 0 R/Count -2/First 204 0 R/Last 205 0 R/Title(2 References)/Dest[120 0 R/XYZ null 746 null]/Prev 200 0 R/Next 206 0 R>>endobj
+204 0 obj<</Parent 203 0 R/Title(2.1 CUPS Documentation)/Dest[120 0 R/XYZ null 694 null]/Next 205 0 R>>endobj
+205 0 obj<</Parent 203 0 R/Title(2.2 Other Documents)/Dest[120 0 R/XYZ null 481 null]/Prev 204 0 R>>endobj
+206 0 obj<</Parent 198 0 R/Count -6/First 207 0 R/Last 218 0 R/Title(3 Internal Interfaces)/Dest[126 0 R/XYZ null 746 null]/Prev 203 0 R/Next 222 0 R>>endobj
+207 0 obj<</Parent 206 0 R/Count -2/First 208 0 R/Last 209 0 R/Title(3.1 Character Set Files)/Dest[126 0 R/XYZ null 694 null]/Next 210 0 R>>endobj
+208 0 obj<</Parent 207 0 R/Title(3.1.1 8-Bit Character Set Files)/Dest[126 0 R/XYZ null 580 null]/Next 209 0 R>>endobj
+209 0 obj<</Parent 207 0 R/Title(3.1.2 Unicode Character Set Files)/Dest[126 0 R/XYZ null 197 null]/Prev 208 0 R>>endobj
+210 0 obj<</Parent 206 0 R/Title(3.2 Language Files)/Dest[129 0 R/XYZ null 543 null]/Prev 207 0 R/Next 211 0 R>>endobj
+211 0 obj<</Parent 206 0 R/Count -2/First 212 0 R/Last 213 0 R/Title(3.3 MIME Files)/Dest[132 0 R/XYZ null 712 null]/Prev 210 0 R/Next 214 0 R>>endobj
+212 0 obj<</Parent 211 0 R/Title(3.3.1 mime.types)/Dest[132 0 R/XYZ null 637 null]/Next 213 0 R>>endobj
+213 0 obj<</Parent 211 0 R/Title(3.3.2 mime.convs)/Dest[132 0 R/XYZ null 275 null]/Prev 212 0 R>>endobj
+214 0 obj<</Parent 206 0 R/Title(3.4 Option Files)/Dest[135 0 R/XYZ null 738 null]/Prev 211 0 R/Next 215 0 R>>endobj
+215 0 obj<</Parent 206 0 R/Count -2/First 216 0 R/Last 217 0 R/Title(3.5 PostScript Printer Description Files)/Dest[135 0 R/XYZ null 477 null]/Prev 214 0 R/Next 218 0 R>>endobj
+216 0 obj<</Parent 215 0 R/Title(3.5.1 PPD Specification)/Dest[135 0 R/XYZ null 389 null]/Next 217 0 R>>endobj
+217 0 obj<</Parent 215 0 R/Title(3.5.2 CUPS Extensions to PPD Files)/Dest[135 0 R/XYZ null 316 null]/Prev 216 0 R>>endobj
+218 0 obj<</Parent 206 0 R/Count -3/First 219 0 R/Last 221 0 R/Title(3.6 Scheduler Configuration Files)/Dest[138 0 R/XYZ null 413 null]/Prev 215 0 R>>endobj
+219 0 obj<</Parent 218 0 R/Title(3.6.1 classes.conf)/Dest[138 0 R/XYZ null 246 null]/Next 220 0 R>>endobj
+220 0 obj<</Parent 218 0 R/Title(3.6.2 cupsd.conf)/Dest[141 0 R/XYZ null 449 null]/Prev 219 0 R/Next 221 0 R>>endobj
+221 0 obj<</Parent 218 0 R/Title(3.6.3 printers.conf)/Dest[150 0 R/XYZ null 597 null]/Prev 220 0 R>>endobj
+222 0 obj<</Parent 198 0 R/Count -9/First 223 0 R/Last 232 0 R/Title(4 External Interfaces)/Dest[156 0 R/XYZ null 746 null]/Prev 206 0 R/Next 233 0 R>>endobj
+223 0 obj<</Parent 222 0 R/Title(4.1 AppSocket Protocol)/Dest[156 0 R/XYZ null 694 null]/Next 224 0 R>>endobj
+224 0 obj<</Parent 222 0 R/Title(4.2 CUPS Browsing Protocol)/Dest[156 0 R/XYZ null 561 null]/Prev 223 0 R/Next 225 0 R>>endobj
+225 0 obj<</Parent 222 0 R/Count -1/First 226 0 R/Last 226 0 R/Title(4.3 CUPS Form File)/Dest[159 0 R/XYZ null 604 null]/Prev 224 0 R/Next 227 0 R>>endobj
+226 0 obj<</Parent 225 0 R/Title(4.3.1 CUPS Form DTD)/Dest[159 0 R/XYZ null 490 null]>>endobj
+227 0 obj<</Parent 222 0 R/Title(4.4 CUPS PostScript File)/Dest[168 0 R/XYZ null 597 null]/Prev 225 0 R/Next 228 0 R>>endobj
+228 0 obj<</Parent 222 0 R/Title(4.5 CUPS Raster File)/Dest[168 0 R/XYZ null 491 null]/Prev 227 0 R/Next 229 0 R>>endobj
+229 0 obj<</Parent 222 0 R/Title(4.6 CUPS Raw Files)/Dest[174 0 R/XYZ null 244 null]/Prev 228 0 R/Next 230 0 R>>endobj
+230 0 obj<</Parent 222 0 R/Title(4.7 Internet Printing Protocol)/Dest[177 0 R/XYZ null 738 null]/Prev 229 0 R/Next 231 0 R>>endobj
+231 0 obj<</Parent 222 0 R/Title(4.8 Line Printer Daemon Protocol)/Dest[177 0 R/XYZ null 678 null]/Prev 230 0 R/Next 232 0 R>>endobj
+232 0 obj<</Parent 222 0 R/Title(4.9 Server Message Block Protocol)/Dest[177 0 R/XYZ null 585 null]/Prev 231 0 R>>endobj
+233 0 obj<</Parent 198 0 R/Title(5 Directories)/Dest[180 0 R/XYZ null 746 null]/Prev 222 0 R/Next 234 0 R>>endobj
+234 0 obj<</Parent 198 0 R/Count -2/First 235 0 R/Last 236 0 R/Title(A Glossary)/Dest[186 0 R/XYZ null 746 null]/Prev 233 0 R>>endobj
+235 0 obj<</Parent 234 0 R/Title(A.1 Terms)/Dest[186 0 R/XYZ null 694 null]/Next 236 0 R>>endobj
+236 0 obj<</Parent 234 0 R/Title(A.2 Acronyms)/Dest[186 0 R/XYZ null 508 null]/Prev 235 0 R>>endobj
+237 0 obj<</Type/Catalog/Pages 107 0 R/Names 66 0 R/PageLayout/SinglePage/Outlines 198 0 R/OpenAction[114 0 R/XYZ null null null]/PageMode/UseOutlines/PageLabels<</Nums[0<</P(title)>>1<</P(eltit)>>2<</S/r>>4<</S/D>>]>>>>endobj
+xref
+0 238
+0000000000 65535 f
+0000000015 00000 n
+0000000226 00000 n
+0000001547 00000 n
+0000001621 00000 n
+0000001699 00000 n
+0000001776 00000 n
+0000001855 00000 n
+0000001931 00000 n
+0000002012 00000 n
+0000002096 00000 n
+0000002155 00000 n
+0000002256 00000 n
+0000002341 00000 n
+0000002442 00000 n
+0000002527 00000 n
+0000002628 00000 n
+0000002712 00000 n
+0000002750 00000 n
+0000002837 00000 n
+0000002922 00000 n
+0000003009 00000 n
+0000003094 00000 n
+0000003125 00000 n
+0000003191 00000 n
+0000003276 00000 n
+0000003332 00000 n
+0000003416 00000 n
+0000003447 00000 n
+0000003551 00000 n
+0000003656 00000 n
+0000003761 00000 n
+0000003865 00000 n
+0000003970 00000 n
+0000004075 00000 n
+0000004179 00000 n
+0000004284 00000 n
+0000004389 00000 n
+0000004494 00000 n
+0000004599 00000 n
+0000004704 00000 n
+0000004809 00000 n
+0000004914 00000 n
+0000005019 00000 n
+0000005124 00000 n
+0000005229 00000 n
+0000005334 00000 n
+0000005439 00000 n
+0000005544 00000 n
+0000005649 00000 n
+0000005754 00000 n
+0000005858 00000 n
+0000005963 00000 n
+0000006068 00000 n
+0000006173 00000 n
+0000006278 00000 n
+0000006383 00000 n
+0000006488 00000 n
+0000006593 00000 n
+0000006698 00000 n
+0000006803 00000 n
+0000006908 00000 n
+0000007012 00000 n
+0000007116 00000 n
+0000007221 00000 n
+0000007326 00000 n
+0000007602 00000 n
+0000007634 00000 n
+0000007666 00000 n
+0000008155 00000 n
+0000008203 00000 n
+0000008251 00000 n
+0000008299 00000 n
+0000008347 00000 n
+0000008395 00000 n
+0000008443 00000 n
+0000008491 00000 n
+0000008539 00000 n
+0000008587 00000 n
+0000008635 00000 n
+0000008683 00000 n
+0000008731 00000 n
+0000008779 00000 n
+0000008827 00000 n
+0000008875 00000 n
+0000008923 00000 n
+0000008971 00000 n
+0000009019 00000 n
+0000009067 00000 n
+0000009115 00000 n
+0000009163 00000 n
+0000009211 00000 n
+0000009259 00000 n
+0000009307 00000 n
+0000009355 00000 n
+0000009403 00000 n
+0000009451 00000 n
+0000009499 00000 n
+0000009547 00000 n
+0000009595 00000 n
+0000009643 00000 n
+0000009692 00000 n
+0000009741 00000 n
+0000009790 00000 n
+0000009839 00000 n
+0000009888 00000 n
+0000009937 00000 n
+0000009986 00000 n
+0000010295 00000 n
+0000010447 00000 n
+0000016805 00000 n
+0000016827 00000 n
+0000016922 00000 n
+0000017024 00000 n
+0000017044 00000 n
+0000017185 00000 n
+0000017640 00000 n
+0000017661 00000 n
+0000017774 00000 n
+0000017958 00000 n
+0000017979 00000 n
+0000018120 00000 n
+0000018897 00000 n
+0000018918 00000 n
+0000019031 00000 n
+0000019220 00000 n
+0000019241 00000 n
+0000019390 00000 n
+0000020468 00000 n
+0000020490 00000 n
+0000020649 00000 n
+0000021671 00000 n
+0000021692 00000 n
+0000021832 00000 n
+0000022966 00000 n
+0000022988 00000 n
+0000023151 00000 n
+0000024430 00000 n
+0000024452 00000 n
+0000024610 00000 n
+0000025820 00000 n
+0000025842 00000 n
+0000025982 00000 n
+0000027486 00000 n
+0000027508 00000 n
+0000027630 00000 n
+0000029275 00000 n
+0000029297 00000 n
+0000029419 00000 n
+0000031121 00000 n
+0000031143 00000 n
+0000031283 00000 n
+0000032602 00000 n
+0000032624 00000 n
+0000032737 00000 n
+0000032931 00000 n
+0000032952 00000 n
+0000033110 00000 n
+0000034268 00000 n
+0000034290 00000 n
+0000034430 00000 n
+0000035514 00000 n
+0000035536 00000 n
+0000035658 00000 n
+0000036422 00000 n
+0000036443 00000 n
+0000036565 00000 n
+0000037393 00000 n
+0000037414 00000 n
+0000037577 00000 n
+0000039125 00000 n
+0000039147 00000 n
+0000039269 00000 n
+0000040713 00000 n
+0000040735 00000 n
+0000040875 00000 n
+0000042095 00000 n
+0000042117 00000 n
+0000042262 00000 n
+0000042872 00000 n
+0000042893 00000 n
+0000043042 00000 n
+0000043703 00000 n
+0000043724 00000 n
+0000043837 00000 n
+0000044029 00000 n
+0000044050 00000 n
+0000044190 00000 n
+0000044835 00000 n
+0000044856 00000 n
+0000044987 00000 n
+0000045253 00000 n
+0000045274 00000 n
+0000045428 00000 n
+0000047829 00000 n
+0000047851 00000 n
+0000047964 00000 n
+0000048133 00000 n
+0000048153 00000 n
+0000048208 00000 n
+0000048313 00000 n
+0000048457 00000 n
+0000048563 00000 n
+0000048672 00000 n
+0000048821 00000 n
+0000048931 00000 n
+0000049038 00000 n
+0000049196 00000 n
+0000049343 00000 n
+0000049462 00000 n
+0000049583 00000 n
+0000049702 00000 n
+0000049853 00000 n
+0000049957 00000 n
+0000050061 00000 n
+0000050178 00000 n
+0000050355 00000 n
+0000050466 00000 n
+0000050588 00000 n
+0000050745 00000 n
+0000050851 00000 n
+0000050968 00000 n
+0000051075 00000 n
+0000051233 00000 n
+0000051343 00000 n
+0000051470 00000 n
+0000051625 00000 n
+0000051719 00000 n
+0000051844 00000 n
+0000051965 00000 n
+0000052084 00000 n
+0000052215 00000 n
+0000052348 00000 n
+0000052469 00000 n
+0000052583 00000 n
+0000052717 00000 n
+0000052814 00000 n
+0000052914 00000 n
+trailer
+<</Size 238/Root 237 0 R/Info 1 0 R>>
+startxref
+53141
+%%EOF
diff --git a/doc/idd.shtml b/doc/idd.shtml
new file mode 100644
index 000000000..64f958b9f
--- /dev/null
+++ b/doc/idd.shtml
@@ -0,0 +1,1417 @@
+<HTML>
+<HEAD>
+ <META NAME="COPYRIGHT" CONTENT="Copyright 1997-2000, All Rights Reserved">
+ <META NAME="DOCNUMBER" CONTENT="CUPS-IDD-1.1">
+ <META NAME="Author" CONTENT="Easy Software Products">
+ <TITLE>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.1.
+
+<EMBED SRC="system-overview.shtml">
+
+<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>
+
+<EMBED SRC="references.shtml">
+
+<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, or between Unicode and printer fonts.
+They are named using the IETF charset names defined in RFCnnnn. These
+files are ASCII text, the content of which is described below. Comments
+can be included by using the <TT>#</TT> character in the first column
+of a line.
+
+<H3>8-Bit Character Set Files</H3>
+
+<P>8-bit character set files start with a line reading:
+
+<UL><PRE>
+charset 8bit
+</PRE></UL>
+
+<P>Following this are lines that define the font information:
+
+<UL><PRE>
+first last direction width normal bold italic bold-italic
+</PRE></UL>
+
+<P><VAR>First</VAR> and <VAR>last</VAR> are the first and last glyphs
+in the font mapping that correspond to that font; a maximum of 256
+characters can be mapped within each group, with a maximum of 256
+mappings (this is a PostScript limitation.) The glyph values are
+hexadecimal.
+
+<P><VAR>Direction</VAR> is the string "ltor", "rtol", or "rtola" indicating
+left-to-right, right-to-left, or right-to-left Arabic text.
+
+<P><VAR>Width</VAR> is the string "single" or "double"; double means that the
+glyphs are twice as wide as ASCII characters in the Courier typeface.
+
+<P><VAR>Normal, bold, italic</VAR>, and <VAR>bold-italic</VAR> are the
+typefaces to use for each presentation. If characters are only available in
+a single style then only one typeface should be listed (e.g. "Symbol".)
+Each font that is listed will be used (and downloaded if needed) when
+printing.
+
+<P>The remaining lines define a character to Unicode glyph mapping for the
+character set. The character and glyph values are hexadecimal:
+
+<UL><PRE>
+xx yyyy
+</PRE></UL>
+
+<H3>Unicode Character Set Files</H3>
+
+<P>Unicode character set files start with a line reading:
+
+<UL><PRE>
+charset encoding
+</PRE></UL>
+
+<P><VAR>Encoding</VAR> is the encoding to use for the text; currently only
+the string "utf8" is supported.
+
+<P>Following this are lines defining the font information:
+
+<UL><PRE>
+first last direction width normal bold italic bold-italic
+</PRE></UL>
+
+<P><VAR>First</VAR> and <VAR>last</VAR> are the first and last glyphs
+in the font mapping that correspond to that font; a maximum of 256
+characters can be mapped within each group, with a maximum of 256
+mappings (this is a PostScript limitation.) The glyph values are
+hexadecimal.
+
+<P><VAR>Direction</VAR> is the string "ltor", "rtol", or "rtola" indicating
+left-to-right, right-to-left, or right-to-left Arabic text.
+
+<P><VAR>Width</VAR> is the string "single" or "double"; double means that the
+glyphs are twice as wide as ASCII characters in the Courier typeface.
+
+<P><VAR>Normal, bold, italic</VAR>, and <VAR>bold-italic</VAR> are the
+typefaces to use for each presentation. If characters are only available in
+a single style then only one typeface should be listed (e.g. "Symbol".)
+Each font that is listed will be used (and downloaded if needed) when
+printing.
+
+<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>cp874
+ <LI>cp1250
+ <LI>cp1251
+ <LI>cp1252
+ <LI>cp1253
+ <LI>cp1254
+ <LI>cp1255
+ <LI>cp1256
+ <LI>cp1257
+ <LI>cp1258
+ <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-10
+ <LI>iso-8859-13
+ <LI>iso-8859-14
+ <LI>iso-8859-15
+ <LI>us-ascii
+ <LI>utf-8
+</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 ")" |
+ "contains(" offset "," length "," 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>Option Files</H2>
+
+<P>CUPS maintains user-defined printer and option files for each
+printer and user on the system. The printers and options defined in the
+system option file (<CODE>/etc/cups/lpoptions</CODE>) are loaded first,
+followed by the user option file (<CODE>$HOME/.lpoptions</CODE>).
+Options in the user file replace those defined in the system file for
+the same destination. Each line in the files can be one of the
+following:
+
+<UL><PRE>
+Dest name option=value option=value ... option=value
+Dest name/instance option=value option=value ... option=value
+Default name option=value option=value ... option=value
+Default name/instance option=value option=value ... option=value
+</PRE></UL>
+
+<P>The line beginning with "Default" indicates the default destination for
+print jobs; a default line in the user option file overrides the default
+defined in the system option file.
+
+<P><VAR>Name</VAR> is the name of a printer known to the local server.
+
+<P><VAR>Instance</VAR> can be any string of letters, numbers, and the underscore
+up to 127 characters in length.
+
+<P>The remainder of the line contains a list of space-separated options
+and their values.
+
+<H2>PostScript Printer Description Files</H2>
+
+<P>PostScript Printer Description ("PPD") files describe the capabilities
+of each printer and are used by CUPS to support printer-specific features
+and intelligent filtering.
+
+<H3>PPD Specification</H3>
+
+<P>The PPD file format is described in
+<A HREF="http://partners.adobe.com/asn/developer/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" or "1.1".
+
+<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>Surrounds a class definition.</TD>
+</TR>
+<TR>
+ <TD>&lt;DefaultClass name&gt;<BR>
+ &lt;/Class&gt;</TD>
+ <TD>Surrounds a class definition for the default destination.</TD>
+</TR>
+<TR>
+ <TD>Accepting</TD>
+ <TD>Specifies whether the class is accepting new jobs. May be
+ the names "Yes" or "No".</TD>
+</TR>
+<TR>
+ <TD>AllowUsers</TD>
+ <TD>Specifies a list of users that are allowed to access the class.</TD>
+</TR>
+<TR>
+ <TD>BannerStart</TD>
+ <TD>Specifies the banner that is printed before other files in a
+ job.</TD>
+</TR>
+<TR>
+ <TD>BannerEnd</TD>
+ <TD>Specifies the banner that is printed after other files in a
+ job.</TD>
+</TR>
+<TR>
+ <TD>DenyUsers</TD>
+ <TD>Specifies a list of users that are not allowed to access the
+ class.</TD>
+</TR>
+<TR>
+ <TD>Info</TD>
+ <TD>A textual description of the class.</TD>
+</TR>
+<TR>
+ <TD>Location</TD>
+ <TD>A textual location of the class.</TD>
+</TR>
+<TR>
+ <TD>MoreInfo</TD>
+ <TD>A URL pointing to additional information on the class.</TD>
+</TR>
+<TR>
+ <TD>Printer</TD>
+ <TD>Specifies a printer that is a member of the class.</TD>
+</TR>
+<TR>
+ <TD>State</TD>
+ <TD>Specifies the initial state of the class; can be "Idle" or
+ "Stopped".</TD>
+</TR>
+<TR>
+ <TD>StateMessage</TD>
+ <TD>Specifies a textual message for the current class state.</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>access_log</TD>
+ <TD>Specifies the location of the access log file. The special name
+ "syslog" can be used to send access log information to the system
+ log.</TD>
+</TR>
+<TR>
+ <TD>Allow</TD>
+ <TD>-</TD>
+ <TD>Allows connections from the specified host, network, or
+ domain.</TD>
+</TR>
+<TR>
+ <TD>AuthClass</TD>
+ <TD>-</TD>
+ <TD>Specifies what level of authentication is required; may be
+ "User", "System", or "Group".</TD>
+</TR>
+<TR>
+ <TD>AuthType</TD>
+ <TD>None</TD>
+ <TD>Specifies the type of authentication to perform; may be
+ "None", "Basic", or "Digest".</TD>
+</TR>
+<TR>
+ <TD>BrowseAddress</TD>
+ <TD>255.255.255.255</TD>
+ <TD>Specifies a broadcast address to send CUPS browsing packets to.</TD>
+</TR>
+<TR>
+ <TD>BrowseAllow</TD>
+ <TD>-</TD>
+ <TD>Specifies hosts or addresses from which browsing information
+ should be used.</TD>
+</TR>
+<TR>
+ <TD>BrowseDeny</TD>
+ <TD>-</TD>
+ <TD>Specifies hosts or addresses from which browsing information
+ should not be used.</TD>
+</TR>
+<TR>
+ <TD>BrowseInterval</TD>
+ <TD>30</TD>
+ <TD>Specifies the number of seconds between browsing updates. A
+ browse interval of 0 seconds disables outgoing packets.</TD>
+</TR>
+<TR>
+ <TD>BrowseOrder</TD>
+ <TD>Allow,Deny</TD>
+ <TD>Specifies the order of BrowseAllow and BrowseDeny directive
+ processing; can be "Deny,Allow" to implicitly deny hosts unless
+ they are allowed by a BrowseAllow line, or "Allow,Deny" to
+ implicitly allow hosts unless they are denied by a BrowseDeny
+ line.</TD>
+</TR>
+<TR>
+ <TD>BrowsePoll</TD>
+ <TD>-</TD>
+ <TD>Specifies a server to poll for available printers and classes.</TD>
+</TR>
+<TR>
+ <TD>BrowsePort</TD>
+ <TD>631</TD>
+ <TD>Specifies the UDP port number to use for browse packets.</TD>
+</TR>
+<TR>
+ <TD>BrowseRelay</TD>
+ <TD>-</TD>
+ <TD>Specifies a source and destination address for relaying browser
+ information from one subnet to another.</TD>
+</TR>
+<TR>
+ <TD>BrowseTimeout</TD>
+ <TD>300</TD>
+ <TD>Specifies the number of seconds to wait until remote destinations
+ are removed from the local destination list.</TD>
+</TR>
+<TR>
+ <TD>Browsing</TD>
+ <TD>On</TD>
+ <TD>Specifies whether or not printer and class browsing is enabled; can
+ be "On" or "Off".</TD>
+</TR>
+<TR>
+ <TD>DefaultCharset</TD>
+ <TD>iso-8859-1</TD>
+ <TD>Specifies the default character set.</TD>
+</TR>
+<TR>
+ <TD>DefaultLanguage</TD>
+ <TD>current locale</TD>
+ <TD>Specifies the default language.</TD>
+</TR>
+<TR>
+ <TD>Deny</TD>
+ <TD>-</TD>
+ <TD>Refuses connections from the specified host, network, or
+ domain.</TD>
+</TR>
+<TR>
+ <TD>DocumentRoot</TD>
+ <TD>/usr/share/doc/cups</TD>
+ <TD>Specifies the document data root directory.</TD>
+</TR>
+<TR>
+ <TD>ErrorLog</TD>
+ <TD>error_log</TD>
+ <TD>Specifies the error log file location. The special name
+ "syslog" can be used to send error log information to the system
+ log.</TD>
+</TR>
+<TR>
+ <TD>Group</TD>
+ <TD>root, sys, system</TD>
+ <TD>Specifies the group name or ID that is used when running
+ external programs.</TD>
+</TR>
+<TR>
+ <TD>HostNameLookups</TD>
+ <TD>Off</TD>
+ <TD>Specifies whether or not to perform reverse IP address lookups to
+ get the actual hostname; may be "On" or "Off". Hostname lookups can
+ significantly degrade the performance of the CUPS server if one or
+ more DNS servers is not functioning properly.</TD>
+</TR>
+<TR>
+ <TD>ImplicitClasses</TD>
+ <TD>On</TD>
+ <TD>Specifies whether or not to automatically create printer classes
+ when more than one printer or class of the same name is detected on
+ the network; may be "On" or "Off".</TD>
+</TR>
+<TR>
+ <TD>KeepAlive</TD>
+ <TD>On</TD>
+ <TD>Specifies whether or not to use the HTTP Keep-Alive feature; may
+ be "On" or "Off".</TD>
+</TR>
+<TR>
+ <TD>KeepAliveTimeout</TD>
+ <TD>30</TD>
+ <TD>Specifies the amount of time to keep the HTTP connection alive
+ before closing it.</TD>
+</TR>
+<TR>
+ <TD>&lt;Location path&gt;<BR>
+ &lt;/Location&gt;</TD>
+ <TD>-</TD>
+ <TD>Specifies a location to restrict access to.</TD>
+</TR>
+<TR>
+ <TD>LogLevel</TD>
+ <TD>info</TD>
+ <TD>Controls the amount of information that is logged in the
+ error log file. Can be one of "debug", "info", "warn", "error",
+ or "none", in decreasing order or verbosity.</TD>
+</TR>
+<TR>
+ <TD>MaxClients</TD>
+ <TD>100</TD>
+ <TD>Specifies the maximum number of simultaneous active clients.
+ This value is internally limited to 1/3 of the total number of
+ available file descriptors.</TD>
+</TR>
+<TR>
+ <TD>MaxLogSize</TD>
+ <TD>0</TD>
+ <TD>Specifies the maximum size of the access, error, and page
+ log files in bytes. If set to 0 then no maximum size is set.
+ Log files are rotated automatically when this size is
+ exceeded.</TD>
+</TR>
+<TR>
+ <TD>MaxRequestSize</TD>
+ <TD>0</TD>
+ <TD>Specifies the maximum size of HTTP requests in bytes. If set to 0
+ then there is no maximum.</TD>
+</TR>
+<TR>
+ <TD>Order</TD>
+ <TD>Allow,Deny</TD>
+ <TD>Specifies the order of Allow and Deny directive processing; can
+ be "Deny,Allow" to implicitly deny hosts unless they are allowed by
+ an Allow line, or "Allow,Deny" to implicitly allow hosts unless they
+ are denied by a Deny line.</TD>
+</TR>
+<TR>
+ <TD>PageLog</TD>
+ <TD>page_log</TD>
+ <TD>Specifies the location of the page log file. The special name
+ "syslog" can be used to send page log information to the system
+ log.</TD>
+</TR>
+<TR>
+ <TD>Port</TD>
+ <TD>631</TD>
+ <TD>Specifies a port number to listen to for HTTP connections.</TD>
+</TR>
+<TR>
+ <TD>Printcap</TD>
+ <TD>/etc/printcap</TD>
+ <TD>Specifies the location of a Berkeley printcap file to update
+ with a list of current printers and classes. If no filename is
+ supplied then this automatic generation is disabled.</TD>
+</TR>
+<TR>
+ <TD>RequestRoot</TD>
+ <TD>/var/spool/cups</TD>
+ <TD>Specifies the location of request files.</TD>
+</TR>
+<TR>
+ <TD>RIPCache</TD>
+ <TD>8m</TD>
+ <TD>Specifies the size of the memory cache in bytes that is used by
+ RIP filters.</TD>
+</TR>
+<TR>
+ <TD>ServerAdmin</TD>
+ <TD>root@ServerName</TD>
+ <TD>Specifies the person to contact with problems.</TD>
+</TR>
+<TR>
+ <TD>ServerName</TD>
+ <TD>hostname</TD>
+ <TD>Specifies the hostname that is supplied to HTTP clients. This
+ is also used to determine the default CUPS server for the CUPS IPP
+ client applications.</TD>
+</TR>
+<TR>
+ <TD>ServerRoot</TD>
+ <TD>/etc/cups</TD>
+ <TD>Specifies the root directory for server configuration files.</TD>
+</TR>
+<TR>
+ <TD>SystemGroup</TD>
+ <TD>root, sys, system</TD>
+ <TD>Specifies the group name used for System class authentication.</TD>
+</TR>
+<TR>
+ <TD>TempDir</TD>
+ <TD>/var/tmp</TD>
+ <TD>Specifies the temporary directory to use.</TD>
+</TR>
+<TR>
+ <TD>Timeout</TD>
+ <TD>300</TD>
+ <TD>The timeout in seconds before client connections are closed
+ in the middle of a request.</TD>
+</TR>
+<TR>
+ <TD>User</TD>
+ <TD>lp</TD>
+ <TD>Specifies the user that is used when running external programs.</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>Accepting</TD>
+ <TD>Specifies whether the printer is accepting new jobs. May be
+ the names "Yes" or "No".</TD>
+</TR>
+<TR>
+ <TD>&lt;DefaultPrinter name&gt;<BR>
+ &lt;/Printer&gt;</TD>
+ <TD>Surrounds the printer definition for a default destination.</TD>
+</TR>
+<TR>
+ <TD>AllowUsers</TD>
+ <TD>Specifies a list of users that are allowed to access the printer.</TD>
+</TR>
+<TR>
+ <TD>BannerStart</TD>
+ <TD>Specifies the banner that is printed before other files in a
+ job.</TD>
+</TR>
+<TR>
+ <TD>BannerEnd</TD>
+ <TD>Specifies the banner that is printed after other files in a
+ job.</TD>
+</TR>
+<TR>
+ <TD>DenyUsers</TD>
+ <TD>Specifies a list of users that are not allowed to access the
+ printer.</TD>
+</TR>
+<TR>
+ <TD>DeviceURI</TD>
+ <TD>Specifies the device-uri attribute for the printer.</TD>
+</TR>
+<TR>
+ <TD>Info</TD>
+ <TD>A textual description of the printer.</TD>
+</TR>
+<TR>
+ <TD>Location</TD>
+ <TD>A textual location of the printer.</TD>
+</TR>
+<TR>
+ <TD>MoreInfo</TD>
+ <TD>A URL pointing to additional information on the printer.</TD>
+</TR>
+<TR>
+ <TD>&lt;Printer name&gt;<BR>
+ &lt;/Printer&gt;</TD>
+ <TD>Surrounds the printer definition.</TD>
+</TR>
+<TR>
+ <TD>State</TD>
+ <TD>Specifies the initial state of the printer; can be "Idle" or
+ "Stopped".</TD>
+</TR>
+<TR>
+ <TD>StateMessage</TD>
+ <TD>Specifies a textual message for the current printer state.</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. The URI method name is "socket".
+
+<P>The AppSocket protocol is used by the Hewlett Packard JetDirect
+network interfaces and print servers, as well as many other vendors'
+products. See the CUPS Software Administrators Manual for a list of
+supported products.
+
+<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 SP "location" SP "info" SP "make-and-model" NL
+</PRE></UL>
+
+<P><VAR>State, uri, location, info</VAR>, and <VAR>make-and-model</VAR>,
+correspond to the IPP <CODE>printer-state</CODE>,
+<CODE>printer-uri-supported</CODE>, <CODE>printer-location</CODE>,
+<CODE>printer-info</CODE>, and <CODE>printer-make-and-model</CODE>
+attributes.
+
+<P><VAR>Type</VAR> 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 Form File</H2>
+
+<P>CUPS Form files are XML files used by the CUPS <CODE>formtops</CODE>
+filter to produce dynamic banner pages and support preprinted forms.
+
+<P>The MIME type for CUPS Form files is
+<CODE>application/vnd.cups-form</CODE>.
+
+<H3>CUPS Form DTD</H3>
+
+<P>The following DTD describes the available elements and attributes in
+a CUPS Form file:
+
+<CENTER><TABLE BORDER>
+<TR>
+<TD><PRE>
+&lt;!ENTITY % Angle "CDATA" -- angle in degrees -->
+
+&lt;!ENTITY % Color "CDATA" -- a color using sRGB: #RRGGBB as Hex values -->
+
+&lt;!ENTITY % Length "CDATA" -- nn for pixels or nn% for percentage length -->
+
+&lt;!ENTITY % Lengths "CDATA" -- comma-separated Length values -->
+
+&lt;!ENTITY % Text "CDATA">
+
+&lt;!ENTITY % heading "H1|H2|H3|H4|H5|H6">
+
+&lt;!ENTITY % preformatted "PRE">
+
+&lt;!ENTITY % i18n
+ "lang %LanguageCode; #IMPLIED -- language code --
+ dir (ltr|rtl) #IMPLIED -- direction for weak/neutral text --"
+ >
+
+&lt;!ENTITY % attrs "%i18n;">
+
+&lt;!ENTITY % fontstyle
+ "B | FONT | I | TT">
+
+&lt;!ENTITY % graphics
+ "BOX | RECT | LINE | POLY | ARC | PIE | TEXT">
+
+&lt;!ENTITY % insert
+ "IMG | VAR">
+
+&lt;!-- %inline; covers inline or "text-level" elements -->
+&lt;!ENTITY % inline "#PCDATA | %fontstyle; | %graphics; | %insert;">
+
+&lt;!ELEMENT (%fontstyle;) - - (%inline;)*>
+&lt;!ATTLIST (%fontstyle;)
+ %attrs; -- %i18n --
+ >
+
+&lt;!ELEMENT BR - O EMPTY -- forced line break -->
+&lt;!ATTLIST BR
+ %attrs; -- %i18n --
+ >
+
+&lt;!ENTITY % block
+ "P | %heading; | %preformatted;">
+
+&lt;!ENTITY % flow "%block; | %inline;">
+
+&lt;!ELEMENT PAGE O O (%flow;)+ -- document body -->
+&lt;!ATTLIST PAGE
+ %attrs; -- %i18n --
+ align (left|center|right) #IMPLIED -- horizontal alignment --
+ valign (top|middle|center|bottom) #IMPLIED -- vertical alignment --
+ >
+
+&lt;!ELEMENT P - O (%inline;)* -- paragraph -->
+&lt;!ATTLIST P
+ %attrs; -- %i18n --
+ align (left|center|right) #IMPLIED -- horizontal alignment --
+ >
+
+&lt;!ELEMENT (%heading;) - - (%inline;)* -- heading -->
+&lt;!ATTLIST (%heading;)
+ %attrs; -- %i18n --
+ align (left|center|right) #IMPLIED -- horizontal alignment --
+ >
+
+&lt;!ELEMENT PRE - - (%inline;)* -- preformatted text -->
+&lt;!ATTLIST PRE
+ %attrs; -- %i18n --
+ align (left|center|right) #IMPLIED -- horizontal alignment --
+ >
+
+&lt;!ELEMENT BOX - O EMPTY -- unfilled box -->
+&lt;!ATTLIST BOX
+ color %Color; #IMPLIED -- override color --
+ height %Length; #REQUIRED -- height of box --
+ thickness %Length; #IMPLIED -- override line thickness --
+ width %Length; #REQUIRED -- width of box --
+ x %Length; #REQUIRED -- horizontal position --
+ y %Length; #REQUIRED -- vertical position --
+ >
+
+&lt;!ELEMENT RECT - O EMPTY -- filled box -->
+&lt;!ATTLIST RECT
+ color %Color; #IMPLIED -- override color --
+ height %Length; #REQUIRED -- height of box --
+ width %Length; #REQUIRED -- width of box --
+ x %Length; #REQUIRED -- horizontal position --
+ y %Length; #REQUIRED -- vertical position --
+ >
+
+&lt;!ELEMENT LINE - O EMPTY -- polyline -->
+&lt;!ATTLIST LINE
+ color %Color; #IMPLIED -- override color --
+ thickness %Length; #IMPLIED -- override line thickness --
+ x %Lengths; #REQUIRED -- horizontal positions --
+ y %Lengths; #REQUIRED -- vertical positions --
+ >
+
+&lt;!ELEMENT POLY - O EMPTY -- polygon (filled) -->
+&lt;!ATTLIST POLY
+ color %Color; #IMPLIED -- override color --
+ x %Lengths; #REQUIRED -- horizontal positions --
+ y %Lengths; #REQUIRED -- vertical positions --
+ >
+
+&lt;!ELEMENT ARC - O EMPTY -- unfilled arc -->
+&lt;!ATTLIST ARC
+ color %Color; #IMPLIED -- override color --
+ end %Angle; #IMPLIED -- override end angle --
+ height %Length; #REQUIRED -- height of arc --
+ start %Angle; #IMPLIED -- override start angle --
+ thickness %Length; #IMPLIED -- override line thickness --
+ width %Length; #REQUIRED -- width of arc --
+ x %Length; #REQUIRED -- horizontal position --
+ y %Length; #REQUIRED -- vertical position --
+ >
+
+&lt;!ELEMENT PIE - O EMPTY -- filled arc -->
+&lt;!ATTLIST PIE
+ color %Color; #IMPLIED -- override color --
+ end %Angle; #IMPLIED -- override end angle --
+ height %Length; #REQUIRED -- height of arc --
+ start %Angle; #IMPLIED -- override start angle --
+ width %Length; #REQUIRED -- width of arc --
+ x %Length; #REQUIRED -- horizontal position --
+ y %Length; #REQUIRED -- vertical position --
+ >
+
+&lt;!ELEMENT TEXT - - (%flow;)* -- text box -->
+&lt;!ATTLIST RECT
+ align (left|center|right) #IMPLIED -- horizontal alignment --
+ height %Length; #REQUIRED -- height of box --
+ valign (top|middle|center|bottom) #IMPLIED -- vertical alignment --
+ width %Length; #REQUIRED -- width of box --
+ x %Length; #REQUIRED -- horizontal position --
+ y %Length; #REQUIRED -- vertical position --
+ >
+
+
+&lt;!ELEMENT IMG - O EMPTY -- Embedded image -->
+&lt;!ATTLIST IMG
+ %attrs; -- %coreattrs, %i18n, %events --
+ src %URI; #REQUIRED -- URI of image to embed --
+ height %Length; #IMPLIED -- override height --
+ width %Length; #IMPLIED -- override width --
+ >
+
+&lt;!ELEMENT HEAD O O (DEFVAR)* -- document head -->
+&lt;!ATTLIST HEAD
+ %i18n; -- lang, dir --
+ >
+
+&lt;!ELEMENT DEFVAR - O EMPTY -- variable definition -->
+&lt;!ATTLIST DEFVAR
+ name CDATA #REQUIRED -- name
+ value CDATA #REQUIRED -- value
+ >
+
+
+&lt;!ENTITY % html.content "HEAD, PAGE">
+
+&lt;!ELEMENT CUPSFORM - - (HEAD) (PAGE)+ -- document root element -->
+&lt;!ATTLIST CUPSFORM
+ %i18n; -- lang, dir --
+ >
+</PRE></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/asn/developer/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 Image 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>Internet Printing Protocol</H2>
+
+<P>The Internet Printing Protocol and the CUPS extensions to it are
+described in the CUPS Implementation of IPP document.
+
+<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>.
+
+<P>The URI method name for LPD is "lpd".
+
+<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>.
+
+<P>The URI method name for SMB is "smb".
+
+<H1>Directories</H1>
+
+<DL>
+
+ <DT>/etc/cups
+ <DD>The scheduler configuration and MIME files reside here.
+
+ <DT>/etc/cups/certs
+ <DD>The authentication certificates reside here.
+
+ <DT>/etc/cups/interfaces
+ <DD>System V interface scripts reside here.
+
+ <DT>/etc/cups/ppd
+ <DD>This directory contains PPD files for each printer.
+
+ <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/cups/backend
+ <DD>The backend filters reside here.
+
+ <DT>/usr/lib/cups/cgi-bin
+ <DD>The CGI programs reside here.
+
+ <DT>/usr/lib/cups/daemon
+ <DD>The polling and LPD daemons reside here.
+
+ <DT>/usr/lib/cups/filter
+ <DD>The file filters reside here.
+
+ <DT>/usr/sbin
+ <DD>The <CODE>accept</CODE>, <CODE>cupsd</CODE>,
+ <CODE>lpadmin</CODE>, <CODE>lpc</CODE>, and <CODE>reject</CODE>
+ commands reside here.
+
+ <DT>/usr/share/cups
+ <DD>This is the root directory of the CUPS static data.
+
+ <DT>/usr/share/cups/charsets
+ <DD>The character set files reside here.
+
+ <DT>/usr/share/cups/data
+ <DD>The 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>/usr/share/doc/cups
+ <DD>The scheduler documentation files reside here.
+
+ <DT>/var/log/cups
+ <DD>The <CODE>access_log</CODE>, <CODE>error_log</CODE>, and
+ <CODE>page_log</CODE> files reside here.
+
+ <DT>/var/spool/cups
+ <DD>This directory contains print job files.
+
+</DL>
+
+<EMBED SRC="glossary.shtml">
+
+</BODY>
+</HTML>
diff --git a/doc/images/accept-jobs.gif b/doc/images/accept-jobs.gif
new file mode 100644
index 000000000..9da7a0dce
--- /dev/null
+++ b/doc/images/accept-jobs.gif
Binary files differ
diff --git a/doc/images/add-class.gif b/doc/images/add-class.gif
new file mode 100644
index 000000000..0f43a6fcd
--- /dev/null
+++ b/doc/images/add-class.gif
Binary files differ
diff --git a/doc/images/add-printer.gif b/doc/images/add-printer.gif
new file mode 100644
index 000000000..90d17eb3f
--- /dev/null
+++ b/doc/images/add-printer.gif
Binary files differ
diff --git a/doc/images/cancel-job.gif b/doc/images/cancel-job.gif
new file mode 100644
index 000000000..3cc1e23bc
--- /dev/null
+++ b/doc/images/cancel-job.gif
Binary files differ
diff --git a/doc/images/cancel-jobs.gif b/doc/images/cancel-jobs.gif
new file mode 100644
index 000000000..2384b903c
--- /dev/null
+++ b/doc/images/cancel-jobs.gif
Binary files differ
diff --git a/doc/images/cancel.gif b/doc/images/cancel.gif
new file mode 100644
index 000000000..e99460678
--- /dev/null
+++ b/doc/images/cancel.gif
Binary files differ
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/config-printer.gif b/doc/images/config-printer.gif
new file mode 100644
index 000000000..22849db7d
--- /dev/null
+++ b/doc/images/config-printer.gif
Binary files differ
diff --git a/doc/images/continue.gif b/doc/images/continue.gif
new file mode 100644
index 000000000..ff774adb3
--- /dev/null
+++ b/doc/images/continue.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/delete-class.gif b/doc/images/delete-class.gif
new file mode 100644
index 000000000..81b1465ac
--- /dev/null
+++ b/doc/images/delete-class.gif
Binary files differ
diff --git a/doc/images/delete-printer.gif b/doc/images/delete-printer.gif
new file mode 100644
index 000000000..41ce85d78
--- /dev/null
+++ b/doc/images/delete-printer.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/hold-job.gif b/doc/images/hold-job.gif
new file mode 100644
index 000000000..ebd448ae9
--- /dev/null
+++ b/doc/images/hold-job.gif
Binary files differ
diff --git a/doc/images/left.gif b/doc/images/left.gif
new file mode 100644
index 000000000..fd7b04104
--- /dev/null
+++ b/doc/images/left.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/manage-classes.gif b/doc/images/manage-classes.gif
new file mode 100644
index 000000000..69d5b0147
--- /dev/null
+++ b/doc/images/manage-classes.gif
Binary files differ
diff --git a/doc/images/manage-jobs.gif b/doc/images/manage-jobs.gif
new file mode 100644
index 000000000..adaff856f
--- /dev/null
+++ b/doc/images/manage-jobs.gif
Binary files differ
diff --git a/doc/images/manage-printers.gif b/doc/images/manage-printers.gif
new file mode 100644
index 000000000..cd897291d
--- /dev/null
+++ b/doc/images/manage-printers.gif
Binary files differ
diff --git a/doc/images/modify-class.gif b/doc/images/modify-class.gif
new file mode 100644
index 000000000..58a0ead82
--- /dev/null
+++ b/doc/images/modify-class.gif
Binary files differ
diff --git a/doc/images/modify-printer.gif b/doc/images/modify-printer.gif
new file mode 100644
index 000000000..593b5f881
--- /dev/null
+++ b/doc/images/modify-printer.gif
Binary files differ
diff --git a/doc/images/navbar.gif b/doc/images/navbar.gif
new file mode 100644
index 000000000..c19f634c0
--- /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/print-test-page.gif b/doc/images/print-test-page.gif
new file mode 100644
index 000000000..807dca10e
--- /dev/null
+++ b/doc/images/print-test-page.gif
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/images/reject-jobs.gif b/doc/images/reject-jobs.gif
new file mode 100644
index 000000000..6d938e308
--- /dev/null
+++ b/doc/images/reject-jobs.gif
Binary files differ
diff --git a/doc/images/release-job.gif b/doc/images/release-job.gif
new file mode 100644
index 000000000..a05cd9cc7
--- /dev/null
+++ b/doc/images/release-job.gif
Binary files differ
diff --git a/doc/images/restart-job.gif b/doc/images/restart-job.gif
new file mode 100644
index 000000000..a007efc5b
--- /dev/null
+++ b/doc/images/restart-job.gif
Binary files differ
diff --git a/doc/images/right.gif b/doc/images/right.gif
new file mode 100644
index 000000000..8ae8213ce
--- /dev/null
+++ b/doc/images/right.gif
Binary files differ
diff --git a/doc/images/show-active.gif b/doc/images/show-active.gif
new file mode 100644
index 000000000..d232ef9f8
--- /dev/null
+++ b/doc/images/show-active.gif
Binary files differ
diff --git a/doc/images/show-completed.gif b/doc/images/show-completed.gif
new file mode 100644
index 000000000..efd206cd5
--- /dev/null
+++ b/doc/images/show-completed.gif
Binary files differ
diff --git a/doc/images/start-class.gif b/doc/images/start-class.gif
new file mode 100644
index 000000000..2b6f43fad
--- /dev/null
+++ b/doc/images/start-class.gif
Binary files differ
diff --git a/doc/images/start-printer.gif b/doc/images/start-printer.gif
new file mode 100644
index 000000000..017bb394a
--- /dev/null
+++ b/doc/images/start-printer.gif
Binary files differ
diff --git a/doc/images/stop-class.gif b/doc/images/stop-class.gif
new file mode 100644
index 000000000..5cb7adfd8
--- /dev/null
+++ b/doc/images/stop-class.gif
Binary files differ
diff --git a/doc/images/stop-printer.gif b/doc/images/stop-printer.gif
new file mode 100644
index 000000000..b3accf3df
--- /dev/null
+++ b/doc/images/stop-printer.gif
Binary files differ
diff --git a/doc/index.html b/doc/index.html
new file mode 100644
index 000000000..1150e4cdf
--- /dev/null
+++ b/doc/index.html
@@ -0,0 +1,36 @@
+<HTML>
+<HEAD>
+ <TITLE>Common UNIX Printing System</TITLE>
+ <LINK REL=STYLESHEET TYPE="text/css" HREF="cups.css">
+ <MAP NAME="navbar">
+ <AREA SHAPE="RECT" COORDS="12,10,50,20" HREF="http://www.easysw.com" ALT="Easy Software Products Home Page">
+ <AREA SHAPE="RECT" COORDS="82,10,196,20" HREF="/admin" ALT="Do Administration Tasks">
+ <AREA SHAPE="RECT" COORDS="216,10,280,20" HREF="/classes" ALT="Manage Printer Classes Status">
+ <AREA SHAPE="RECT" COORDS="300,10,336,20" HREF="/documentation.html" ALT="On-Line Help">
+ <AREA SHAPE="RECT" COORDS="356,10,394,20" HREF="/jobs" ALT="Manage Jobs">
+ <AREA SHAPE="RECT" COORDS="414,10,476,20" HREF="/printers" ALT="Manage Printers">
+ <AREA SHAPE="RECT" COORDS="496,10,568,20" HREF="http://www.cups.org" ALT="Download the Current CUPS Software">
+ </MAP>
+</HEAD>
+
+<BODY BGCOLOR="#cccc99" TEXT="#000000" LINK="#0000FF" VLINK="#FF00FF">
+<CENTER>
+<IMG SRC="/images/navbar.gif" WIDTH="583" HEIGHT="30" USEMAP="#navbar" BORDER="0" ALT="Common UNIX Printing System">
+</CENTER>
+
+<H1><A HREF="admin">Do Administration Tasks</A></H1>
+<H1><A HREF="classes">Manage Printer Classes</A></H1>
+<H1><A HREF="documentation.html">On-Line Help</A></H1>
+<H1><A HREF="jobs">Manage Jobs</A></H1>
+<H1><A HREF="printers">Manage Printers</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-2000 by Easy Software Products,
+All Rights Reserved.
+
+</BODY>
+</HTML>
diff --git a/doc/ipp.html b/doc/ipp.html
new file mode 100644
index 000000000..6f78d4e13
--- /dev/null
+++ b/doc/ipp.html
@@ -0,0 +1,1301 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
+<HTML>
+<HEAD>
+<TITLE>CUPS Implementation of IPP</TITLE>
+<META NAME="AUTHOR" CONTENT="Easy Software Products">
+<META NAME="COPYRIGHT" CONTENT="Copyright 1997-2000, All Rights Reserved">
+<META NAME="DOCNUMBER" CONTENT="CUPS-IPP-1.1">
+<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
+<STYLE>
+BODY { font-family: serif; font-size: 11.0pt }
+H1 { font-family: sans-serif; font-size: 20.0pt }
+H2 { font-family: sans-serif; font-size: 17.0pt }
+H3 { font-family: sans-serif; font-size: 14.0pt }
+H4 { font-family: sans-serif; font-size: 11.0pt }
+H5 { font-family: sans-serif; font-size: 9.0pt }
+H6 { font-family: sans-serif; font-size: 8.0pt }
+SUB { font-size: 8.0pt }
+SUP { font-size: 8.0pt }
+PRE { font-size: 9.0pt }
+</STYLE>
+</HEAD>
+<BODY>
+<CENTER><A HREF="#CONTENTS"><IMG SRC="images/cups-large.gif" BORDER="0"><BR>
+<H1>CUPS Implementation of IPP</H1></A><BR>
+CUPS-IPP-1.1<BR>
+Easy Software Products<BR>
+Copyright 1997-2000, 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 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 Overview</A></B>
+<UL>
+<LI><A HREF="#3_1">3.1 IPP URIs</A></LI>
+<LI><A HREF="#3_2">3.2 CUPS IPP Operations</A></LI>
+</UL>
+<B><A HREF="#4">4 Operations</A></B>
+<UL>
+<LI><A HREF="#4_1">4.1 Print-Job Operation</A></LI>
+<UL>
+<LI><A HREF="#4_1_1">4.1.1 Print-Job Request</A></LI>
+<LI><A HREF="#4_1_2">4.1.2 Print-Job Response</A></LI>
+</UL>
+<LI><A HREF="#4_2">4.2 Create-Job Operation</A></LI>
+<UL>
+<LI><A HREF="#4_2_1">4.2.1 Create-Job Request</A></LI>
+<LI><A HREF="#4_2_2">4.2.2 Create-Job Response</A></LI>
+</UL>
+<LI><A HREF="#4_3">4.3 Set-Job-Attributes Operation</A></LI>
+<UL>
+<LI><A HREF="#4_3_1">4.3.1 Set-Job-Attributes Request</A></LI>
+<LI><A HREF="#4_3_2">4.3.2 Set-Job-Attributes Response</A></LI>
+</UL>
+<LI><A HREF="#4_4">4.4 CUPS-Get-Default Operation</A></LI>
+<UL>
+<LI><A HREF="#4_4_1">4.4.1 CUPS-Get-Default Request</A></LI>
+<LI><A HREF="#4_4_2">4.4.2 CUPS-Get-Default Response</A></LI>
+</UL>
+<LI><A HREF="#4_5">4.5 CUPS-Get-Printers Operation</A></LI>
+<UL>
+<LI><A HREF="#4_5_1">4.5.1 CUPS-Get-Printers Request</A></LI>
+<LI><A HREF="#4_5_2">4.5.2 CUPS-Get-Printers Response</A></LI>
+</UL>
+<LI><A HREF="#4_6">4.6 CUPS-Add-Printer Operation</A></LI>
+<UL>
+<LI><A HREF="#4_6_1">4.6.1 CUPS-Add-Printer Request</A></LI>
+<LI><A HREF="#4_6_2">4.6.2 CUPS-Add-Printer Response</A></LI>
+</UL>
+<LI><A HREF="#4_7">4.7 CUPS-Delete-Printer Operation</A></LI>
+<UL>
+<LI><A HREF="#4_7_1">4.7.1 CUPS-Delete-Printer Request</A></LI>
+<LI><A HREF="#4_7_2">4.7.2 CUPS-Delete-Printer Response</A></LI>
+</UL>
+<LI><A HREF="#4_8">4.8 CUPS-Get-Classes Operation</A></LI>
+<UL>
+<LI><A HREF="#4_8_1">4.8.1 CUPS-Get-Classes Request</A></LI>
+<LI><A HREF="#4_8_2">4.8.2 CUPS-Get-Classes Response</A></LI>
+</UL>
+<LI><A HREF="#4_9">4.9 CUPS-Add-Class Operation</A></LI>
+<UL>
+<LI><A HREF="#4_9_1">4.9.1 CUPS-Add-Class Request</A></LI>
+<LI><A HREF="#4_9_2">4.9.2 CUPS-Add-Class Response</A></LI>
+</UL>
+<LI><A HREF="#4_10">4.10 CUPS-Delete-Class Operation</A></LI>
+<UL>
+<LI><A HREF="#4_10_1">4.10.1 CUPS-Delete-Class Request</A></LI>
+<LI><A HREF="#4_10_2">4.10.2 CUPS-Delete-Class Response</A></LI>
+</UL>
+<LI><A HREF="#4_11">4.11 CUPS-Accept-Jobs Operation</A></LI>
+<UL>
+<LI><A HREF="#4_11_1">4.11.1 CUPS-Accept-Jobs Request</A></LI>
+<LI><A HREF="#4_11_2">4.11.2 CUPS-Accept-Jobs Response</A></LI>
+</UL>
+<LI><A HREF="#4_12">4.12 CUPS-Reject-Jobs Operation</A></LI>
+<UL>
+<LI><A HREF="#4_12_1">4.12.1 CUPS-Reject-Jobs Request</A></LI>
+<LI><A HREF="#4_12_2">4.12.2 CUPS-Reject-Jobs Response</A></LI>
+</UL>
+<LI><A HREF="#4_13">4.13 CUPS-Set-Default Operation</A></LI>
+<UL>
+<LI><A HREF="#4_13_1">4.13.1 CUPS-Set-Default Request</A></LI>
+<LI><A HREF="#4_13_2">4.13.2 CUPS-Set-Default Response</A></LI>
+</UL>
+<LI><A HREF="#4_14">4.14 CUPS-Get-Devices Operation</A></LI>
+<UL>
+<LI><A HREF="#4_14_1">4.14.1 CUPS-Get-Devices Request</A></LI>
+<LI><A HREF="#4_14_2">4.14.2 CUPS-Get-Devices Response</A></LI>
+</UL>
+<LI><A HREF="#4_15">4.15 CUPS-Get-PPDs Operation</A></LI>
+<UL>
+<LI><A HREF="#4_15_1">4.15.1 CUPS-Get-PPDs Request</A></LI>
+<LI><A HREF="#4_15_2">4.15.2 CUPS-Get-PPDs Response</A></LI>
+</UL>
+<LI><A HREF="#4_16">4.16 CUPS-Move-Job Operation</A></LI>
+<UL>
+<LI><A HREF="#4_16_1">4.16.1 CUPS-Move-Job Request</A></LI>
+<LI><A HREF="#4_16_2">4.16.2 CUPS-Move-Job Response</A></LI>
+</UL>
+</UL>
+<B><A HREF="#5">5 Attributes</A></B>
+<UL>
+<LI><A HREF="#5_1">5.1 Device Attributes</A></LI>
+<UL>
+<LI><A HREF="#5_1_1">5.1.1 device-class (type2 keyword)</A></LI>
+<LI><A HREF="#5_1_2">5.1.2 device-info (text(127))</A></LI>
+<LI><A HREF="#5_1_3">5.1.3 device-make-and-model (text(127))</A></LI>
+<LI><A HREF="#5_1_4">5.1.4 device-uri (uri)</A></LI>
+</UL>
+<LI><A HREF="#5_2">5.2 Job Template Attributes</A></LI>
+<UL>
+<LI><A HREF="#5_2_1">5.2.1 blackplot (boolean)</A></LI>
+<LI><A HREF="#5_2_2">5.2.2 brightness (integer(0:200))</A></LI>
+<LI><A HREF="#5_2_3">5.2.3 columns (integer(1:4))</A></LI>
+<LI><A HREF="#5_2_4">5.2.4 cpi (type2 enum)</A></LI>
+<LI><A HREF="#5_2_5">5.2.5 fitplot (boolean)</A></LI>
+<LI><A HREF="#5_2_6">5.2.6 gamma (integer(1:10000))</A></LI>
+<LI><A HREF="#5_2_7">5.2.7 hue (integer(-180:180))</A></LI>
+<LI><A HREF="#5_2_8">5.2.8 job-billing (name(MAX))</A></LI>
+<LI><A HREF="#5_2_9">5.2.9 job-sheets (1setof type3 keyword | name(MAX))</A>
+</LI>
+<LI><A HREF="#5_2_10">5.2.10 lpi (type2 enum)</A></LI>
+<LI><A HREF="#5_2_11">5.2.11 page-bottom (integer(0:MAX))</A></LI>
+<LI><A HREF="#5_2_12">5.2.12 page-left (integer(0:MAX))</A></LI>
+<LI><A HREF="#5_2_13">5.2.13 page-right (integer(0:MAX))</A></LI>
+<LI><A HREF="#5_2_14">5.2.14 page-set (type2 keyword)</A></LI>
+<LI><A HREF="#5_2_15">5.2.15 page-top (integer(0:MAX))</A></LI>
+<LI><A HREF="#5_2_16">5.2.16 penwidth (integer(0:MAX))</A></LI>
+<LI><A HREF="#5_2_17">5.2.17 ppi (integer(1:MAX))</A></LI>
+<LI><A HREF="#5_2_18">5.2.18 prettyprint (boolean)</A></LI>
+<LI><A HREF="#5_2_19">5.2.19 saturation (integer(0:200))</A></LI>
+<LI><A HREF="#5_2_20">5.2.20 scaling (integer(1:1000))</A></LI>
+<LI><A HREF="#5_2_21">5.2.21 wrap (boolean)</A></LI>
+</UL>
+<LI><A HREF="#5_3">5.3 PPD Attributes</A></LI>
+<UL>
+<LI><A HREF="#5_3_1">5.3.1 ppd-natural-language (naturalLanguage)</A></LI>
+<LI><A HREF="#5_3_2">5.3.2 ppd-make (text(127))</A></LI>
+<LI><A HREF="#5_3_3">5.3.3 ppd-make-and-model (text(127))</A></LI>
+<LI><A HREF="#5_3_4">5.3.4 ppd-name (name(255))</A></LI>
+</UL>
+<LI><A HREF="#5_4">5.4 Printer Attributes</A></LI>
+<UL>
+<LI><A HREF="#5_4_1">5.4.1 job-sheets-default (1setof type3 keyword |
+name(MAX))</A></LI>
+<LI><A HREF="#5_4_2">5.4.2 job-sheets-supported (1setof type3 keyword |
+name(MAX))</A></LI>
+<LI><A HREF="#5_4_3">5.4.3 printer-type (type2 enum)</A></LI>
+<LI><A HREF="#5_4_4">5.4.4 printer-type-mask (type2 enum)</A></LI>
+<LI><A HREF="#5_4_5">5.4.5 requesting-user-name-allowed (1setof
+name(127))</A></LI>
+<LI><A HREF="#5_4_6">5.4.6 requesting-user-name-denied (1setof
+name(127))</A></LI>
+</UL>
+<LI><A HREF="#5_5">5.5 Printer Class Attributes</A></LI>
+<UL>
+<LI><A HREF="#5_5_1">5.5.1 member-names (1setof name(127))</A></LI>
+<LI><A HREF="#5_5_2">5.5.2 member-uris (1setof uri)</A></LI>
+</UL>
+</UL>
+<HR>
+<H1><A NAME="1">1 Scope</A></H1>
+<H2><A NAME="1_1">1.1 Identification</A></H2>
+<P>This document provides an overview of the Internet Printing Protocol
+(&quot;IPP&quot;) version 1.1 as implemented in the Common UNIX Printing System
+(&quot;CUPS&quot;) version 1.1. </P>
+<H2><A NAME="1_2">1.2 Document Overview</A></H2>
+<P>This document is organized into the following sections: </P>
+<UL>
+<LI><A HREF="#1">1 - Scope</A></LI>
+<LI><A HREF="#2">2 - References</A></LI>
+<LI><A HREF="#3">3 - Overview</A></LI>
+<LI><A HREF="#4">4 - Operations</A></LI>
+<LI><A HREF="#5">5 - Attributes</A></LI>
+<LI><A HREF="#6">A - Glossary</A></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.1: CUPS Configuration Management Plan </LI>
+<LI>CUPS-IDD-1.1: CUPS System Interface Design Description </LI>
+<LI>CUPS-IPP-1.1: CUPS Implmentation of IPP </LI>
+<LI>CUPS-SAM-1.1.x: CUPS Software Administrators Manual </LI>
+<LI>CUPS-SDD-1.1: CUPS Software Design Description </LI>
+<LI>CUPS-SPM-1.1: CUPS Software Programming Manual </LI>
+<LI>CUPS-SSR-1.1: CUPS Software Security Report </LI>
+<LI>CUPS-STP-1.1: CUPS Software Test Plan </LI>
+<LI>CUPS-SUM-1.1.x: CUPS Software Users Manual </LI>
+<LI>CUPS-SVD-1.1.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>Adobe PostScript Printer Description File Format Specification,
+ Version 4.3. </LI>
+<LI>Adobe PostScript Language Reference, Third Edition. </LI>
+<LI>IPP: Job and Printer Set Operations </LI>
+<LI>IPP/1.1: Encoding and Transport </LI>
+<LI>IPP/1.1: Implementers Guide </LI>
+<LI>IPP/1.1: Model and Semantics </LI>
+<LI>RFC 1179, Line Printer Daemon Protocol </LI>
+<LI>RFC 2567, Design Goals for an Internet Printing Protocol </LI>
+<LI>RFC 2568, Rationale for the Structure of the Model and Protocol
+ for the Internet Printing Protocol </LI>
+<LI>RFC 2569, Mapping between LPD and IPP Protocols </LI>
+<LI>RFC 2616, Hypertext Transfer Protocol -- HTTP/1.1 </LI>
+<LI>RFC 2617, HTTP Authentication: Basic and Digest Access
+ Authentication </LI>
+</UL>
+<H1><A NAME="3">3 Overview</A></H1>
+<P>CUPS 1.1 implements IPP/1.1 and the operations and attributes
+defined in the &quot;IPP: Job and Printer Set Operations&quot;, &quot;IPP/1.1:
+Output-bin Attribute Extension&quot;, and &quot;IPP/1.1: finishings 'fold','
+trim', and 'bale' attribute values extension&quot; specifications. </P>
+<P>CUPS also provides 13 new operations and many new attributes to
+support multiple IPP printers and printer classes on a single host. </P>
+<H2><A NAME="3_1">3.1 IPP URIs</A></H2>
+<P>CUPS supports both the &quot;http&quot; and &quot;ipp&quot; methods. The following
+resource names are used: </P>
+<DL>
+<DT>method://hostname:port/ </DT>
+<DD>Can be used for all &quot;get&quot; operations. </DD>
+<DT>method://hostname:port/admin </DT>
+<DD>Used for all administrative operations. </DD>
+<DT>method://hostname:port/classes/name </DT>
+<DD>Specifies a printer class. </DD>
+<DT>method://hostname:port/jobs/id </DT>
+<DD>Specifies a job. </DD>
+<DT>method://hostname:port/printers/name </DT>
+<DD>Specifies a printer. </DD>
+</DL>
+<P>So a typical printer URI would be
+&quot;ipp://foo.bar.com/printers/LaserJet&quot;. </P>
+<P>In addition, the CUPS server also supports normal browser access to
+&quot;method://hostname:port/admin/&quot;, &quot;method://hostname:port/classes/&quot;,
+&quot;method://hostname:port/jobs/&quot;, and &quot;method://hostname:port/printers/&quot;
+to view and manage resources on the server dynamically. </P>
+<H2><A NAME="3_2">3.2 CUPS IPP Operations</A></H2>
+<P>CUPS provides 13 extension operations in addition to most of the
+standard IPP and registered extension operations:
+<CENTER>
+<TABLE BORDER WIDTH="80%">
+<TR><TH VALIGN="TOP">Operation Name</TH><TH VALIGN="TOP">CUPS</TH><TH VALIGN="TOP">
+Code</TH><TH VALIGN="TOP">Brief Description</TH></TR>
+<TR><TD VALIGN="TOP">Print-Job</TD><TD VALIGN="TOP">1.0</TD><TD VALIGN="TOP">
+0x0002</TD><TD VALIGN="TOP">Print a file.</TD></TR>
+<TR><TD VALIGN="TOP">Validate-Job</TD><TD VALIGN="TOP">1.0</TD><TD VALIGN="TOP">
+0x0004</TD><TD VALIGN="TOP">Validate job attributes.</TD></TR>
+<TR><TD VALIGN="TOP">Create-Job</TD><TD VALIGN="TOP">1.1</TD><TD VALIGN="TOP">
+0x0005</TD><TD VALIGN="TOP">Create a print job.</TD></TR>
+<TR><TD VALIGN="TOP">Send-Document</TD><TD VALIGN="TOP">1.1</TD><TD VALIGN="TOP">
+0x0006</TD><TD VALIGN="TOP">Send a file for a print job.</TD></TR>
+<TR><TD VALIGN="TOP">Cancel-Job</TD><TD VALIGN="TOP">1.0</TD><TD VALIGN="TOP">
+0x0008</TD><TD VALIGN="TOP">Cancel a print job.</TD></TR>
+<TR><TD VALIGN="TOP">Get-Job-Attributes</TD><TD VALIGN="TOP">1.0</TD><TD VALIGN="TOP">
+0x0009</TD><TD VALIGN="TOP">Get job attributes.</TD></TR>
+<TR><TD VALIGN="TOP">Get-Jobs</TD><TD VALIGN="TOP">1.0</TD><TD VALIGN="TOP">
+0x000A</TD><TD VALIGN="TOP">Get all jobs.</TD></TR>
+<TR><TD VALIGN="TOP">Get-Printer-Attributes</TD><TD VALIGN="TOP">1.0</TD><TD
+VALIGN="TOP">0x000B</TD><TD VALIGN="TOP">Get printer attributes.</TD></TR>
+<TR><TD VALIGN="TOP">Hold-Job</TD><TD VALIGN="TOP">1.1</TD><TD VALIGN="TOP">
+0x000C</TD><TD VALIGN="TOP">Hold a job for printing.</TD></TR>
+<TR><TD VALIGN="TOP">Release-Job</TD><TD VALIGN="TOP">1.1</TD><TD VALIGN="TOP">
+0x000D</TD><TD VALIGN="TOP">Release a job for printing.</TD></TR>
+<TR><TD VALIGN="TOP">Pause-Printer</TD><TD VALIGN="TOP">1.0</TD><TD VALIGN="TOP">
+0x0010</TD><TD VALIGN="TOP">Pause printing on a printer.</TD></TR>
+<TR><TD VALIGN="TOP">Resume-Printer</TD><TD VALIGN="TOP">1.0</TD><TD VALIGN="TOP">
+0x0011</TD><TD VALIGN="TOP">Resume printing on a printer.</TD></TR>
+<TR><TD VALIGN="TOP">Purge-Jobs</TD><TD VALIGN="TOP">1.0</TD><TD VALIGN="TOP">
+0x0012</TD><TD VALIGN="TOP">Purge all jobs.</TD></TR>
+<TR><TD VALIGN="TOP">Set-Job-Attributes</TD><TD VALIGN="TOP">1.1</TD><TD VALIGN="TOP">
+0x0014</TD><TD VALIGN="TOP">Set attributes for a pending or held job.</TD>
+</TR>
+<TR><TD VALIGN="TOP">CUPS-Get-Default</TD><TD VALIGN="TOP">1.0</TD><TD VALIGN="TOP">
+0x4001</TD><TD VALIGN="TOP">Get the default destination.</TD></TR>
+<TR><TD VALIGN="TOP">CUPS-Get-Printers</TD><TD VALIGN="TOP">1.0</TD><TD VALIGN="TOP">
+0x4002</TD><TD VALIGN="TOP">Get all of the available printers.</TD></TR>
+<TR><TD VALIGN="TOP">CUPS-Add-Printer</TD><TD VALIGN="TOP">1.0</TD><TD VALIGN="TOP">
+0x4003</TD><TD VALIGN="TOP">Add or modify a printer.</TD></TR>
+<TR><TD VALIGN="TOP">CUPS-Delete-Printer</TD><TD VALIGN="TOP">1.0</TD><TD
+VALIGN="TOP">0x4004</TD><TD VALIGN="TOP">Delete a printer.</TD></TR>
+<TR><TD VALIGN="TOP">CUPS-Get-Classes</TD><TD VALIGN="TOP">1.0</TD><TD VALIGN="TOP">
+0x4005</TD><TD VALIGN="TOP">Get all of the available printer classes.</TD>
+</TR>
+<TR><TD VALIGN="TOP">CUPS-Add-Class</TD><TD VALIGN="TOP">1.0</TD><TD VALIGN="TOP">
+0x4006</TD><TD VALIGN="TOP">Add or modify a printer class.</TD></TR>
+<TR><TD VALIGN="TOP">CUPS-Delete-Class</TD><TD VALIGN="TOP">1.0</TD><TD VALIGN="TOP">
+0x4007</TD><TD VALIGN="TOP">Delete a printer class.</TD></TR>
+<TR><TD VALIGN="TOP">CUPS-Accept-Jobs</TD><TD VALIGN="TOP">1.0</TD><TD VALIGN="TOP">
+0x4008</TD><TD VALIGN="TOP">Accept jobs on a printer or printer class.</TD>
+</TR>
+<TR><TD VALIGN="TOP">CUPS-Reject-Jobs</TD><TD VALIGN="TOP">1.0</TD><TD VALIGN="TOP">
+0x4009</TD><TD VALIGN="TOP">Reject jobs on a printer or printer class.</TD>
+</TR>
+<TR><TD VALIGN="TOP">CUPS-Set-Default</TD><TD VALIGN="TOP">1.0</TD><TD VALIGN="TOP">
+0x400A</TD><TD VALIGN="TOP">Set the default destination.</TD></TR>
+<TR><TD VALIGN="TOP">CUPS-Get-Devices</TD><TD VALIGN="TOP">1.1</TD><TD VALIGN="TOP">
+0x400B</TD><TD VALIGN="TOP">Get all of the available devices.</TD></TR>
+<TR><TD VALIGN="TOP">CUPS-Get-PPDs</TD><TD VALIGN="TOP">1.1</TD><TD VALIGN="TOP">
+0x400C</TD><TD VALIGN="TOP">Get all of the available PPDs.</TD></TR>
+<TR><TD VALIGN="TOP">CUPS-Move-Job</TD><TD VALIGN="TOP">1.1</TD><TD VALIGN="TOP">
+0x400D</TD><TD VALIGN="TOP">Move a job to a different printer.</TD></TR>
+</TABLE>
+</CENTER>
+</P>
+<H1><A NAME="4">4 Operations</A></H1>
+<P>The following sections describe the operations supported by CUPS. In
+the interest of brevity, operations which use only the standard IPP
+attributes are not described. </P>
+<H2><A NAME="4_1">4.1 Print-Job Operation</A></H2>
+<P>The Print-Job operation (0x0002) prints a file. </P>
+<H3><A NAME="4_1_1">4.1.1 Print-Job Request</A></H3>
+<P>The following groups of attributes are supplied as part of the
+Print-Job request: </P>
+<P>Group 1: Operation Attributes </P>
+<UL>
+<P>Natural Language and Character Set: </P>
+<P>The &quot;attributes-charset&quot; and &quot;attributes-natural-language&quot;
+ attributes as described in section 3.1.4.1 of the IPP Model and
+ Semantics document. </P>
+<P>&quot;printer-uri&quot; (uri): </P>
+<P>The client MUST supply a URI for the specified printer. </P>
+</UL>
+<P>Group 2: Job Template Attributes </P>
+<UL>
+<P>&quot;job-billing&quot; (name(MAX)): </P>
+<P><I>(CUPS 1.1 and higher)</I></P>
+<P>The client OPTIONALLY supplies a billing string that is logged with
+the page accounting information. </P>
+<P>&quot;job-sheets&quot; (1setof type3 keyword | name(MAX)): </P>
+<P><I>(CUPS 1.1 and higher)</I></P>
+<P>The client OPTIONALLY supplies one or two banner pages that are
+printed before and after any files in the print job. The name of
+&quot;none&quot; is reserved to indicate that no banner page should be printed.
+If the client does not specify this attribute then the value of the
+&quot;job-sheets-default&quot; printer object attribute is used. </P>
+<P><B>Note:</B> Standard IPP only allows specification of a single
+ job-sheets attribute value. </P>
+<P>&quot;media&quot; (1setof type3 keyword | name(MAX)): </P>
+<P>The client OPTIONALLY supplies one or more media attributes
+ specifying the size, type, source, and color of the output media. If
+the client does not specify this attribute then the value of the
+&quot;media-default&quot; printer object attribute is used. </P>
+<P><B>Note:</B> Standard IPP only allows specification of a single
+ media attribute value. </P>
+<P>Other Job Template Attributes </P>
+</UL>
+<P>The Print-Job request is followed by a file to be printed. </P>
+<H3><A NAME="4_1_2">4.1.2 Print-Job Response</A></H3>
+<P>The following groups of attributes are send as part of the Print-Job
+Response: </P>
+<P>Group 1: Operation Attributes </P>
+<UL>
+<P>Status Message: </P>
+<P>The standard response status message. </P>
+<P>Natural Language and Character Set: </P>
+<P>The &quot;attributes-charset&quot; and &quot;attributes-natural-language&quot;
+ attributes as described in section 3.1.4.2 of the IPP Model and
+ Semantics document. </P>
+</UL>
+<P>Group 2: Job Attributes </P>
+<UL>
+<P>Standard Job Attributes </P>
+</UL>
+<H2><A NAME="4_2">4.2 Create-Job Operation</A></H2>
+<P>The Create-Job operation (0x0005) creates a new, empty print job. </P>
+<H3><A NAME="4_2_1">4.2.1 Create-Job Request</A></H3>
+<P>The following groups of attributes are supplied as part of the
+Create-Job request: </P>
+<P>Group 1: Operation Attributes </P>
+<UL>
+<P>Natural Language and Character Set: </P>
+<P>The &quot;attributes-charset&quot; and &quot;attributes-natural-language&quot;
+ attributes as described in section 3.1.4.1 of the IPP Model and
+ Semantics document. </P>
+<P>&quot;printer-uri&quot; (uri): </P>
+<P>The client MUST supply a URI for the specified printer. </P>
+</UL>
+<P>Group 2: Job Template Attributes </P>
+<UL>
+<P>&quot;job-billing&quot; (name(MAX)): </P>
+<P><I>(CUPS 1.1 and higher)</I></P>
+<P>The client OPTIONALLY supplies a billing string that is logged with
+the page accounting information. </P>
+<P>&quot;job-sheets&quot; (1setof type3 keyword | name(MAX)): </P>
+<P><I>(CUPS 1.1 and higher)</I></P>
+<P>The client OPTIONALLY supplies one or two banner pages that are
+printed before and after any files in the print job. The name of
+&quot;none&quot; is reserved to indicate that no banner page should be printed.
+If the client does not specify this attribute then the value of the
+&quot;job-sheets-default&quot; printer object attribute is used. </P>
+<P><B>Note:</B> Standard IPP only allows specification of a single
+ job-sheets attribute value. </P>
+<P>&quot;media&quot; (1setof type3 keyword | name(MAX)): </P>
+<P>The client OPTIONALLY supplies one or more media attributes
+ specifying the size, type, source, and color of the output media. If
+the client does not specify this attribute then the value of the
+&quot;media-default&quot; printer object attribute is used. </P>
+<P><B>Note:</B> Standard IPP only allows specification of a single
+ media attribute value. </P>
+<P>Standard Job Template Attributes </P>
+</UL>
+<H3><A NAME="4_2_2">4.2.2 Create-Job Response</A></H3>
+<P>The following groups of attributes are send as part of the
+Create-Job Response: </P>
+<P>Group 1: Operation Attributes </P>
+<UL>
+<P>Status Message: </P>
+<P>The standard response status message. </P>
+<P>Natural Language and Character Set: </P>
+<P>The &quot;attributes-charset&quot; and &quot;attributes-natural-language&quot;
+ attributes as described in section 3.1.4.2 of the IPP Model and
+ Semantics document. </P>
+</UL>
+<P>Group 2: Job Attributes </P>
+<UL>
+<P>Standard Job Attributes </P>
+</UL>
+<H2><A NAME="4_3">4.3 Set-Job-Attributes Operation</A></H2>
+<P>The Set-Job-Attributes operation (0x0014) changes the attributes of
+an active (not completed) job. </P>
+<H3><A NAME="4_3_1">4.3.1 Set-Job-Attributes Request</A></H3>
+<P>The following groups of attributes are supplied as part of the
+Set-Job-Attributes request: </P>
+<P>Group 1: Operation Attributes </P>
+<UL>
+<P>Natural Language and Character Set: </P>
+<P>The &quot;attributes-charset&quot; and &quot;attributes-natural-language&quot;
+ attributes as described in section 3.1.4.1 of the IPP Model and
+ Semantics document. </P>
+<P>&quot;printer-uri&quot; (uri) and &quot;job-id&quot; (integer) </P>
+<P><I>OR</I></P>
+<P>&quot;job-uri&quot;: </P>
+<P>The client MUST supply a URI for the specified printer and a job ID
+number, or the job URI. </P>
+</UL>
+<P>Group 2: Job Template Attributes </P>
+<UL>
+<P>&quot;job-sheets&quot; (1setof type3 keyword | name(MAX)): </P>
+<P><I>(CUPS 1.1 and higher)</I></P>
+<P>The client OPTIONALLY supplies one or two banner pages that are
+printed before and after any files in the print job. The name of
+&quot;none&quot; is reserved to indicate that no banner page should be printed.
+If the client does not specify this attribute then the value of the
+&quot;job-sheets-default&quot; printer object attribute is used. </P>
+<P><B>Note:</B> Standard IPP only allows specification of a single
+ job-sheets attribute value. </P>
+<P>&quot;media&quot; (1setof type3 keyword | name(MAX)): </P>
+<P>The client OPTIONALLY supplies one or more media attributes
+ specifying the size, type, source, and color of the output media. If
+the client does not specify this attribute then the value of the
+&quot;media-default&quot; printer object attribute is used. </P>
+<P><B>Note:</B> Standard IPP only allows specification of a single
+ media attribute value. </P>
+<P>Other Job Template Attributes </P>
+</UL>
+<H3><A NAME="4_3_2">4.3.2 Set-Job-Attributes Response</A></H3>
+<P>The following groups of attributes are send as part of the
+Set-Job-Attributes Response: </P>
+<P>Group 1: Operation Attributes </P>
+<UL>
+<P>Status Message: </P>
+<P>The standard response status message. </P>
+<P>Natural Language and Character Set: </P>
+<P>The &quot;attributes-charset&quot; and &quot;attributes-natural-language&quot;
+ attributes as described in section 3.1.4.2 of the IPP Model and
+ Semantics document. </P>
+</UL>
+<H2><A NAME="4_4">4.4 CUPS-Get-Default Operation</A></H2>
+<P>The CUPS-Get-Default operation (0x4001) returns the default printer
+URI and attributes. </P>
+<H3><A NAME="4_4_1">4.4.1 CUPS-Get-Default Request</A></H3>
+<P>The following groups of attributes are supplied as part of the
+CUPS-Get-Default request: </P>
+<P>Group 1: Operation Attributes </P>
+<UL>
+<P>Natural Language and Character Set: </P>
+<P>The &quot;attributes-charset&quot; and &quot;attributes-natural-language&quot;
+ attributes as described in section 3.1.4.1 of the IPP Model and
+ Semantics document. </P>
+<P>&quot;requested-attributes&quot; (1setOf keyword) : </P>
+<P>The client OPTIONALLY supplies a set of attribute names and/or
+attribute group names in whose values the requester is interested. If
+the client omits this attribute, the server responds as if this
+attribute had been supplied with a value of 'all'. </P>
+</UL>
+<H3><A NAME="4_4_2">4.4.2 CUPS-Get-Default Response</A></H3>
+<P>The following groups of attributes are send as part of the
+CUPS-Get-Default Response: </P>
+<P>Group 1: Operation Attributes </P>
+<UL>
+<P>Status Message: </P>
+<P>The standard response status message. </P>
+<P>Natural Language and Character Set: </P>
+<P>The &quot;attributes-charset&quot; and &quot;attributes-natural-language&quot;
+ attributes as described in section 3.1.4.2 of the IPP Model and
+ Semantics document. </P>
+</UL>
+<P>Group 2: Printer Object Attributes </P>
+<UL>
+<P>The set of requested attributes and their current values. </P>
+</UL>
+<H2><A NAME="4_5">4.5 CUPS-Get-Printers Operation</A></H2>
+<P>The CUPS-Get-Printers operation (0x4002) returns the printer
+attributes for every printer known to the system. This may include
+printers that are not served directly by the server. </P>
+<H3><A NAME="4_5_1">4.5.1 CUPS-Get-Printers Request</A></H3>
+<P>The following groups of attributes are supplied as part of the
+CUPS-Get-Printers request: </P>
+<P>Group 1: Operation Attributes </P>
+<UL>
+<P>Natural Language and Character Set: </P>
+<P>The &quot;attributes-charset&quot; and &quot;attributes-natural-language&quot;
+ attributes as described in section 3.1.4.1 of the IPP Model and
+ Semantics document. </P>
+<P>&quot;limit&quot; (integer (1:MAX)): </P>
+<P>The client OPTIONALLY supplies this attribute limiting the number
+of printers that are returned. </P>
+<P>&quot;printer-info&quot; (text(127)): </P>
+<P><I>(CUPS 1.1 and higher)</I></P>
+<P>The client OPTIONALLY supplies this attribute to select which
+printers are returned. </P>
+<P>&quot;printer-location&quot; (text(127)): </P>
+<P><I>(CUPS 1.1 and higher)</I></P>
+<P>The client OPTIONALLY supplies this attribute to select which
+printers are returned. </P>
+<P>&quot;printer-type&quot; (type2 enum): </P>
+<P><I>(CUPS 1.1 and higher)</I></P>
+<P>The client OPTIONALLY supplies a printer type enumeration to select
+which printers are returned. </P>
+<P>&quot;printer-type-mask&quot; (type2 enum): </P>
+<P><I>(CUPS 1.1 and higher)</I></P>
+<P>The client OPTIONALLY supplies a printer type mask enumeration to
+select which bits are used in the &quot;printer-type&quot; attribute. </P>
+<P>&quot;requested-attributes&quot; (1setOf keyword) : </P>
+<P>The client OPTIONALLY supplies a set of attribute names and/or
+attribute group names in whose values the requester is interested. If
+the client omits this attribute, the server responds as if this
+attribute had been supplied with a value of 'all'. </P>
+</UL>
+<H3><A NAME="4_5_2">4.5.2 CUPS-Get-Printers Response</A></H3>
+<P>The following groups of attributes are send as part of the
+CUPS-Get-Printers Response: </P>
+<P>Group 1: Operation Attributes </P>
+<UL>
+<P>Status Message: </P>
+<P>The standard response status message. </P>
+<P>Natural Language and Character Set: </P>
+<P>The &quot;attributes-charset&quot; and &quot;attributes-natural-language&quot;
+ attributes as described in section 3.1.4.2 of the IPP Model and
+ Semantics document. </P>
+</UL>
+<P>Group 2: Printer Object Attributes </P>
+<UL>
+<P>The set of requested attributes and their current values for each
+printer. </P>
+</UL>
+<H2><A NAME="4_6">4.6 CUPS-Add-Printer Operation</A></H2>
+<P>The CUPS-Add-Printer operation (0x4003) adds a new printer or
+modifies an existing printer on the system. </P>
+<H3><A NAME="4_6_1">4.6.1 CUPS-Add-Printer Request</A></H3>
+<P>The following groups of attributes are supplied as part of the
+CUPS-Add-Printer request: </P>
+<P>Group 1: Operation Attributes </P>
+<UL>
+<P>Natural Language and Character Set: </P>
+<P>The &quot;attributes-charset&quot; and &quot;attributes-natural-language&quot;
+ attributes as described in section 3.1.4.1 of the IPP Model and
+ Semantics document. </P>
+<P>&quot;printer-uri&quot; (uri): </P>
+<P>The client MUST supply a URI for the specified printer. </P>
+</UL>
+<P>Group 2: Printer Object Attributes </P>
+<UL>
+<P>&quot;banner-end-default&quot; (name(127)): </P>
+<P><I>(CUPS 1.1 and higher)</I></P>
+<P>The client OPTIONALLY supplies a banner page name that is printed
+after files in a job. The reserved name &quot;none&quot; is used to specify that
+no banner page should be printed. </P>
+<P>&quot;banner-start-default&quot; (name(127)): </P>
+<P><I>(CUPS 1.1 and higher)</I></P>
+<P>The client OPTIONALLY supplies a banner page name that is printed
+before files in a job. The reserved name &quot;none&quot; is used to specify
+that no banner page should be printed. </P>
+<P>&quot;device-uri&quot; (uri): </P>
+<P>The client OPTIONALLY supplies a device URI for the specified
+printer. </P>
+<P>&quot;ppd-name&quot; (name(127)): </P>
+<P>The client OPTIONALLY supplies a PPD name for the specified
+ printer. </P>
+<P>&quot;printer-is-accepting-jobs&quot; (boolean): </P>
+<P>The client OPTIONALLY supplies this boolean attribute indicating
+whether or not the printer object should accept new jobs. </P>
+<P>&quot;printer-info&quot; (text(127)): </P>
+<P>The client OPTIONALLY supplies this attribute indicating the
+ printer information string. </P>
+<P>&quot;printer-location&quot; (text(127)): </P>
+<P>The client OPTIONALLY supplies this attribute indicating a textual
+location of the printer. </P>
+<P>&quot;printer-more-info&quot; (uri): </P>
+<P>The client OPTIONALLY supplies this attribute indicating a URI for
+additional printer information. </P>
+<P>&quot;printer-state&quot; (type2 enum): </P>
+<P>The client OPTIONALLY supplies this attribute indicating the
+ initial/current state of the printer. Only the &quot;idle&quot; and &quot;stopped&quot;
+ enumerations are recognized. </P>
+<P>&quot;printer-state-message&quot; (text(MAX)): </P>
+<P>The client OPTIONALLY supplies this attribute indicating a textual
+reason for the current printer state. </P>
+<P>&quot;requesting-user-name-allowed&quot; (1setof name(127)) </P>
+<P><I>OR</I></P>
+<P>&quot;requesting-user-name-denied&quot; (1setof name(127)): </P>
+<P>The client OPTIONALLY supplies one of these attributes to specify
+an access control list for incoming print jobs. The special name
+&quot;ALLUSERS&quot; is reserved to indicate that all users are allowed or
+denied. </P>
+</UL>
+<P>The CUPS-Add-Printer request can optionally be followed by a PPD
+file or System V interface script to be used for the printer. The
+&quot;ppd-name&quot; attribute overrides any file that is attached to the end of
+the request with a local CUPS PPD file. </P>
+<H3><A NAME="4_6_2">4.6.2 CUPS-Add-Printer Response</A></H3>
+<P>The following groups of attributes are send as part of the
+CUPS-Add-Printer Response: </P>
+<P>Group 1: Operation Attributes </P>
+<UL>
+<P>Status Message: </P>
+<P>The standard response status message. </P>
+<P>Natural Language and Character Set: </P>
+<P>The &quot;attributes-charset&quot; and &quot;attributes-natural-language&quot;
+ attributes as described in section 3.1.4.2 of the IPP Model and
+ Semantics document. </P>
+</UL>
+<H2><A NAME="4_7">4.7 CUPS-Delete-Printer Operation</A></H2>
+<P>The CUPS-Delete-Printer operation (0x4004) removes an existing
+printer from the system. </P>
+<H3><A NAME="4_7_1">4.7.1 CUPS-Delete-Printer Request</A></H3>
+<P>The following groups of attributes are supplied as part of the
+CUPS-Delete-Printer request: </P>
+<P>Group 1: Operation Attributes </P>
+<UL>
+<P>Natural Language and Character Set: </P>
+<P>The &quot;attributes-charset&quot; and &quot;attributes-natural-language&quot;
+ attributes as described in section 3.1.4.1 of the IPP Model and
+ Semantics document. </P>
+<P>&quot;printer-uri&quot; (uri): </P>
+<P>The client MUST supply a URI for the specified printer. </P>
+</UL>
+<H3><A NAME="4_7_2">4.7.2 CUPS-Delete-Printer Response</A></H3>
+<P>The following groups of attributes are send as part of the
+CUPS-Delete-Printer Response: </P>
+<P>Group 1: Operation Attributes </P>
+<UL>
+<P>Status Message: </P>
+<P>The standard response status message. </P>
+<P>Natural Language and Character Set: </P>
+<P>The &quot;attributes-charset&quot; and &quot;attributes-natural-language&quot;
+ attributes as described in section 3.1.4.2 of the IPP Model and
+ Semantics document. </P>
+</UL>
+<H2><A NAME="4_8">4.8 CUPS-Get-Classes Operation</A></H2>
+<P>The CUPS-Get-Classes operation (0x4005) returns the printer
+attributes for every printer class known to the system. This may
+include printer classes that are not served directly by the server. </P>
+<H3><A NAME="4_8_1">4.8.1 CUPS-Get-Classes Request</A></H3>
+<P>The following groups of attributes are supplied as part of the
+CUPS-Get-Classes request: </P>
+<P>Group 1: Operation Attributes </P>
+<UL>
+<P>Natural Language and Character Set: </P>
+<P>The &quot;attributes-charset&quot; and &quot;attributes-natural-language&quot;
+ attributes as described in section 3.1.4.1 of the IPP Model and
+ Semantics document. </P>
+<P>&quot;limit&quot; (integer (1:MAX)): </P>
+<P>The client OPTIONALLY supplies this attribute limiting the number
+of printer classes that are returned. </P>
+<P>&quot;printer-info&quot; (text(127)): </P>
+<P><I>(CUPS 1.1 and higher)</I></P>
+<P>The client OPTIONALLY supplies this attribute to select which
+printer classes are returned. </P>
+<P>&quot;printer-location&quot; (text(127)): </P>
+<P><I>(CUPS 1.1 and higher)</I></P>
+<P>The client OPTIONALLY supplies this attribute to select which
+printer classes are returned. </P>
+<P>&quot;printer-type&quot; (type2 enum): </P>
+<P><I>(CUPS 1.1 and higher)</I></P>
+<P>The client OPTIONALLY supplies a printer type enumeration to select
+which printer classes are returned. </P>
+<P>&quot;printer-type-mask&quot; (type2 enum): </P>
+<P><I>(CUPS 1.1 and higher)</I></P>
+<P>The client OPTIONALLY supplies a printer type mask enumeration to
+select which bits are used in the &quot;printer-type&quot; attribute. </P>
+<P>&quot;requested-attributes&quot; (1setOf keyword) : </P>
+<P>The client OPTIONALLY supplies a set of attribute names and/or
+attribute group names in whose values the requester is interested. If
+the client omits this attribute, the server responds as if this
+attribute had been supplied with a value of 'all'. </P>
+</UL>
+<H3><A NAME="4_8_2">4.8.2 CUPS-Get-Classes Response</A></H3>
+<P>The following groups of attributes are send as part of the
+CUPS-Get-Classes Response: </P>
+<P>Group 1: Operation Attributes </P>
+<UL>
+<P>Status Message: </P>
+<P>The standard response status message. </P>
+<P>Natural Language and Character Set: </P>
+<P>The &quot;attributes-charset&quot; and &quot;attributes-natural-language&quot;
+ attributes as described in section 3.1.4.2 of the IPP Model and
+ Semantics document. </P>
+</UL>
+<P>Group 2: Printer Class Object Attributes </P>
+<UL>
+<P>The set of requested attributes and their current values for each
+printer class. </P>
+</UL>
+<H2><A NAME="4_9">4.9 CUPS-Add-Class Operation</A></H2>
+<P>The CUPS-Add-Class operation (0x4006) adds a new printer class or
+modifies and existing printer class on the system. </P>
+<H3><A NAME="4_9_1">4.9.1 CUPS-Add-Class Request</A></H3>
+<P>The following groups of attributes are supplied as part of the
+CUPS-Add-Class request: </P>
+<P>Group 1: Operation Attributes </P>
+<UL>
+<P>Natural Language and Character Set: </P>
+<P>The &quot;attributes-charset&quot; and &quot;attributes-natural-language&quot;
+ attributes as described in section 3.1.4.1 of the IPP Model and
+ Semantics document. </P>
+<P>&quot;printer-uri&quot; (uri): </P>
+<P>The client MUST supply a URI for the specified printer class. </P>
+</UL>
+<P>Group 2: Printer Object Attributes </P>
+<UL>
+<P>&quot;member-uris&quot; (1setof uri): </P>
+<P>The client OPTIONALLY supplies the &quot;member-uris&quot; set specifying the
+printers and printer classes that are part of the class. </P>
+<P>&quot;printer-is-accepting-jobs&quot; (boolean): </P>
+<P>The client OPTIONALLY supplies this boolean attribute indicating
+whether or not the printer object should accept new jobs. </P>
+<P>&quot;printer-info&quot; (text(127)): </P>
+<P>The client OPTIONALLY supplies this attribute indicating the
+ printer information string. </P>
+<P>&quot;printer-location&quot; (text(127)): </P>
+<P>The client OPTIONALLY supplies this attribute indicating a textual
+location of the printer. </P>
+<P>&quot;printer-more-info&quot; (uri): </P>
+<P>The client OPTIONALLY supplies this attribute indicating a URI for
+additional printer information. </P>
+<P>&quot;printer-state&quot; (type2 enum): </P>
+<P>The client OPTIONALLY supplies this attribute indicating the
+ initial/current state of the printer. Only the &quot;idle&quot; and &quot;stopped&quot;
+ enumerations are recognized. </P>
+<P>&quot;printer-state-message&quot; (text(MAX)): </P>
+<P>The client OPTIONALLY supplies this attribute indicating a textual
+reason for the current printer state. </P>
+<P>&quot;requesting-user-name-allowed&quot; (1setof name(127)) </P>
+<P><I>OR</I></P>
+<P>&quot;requesting-user-name-denied&quot; (1setof name(127)): </P>
+<P>The client OPTIONALLY supplies one of these attributes to specify
+an access control list for incoming print jobs. The special name
+&quot;ALLUSERS&quot; is reserved to indicate that all users are allowed or
+denied. </P>
+</UL>
+<H3><A NAME="4_9_2">4.9.2 CUPS-Add-Class Response</A></H3>
+<P>The following groups of attributes are send as part of the
+CUPS-Add-Class Response: </P>
+<P>Group 1: Operation Attributes </P>
+<UL>
+<P>Status Message: </P>
+<P>The standard response status message. </P>
+<P>Natural Language and Character Set: </P>
+<P>The &quot;attributes-charset&quot; and &quot;attributes-natural-language&quot;
+ attributes as described in section 3.1.4.2 of the IPP Model and
+ Semantics document. </P>
+</UL>
+<H2><A NAME="4_10">4.10 CUPS-Delete-Class Operation</A></H2>
+<P>The CUPS-Delete-Class operation (0x4007) removes an existing printer
+class from the system. </P>
+<H3><A NAME="4_10_1">4.10.1 CUPS-Delete-Class Request</A></H3>
+<P>The following groups of attributes are supplied as part of the
+CUPS-Delete-Class request: </P>
+<P>Group 1: Operation Attributes </P>
+<UL>
+<P>Natural Language and Character Set: </P>
+<P>The &quot;attributes-charset&quot; and &quot;attributes-natural-language&quot;
+ attributes as described in section 3.1.4.1 of the IPP Model and
+ Semantics document. </P>
+<P>&quot;printer-uri&quot; (uri): </P>
+<P>The client MUST supply a URI for the specified printer class. </P>
+</UL>
+<H3><A NAME="4_10_2">4.10.2 CUPS-Delete-Class Response</A></H3>
+<P>The following groups of attributes are send as part of the
+CUPS-Delete-Class Response: </P>
+<P>Group 1: Operation Attributes </P>
+<UL>
+<P>Status Message: </P>
+<P>The standard response status message. </P>
+<P>Natural Language and Character Set: </P>
+<P>The &quot;attributes-charset&quot; and &quot;attributes-natural-language&quot;
+ attributes as described in section 3.1.4.2 of the IPP Model and
+ Semantics document. </P>
+</UL>
+<H2><A NAME="4_11">4.11 CUPS-Accept-Jobs Operation</A></H2>
+<P>The CUPS-Accept-Jobs operation (0x4008) sets the
+&quot;printer-is-accepting-jobs&quot; attribute to true for the specified printer
+or printer class. </P>
+<H3><A NAME="4_11_1">4.11.1 CUPS-Accept-Jobs Request</A></H3>
+<P>The following groups of attributes are supplied as part of the
+CUPS-Accept-Jobs request: </P>
+<P>Group 1: Operation Attributes </P>
+<UL>
+<P>Natural Language and Character Set: </P>
+<P>The &quot;attributes-charset&quot; and &quot;attributes-natural-language&quot;
+ attributes as described in section 3.1.4.1 of the IPP Model and
+ Semantics document. </P>
+<P>&quot;printer-uri&quot; (uri): </P>
+<P>The client MUST supply a URI for the specified printer or printer
+class. </P>
+</UL>
+<H3><A NAME="4_11_2">4.11.2 CUPS-Accept-Jobs Response</A></H3>
+<P>The following groups of attributes are send as part of the
+CUPS-Accept-Jobs Response: </P>
+<P>Group 1: Operation Attributes </P>
+<UL>
+<P>Status Message: </P>
+<P>The standard response status message. </P>
+<P>Natural Language and Character Set: </P>
+<P>The &quot;attributes-charset&quot; and &quot;attributes-natural-language&quot;
+ attributes as described in section 3.1.4.2 of the IPP Model and
+ Semantics document. </P>
+</UL>
+<H2><A NAME="4_12">4.12 CUPS-Reject-Jobs Operation</A></H2>
+<P>The CUPS-Reject-Jobs operation (0x4009) sets
+the&quot;printer-is-accepting-jobs&quot; attribute to false for the specified
+printer or printer class. </P>
+<H3><A NAME="4_12_1">4.12.1 CUPS-Reject-Jobs Request</A></H3>
+<P>The following groups of attributes are supplied as part of the
+CUPS-Reject-Jobs request: </P>
+<P>Group 1: Operation Attributes </P>
+<UL>
+<P>Natural Language and Character Set: </P>
+<P>The &quot;attributes-charset&quot; and &quot;attributes-natural-language&quot;
+ attributes as described in section 3.1.4.1 of the IPP Model and
+ Semantics document. </P>
+<P>&quot;printer-uri&quot; (uri): </P>
+<P>The client MUST supply a URI for the specified printer or printer
+class. </P>
+</UL>
+<P>Group 2: Printer Object Attributes </P>
+<UL>
+<P>&quot;printer-state-message&quot; (text(MAX)): </P>
+<P>The client OPTIONALLY supplies this attribute indicating a textual
+reason for the current printer state. </P>
+</UL>
+<H3><A NAME="4_12_2">4.12.2 CUPS-Reject-Jobs Response</A></H3>
+<P>The following groups of attributes are send as part of the
+CUPS-Reject-Jobs Response: </P>
+<P>Group 1: Operation Attributes </P>
+<UL>
+<P>Status Message: </P>
+<P>The standard response status message. </P>
+<P>Natural Language and Character Set: </P>
+<P>The &quot;attributes-charset&quot; and &quot;attributes-natural-language&quot;
+ attributes as described in section 3.1.4.2 of the IPP Model and
+ Semantics document. </P>
+</UL>
+<H2><A NAME="4_13">4.13 CUPS-Set-Default Operation</A></H2>
+<P>The CUPS-Set-Default operation (0x400A) sets the default printer
+destination for all clients when a resource name of &quot;/printers&quot; is
+specified. </P>
+<H3><A NAME="4_13_1">4.13.1 CUPS-Set-Default Request</A></H3>
+<P>The following groups of attributes are supplied as part of the
+CUPS-Set-Default request: </P>
+<P>Group 1: Operation Attributes </P>
+<UL>
+<P>Natural Language and Character Set: </P>
+<P>The &quot;attributes-charset&quot; and &quot;attributes-natural-language&quot;
+ attributes as described in section 3.1.4.1 of the IPP Model and
+ Semantics document. </P>
+<P>&quot;printer-uri&quot; (uri): </P>
+<P>The client MUST supply a URI for the specified printer or printer
+class. </P>
+</UL>
+<H3><A NAME="4_13_2">4.13.2 CUPS-Set-Default Response</A></H3>
+<P>The following groups of attributes are send as part of the
+CUPS-Set-Default Response: </P>
+<P>Group 1: Operation Attributes </P>
+<UL>
+<P>Status Message: </P>
+<P>The standard response status message. </P>
+<P>Natural Language and Character Set: </P>
+<P>The &quot;attributes-charset&quot; and &quot;attributes-natural-language&quot;
+ attributes as described in section 3.1.4.2 of the IPP Model and
+ Semantics document. </P>
+</UL>
+<H2><A NAME="4_14">4.14 CUPS-Get-Devices Operation</A></H2>
+<P>The CUPS-Get-Devices operation (0x400B) returns all of the supported
+device-uri's for the server (CUPS 1.1 and higher). </P>
+<H3><A NAME="4_14_1">4.14.1 CUPS-Get-Devices Request</A></H3>
+<P>The following groups of attributes are supplied as part of the
+CUPS-Get-Devices request: </P>
+<P>Group 1: Operation Attributes </P>
+<UL>
+<P>Natural Language and Character Set: </P>
+<P>The &quot;attributes-charset&quot; and &quot;attributes-natural-language&quot;
+ attributes as described in section 3.1.4.1 of the IPP Model and
+ Semantics document. </P>
+<P>&quot;device-class&quot; (type1 keyword): </P>
+<P>The client OPTIONALLY supplies a device class keyword to select
+ which devices are returned. </P>
+<P>&quot;limit&quot; (integer (1:MAX)): </P>
+<P>The client OPTIONALLY supplies this attribute limiting the number of
+ devices that are returned. </P>
+<P>&quot;requested-attributes&quot; (1setOf keyword) : </P>
+<P>The client OPTIONALLY supplies a set of attribute names and/or
+ attribute group names in whose values the requester is interested. If
+ the client omits this attribute, the server responds as if this
+ attribute had been supplied with a value of 'all'. </P>
+</UL>
+<H3><A NAME="4_14_2">4.14.2 CUPS-Get-Devices Response</A></H3>
+<P>The following groups of attributes are send as part of the
+CUPS-Get-Devices Response: </P>
+<P>Group 1: Operation Attributes </P>
+<UL>
+<P>Status Message: </P>
+<P>The standard response status message. </P>
+<P>Natural Language and Character Set: </P>
+<P>The &quot;attributes-charset&quot; and &quot;attributes-natural-language&quot;
+ attributes as described in section 3.1.4.2 of the IPP Model and
+ Semantics document. </P>
+</UL>
+<P>Group 2: Device Object Attributes </P>
+<UL>
+<P>The set of requested attributes and their current values for each
+device. </P>
+</UL>
+<H2><A NAME="4_15">4.15 CUPS-Get-PPDs Operation</A></H2>
+<P>The CUPS-Get-PPDs operation (0x400C) returns all of the locally
+available PPD files on the system (CUPS 1.1 and higher). </P>
+<H3><A NAME="4_15_1">4.15.1 CUPS-Get-PPDs Request</A></H3>
+<P>The following groups of attributes are supplied as part of the
+CUPS-Get-PPDs request: </P>
+<P>Group 1: Operation Attributes </P>
+<UL>
+<P>Natural Language and Character Set: </P>
+<P>The &quot;attributes-charset&quot; and &quot;attributes-natural-language&quot;
+ attributes as described in section 3.1.4.1 of the IPP Model and
+ Semantics document. </P>
+<P>&quot;limit&quot; (integer (1:MAX)): </P>
+<P>The client OPTIONALLY supplies this attribute limiting the number of
+ PPDs that are returned. </P>
+<P>&quot;ppd-make&quot; (text(127)): </P>
+<P>The client OPTIONALLY supplies a printer manufacturer to select
+ which PPDs are returned. </P>
+<P>&quot;requested-attributes&quot; (1setOf keyword) : </P>
+<P>The client OPTIONALLY supplies a set of attribute names and/or
+ attribute group names in whose values the requester is interested. If
+ the client omits this attribute, the server responds as if this
+ attribute had been supplied with a value of 'all'. </P>
+</UL>
+<H3><A NAME="4_15_2">4.15.2 CUPS-Get-PPDs Response</A></H3>
+<P>The following groups of attributes are send as part of the
+CUPS-Get-PPDs Response: </P>
+<P>Group 1: Operation Attributes </P>
+<UL>
+<P>Status Message: </P>
+<P>The standard response status message. </P>
+<P>Natural Language and Character Set: </P>
+<P>The &quot;attributes-charset&quot; and &quot;attributes-natural-language&quot;
+ attributes as described in section 3.1.4.2 of the IPP Model and
+ Semantics document. </P>
+</UL>
+<P>Group 2: PPD Attributes </P>
+<UL>
+<P>The set of requested attributes and their current values for each
+ PPD file. </P>
+</UL>
+<H2><A NAME="4_16">4.16 CUPS-Move-Job Operation</A></H2>
+<P>The CUPS-Move-Job operation (0x400D) moves an active print job to a
+different printer (CUPS 1.1 and higher). </P>
+<H3><A NAME="4_16_1">4.16.1 CUPS-Move-Job Request</A></H3>
+<P>The following groups of attributes are supplied as part of the
+CUPS-Move-Job request: </P>
+<P>Group 1: Operation Attributes </P>
+<UL>
+<P>Natural Language and Character Set: </P>
+<P>The &quot;attributes-charset&quot; and &quot;attributes-natural-language&quot;
+ attributes as described in section 3.1.4.1 of the IPP Model and
+ Semantics document. </P>
+<P>&quot;printer-uri&quot; (uri) and &quot;job-id&quot; (integer) </P>
+<P><I>OR</I></P>
+<P>&quot;job-uri&quot;: </P>
+<P>The client MUST supply a URI for the specified printer and a job ID
+number, or the job URI. </P>
+</UL>
+<H3><A NAME="4_16_2">4.16.2 CUPS-Move-Job Response</A></H3>
+<P>The following groups of attributes are send as part of the
+CUPS-Move-Job Response: </P>
+<P>Group 1: Operation Attributes </P>
+<UL>
+<P>Status Message: </P>
+<P>The standard response status message. </P>
+<P>Natural Language and Character Set: </P>
+<P>The &quot;attributes-charset&quot; and &quot;attributes-natural-language&quot;
+ attributes as described in section 3.1.4.2 of the IPP Model and
+ Semantics document. </P>
+</UL>
+<H1><A NAME="5">5 Attributes</A></H1>
+<P>CUPS provides many extension attributes to support multiple devices,
+PPD files, standard job filters, printers, and printer classes. </P>
+<H2><A NAME="5_1">5.1 Device Attributes</A></H2>
+<P>Device attributes are returned by the CUPS-Get-Devices operation and
+enumerate all of the available hardware devices and network protocols
+that are supported by the server. </P>
+<H3><A NAME="5_1_1">5.1.1 device-class (type2 keyword)</A></H3>
+<P>The device-class attribute specifies the class of device and can be
+one of the following: </P>
+<UL>
+<LI>&quot;file&quot; - a disk file. </LI>
+<LI>&quot;direct&quot; - a parallel or fixed-rate serial data port, currently
+used for Centronics, IEEE-1284, and USB printer ports. </LI>
+<LI>&quot;serial&quot; - a variable-rate serial port. </LI>
+<LI>&quot;network&quot; - a network connection, typically via AppSocket, HTTP,
+IPP, LPD, or SMB/CIFS protocols. </LI>
+</UL>
+<H3><A NAME="5_1_2">5.1.2 device-info (text(127))</A></H3>
+<P>The device-info attribute specifies a human-readable string
+describing the device, e.g. &quot;Parallel Port #1&quot;. </P>
+<H3><A NAME="5_1_3">5.1.3 device-make-and-model (text(127))</A></H3>
+<P>The device-makr-and-model attribute specifies a device
+identification string provided by the printer connected to the device.
+If the device or printer does not support identification then this
+attribute contains the string &quot;unknown&quot;. </P>
+<H3><A NAME="5_1_4">5.1.4 device-uri (uri)</A></H3>
+<P>The device-uri attribute specifies a unique identifier for the
+device. The actual format of the device-uri string depends on the value
+of the device-class attribute: </P>
+<UL>
+<LI>&quot;file&quot; - The device-uri will be of the form
+ &quot;file:/path/to/filename&quot;. </LI>
+<LI>&quot;direct&quot; - The device-uri will be of the form
+ &quot;method:/dev/filename&quot;, where method may be &quot;parallel&quot; or &quot;usb&quot; in
+the current implementation. </LI>
+<LI>&quot;serial&quot; - The device-uri will be of the form
+ &quot;serial:/dev/filename?baud=value+parity=value+flow=value&quot;. The parity
+value can be one of &quot;none&quot;, &quot;even&quot;, or &quot;odd&quot;. The flow value can be
+one of &quot;none&quot;, &quot;soft&quot; (XON/XOFF handshaking), or &quot;hard&quot; (CTS/RTS
+handshaking). </LI>
+<P>The URI returned by CUPS-Get-Devices will contain the maximum baud
+rate supported by the device and the best type of flow control
+available (&quot;soft&quot; or &quot;hard&quot;). </P>
+<LI>&quot;network&quot; - The device-uri will be of the form
+ &quot;method://[username:password@]hostname[:port]/[resource]&quot;, where
+method may be &quot;http&quot;, &quot;ipp&quot;, &quot;lpd&quot;, &quot;smb&quot;, or &quot;socket&quot; in the current
+implementation. </LI>
+<P>The URI returned by CUPS-Get-Devices will only contain the method
+name followed by two slashes (&quot;method://&quot;). It is up to the client
+application to add the appropriate host and other information when
+adding a new printer. </P>
+<P>The URI returned by Get-Printer-Attributes and CUPS-Get-Printers
+has any username and password information stripped; the information is
+still stored and used by the server internally to perform any needed
+authentication. </P>
+</UL>
+<H2><A NAME="5_2">5.2 Job Template Attributes</A></H2>
+<H3><A NAME="5_2_1">5.2.1 blackplot (boolean)</A></H3>
+<P>The blackplot attribute specifies whether HP-GL/2 plot files should
+be rendered entirely in black ink (blackplot=true) or using the colors
+and shades specified in the file (blackplot=false). The default value
+is false. </P>
+<H3><A NAME="5_2_2">5.2.2 brightness (integer(0:200))</A></H3>
+<P>The brightness attribute specifies the overall brightness of the
+printed output in percent. A brightness of 100 is normal, while 200 is
+twice as bright and 50 is half as bright. The default value is 100. </P>
+<P>Brightness is applied to the Cyan, Magenta, Yellow, and Black values
+using the function &quot;f(x) = brightness / 100 * x&quot;. </P>
+<H3><A NAME="5_2_3">5.2.3 columns (integer(1:4))</A></H3>
+<P>The columns attribute specifies the number of columns to generate
+when printing text files. The default value is 1. </P>
+<H3><A NAME="5_2_4">5.2.4 cpi (type2 enum)</A></H3>
+<P>The cpi attribute specifies the number of characters per inch when
+printing text files. Only the values 10, 12, and 17 are currently
+supported. The default value is 10. </P>
+<H3><A NAME="5_2_5">5.2.5 fitplot (boolean)</A></H3>
+<P>The fitplot attribute specifies whether to scale HP-GL/2 plot files
+to fit on the selected media (fitplot=true) or use the physical scale
+specified in the plot file (fitplot=false). The default value is false. </P>
+<H3><A NAME="5_2_6">5.2.6 gamma (integer(1:10000))</A></H3>
+<P>The gamma attribute specifies the luminance correction for the
+output. A value of 1000 specifies no correction, while values of 2000
+and 500 will generate lighter and darker output, respectively. The
+default value is 1000. </P>
+<P>Gamma is applied to the Red, Green, and Blue values (or luminance
+for grayscale output) using the function &quot;f(x) = x<SUP>(1000/gamma)</SUP>
+&quot;. </P>
+<H3><A NAME="5_2_7">5.2.7 hue (integer(-180:180))</A></H3>
+<P>The hue attribute specifies a color hue rotation when printing image
+files. The default value is 0. </P>
+<H3><A NAME="5_2_8">5.2.8 job-billing (name(MAX))</A></H3>
+<P><I>(CUPS 1.1 and higher)</I></P>
+<P>The job-billing attribute provides a name value to associate with a
+job for billing purposes. </P>
+<H3><A NAME="5_2_9">5.2.9 job-sheets (1setof type3 keyword | name(MAX))</A>
+</H3>
+<P><I>(CUPS 1.1 and higher)</I></P>
+<P>The job-sheets attribute specifies one or two banner files that are
+printed before and after a job. The reserved value of &quot;none&quot; disables
+banner printing. The default value is stored in the job-sheets-default
+attribute. </P>
+<P>If only one value is supplied, the banner file is printed before the
+job. If two values are supplied, the first value is used as the
+starting banner file and the second as the ending banner file. </P>
+<H3><A NAME="5_2_10">5.2.10 lpi (type2 enum)</A></H3>
+<P>The lpi attribute specifies the number of lines per inch when
+printing text files. Only the values 6 and 8 are currently supported.
+The default value is 6. </P>
+<H3><A NAME="5_2_11">5.2.11 page-bottom (integer(0:MAX))</A></H3>
+<P>The page-bottom attribute specifies the bottom margin in points (72
+points equals 1 inch). The default value is the device physical margin. </P>
+<H3><A NAME="5_2_12">5.2.12 page-left (integer(0:MAX))</A></H3>
+<P>The page-left attribute specifies the left margin in points (72
+points equals 1 inch). The default value is the device physical margin. </P>
+<H3><A NAME="5_2_13">5.2.13 page-right (integer(0:MAX))</A></H3>
+<P>The page-right attribute specifies the right margin in points (72
+points equals 1 inch). The default value is the device physical margin. </P>
+<H3><A NAME="5_2_14">5.2.14 page-set (type2 keyword)</A></H3>
+<P>The page-set attribute specifies which pages to print in a file. The
+supported keywords are &quot;all&quot;, &quot;even&quot;, and &quot;odd&quot;. The default value is
+&quot;all&quot;. </P>
+<H3><A NAME="5_2_15">5.2.15 page-top (integer(0:MAX))</A></H3>
+<P>The page-top attribute specifies the top margin in points (72 points
+equals 1 inch). The default value is the device physical margin. </P>
+<H3><A NAME="5_2_16">5.2.16 penwidth (integer(0:MAX))</A></H3>
+<P>The penwidth attribute specifies the default pen width in
+micrometers when printing HP-GL/2 plot files. The default value is 1000
+(1 millimeter). </P>
+<H3><A NAME="5_2_17">5.2.17 ppi (integer(1:MAX))</A></H3>
+<P>The ppi attribute specifies the resolution of an image file in
+pixels per inch. The default value is the resolution included with the
+file or 128 if no resolution information is available. </P>
+<H3><A NAME="5_2_18">5.2.18 prettyprint (boolean)</A></H3>
+<P>The prettyprint attribute specifies whether text files should be
+printed with a shaded header and keyword highlighting
+(prettyprint=true) or without additional formatting
+(prettyprint=false). The default value is false. </P>
+<H3><A NAME="5_2_19">5.2.19 saturation (integer(0:200))</A></H3>
+<P>The saturation attribute specifies the color saturation when
+printing image files. A saturation of 100 is normal, while values of 50
+and 200 will be half and twice as colorful, respectively. The default
+value is 100. </P>
+<H3><A NAME="5_2_20">5.2.20 scaling (integer(1:1000))</A></H3>
+<P>The scaling attribute specifies the scaling of image files with
+respect to the selected media. A value of 100 specifies that the image
+file should fit 100% of the page, or as much as possible given the
+image dimensions. The default value is unspecified. </P>
+<P>The scaling attribute overrides the ppi attribute if specified. </P>
+<H3><A NAME="5_2_21">5.2.21 wrap (boolean)</A></H3>
+<P>The wrap attribute specifies whether long lines should be wrapped
+(wrap=true) or not (wrap=false) when printing text files. The default
+value is true. </P>
+<H2><A NAME="5_3">5.3 PPD Attributes</A></H2>
+<H3><A NAME="5_3_1">5.3.1 ppd-natural-language (naturalLanguage)</A></H3>
+<P>The ppd-natural-language attribute specifies the language encoding
+of the PPD file (the LanguageVersion attribute in the PPD file). If the
+language is unknown or undefined then &quot;en&quot; (English) is assumed. </P>
+<H3><A NAME="5_3_2">5.3.2 ppd-make (text(127))</A></H3>
+<P>The ppd-make attribute specifies the manufacturer of the printer
+(the Manufacturer attribute in the PPD file). If the manufacturer is
+not specified in the PPD file then an educated guess is made using the
+NickName attribute in the PPD file. </P>
+<H3><A NAME="5_3_3">5.3.3 ppd-make-and-model (text(127))</A></H3>
+<P>The ppd-make-and-model attribute specifies the manufacturer and
+model name of the PPD file (the NickName attribute in the PPD file). If
+the make and model is not specified in the PPD file then the ModelName
+or ShortNickName attributes are used instead. </P>
+<H3><A NAME="5_3_4">5.3.4 ppd-name (name(255))</A></H3>
+<P>The ppd-name attribute specifies the PPD filename on the server
+relative to the model directory. The forward slash (/) is used to
+delineate directories. </P>
+<H2><A NAME="5_4">5.4 Printer Attributes</A></H2>
+<H3><A NAME="5_4_1">5.4.1 job-sheets-default (1setof type3 keyword |
+name(MAX))</A></H3>
+<P><I>(CUPS 1.1 and higher)</I></P>
+<P>The job-sheets-default attribute specifies the default banner
+file(s) to print before and after each job. The value &quot;none&quot; specifies
+that no banner should be printed. </P>
+<H3><A NAME="5_4_2">5.4.2 job-sheets-supported (1setof type3 keyword |
+name(MAX))</A></H3>
+<P><I>(CUPS 1.1 and higher)</I></P>
+<P>The job-sheets-supported attribute specifies the available banner
+files. There will always be at least one banner file available called
+&quot;none&quot;. </P>
+<H3><A NAME="5_4_3">5.4.3 printer-type (type2 enum)</A></H3>
+<P>The printer-type attribute specifies printer type and capability
+bits for the printer or class. The default value is computed from
+internal state information and the PPD file for the printer. The
+following bits are defined:
+<CENTER>
+<TABLE BORDER WIDTH="80%">
+<TR><TH>Bit</TH><TH>Description</TH></TR>
+<TR><TD VALIGN="TOP">0x00000001</TD><TD VALIGN="TOP">Is a printer class.</TD>
+</TR>
+<TR><TD VALIGN="TOP">0x00000002</TD><TD VALIGN="TOP">Is a remote
+destination.</TD></TR>
+<TR><TD VALIGN="TOP">0x00000004</TD><TD VALIGN="TOP">Can print in black.</TD>
+</TR>
+<TR><TD VALIGN="TOP">0x00000008</TD><TD VALIGN="TOP">Can print in color.</TD>
+</TR>
+<TR><TD VALIGN="TOP">0x00000010</TD><TD VALIGN="TOP">Can print on both
+sides of the page in hardware.</TD></TR>
+<TR><TD VALIGN="TOP">0x00000020</TD><TD VALIGN="TOP">Can staple output.</TD>
+</TR>
+<TR><TD VALIGN="TOP">0x00000040</TD><TD VALIGN="TOP">Can do fast copies
+in hardware.</TD></TR>
+<TR><TD VALIGN="TOP">0x00000080</TD><TD VALIGN="TOP">Can do fast copy
+collation in hardware.</TD></TR>
+<TR><TD VALIGN="TOP">0x00000100</TD><TD VALIGN="TOP">Can punch output.</TD>
+</TR>
+<TR><TD VALIGN="TOP">0x00000200</TD><TD VALIGN="TOP">Can cover output.</TD>
+</TR>
+<TR><TD VALIGN="TOP">0x00000400</TD><TD VALIGN="TOP">Can bind output.</TD>
+</TR>
+<TR><TD VALIGN="TOP">0x00000800</TD><TD VALIGN="TOP">Can sort output.</TD>
+</TR>
+<TR><TD VALIGN="TOP">0x00001000</TD><TD VALIGN="TOP">Can handle media
+up to US-Legal/A4.</TD></TR>
+<TR><TD VALIGN="TOP">0x00002000</TD><TD VALIGN="TOP">Can handle media
+from US-Legal/A4 to ISO-C/A2.</TD></TR>
+<TR><TD VALIGN="TOP">0x00004000</TD><TD VALIGN="TOP">Can handle media
+larger than ISO-C/A2.</TD></TR>
+<TR><TD VALIGN="TOP">0x00008000</TD><TD VALIGN="TOP">Can handle
+user-defined media sizes.</TD></TR>
+<TR><TD VALIGN="TOP">0x00010000</TD><TD VALIGN="TOP">Is an implicit
+(server-generated) class.</TD></TR>
+</TABLE>
+</CENTER>
+</P>
+<H3><A NAME="5_4_4">5.4.4 printer-type-mask (type2 enum)</A></H3>
+<P><I>(CUPS 1.1 and higher)</I></P>
+<P>The printer-type-mask attribute is used to choose printers or
+classes with the CUPS-Get-Printers and CUPS-Get-Classes operations. The
+bits are defined identically to the printer-type attribute and default
+to all 1's. </P>
+<H3><A NAME="5_4_5">5.4.5 requesting-user-name-allowed (1setof
+name(127))</A></H3>
+<P><I>(CUPS 1.1 and higher)</I></P>
+<P>The requesting-user-name-allowed attribute lists all of the users
+that are allowed to access a printer or class. Either this attribute or
+the requesting-user-name-denied attribute will be defined, but not
+both. </P>
+<P>The special name &quot;ALLUSERS&quot; is reserved to indicate that all users
+are allowed. </P>
+<H3><A NAME="5_4_6">5.4.6 requesting-user-name-denied (1setof name(127))</A>
+</H3>
+<P><I>(CUPS 1.1 and higher)</I></P>
+<P>The requesting-user-name-denied attribute lists all of the users
+that are not allowed to access a printer or class. Either this
+attribute or the requesting-user-name-allowed attribute will be
+defined, but not both. </P>
+<P>The special name &quot;ALLUSERS&quot; is reserved to indicate that all users
+are denied. </P>
+<H2><A NAME="5_5">5.5 Printer Class Attributes</A></H2>
+<H3><A NAME="5_5_1">5.5.1 member-names (1setof name(127))</A></H3>
+<P>The member-names attribute specifies each of the printer-name
+attributes of the member printers and classes. Each name corresponds to
+the same element of the member-uris attribute. </P>
+<H3><A NAME="5_5_2">5.5.2 member-uris (1setof uri)</A></H3>
+<P>The member-uris attribute specifies each of the printer-uri
+attributes of the member printers and classes. Each URI corresponds to
+the same element of the member-names attribute. </P>
+</BODY>
+</HTML>
diff --git a/doc/ipp.pdf b/doc/ipp.pdf
new file mode 100644
index 000000000..8d39e6945
--- /dev/null
+++ b/doc/ipp.pdf
@@ -0,0 +1,1380 @@
+%PDF-1.2
+%âãÏÓ
+1 0 obj<</Producer(htmldoc 1.8.5 Copyright 1997-2000 Easy Software Products, All Rights Reserved.)/CreationDate(D:20000418184108Z)/Title(CUPS Implementation of IPP)/Author(Easy Software Products)>>endobj
+2 0 obj<</Type/Encoding/Differences[ 32/space/exclam/quotedbl/numbersign/dollar/percent/ampersand/quotesingle/parenleft/parenright/asterisk/plus/comma/minus/period/slash/zero/one/two/three/four/five/six/seven/eight/nine/colon/semicolon/less/equal/greater/question/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/grave/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 128/Euro 130/quotesinglbase/florin/quotedblbase/ellipsis/dagger/daggerdbl/circumflex/perthousand/Scaron/guilsinglleft/OE 145/quoteleft/quoteright/quotedblleft/quotedblright/bullet/endash/emdash/tilde/trademark/scaron/guilsinglright/oe 159/Ydieresis/space/exclamdown/cent/sterling/currency/yen/brokenbar/section/dieresis/copyright/ordfeminine/guillemotleft/logicalnot/hyphen/registered/macron/degree/plusminus/twosuperior/threesuperior/acute/mu/paragraph/periodcentered/cedilla/onesuperior/ordmasculine/guillemotright/onequarter/onehalf/threequarters/questiondown/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]>>endobj
+3 0 obj<</Type/Font/Subtype/Type1/BaseFont/Times-Roman/Encoding 2 0 R>>endobj
+4 0 obj<</Type/Font/Subtype/Type1/BaseFont/Times-Bold/Encoding 2 0 R>>endobj
+5 0 obj<</Type/Font/Subtype/Type1/BaseFont/Times-Italic/Encoding 2 0 R>>endobj
+6 0 obj<</Type/Font/Subtype/Type1/BaseFont/Helvetica/Encoding 2 0 R>>endobj
+7 0 obj<</Type/Font/Subtype/Type1/BaseFont/Helvetica-Bold/Encoding 2 0 R>>endobj
+8 0 obj<</Type/Font/Subtype/Type1/BaseFont/Symbol>>endobj
+9 0 obj<</Subtype/Link/Rect[108.0 513.6 152.1 526.6]/Border[0 0 0]/Dest[231 0 R/XYZ null 818 0]>>endobj
+10 0 obj<</Subtype/Link/Rect[108.0 500.4 174.1 513.4]/Border[0 0 0]/Dest[237 0 R/XYZ null 818 0]>>endobj
+11 0 obj<</Subtype/Link/Rect[108.0 487.2 168.6 500.2]/Border[0 0 0]/Dest[243 0 R/XYZ null 818 0]>>endobj
+12 0 obj<</Subtype/Link/Rect[108.0 474.0 173.5 487.0]/Border[0 0 0]/Dest[249 0 R/XYZ null 818 0]>>endobj
+13 0 obj<</Subtype/Link/Rect[108.0 460.8 169.2 473.8]/Border[0 0 0]/Dest[309 0 R/XYZ null 818 0]>>endobj
+14 0 obj[9 0 R
+10 0 R
+11 0 R
+12 0 R
+13 0 R
+]endobj
+15 0 obj<</Subtype/Link/Rect[72.0 670.8 107.8 683.8]/Border[0 0 0]/Dest[231 0 R/XYZ null 818 0]>>endobj
+16 0 obj<</Subtype/Link/Rect[108.0 657.6 183.8 670.6]/Border[0 0 0]/Dest[231 0 R/XYZ null 735 0]>>endobj
+17 0 obj<</Subtype/Link/Rect[108.0 644.4 216.4 657.4]/Border[0 0 0]/Dest[231 0 R/XYZ null 654 0]>>endobj
+18 0 obj<</Subtype/Link/Rect[72.0 618.0 131.6 631.0]/Border[0 0 0]/Dest[237 0 R/XYZ null 818 0]>>endobj
+19 0 obj<</Subtype/Link/Rect[108.0 604.8 222.6 617.8]/Border[0 0 0]/Dest[237 0 R/XYZ null 735 0]>>endobj
+20 0 obj<</Subtype/Link/Rect[108.0 591.6 202.4 604.6]/Border[0 0 0]/Dest[237 0 R/XYZ null 522 0]>>endobj
+21 0 obj<</Subtype/Link/Rect[72.0 565.2 125.5 578.2]/Border[0 0 0]/Dest[243 0 R/XYZ null 818 0]>>endobj
+22 0 obj<</Subtype/Link/Rect[108.0 552.0 166.4 565.0]/Border[0 0 0]/Dest[243 0 R/XYZ null 663 0]>>endobj
+23 0 obj<</Subtype/Link/Rect[108.0 538.8 221.7 551.8]/Border[0 0 0]/Dest[243 0 R/XYZ null 371 0]>>endobj
+24 0 obj<</Subtype/Link/Rect[72.0 512.4 132.8 525.4]/Border[0 0 0]/Dest[249 0 R/XYZ null 818 0]>>endobj
+25 0 obj<</Subtype/Link/Rect[108.0 499.2 214.1 512.2]/Border[0 0 0]/Dest[249 0 R/XYZ null 715 0]>>endobj
+26 0 obj<</Subtype/Link/Rect[144.0 486.0 249.8 499.0]/Border[0 0 0]/Dest[249 0 R/XYZ null 634 0]>>endobj
+27 0 obj<</Subtype/Link/Rect[144.0 472.8 256.5 485.8]/Border[0 0 0]/Dest[252 0 R/XYZ null 672 0]>>endobj
+28 0 obj<</Subtype/Link/Rect[108.0 459.6 221.4 472.6]/Border[0 0 0]/Dest[252 0 R/XYZ null 429 0]>>endobj
+29 0 obj<</Subtype/Link/Rect[144.0 446.4 257.1 459.4]/Border[0 0 0]/Dest[252 0 R/XYZ null 347 0]>>endobj
+30 0 obj<</Subtype/Link/Rect[144.0 433.2 263.9 446.2]/Border[0 0 0]/Dest[255 0 R/XYZ null 435 0]>>endobj
+31 0 obj<</Subtype/Link/Rect[108.0 420.0 257.0 433.0]/Border[0 0 0]/Dest[258 0 R/XYZ null 800 0]>>endobj
+32 0 obj<</Subtype/Link/Rect[144.0 406.8 292.7 419.8]/Border[0 0 0]/Dest[258 0 R/XYZ null 718 0]>>endobj
+33 0 obj<</Subtype/Link/Rect[144.0 393.6 299.4 406.6]/Border[0 0 0]/Dest[261 0 R/XYZ null 782 0]>>endobj
+34 0 obj<</Subtype/Link/Rect[108.0 380.4 260.0 393.4]/Border[0 0 0]/Dest[261 0 R/XYZ null 591 0]>>endobj
+35 0 obj<</Subtype/Link/Rect[144.0 367.2 295.7 380.2]/Border[0 0 0]/Dest[261 0 R/XYZ null 509 0]>>endobj
+36 0 obj<</Subtype/Link/Rect[144.0 354.0 302.5 367.0]/Border[0 0 0]/Dest[261 0 R/XYZ null 277 0]>>endobj
+37 0 obj<</Subtype/Link/Rect[108.0 340.8 261.3 353.8]/Border[0 0 0]/Dest[264 0 R/XYZ null 700 0]>>endobj
+38 0 obj<</Subtype/Link/Rect[144.0 327.6 297.0 340.6]/Border[0 0 0]/Dest[264 0 R/XYZ null 605 0]>>endobj
+39 0 obj<</Subtype/Link/Rect[144.0 314.4 303.7 327.4]/Border[0 0 0]/Dest[267 0 R/XYZ null 659 0]>>endobj
+40 0 obj<</Subtype/Link/Rect[108.0 301.2 260.0 314.2]/Border[0 0 0]/Dest[267 0 R/XYZ null 415 0]>>endobj
+41 0 obj<</Subtype/Link/Rect[144.0 288.0 295.7 301.0]/Border[0 0 0]/Dest[267 0 R/XYZ null 334 0]>>endobj
+42 0 obj<</Subtype/Link/Rect[144.0 274.8 302.5 287.8]/Border[0 0 0]/Dest[273 0 R/XYZ null 619 0]>>endobj
+43 0 obj<</Subtype/Link/Rect[108.0 261.6 269.8 274.6]/Border[0 0 0]/Dest[273 0 R/XYZ null 429 0]>>endobj
+44 0 obj<</Subtype/Link/Rect[144.0 248.4 305.5 261.4]/Border[0 0 0]/Dest[273 0 R/XYZ null 347 0]>>endobj
+45 0 obj<</Subtype/Link/Rect[144.0 235.2 312.2 248.2]/Border[0 0 0]/Dest[276 0 R/XYZ null 782 0]>>endobj
+46 0 obj<</Subtype/Link/Rect[108.0 222.0 260.0 235.0]/Border[0 0 0]/Dest[276 0 R/XYZ null 591 0]>>endobj
+47 0 obj<</Subtype/Link/Rect[144.0 208.8 295.7 221.8]/Border[0 0 0]/Dest[276 0 R/XYZ null 496 0]>>endobj
+48 0 obj<</Subtype/Link/Rect[144.0 195.6 302.5 208.6]/Border[0 0 0]/Dest[279 0 R/XYZ null 540 0]>>endobj
+49 0 obj<</Subtype/Link/Rect[108.0 182.4 253.9 195.4]/Border[0 0 0]/Dest[279 0 R/XYZ null 297 0]>>endobj
+50 0 obj<</Subtype/Link/Rect[144.0 169.2 289.6 182.2]/Border[0 0 0]/Dest[279 0 R/XYZ null 202 0]>>endobj
+51 0 obj<</Subtype/Link/Rect[144.0 156.0 296.4 169.0]/Border[0 0 0]/Dest[285 0 R/XYZ null 782 0]>>endobj
+52 0 obj<</Subtype/Link/Rect[108.0 142.8 269.2 155.8]/Border[0 0 0]/Dest[285 0 R/XYZ null 591 0]>>endobj
+53 0 obj<</Subtype/Link/Rect[144.0 129.6 304.9 142.6]/Border[0 0 0]/Dest[285 0 R/XYZ null 509 0]>>endobj
+54 0 obj<</Subtype/Link/Rect[144.0 116.4 311.6 129.4]/Border[0 0 0]/Dest[285 0 R/XYZ null 304 0]>>endobj
+55 0 obj<</Subtype/Link/Rect[108.0 103.2 267.4 116.2]/Border[0 0 0]/Dest[288 0 R/XYZ null 800 0]>>endobj
+56 0 obj<</Subtype/Link/Rect[144.0 90.0 303.1 103.0]/Border[0 0 0]/Dest[288 0 R/XYZ null 705 0]>>endobj
+57 0 obj<</Subtype/Link/Rect[144.0 76.8 309.8 89.8]/Border[0 0 0]/Dest[288 0 R/XYZ null 499 0]>>endobj
+58 0 obj<</Subtype/Link/Rect[108.0 63.6 264.3 76.6]/Border[0 0 0]/Dest[288 0 R/XYZ null 309 0]>>endobj
+59 0 obj[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
+]endobj
+60 0 obj<</Subtype/Link/Rect[108.0 670.8 264.0 683.8]/Border[0 0 0]/Dest[288 0 R/XYZ null 214 0]>>endobj
+61 0 obj<</Subtype/Link/Rect[108.0 657.6 270.7 670.6]/Border[0 0 0]/Dest[291 0 R/XYZ null 593 0]>>endobj
+62 0 obj<</Subtype/Link/Rect[72.0 644.4 227.7 657.4]/Border[0 0 0]/Dest[291 0 R/XYZ null 402 0]>>endobj
+63 0 obj<</Subtype/Link/Rect[108.0 631.2 263.4 644.2]/Border[0 0 0]/Dest[291 0 R/XYZ null 307 0]>>endobj
+64 0 obj<</Subtype/Link/Rect[108.0 618.0 270.1 631.0]/Border[0 0 0]/Dest[294 0 R/XYZ null 782 0]>>endobj
+65 0 obj<</Subtype/Link/Rect[72.0 604.8 232.0 617.8]/Border[0 0 0]/Dest[294 0 R/XYZ null 591 0]>>endobj
+66 0 obj<</Subtype/Link/Rect[108.0 591.6 267.7 604.6]/Border[0 0 0]/Dest[294 0 R/XYZ null 496 0]>>endobj
+67 0 obj<</Subtype/Link/Rect[108.0 578.4 274.4 591.4]/Border[0 0 0]/Dest[297 0 R/XYZ null 782 0]>>endobj
+68 0 obj<</Subtype/Link/Rect[72.0 565.2 221.0 578.2]/Border[0 0 0]/Dest[297 0 R/XYZ null 538 0]>>endobj
+69 0 obj<</Subtype/Link/Rect[108.0 552.0 256.7 565.0]/Border[0 0 0]/Dest[297 0 R/XYZ null 443 0]>>endobj
+70 0 obj<</Subtype/Link/Rect[108.0 538.8 263.4 551.8]/Border[0 0 0]/Dest[300 0 R/XYZ null 782 0]>>endobj
+71 0 obj<</Subtype/Link/Rect[72.0 525.6 221.6 538.6]/Border[0 0 0]/Dest[300 0 R/XYZ null 538 0]>>endobj
+72 0 obj<</Subtype/Link/Rect[108.0 512.4 257.3 525.4]/Border[0 0 0]/Dest[300 0 R/XYZ null 443 0]>>endobj
+73 0 obj<</Subtype/Link/Rect[108.0 499.2 264.0 512.2]/Border[0 0 0]/Dest[300 0 R/XYZ null 185 0]>>endobj
+74 0 obj<</Subtype/Link/Rect[36.0 472.8 92.5 485.8]/Border[0 0 0]/Dest[309 0 R/XYZ null 818 0]>>endobj
+75 0 obj<</Subtype/Link/Rect[72.0 459.6 166.4 472.6]/Border[0 0 0]/Dest[309 0 R/XYZ null 715 0]>>endobj
+76 0 obj<</Subtype/Link/Rect[108.0 446.4 264.8 459.4]/Border[0 0 0]/Dest[309 0 R/XYZ null 621 0]>>endobj
+77 0 obj<</Subtype/Link/Rect[108.0 433.2 235.8 446.2]/Border[0 0 0]/Dest[309 0 R/XYZ null 468 0]>>endobj
+78 0 obj<</Subtype/Link/Rect[108.0 420.0 297.7 433.0]/Border[0 0 0]/Dest[309 0 R/XYZ null 408 0]>>endobj
+79 0 obj<</Subtype/Link/Rect[108.0 406.8 202.2 419.8]/Border[0 0 0]/Dest[309 0 R/XYZ null 322 0]>>endobj
+80 0 obj<</Subtype/Link/Rect[72.0 393.6 194.8 406.6]/Border[0 0 0]/Dest[312 0 R/XYZ null 634 0]>>endobj
+81 0 obj<</Subtype/Link/Rect[108.0 380.4 218.6 393.4]/Border[0 0 0]/Dest[312 0 R/XYZ null 562 0]>>endobj
+82 0 obj<</Subtype/Link/Rect[108.0 367.2 251.0 380.2]/Border[0 0 0]/Dest[312 0 R/XYZ null 475 0]>>endobj
+83 0 obj<</Subtype/Link/Rect[108.0 354.0 232.0 367.0]/Border[0 0 0]/Dest[312 0 R/XYZ null 363 0]>>endobj
+84 0 obj<</Subtype/Link/Rect[108.0 340.8 207.9 353.8]/Border[0 0 0]/Dest[312 0 R/XYZ null 289 0]>>endobj
+85 0 obj<</Subtype/Link/Rect[108.0 327.6 204.5 340.6]/Border[0 0 0]/Dest[312 0 R/XYZ null 216 0]>>endobj
+86 0 obj<</Subtype/Link/Rect[108.0 314.4 249.1 327.4]/Border[0 0 0]/Dest[315 0 R/XYZ null 782 0]>>endobj
+87 0 obj<</Subtype/Link/Rect[108.0 301.2 238.8 314.2]/Border[0 0 0]/Dest[315 0 R/XYZ null 655 0]>>endobj
+88 0 obj<</Subtype/Link/Rect[108.0 288.0 248.6 301.0]/Border[0 0 0]/Dest[315 0 R/XYZ null 595 0]>>endobj
+89 0 obj<</Subtype/Link/Rect[108.0 274.8 349.8 287.8]/Border[0 0 0]/Dest[315 0 R/XYZ null 509 0]>>endobj
+90 0 obj<</Subtype/Link/Rect[108.0 261.6 211.6 274.6]/Border[0 0 0]/Dest[315 0 R/XYZ null 370 0]>>endobj
+91 0 obj<</Subtype/Link/Rect[108.0 248.4 278.6 261.4]/Border[0 0 0]/Dest[315 0 R/XYZ null 297 0]>>endobj
+92 0 obj<</Subtype/Link/Rect[108.0 235.2 262.1 248.2]/Border[0 0 0]/Dest[315 0 R/XYZ null 223 0]>>endobj
+93 0 obj<</Subtype/Link/Rect[108.0 222.0 268.2 235.0]/Border[0 0 0]/Dest[318 0 R/XYZ null 782 0]>>endobj
+94 0 obj<</Subtype/Link/Rect[108.0 208.8 253.2 221.8]/Border[0 0 0]/Dest[318 0 R/XYZ null 708 0]>>endobj
+95 0 obj<</Subtype/Link/Rect[108.0 195.6 261.5 208.6]/Border[0 0 0]/Dest[318 0 R/XYZ null 635 0]>>endobj
+96 0 obj<</Subtype/Link/Rect[108.0 182.4 261.4 195.4]/Border[0 0 0]/Dest[318 0 R/XYZ null 562 0]>>endobj
+97 0 obj<</Subtype/Link/Rect[108.0 169.2 234.5 182.2]/Border[0 0 0]/Dest[318 0 R/XYZ null 489 0]>>endobj
+98 0 obj<</Subtype/Link/Rect[108.0 156.0 229.6 169.0]/Border[0 0 0]/Dest[318 0 R/XYZ null 415 0]>>endobj
+99 0 obj<</Subtype/Link/Rect[108.0 142.8 254.6 155.8]/Border[0 0 0]/Dest[318 0 R/XYZ null 342 0]>>endobj
+100 0 obj<</Subtype/Link/Rect[108.0 129.6 247.9 142.6]/Border[0 0 0]/Dest[318 0 R/XYZ null 269 0]>>endobj
+101 0 obj<</Subtype/Link/Rect[108.0 116.4 205.1 129.4]/Border[0 0 0]/Dest[321 0 R/XYZ null 782 0]>>endobj
+102 0 obj<</Subtype/Link/Rect[72.0 103.2 155.4 116.2]/Border[0 0 0]/Dest[321 0 R/XYZ null 723 0]>>endobj
+103 0 obj<</Subtype/Link/Rect[108.0 90.0 315.9 103.0]/Border[0 0 0]/Dest[321 0 R/XYZ null 651 0]>>endobj
+104 0 obj<</Subtype/Link/Rect[108.0 76.8 229.7 89.8]/Border[0 0 0]/Dest[321 0 R/XYZ null 577 0]>>endobj
+105 0 obj<</Subtype/Link/Rect[108.0 63.6 285.5 76.6]/Border[0 0 0]/Dest[321 0 R/XYZ null 491 0]>>endobj
+106 0 obj[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[144.0 670.8 273.0 683.8]/Border[0 0 0]/Dest[321 0 R/XYZ null 405 0]>>endobj
+108 0 obj<</Subtype/Link/Rect[108.0 657.6 201.2 670.6]/Border[0 0 0]/Dest[321 0 R/XYZ null 346 0]>>endobj
+109 0 obj<</Subtype/Link/Rect[144.0 644.4 422.6 657.4]/Border[0 0 0]/Dest[321 0 R/XYZ null 274 0]>>endobj
+110 0 obj<</Subtype/Link/Rect[144.0 631.2 435.4 644.2]/Border[0 0 0]/Dest[321 0 R/XYZ null 174 0]>>endobj
+111 0 obj<</Subtype/Link/Rect[144.0 618.0 284.9 631.0]/Border[0 0 0]/Dest[324 0 R/XYZ null 738 0]>>endobj
+112 0 obj<</Subtype/Link/Rect[144.0 604.8 314.4 617.8]/Border[0 0 0]/Dest[324 0 R/XYZ null 275 0]>>endobj
+113 0 obj<</Subtype/Link/Rect[144.0 591.6 397.5 604.6]/Border[0 0 0]/Dest[327 0 R/XYZ null 782 0]>>endobj
+114 0 obj<</Subtype/Link/Rect[144.0 578.4 392.0 591.4]/Border[0 0 0]/Dest[327 0 R/XYZ null 655 0]>>endobj
+115 0 obj<</Subtype/Link/Rect[108.0 565.2 227.8 578.2]/Border[0 0 0]/Dest[327 0 R/XYZ null 544 0]>>endobj
+116 0 obj<</Subtype/Link/Rect[144.0 552.0 326.5 565.0]/Border[0 0 0]/Dest[327 0 R/XYZ null 472 0]>>endobj
+117 0 obj<</Subtype/Link/Rect[144.0 538.8 279.4 551.8]/Border[0 0 0]/Dest[327 0 R/XYZ null 399 0]>>endobj
+118 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
+]endobj
+119 0 obj<</Dests 120 0 R>>endobj
+120 0 obj<</Kids[121 0 R]>>endobj
+121 0 obj<</Limits[(1)(ipp.shtml)]/Names[(1)122 0 R(1_1)123 0 R(1_2)124 0 R(2)125 0 R(2_1)126 0 R(2_2)127 0 R(3)128 0 R(3_1)129 0 R(3_2)130 0 R(4)131 0 R(4_1)132 0 R(4_10)133 0 R(4_10_1)134 0 R(4_10_2)135 0 R(4_11)136 0 R(4_11_1)137 0 R(4_11_2)138 0 R(4_12)139 0 R(4_12_1)140 0 R(4_12_2)141 0 R(4_13)142 0 R(4_13_1)143 0 R(4_13_2)144 0 R(4_14)145 0 R(4_14_1)146 0 R(4_14_2)147 0 R(4_15)148 0 R(4_15_1)149 0 R(4_15_2)150 0 R(4_16)151 0 R(4_16_1)152 0 R(4_16_2)153 0 R(4_1_1)154 0 R(4_1_2)155 0 R(4_2)156 0 R(4_2_1)157 0 R(4_2_2)158 0 R(4_3)159 0 R(4_3_1)160 0 R(4_3_2)161 0 R(4_4)162 0 R(4_4_1)163 0 R(4_4_2)164 0 R(4_5)165 0 R(4_5_1)166 0 R(4_5_2)167 0 R(4_6)168 0 R(4_6_1)169 0 R(4_6_2)170 0 R(4_7)171 0 R(4_7_1)172 0 R(4_7_2)173 0 R(4_8)174 0 R(4_8_1)175 0 R(4_8_2)176 0 R(4_9)177 0 R(4_9_1)178 0 R(4_9_2)179 0 R(5)180 0 R(5_1)181 0 R(5_1_1)182 0 R(5_1_2)183 0 R(5_1_3)184 0 R(5_1_4)185 0 R(5_2)186 0 R(5_2_1)187 0 R(5_2_10)188 0 R(5_2_11)189 0 R(5_2_12)190 0 R(5_2_13)191 0 R(5_2_14)192 0 R(5_2_15)193 0 R(5_2_16)194 0 R(5_2_17)195 0 R(5_2_18)196 0 R(5_2_19)197 0 R(5_2_2)198 0 R(5_2_20)199 0 R(5_2_21)200 0 R(5_2_3)201 0 R(5_2_4)202 0 R(5_2_5)203 0 R(5_2_6)204 0 R(5_2_7)205 0 R(5_2_8)206 0 R(5_2_9)207 0 R(5_3)208 0 R(5_3_1)209 0 R(5_3_2)210 0 R(5_3_3)211 0 R(5_3_4)212 0 R(5_4)213 0 R(5_4_1)214 0 R(5_4_2)215 0 R(5_4_3)216 0 R(5_4_4)217 0 R(5_4_5)218 0 R(5_4_6)219 0 R(5_5)220 0 R(5_5_1)221 0 R(5_5_2)222 0 R(ipp.shtml)223 0 R]>>endobj
+122 0 obj<</D[231 0 R/XYZ null 818 null]>>endobj
+123 0 obj<</D[231 0 R/XYZ null 735 null]>>endobj
+124 0 obj<</D[231 0 R/XYZ null 654 null]>>endobj
+125 0 obj<</D[237 0 R/XYZ null 818 null]>>endobj
+126 0 obj<</D[237 0 R/XYZ null 735 null]>>endobj
+127 0 obj<</D[237 0 R/XYZ null 522 null]>>endobj
+128 0 obj<</D[243 0 R/XYZ null 818 null]>>endobj
+129 0 obj<</D[243 0 R/XYZ null 663 null]>>endobj
+130 0 obj<</D[243 0 R/XYZ null 371 null]>>endobj
+131 0 obj<</D[249 0 R/XYZ null 818 null]>>endobj
+132 0 obj<</D[249 0 R/XYZ null 715 null]>>endobj
+133 0 obj<</D[285 0 R/XYZ null 591 null]>>endobj
+134 0 obj<</D[285 0 R/XYZ null 509 null]>>endobj
+135 0 obj<</D[285 0 R/XYZ null 304 null]>>endobj
+136 0 obj<</D[288 0 R/XYZ null 800 null]>>endobj
+137 0 obj<</D[288 0 R/XYZ null 705 null]>>endobj
+138 0 obj<</D[288 0 R/XYZ null 499 null]>>endobj
+139 0 obj<</D[288 0 R/XYZ null 309 null]>>endobj
+140 0 obj<</D[288 0 R/XYZ null 214 null]>>endobj
+141 0 obj<</D[291 0 R/XYZ null 593 null]>>endobj
+142 0 obj<</D[291 0 R/XYZ null 402 null]>>endobj
+143 0 obj<</D[291 0 R/XYZ null 307 null]>>endobj
+144 0 obj<</D[294 0 R/XYZ null 782 null]>>endobj
+145 0 obj<</D[294 0 R/XYZ null 591 null]>>endobj
+146 0 obj<</D[294 0 R/XYZ null 496 null]>>endobj
+147 0 obj<</D[297 0 R/XYZ null 782 null]>>endobj
+148 0 obj<</D[297 0 R/XYZ null 538 null]>>endobj
+149 0 obj<</D[297 0 R/XYZ null 443 null]>>endobj
+150 0 obj<</D[300 0 R/XYZ null 782 null]>>endobj
+151 0 obj<</D[300 0 R/XYZ null 538 null]>>endobj
+152 0 obj<</D[300 0 R/XYZ null 443 null]>>endobj
+153 0 obj<</D[300 0 R/XYZ null 185 null]>>endobj
+154 0 obj<</D[249 0 R/XYZ null 634 null]>>endobj
+155 0 obj<</D[252 0 R/XYZ null 672 null]>>endobj
+156 0 obj<</D[252 0 R/XYZ null 429 null]>>endobj
+157 0 obj<</D[252 0 R/XYZ null 347 null]>>endobj
+158 0 obj<</D[255 0 R/XYZ null 435 null]>>endobj
+159 0 obj<</D[258 0 R/XYZ null 800 null]>>endobj
+160 0 obj<</D[258 0 R/XYZ null 718 null]>>endobj
+161 0 obj<</D[261 0 R/XYZ null 782 null]>>endobj
+162 0 obj<</D[261 0 R/XYZ null 591 null]>>endobj
+163 0 obj<</D[261 0 R/XYZ null 509 null]>>endobj
+164 0 obj<</D[261 0 R/XYZ null 277 null]>>endobj
+165 0 obj<</D[264 0 R/XYZ null 700 null]>>endobj
+166 0 obj<</D[264 0 R/XYZ null 605 null]>>endobj
+167 0 obj<</D[267 0 R/XYZ null 659 null]>>endobj
+168 0 obj<</D[267 0 R/XYZ null 415 null]>>endobj
+169 0 obj<</D[267 0 R/XYZ null 334 null]>>endobj
+170 0 obj<</D[273 0 R/XYZ null 619 null]>>endobj
+171 0 obj<</D[273 0 R/XYZ null 429 null]>>endobj
+172 0 obj<</D[273 0 R/XYZ null 347 null]>>endobj
+173 0 obj<</D[276 0 R/XYZ null 782 null]>>endobj
+174 0 obj<</D[276 0 R/XYZ null 591 null]>>endobj
+175 0 obj<</D[276 0 R/XYZ null 496 null]>>endobj
+176 0 obj<</D[279 0 R/XYZ null 540 null]>>endobj
+177 0 obj<</D[279 0 R/XYZ null 297 null]>>endobj
+178 0 obj<</D[279 0 R/XYZ null 202 null]>>endobj
+179 0 obj<</D[285 0 R/XYZ null 782 null]>>endobj
+180 0 obj<</D[309 0 R/XYZ null 818 null]>>endobj
+181 0 obj<</D[309 0 R/XYZ null 715 null]>>endobj
+182 0 obj<</D[309 0 R/XYZ null 621 null]>>endobj
+183 0 obj<</D[309 0 R/XYZ null 468 null]>>endobj
+184 0 obj<</D[309 0 R/XYZ null 408 null]>>endobj
+185 0 obj<</D[309 0 R/XYZ null 322 null]>>endobj
+186 0 obj<</D[312 0 R/XYZ null 634 null]>>endobj
+187 0 obj<</D[312 0 R/XYZ null 562 null]>>endobj
+188 0 obj<</D[315 0 R/XYZ null 370 null]>>endobj
+189 0 obj<</D[315 0 R/XYZ null 297 null]>>endobj
+190 0 obj<</D[315 0 R/XYZ null 223 null]>>endobj
+191 0 obj<</D[318 0 R/XYZ null 782 null]>>endobj
+192 0 obj<</D[318 0 R/XYZ null 708 null]>>endobj
+193 0 obj<</D[318 0 R/XYZ null 635 null]>>endobj
+194 0 obj<</D[318 0 R/XYZ null 562 null]>>endobj
+195 0 obj<</D[318 0 R/XYZ null 489 null]>>endobj
+196 0 obj<</D[318 0 R/XYZ null 415 null]>>endobj
+197 0 obj<</D[318 0 R/XYZ null 342 null]>>endobj
+198 0 obj<</D[312 0 R/XYZ null 475 null]>>endobj
+199 0 obj<</D[318 0 R/XYZ null 269 null]>>endobj
+200 0 obj<</D[321 0 R/XYZ null 782 null]>>endobj
+201 0 obj<</D[312 0 R/XYZ null 363 null]>>endobj
+202 0 obj<</D[312 0 R/XYZ null 289 null]>>endobj
+203 0 obj<</D[312 0 R/XYZ null 216 null]>>endobj
+204 0 obj<</D[315 0 R/XYZ null 782 null]>>endobj
+205 0 obj<</D[315 0 R/XYZ null 655 null]>>endobj
+206 0 obj<</D[315 0 R/XYZ null 595 null]>>endobj
+207 0 obj<</D[315 0 R/XYZ null 509 null]>>endobj
+208 0 obj<</D[321 0 R/XYZ null 723 null]>>endobj
+209 0 obj<</D[321 0 R/XYZ null 651 null]>>endobj
+210 0 obj<</D[321 0 R/XYZ null 577 null]>>endobj
+211 0 obj<</D[321 0 R/XYZ null 491 null]>>endobj
+212 0 obj<</D[321 0 R/XYZ null 405 null]>>endobj
+213 0 obj<</D[321 0 R/XYZ null 346 null]>>endobj
+214 0 obj<</D[321 0 R/XYZ null 274 null]>>endobj
+215 0 obj<</D[321 0 R/XYZ null 174 null]>>endobj
+216 0 obj<</D[324 0 R/XYZ null 738 null]>>endobj
+217 0 obj<</D[324 0 R/XYZ null 275 null]>>endobj
+218 0 obj<</D[327 0 R/XYZ null 782 null]>>endobj
+219 0 obj<</D[327 0 R/XYZ null 655 null]>>endobj
+220 0 obj<</D[327 0 R/XYZ null 544 null]>>endobj
+221 0 obj<</D[327 0 R/XYZ null 472 null]>>endobj
+222 0 obj<</D[327 0 R/XYZ null 399 null]>>endobj
+223 0 obj<</D[231 0 R/XYZ null 698 null]>>endobj
+224 0 obj<</Type/Pages/MediaBox[0 0 595 792]/Count 40/Kids[225 0 R
+228 0 R
+333 0 R
+336 0 R
+339 0 R
+342 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
+267 0 R
+270 0 R
+273 0 R
+276 0 R
+279 0 R
+282 0 R
+285 0 R
+288 0 R
+291 0 R
+294 0 R
+297 0 R
+300 0 R
+303 0 R
+306 0 R
+309 0 R
+312 0 R
+315 0 R
+318 0 R
+321 0 R
+324 0 R
+327 0 R
+330 0 R
+]>>endobj
+225 0 obj<</Type/Page/Parent 224 0 R/Contents 226 0 R/Resources<</ProcSet[/PDF/Text/ImageB/ImageC/ImageI]/Font<</F4 3 0 R/F8 6 0 R/F9 7 0 R>>>>>>endobj
+226 0 obj<</Length 227 0 R/Filter/FlateDecode>>stream
+xÚìÏsë8rÇIŠºÌ‰öŒî´üt§çyšÝ­ÚJñÙfvçÖ$ÁÊ!•S*Ç6•üÿ±~X"H€èn
+ ‚B¹ü29×$X,0X
+Ú Ñ ê£s5Ѭåà‹+›— 9Ä`™ãÅ`ž‹,öD.1?V{àÊàŠRç\uD98æY\‰c®}HÙ8媣`Jé++D91WTθš°ŒÒË TÔ  LÔ‹Š4ðZåªBÃ2t,OãQ ÔxŒsÆq‰±FãzX¢Æ#F,Ï%£FL×]w÷Ûí—×cy>|ðÛa0IwÅ÷_†ƒüxÂ[‡ÿîŠG’þÅ˃ŸßÝuoœ*=[ í,¸ö~ºªÛk[ça=xì.Bú±àæØ\ìPƒ–.Î$[q¹¸¡==·aMJ&³»XéÔœ.K™\¬@ž› É貘ÇUùÑv‡]¶cqeSÙàÕ›eN¤ܯjؤ½s’  ®|²¡ÅϼJé\Í Xd°˜ÎEùØÍÉÜZ9ƹfÂ"‚­¨\Õ FÈ1Å’È•Ï…EÛи'’ –Y¹0p7ñú¥uZj+Cg‘¼c,Òé« …«rÎX•½ÅÍÁ‘jÄ¥{.B J<—°ÒlKÃ7D=Ì:¸ÎåÛÁÉ %i=•œkˆàBf½ .Ú`Ø`¹Š¹2O”,Á W­Ç’ó ìÍПR4q‡ãÚa…„–¤8®
+¢6(®–KH·Kh%ºÖRÕÓbQ®•–ër›®Îa‡Àà ¯Çé¹ £^ >ÜÌÑ–œáµCrí´\Mçù
+.A5C#WªçÚŸòáªÕr[¬àª©fhäŠõ\盽§5Rì•K,×êZ[Éu6Ž;êÊ12BªWNZ,×:{—'%WG¤(³ÔgO@• ž«x j®Š5K5†ˆëWC6Ãq®ZÂp ÖbI…Ó5 ÊFÒâ¹Ä»Y¨¹®&EZ,ip-â\9%pžê¹*ÖšNè(;
+\n£á¬5;c+\ŒôÅq®æ"
+®K‰kvª‰@“¤¥pµ¥×qgéB™ÐdcMãº(½Ž«àôWòD@“ë¢ô:®Œ3¾êÙwCÅuQz WÍÛÃÈ0’ ¤h#!r]”^ÃUðûMÏ%s Æð2r•^Õñ6g
+ÌÃ’lì¨\g¥Ws5Ì]Œ
+㘴¶ÑR¹ÎJ¯æ½ØER2WÁ^f®“Ò«¹rfæŽÀ8f DQ)ëdÝ…~^¹e¢IàkSYŽ­\§Öç*®³Ý FÈ‘#š ”(ªT?º²k ±ÌukDŸ+?krN7Ä="à
+vt6מÍ•4™¿peM/\œ¥ùp‰'óg®úÃÉ<–k2ßÎ-‡Í‹ó·•Ü:\[æ»uîªßFwÒ²ßmµwpœOm›.Ò,²
+£®[‘"K~­lâŽÃÕ¿(—óM
+-áðì8—Ðpz®”ÃÕ¿¨7RNf™
+E2þW=/WÁá’.ªn»Ê·EÞÙ¹j—t‘趲¾Æ6ÓJ\‚Ã%_”ßö¿ž#Wé“«{œÍ%_T ö+Càª8\òE½H0?qÙL—Q\Ù8WÃá’/’gÆé>®Îq><Wï"©ÃÊbB®ƒ¾‰‡K¾HN#‚—qy#¶äÊ£îíZðS¹zýèN«i¸®oÐ4Qp¸ú=w–"p…S^Nù‡?ßÞ?oäZJ¬èžó2Ûâr{Ï\ù'×'׸V”+]&WöÉõÉõÉõÉõÉ…ãZr}rùçŠì¹Þf>ès¥”ºór=ŸÞa?bšK©;3×Cÿøšü²¡ÎæuÕ»êªHqPJÛ:k®|¾!çéw·§†u\y1Ô×áŒF~™WsÛìø9QeäÚÉÔ—Pì(É/4¬®KüuòÈȵ™‚ TSë¼;Àz齺 ®u+ M?\™j« ºã “Òdúu\©l)^¸š÷§»nÝî«ko»õʺ ®Dºµ®êºQ(¾unÚ‘À[v…².Ÿ«öÉUtv¬÷7ïÒqY·äsM]Õ2¿ú®òöNå“+ª›v^Ȭ©K㊯ۧoƒ8ñÆ•u—áòžÒߌ§©KãJ®£0qÙÄóÒŸá¦×VËIÚªº$®Õõq¼=™t®êöÓ5ä$mU]WÚ}tk;.CúáõÏÍÝ[ÙtĪ윑Ò×%qÏ]ÓÆ+W¢ûѧ“T<¶ÔçÊ/ƒô8„wÞ¸2]:ÎeO¹éÀd#©;x®ýåóŽŸ|ðÆuŽš¸1‘†‘¶.‰«¸ÈÐÑÒKo\—½Îûƒ:À:eR¼Ë¹¶.‰ nÙ¯qëë¢Z{þ⇬ã‹õu•\ÃÄÎEu¯G÷å«“Òkmñîm®¾X_—ÂU_ `ü`;®±}½n=é½ÄÕ0‹V[—ÂÕ\~<ºeK®±}ØByŽCš—ƺ.]_ˆ¾ç²Ú_ÚÌÒ|Õ¢¯Kàj/Žùô[\½,Ú¸ôdŠ¨Ká:;æ“[öÉÕyÑ–L¡ú’9]]
+×Ù1×'ï“ëý ì —J•l:ZÇuvÌõ©¾—1ïë9Sš\ùPÔuñ~ùäËãÿc—‹|¶—‡á¤¦ÒLrTu \ÕÉOnÙ?×­µ«^(u@Õ%póÉ-OÁõ.
+‰vÐŒÕ%póÉ-[raß-Z‹ÁX]—x÷b멸n‰×f®^]×Ù1ŸEr”Ë&ûø*™ìŠµ\cu)\o&˜4·õS/\²{ãb§®.…ëM2âúì-¹JÜú¡k¬.…ëØÓUt}Q7®D²­x”KW—Âçð±‰ËæÜM.ý9Õù±º®ê|²&±çÚO¿6ÝyÈJË5V—ÂUßœº7®ªÛŸûÞmz«Kájn³Q.›sˆçkãï·M­–k¬®!ž—Þ¶%ä]A ®qö¶ ©Ÿ,ŽÕ¥puÞ ë«™«õ¹Fê’¸r—Õ¹ìf¤jŸk¤.ƒ«´çZãÒmú¡ÿ VÑ×%qƒßxàjô
+3àj¸ëQ2\ŸÊ(—Õû7䵘_ÚQ.}]—îý¬n¹:}l \Úº$®úêÓǹ¬²Þè!Ón”ØÔµ*–ïí¹Ê×ëMóU|–ïY
+¶X¾ë“kz.óë–—ÉUüÓr•‹ä2/pÉe3aþäšžËjb¹h®õ"¹Ä?-WºH.ólõA¹’ere0ðµü>ŽO®¸ö0ðÅ}Ïn‘\ðA¹ª Z~¯Ù\Åô²×å÷ÐÍULMÞá¾70¸@ªrÃ\ e’ºÒò{9ç*Š+[\Àa
+%°ß{ZÀ‘›ì ù=Å¡­´e&=@~¯thG„â‚¥Âèo‘ßoXÀQã¸÷}ô•±
+-V裥ÈaÜ媗#ˆ(Â-ôDˆ *âƒ-ôéBäpÝåB} Â(»‚-ôÇ¥€VÏP„#C̾:\ÕB")£œ· Å }ºÙHd.„Ð'‹TæB,ÝÄ‹u+_†pd8
+h)‚¸xí\Åú,(ÖCéÑg­¡% ÇÜ ýŽhIÂ1ó
+œéöÔ1GNÀY™8樼A:©Ôï˜gB©œÇ ÜÏ™.H,8Ž¸Ãtºc wàÊêd†kÍÁ…Tú‰:,c=càÔ4Æ €?R§é°ŒÖÛNÓaÓ™_‚& :C¢Åqí±`»0ºk…äªé0‘qƒph­ ±÷UKó„ªY.´v†èuâ,øV­!zÕzìÓÝ ¹ð†èQ:j ›ÖÖãrfWŠØ ¬OKüñÍPË%¢¹-±±’.h­ Ñ“&æV>¬­'BõÊXZ?Þ¹±´°öõ^†˜È,e \<1÷3–½í#×½{&ܸ¥rU¤s©ÍJÉ\"š Ldö
+\EiW°/)áøf08Ô&[õ cœFwžÿf<[l2êR6W›qÀúßtŽ*OôÛø\‹+Š¿S;‹añcq€‰‹ÙaQt_zî¬ñ¹‘‘«ˆ¸ÿ¥ó/9k·6\"â—{”56¼OßYqYtØqœ=šÌñ…Ie˜>˜¹l:ìŒöªÿ짌ý¹K.ê4Li¯ƒ~/ÛÌæqµ¶\Mä¦ÜÝo·Û/¯oåe»}È,?mcÍå¢ÃœSþ#†KȵqÀe'‰³tŽ+¼Û9á
+®ÃÌK_8.±´îBrvm¦(ˆ=$W›…ÄupÇU/«»Ð\¬•O_ºäj‚áÚ´.¹‚ÑzÜöžKdKÑxw gÑ q!Øý^
+—XŒÒ¸°DôÖ!‰kvKÄgиš…X!•kæø—°MäšÕ)¹/T®9½3eû‰Ê5c`OJš#sÍ6Ähi/t®™†1±ŒÁ5Ï#îí2¸fbÔ,%=pÍ`sM®ôT9×ÄÚÁÈ
+·¶øhß"7\o¶è®Ë’CW+Âé,—\ŽºìþІÆÕŠ'küâª-.¹ø©ÕªGw-qËe‘^HÝž“‹Kæ–ʇìþ‹ë6øà"æÅÇ[ï¹ðÃõV^qÙññýw/·÷Æuê5ƒAÞm_}ÝÛ'ש۞¶wê~z|õy_ß\çŽ;žpØ>ÜËývûøúêý–“pÍPàÿ
+ä
+ág©vêˆÅ
+iü÷GO³é®7˜e ጱ ìúJ´P*«ÌS5žæ28o8îÞendstream
+endobj
+227 0 obj
+6280
+endobj
+228 0 obj<</Type/Page/Parent 224 0 R/Contents 229 0 R/Resources<</ProcSet[/PDF/Text]>>>>endobj
+229 0 obj<</Length 230 0 R/Filter/FlateDecode>>stream
+xÚ+ä2T0
+ár á
+ä
+endobj
+230 0 obj
+31
+endobj
+231 0 obj<</Type/Page/Parent 224 0 R/Contents 232 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 3 0 R/F8 6 0 R/F9 7 0 R/Fc 8 0 R>>>>/Annots 14 0 R>>endobj
+232 0 obj<</Length 233 0 R/Filter/FlateDecode>>stream
+xÚµ•;oÛ0€wÿŠC¦d°*Ò”dwKÒ6ðÒº±tðâJ”ÃB]‰qþúz´iT "øÝñ>MýœñG ¡°ˆ!“³«töîÓ
+hi3q ÍÏ l3uäé;K7;I@í|@`óJ‹Bd{-TåP„x”ÆM•Þ‹r•=HÄáX«“Èyû
+Ô‰×'ÁA ï9¬+ÍëŠkØÔ3W|QZeª„ÝùÙz³9Û]
+ö y,¹ÉÌs¶\š,\•&éµ’ñ»Ïëo}ÞíS£¹4Y¯ï6Û¿ÒϬY,5…­É_ýDq|Wõa_‰_¶R­¬r¡ÊR=š’ž™mlÞ»|™ÏG—¸f—rGã ð1…Aˆ­4b·7ØÐ –,ƒ$0†nPÂöô
+endobj
+233 0 obj
+576
+endobj
+234 0 obj<</Type/Page/Parent 224 0 R/Contents 235 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F8 6 0 R>>>>>>endobj
+235 0 obj<</Length 236 0 R/Filter/FlateDecode>>stream
+xÚ+ä2T0
+áÒw³P04TIS047Ò3S072PIÑp VðÌ-ÈIÍMÍ+I,ÉÌÏSÈOSð Ð ÉâÒ…¨Õ…*6‰™˜˜…À|C…àäü‚T¨kW 
+endobj
+236 0 obj
+108
+endobj
+237 0 obj<</Type/Page/Parent 224 0 R/Contents 238 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 3 0 R/F8 6 0 R/F9 7 0 R/Fc 8 0 R>>>>>>endobj
+238 0 obj<</Length 239 0 R/Filter/FlateDecode>>stream
+xÚ­–Ks¢@Çï~Š>f«”ºø¸I¶b…’Ó^Fht¶`Æ*ñÛoøܘªÅ¤<¨Ðóë÷þ4\hÓÇ…žâ¼q5®§ðÚ¥tÇïu!J®<˜cŠ
+EŒú[ô»4q{•IËw¯4r\?!Ld\ä( 3\ŠÊ¼ ®»5÷|§dF+„Tf™|åbYLŽ× v^XlÀ¬èÒÎfXã-ØëS{ö/ÏïÁ©ç+ë¡5ž-×q‡•¿±)_ªò7c‚-Ѳ!Ș8åSàn§ÊóCøÃdr7Ú` JYŒ0AÍ—Â~ÅŠ¯µ©å"8Žÿ!_g‡zÉèv}f8šY¦ó¶ \¦æ•)„Q’sÁµ¡úH¥m}
+–]À?-ËŽþåƒÙ9t äR±<·ƒuqÔáü:ĸPÜlh!ÖR™ ¸QpŽ¡¾tìÂçú÷¬ñ3m{™œÇ¾ÕÎÛ»Îí4¡U-c·íô+]ðàɬPí…Aÿ§()Z' Áð•ª0Jä‚Fj–ÉÐìp»±ÇÙÁ”gS©rf \cÌS—;×Ü£ëtœZ5~çø‘‰eAtÚ&D+®¸M¸uVOR0„rL$û¤B4ð´ÆJðt]Þu9··"–‰íŽGŠ ]{ö,+`¥âÚA½+x‚—qf2Á¬ (Äœ ÃãzÉͧcúÙ4á‘ <Ì
+x &¥gêØ]¿EžïúM¸ßÐ|3ÕôѲjµà>ŠÊ©¸„NݲÇaTPUì•U Ó<.˜ð¥UêQLoBú»ŠÙß2·*èµ=çì;T·ßvÝêØÿ·Qãgã/4s¯6endstream
+endobj
+239 0 obj
+706
+endobj
+240 0 obj<</Type/Page/Parent 224 0 R/Contents 241 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F8 6 0 R>>>>>>endobj
+241 0 obj<</Length 242 0 R/Filter/FlateDecode>>stream
+xÚ+ä2T0
+áÒw³P04TIS047Ò3S072PIÑp VðÌ-ÈIÍMÍ+I,ÉÌÏSÈOSð Ð ÉâÒ…¨Õ…*6‰™…À|#… Ô´Ô¢Ô¼äÔb”kW 
+endobj
+242 0 obj
+113
+endobj
+243 0 obj<</Type/Page/Parent 224 0 R/Contents 244 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 3 0 R/F5 4 0 R/F6 5 0 R/F8 6 0 R/F9 7 0 R>>>>>>endobj
+244 0 obj<</Length 245 0 R/Filter/FlateDecode>>stream
+xÚ¥—Ûrâ8†ïó]Ü°[„¶ÜÍi§2µµÉdï AS¶å•D2yûmµlsH b¶R•ùïî_Ÿ¤¶ø÷ŠC„&#§°*¯>.®†Ì`ÁbOÒI ‹ü·1Ü= ý$Åóï‹(ˆs/ŒRF’O÷sàŒƒ,ëB”¢²nïï‡n(«r°ª:³RU††2kµ\n­0‹µ¬D²"a#oà›Z’î^ËÊ
+ sa]y,ÊÇläŠÞu {×äÊÝÀÝÖÖ[;Xb¶m øòÓŠÊ ¥.ëNŽ¥¥ÙÈêÑ@­Š¼ÝŒ*û^Ø_f…èUî¼ÃSVlq¢K¦+¹–+ïŒ5‘ ²Â(¨µz’9Æò1TâùO™U/4¾ÇÉ*0ÛºVÚB¹-¬DÔŽ2¦"D>°ùräxUdÆ`
+UAg‹¡e,óK:>i–4ŽØ”V—Î%ø~kί{ãÉÀRÙ_Áµuσ–5~*…ݨÜ0XàS¤\¨gô
+–>-ÖȾ€Þ£°½=àì°Þ`?þDÁ,/euºêÃ~9ÒJc]µ'ñÿê6ë8t#§ËÏýtHÛý
+|ÔR¬á³0+-kÛøø²¸ŠX„ïâîß÷¯OÙa¦Éù3Ž"üˆrþ†˜§¶W§,=/žÍpN^'èþœx„³i3óÙwjº.´+˜pç7%šôÎàœ‘{š=g‘!‡4ýŒ¢hDÐ’#ã!^ËB°wð$)‹Âñ:‰Cñx¯ñx(1÷'៬yfE0™øLïÚìÞ½à=HãÄY …DêPH$…ä}œ‚4âXÒ-N!âo JùèöѽÎÐá KÃé:”‰Céx§èD¼y½‰*|V«­»cJ¹Íáò”`R³„ÅÁ Hȉ´˜¼‰”¦¼»ó­D|ʦG[ˆ¢/
+endobj
+245 0 obj
+1118
+endobj
+246 0 obj<</Type/Page/Parent 224 0 R/Contents 247 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 3 0 R/F8 6 0 R>>>>>>endobj
+247 0 obj<</Length 248 0 R/Filter/FlateDecode>>stream
+xÚ­˜ÝrÚ0…ïy
+]¶¸’%ÿp™@“¦3Ò@À€Hœ185†¶oß•Ö1¿Â«L'™ñ-ÇßhÖþÕŒÃ`26¿óUïvÚût§˜lºd‘§2HÙtñá^×ýq•¯k]õoêºÊgÛZo>N_zB„©"psE¦AŒøÎù­¹¦¢@â5¨Ä^±ËÚJ}žöxÀÁTûçñž©ÔøHð_ò d•f“ bsСÚÚ¸* ‚¨[×Ä¡Rme1°p¯nH+–ð•p_ÊbÑÿZÎÎøˆ |†Ç|ÌZ–±—rÆ–e…¨òõS (¢d@VMdÅT@èÃHÉ@™›|Ô…Î6šÌht̨YþL2 “US1Y1úpa
+%\‚ûg[€Ôô¥Ù?e ´pX¹dMóu‘ x “²j*)+¦’B.R\â~ØlW~ ÄéŽ2ÞE*$'“B5‘Š‰¤RQb’·Õ“m<Rz‹ðdC™Õ,+
+Óy]©%±Gj£šJÆŠ©dЇ‹LbjOàŒ.×η Ù$Ô1!¨rp®Ùp‚¤× »­*ö¬!ß^»(ötTSÙY1•úp±S!úðçxÒ7CÂH/³mQwï-èÎ'ƒúY³–€Ï ôbVçåº –Œ=bÕTXVL……>\°Âc½…ÕÖ†D+<§eú°\ZhÙ.Ë‹lVè·ÔêlN{<ª©Ì¬˜Ê }¸˜ñ=±›Å‚ñ
+ËŠ©°Ð‡ V(b¾éE:¯äz'Ò@‰È#ÛQMeÅTPèÊ‹ýžšÏõkMKRz²«ìr;“îìíÑN"´G5Š‰àp2åiÿ¨_ôÜÝàôÉÇ,ÿèåq šŠ.Qô3 ñáBß¾?&žëÍù¨ÿ¾UFÊ#öQM…eÅTXèÃKñ³é~—Ï)ÏEêâ{¿‹ækv“Ê#ûQM%fÅTbèÃEÌ^:ñÇ#®!—)ØÅJ(øG5••SY¡+Î[RßÊñe :{hÖ6oëþÁb©+½®‰Ãk8P™j"-i5>Îi}ºK›wò"1¯*’°ÇV¯…^ÁÍÚÔ1»åa<6÷ÛGm¿ã&CD&Ù÷®v¹þÝ ùÑû*f—endstream
+endobj
+248 0 obj
+1005
+endobj
+249 0 obj<</Type/Page/Parent 224 0 R/Contents 250 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 3 0 R/F5 4 0 R/F6 5 0 R/F8 6 0 R/F9 7 0 R>>>>>>endobj
+250 0 obj<</Length 251 0 R/Filter/FlateDecode>>stream
+xÚÅVËÚ<¿óWŒ8´¤(û¸m«öÓVû %+µ“ Äû;µ¥Týã;c£Zµê¡â!;3¿× |mÅЧO çŽ!]µÞ$­Wï/aЇdA'ãó$Yg%á¤V¶›<Ñ•Äq¸ÒŒ#)ɺ(ôZª%XLýuÈЦFÎë]°UYjã0ƒùÞ>N¦Ü(I*‡­½€¹Ágé6g{o2
+ãÅu MhtUë»­ÿW„øª®w¥ù.gwù^¸Êˆn…ZVb‰@FÀÛ\‘’§0Å“ò ¿Ý`í¥tÙ¢kû7÷T(Ý+êÒí†MÎ2ÊÏ6}0$1IУÈÔÔ9w:ÃÂ÷šâJ('Sª¤Ój…ÊEGPÛÞA4½ÊÈ69KY÷W|RZ9¸{œ&A÷ ¹þøé†|2! %¦rÁnÔ%}«Þ¾’AöÁ°5 ®ÊB8|Aùö“ž÷æ²(( N‰Î:wןgÝä«÷ãÓôÌ:<zÀ‘cr¹ÌÑ̺/¦­¦÷0Inî¯oo¿lÃÅá®!Ð ~¸\8
+½\ݵt¹W ôáHS]‘æjydT¤ÔʇíÄæisDg™fLYa;7%áܬ5ýø7ìµ¢%D¯5Ì…Rx¦iƒ<„ÁmÚxHüÂpˆ†P¿ì‰A-~ˆy Ùñü¶ul³¼´.Ñ<Se§)þ™L9,¾­:À6×UÁ
+šç:é çO4œG-›ŠTžvùv3¿>õà^;¼:t¨ÓýïoD»´4,.^‹`)oBƒq§q¹fRüQÚþ83+΃ﳿÖjÕà ‘yò;žùîg`ueR<;Ö‘•ê‚
+ÖVèÊ•• •Ùjø[sƒòµò~e ¿ú0ºèG—dó÷wIëcë'+;ªOendstream
+endobj
+251 0 obj
+862
+endobj
+252 0 obj<</Type/Page/Parent 224 0 R/Contents 253 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 3 0 R/F5 4 0 R/F8 6 0 R/F9 7 0 R>>>>>>endobj
+253 0 obj<</Length 254 0 R/Filter/FlateDecode>>stream
+xÚÍUMoÚ@½ó+F>T;¶q€pK£¦JU©.k{ ­wÝuÓüûÎÚ†š&‡\*ž÷æÍÛõã ‚>Œ'î›—ƒÏéàä2(‚tãMfI0ƒ´z%œùnX-¬•æÒ¢•=`nY«yV[n 6X£ôZv­BðãI¸N×Êâ¼î€†°²LLpµ\‚’â˜êÉ€©0çž3Ë•µ†Ë­@hõ€1QcÛ»±÷Dò›Ê ŲŒòÎwÆåú4ä>;½GXºÁ|W¡ñ±FcÝDåØ`ƒ 'x« ÃN†Ý´g%Ý´ãq0q-“
+â^Ó[4•’9´p4&lµª+Ó̽§ L#”0ÓÖ…íõÊü…_];ˆæpS¡n%=”£¯íÄÖh Ûµr<ÍnkºÃsO\MÙÖ¼ÜÆ55ðÉmMq r¸¸gšåÎK+´¯¡xG÷sJ6Hþs•ý€l[û¢kíf @“Ó_Z!—$^ÞÌ>¦å$AÜaFcÚavj:'.T¢ÁZaɤå9uRy]¢´Á‘}Zuãyã·7umU;Nk,4uŽh'a{øˆ#\h$÷6ÛÝ/ïMõ
+Ô~Ûëaø; ÃÓõò&Nâ€Ä§O@ÇÃ>·~†•½åè8ˆúÝoÛsòaG×U%8¹º‡Ôȹúÿô`ôö¨zݵìך{´lúY^›''¥iÑ‹»UÚ
+ïîµ»Û+Z”n»{—¸v-ß±û?®×~¾G¶ò3.ÙÀ‘“¬Äõpqþs=êHž\Î:ûDã ¹mr6m=wq·\Á! ›zÿ. yVÑ4¦<‡.yÖ‡{»½sI?
+endobj
+254 0 obj
+667
+endobj
+255 0 obj<</Type/Page/Parent 224 0 R/Contents 256 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 3 0 R/F5 4 0 R/F6 5 0 R/F8 6 0 R/F9 7 0 R>>>>>>endobj
+256 0 obj<</Length 257 0 R/Filter/FlateDecode>>stream
+xÚÅUKoÚ@¾ó+Fœ@Š] Ô!ÜÒ¨­RåA •Z‰ËbÁéz×Ý]QõÇwÆ6 MµRÅy½3ßc¾]oУ_
+GC³¸3ï\}žL!ð*†Uº\¡™w»³*Ö=ðú¡?äý³B$STî'³ëû»Ë››¯`‹<§E ©”©Z‚u†ÿÜJ8H-H½\b ëÔ­h !KEºPŽö1¡¿Ï(©J´É„KµòëW[í½ðì
+ÑÙ6Ì;E§p›ð 7kmbø Jd8ïÜ^~™wçÝq¥&<VóÕk…  ¸µ†…P
+M)ÓV&CªÉG6,ôa‰)Gû„Ú@’J´GFTnqr˜«RÝV„Øf{ Z4ÔÙiHUœFÂa«¸€]éB2-®“¨Û©Œ5ÑWÚÍ1J“ õ$4áh¸‹¢„ÀŠá£EɉöfäŘˆBºvg@/0r È]Gj_X¢UÍàõñ î´Ãñá„:0u䥠Ñ_O&4¹!¥^Ûš7ûAYb~,åM"ì8îé)eE.Ã8/JÛ‹3“qJœ›-ûMuhxé<+ÑÏÀêÂDxÖô‘iI ëQèÂå…«:ó¨áo‡Û
+ ·h-ݧ¢j·Î˜W¸&«jšNÞÑK#$ܵ,ÊKT\­„ç`ŠîJ{§Ý‹h3«vY¹ÿBU­=Y·n8f!FÑ#ÝztMZŠkøO#jÄ´¶“St«c”%Ö3A_Ÿˆ:é¨ÈèH”â¼}Ã*wûã2(ÏúºËS3F£:Á ¤…A+ÊÏ5ø)ÆÄ°äœ÷i¯wÞïý1sÃQÏ¿€rß?¿µ>¶~1#ˆ;endstream
+endobj
+257 0 obj
+789
+endobj
+258 0 obj<</Type/Page/Parent 224 0 R/Contents 259 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 3 0 R/F5 4 0 R/F6 5 0 R/F8 6 0 R/F9 7 0 R>>>>>>endobj
+259 0 obj<</Length 260 0 R/Filter/FlateDecode>>stream
+xÚÅVMoI½ó+Jœ@òL†À.7o¾Dd¯K‰Ä¥™) ¡{ÒÝc‡Õþø­ê ÆØJ”Ã
+KÈLU½WïU|o%УWéÿòM믬õæ㟌 [Ò“áh!+:ƒ8…ºè³^Dçι¨Z˜Vh„“Zu³;J@’„Ĩ?Œœ˜­ñT¢Þ%¼ÓûÑë%ƒyòµP+zè(GÄ.A(¹“÷HáJ;Èõ¦*ÑaAYwzxâ=hàÓô‘wœœ"pƒßk´îUÞK]–úAª¬Œ®«Àd_B[WU)±
+´9ýK­JsÏ”ÄqÒ`&iÜgÌFƒÉõ5\êK5ÃPNæTIçõ•‹¨¶+#µÕF¶Éhz#{=Oò8’È+4ónpnøܹéÍ‹¦ú:\ý”J9ù¨\Þβ`ëÜÞLh ŒïÇV˜Ë%›ÝõÜ LÞƒª7 4gÐDó§”훌= †÷Ç@óÒ ‡¯xîIÛ5¢³,@Bî±ÀÛ
+Sø†Ûm
+ø”Øà¼syþeÞwÇ/Š3ï¼»½žABKÁä×rµÞ‹ùÂ4ÂL¯³Éôêüââënêi!ú~4,„R$I% ç÷#(UÀIÅ0®b¤ÛÂR–¡ÝƒÙ¡ábõ|ž_m`
+ܯV[b$o“EsO•¦,dÎ"zXõ„ ص®K&°ãÃdyêö]šèó‰ no©&¡=®“ ïEYãnÝ<Š
+\Šº¤ ÛM‰^ÜѺAî+RùÚ­àÁÛç\i‡ã§u`æHKAÖó–iÅÃÊÇÊî¦4§„/X:a¥ŸÈ†ãA?¾g›¸ÁBŠ_š¶_ž™ σÇ9<4ê|sýÊÉðÌ£ŸÕµÉñìXGš¨\—T°±B×®ª]¨ÌVÃïš{´yÉeø|=ñ´¥SêļvrÞ|ü£!‘¤Cúò¦½éÆ„¿oùh?Â?Ú’QŸ¢£Q¿ÇÁIÏŸ¯~ ÷³?>d­¿[ÿŒM²Åendstream
+endobj
+260 0 obj
+850
+endobj
+261 0 obj<</Type/Page/Parent 224 0 R/Contents 262 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 3 0 R/F8 6 0 R/F9 7 0 R>>>>>>endobj
+262 0 obj<</Length 263 0 R/Filter/FlateDecode>>stream
+xÚÕUÁNÛ@½ç+F¹
+‡L×KUj“ˬ´ª+j ¼«Á5‚A)€¨¸¶.l ö݉ãÛb¹puÀu…šÛ\IèP.—Ú{Jž[nk—h _í”r‚åRp-@·|îÔ &|¹¢ æL¹\Õ‚ÃÙškžYÔ®‘×XúAFÉmß#·²)méþ3ç 4ýE¹$3ß{²0 £–“ÅÍ[['³\*…çšcÉ¥Í3ª¤²ºDiÃf–´c7ËÀY— Ããf8»Íƒ šÍG\òº°ço.ÁL=êîpø#ÙÝNíJã…Š6±Ò¹t6ÞÞL¼ä΀Nj»°A‡£VhÈv9oð¾Fc÷ÞÖºªŠw6v‡O7|{­ë¿¹\låÚ’ÚoBtÔ}ÚF‚¯—ðn”´¯5™Ñ$-à,\_N§_'CêI¯}6:¼t)Jo½öSnƒÔæf­šël«¹^Hßà£^ ¹K§¯>„Ic@+I•¹u
+endobj
+263 0 obj
+633
+endobj
+264 0 obj<</Type/Page/Parent 224 0 R/Contents 265 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 3 0 R/F6 5 0 R/F8 6 0 R/F9 7 0 R>>>>>>endobj
+265 0 obj<</Length 266 0 R/Filter/FlateDecode>>stream
+xÚÝUQOÛ0~ï¯8ñÔ>$ÄI–v}ch $ ÝȤMÊ‹›¸!±ƒíÀúïwvÒPV„˜x˜R)j|çû¾ï¾³ïG|D‰ýåõèK::>‹H×öS2‹ý¤Å8-Qc_µ†i//©ÒÌųAM«håUTlZºa1,ÕP0ã_V
+endobj
+266 0 obj
+676
+endobj
+267 0 obj<</Type/Page/Parent 224 0 R/Contents 268 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 3 0 R/F8 6 0 R/F9 7 0 R>>>>>>endobj
+268 0 obj<</Length 269 0 R/Filter/FlateDecode>>stream
+xÚÍUMoÚ@½ó+F\’Hµ±Á…4·4j"¤$Ðà*qY¼cpb¯Ýuÿ¾³kcÀùhÕ\*ÀÎç›yo÷©ãƒGF} !Ê:ßÃNï2
++rÁÉLibëÙ*ºëpÅ8,Ňu¢WÑöe@±4=²³í]~?0pˆÎ`àM²ÀýJY/î§3ç
+µ3­¶£àζQÞq§5æ8OÓ|ˆe5+u0ÖŠ
+7P
+&íÔ Ò÷«µwyeWàŸÁ¤Ørî¼Éo| ”­óL3]*¸A¥Øߢ…Ò´d&y=deOLLVÅ´IxKFÉR¸fbY’Ýp.VL²È,z†ú­*ÝÝœˆœ‰o]¹oUj'­Sw§€£Šè¯• 1²Ø®ïn¿Å‡zªãénrŽ©­5ÃŒ D”)H±B[pÎþÀªéöÏ ÞLFÌïر’O£ÏƒÆ©2õ’HˆJ) ßk…Ĥ,dtEÔòßQrÔP2ðªK) fZŠœsî4­m ð!!_…å oæÇÞKàyº17òëæ~£ö²œ'±½!àK¢´áuc•F7„8kéÉÛWÓÐõ_wqWÍêÓZÚªü-=í׫wó)5ýŸÜ÷?Áýý×eû
+•21
+}ÍO>xHnîga5þ çþnl mQ`dhØý2ë]žÖÄðG}·z£m¸Ù.Œ³"Eƒ¤ZÁ&ȶRåíŒúÞß^çÁȼþÖݘƒaçgç7ÉŠždendstream
+endobj
+269 0 obj
+768
+endobj
+270 0 obj<</Type/Page/Parent 224 0 R/Contents 271 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 3 0 R/F6 5 0 R/F8 6 0 R>>>>>>endobj
+271 0 obj<</Length 272 0 R/Filter/FlateDecode>>stream
+xÚÕ•MoÛ0 †ïù„OÉAnœdI·[÷‰
+_öáS`³e¸pÑ›ñ›‹ø Da\æPŠm‰z3éüý“!«J gq²>ûtrzú LÛ4d4À¡£†oØ’[-¤ÅxaéÿBTä.$E\ª4Êë4I)š‡3§¤Ñ þN>K •ÄÀåi Ù¬Ó`&Š›.¿T÷„M©Ú*‡Õ°O>”14ÍX®íѶ ¥ñŸ÷-Çï"CÖjáºE?}—©µËçk š{(A„°~$Ü49sí>¤Cäãøm×›Ä;;†ñ,ÃÆ
+¹eÔãhR¥*äò`[Ò©ôÁÀ­Õ"m-ÒÙæ"ãN®K$> Ä)•õ¬= ¨ô3ûàèûãëAâµ›³¿Y(W€Åö©íôEì„¿Kë”tMv%Á¯Üî…ªTæýžŒƒËÛò
+-PÅ]ܽl5ÝÀÛ®=eöÿ5\žçÂAߎöíe£Mfýå°7 Î
+endobj
+272 0 obj
+671
+endobj
+273 0 obj<</Type/Page/Parent 224 0 R/Contents 274 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 3 0 R/F6 5 0 R/F8 6 0 R/F9 7 0 R>>>>>>endobj
+274 0 obj<</Length 275 0 R/Filter/FlateDecode>>stream
+xÚÍUËnÛ0¼û+:%©–­Úini’òpý(PÀšZÇ $R!©¤þûã¸NP —ÂC|ììÎÌ.:)té—°ýȲóuÖùôm
+³/ N²äfùÑíäxvO{Y»×…¸7H2ÞŠ,>Ôè¼ÒwqíÐÆZ”ç¨æ,ŽR‡Þ¬€Wé£7\/ŽO9ÚNŒÙA
+µ‡Ûñlt{svuõ \]U´èÀh
+á×è„÷V-kOëÞ€«PªÕ„!%:ÒhoM…rVƶPi?é1”ÒÒ””+TVܽYº¿‰$Š&Qˆ(ùôr2@9°„k1g@¥s%…GÊFxE\´a‘¿Ìæ{ˆÆB`#á˜h}U÷ù|<Ïò<sBh¡å$Õd*¯Œ¦¸X"U
+”±ÞwÖÔ•ãìvĪ9NZ8¨„õÏÉ¿ ¶ïÜïÒS¸­Ð
+ ζáùì®ÜS/|íàšl*îðP8/t.lΖkðx…ï”áN²wç†6-1y%ô]Mû¤Pçka…䬧è¡D/ Ä’S‡FÍÍÝ BÇE:zÅ›#O³Ã–¤½ÒÄ¡ljï'i’%½ý¤ŽÆc¸69 ÖK¡½’ÉȺ¤ÞqÀ•l:%놔%à Êèq«Ë–öw]pè¦Ù
+¶8êþκÝlqL´—äv6:àoÕ µç•5eS‡kší=¿“ô æ$ôćãa×îA¶mø!ßþŸ.K?à²Ý÷«Õ7®­â÷ŠþÞ} ®çÓYP€‡ð|2ÚNÒð±.Ï35}Ò
+ödAÖ¾©Íô•UœVЀj ü›w"öèt<ìuÿqfCÜœN?óÂå¬ó£óà·}»endstream
+endobj
+275 0 obj
+786
+endobj
+276 0 obj<</Type/Page/Parent 224 0 R/Contents 277 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 3 0 R/F6 5 0 R/F8 6 0 R/F9 7 0 R>>>>>>endobj
+277 0 obj<</Length 278 0 R/Filter/FlateDecode>>stream
+xÚÝUMo›@½ûWŒ|²(¶sKÓ&Š”7¦R+qÙÀØÐÂ.Ù]’øßw(qâ4”KUÙ’íÝ}3ï½y‹oG <z1"ûN«ÑÇxtp²
+E––[¸Ù¶”ÚeõèPSœ p£Þ—íK½ÆÛµy÷-iêº,ð囲ÛOuýÞuKþÍL³wdz‡ê¸,ª‚˜%› éI&ìðâè[2M¦/éJÉwaàjŸ]]Ÿÿ= ›
+¶9læÓYgæ{ö0‹ì¢¿X¸xoÿ‡þ¾Œ~3f€Cendstream
+endobj
+278 0 obj
+681
+endobj
+279 0 obj<</Type/Page/Parent 224 0 R/Contents 280 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 3 0 R/F6 5 0 R/F8 6 0 R/F9 7 0 R>>>>>>endobj
+280 0 obj<</Length 281 0 R/Filter/FlateDecode>>stream
+xÚµUMoâ0½ó+F\J¥MJ
+-·nµ­ÚÂö°OˆÛÄIm§lÿýŽ
+íTš ý¥ÐTJ"÷›õ-†nÓêÙ^ÎôC–=ûcà&.Ç5þè5þÿË×bùôJÇ*„l164í€]˜1J¬*ƒ¯TPøX¡6Ƚ]¶BÍ4|ÞŠ/aÜêü'':
+m芙âÄÚ­Øž¼îi[ð–6ËàšÉuEû.R)S,¶×<GóÖ”î^/¦br[×u¾ÜõÑ^ÖÝ=ÐMGÓO"Ò0v܇~à‡þ å†FÔÉl7ÇÌÍšcΤ1TÄ”Wi9ï¥`µºƒ1ÌšŒ»ë€éêÞú}¡”u„v=€Oó ‘ ·¯RÊz¾IIBéBÖ~÷æíÌöëç;ôÏj·œsî5ð¶VøЙ­¦bçŸe¯ÿ'ì÷#z7·!‰›C@@0ó‚‹D4dðÐƺ¼UöÖó¦ŸIŽ¼¸þ˸ÑÛÜ‚wWËøå¬m·ò¶ŸÖ\Ú—ÂöÉhœ\ž6l‚aDDg£Z÷/5ÉË ­7ëi„–LìlŒTçýO>QáÈþá»ê`d~,:?;iO‘¶endstream
+endobj
+281 0 obj
+787
+endobj
+282 0 obj<</Type/Page/Parent 224 0 R/Contents 283 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 3 0 R/F6 5 0 R/F8 6 0 R>>>>>>endobj
+283 0 obj<</Length 284 0 R/Filter/FlateDecode>>stream
+xÚµVßoÚ0~ç¯8å©}HJ
+]ú¤Ðú//:Ÿ²ÎÙÍ
+X̯gó¨X†òš´©(aS.„z’K žtÝIºû›Ö@­FRk8j4LûÃÄ¿£«Åt㢔è·îv@Ðzû0=ï‘m|Þëzãt6^×û‡ôA¹|Œø]|U¯˜¡-©ËÑ[_g/ßïOÁendstream
+endobj
+284 0 obj
+775
+endobj
+285 0 obj<</Type/Page/Parent 224 0 R/Contents 286 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 3 0 R/F8 6 0 R/F9 7 0 R>>>>>>endobj
+286 0 obj<</Length 287 0 R/Filter/FlateDecode>>stream
+xÚíUMoÚ@½ó+FœÂÁ[¯mÙ[J?„TZæÆekÄ•½vv×mòï;ëu $nU Eê¡â
+I
+fÝì!ã,bAÏÉCg^/ébµ‚ecÙqm°Òuª³¶Bi˜ó‘ÜO¬ž•.òÙÔùÏ}çÉ;,Ñ`oËQõ?Fà%®>ºµ»ò"ßOvÒ¼ª¿Ûá$àC¡ÍL£
+i•Ì:Ø^ÕU7Š~Ô«§ ÷qõÂÅ¿®Ëø
+endobj
+287 0 obj
+544
+endobj
+288 0 obj<</Type/Page/Parent 224 0 R/Contents 289 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 3 0 R/F8 6 0 R/F9 7 0 R>>>>>>endobj
+289 0 obj<</Length 290 0 R/Filter/FlateDecode>>stream
+xÚÍUMoÚ@½ó+F>…ƒÕ@niÔTT¥%`n\6ë112kgw­¶ÿ¾³þÀP›5ª@âkÞ¼7ož†ç.=<"óä»ÁÇxp}?oqJ¿D£‘Aœ\…ŽçÁÝj¾´o9ÇRÛ_ŠGßK”Lg…Æ[Â…@E5Îö#'4¸ø OaEƒõ•û3tÝñz
+µMåV)3¡QÚ™²YÊÄÆÞЦµÌ+  вBH iPF
+J&µ)Ñç\’ ßM;ZGóÙ4ï¦7n÷ ¦–¹/þÆt%Y_™ØTlƒÀDwOL2nœXâI{3…ÕK¶9Ój¬yøƒhZÛyÛÚz1¨‚§4h&h¹¼V8žCþm«u`:ŸÃ¬H0¯¹–¸cBgœ:¼Ú¡ÐΑÔ}P*™Y%zYÏÍÃÉo¡a¶ZÆý¿€Áj1íÂoŽ‹mL~ÿ\`TY…'ÅßÓÒ‘]—¥¦¥*˜¡R´ÑsV*M»a2¡t6|æƒÙ5˜ãýŸô/`ë„®3nCÐF`["{ó<„ÈÉÁ|õ}LY®þÑô»y¨ú=ä!ß;Èëûq;DäB4þÐxaDÁtWæhÒ°‘^J’ÙÞȧ:{仦ØwkzÏu&à¾ò_õS<xüºeendstream
+endobj
+290 0 obj
+572
+endobj
+291 0 obj<</Type/Page/Parent 224 0 R/Contents 292 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 3 0 R/F8 6 0 R/F9 7 0 R>>>>>>endobj
+292 0 obj<</Length 293 0 R/Filter/FlateDecode>>stream
+xÚÕUKoÚ@¾ó+Fœà`㗠ͦ¥¢
+‚#µ’/‹=€£Åvv×Júï3»kœÄy¨RT©À;¯ï1ÞÛž}|˜Ž!=ö>ǽÑ,߇x§Ï"÷ âlúL)‘ok…ÒILHT}`Eöì `ªŒ;œûší‘"ÚC`2”)ýÅ ò$¦*/ ]ß\ßô<püÐ tÏrŠÚÎW+X”rÓkƒGV¨<¥JeZ±Pn“ŒÝH§õ+‘
+…S‹¼É€¾’áy'HãIyNé°¸ÞÄ ëªâ¿Áõz»R˜Î²Â4ßå4kSèàô3åLJÓÛ!¢ÚÂßDYWœÃª‰[no%L[tÊÓŒv^©˜BçˆRÞ’Â{• ÓŸÉð]ËU<_^M//Y9Q­¹|¤žÈÎò”©¼ØF]·f2IìŸà¦µº\3OG 3Á;š}?Ò1ÈÃÐë
+endobj
+293 0 obj
+663
+endobj
+294 0 obj<</Type/Page/Parent 224 0 R/Contents 295 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 3 0 R/F8 6 0 R/F9 7 0 R>>>>>>endobj
+295 0 obj<</Length 296 0 R/Filter/FlateDecode>>stream
+xÚÍUMoÚ@½ó+F\¤ÚñÚ¤¹%i!%*µ—=àmíµ³»Í¿ïìÚ`iÔ(—
+àùzïÍÛõCA@ÑÐ~“¢w1ï|ù,†ù’"ÃSæ‡0OcŸEôëòÛtæÍÐxŸpÉëÜÀꪔóŸTcM¡ýØÎ3„e™çåZȬTYWÊ%pc”¸¯ jà
+A£Lk¨¸26l¨ì¯ÃÎì´W¶+°3˜T¨¸¥„óm{›KܶÉ3ÃM­áµæ«ƒV®6\¦\¥ Úyö‰­)š¯æ–‚ŠçpÍ媦8P9\f\ñÄ bðÒ”~§€—P²FÓw•»Ù´öò¶uÿ™nRÔ ýÅ„$ Ç=ò™ûa;Óíf¶¢Ž§S¸)SÌݬ\‘P§2© ”Æo6IÙMzVº8ðO[ÄÍV®ÜVEB0¶¢¿êƒ²r»«Åqð;‚‹Å€'¾’¨åùƺ®ªR¢˜ºJ¯VâH“§TFõH"/Ží€=ÊÌgŽe&VªÅ ãÖÚÛ‹"¸aFÉ ïð¡FmÞínâ |Ùá»óT3ï]ÿ?íÈÞaǨýÖIεîÓÚÍS… ~áÓºTéb𵄤—&Óùxr{~}ýc³ÂÜš
+\¿M0%¡Ï ?¬3‘dmR³ËÆ¢˜ ËE!Œ…$¤Á•ó$;»9ÿ¾¼–É„î¤×ÙÌ*&ëâžš“~T&ãæuh­­0õº}Y¤Œ¶<YvêÁ›å£Ïü’6 Ó: Ýcw4Ú yc•Í;cÇ<¯Ü¢)l:ýpè}7®i!•$˾Xvo…æO7ÅÒeî íf<…{DÙÖµ0Qt¸,É#º•ŽÚ;ä´½ X4¤dºûÑgUŽÖºÍ9¥:ò¸-òØ(¤\o69tt !ø×ìçyïkï§rendstream
+endobj
+296 0 obj
+724
+endobj
+297 0 obj<</Type/Page/Parent 224 0 R/Contents 298 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 3 0 R/F8 6 0 R/F9 7 0 R>>>>>>endobj
+298 0 obj<</Length 299 0 R/Filter/FlateDecode>>stream
+xÚÍUKoÚ@¾ó+F\Rí°àå–&m„”¨ÔJ\{À›¬Ý]Còï;»6˜‡ÚæRùb{çñÍ7ßÌþj0èÐÃ`Ð…^¤ñyÞ8ýú X
+fh^ÊÒ¬ðB2ÖhšÎóð -C{²
+Ý|›†uHŸH‰ÃÐÕÞó™Oͪr²^ÙÁŠÔñt
+×Y„ÒåšaÂS#BŠ”…E‚©qÅy‡„•ìvGP¶&Ë;JôÃŽCtmTø«@mß!nJLP„‚°PŠr†K2#(@ÆT–Íä—¢"5¬¨¨ ã+=~¬2^èºùojñ©O¶Ì¢Õy:óE›@é)á”r'E™…ôõ|Ã…äK‰@î°’P“³5ÑTgBql†#î™Ï\Õ±XǨíº²jμ^Ïïïê"ã§(oKß=cEžK¯Ï™KVµì]3öN{ÇD@mJ‘B¶h‰ÔàšêY´ØèúìÇ¢½h¿TWH¤“È'Óùxrsvuõs× Mù…®
+E&#>¥ým,hHª7í§ß«{dÁ1êìd÷ø¸ÍTD÷×
+endobj
+299 0 obj
+788
+endobj
+300 0 obj<</Type/Page/Parent 224 0 R/Contents 301 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 3 0 R/F6 5 0 R/F8 6 0 R/F9 7 0 R>>>>>>endobj
+301 0 obj<</Length 302 0 R/Filter/FlateDecode>>stream
+xÚÍUMoÚ@½ó+FœˆT;^ã8”[›4Ui(˜—=†l¯³»NÛßÙµCøjÔ*ªTxçͼ™÷fyì1èÅ`ÛwZö>&½ó›÷À"Hr:‰GÌ!É‘Ï.èÛÕr¶ðnÑx³Ùµ†9êZVÏ’BEÀX‹òÂØ,*Ù ä²(äwQ­a­dSk9pc”¸o jà
+Ac•×Pseì±!ØéJc[j§À­M l w5*n„¬àÃ6·¥®¶Á ÃM£aŠZóõQ*ËU^e\e ºzö‰Å”-Æ?À|¥CÅ ø«uCç@p¸ÚpÅSƒ
+hNU鿴塞Ñôr÷ jS{E—º¿74 ê”~b¢¢¦®÷¡ÏüÈ»šlØj×Mt2›ÁTfX¸Z ,yeDJ™dÚ”Xל·;°vºáHƒWæê&‡N9… jC¬vÙR9" ¤RT žxAad ÈÓËŸ‹¢¯óߥu’#þ¨s`ܺb*ŸÐû,ï_DÕ€û¹5Êjüˆ‚àzu%Zž@º‰'„Z bù@áF‡Lä9:â] lV`>³ÝÌ{#ÖT«³—nºmò†C?~î… ûÌæíìÞ¼LM]O/Ô¶X'Ô›öéÿt?{ƒûw¨ö;©½F‰>éMdÇ“|á‰Ì>´k+u+Z|,ÚÝü·zº<6û©)¥$!ùmº\$­¢?ɇËùÄíŒíGטŠÜêüìIË;ÓN®¡jÊ{Tï ‹¶O }àH·_{ž <ùoø£JíÈó›QG‹]†ÔJ<r×…ÛÏIYhÕmT˜lஹ6Ö» FÎÛŒùüÑßݧ¤÷­÷ G 8¸endstream
+endobj
+302 0 obj
+647
+endobj
+303 0 obj<</Type/Page/Parent 224 0 R/Contents 304 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 3 0 R/F8 6 0 R>>>>>>endobj
+304 0 obj<</Length 305 0 R/Filter/FlateDecode>>stream
+xÚm‘QOƒ0…ßù7{Ò‡v´0 >:Yâ
+þ€Zî6 ”I/ÿß[FŒ1¦I“Þs¾sÒö+RóRkH2°}t_Gë]
+JA} £¬HeusS‘¡Éý7'¼»­?£„ÎdÔúŒàɸƌ Œè/ƒóó$0ý•‘˜GÓÁ³q§‰u`¶g3K8B…ô_ËÊíÇDè…e³GZÍäoÁ]£E·D³ãGã¡Aoùˆ ´<Zj‰T2•zéT‰Ô¡s8qí¾,á04ØÍ]öÆQk9i°SŽæË­wÅòr*ÉdJç¼sÈö½¬`ß_: ^3×q0‡L¨\³Oä:æTª wF<!‰²|ðð¶¼ið§yø“Ù«7aðXG¯Ñ7¿ãŠendstream
+endobj
+305 0 obj
+292
+endobj
+306 0 obj<</Type/Page/Parent 224 0 R/Contents 307 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F8 6 0 R>>>>>>endobj
+307 0 obj<</Length 308 0 R/Filter/FlateDecode>>stream
+xÚ-ŒË ÷÷+ÎR`/µÔµõ‘îPñŒ¥‰¦@ ü5gV“Éy£úŒQëÂÝÓÖÒê°3ìn•ÔhU;,º«¹ ÷óä¼ ù–1 ŽèYÚ'‰_+þ±ÒEÖ̲ÁW¬%7R¡œˆ£Ë˜]ÂÙ¥9†äJ»·t¢7ø$žendstream
+endobj
+308 0 obj
+127
+endobj
+309 0 obj<</Type/Page/Parent 224 0 R/Contents 310 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 3 0 R/F8 6 0 R/F9 7 0 R/Fc 8 0 R>>>>>>endobj
+310 0 obj<</Length 311 0 R/Filter/FlateDecode>>stream
+xÚ¥V]Ú8}Ÿ_q•¾0Z†ÀÂt¤ÕªóÁv¤Ý-[©¼˜ø2¸$vê80üû½vL€@ÛÙY!¡$¾¾÷žããc»èÂýº0Œ¡7€$»¸^tFï!¾‚é‚FÃ>LyëWø`ŒóÒ`q9ýJ!}èv«0D.èîi<\«µàX@ÆäðÅ ,„’Àêù`ež+m +S#òãZ$X´a<¾‡…Híca˜äLsøªæö›AM_s-¤{²}Põn/ŠmuŠÝA’²¢À"ªZ}Ý¡oµ];<Qî]É×
+CRA˜)N³ß€ƒ2èƒ çù FÎ+ }K*ßñÀ¼'×æàÅ×ð¿ÔF½' ‚ÇÅÁ«]Üx¹¢ÚR™ÚÎåi–ý‡AU ÒùC£ßmPÊ•Tù†û;~J-ˆVú6ü<‡¥ßÊ=‡ÐnìC&l–˜’6 ed¾ê—»BÑ€Æ1GÉÉ
+)´eÓsNúv/l`Ý:næf«3p±7œ™eǨŽ}“,ÃàÍÞùššš¥â7ŠÛWlÃf‰t€UƒtiØÒ¼ÓvÎXùe1@TTz+‘Ñ5"£''½ÿã¨?ÇrÚ_5ûÛïsVòßÜRÿBý ³õ/ :íªÇ ’T5êUq|>’‚æ]'À5ÊÀm 8÷ilÞ% P Z®Yë˧¿;_>F°$_)–lEŠ]VùìÕ#hÔ›µî¦“ÎçéäxBäÃwÙÓçÇ£›ÑÉ­Èñé]À1š±‘•Xº :÷™“ÍT_&ìü9ÆžMªãÀf×*Ýß®²Ç^#ôÝwF×;¸hàï‡kaا+;퇇éÅ?ÿÛ{Äendstream
+endobj
+311 0 obj
+1079
+endobj
+312 0 obj<</Type/Page/Parent 224 0 R/Contents 313 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 3 0 R/F8 6 0 R/F9 7 0 R/Fc 8 0 R>>>>>>endobj
+313 0 obj<</Length 314 0 R/Filter/FlateDecode>>stream
+xÚ•VMoãF ½ûW>%…%Kòg],ÐͶݦhQ·õŠ8‡±DGjF3êÌ(Žÿ}É‘;JÖI‘
+)aƒ ·àȲզäàñˆ€ ²_¢Ëu¶oj‹F‰•°– ³ïosmu³¨´q·ÃƒV×&ÅÛþ
+uCó&˜„eã|tΛÚQœ÷‰¤Ön!lÙÃaôìÖ)ì° pZ¡%ܪÂì;ßä©…¸±Ž¹·NªˆózÃ2ùR¢4àó+!i>®K}…†ñ|a
+1c”šb•k ›øâŸ@@wŒ£æD&„ñ‹ÞÀ
+i‡˜õ#OQcŽ¢„“¨¡‘bÂ6R¤÷•ÔŽf¾ÑZ¢PëËçÇÖ¡þ!IÀV˜Û‚70Gž3ü¼ >ÿ:LÀ;n I&›ëZf|/´î2OÜA¢ƒ6ÕãÒ‡û1T×!ãgj\_òÕ–÷į¢–Ú4£¶¹È8Q[Mv¸0Χ8[!-ò^7Ú±µtð dMcµüÞ7|AãhN4&°1Å]îZ¾óšõE´H¢h}ù¡ÇØ×å4m`Y;º¶òÖÜ]WíªÚqôH)ñÂÇŽ{EÝó³ x‡%ËsDåò.»© Е4
+endobj
+314 0 obj
+1036
+endobj
+315 0 obj<</Type/Page/Parent 224 0 R/Contents 316 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 3 0 R/F6 5 0 R/F8 6 0 R/F9 7 0 R>>>>>>endobj
+316 0 obj<</Length 317 0 R/Filter/FlateDecode>>stream
+xÚµVMoÛF½ëW |’
+ø~,ê>ûK-ÖŠ$MC¿×°Š†2ô’t-ƒ¡Ó47$ìèóŠÒO,‡=§IE³ÃâÅÁ$´WŠÒ\A!­Øpð>ÓQD„c%,@*?d'ÑñäžáÐß—„«>xp§p]¸®}¸3°l ìÆ@‘˜šþb`>ž‡)¥±ï£ÎRD®[ë„ñsž˜ÉôF̵z:Šª|]aÉêV’¶XT)¹wÍšàã—öyoø.éjVô]K¤Ê«ÁÜ;|tDZÿ•¹fßž¦…¶bº†K¢£%¡gµ!Ê/4ñæZºx¢vN7ç—ÞôîÙ]`àÜù½¹fK¤¿VSbÚÜý{üÖ‰š6§(›¼
+endobj
+317 0 obj
+910
+endobj
+318 0 obj<</Type/Page/Parent 224 0 R/Contents 319 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 3 0 R/F8 6 0 R/F9 7 0 R>>>>>>endobj
+319 0 obj<</Length 320 0 R/Filter/FlateDecode>>stream
+xÚÍVÁŽÛ6½û+
+ØÀZ+É»ö¦@iÑ´Z`ƒ¸@¾p­‘Å–’²³ßGÒJ´îjäX€dsføÞ›y”?L2JñÉh¹ò×®žü¼™\¿yEÙ mJ¬¬î²$§M1»Mò$[R+ö¼0r_9ÚÎdãxÏf;KüóõßÛùv>ßüƒôʲ˜¾ÈWÉOßT<ÌÎùÐ9&ÛòN–’-9„ÄÕZ˜½lW«±‡ÅVë¼æPe³«¶ó„|é‚KÑ)G¡:&Šy,@-#‚rÕ£•;¡N›$ñg‹å2Y}"|A[ötÝcË9ýËGmŠ/áêÓžcz¬ä®
+!
+*¥âÈÈvm«ã¢ßÐ’0LS¡ÔôŠ¦|àwÑg4§º(¦#¢„ä Œo#t§Ûol°Ïk¯_û~š»¢–›£,\õ TûÌ1ª=hRŒãZٱñC€•ÐzÙìé÷ûÅo\C¥]˜¸ÿæ™Yš¦
+1Gžê
+¸á(!¥ ŵ¡,¿#YR£‡(dSjS‹ø ç„TâÞzYš;4„|¹=h­X4—”ä<ow`CŽ?žzK¶Ò*èãô¼„gY1nÆÛ»·ÿ™JåK?<ÛÙ
+×™(úCåizq¼¹cS¶Ó
+¤O-ôyæà×Ã8Ì$Œg"6^ uåÏcÌR k}ðm”lH¨”oS%T~tGÐÑ”Ò1wÀéäÕãˆ~Øþeõò”,®Ø—3½Á/kwÊ®_µHqäNàý{(„²Â7L`Í…^ÇÈ!Jx¦ßpáBþÀø§/¥ó©?øîô’¸òÓ ëïAÜ[m­„=i›A¡gZcÑC›œíý¬Ì]ÓC*úø—uÒ6F'žq8TžV»~swÒ?[çhÝj™ÆÚ¿üuÿžÞÖ­b uŸfîíý½O[ÄèÅ=Fð2¨¸ÌVÉšÒ¯ûgõëfònòºendstream
+endobj
+320 0 obj
+848
+endobj
+321 0 obj<</Type/Page/Parent 224 0 R/Contents 322 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 3 0 R/F6 5 0 R/F8 6 0 R/F9 7 0 R>>>>>>endobj
+322 0 obj<</Length 323 0 R/Filter/FlateDecode>>stream
+xÚµ–]oÚ0†ïùG\ÁERt»è¶Nª´NLeÓ.rc’’’Ø™í”UÚß±ÐòQ@“¦J8çë}üÆæW/€ý0 a<…¤ê}Xô®>¿ƒ ‚EFO¦×Â"LüÐØHVC<X
+Q"ãñp¸x¤ø‚ÀÅ{áÔLü"GÌ´–ŲѪƤÈ
+T°ÉQç(¡|eÁiIå¢)SXº¬Sêb>½×²ÁxBº[ÌX©Ì*âfjŒÝ µ,¸.¨®Æß²¢D僙&ÅŒ5¥†'V6…SÙwHð¬ük'x óù'¸éæWÛЖ79­êPשǙn$+½’ñUÃVHó¶K_Ú•3ÈŽ9†øÁ6
+2²4;d«*쌣£öM®ÜPa<'“‹Hó]ù»|;-¨Ó§P>m‰%ÓÅÉŽŽ…’-ä³;3!7L¦{TÉTNƒ^¹wÖʤ*”O/79u[…Æ8ubF0oß®‹N͈NÍG±ôTŽ¨•×ÐdJ…Ú¸å¹Æ1¬ñy#d
+Àq¼¿ùùÂqzÈ1|ü>€€ŠoäÅŠ®š3ÜLñÖtÏ—Œsj-9PŽ€Ù³…î0¢ìŒÉ2Y’›v öлۨÏÇþN'¦ÉÍ]——ÛÑ_'ÝQåW‚TS×äi{£þ?°×m|0 iŽ)ý·96㮪K¬kò'y–¸›ÏMšç¢½Y8:ó#šEd25ÌÂí¢÷­÷É}ºendstream
+endobj
+323 0 obj
+792
+endobj
+324 0 obj<</Type/Page/Parent 224 0 R/Contents 325 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 3 0 R/F5 4 0 R/F6 5 0 R/F8 6 0 R/F9 7 0 R>>>>>>endobj
+325 0 obj<</Length 326 0 R/Filter/FlateDecode>>stream
+xÚ•—MsÛ6†ïú;¹Ô>Aˆ¢zKœ&ã™ÎT­•›/ IH@‚% «ê¯Ï‚зĴ=òØ|<Xî ìþ;Š€àwqâ~²bôi>zú Š`¾Ä'IÊÂæùÃ|-à»^f-„5ÙT•®­È[[ËÅÆ
+0•ÈäR
+ÅüKÅJÀ‚—¥¨a)•0!àDµ€­T
+¸ÚòŠ-(Á]ŠÇù÷ ŠCê>}6gÆ•ÂÅ?”8àCè†<}™BÄ<uÇaâCÆPÕ²´¢ì®ðöà~Qå¦x{ô#Oû h²Ã~/ÆÝÛç^
+ã}¦qa &>Ÿ¤u£ Ã÷ŸÏÂdµ¬ÜÒîÉó ¦Ññ㟯ÀÒpãdbr‘?)Á}á’¯wÄÑ8:ªišà:'õE>zݘø”$ÿÿ5€$öx/¸·cÀ}´;0™ËñÞ˜ú¦ŸyG8z W‹B[}ceÙ¼Â.Bê‚ß›°Q·FÄ¿Ø#!» |楦,Ï~tÁ†ö†kÔmplJ|úáÒv¸L+]wÀ1<¿ho8¯n…›s´ˆ´ ¡#Ú®ÁH|à—ÞŠ|åL k^ç[ô^wÂXÇ«[¹ÙôÒ0ô–•
+½±xät±±x€_¼º•N/ýÂnÙr Kw7dºr‡î€ Òx€m¼º”L/m“þtç²Sùãy
+ž.…È%‡MV÷×àO±âêé#ë‚%t€)¼º –¦é¹)h'lSz±:ô—׿‚秴ƒ›¦t€c¼º•;9KKÖI­x½re)þ³7mBØÈ«[iÙäÜFé/x7ëë}º‡7òÑU²Q °–W·Òb¥{²–Ë_rS²á!^TJfÒbÿ€Èx:+} –âùÛc¯2“Òh€é¼ún5|ês\Qžúvѯ7?î6;Ém³óöðümö
+Öý®—XË6iš#¿Ø©CÂæ_lîÌ’­µ6Gµ9v@x‘o%I®6r‹_… fR\õCGÍó~´®\ôñ~Ý7S×M `õUZéűí Ûó¡C%Ž¸Z<úÍì{ÌtŒhâÎÄ„2&|/˜"¢ÀEýµ…ßËlæ†^Lh“PqÓı›œôîR1±þýy@érendstream
+endobj
+326 0 obj
+1069
+endobj
+327 0 obj<</Type/Page/Parent 224 0 R/Contents 328 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 3 0 R/F6 5 0 R/F8 6 0 R/F9 7 0 R>>>>>>endobj
+328 0 obj<</Length 329 0 R/Filter/FlateDecode>>stream
+xÚÅUÁn›@½û+F9ÙR!^ÀàÓÈ•,åàÆøæËÆa+XœÝuóûe¡ÆvJõqaæͼ÷öeÄ`Jƒ$€0†¬}OG·?î€EîéM<g~
+²’kíÛA¨=
+endobj
+329 0 obj
+528
+endobj
+330 0 obj<</Type/Page/Parent 224 0 R/Contents 331 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F8 6 0 R>>>>>>endobj
+331 0 obj<</Length 332 0 R/Filter/FlateDecode>>stream
+xÚ-?‚0Ä÷~Š7Ò¡µ¯üsÖh†±n,DC‹@‰_ß6š»å~¹Ü- A!¤EôÓ²“a‡ëÁô€¥–”Zé’ó£¾CeßYr¾õãì`kn^Lüºâ_N³µ*d| —™Ìa¥e§ÍnûF«p­%ÑNÓü¡š7òa2ÒtÙð†Ç‹a7öí[.èendstream
+endobj
+332 0 obj
+150
+endobj
+333 0 obj<</Type/Page/Parent 224 0 R/Contents 334 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 3 0 R/F5 4 0 R/F8 6 0 R/F9 7 0 R>>>>/Annots 59 0 R>>endobj
+334 0 obj<</Length 335 0 R/Filter/FlateDecode>>stream
+xÚÍ›]WÜ6†ïù¾l/p<ú°äË6izèM(!?€ÓÒÃW—Mû÷+if¬‘Y¬Ýž³»s
+¿é‘Õ‡E°^‡Ïhøý”«yÊ[ŸF¦–'<£ì’±`a41.Æ›q5>\É›Ìк7>ëõrÂÚ~H.—²
+›&»uÚªNv&z¾D 3éý—óÏ 7$_’Övñ–Hƙų`²¾?îm©©nûl±ØÈ6aƒçõ¨uÍ&˜ã»V—
+éÓúÏqµ v¾<ˆ­£0ŠŒÅæ ÞÄ ÐË6¥Ø&Ò÷›lBGcCÑ£ ˜ÕQcIÃ(Q
+#5oD¦¥[IZpè)µRc£U@š’ºnÄs±ü<
+'ÎÙùùëqé}3ÄÊVp(œ8_.ÎR¦ÅÇ›»5é;-\:5„_t¦%É«~AtOÉ¡°ÙÞ› W¦dQœX‹7P^'±ž,î@°OOã*™<š ¾tÔ{i]Ç8—]Ñðû ®cœÇ…—Yv†õ±æ4Œ¥T(ÝÖ7í;n9óMoÐkã@°±ÉtLoÑtMÕv˜8øе R˜Hç«Û‡õéo__ç DL^5Rœ€Ó¡•µ9úy]{§¢öÐXã£.6ØXCÂYÖ{Iqq)*dJÅ·\Ϲù/Æ®Õÿþ>>¯#Mëv8†Ü5…µKƇ2¤†PX{4½¨±ªiL$@¢(N°íUf¢7qZH"w äÅøüüf¤¼†ƒ í+¢L^mc#WO£,.kMµz2:‡/A'ÔûÕxµ+" V¾’;³t}ìÂç+‰ Šæ^jˆÄE¯Uuó ®
+Å ¶•è3ÛF–Hî@Lé æQ•õà±”E!°‘UÖÃÀ%QUíƒI8þEq‚m¯ò„ xoäÄ,ü£;xfËî¡C–L=ØÈî¡=y‡®xSÀÀÄ”ÞA ÏcòæÓŸÖëÕí×ïëñyIf¿¤¯8t`îÜ=½(„®’Ér!ĆÈd§¸êª_0)ê¡JÅ ¶“Ôvß(ÕÅô•hî ¸"fß8€ÔUu­OUH ¡nÚï$u«>A$¯DQœ`ÿC]B+m
+4w öÌ/ö.0T6ýµ±yÓÂ!LÏÅÎÔ<‚8q¾+A'T|<?ý5(üa¼¹ú~·´ù/Pñ®ù98tÈܹKxËjAgLc­D¹K ‘ÆZs¹3u“ RD—(Šl¥*&±Ž!ÁÜA·êåÒbÿoªâÂö°!ÄUK[uâÐp%ãˆÚYXÂ*'|¥Èyþ3¸j ÍwØÖÐõ\×l͈aîêDñ>ïÜ%]ÓsÞ¸Z\?dV¼_ÃœÌúÙÞ>çØÔrúªA凜¾jÐ\âlÕ˜±%ŠâÛEjÁŠæ`§ßxÊaì ¹ÂÉ‹ý¯ ªêº!?ÎaC¨ë;®p¶jL¢Kƶ»ºDV‚dêÀèÂ#`ßÞ •wªïó'6²E¨ÞqUë+ÁœXò¡Q܉šöÓ·o¬ìòê28­
+0wp›v8¿~Е,¶:—8lˆ,¶”Ã}Ý!ˆƒ…§
+Å(q[k-h1‘ã² `sórërïN\·ëò“6„¸psu‹ V¢(¢Êí(.±Ušø›;À†KØë›<è—¼8•ìÁÜPX_ñ¦`A/0/Ÿ.Þß]=?/oNf/
+,wðoaáÐ/§/8+6rú‚3\á|Õ˜„U§@QÜ´»*-P¼x(ÀÜÁ¼Ü›<Èò¡&nßå'7lq{àç«ÞÀ$°DQfOnÛŠK`\>`î
+«—k[©JܸÙЗ\ « ›‘û\/Ôì ÇãiF÷¾Ø…짓$
+Ø8`‰ NÀô×ëëñ)½#^ô ï1)<lÚ}„Þ¯T•Wð–Ni¤œ²¶¨`–=
+Çß_dyóºé=T?—üJ¼âjÎîŸîÆéo×â?ŸŸ§I”>uøVû–ôùýä?§tŽvendstream
+endobj
+335 0 obj
+2326
+endobj
+336 0 obj<</Type/Page/Parent 224 0 R/Contents 337 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 3 0 R/F5 4 0 R/F8 6 0 R/F9 7 0 R>>>>/Annots 106 0 R>>endobj
+337 0 obj<</Length 338 0 R/Filter/FlateDecode>>stream
+xÚÍœËr7†÷z
+.åiÜö.‰Ç©LMj4¶¦jÚPRK¦Í[ÈV<©ÊÃ.ÀAÓèdH%U`œþþ@ÿ7å— :!öo:áÊýs·ºøþúâõ»nÂÈäúaBe7QZL®ï/¯ç·Ë~²y˜ü°YýzØ¿ºþtñ·ë 2#–à~P÷ãýÍfd¢¸œ™ÉjB ›1(-'~ôÄîqòú˜Pê>P»Xecí'Še3:yös0Ú~PÇfªü 
+Z¨Êwð[Ü> ½ßéú̯ó·
+¨&ËD0Yæ¾´/$ ã棳[&ƒ›UgbûDÙׯ B5¬¶Ò%ùy;à9Ó h£^q[/ß/ñƒ7Pêm­sû¥¡€¬²`iVòšèèa·2–cÔìÞ«>½[Î÷Õu±ÄP+µ.¡1
+ê=µX?lê43»nܼ
+íWZQh_@B›èA3Þ’8Ì®ÉI ‚z…D^Í?÷Óùú~ºÚÜ÷ËçјfÙÊmblÑ_“ZŸdVÝÔTSŸØÂ÷¤© 1QUÑRHT¸›õ
+LÝ:¯@A= ®ûÕv9*)#œƒ™10t_MÊìôG€Ï÷›i¼#BæT
+è*¦bÖLÅ‘D…›²(¨Øír~÷y»ÜTO®2ÃåawúWc€Šyøv³YöóuJœüÜŠ7…æ eR_@Bûcbº™ID…;'-PP°ÛÝâñã°î«3qYC¦¦&¥C
+õu·Y>­Ö5uyAóç, iK߈¨¬õ™s®µš‰’*‘eR%*&UÖNª@¢Ôm(¨Øݶ–MÑÓîàãfÅ
+ËÆê#Ì)¼ÿØ÷þµ8MHíöº $D¦µ‰}?l*DÄp¯‡vTC@$ºô\™Ÿa€Å wþQà â`G­Dˆ•L| ø{…^Ù‰w-DÔáè$þè¦9ð¾1Jhä1'”´fŒúQÁnÌ$Þ²>‰Ë‡ rƒ
+´LožU&˜ò“zp'ñ¶óÇ~z»†ÍªE÷BzK.  fä ûÖN8¿h*L:® %¤0¡yVA›Ç• ¼Ãb
+ |Ã'” ¬©š
+G˜pC
+X
+gFPXŒˆ`)SŸaIÞT™ã‹>¡„TæeNÝT9¨ËV,
+Gؗݼ6±ÈO»Õ§…Šé:0‰Ùîç­_ý ×üÍeŠßàPéjoÜx
+‹{Ô ¾Œr›tÛüD\¨îfÏ^ᓧ»ôDÍm&éaà&öT»‰ºêЯÀO~Zm—ýª_aÖ´y˜ütuå@SªÝäT‡[‹ˆò¯‹ÿrl»Nendstream
+endobj
+338 0 obj
+2813
+endobj
+339 0 obj<</Type/Page/Parent 224 0 R/Contents 340 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 3 0 R/F8 6 0 R/F9 7 0 R>>>>/Annots 118 0 R>>endobj
+340 0 obj<</Length 341 0 R/Filter/FlateDecode>>stream
+xÚŘMoÛ8†ïþ<&«üÔDZ[´EÒ­èÁg­tÕZ¶kÉ(
+ìßq(ݵ趛lÄ`8zHÍûrHúËL ¿JZ˜\üÙÎ~[Ìž½ª„–bñ ”«D^X±Xß,V÷›Zìċݶ¯·}w»ø4{¹˜ÉLÿ(üóûk@eRäÆe¥hE•Ã‡olÄ{¤Ÿ=pø(ž½²B)f‘|t™É¬¸8
+·BÙ|“†¡~‚í÷ëùvÕÖ—yÑÂë«s œxˤ-o´sËÛå-RƒDBWöø?F]| 3¼…ÖYoátæ¨A/çÚ@{HðDzÙ£­(]¦cuéîЀ'—i
+×1Z,"†
+ Wg
+„úÎs¹ÌÌy.i7(r´ÛÐ`v+ÇgRf#Žrv¤Ffó¨½ßVæ(á„Åk¦=Ÿâr°ƒžª’–ÍO«!ÃŒ\½=¶>×ExŠMÊè„Dyu:wù“¨ále“g«@òûB„òýÆeš·«îsj
+d­Ð*™:©Gà¤Ï§JÈ+O¯¤HPSòQ¤¡ÁDrÅx>ƒB–‰HÚJt*GQ?Áõ—cÝõÍöãüØXX-æ«Íf÷uª|s, Rà> h”+Ê7c@UÓkuD¤
+°üóJ3)Îp±qF»ápýðÓÅÆÎ;.q± œáÞq¨›H×]lM)…²rõîÅfÕuÕ‘=›£;§…
+endobj
+341 0 obj
+1091
+endobj
+342 0 obj<</Type/Page/Parent 224 0 R/Contents 343 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F8 6 0 R>>>>>>endobj
+343 0 obj<</Length 344 0 R/Filter/FlateDecode>>stream
+xÚ+ä2T0
+áÒw³P04TIS047Ò3S072PIÑp VðÌ-ÈIÍMÍ+I,ÉÌÏSÈOSð Ð ÉâÒ…¨Õ…*Î, º†pr
+endobj
+344 0 obj
+94
+endobj
+345 0 obj<</Count 6/First 346 0 R/Last 405 0 R>>endobj
+346 0 obj<</Parent 345 0 R/Title(Table of Contents)/Dest[333 0 R/XYZ null 756 null]/Next 347 0 R>>endobj
+347 0 obj<</Parent 345 0 R/Count -2/First 348 0 R/Last 349 0 R/Title(1 Scope)/Dest[231 0 R/XYZ null 746 null]/Prev 346 0 R/Next 350 0 R>>endobj
+348 0 obj<</Parent 347 0 R/Title(1.1 Identification)/Dest[231 0 R/XYZ null 694 null]/Next 349 0 R>>endobj
+349 0 obj<</Parent 347 0 R/Title(1.2 Document Overview)/Dest[231 0 R/XYZ null 613 null]/Prev 348 0 R>>endobj
+350 0 obj<</Parent 345 0 R/Count -2/First 351 0 R/Last 352 0 R/Title(2 References)/Dest[237 0 R/XYZ null 746 null]/Prev 347 0 R/Next 353 0 R>>endobj
+351 0 obj<</Parent 350 0 R/Title(2.1 CUPS Documentation)/Dest[237 0 R/XYZ null 694 null]/Next 352 0 R>>endobj
+352 0 obj<</Parent 350 0 R/Title(2.2 Other Documents)/Dest[237 0 R/XYZ null 481 null]/Prev 351 0 R>>endobj
+353 0 obj<</Parent 345 0 R/Count -2/First 354 0 R/Last 355 0 R/Title(3 Overview)/Dest[243 0 R/XYZ null 746 null]/Prev 350 0 R/Next 356 0 R>>endobj
+354 0 obj<</Parent 353 0 R/Title(3.1 IPP URIs)/Dest[243 0 R/XYZ null 622 null]/Next 355 0 R>>endobj
+355 0 obj<</Parent 353 0 R/Title(3.2 CUPS IPP Operations)/Dest[243 0 R/XYZ null 330 null]/Prev 354 0 R>>endobj
+356 0 obj<</Parent 345 0 R/Count -16/First 357 0 R/Last 402 0 R/Title(4 Operations)/Dest[249 0 R/XYZ null 746 null]/Prev 353 0 R/Next 405 0 R>>endobj
+357 0 obj<</Parent 356 0 R/Count -2/First 358 0 R/Last 359 0 R/Title(4.1 Print-Job Operation)/Dest[249 0 R/XYZ null 675 null]/Next 360 0 R>>endobj
+358 0 obj<</Parent 357 0 R/Title(4.1.1 Print-Job Request)/Dest[249 0 R/XYZ null 600 null]/Next 359 0 R>>endobj
+359 0 obj<</Parent 357 0 R/Title(4.1.2 Print-Job Response)/Dest[252 0 R/XYZ null 639 null]/Prev 358 0 R>>endobj
+360 0 obj<</Parent 356 0 R/Count -2/First 361 0 R/Last 362 0 R/Title(4.2 Create-Job Operation)/Dest[252 0 R/XYZ null 388 null]/Prev 357 0 R/Next 363 0 R>>endobj
+361 0 obj<</Parent 360 0 R/Title(4.2.1 Create-Job Request)/Dest[252 0 R/XYZ null 313 null]/Next 362 0 R>>endobj
+362 0 obj<</Parent 360 0 R/Title(4.2.2 Create-Job Response)/Dest[255 0 R/XYZ null 401 null]/Prev 361 0 R>>endobj
+363 0 obj<</Parent 356 0 R/Count -2/First 364 0 R/Last 365 0 R/Title(4.3 Set-Job-Attributes Operation)/Dest[258 0 R/XYZ null 738 null]/Prev 360 0 R/Next 366 0 R>>endobj
+364 0 obj<</Parent 363 0 R/Title(4.3.1 Set-Job-Attributes Request)/Dest[258 0 R/XYZ null 684 null]/Next 365 0 R>>endobj
+365 0 obj<</Parent 363 0 R/Title(4.3.2 Set-Job-Attributes Response)/Dest[261 0 R/XYZ null 731 null]/Prev 364 0 R>>endobj
+366 0 obj<</Parent 356 0 R/Count -2/First 367 0 R/Last 368 0 R/Title(4.4 CUPS-Get-Default Operation)/Dest[261 0 R/XYZ null 550 null]/Prev 363 0 R/Next 369 0 R>>endobj
+367 0 obj<</Parent 366 0 R/Title(4.4.1 CUPS-Get-Default Request)/Dest[261 0 R/XYZ null 475 null]/Next 368 0 R>>endobj
+368 0 obj<</Parent 366 0 R/Title(4.4.2 CUPS-Get-Default Response)/Dest[261 0 R/XYZ null 244 null]/Prev 367 0 R>>endobj
+369 0 obj<</Parent 356 0 R/Count -2/First 370 0 R/Last 371 0 R/Title(4.5 CUPS-Get-Printers Operation)/Dest[264 0 R/XYZ null 659 null]/Prev 366 0 R/Next 372 0 R>>endobj
+370 0 obj<</Parent 369 0 R/Title(4.5.1 CUPS-Get-Printers Request)/Dest[264 0 R/XYZ null 571 null]/Next 371 0 R>>endobj
+371 0 obj<</Parent 369 0 R/Title(4.5.2 CUPS-Get-Printers Response)/Dest[267 0 R/XYZ null 625 null]/Prev 370 0 R>>endobj
+372 0 obj<</Parent 356 0 R/Count -2/First 373 0 R/Last 374 0 R/Title(4.6 CUPS-Add-Printer Operation)/Dest[267 0 R/XYZ null 375 null]/Prev 369 0 R/Next 375 0 R>>endobj
+373 0 obj<</Parent 372 0 R/Title(4.6.1 CUPS-Add-Printer Request)/Dest[267 0 R/XYZ null 300 null]/Next 374 0 R>>endobj
+374 0 obj<</Parent 372 0 R/Title(4.6.2 CUPS-Add-Printer Response)/Dest[273 0 R/XYZ null 586 null]/Prev 373 0 R>>endobj
+375 0 obj<</Parent 356 0 R/Count -2/First 376 0 R/Last 377 0 R/Title(4.7 CUPS-Delete-Printer Operation)/Dest[273 0 R/XYZ null 388 null]/Prev 372 0 R/Next 378 0 R>>endobj
+376 0 obj<</Parent 375 0 R/Title(4.7.1 CUPS-Delete-Printer Request)/Dest[273 0 R/XYZ null 313 null]/Next 377 0 R>>endobj
+377 0 obj<</Parent 375 0 R/Title(4.7.2 CUPS-Delete-Printer Response)/Dest[276 0 R/XYZ null 731 null]/Prev 376 0 R>>endobj
+378 0 obj<</Parent 356 0 R/Count -2/First 379 0 R/Last 380 0 R/Title(4.8 CUPS-Get-Classes Operation)/Dest[276 0 R/XYZ null 550 null]/Prev 375 0 R/Next 381 0 R>>endobj
+379 0 obj<</Parent 378 0 R/Title(4.8.1 CUPS-Get-Classes Request)/Dest[276 0 R/XYZ null 462 null]/Next 380 0 R>>endobj
+380 0 obj<</Parent 378 0 R/Title(4.8.2 CUPS-Get-Classes Response)/Dest[279 0 R/XYZ null 507 null]/Prev 379 0 R>>endobj
+381 0 obj<</Parent 356 0 R/Count -2/First 382 0 R/Last 383 0 R/Title(4.9 CUPS-Add-Class Operation)/Dest[279 0 R/XYZ null 256 null]/Prev 378 0 R/Next 384 0 R>>endobj
+382 0 obj<</Parent 381 0 R/Title(4.9.1 CUPS-Add-Class Request)/Dest[279 0 R/XYZ null 168 null]/Next 383 0 R>>endobj
+383 0 obj<</Parent 381 0 R/Title(4.9.2 CUPS-Add-Class Response)/Dest[285 0 R/XYZ null 731 null]/Prev 382 0 R>>endobj
+384 0 obj<</Parent 356 0 R/Count -2/First 385 0 R/Last 386 0 R/Title(4.10 CUPS-Delete-Class Operation)/Dest[285 0 R/XYZ null 550 null]/Prev 381 0 R/Next 387 0 R>>endobj
+385 0 obj<</Parent 384 0 R/Title(4.10.1 CUPS-Delete-Class Request)/Dest[285 0 R/XYZ null 475 null]/Next 386 0 R>>endobj
+386 0 obj<</Parent 384 0 R/Title(4.10.2 CUPS-Delete-Class Response)/Dest[285 0 R/XYZ null 270 null]/Prev 385 0 R>>endobj
+387 0 obj<</Parent 356 0 R/Count -2/First 388 0 R/Last 389 0 R/Title(4.11 CUPS-Accept-Jobs Operation)/Dest[288 0 R/XYZ null 738 null]/Prev 384 0 R/Next 390 0 R>>endobj
+388 0 obj<</Parent 387 0 R/Title(4.11.1 CUPS-Accept-Jobs Request)/Dest[288 0 R/XYZ null 671 null]/Next 389 0 R>>endobj
+389 0 obj<</Parent 387 0 R/Title(4.11.2 CUPS-Accept-Jobs Response)/Dest[288 0 R/XYZ null 466 null]/Prev 388 0 R>>endobj
+390 0 obj<</Parent 356 0 R/Count -2/First 391 0 R/Last 392 0 R/Title(4.12 CUPS-Reject-Jobs Operation)/Dest[288 0 R/XYZ null 268 null]/Prev 387 0 R/Next 393 0 R>>endobj
+391 0 obj<</Parent 390 0 R/Title(4.12.1 CUPS-Reject-Jobs Request)/Dest[288 0 R/XYZ null 180 null]/Next 392 0 R>>endobj
+392 0 obj<</Parent 390 0 R/Title(4.12.2 CUPS-Reject-Jobs Response)/Dest[291 0 R/XYZ null 559 null]/Prev 391 0 R>>endobj
+393 0 obj<</Parent 356 0 R/Count -2/First 394 0 R/Last 395 0 R/Title(4.13 CUPS-Set-Default Operation)/Dest[291 0 R/XYZ null 361 null]/Prev 390 0 R/Next 396 0 R>>endobj
+394 0 obj<</Parent 393 0 R/Title(4.13.1 CUPS-Set-Default Request)/Dest[291 0 R/XYZ null 274 null]/Next 395 0 R>>endobj
+395 0 obj<</Parent 393 0 R/Title(4.13.2 CUPS-Set-Default Response)/Dest[294 0 R/XYZ null 731 null]/Prev 394 0 R>>endobj
+396 0 obj<</Parent 356 0 R/Count -2/First 397 0 R/Last 398 0 R/Title(4.14 CUPS-Get-Devices Operation)/Dest[294 0 R/XYZ null 550 null]/Prev 393 0 R/Next 399 0 R>>endobj
+397 0 obj<</Parent 396 0 R/Title(4.14.1 CUPS-Get-Devices Request)/Dest[294 0 R/XYZ null 462 null]/Next 398 0 R>>endobj
+398 0 obj<</Parent 396 0 R/Title(4.14.2 CUPS-Get-Devices Response)/Dest[297 0 R/XYZ null 731 null]/Prev 397 0 R>>endobj
+399 0 obj<</Parent 356 0 R/Count -2/First 400 0 R/Last 401 0 R/Title(4.15 CUPS-Get-PPDs Operation)/Dest[297 0 R/XYZ null 497 null]/Prev 396 0 R/Next 402 0 R>>endobj
+400 0 obj<</Parent 399 0 R/Title(4.15.1 CUPS-Get-PPDs Request)/Dest[297 0 R/XYZ null 409 null]/Next 401 0 R>>endobj
+401 0 obj<</Parent 399 0 R/Title(4.15.2 CUPS-Get-PPDs Response)/Dest[300 0 R/XYZ null 731 null]/Prev 400 0 R>>endobj
+402 0 obj<</Parent 356 0 R/Count -2/First 403 0 R/Last 404 0 R/Title(4.16 CUPS-Move-Job Operation)/Dest[300 0 R/XYZ null 497 null]/Prev 399 0 R>>endobj
+403 0 obj<</Parent 402 0 R/Title(4.16.1 CUPS-Move-Job Request)/Dest[300 0 R/XYZ null 409 null]/Next 404 0 R>>endobj
+404 0 obj<</Parent 402 0 R/Title(4.16.2 CUPS-Move-Job Response)/Dest[300 0 R/XYZ null 151 null]/Prev 403 0 R>>endobj
+405 0 obj<</Parent 345 0 R/Count -5/First 406 0 R/Last 445 0 R/Title(5 Attributes)/Dest[309 0 R/XYZ null 746 null]/Prev 356 0 R>>endobj
+406 0 obj<</Parent 405 0 R/Count -4/First 407 0 R/Last 410 0 R/Title(5.1 Device Attributes)/Dest[309 0 R/XYZ null 675 null]/Next 411 0 R>>endobj
+407 0 obj<</Parent 406 0 R/Title(5.1.1 device-class \(type2 keyword\))/Dest[309 0 R/XYZ null 587 null]/Next 408 0 R>>endobj
+408 0 obj<</Parent 406 0 R/Title(5.1.2 device-info \(text\(127\)\))/Dest[309 0 R/XYZ null 435 null]/Prev 407 0 R/Next 409 0 R>>endobj
+409 0 obj<</Parent 406 0 R/Title(5.1.3 device-make-and-model \(text\(127\)\))/Dest[309 0 R/XYZ null 375 null]/Prev 408 0 R/Next 410 0 R>>endobj
+410 0 obj<</Parent 406 0 R/Title(5.1.4 device-uri \(uri\))/Dest[309 0 R/XYZ null 288 null]/Prev 409 0 R>>endobj
+411 0 obj<</Parent 405 0 R/Count -21/First 412 0 R/Last 432 0 R/Title(5.2 Job Template Attributes)/Dest[312 0 R/XYZ null 593 null]/Prev 406 0 R/Next 433 0 R>>endobj
+412 0 obj<</Parent 411 0 R/Title(5.2.1 blackplot \(boolean\))/Dest[312 0 R/XYZ null 528 null]/Next 413 0 R>>endobj
+413 0 obj<</Parent 411 0 R/Title(5.2.2 brightness \(integer\(0:200\)\))/Dest[312 0 R/XYZ null 442 null]/Prev 412 0 R/Next 414 0 R>>endobj
+414 0 obj<</Parent 411 0 R/Title(5.2.3 columns \(integer\(1:4\)\))/Dest[312 0 R/XYZ null 329 null]/Prev 413 0 R/Next 415 0 R>>endobj
+415 0 obj<</Parent 411 0 R/Title(5.2.4 cpi \(type2 enum\))/Dest[312 0 R/XYZ null 256 null]/Prev 414 0 R/Next 416 0 R>>endobj
+416 0 obj<</Parent 411 0 R/Title(5.2.5 fitplot \(boolean\))/Dest[312 0 R/XYZ null 183 null]/Prev 415 0 R/Next 417 0 R>>endobj
+417 0 obj<</Parent 411 0 R/Title(5.2.6 gamma \(integer\(1:10000\)\))/Dest[315 0 R/XYZ null 731 null]/Prev 416 0 R/Next 418 0 R>>endobj
+418 0 obj<</Parent 411 0 R/Title(5.2.7 hue \(integer\(-180:180\)\))/Dest[315 0 R/XYZ null 622 null]/Prev 417 0 R/Next 419 0 R>>endobj
+419 0 obj<</Parent 411 0 R/Title(5.2.8 job-billing \(name\(MAX\)\))/Dest[315 0 R/XYZ null 562 null]/Prev 418 0 R/Next 420 0 R>>endobj
+420 0 obj<</Parent 411 0 R/Title(5.2.9 job-sheets \(1setof type3 keyword | name\(MAX\)\))/Dest[315 0 R/XYZ null 475 null]/Prev 419 0 R/Next 421 0 R>>endobj
+421 0 obj<</Parent 411 0 R/Title(5.2.10 lpi \(type2 enum\))/Dest[315 0 R/XYZ null 336 null]/Prev 420 0 R/Next 422 0 R>>endobj
+422 0 obj<</Parent 411 0 R/Title(5.2.11 page-bottom \(integer\(0:MAX\)\))/Dest[315 0 R/XYZ null 263 null]/Prev 421 0 R/Next 423 0 R>>endobj
+423 0 obj<</Parent 411 0 R/Title(5.2.12 page-left \(integer\(0:MAX\)\))/Dest[315 0 R/XYZ null 190 null]/Prev 422 0 R/Next 424 0 R>>endobj
+424 0 obj<</Parent 411 0 R/Title(5.2.13 page-right \(integer\(0:MAX\)\))/Dest[318 0 R/XYZ null 731 null]/Prev 423 0 R/Next 425 0 R>>endobj
+425 0 obj<</Parent 411 0 R/Title(5.2.14 page-set \(type2 keyword\))/Dest[318 0 R/XYZ null 675 null]/Prev 424 0 R/Next 426 0 R>>endobj
+426 0 obj<</Parent 411 0 R/Title(5.2.15 page-top \(integer\(0:MAX\)\))/Dest[318 0 R/XYZ null 601 null]/Prev 425 0 R/Next 427 0 R>>endobj
+427 0 obj<</Parent 411 0 R/Title(5.2.16 penwidth \(integer\(0:MAX\)\))/Dest[318 0 R/XYZ null 528 null]/Prev 426 0 R/Next 428 0 R>>endobj
+428 0 obj<</Parent 411 0 R/Title(5.2.17 ppi \(integer\(1:MAX\)\))/Dest[318 0 R/XYZ null 455 null]/Prev 427 0 R/Next 429 0 R>>endobj
+429 0 obj<</Parent 411 0 R/Title(5.2.18 prettyprint \(boolean\))/Dest[318 0 R/XYZ null 382 null]/Prev 428 0 R/Next 430 0 R>>endobj
+430 0 obj<</Parent 411 0 R/Title(5.2.19 saturation \(integer\(0:200\)\))/Dest[318 0 R/XYZ null 309 null]/Prev 429 0 R/Next 431 0 R>>endobj
+431 0 obj<</Parent 411 0 R/Title(5.2.20 scaling \(integer\(1:1000\)\))/Dest[318 0 R/XYZ null 235 null]/Prev 430 0 R/Next 432 0 R>>endobj
+432 0 obj<</Parent 411 0 R/Title(5.2.21 wrap \(boolean\))/Dest[321 0 R/XYZ null 731 null]/Prev 431 0 R>>endobj
+433 0 obj<</Parent 405 0 R/Count -4/First 434 0 R/Last 437 0 R/Title(5.3 PPD Attributes)/Dest[321 0 R/XYZ null 682 null]/Prev 411 0 R/Next 438 0 R>>endobj
+434 0 obj<</Parent 433 0 R/Title(5.3.1 ppd-natural-language \(naturalLanguage\))/Dest[321 0 R/XYZ null 617 null]/Next 435 0 R>>endobj
+435 0 obj<</Parent 433 0 R/Title(5.3.2 ppd-make \(text\(127\)\))/Dest[321 0 R/XYZ null 544 null]/Prev 434 0 R/Next 436 0 R>>endobj
+436 0 obj<</Parent 433 0 R/Title(5.3.3 ppd-make-and-model \(text\(127\)\))/Dest[321 0 R/XYZ null 457 null]/Prev 435 0 R/Next 437 0 R>>endobj
+437 0 obj<</Parent 433 0 R/Title(5.3.4 ppd-name \(name\(255\)\))/Dest[321 0 R/XYZ null 371 null]/Prev 436 0 R>>endobj
+438 0 obj<</Parent 405 0 R/Count -6/First 439 0 R/Last 444 0 R/Title(5.4 Printer Attributes)/Dest[321 0 R/XYZ null 305 null]/Prev 433 0 R/Next 445 0 R>>endobj
+439 0 obj<</Parent 438 0 R/Title(5.4.1 job-sheets-default \(1setof type3 keyword | name\(MAX\)\))/Dest[321 0 R/XYZ null 240 null]/Next 440 0 R>>endobj
+440 0 obj<</Parent 438 0 R/Title(5.4.2 job-sheets-supported \(1setof type3 keyword | name\(MAX\)\))/Dest[321 0 R/XYZ null 141 null]/Prev 439 0 R/Next 441 0 R>>endobj
+441 0 obj<</Parent 438 0 R/Title(5.4.3 printer-type \(type2 enum\))/Dest[324 0 R/XYZ null 705 null]/Prev 440 0 R/Next 442 0 R>>endobj
+442 0 obj<</Parent 438 0 R/Title(5.4.4 printer-type-mask \(type2 enum\))/Dest[324 0 R/XYZ null 241 null]/Prev 441 0 R/Next 443 0 R>>endobj
+443 0 obj<</Parent 438 0 R/Title(5.4.5 requesting-user-name-allowed \(1setof name\(127\)\))/Dest[327 0 R/XYZ null 731 null]/Prev 442 0 R/Next 444 0 R>>endobj
+444 0 obj<</Parent 438 0 R/Title(5.4.6 requesting-user-name-denied \(1setof name\(127\)\))/Dest[327 0 R/XYZ null 622 null]/Prev 443 0 R>>endobj
+445 0 obj<</Parent 405 0 R/Count -2/First 446 0 R/Last 447 0 R/Title(5.5 Printer Class Attributes)/Dest[327 0 R/XYZ null 503 null]/Prev 438 0 R>>endobj
+446 0 obj<</Parent 445 0 R/Title(5.5.1 member-names \(1setof name\(127\)\))/Dest[327 0 R/XYZ null 438 null]/Next 447 0 R>>endobj
+447 0 obj<</Parent 445 0 R/Title(5.5.2 member-uris \(1setof uri\))/Dest[327 0 R/XYZ null 365 null]/Prev 446 0 R>>endobj
+448 0 obj<</Type/Catalog/Pages 224 0 R/Names 119 0 R/PageLayout/SinglePage/Outlines 345 0 R/OpenAction[231 0 R/XYZ null null null]/PageMode/UseOutlines/PageLabels<</Nums[0<</P(title)>>1<</P(eltit)>>2<</S/r>>6<</S/D>>]>>>>endobj
+xref
+0 449
+0000000000 65535 f
+0000000015 00000 n
+0000000219 00000 n
+0000001785 00000 n
+0000001863 00000 n
+0000001940 00000 n
+0000002019 00000 n
+0000002095 00000 n
+0000002176 00000 n
+0000002234 00000 n
+0000002338 00000 n
+0000002443 00000 n
+0000002548 00000 n
+0000002653 00000 n
+0000002758 00000 n
+0000002809 00000 n
+0000002913 00000 n
+0000003018 00000 n
+0000003123 00000 n
+0000003227 00000 n
+0000003332 00000 n
+0000003437 00000 n
+0000003541 00000 n
+0000003646 00000 n
+0000003751 00000 n
+0000003855 00000 n
+0000003960 00000 n
+0000004065 00000 n
+0000004170 00000 n
+0000004275 00000 n
+0000004380 00000 n
+0000004485 00000 n
+0000004590 00000 n
+0000004695 00000 n
+0000004800 00000 n
+0000004905 00000 n
+0000005010 00000 n
+0000005115 00000 n
+0000005220 00000 n
+0000005325 00000 n
+0000005430 00000 n
+0000005535 00000 n
+0000005640 00000 n
+0000005745 00000 n
+0000005850 00000 n
+0000005955 00000 n
+0000006060 00000 n
+0000006165 00000 n
+0000006270 00000 n
+0000006375 00000 n
+0000006480 00000 n
+0000006585 00000 n
+0000006690 00000 n
+0000006795 00000 n
+0000006900 00000 n
+0000007005 00000 n
+0000007110 00000 n
+0000007214 00000 n
+0000007317 00000 n
+0000007420 00000 n
+0000007745 00000 n
+0000007850 00000 n
+0000007955 00000 n
+0000008059 00000 n
+0000008164 00000 n
+0000008269 00000 n
+0000008373 00000 n
+0000008478 00000 n
+0000008583 00000 n
+0000008687 00000 n
+0000008792 00000 n
+0000008897 00000 n
+0000009001 00000 n
+0000009106 00000 n
+0000009211 00000 n
+0000009314 00000 n
+0000009418 00000 n
+0000009523 00000 n
+0000009628 00000 n
+0000009733 00000 n
+0000009838 00000 n
+0000009942 00000 n
+0000010047 00000 n
+0000010152 00000 n
+0000010257 00000 n
+0000010362 00000 n
+0000010467 00000 n
+0000010572 00000 n
+0000010677 00000 n
+0000010782 00000 n
+0000010887 00000 n
+0000010992 00000 n
+0000011097 00000 n
+0000011202 00000 n
+0000011307 00000 n
+0000011412 00000 n
+0000011517 00000 n
+0000011622 00000 n
+0000011727 00000 n
+0000011832 00000 n
+0000011937 00000 n
+0000012043 00000 n
+0000012149 00000 n
+0000012254 00000 n
+0000012359 00000 n
+0000012463 00000 n
+0000012567 00000 n
+0000012913 00000 n
+0000013019 00000 n
+0000013125 00000 n
+0000013231 00000 n
+0000013337 00000 n
+0000013443 00000 n
+0000013549 00000 n
+0000013655 00000 n
+0000013761 00000 n
+0000013867 00000 n
+0000013973 00000 n
+0000014079 00000 n
+0000014185 00000 n
+0000014219 00000 n
+0000014253 00000 n
+0000015695 00000 n
+0000015744 00000 n
+0000015793 00000 n
+0000015842 00000 n
+0000015891 00000 n
+0000015940 00000 n
+0000015989 00000 n
+0000016038 00000 n
+0000016087 00000 n
+0000016136 00000 n
+0000016185 00000 n
+0000016234 00000 n
+0000016283 00000 n
+0000016332 00000 n
+0000016381 00000 n
+0000016430 00000 n
+0000016479 00000 n
+0000016528 00000 n
+0000016577 00000 n
+0000016626 00000 n
+0000016675 00000 n
+0000016724 00000 n
+0000016773 00000 n
+0000016822 00000 n
+0000016871 00000 n
+0000016920 00000 n
+0000016969 00000 n
+0000017018 00000 n
+0000017067 00000 n
+0000017116 00000 n
+0000017165 00000 n
+0000017214 00000 n
+0000017263 00000 n
+0000017312 00000 n
+0000017361 00000 n
+0000017410 00000 n
+0000017459 00000 n
+0000017508 00000 n
+0000017557 00000 n
+0000017606 00000 n
+0000017655 00000 n
+0000017704 00000 n
+0000017753 00000 n
+0000017802 00000 n
+0000017851 00000 n
+0000017900 00000 n
+0000017949 00000 n
+0000017998 00000 n
+0000018047 00000 n
+0000018096 00000 n
+0000018145 00000 n
+0000018194 00000 n
+0000018243 00000 n
+0000018292 00000 n
+0000018341 00000 n
+0000018390 00000 n
+0000018439 00000 n
+0000018488 00000 n
+0000018537 00000 n
+0000018586 00000 n
+0000018635 00000 n
+0000018684 00000 n
+0000018733 00000 n
+0000018782 00000 n
+0000018831 00000 n
+0000018880 00000 n
+0000018929 00000 n
+0000018978 00000 n
+0000019027 00000 n
+0000019076 00000 n
+0000019125 00000 n
+0000019174 00000 n
+0000019223 00000 n
+0000019272 00000 n
+0000019321 00000 n
+0000019370 00000 n
+0000019419 00000 n
+0000019468 00000 n
+0000019517 00000 n
+0000019566 00000 n
+0000019615 00000 n
+0000019664 00000 n
+0000019713 00000 n
+0000019762 00000 n
+0000019811 00000 n
+0000019860 00000 n
+0000019909 00000 n
+0000019958 00000 n
+0000020007 00000 n
+0000020056 00000 n
+0000020105 00000 n
+0000020154 00000 n
+0000020203 00000 n
+0000020252 00000 n
+0000020301 00000 n
+0000020350 00000 n
+0000020399 00000 n
+0000020448 00000 n
+0000020497 00000 n
+0000020546 00000 n
+0000020595 00000 n
+0000020644 00000 n
+0000020693 00000 n
+0000021082 00000 n
+0000021234 00000 n
+0000027585 00000 n
+0000027607 00000 n
+0000027702 00000 n
+0000027804 00000 n
+0000027824 00000 n
+0000027978 00000 n
+0000028625 00000 n
+0000028646 00000 n
+0000028759 00000 n
+0000028938 00000 n
+0000028959 00000 n
+0000029099 00000 n
+0000029876 00000 n
+0000029897 00000 n
+0000030010 00000 n
+0000030194 00000 n
+0000030215 00000 n
+0000030364 00000 n
+0000031553 00000 n
+0000031575 00000 n
+0000031697 00000 n
+0000032773 00000 n
+0000032795 00000 n
+0000032944 00000 n
+0000033877 00000 n
+0000033898 00000 n
+0000034038 00000 n
+0000034776 00000 n
+0000034797 00000 n
+0000034946 00000 n
+0000035806 00000 n
+0000035827 00000 n
+0000035976 00000 n
+0000036897 00000 n
+0000036918 00000 n
+0000037049 00000 n
+0000037753 00000 n
+0000037774 00000 n
+0000037914 00000 n
+0000038661 00000 n
+0000038682 00000 n
+0000038813 00000 n
+0000039652 00000 n
+0000039673 00000 n
+0000039804 00000 n
+0000040546 00000 n
+0000040567 00000 n
+0000040707 00000 n
+0000041564 00000 n
+0000041585 00000 n
+0000041725 00000 n
+0000042477 00000 n
+0000042498 00000 n
+0000042638 00000 n
+0000043496 00000 n
+0000043517 00000 n
+0000043648 00000 n
+0000044494 00000 n
+0000044515 00000 n
+0000044646 00000 n
+0000045261 00000 n
+0000045282 00000 n
+0000045413 00000 n
+0000046056 00000 n
+0000046077 00000 n
+0000046208 00000 n
+0000046942 00000 n
+0000046963 00000 n
+0000047094 00000 n
+0000047889 00000 n
+0000047910 00000 n
+0000048041 00000 n
+0000048900 00000 n
+0000048921 00000 n
+0000049061 00000 n
+0000049779 00000 n
+0000049800 00000 n
+0000049922 00000 n
+0000050285 00000 n
+0000050306 00000 n
+0000050419 00000 n
+0000050617 00000 n
+0000050638 00000 n
+0000050778 00000 n
+0000051928 00000 n
+0000051950 00000 n
+0000052090 00000 n
+0000053197 00000 n
+0000053219 00000 n
+0000053359 00000 n
+0000054340 00000 n
+0000054361 00000 n
+0000054492 00000 n
+0000055411 00000 n
+0000055432 00000 n
+0000055572 00000 n
+0000056435 00000 n
+0000056456 00000 n
+0000056605 00000 n
+0000057745 00000 n
+0000057767 00000 n
+0000057907 00000 n
+0000058506 00000 n
+0000058527 00000 n
+0000058640 00000 n
+0000058861 00000 n
+0000058882 00000 n
+0000059036 00000 n
+0000061433 00000 n
+0000061455 00000 n
+0000061610 00000 n
+0000064494 00000 n
+0000064516 00000 n
+0000064662 00000 n
+0000065824 00000 n
+0000065846 00000 n
+0000065959 00000 n
+0000066124 00000 n
+0000066144 00000 n
+0000066199 00000 n
+0000066304 00000 n
+0000066448 00000 n
+0000066554 00000 n
+0000066663 00000 n
+0000066812 00000 n
+0000066922 00000 n
+0000067029 00000 n
+0000067176 00000 n
+0000067276 00000 n
+0000067387 00000 n
+0000067537 00000 n
+0000067684 00000 n
+0000067795 00000 n
+0000067907 00000 n
+0000068068 00000 n
+0000068180 00000 n
+0000068293 00000 n
+0000068462 00000 n
+0000068582 00000 n
+0000068703 00000 n
+0000068870 00000 n
+0000068988 00000 n
+0000069107 00000 n
+0000069275 00000 n
+0000069394 00000 n
+0000069514 00000 n
+0000069681 00000 n
+0000069799 00000 n
+0000069918 00000 n
+0000070088 00000 n
+0000070209 00000 n
+0000070331 00000 n
+0000070498 00000 n
+0000070616 00000 n
+0000070735 00000 n
+0000070900 00000 n
+0000071016 00000 n
+0000071133 00000 n
+0000071302 00000 n
+0000071422 00000 n
+0000071543 00000 n
+0000071711 00000 n
+0000071830 00000 n
+0000071950 00000 n
+0000072118 00000 n
+0000072237 00000 n
+0000072357 00000 n
+0000072525 00000 n
+0000072644 00000 n
+0000072764 00000 n
+0000072932 00000 n
+0000073051 00000 n
+0000073171 00000 n
+0000073336 00000 n
+0000073452 00000 n
+0000073569 00000 n
+0000073721 00000 n
+0000073837 00000 n
+0000073954 00000 n
+0000074090 00000 n
+0000074235 00000 n
+0000074359 00000 n
+0000074493 00000 n
+0000074637 00000 n
+0000074749 00000 n
+0000074914 00000 n
+0000075029 00000 n
+0000075167 00000 n
+0000075300 00000 n
+0000075425 00000 n
+0000075551 00000 n
+0000075686 00000 n
+0000075820 00000 n
+0000075954 00000 n
+0000076110 00000 n
+0000076236 00000 n
+0000076376 00000 n
+0000076514 00000 n
+0000076653 00000 n
+0000076787 00000 n
+0000076924 00000 n
+0000077061 00000 n
+0000077193 00000 n
+0000077324 00000 n
+0000077463 00000 n
+0000077600 00000 n
+0000077711 00000 n
+0000077866 00000 n
+0000078000 00000 n
+0000078131 00000 n
+0000078272 00000 n
+0000078390 00000 n
+0000078549 00000 n
+0000078700 00000 n
+0000078866 00000 n
+0000079000 00000 n
+0000079139 00000 n
+0000079297 00000 n
+0000079441 00000 n
+0000079593 00000 n
+0000079722 00000 n
+0000079842 00000 n
+trailer
+<</Size 449/Root 448 0 R/Info 1 0 R>>
+startxref
+80070
+%%EOF
diff --git a/doc/ipp.shtml b/doc/ipp.shtml
new file mode 100644
index 000000000..6e84ef124
--- /dev/null
+++ b/doc/ipp.shtml
@@ -0,0 +1,1838 @@
+<HTML>
+<HEAD>
+ <META NAME="COPYRIGHT" CONTENT="Copyright 1997-2000, All Rights Reserved">
+ <META NAME="DOCNUMBER" CONTENT="CUPS-IPP-1.1">
+ <META NAME="Author" CONTENT="Easy Software Products">
+ <TITLE>CUPS Implementation of IPP</TITLE>
+</HEAD>
+<BODY>
+
+<H1>Scope</H1>
+
+<H2>Identification</H2>
+
+<P>This document provides an overview of the Internet Printing Protocol
+("IPP") version 1.1 as implemented in the Common UNIX Printing System
+("CUPS") version 1.1.
+
+<EMBED SRC="system-overview.shtml">
+
+<H2>Document Overview</H2>
+
+<P>This document is organized into the following sections:
+
+<UL>
+ <LI><A HREF="#1">1 - Scope</A>
+ <LI><A HREF="#2">2 - References</A>
+ <LI><A HREF="#3">3 - Overview</A>
+ <LI><A HREF="#4">4 - Operations</A>
+ <LI><A HREF="#5">5 - Attributes</A>
+ <LI><A HREF="#6">A - Glossary</A>
+</UL>
+
+<EMBED SRC="references.shtml">
+
+<H1>Overview</H1>
+
+<P>CUPS 1.1 implements IPP/1.1 and the operations and attributes
+defined in the "IPP: Job and Printer Set Operations",
+"IPP/1.1: Output-bin Attribute Extension", and "IPP/1.1: finishings
+'fold',' trim', and 'bale' attribute values extension" specifications.
+
+<P>CUPS also provides 13 new operations and many new attributes to
+support multiple IPP printers and printer classes on a single host.
+
+<H2>IPP URIs</H2>
+
+<P>CUPS supports both the "http" and "ipp" methods. The following
+resource names are used:
+
+<DL>
+
+ <DT>method://hostname:port/
+
+ <DD>Can be used for all "get" operations.
+
+ <DT>method://hostname:port/admin
+
+ <DD>Used for all administrative operations.
+
+ <DT>method://hostname:port/classes/name
+
+ <DD>Specifies a printer class.
+
+ <DT>method://hostname:port/jobs/id
+
+ <DD>Specifies a job.
+
+ <DT>method://hostname:port/printers/name
+
+ <DD>Specifies a printer.
+
+</DL>
+
+<P>So a typical printer URI would be "ipp://foo.bar.com/printers/LaserJet".
+
+<P>In addition, the CUPS server also supports normal browser access to
+"method://hostname:port/admin/", "method://hostname:port/classes/",
+"method://hostname:port/jobs/", and "method://hostname:port/printers/"
+to view and manage resources on the server dynamically.
+
+<H2>CUPS IPP Operations</H2>
+
+<P>CUPS provides 13 extension operations in addition to most of the
+standard IPP and registered extension operations:
+
+<CENTER><TABLE BORDER WIDTH="80%">
+<TR>
+ <TH VALIGN="TOP">Operation Name</TH>
+ <TH VALIGN="TOP">CUPS</TH>
+ <TH VALIGN="TOP">Code</TH>
+ <TH VALIGN="TOP">Brief Description</TH>
+</TR>
+<TR>
+ <TD VALIGN="TOP">Print-Job</TD>
+ <TD VALIGN="TOP">1.0</TD>
+ <TD VALIGN="TOP">0x0002</TD>
+ <TD VALIGN="TOP">Print a file.</TD>
+</TR>
+<TR>
+ <TD VALIGN="TOP">Validate-Job</TD>
+ <TD VALIGN="TOP">1.0</TD>
+ <TD VALIGN="TOP">0x0004</TD>
+ <TD VALIGN="TOP">Validate job attributes.</TD>
+</TR>
+<TR>
+ <TD VALIGN="TOP">Create-Job</TD>
+ <TD VALIGN="TOP">1.1</TD>
+ <TD VALIGN="TOP">0x0005</TD>
+ <TD VALIGN="TOP">Create a print job.</TD>
+</TR>
+<TR>
+ <TD VALIGN="TOP">Send-Document</TD>
+ <TD VALIGN="TOP">1.1</TD>
+ <TD VALIGN="TOP">0x0006</TD>
+ <TD VALIGN="TOP">Send a file for a print job.</TD>
+</TR>
+<TR>
+ <TD VALIGN="TOP">Cancel-Job</TD>
+ <TD VALIGN="TOP">1.0</TD>
+ <TD VALIGN="TOP">0x0008</TD>
+ <TD VALIGN="TOP">Cancel a print job.</TD>
+</TR>
+<TR>
+ <TD VALIGN="TOP">Get-Job-Attributes</TD>
+ <TD VALIGN="TOP">1.0</TD>
+ <TD VALIGN="TOP">0x0009</TD>
+ <TD VALIGN="TOP">Get job attributes.</TD>
+</TR>
+<TR>
+ <TD VALIGN="TOP">Get-Jobs</TD>
+ <TD VALIGN="TOP">1.0</TD>
+ <TD VALIGN="TOP">0x000A</TD>
+ <TD VALIGN="TOP">Get all jobs.</TD>
+</TR>
+<TR>
+ <TD VALIGN="TOP">Get-Printer-Attributes</TD>
+ <TD VALIGN="TOP">1.0</TD>
+ <TD VALIGN="TOP">0x000B</TD>
+ <TD VALIGN="TOP">Get printer attributes.</TD>
+</TR>
+<TR>
+ <TD VALIGN="TOP">Hold-Job</TD>
+ <TD VALIGN="TOP">1.1</TD>
+ <TD VALIGN="TOP">0x000C</TD>
+ <TD VALIGN="TOP">Hold a job for printing.</TD>
+</TR>
+<TR>
+ <TD VALIGN="TOP">Release-Job</TD>
+ <TD VALIGN="TOP">1.1</TD>
+ <TD VALIGN="TOP">0x000D</TD>
+ <TD VALIGN="TOP">Release a job for printing.</TD>
+</TR>
+<TR>
+ <TD VALIGN="TOP">Pause-Printer</TD>
+ <TD VALIGN="TOP">1.0</TD>
+ <TD VALIGN="TOP">0x0010</TD>
+ <TD VALIGN="TOP">Pause printing on a printer.</TD>
+</TR>
+<TR>
+ <TD VALIGN="TOP">Resume-Printer</TD>
+ <TD VALIGN="TOP">1.0</TD>
+ <TD VALIGN="TOP">0x0011</TD>
+ <TD VALIGN="TOP">Resume printing on a printer.</TD>
+</TR>
+<TR>
+ <TD VALIGN="TOP">Purge-Jobs</TD>
+ <TD VALIGN="TOP">1.0</TD>
+ <TD VALIGN="TOP">0x0012</TD>
+ <TD VALIGN="TOP">Purge all jobs.</TD>
+</TR>
+<TR>
+ <TD VALIGN="TOP">Set-Job-Attributes</TD>
+ <TD VALIGN="TOP">1.1</TD>
+ <TD VALIGN="TOP">0x0014</TD>
+ <TD VALIGN="TOP">Set attributes for a pending or held job.</TD>
+</TR>
+<TR>
+ <TD VALIGN="TOP">CUPS-Get-Default</TD>
+ <TD VALIGN="TOP">1.0</TD>
+ <TD VALIGN="TOP">0x4001</TD>
+ <TD VALIGN="TOP">Get the default destination.</TD>
+</TR>
+<TR>
+ <TD VALIGN="TOP">CUPS-Get-Printers</TD>
+ <TD VALIGN="TOP">1.0</TD>
+ <TD VALIGN="TOP">0x4002</TD>
+ <TD VALIGN="TOP">Get all of the available printers.</TD>
+</TR>
+<TR>
+ <TD VALIGN="TOP">CUPS-Add-Printer</TD>
+ <TD VALIGN="TOP">1.0</TD>
+ <TD VALIGN="TOP">0x4003</TD>
+ <TD VALIGN="TOP">Add or modify a printer.</TD>
+</TR>
+<TR>
+ <TD VALIGN="TOP">CUPS-Delete-Printer</TD>
+ <TD VALIGN="TOP">1.0</TD>
+ <TD VALIGN="TOP">0x4004</TD>
+ <TD VALIGN="TOP">Delete a printer.</TD>
+</TR>
+<TR>
+ <TD VALIGN="TOP">CUPS-Get-Classes</TD>
+ <TD VALIGN="TOP">1.0</TD>
+ <TD VALIGN="TOP">0x4005</TD>
+ <TD VALIGN="TOP">Get all of the available printer classes.</TD>
+</TR>
+<TR>
+ <TD VALIGN="TOP">CUPS-Add-Class</TD>
+ <TD VALIGN="TOP">1.0</TD>
+ <TD VALIGN="TOP">0x4006</TD>
+ <TD VALIGN="TOP">Add or modify a printer class.</TD>
+</TR>
+<TR>
+ <TD VALIGN="TOP">CUPS-Delete-Class</TD>
+ <TD VALIGN="TOP">1.0</TD>
+ <TD VALIGN="TOP">0x4007</TD>
+ <TD VALIGN="TOP">Delete a printer class.</TD>
+</TR>
+<TR>
+ <TD VALIGN="TOP">CUPS-Accept-Jobs</TD>
+ <TD VALIGN="TOP">1.0</TD>
+ <TD VALIGN="TOP">0x4008</TD>
+ <TD VALIGN="TOP">Accept jobs on a printer or printer class.</TD>
+</TR>
+<TR>
+ <TD VALIGN="TOP">CUPS-Reject-Jobs</TD>
+ <TD VALIGN="TOP">1.0</TD>
+ <TD VALIGN="TOP">0x4009</TD>
+ <TD VALIGN="TOP">Reject jobs on a printer or printer class.</TD>
+</TR>
+<TR>
+ <TD VALIGN="TOP">CUPS-Set-Default</TD>
+ <TD VALIGN="TOP">1.0</TD>
+ <TD VALIGN="TOP">0x400A</TD>
+ <TD VALIGN="TOP">Set the default destination.</TD>
+</TR>
+<TR>
+ <TD VALIGN="TOP">CUPS-Get-Devices</TD>
+ <TD VALIGN="TOP">1.1</TD>
+ <TD VALIGN="TOP">0x400B</TD>
+ <TD VALIGN="TOP">Get all of the available devices.</TD>
+</TR>
+<TR>
+ <TD VALIGN="TOP">CUPS-Get-PPDs</TD>
+ <TD VALIGN="TOP">1.1</TD>
+ <TD VALIGN="TOP">0x400C</TD>
+ <TD VALIGN="TOP">Get all of the available PPDs.</TD>
+</TR>
+<TR>
+ <TD VALIGN="TOP">CUPS-Move-Job</TD>
+ <TD VALIGN="TOP">1.1</TD>
+ <TD VALIGN="TOP">0x400D</TD>
+ <TD VALIGN="TOP">Move a job to a different printer.</TD>
+</TR>
+</TABLE>
+</CENTER>
+
+<H1>Operations</H1>
+
+<P>The following sections describe the operations supported by CUPS.
+In the interest of brevity, operations which use only the standard
+IPP attributes are not described.
+
+<H2>Print-Job Operation</H2>
+
+<P>The Print-Job operation (0x0002) prints a file.
+
+<H3>Print-Job Request</H3>
+
+<P>The following groups of attributes are supplied as part of the
+Print-Job request:
+
+<P>Group 1: Operation Attributes
+
+<UL>
+
+ <P>Natural Language and Character Set:
+
+ <P>The "attributes-charset" and "attributes-natural-language"
+ attributes as described in section 3.1.4.1 of the IPP Model and
+ Semantics document.
+
+ <P>"printer-uri" (uri):
+
+ <P>The client MUST supply a URI for the specified printer.
+
+</UL>
+
+<P>Group 2: Job Template Attributes
+
+<UL>
+
+ <P>"job-billing" (name(MAX)):
+
+ <P><I>(CUPS 1.1 and higher)</I>
+
+ <P>The client OPTIONALLY supplies a billing string that is logged
+ with the page accounting information.
+
+ <P>"job-sheets" (1setof type3 keyword | name(MAX)):
+
+ <P><I>(CUPS 1.1 and higher)</I>
+
+ <P>The client OPTIONALLY supplies one or two banner pages that
+ are printed before and after any files in the print job. The
+ name of "none" is reserved to indicate that no banner page
+ should be printed. If the client does not specify this
+ attribute then the value of the "job-sheets-default" printer
+ object attribute is used.
+
+ <P><B>Note:</B> Standard IPP only allows specification of a single
+ job-sheets attribute value.
+
+ <P>"media" (1setof type3 keyword | name(MAX)):
+
+ <P>The client OPTIONALLY supplies one or more media attributes
+ specifying the size, type, source, and color of the output
+ media. If the client does not specify this attribute then the
+ value of the "media-default" printer object attribute is used.
+
+ <P><B>Note:</B> Standard IPP only allows specification of a single
+ media attribute value.
+
+ <P>Other Job Template Attributes
+
+</UL>
+
+<P>The Print-Job request is followed by a file to be printed.
+
+<H3>Print-Job Response</H3>
+
+<P>The following groups of attributes are send as part of the Print-Job
+Response:
+
+<P>Group 1: Operation Attributes
+
+<UL>
+
+ <P>Status Message:
+
+ <P>The standard response status message.
+
+ <P>Natural Language and Character Set:
+
+ <P>The "attributes-charset" and "attributes-natural-language"
+ attributes as described in section 3.1.4.2 of the IPP Model and
+ Semantics document.
+
+</UL>
+
+<P>Group 2: Job Attributes
+
+<UL>
+
+ <P>Standard Job Attributes
+
+</UL>
+
+<H2>Create-Job Operation</H2>
+
+<P>The Create-Job operation (0x0005) creates a new, empty print job.
+
+<H3>Create-Job Request</H3>
+
+<P>The following groups of attributes are supplied as part of the
+Create-Job request:
+
+<P>Group 1: Operation Attributes
+
+<UL>
+
+ <P>Natural Language and Character Set:
+
+ <P>The "attributes-charset" and "attributes-natural-language"
+ attributes as described in section 3.1.4.1 of the IPP Model and
+ Semantics document.
+
+ <P>"printer-uri" (uri):
+
+ <P>The client MUST supply a URI for the specified printer.
+
+</UL>
+
+<P>Group 2: Job Template Attributes
+
+<UL>
+
+ <P>"job-billing" (name(MAX)):
+
+ <P><I>(CUPS 1.1 and higher)</I>
+
+ <P>The client OPTIONALLY supplies a billing string that is logged
+ with the page accounting information.
+
+ <P>"job-sheets" (1setof type3 keyword | name(MAX)):
+
+ <P><I>(CUPS 1.1 and higher)</I>
+
+ <P>The client OPTIONALLY supplies one or two banner pages that
+ are printed before and after any files in the print job. The
+ name of "none" is reserved to indicate that no banner page
+ should be printed. If the client does not specify this
+ attribute then the value of the "job-sheets-default" printer
+ object attribute is used.
+
+ <P><B>Note:</B> Standard IPP only allows specification of a single
+ job-sheets attribute value.
+
+ <P>"media" (1setof type3 keyword | name(MAX)):
+
+ <P>The client OPTIONALLY supplies one or more media attributes
+ specifying the size, type, source, and color of the output
+ media. If the client does not specify this attribute then the
+ value of the "media-default" printer object attribute is used.
+
+ <P><B>Note:</B> Standard IPP only allows specification of a single
+ media attribute value.
+
+ <P>Standard Job Template Attributes
+
+</UL>
+
+<H3>Create-Job Response</H3>
+
+<P>The following groups of attributes are send as part of the
+Create-Job Response:
+
+<P>Group 1: Operation Attributes
+
+<UL>
+
+ <P>Status Message:
+
+ <P>The standard response status message.
+
+ <P>Natural Language and Character Set:
+
+ <P>The "attributes-charset" and "attributes-natural-language"
+ attributes as described in section 3.1.4.2 of the IPP Model and
+ Semantics document.
+
+</UL>
+
+<P>Group 2: Job Attributes
+
+<UL>
+
+ <P>Standard Job Attributes
+
+</UL>
+
+<H2>Set-Job-Attributes Operation</H2>
+
+<P>The Set-Job-Attributes operation (0x0014) changes the attributes of
+an active (not completed) job.
+
+<H3>Set-Job-Attributes Request</H3>
+
+<P>The following groups of attributes are supplied as part of the
+Set-Job-Attributes request:
+
+<P>Group 1: Operation Attributes
+
+<UL>
+
+ <P>Natural Language and Character Set:
+
+ <P>The "attributes-charset" and "attributes-natural-language"
+ attributes as described in section 3.1.4.1 of the IPP Model and
+ Semantics document.
+
+ <P>"printer-uri" (uri) and "job-id" (integer)
+ <P><I>OR</I>
+ <P>"job-uri":
+
+ <P>The client MUST supply a URI for the specified printer and
+ a job ID number, or the job URI.
+
+</UL>
+
+<P>Group 2: Job Template Attributes
+
+<UL>
+
+ <P>"job-sheets" (1setof type3 keyword | name(MAX)):
+
+ <P><I>(CUPS 1.1 and higher)</I>
+
+ <P>The client OPTIONALLY supplies one or two banner pages that
+ are printed before and after any files in the print job. The
+ name of "none" is reserved to indicate that no banner page
+ should be printed. If the client does not specify this
+ attribute then the value of the "job-sheets-default" printer
+ object attribute is used.
+
+ <P><B>Note:</B> Standard IPP only allows specification of a single
+ job-sheets attribute value.
+
+ <P>"media" (1setof type3 keyword | name(MAX)):
+
+ <P>The client OPTIONALLY supplies one or more media attributes
+ specifying the size, type, source, and color of the output
+ media. If the client does not specify this attribute then the
+ value of the "media-default" printer object attribute is used.
+
+ <P><B>Note:</B> Standard IPP only allows specification of a single
+ media attribute value.
+
+ <P>Other Job Template Attributes
+
+</UL>
+
+<H3>Set-Job-Attributes Response</H3>
+
+<P>The following groups of attributes are send as part of the Set-Job-Attributes
+Response:
+
+<P>Group 1: Operation Attributes
+
+<UL>
+
+ <P>Status Message:
+
+ <P>The standard response status message.
+
+ <P>Natural Language and Character Set:
+
+ <P>The "attributes-charset" and "attributes-natural-language"
+ attributes as described in section 3.1.4.2 of the IPP Model and
+ Semantics document.
+
+</UL>
+
+<H2>CUPS-Get-Default Operation</H2>
+
+<P>The CUPS-Get-Default operation (0x4001) returns the default printer
+URI and attributes.
+
+<H3>CUPS-Get-Default Request</H3>
+
+<P>The following groups of attributes are supplied as part of the
+CUPS-Get-Default request:
+
+<P>Group 1: Operation Attributes
+
+<UL>
+
+ <P>Natural Language and Character Set:
+
+ <P>The "attributes-charset" and "attributes-natural-language"
+ attributes as described in section 3.1.4.1 of the IPP Model and
+ Semantics document.
+
+ <P>"requested-attributes" (1setOf keyword) :
+
+ <P>The client OPTIONALLY supplies a set of attribute names
+ and/or attribute group names in whose values the requester is
+ interested. If the client omits this attribute, the server
+ responds as if this attribute had been supplied with a value of
+ 'all'.
+
+</UL>
+
+<H3>CUPS-Get-Default Response</H3>
+
+<P>The following groups of attributes are send as part of the
+CUPS-Get-Default Response:
+
+<P>Group 1: Operation Attributes
+
+<UL>
+
+ <P>Status Message:
+
+ <P>The standard response status message.
+
+ <P>Natural Language and Character Set:
+
+ <P>The "attributes-charset" and "attributes-natural-language"
+ attributes as described in section 3.1.4.2 of the IPP Model and
+ Semantics document.
+
+</UL>
+
+<P>Group 2: Printer Object Attributes
+
+<UL>
+
+ <P>The set of requested attributes and their current values.
+
+</UL>
+
+<H2>CUPS-Get-Printers Operation</H2>
+
+<P>The CUPS-Get-Printers operation (0x4002) returns the printer
+attributes for every printer known to the system. This may include
+printers that are not served directly by the server.
+
+<H3>CUPS-Get-Printers Request</H3>
+
+<P>The following groups of attributes are supplied as part of the
+CUPS-Get-Printers request:
+
+<P>Group 1: Operation Attributes
+
+<UL>
+
+ <P>Natural Language and Character Set:
+
+ <P>The "attributes-charset" and "attributes-natural-language"
+ attributes as described in section 3.1.4.1 of the IPP Model and
+ Semantics document.
+
+ <P>"limit" (integer (1:MAX)):
+
+ <P>The client OPTIONALLY supplies this attribute limiting the
+ number of printers that are returned.
+
+ <P>"printer-info" (text(127)):
+
+ <P><I>(CUPS 1.1 and higher)</I>
+
+ <P>The client OPTIONALLY supplies this attribute to
+ select which printers are returned.
+
+ <P>"printer-location" (text(127)):
+
+ <P><I>(CUPS 1.1 and higher)</I>
+
+ <P>The client OPTIONALLY supplies this attribute to
+ select which printers are returned.
+
+ <P>"printer-type" (type2 enum):
+
+ <P><I>(CUPS 1.1 and higher)</I>
+
+ <P>The client OPTIONALLY supplies a printer type enumeration to
+ select which printers are returned.
+
+ <P>"printer-type-mask" (type2 enum):
+
+ <P><I>(CUPS 1.1 and higher)</I>
+
+ <P>The client OPTIONALLY supplies a printer type mask
+ enumeration to select which bits are used in the "printer-type"
+ attribute.
+
+ <P>"requested-attributes" (1setOf keyword) :
+
+ <P>The client OPTIONALLY supplies a set of attribute names
+ and/or attribute group names in whose values the requester is
+ interested. If the client omits this attribute, the server
+ responds as if this attribute had been supplied with a value of
+ 'all'.
+
+</UL>
+
+<H3>CUPS-Get-Printers Response</H3>
+
+<P>The following groups of attributes are send as part of the
+CUPS-Get-Printers Response:
+
+<P>Group 1: Operation Attributes
+
+<UL>
+
+ <P>Status Message:
+
+ <P>The standard response status message.
+
+ <P>Natural Language and Character Set:
+
+ <P>The "attributes-charset" and "attributes-natural-language"
+ attributes as described in section 3.1.4.2 of the IPP Model and
+ Semantics document.
+
+</UL>
+
+<P>Group 2: Printer Object Attributes
+
+<UL>
+
+ <P>The set of requested attributes and their current values for
+ each printer.
+
+</UL>
+
+<H2>CUPS-Add-Printer Operation</H2>
+
+<P>The CUPS-Add-Printer operation (0x4003) adds a new printer or
+modifies an existing printer on the system.
+
+<H3>CUPS-Add-Printer Request</H3>
+
+<P>The following groups of attributes are supplied as part of the
+CUPS-Add-Printer request:
+
+<P>Group 1: Operation Attributes
+
+<UL>
+
+ <P>Natural Language and Character Set:
+
+ <P>The "attributes-charset" and "attributes-natural-language"
+ attributes as described in section 3.1.4.1 of the IPP Model and
+ Semantics document.
+
+ <P>"printer-uri" (uri):
+
+ <P>The client MUST supply a URI for the specified printer.
+
+</UL>
+
+<P>Group 2: Printer Object Attributes
+
+<UL>
+
+ <P>"banner-end-default" (name(127)):
+
+ <P><I>(CUPS 1.1 and higher)</I>
+
+ <P>The client OPTIONALLY supplies a banner page name that is
+ printed after files in a job. The reserved name "none" is used to
+ specify that no banner page should be printed.
+
+ <P>"banner-start-default" (name(127)):
+
+ <P><I>(CUPS 1.1 and higher)</I>
+
+ <P>The client OPTIONALLY supplies a banner page name that is
+ printed before files in a job. The reserved name "none" is used to
+ specify that no banner page should be printed.
+
+ <P>"device-uri" (uri):
+
+ <P>The client OPTIONALLY supplies a device URI for the
+ specified printer.
+
+ <P>"ppd-name" (name(127)):
+
+ <P>The client OPTIONALLY supplies a PPD name for the specified
+ printer.
+
+ <P>"printer-is-accepting-jobs" (boolean):
+
+ <P>The client OPTIONALLY supplies this boolean attribute
+ indicating whether or not the printer object should accept new jobs.
+
+ <P>"printer-info" (text(127)):
+
+ <P>The client OPTIONALLY supplies this attribute indicating the
+ printer information string.
+
+ <P>"printer-location" (text(127)):
+
+ <P>The client OPTIONALLY supplies this attribute indicating a
+ textual location of the printer.
+
+ <P>"printer-more-info" (uri):
+
+ <P>The client OPTIONALLY supplies this attribute indicating a
+ URI for additional printer information.
+
+ <P>"printer-state" (type2 enum):
+
+ <P>The client OPTIONALLY supplies this attribute indicating the
+ initial/current state of the printer. Only the "idle" and "stopped"
+ enumerations are recognized.
+
+ <P>"printer-state-message" (text(MAX)):
+
+ <P>The client OPTIONALLY supplies this attribute indicating a
+ textual reason for the current printer state.
+
+ <P>"requesting-user-name-allowed" (1setof name(127))
+ <P><I>OR</I>
+ <P>"requesting-user-name-denied" (1setof name(127)):
+
+ <P>The client OPTIONALLY supplies one of these attributes to
+ specify an access control list for incoming print jobs. The
+ special name "ALLUSERS" is reserved to indicate that all users
+ are allowed or denied.
+
+</UL>
+
+<P>The CUPS-Add-Printer request can optionally be followed by a PPD
+file or System V interface script to be used for the printer. The
+"ppd-name" attribute overrides any file that is attached to the end of
+the request with a local CUPS PPD file.
+
+<H3>CUPS-Add-Printer Response</H3>
+
+<P>The following groups of attributes are send as part of the
+CUPS-Add-Printer Response:
+
+<P>Group 1: Operation Attributes
+
+<UL>
+
+ <P>Status Message:
+
+ <P>The standard response status message.
+
+ <P>Natural Language and Character Set:
+
+ <P>The "attributes-charset" and "attributes-natural-language"
+ attributes as described in section 3.1.4.2 of the IPP Model and
+ Semantics document.
+
+</UL>
+
+<H2>CUPS-Delete-Printer Operation</H2>
+
+<P>The CUPS-Delete-Printer operation (0x4004) removes an existing
+printer from the system.
+
+<H3>CUPS-Delete-Printer Request</H3>
+
+<P>The following groups of attributes are supplied as part of the
+CUPS-Delete-Printer request:
+
+<P>Group 1: Operation Attributes
+
+<UL>
+
+ <P>Natural Language and Character Set:
+
+ <P>The "attributes-charset" and "attributes-natural-language"
+ attributes as described in section 3.1.4.1 of the IPP Model and
+ Semantics document.
+
+ <P>"printer-uri" (uri):
+
+ <P>The client MUST supply a URI for the specified printer.
+
+</UL>
+
+<H3>CUPS-Delete-Printer Response</H3>
+
+<P>The following groups of attributes are send as part of the
+CUPS-Delete-Printer Response:
+
+<P>Group 1: Operation Attributes
+
+<UL>
+
+ <P>Status Message:
+
+ <P>The standard response status message.
+
+ <P>Natural Language and Character Set:
+
+ <P>The "attributes-charset" and "attributes-natural-language"
+ attributes as described in section 3.1.4.2 of the IPP Model and
+ Semantics document.
+
+</UL>
+
+<H2>CUPS-Get-Classes Operation</H2>
+
+<P>The CUPS-Get-Classes operation (0x4005) returns the printer
+attributes for every printer class known to the system. This may
+include printer classes that are not served directly by the server.
+
+<H3>CUPS-Get-Classes Request</H3>
+
+<P>The following groups of attributes are supplied as part of the
+CUPS-Get-Classes request:
+
+<P>Group 1: Operation Attributes
+
+<UL>
+
+ <P>Natural Language and Character Set:
+
+ <P>The "attributes-charset" and "attributes-natural-language"
+ attributes as described in section 3.1.4.1 of the IPP Model and
+ Semantics document.
+
+ <P>"limit" (integer (1:MAX)):
+
+ <P>The client OPTIONALLY supplies this attribute limiting the
+ number of printer classes that are returned.
+
+ <P>"printer-info" (text(127)):
+
+ <P><I>(CUPS 1.1 and higher)</I>
+
+ <P>The client OPTIONALLY supplies this attribute to
+ select which printer classes are returned.
+
+ <P>"printer-location" (text(127)):
+
+ <P><I>(CUPS 1.1 and higher)</I>
+
+ <P>The client OPTIONALLY supplies this attribute to
+ select which printer classes are returned.
+
+ <P>"printer-type" (type2 enum):
+
+ <P><I>(CUPS 1.1 and higher)</I>
+
+ <P>The client OPTIONALLY supplies a printer type enumeration to
+ select which printer classes are returned.
+
+ <P>"printer-type-mask" (type2 enum):
+
+ <P><I>(CUPS 1.1 and higher)</I>
+
+ <P>The client OPTIONALLY supplies a printer type mask
+ enumeration to select which bits are used in the "printer-type"
+ attribute.
+
+ <P>"requested-attributes" (1setOf keyword) :
+
+ <P>The client OPTIONALLY supplies a set of attribute names
+ and/or attribute group names in whose values the requester is
+ interested. If the client omits this attribute, the server responds as
+ if this attribute had been supplied with a value of 'all'.
+
+</UL>
+
+<H3>CUPS-Get-Classes Response</H3>
+
+<P>The following groups of attributes are send as part of the
+CUPS-Get-Classes Response:
+
+<P>Group 1: Operation Attributes
+
+<UL>
+
+ <P>Status Message:
+
+ <P>The standard response status message.
+
+ <P>Natural Language and Character Set:
+
+ <P>The "attributes-charset" and "attributes-natural-language"
+ attributes as described in section 3.1.4.2 of the IPP Model and
+ Semantics document.
+
+</UL>
+
+<P>Group 2: Printer Class Object Attributes
+
+<UL>
+
+ <P>The set of requested attributes and their current values for
+ each printer class.
+
+</UL>
+
+<H2>CUPS-Add-Class Operation</H2>
+
+<P>The CUPS-Add-Class operation (0x4006) adds a new printer class or
+modifies and existing printer class on the system.
+
+<H3>CUPS-Add-Class Request</H3>
+
+<P>The following groups of attributes are supplied as part of the
+CUPS-Add-Class request:
+
+<P>Group 1: Operation Attributes
+
+<UL>
+
+ <P>Natural Language and Character Set:
+
+ <P>The "attributes-charset" and "attributes-natural-language"
+ attributes as described in section 3.1.4.1 of the IPP Model and
+ Semantics document.
+
+ <P>"printer-uri" (uri):
+
+ <P>The client MUST supply a URI for the specified printer class.
+
+</UL>
+
+<P>Group 2: Printer Object Attributes
+
+<UL>
+
+ <P>"member-uris" (1setof uri):
+
+ <P>The client OPTIONALLY supplies the "member-uris" set
+ specifying the printers and printer classes that are part of the class.
+
+ <P>"printer-is-accepting-jobs" (boolean):
+
+ <P>The client OPTIONALLY supplies this boolean attribute
+ indicating whether or not the printer object should accept new jobs.
+
+ <P>"printer-info" (text(127)):
+
+ <P>The client OPTIONALLY supplies this attribute indicating the
+ printer information string.
+
+ <P>"printer-location" (text(127)):
+
+ <P>The client OPTIONALLY supplies this attribute indicating a
+ textual location of the printer.
+
+ <P>"printer-more-info" (uri):
+
+ <P>The client OPTIONALLY supplies this attribute indicating a
+ URI for additional printer information.
+
+ <P>"printer-state" (type2 enum):
+
+ <P>The client OPTIONALLY supplies this attribute indicating the
+ initial/current state of the printer. Only the "idle" and "stopped"
+ enumerations are recognized.
+
+ <P>"printer-state-message" (text(MAX)):
+
+ <P>The client OPTIONALLY supplies this attribute indicating a
+ textual reason for the current printer state.
+
+ <P>"requesting-user-name-allowed" (1setof name(127))
+ <P><I>OR</I>
+ <P>"requesting-user-name-denied" (1setof name(127)):
+
+ <P>The client OPTIONALLY supplies one of these attributes to
+ specify an access control list for incoming print jobs. The
+ special name "ALLUSERS" is reserved to indicate that all users
+ are allowed or denied.
+
+</UL>
+
+<H3>CUPS-Add-Class Response</H3>
+
+<P>The following groups of attributes are send as part of the CUPS-Add-Class Response:
+
+<P>Group 1: Operation Attributes
+
+<UL>
+
+ <P>Status Message:
+
+ <P>The standard response status message.
+
+ <P>Natural Language and Character Set:
+
+ <P>The "attributes-charset" and "attributes-natural-language"
+ attributes as described in section 3.1.4.2 of the IPP Model and
+ Semantics document.
+
+</UL>
+
+<H2>CUPS-Delete-Class Operation</H2>
+
+<P>The CUPS-Delete-Class operation (0x4007) removes an existing printer
+class from the system.
+
+<H3>CUPS-Delete-Class Request</H3>
+
+<P>The following groups of attributes are supplied as part of the
+CUPS-Delete-Class request:
+
+<P>Group 1: Operation Attributes
+
+<UL>
+
+ <P>Natural Language and Character Set:
+
+ <P>The "attributes-charset" and "attributes-natural-language"
+ attributes as described in section 3.1.4.1 of the IPP Model and
+ Semantics document.
+
+ <P>"printer-uri" (uri):
+
+ <P>The client MUST supply a URI for the specified printer class.
+
+</UL>
+
+<H3>CUPS-Delete-Class Response</H3>
+
+<P>The following groups of attributes are send as part of the
+CUPS-Delete-Class Response:
+
+<P>Group 1: Operation Attributes
+
+<UL>
+
+ <P>Status Message:
+
+ <P>The standard response status message.
+
+ <P>Natural Language and Character Set:
+
+ <P>The "attributes-charset" and "attributes-natural-language"
+ attributes as described in section 3.1.4.2 of the IPP Model and
+ Semantics document.
+
+</UL>
+
+<H2>CUPS-Accept-Jobs Operation</H2>
+
+<P>The CUPS-Accept-Jobs operation (0x4008) sets the
+"printer-is-accepting-jobs" attribute to true for the specified printer
+or printer class.
+
+<H3>CUPS-Accept-Jobs Request</H3>
+
+<P>The following groups of attributes are supplied as part of the
+CUPS-Accept-Jobs request:
+
+<P>Group 1: Operation Attributes
+
+<UL>
+
+ <P>Natural Language and Character Set:
+
+ <P>The "attributes-charset" and "attributes-natural-language"
+ attributes as described in section 3.1.4.1 of the IPP Model and
+ Semantics document.
+
+ <P>"printer-uri" (uri):
+
+ <P>The client MUST supply a URI for the specified printer or printer class.
+
+</UL>
+
+<H3>CUPS-Accept-Jobs Response</H3>
+
+<P>The following groups of attributes are send as part of the
+CUPS-Accept-Jobs Response:
+
+<P>Group 1: Operation Attributes
+
+<UL>
+
+ <P>Status Message:
+
+ <P>The standard response status message.
+
+ <P>Natural Language and Character Set:
+
+ <P>The "attributes-charset" and "attributes-natural-language"
+ attributes as described in section 3.1.4.2 of the IPP Model and
+ Semantics document.
+
+</UL>
+
+<H2>CUPS-Reject-Jobs Operation</H2>
+
+<P>The CUPS-Reject-Jobs operation (0x4009) sets
+the"printer-is-accepting-jobs" attribute to false for the specified
+printer or printer class.
+
+<H3>CUPS-Reject-Jobs Request</H3>
+
+<P>The following groups of attributes are supplied as part of the
+CUPS-Reject-Jobs request:
+
+<P>Group 1: Operation Attributes
+
+<UL>
+
+ <P>Natural Language and Character Set:
+
+ <P>The "attributes-charset" and "attributes-natural-language"
+ attributes as described in section 3.1.4.1 of the IPP Model and
+ Semantics document.
+
+ <P>"printer-uri" (uri):
+
+ <P>The client MUST supply a URI for the specified printer or printer class.
+
+</UL>
+
+<P>Group 2: Printer Object Attributes
+
+<UL>
+
+ <P>"printer-state-message" (text(MAX)):
+
+ <P>The client OPTIONALLY supplies this attribute indicating a
+ textual reason for the current printer state.
+
+</UL>
+
+<H3>CUPS-Reject-Jobs Response</H3>
+
+<P>The following groups of attributes are send as part of the
+CUPS-Reject-Jobs Response:
+
+<P>Group 1: Operation Attributes
+
+<UL>
+
+ <P>Status Message:
+
+ <P>The standard response status message.
+
+ <P>Natural Language and Character Set:
+
+ <P>The "attributes-charset" and "attributes-natural-language"
+ attributes as described in section 3.1.4.2 of the IPP Model and
+ Semantics document.
+
+</UL>
+
+<H2>CUPS-Set-Default Operation</H2>
+
+<P>The CUPS-Set-Default operation (0x400A) sets the default printer
+destination for all clients when a resource name of "/printers" is
+specified.
+
+<H3>CUPS-Set-Default Request</H3>
+
+<P>The following groups of attributes are supplied as part of the
+CUPS-Set-Default request:
+
+<P>Group 1: Operation Attributes
+
+<UL>
+
+ <P>Natural Language and Character Set:
+
+ <P>The "attributes-charset" and "attributes-natural-language"
+ attributes as described in section 3.1.4.1 of the IPP Model and
+ Semantics document.
+
+ <P>"printer-uri" (uri):
+
+ <P>The client MUST supply a URI for the specified printer or
+ printer class.
+
+</UL>
+
+<H3>CUPS-Set-Default Response</H3>
+
+<P>The following groups of attributes are send as part of the
+CUPS-Set-Default Response:
+
+<P>Group 1: Operation Attributes
+
+<UL>
+
+ <P>Status Message:
+
+ <P>The standard response status message.
+
+ <P>Natural Language and Character Set:
+
+ <P>The "attributes-charset" and "attributes-natural-language"
+ attributes as described in section 3.1.4.2 of the IPP Model and
+ Semantics document.
+
+</UL>
+
+<H2>CUPS-Get-Devices Operation</H2>
+
+<P>The CUPS-Get-Devices operation (0x400B) returns all of the supported
+device-uri's for the server (CUPS 1.1 and higher).
+
+<H3>CUPS-Get-Devices Request</H3>
+
+<P>The following groups of attributes are supplied as part of the
+CUPS-Get-Devices request:
+
+<P>Group 1: Operation Attributes
+
+<UL>
+
+ <P>Natural Language and Character Set:
+
+ <P>The "attributes-charset" and "attributes-natural-language"
+ attributes as described in section 3.1.4.1 of the IPP Model and
+ Semantics document.
+
+ <P>"device-class" (type1 keyword):
+
+ <P>The client OPTIONALLY supplies a device class keyword to select
+ which devices are returned.
+
+ <P>"limit" (integer (1:MAX)):
+
+ <P>The client OPTIONALLY supplies this attribute limiting the number of
+ devices that are returned.
+
+ <P>"requested-attributes" (1setOf keyword) :
+
+ <P>The client OPTIONALLY supplies a set of attribute names and/or
+ attribute group names in whose values the requester is interested. If
+ the client omits this attribute, the server responds as if this
+ attribute had been supplied with a value of 'all'.
+
+</UL>
+
+<H3>CUPS-Get-Devices Response</H3>
+
+<P>The following groups of attributes are send as part of the
+CUPS-Get-Devices Response:
+
+<P>Group 1: Operation Attributes
+
+<UL>
+
+ <P>Status Message:
+
+ <P>The standard response status message.
+
+ <P>Natural Language and Character Set:
+
+ <P>The "attributes-charset" and "attributes-natural-language"
+ attributes as described in section 3.1.4.2 of the IPP Model and
+ Semantics document.
+
+</UL>
+
+<P>Group 2: Device Object Attributes
+
+<UL>
+
+ <P>The set of requested attributes and their current values for
+ each device.
+
+</UL>
+
+<H2>CUPS-Get-PPDs Operation</H2>
+
+<P>The CUPS-Get-PPDs operation (0x400C) returns all of the locally
+available PPD files on the system (CUPS 1.1 and higher).
+
+<H3>CUPS-Get-PPDs Request</H3>
+
+<P>The following groups of attributes are supplied as part of the
+CUPS-Get-PPDs request:
+
+<P>Group 1: Operation Attributes
+
+<UL>
+
+ <P>Natural Language and Character Set:
+
+ <P>The "attributes-charset" and "attributes-natural-language"
+ attributes as described in section 3.1.4.1 of the IPP Model and
+ Semantics document.
+
+ <P>"limit" (integer (1:MAX)):
+
+ <P>The client OPTIONALLY supplies this attribute limiting the number of
+ PPDs that are returned.
+
+ <P>"ppd-make" (text(127)):
+
+ <P>The client OPTIONALLY supplies a printer manufacturer to select
+ which PPDs are returned.
+
+ <P>"requested-attributes" (1setOf keyword) :
+
+ <P>The client OPTIONALLY supplies a set of attribute names and/or
+ attribute group names in whose values the requester is interested. If
+ the client omits this attribute, the server responds as if this
+ attribute had been supplied with a value of 'all'.
+
+</UL>
+
+<H3>CUPS-Get-PPDs Response</H3>
+
+<P>The following groups of attributes are send as part of the
+CUPS-Get-PPDs Response:
+
+<P>Group 1: Operation Attributes
+
+<UL>
+
+ <P>Status Message:
+
+ <P>The standard response status message.
+
+ <P>Natural Language and Character Set:
+
+ <P>The "attributes-charset" and "attributes-natural-language"
+ attributes as described in section 3.1.4.2 of the IPP Model and
+ Semantics document.
+
+</UL>
+
+<P>Group 2: PPD Attributes
+
+<UL>
+
+ <P>The set of requested attributes and their current values for each
+ PPD file.
+
+</UL>
+
+<H2>CUPS-Move-Job Operation</H2>
+
+<P>The CUPS-Move-Job operation (0x400D) moves an active print job to a
+different printer (CUPS 1.1 and higher).
+
+<H3>CUPS-Move-Job Request</H3>
+
+<P>The following groups of attributes are supplied as part of the
+CUPS-Move-Job request:
+
+<P>Group 1: Operation Attributes
+
+<UL>
+
+ <P>Natural Language and Character Set:
+
+ <P>The "attributes-charset" and "attributes-natural-language"
+ attributes as described in section 3.1.4.1 of the IPP Model and
+ Semantics document.
+
+ <P>"printer-uri" (uri) and "job-id" (integer)
+ <P><I>OR</I>
+ <P>"job-uri":
+
+ <P>The client MUST supply a URI for the specified printer and
+ a job ID number, or the job URI.
+
+</UL>
+
+<H3>CUPS-Move-Job Response</H3>
+
+<P>The following groups of attributes are send as part of the
+CUPS-Move-Job Response:
+
+<P>Group 1: Operation Attributes
+
+<UL>
+
+ <P>Status Message:
+
+ <P>The standard response status message.
+
+ <P>Natural Language and Character Set:
+
+ <P>The "attributes-charset" and "attributes-natural-language"
+ attributes as described in section 3.1.4.2 of the IPP Model and
+ Semantics document.
+
+</UL>
+
+<H1>Attributes</H1>
+
+<P>CUPS provides many extension attributes to support multiple devices,
+PPD files, standard job filters, printers, and printer classes.
+
+<H2>Device Attributes</H2>
+
+<P>Device attributes are returned by the CUPS-Get-Devices operation and
+enumerate all of the available hardware devices and network protocols
+that are supported by the server.
+
+<H3>device-class (type2 keyword)</H3>
+
+<P>The device-class attribute specifies the class of device and can be
+one of the following:
+
+<UL>
+
+ <LI>"file" - a disk file.
+
+ <LI>"direct" - a parallel or fixed-rate serial data port,
+ currently used for Centronics, IEEE-1284, and USB printer
+ ports.
+
+ <LI>"serial" - a variable-rate serial port.
+
+ <LI>"network" - a network connection, typically via AppSocket,
+ HTTP, IPP, LPD, or SMB/CIFS protocols.
+
+</UL>
+
+<H3>device-info (text(127))</H3>
+
+<P>The device-info attribute specifies a human-readable string describing
+the device, e.g. "Parallel Port #1".
+
+<H3>device-make-and-model (text(127))</H3>
+
+<P>The device-makr-and-model attribute specifies a device
+identification string provided by the printer connected to the device.
+If the device or printer does not support identification then this
+attribute contains the string "unknown".
+
+<H3>device-uri (uri)</H3>
+
+<P>The device-uri attribute specifies a unique identifier for the
+device. The actual format of the device-uri string depends on the value
+of the device-class attribute:
+
+<UL>
+
+ <LI>"file" - The device-uri will be of the form
+ "file:/path/to/filename".
+
+ <LI>"direct" - The device-uri will be of the form
+ "method:/dev/filename", where method may be "parallel" or "usb"
+ in the current implementation.
+
+ <LI>"serial" - The device-uri will be of the form
+ "serial:/dev/filename?baud=value+parity=value+flow=value".
+ The parity value can be one of "none", "even", or "odd".
+ The flow value can be one of "none", "soft" (XON/XOFF
+ handshaking), or "hard" (CTS/RTS handshaking).
+
+ <P>The URI returned by CUPS-Get-Devices will contain the
+ maximum baud rate supported by the device and the best
+ type of flow control available ("soft" or "hard").
+
+ <LI>"network" - The device-uri will be of the form
+ "method://[username:password@]hostname[:port]/[resource]",
+ where method may be "http", "ipp", "lpd", "smb", or
+ "socket" in the current implementation.
+
+ <P>The URI returned by CUPS-Get-Devices will only contain
+ the method name followed by two slashes ("method://").
+ It is up to the client application to add the appropriate
+ host and other information when adding a new printer.
+
+ <P>The URI returned by Get-Printer-Attributes and
+ CUPS-Get-Printers has any username and password information
+ stripped; the information is still stored and used by the
+ server internally to perform any needed authentication.
+
+</UL>
+
+<H2>Job Template Attributes</H2>
+
+<H3>blackplot (boolean)</H3>
+
+<P>The blackplot attribute specifies whether HP-GL/2 plot files should be
+rendered entirely in black ink (blackplot=true) or using the colors and shades
+specified in the file (blackplot=false). The default value is false.
+
+<H3>brightness (integer(0:200))</H3>
+
+<P>The brightness attribute specifies the overall brightness of the printed
+output in percent. A brightness of 100 is normal, while 200 is twice as
+bright and 50 is half as bright. The default value is 100.
+
+<P>Brightness is applied to the Cyan, Magenta, Yellow, and Black values using
+the function "f(x) = brightness / 100 * x".
+
+<H3>columns (integer(1:4))</H3>
+
+<P>The columns attribute specifies the number of columns to generate when
+printing text files. The default value is 1.
+
+<H3>cpi (type2 enum)</H3>
+
+<P>The cpi attribute specifies the number of characters per inch when
+printing text files. Only the values 10, 12, and 17 are currently
+supported. The default value is 10.
+
+<H3>fitplot (boolean)</H3>
+
+<P>The fitplot attribute specifies whether to scale HP-GL/2 plot files to
+fit on the selected media (fitplot=true) or use the physical scale specified
+in the plot file (fitplot=false). The default value is false.
+
+<H3>gamma (integer(1:10000))</H3>
+
+<P>The gamma attribute specifies the luminance correction for the output.
+A value of 1000 specifies no correction, while values of 2000 and 500 will
+generate lighter and darker output, respectively. The default value is
+1000.
+
+<P>Gamma is applied to the Red, Green, and Blue values (or luminance for
+grayscale output) using the function "f(x) = x<SUP>(1000/gamma)</SUP>".
+
+<H3>hue (integer(-180:180))</H3>
+
+<P>The hue attribute specifies a color hue rotation when printing image
+files. The default value is 0.
+
+<H3>job-billing (name(MAX))</H3>
+
+<P><I>(CUPS 1.1 and higher)</I>
+
+<P>The job-billing attribute provides a name value to associate with a job
+for billing purposes.
+
+<H3>job-sheets (1setof type3 keyword | name(MAX))</H3>
+
+<P><I>(CUPS 1.1 and higher)</I>
+
+<P>The job-sheets attribute specifies one or two banner files that are printed
+before and after a job. The reserved value of "none" disables banner printing.
+The default value is stored in the job-sheets-default attribute.
+
+<P>If only one value is supplied, the banner file is printed before the job.
+If two values are supplied, the first value is used as the starting banner
+file and the second as the ending banner file.
+
+<H3>lpi (type2 enum)</H3>
+
+<P>The lpi attribute specifies the number of lines per inch when
+printing text files. Only the values 6 and 8 are currently supported.
+The default value is 6.
+
+<H3>page-bottom (integer(0:MAX))</H3>
+
+<P>The page-bottom attribute specifies the bottom margin in points (72 points
+equals 1 inch). The default value is the device physical margin.
+
+<H3>page-left (integer(0:MAX))</H3>
+
+<P>The page-left attribute specifies the left margin in points (72 points
+equals 1 inch). The default value is the device physical margin.
+
+<H3>page-right (integer(0:MAX))</H3>
+
+<P>The page-right attribute specifies the right margin in points (72 points
+equals 1 inch). The default value is the device physical margin.
+
+<H3>page-set (type2 keyword)</H3>
+
+<P>The page-set attribute specifies which pages to print in a file. The
+supported keywords are "all", "even", and "odd". The default value is
+"all".
+
+<H3>page-top (integer(0:MAX))</H3>
+
+<P>The page-top attribute specifies the top margin in points (72 points
+equals 1 inch). The default value is the device physical margin.
+
+<H3>penwidth (integer(0:MAX))</H3>
+
+<P>The penwidth attribute specifies the default pen width in micrometers
+when printing HP-GL/2 plot files. The default value is 1000 (1 millimeter).
+
+<H3>ppi (integer(1:MAX))</H3>
+
+<P>The ppi attribute specifies the resolution of an image file in pixels
+per inch. The default value is the resolution included with the file or
+128 if no resolution information is available.
+
+<H3>prettyprint (boolean)</H3>
+
+<P>The prettyprint attribute specifies whether text files should be printed
+with a shaded header and keyword highlighting (prettyprint=true) or without
+additional formatting (prettyprint=false). The default value is false.
+
+<H3>saturation (integer(0:200))</H3>
+
+<P>The saturation attribute specifies the color saturation when
+printing image files. A saturation of 100 is normal, while values of 50
+and 200 will be half and twice as colorful, respectively. The default
+value is 100.
+
+<H3>scaling (integer(1:1000))</H3>
+
+<P>The scaling attribute specifies the scaling of image files with
+respect to the selected media. A value of 100 specifies that the image
+file should fit 100% of the page, or as much as possible given the
+image dimensions. The default value is unspecified.
+
+<P>The scaling attribute overrides the ppi attribute if specified.
+
+<H3>wrap (boolean)</H3>
+
+<P>The wrap attribute specifies whether long lines should be wrapped
+(wrap=true) or not (wrap=false) when printing text files. The default
+value is true.
+
+<H2>PPD Attributes</H2>
+
+<H3>ppd-natural-language (naturalLanguage)</H3>
+
+<P>The ppd-natural-language attribute specifies the language encoding
+of the PPD file (the LanguageVersion attribute in the PPD file). If the
+language is unknown or undefined then "en" (English) is assumed.
+
+<H3>ppd-make (text(127))</H3>
+
+<P>The ppd-make attribute specifies the manufacturer of the printer
+(the Manufacturer attribute in the PPD file). If the manufacturer
+is not specified in the PPD file then an educated guess is made using
+the NickName attribute in the PPD file.
+
+<H3>ppd-make-and-model (text(127))</H3>
+
+<P>The ppd-make-and-model attribute specifies the manufacturer and model
+name of the PPD file (the NickName attribute in the PPD file). If the
+make and model is not specified in the PPD file then the ModelName or
+ShortNickName attributes are used instead.
+
+<H3>ppd-name (name(255))</H3>
+
+<P>The ppd-name attribute specifies the PPD filename on the server
+relative to the model directory. The forward slash (/) is used to
+delineate directories.
+
+<H2>Printer Attributes</H2>
+
+<H3>job-sheets-default (1setof type3 keyword | name(MAX))</H3>
+
+<P><I>(CUPS 1.1 and higher)</I>
+
+<P>The job-sheets-default attribute specifies the default banner file(s) to
+print before and after each job. The value "none" specifies that no banner
+should be printed.
+
+<H3>job-sheets-supported (1setof type3 keyword | name(MAX))</H3>
+
+<P><I>(CUPS 1.1 and higher)</I>
+
+<P>The job-sheets-supported attribute specifies the available banner files.
+There will always be at least one banner file available called "none".
+
+<H3>printer-type (type2 enum)</H3>
+
+<P>The printer-type attribute specifies printer type and capability bits for
+the printer or class. The default value is computed from internal state
+information and the PPD file for the printer. The following bits are defined:
+
+<CENTER><TABLE WIDTH="80%" BORDER>
+<TR>
+ <TH>Bit</TH>
+ <TH>Description</TH>
+</TR>
+<TR>
+ <TD VALIGN="TOP">0x00000001</TD>
+ <TD VALIGN="TOP">Is a printer class.</TD>
+</TR>
+<TR>
+ <TD VALIGN="TOP">0x00000002</TD>
+ <TD VALIGN="TOP">Is a remote destination.</TD>
+</TR>
+<TR>
+ <TD VALIGN="TOP">0x00000004</TD>
+ <TD VALIGN="TOP">Can print in black.</TD>
+</TR>
+<TR>
+ <TD VALIGN="TOP">0x00000008</TD>
+ <TD VALIGN="TOP">Can print in color.</TD>
+</TR>
+<TR>
+ <TD VALIGN="TOP">0x00000010</TD>
+ <TD VALIGN="TOP">Can print on both sides of the page in hardware.</TD>
+</TR>
+<TR>
+ <TD VALIGN="TOP">0x00000020</TD>
+ <TD VALIGN="TOP">Can staple output.</TD>
+</TR>
+<TR>
+ <TD VALIGN="TOP">0x00000040</TD>
+ <TD VALIGN="TOP">Can do fast copies in hardware.</TD>
+</TR>
+<TR>
+ <TD VALIGN="TOP">0x00000080</TD>
+ <TD VALIGN="TOP">Can do fast copy collation in hardware.</TD>
+</TR>
+<TR>
+ <TD VALIGN="TOP">0x00000100</TD>
+ <TD VALIGN="TOP">Can punch output.</TD>
+</TR>
+<TR>
+ <TD VALIGN="TOP">0x00000200</TD>
+ <TD VALIGN="TOP">Can cover output.</TD>
+</TR>
+<TR>
+ <TD VALIGN="TOP">0x00000400</TD>
+ <TD VALIGN="TOP">Can bind output.</TD>
+</TR>
+<TR>
+ <TD VALIGN="TOP">0x00000800</TD>
+ <TD VALIGN="TOP">Can sort output.</TD>
+</TR>
+<TR>
+ <TD VALIGN="TOP">0x00001000</TD>
+ <TD VALIGN="TOP">Can handle media up to US-Legal/A4.</TD>
+</TR>
+<TR>
+ <TD VALIGN="TOP">0x00002000</TD>
+ <TD VALIGN="TOP">Can handle media from US-Legal/A4 to ISO-C/A2.</TD>
+</TR>
+<TR>
+ <TD VALIGN="TOP">0x00004000</TD>
+ <TD VALIGN="TOP">Can handle media larger than ISO-C/A2.</TD>
+</TR>
+<TR>
+ <TD VALIGN="TOP">0x00008000</TD>
+ <TD VALIGN="TOP">Can handle user-defined media sizes.</TD>
+</TR>
+<TR>
+ <TD VALIGN="TOP">0x00010000</TD>
+ <TD VALIGN="TOP">Is an implicit (server-generated) class.</TD>
+</TR>
+</TABLE></CENTER>
+
+<H3>printer-type-mask (type2 enum)</H3>
+
+<P><I>(CUPS 1.1 and higher)</I>
+
+<P>The printer-type-mask attribute is used to choose printers or classes with
+the CUPS-Get-Printers and CUPS-Get-Classes operations. The bits are defined
+identically to the printer-type attribute and default to all 1's.
+
+<H3>requesting-user-name-allowed (1setof name(127))</H3>
+
+<P><I>(CUPS 1.1 and higher)</I>
+
+<P>The requesting-user-name-allowed attribute lists all of the users that are
+allowed to access a printer or class. Either this attribute or the
+requesting-user-name-denied attribute will be defined, but not both.
+
+<P>The special name "ALLUSERS" is reserved to indicate that all users
+are allowed.
+
+<H3>requesting-user-name-denied (1setof name(127))</H3>
+
+<P><I>(CUPS 1.1 and higher)</I>
+
+<P>The requesting-user-name-denied attribute lists all of the users that are
+not allowed to access a printer or class. Either this attribute or the
+requesting-user-name-allowed attribute will be defined, but not both.
+
+<P>The special name "ALLUSERS" is reserved to indicate that all users
+are denied.
+
+<H2>Printer Class Attributes</H2>
+
+<H3>member-names (1setof name(127))</H3>
+
+<P>The member-names attribute specifies each of the printer-name attributes of
+the member printers and classes. Each name corresponds to the same element of
+the member-uris attribute.
+
+<H3>member-uris (1setof uri)</H3>
+
+<P>The member-uris attribute specifies each of the printer-uri attributes of
+the member printers and classes. Each URI corresponds to the same element of
+the member-names attribute.
+
+<EMBED SRC="glossary.shtml">
+
+</BODY>
+</HTML>
diff --git a/doc/overview.html b/doc/overview.html
new file mode 100644
index 000000000..8a711ba04
--- /dev/null
+++ b/doc/overview.html
@@ -0,0 +1,292 @@
+<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>October 4, 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. Printer classes are collections of printers. Jobs
+sent to a class are forwarded to the first available printer in the
+class, round-robin fashion.
+
+<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>cupsFilter</I> entries in the printer PPD
+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.
+Each filter has a relative cost associated with it, and the filtering
+algorithm chooses the set of filters that will convert the file to the
+needed format with the lowest total "cost".
+
+<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,
+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 GNU General Public License
+which means that it is basically free except for binary-only
+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..1412942da
--- /dev/null
+++ b/doc/overview.pdf
Binary files differ
diff --git a/doc/references.shtml b/doc/references.shtml
new file mode 100644
index 000000000..2d2375b69
--- /dev/null
+++ b/doc/references.shtml
@@ -0,0 +1,40 @@
+<H1>References</H1>
+
+<H2>CUPS Documentation</H2>
+
+<P>The following CUPS documentation is referenced by this document:
+
+<UL>
+ <LI>CUPS-CMP-1.1: CUPS Configuration Management Plan
+ <LI>CUPS-IDD-1.1: CUPS System Interface Design Description
+ <LI>CUPS-IPP-1.1: CUPS Implmentation of IPP
+ <LI>CUPS-SAM-1.1.x: CUPS Software Administrators Manual
+ <LI>CUPS-SDD-1.1: CUPS Software Design Description
+ <LI>CUPS-SPM-1.1: CUPS Software Programming Manual
+ <LI>CUPS-SSR-1.1: CUPS Software Security Report
+ <LI>CUPS-STP-1.1: CUPS Software Test Plan
+ <LI>CUPS-SUM-1.1.x: CUPS Software Users Manual
+ <LI>CUPS-SVD-1.1.x: CUPS Software Version Description
+</UL>
+
+<H2>Other Documents</H2>
+
+<P>The following non-CUPS documents are referenced by this document:
+
+<UL>
+ <LI>Adobe PostScript Printer Description File Format Specification,
+ Version 4.3.
+ <LI>Adobe PostScript Language Reference, Third Edition.
+ <LI>IPP: Job and Printer Set Operations
+ <LI>IPP/1.1: Encoding and Transport
+ <LI>IPP/1.1: Implementers Guide
+ <LI>IPP/1.1: Model and Semantics
+ <LI>RFC 1179, Line Printer Daemon Protocol
+ <LI>RFC 2567, Design Goals for an Internet Printing Protocol
+ <LI>RFC 2568, Rationale for the Structure of the Model and Protocol
+ for the Internet Printing Protocol
+ <LI>RFC 2569, Mapping between LPD and IPP Protocols
+ <LI>RFC 2616, Hypertext Transfer Protocol -- HTTP/1.1
+ <LI>RFC 2617, HTTP Authentication: Basic and Digest Access
+ Authentication
+</UL>
diff --git a/doc/sam.html b/doc/sam.html
new file mode 100644
index 000000000..077e0d6b4
--- /dev/null
+++ b/doc/sam.html
@@ -0,0 +1,937 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
+<HTML>
+<HEAD>
+<TITLE>CUPS Software Administrators Manual</TITLE>
+<META NAME="AUTHOR" CONTENT="Easy Software Products">
+<META NAME="COPYRIGHT" CONTENT="Copyright 1997-2000, All Rights Reserved">
+<META NAME="DOCNUMBER" CONTENT="CUPS-SAM-1.1">
+<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
+<STYLE>
+BODY { font-family: serif; font-size: 11.0pt }
+H1 { font-family: sans-serif; font-size: 20.0pt }
+H2 { font-family: sans-serif; font-size: 17.0pt }
+H3 { font-family: sans-serif; font-size: 14.0pt }
+H4 { font-family: sans-serif; font-size: 11.0pt }
+H5 { font-family: sans-serif; font-size: 9.0pt }
+H6 { font-family: sans-serif; font-size: 8.0pt }
+SUB { font-size: 8.0pt }
+SUP { font-size: 8.0pt }
+PRE { font-size: 9.0pt }
+A:link { text-decoration: underline }
+A:visited { text-decoration: underline }
+A:active { text-decoration: underline }
+</STYLE>
+</HEAD>
+<BODY>
+<CENTER><A HREF="#CONTENTS"><IMG SRC="images/cups-large.gif" BORDER="0"><BR>
+<H1>CUPS Software Administrators Manual</H1></A><BR>
+CUPS-SAM-1.1<BR>
+Easy Software Products<BR>
+Copyright 1997-2000, 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>
+<B><A HREF="#7">A - Using CUPS with SAMBA</A></B>
+<UL>
+<LI><A HREF="#7_1">What is SAMBA?</A></LI>
+<LI><A HREF="#7_2">How Do I Configure SAMBA for CUPS?</A></LI>
+<LI><A HREF="#7_3">How Do I Configure CUPS for SAMBA?</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.1.
+<H2><A NAME="1_1">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 <A HREF="http://www.easysw.com">
+Easy Software Products</A> 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 5.50) and an image file RIP that
+is 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>
+<LI>A - Using CUPS with SAMBA</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>
+<DT>smb://[username:password@]workgroup/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>
+<H1 ALIGN="RIGHT"><A NAME="7">A - Using CUPS with SAMBA</A></H1>
+<P>This appendix describes how to use CUPS with SAMBA. </P>
+<H2><A NAME="7_1">What is SAMBA?</A></H2>
+<P>In case you haven't heard of SAMBA, it is basically a software
+package that allows you to configure your UNIX system as a Windows file
+and printer server. It also allows you to access files and printers on
+a Windows system. Like CUPS, SAMBA is free software. </P>
+<P>SAMBA version 2.0.6 is the first release of SAMBA that supports
+CUPS. You can download SAMBA from: </P>
+<P ALIGN="CENTER"><A HREF="http://www.samba.org">http://www.samba.org</A>
+</P>
+<H2><A NAME="7_2">How Do I Configure SAMBA for CUPS?</A></H2>
+<P>To configure SAMBA for CUPS, edit the <CODE>smb.conf</CODE> file and
+replace the existing printing commands and options with the line: </P>
+<UL>
+<PRE>
+printing = cups
+</PRE>
+</UL>
+<P>That's all there is to it! Remote users will now be able to browse
+and print to printers on your system. </P>
+<H2><A NAME="7_3">How Do I Configure CUPS for SAMBA?</A></H2>
+<P>To configure CUPS for SAMBA, run the following command: </P>
+<UL>
+<PRE>
+% ln -s `which smbspool` /var/cups/backend/smb ENTER
+</PRE>
+</UL>
+<P>The <CODE>smbspool</CODE> program is provided with SAMBA starting
+with SAMBA 2.0.6. Once you have made the link you can use the <CODE>smb</CODE>
+ method in the device URI for your printers: </P>
+<UL>
+<PRE>
+% lpadmin -p <I>printer</I> -v smb://<I>hostname/printer</I> ... ENTER
+% lpadmin -p <I>printer</I> -v smb://<I>workgroup/hostname/printer</I> ... ENTER
+</PRE>
+</UL>
+<P>The second form only needs to be used if the Windows system is in a
+different workgroup. </P>
+</BODY>
+</HTML>
diff --git a/doc/sam.pdf b/doc/sam.pdf
new file mode 100644
index 000000000..ce209713e
--- /dev/null
+++ b/doc/sam.pdf
@@ -0,0 +1,841 @@
+%PDF-1.2
+%âãÏÓ
+1 0 obj<</Producer(htmldoc 1.8.5 Copyright 1997-2000 Easy Software Products, All Rights Reserved.)/CreationDate(D:20000418184114Z)/Title(CUPS Software Administrators Manual)/Author(Easy Software Products)>>endobj
+2 0 obj<</Type/Encoding/Differences[ 32/space/exclam/quotedbl/numbersign/dollar/percent/ampersand/quotesingle/parenleft/parenright/asterisk/plus/comma/minus/period/slash/zero/one/two/three/four/five/six/seven/eight/nine/colon/semicolon/less/equal/greater/question/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/grave/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 128/Euro 130/quotesinglbase/florin/quotedblbase/ellipsis/dagger/daggerdbl/circumflex/perthousand/Scaron/guilsinglleft/OE 145/quoteleft/quoteright/quotedblleft/quotedblright/bullet/endash/emdash/tilde/trademark/scaron/guilsinglright/oe 159/Ydieresis/space/exclamdown/cent/sterling/currency/yen/brokenbar/section/dieresis/copyright/ordfeminine/guillemotleft/logicalnot/hyphen/registered/macron/degree/plusminus/twosuperior/threesuperior/acute/mu/paragraph/periodcentered/cedilla/onesuperior/ordmasculine/guillemotright/onequarter/onehalf/threequarters/questiondown/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]>>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<</S/URI/URI(http://www.easysw.com)>>endobj
+12 0 obj<</Subtype/Link/Rect[157.2 307.6 262.3 320.6]/Border[0 0 0]/A 11 0 R>>endobj
+13 0 obj[12 0 R
+]endobj
+14 0 obj<</Subtype/Link/Rect[395.9 367.6 533.7 380.6]/Border[0 0 0]/Dest[144 0 R/XYZ null 225 0]>>endobj
+15 0 obj<</S/URI/URI(ftp://ftp.easysw.com/pub/libraries)>>endobj
+16 0 obj<</Subtype/Link/Rect[108.0 146.8 291.6 157.8]/Border[0 0 0]/A 15 0 R>>endobj
+17 0 obj<</S/URI/URI(ftp://ftp.gnu.org/pub/groff)>>endobj
+18 0 obj<</Subtype/Link/Rect[108.0 83.2 253.8 94.2]/Border[0 0 0]/A 17 0 R>>endobj
+19 0 obj[14 0 R
+16 0 R
+18 0 R
+]endobj
+20 0 obj<</S/URI/URI(http://www.easysw.com/htmldoc)>>endobj
+21 0 obj<</Subtype/Link/Rect[72.0 696.8 228.6 705.8]/Border[0 0 0]/A 20 0 R>>endobj
+22 0 obj[21 0 R
+]endobj
+23 0 obj<</S/URI/URI(http://www.samba.org)>>endobj
+24 0 obj<</Subtype/Link/Rect[266.3 234.4 364.7 247.4]/Border[0 0 0]/A 23 0 R>>endobj
+25 0 obj[24 0 R
+]endobj
+26 0 obj<</Subtype/Link/Rect[72.0 670.8 107.4 683.8]/Border[0 0 0]/Dest[135 0 R/XYZ null 818 0]>>endobj
+27 0 obj<</Subtype/Link/Rect[108.0 657.6 186.5 670.6]/Border[0 0 0]/Dest[135 0 R/XYZ null 435 0]>>endobj
+28 0 obj<</Subtype/Link/Rect[108.0 644.4 199.9 657.4]/Border[0 0 0]/Dest[135 0 R/XYZ null 209 0]>>endobj
+29 0 obj<</Subtype/Link/Rect[72.0 618.0 229.0 631.0]/Border[0 0 0]/Dest[141 0 R/XYZ null 818 0]>>endobj
+30 0 obj<</Subtype/Link/Rect[108.0 604.8 246.4 617.8]/Border[0 0 0]/Dest[141 0 R/XYZ null 415 0]>>endobj
+31 0 obj<</Subtype/Link/Rect[144.0 591.6 205.1 604.6]/Border[0 0 0]/Dest[141 0 R/XYZ null 343 0]>>endobj
+32 0 obj<</Subtype/Link/Rect[144.0 578.4 221.3 591.4]/Border[0 0 0]/Dest[144 0 R/XYZ null 728 0]>>endobj
+33 0 obj<</Subtype/Link/Rect[144.0 565.2 244.2 578.2]/Border[0 0 0]/Dest[144 0 R/XYZ null 378 0]>>endobj
+34 0 obj<</Subtype/Link/Rect[144.0 552.0 240.5 565.0]/Border[0 0 0]/Dest[144 0 R/XYZ null 294 0]>>endobj
+35 0 obj<</Subtype/Link/Rect[108.0 538.8 245.8 551.8]/Border[0 0 0]/Dest[144 0 R/XYZ null 225 0]>>endobj
+36 0 obj<</Subtype/Link/Rect[72.0 512.4 221.2 525.4]/Border[0 0 0]/Dest[153 0 R/XYZ null 818 0]>>endobj
+37 0 obj<</Subtype/Link/Rect[108.0 499.2 212.5 512.2]/Border[0 0 0]/Dest[153 0 R/XYZ null 428 0]>>endobj
+38 0 obj<</Subtype/Link/Rect[108.0 486.0 246.4 499.0]/Border[0 0 0]/Dest[153 0 R/XYZ null 334 0]>>endobj
+39 0 obj<</Subtype/Link/Rect[144.0 472.8 280.6 485.8]/Border[0 0 0]/Dest[156 0 R/XYZ null 382 0]>>endobj
+40 0 obj<</Subtype/Link/Rect[108.0 459.6 190.8 472.6]/Border[0 0 0]/Dest[156 0 R/XYZ null 235 0]>>endobj
+41 0 obj<</Subtype/Link/Rect[108.0 446.4 173.7 459.4]/Border[0 0 0]/Dest[159 0 R/XYZ null 800 0]>>endobj
+42 0 obj<</Subtype/Link/Rect[108.0 433.2 223.8 446.2]/Border[0 0 0]/Dest[159 0 R/XYZ null 618 0]>>endobj
+43 0 obj<</Subtype/Link/Rect[108.0 420.0 240.9 433.0]/Border[0 0 0]/Dest[159 0 R/XYZ null 501 0]>>endobj
+44 0 obj<</Subtype/Link/Rect[108.0 406.8 263.2 419.8]/Border[0 0 0]/Dest[159 0 R/XYZ null 346 0]>>endobj
+45 0 obj<</Subtype/Link/Rect[72.0 380.4 229.2 393.4]/Border[0 0 0]/Dest[165 0 R/XYZ null 818 0]>>endobj
+46 0 obj<</Subtype/Link/Rect[108.0 367.2 255.6 380.2]/Border[0 0 0]/Dest[165 0 R/XYZ null 428 0]>>endobj
+47 0 obj<</Subtype/Link/Rect[108.0 354.0 181.0 367.0]/Border[0 0 0]/Dest[165 0 R/XYZ null 271 0]>>endobj
+48 0 obj<</Subtype/Link/Rect[108.0 340.8 211.0 353.8]/Border[0 0 0]/Dest[168 0 R/XYZ null 702 0]>>endobj
+49 0 obj<</Subtype/Link/Rect[144.0 327.6 162.3 340.6]/Border[0 0 0]/Dest[168 0 R/XYZ null 568 0]>>endobj
+50 0 obj<</Subtype/Link/Rect[144.0 314.4 171.5 327.4]/Border[0 0 0]/Dest[168 0 R/XYZ null 481 0]>>endobj
+51 0 obj<</Subtype/Link/Rect[144.0 301.2 195.9 314.2]/Border[0 0 0]/Dest[168 0 R/XYZ null 318 0]>>endobj
+52 0 obj<</Subtype/Link/Rect[144.0 288.0 213.7 301.0]/Border[0 0 0]/Dest[168 0 R/XYZ null 192 0]>>endobj
+53 0 obj<</Subtype/Link/Rect[108.0 274.8 177.4 287.8]/Border[0 0 0]/Dest[171 0 R/XYZ null 726 0]>>endobj
+54 0 obj<</Subtype/Link/Rect[144.0 261.6 183.1 274.6]/Border[0 0 0]/Dest[171 0 R/XYZ null 645 0]>>endobj
+55 0 obj<</Subtype/Link/Rect[144.0 248.4 169.7 261.4]/Border[0 0 0]/Dest[171 0 R/XYZ null 340 0]>>endobj
+56 0 obj<</Subtype/Link/Rect[144.0 235.2 171.5 248.2]/Border[0 0 0]/Dest[171 0 R/XYZ null 201 0]>>endobj
+57 0 obj<</Subtype/Link/Rect[144.0 222.0 167.8 235.0]/Border[0 0 0]/Dest[174 0 R/XYZ null 404 0]>>endobj
+58 0 obj<</Subtype/Link/Rect[144.0 208.8 188.6 221.8]/Border[0 0 0]/Dest[177 0 R/XYZ null 604 0]>>endobj
+59 0 obj<</Subtype/Link/Rect[144.0 195.6 189.8 208.6]/Border[0 0 0]/Dest[177 0 R/XYZ null 478 0]>>endobj
+60 0 obj<</Subtype/Link/Rect[144.0 182.4 220.4 195.4]/Border[0 0 0]/Dest[177 0 R/XYZ null 299 0]>>endobj
+61 0 obj<</Subtype/Link/Rect[144.0 169.2 204.5 182.2]/Border[0 0 0]/Dest[177 0 R/XYZ null 213 0]>>endobj
+62 0 obj<</Subtype/Link/Rect[108.0 156.0 163.9 169.0]/Border[0 0 0]/Dest[180 0 R/XYZ null 800 0]>>endobj
+63 0 obj<</Subtype/Link/Rect[144.0 142.8 195.0 155.8]/Border[0 0 0]/Dest[180 0 R/XYZ null 691 0]>>endobj
+64 0 obj<</Subtype/Link/Rect[144.0 129.6 197.5 142.6]/Border[0 0 0]/Dest[180 0 R/XYZ null 240 0]>>endobj
+65 0 obj<</Subtype/Link/Rect[72.0 103.2 180.0 116.2]/Border[0 0 0]/Dest[189 0 R/XYZ null 818 0]>>endobj
+66 0 obj<</Subtype/Link/Rect[108.0 90.0 232.4 103.0]/Border[0 0 0]/Dest[189 0 R/XYZ null 428 0]>>endobj
+67 0 obj<</Subtype/Link/Rect[108.0 76.8 195.4 89.8]/Border[0 0 0]/Dest[189 0 R/XYZ null 348 0]>>endobj
+68 0 obj<</Subtype/Link/Rect[108.0 63.6 188.7 76.6]/Border[0 0 0]/Dest[192 0 R/XYZ null 557 0]>>endobj
+69 0 obj[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
+]endobj
+70 0 obj<</Subtype/Link/Rect[72.0 670.8 152.0 683.8]/Border[0 0 0]/Dest[192 0 R/XYZ null 264 0]>>endobj
+71 0 obj<</Subtype/Link/Rect[36.0 644.4 180.1 657.4]/Border[0 0 0]/Dest[201 0 R/XYZ null 818 0]>>endobj
+72 0 obj<</Subtype/Link/Rect[72.0 631.2 152.7 644.2]/Border[0 0 0]/Dest[201 0 R/XYZ null 428 0]>>endobj
+73 0 obj<</Subtype/Link/Rect[72.0 618.0 255.9 631.0]/Border[0 0 0]/Dest[201 0 R/XYZ null 282 0]>>endobj
+74 0 obj<</Subtype/Link/Rect[72.0 604.8 255.9 617.8]/Border[0 0 0]/Dest[204 0 R/XYZ null 800 0]>>endobj
+75 0 obj[70 0 R
+71 0 R
+72 0 R
+73 0 R
+74 0 R
+]endobj
+76 0 obj<</Dests 77 0 R>>endobj
+77 0 obj<</Kids[78 0 R]>>endobj
+78 0 obj<</Limits[(1)(sam.shtml)]/Names[(1)79 0 R(1_1)80 0 R(1_2)81 0 R(2)82 0 R(2_1)83 0 R(2_1_1)84 0 R(2_1_2)85 0 R(2_1_3)86 0 R(2_1_4)87 0 R(3)88 0 R(3_1)89 0 R(3_2)90 0 R(3_2_1)91 0 R(3_3)92 0 R(3_4)93 0 R(3_5)94 0 R(3_6)95 0 R(3_7)96 0 R(4)97 0 R(4_1)98 0 R(4_2)99 0 R(4_3)100 0 R(4_3_1)101 0 R(4_3_2)102 0 R(4_3_3)103 0 R(4_3_4)104 0 R(4_4)105 0 R(4_4_1)106 0 R(4_4_2)107 0 R(4_4_3)108 0 R(4_4_4)109 0 R(4_4_5)110 0 R(4_4_6)111 0 R(4_4_7)112 0 R(4_4_8)113 0 R(4_5)114 0 R(4_5_1)115 0 R(4_5_2)116 0 R(5)117 0 R(5_1)118 0 R(5_2)119 0 R(5_3)120 0 R(5_4)121 0 R(6)122 0 R(6_1)123 0 R(6_2)124 0 R(6_3)125 0 R(binary)126 0 R(sam.shtml)127 0 R]>>endobj
+79 0 obj<</D[135 0 R/XYZ null 818 null]>>endobj
+80 0 obj<</D[135 0 R/XYZ null 435 null]>>endobj
+81 0 obj<</D[135 0 R/XYZ null 209 null]>>endobj
+82 0 obj<</D[141 0 R/XYZ null 818 null]>>endobj
+83 0 obj<</D[141 0 R/XYZ null 415 null]>>endobj
+84 0 obj<</D[141 0 R/XYZ null 343 null]>>endobj
+85 0 obj<</D[144 0 R/XYZ null 728 null]>>endobj
+86 0 obj<</D[144 0 R/XYZ null 378 null]>>endobj
+87 0 obj<</D[144 0 R/XYZ null 294 null]>>endobj
+88 0 obj<</D[153 0 R/XYZ null 818 null]>>endobj
+89 0 obj<</D[153 0 R/XYZ null 428 null]>>endobj
+90 0 obj<</D[153 0 R/XYZ null 334 null]>>endobj
+91 0 obj<</D[156 0 R/XYZ null 382 null]>>endobj
+92 0 obj<</D[156 0 R/XYZ null 235 null]>>endobj
+93 0 obj<</D[159 0 R/XYZ null 800 null]>>endobj
+94 0 obj<</D[159 0 R/XYZ null 618 null]>>endobj
+95 0 obj<</D[159 0 R/XYZ null 501 null]>>endobj
+96 0 obj<</D[159 0 R/XYZ null 346 null]>>endobj
+97 0 obj<</D[165 0 R/XYZ null 818 null]>>endobj
+98 0 obj<</D[165 0 R/XYZ null 428 null]>>endobj
+99 0 obj<</D[165 0 R/XYZ null 271 null]>>endobj
+100 0 obj<</D[168 0 R/XYZ null 702 null]>>endobj
+101 0 obj<</D[168 0 R/XYZ null 568 null]>>endobj
+102 0 obj<</D[168 0 R/XYZ null 481 null]>>endobj
+103 0 obj<</D[168 0 R/XYZ null 318 null]>>endobj
+104 0 obj<</D[168 0 R/XYZ null 192 null]>>endobj
+105 0 obj<</D[171 0 R/XYZ null 726 null]>>endobj
+106 0 obj<</D[171 0 R/XYZ null 645 null]>>endobj
+107 0 obj<</D[171 0 R/XYZ null 340 null]>>endobj
+108 0 obj<</D[171 0 R/XYZ null 201 null]>>endobj
+109 0 obj<</D[174 0 R/XYZ null 404 null]>>endobj
+110 0 obj<</D[177 0 R/XYZ null 604 null]>>endobj
+111 0 obj<</D[177 0 R/XYZ null 478 null]>>endobj
+112 0 obj<</D[177 0 R/XYZ null 299 null]>>endobj
+113 0 obj<</D[177 0 R/XYZ null 213 null]>>endobj
+114 0 obj<</D[180 0 R/XYZ null 800 null]>>endobj
+115 0 obj<</D[180 0 R/XYZ null 691 null]>>endobj
+116 0 obj<</D[180 0 R/XYZ null 240 null]>>endobj
+117 0 obj<</D[189 0 R/XYZ null 818 null]>>endobj
+118 0 obj<</D[189 0 R/XYZ null 428 null]>>endobj
+119 0 obj<</D[189 0 R/XYZ null 348 null]>>endobj
+120 0 obj<</D[192 0 R/XYZ null 557 null]>>endobj
+121 0 obj<</D[192 0 R/XYZ null 264 null]>>endobj
+122 0 obj<</D[201 0 R/XYZ null 818 null]>>endobj
+123 0 obj<</D[201 0 R/XYZ null 428 null]>>endobj
+124 0 obj<</D[201 0 R/XYZ null 282 null]>>endobj
+125 0 obj<</D[204 0 R/XYZ null 800 null]>>endobj
+126 0 obj<</D[144 0 R/XYZ null 225 null]>>endobj
+127 0 obj<</D[135 0 R/XYZ null 698 null]>>endobj
+128 0 obj<</Type/Pages/MediaBox[0 0 595 792]/Count 28/Kids[129 0 R
+132 0 R
+207 0 R
+210 0 R
+135 0 R
+138 0 R
+141 0 R
+144 0 R
+147 0 R
+150 0 R
+153 0 R
+156 0 R
+159 0 R
+162 0 R
+165 0 R
+168 0 R
+171 0 R
+174 0 R
+177 0 R
+180 0 R
+183 0 R
+186 0 R
+189 0 R
+192 0 R
+195 0 R
+198 0 R
+201 0 R
+204 0 R
+]>>endobj
+129 0 obj<</Type/Page/Parent 128 0 R/Contents 130 0 R/Resources<</ProcSet[/PDF/Text/ImageB/ImageC/ImageI]/Font<</F4 5 0 R/F8 8 0 R/F9 9 0 R>>>>>>endobj
+130 0 obj<</Length 131 0 R/Filter/FlateDecode>>stream
+xÚìÏsë8rÇIŠºÌ‰öŒî´üt§çyšÝ­ÚJñÙfvçÖ$ÁÊ!•S*Ç6•üÿ±~X"H€èn
+ ‚B¹ü29×$X,0X
+Ú Ñ ê£s5Ѭåà‹+›— 9Ä`™ãÅ`ž‹,öD.1?V{àÊàŠRç\uD98æY\‰c®}HÙ8媣`Jé++D91WTθš°ŒÒË TÔ  LÔ‹Š4ðZåªBÃ2t,OãQ ÔxŒsÆq‰±FãzX¢Æ#F,Ï%£FL×]w÷Ûí—×cy>|ðÛa0IwÅ÷_†ƒüxÂ[‡ÿîŠG’þÅ˃ŸßÝuoœ*=[ í,¸ö~ºªÛk[ça=xì.Bú±àæØ\ìPƒ–.Î$[q¹¸¡==·aMJ&³»XéÔœ.K™\¬@ž› É貘ÇUùÑv‡]¶cqeSÙàÕ›eN¤ܯjؤ½s’  ®|²¡ÅϼJé\Í Xd°˜ÎEùØÍÉÜZ9ƹfÂ"‚­¨\Õ FÈ1Å’È•Ï…EÛи'’ –Y¹0p7ñú¥uZj+Cg‘¼c,Òé« …«rÎX•½ÅÍÁ‘jÄ¥{.B J<—°ÒlKÃ7D=Ì:¸ÎåÛÁÉ %i=•œkˆàBf½ .Ú`Ø`¹Š¹2O”,Á W­Ç’ó ìÍПR4q‡ãÚa…„–¤8®
+¢6(®–KH·Kh%ºÖRÕÓbQ®•–ër›®Îa‡Àà ¯Çé¹ £^ >ÜÌÑ–œáµCrí´\Mçù
+.A5C#WªçÚŸòáªÕr[¬àª©fhäŠõ\盽§5Rì•K,×êZ[Éu6Ž;êÊ12BªWNZ,×:{—'%WG¤(³ÔgO@• ž«x j®Š5K5†ˆëWC6Ãq®ZÂp ÖbI…Ó5 ÊFÒâ¹Ä»Y¨¹®&EZ,ip-â\9%pžê¹*ÖšNè(;
+\n£á¬5;c+\ŒôÅq®æ"
+®K‰kvª‰@“¤¥pµ¥×qgéB™ÐdcMãº(½Ž«àôWòD@“ë¢ô:®Œ3¾êÙwCÅuQz WÍÛÃÈ0’ ¤h#!r]”^ÃUðûMÏ%s Æð2r•^Õñ6g
+ÌÃ’lì¨\g¥Ws5Ì]Œ
+㘴¶ÑR¹ÎJ¯æ½ØER2WÁ^f®“Ò«¹rfæŽÀ8f DQ)ëdÝ…~^¹e¢IàkSYŽ­\§Öç*®³Ý FÈ‘#š ”(ªT?º²k ±ÌukDŸ+?krN7Ä="à
+vt6מÍ•4™¿peM/\œ¥ùp‰'óg®úÃÉ<–k2ßÎ-‡Í‹ó·•Ü:\[æ»uîªßFwÒ²ßmµwpœOm›.Ò,²
+£®[‘"K~­lâŽÃÕ¿(—óM
+-áðì8—Ðpz®”ÃÕ¿¨7RNf™
+E2þW=/WÁá’.ªn»Ê·EÞÙ¹j—t‘趲¾Æ6ÓJ\‚Ã%_”ßö¿ž#Wé“«{œÍ%_T ö+Càª8\òE½H0?qÙL—Q\Ù8WÃá’/’gÆé>®Îq><Wï"©ÃÊbB®ƒ¾‰‡K¾HN#‚—qy#¶äÊ£îíZðS¹zýèN«i¸®oÐ4Qp¸ú=w–"p…S^Nù‡?ßÞ?oäZJ¬èžó2Ûâr{Ï\ù'×'׸V”+]&WöÉõÉõÉõÉõÉ…ãZr}rùçŠì¹Þf>ès¥”ºór=ŸÞa?bšK©;3×Cÿøšü²¡ÎæuÕ»êªHqPJÛ:k®|¾!çéw·§†u\y1Ô×áŒF~™WsÛìø9QeäÚÉÔ—Pì(É/4¬®KüuòÈȵ™‚ TSë¼;Àz齺 ®u+ M?\™j« ºã “Òdúu\©l)^¸š÷§»nÝî«ko»õʺ ®Dºµ®êºQ(¾unÚ‘À[v…².Ÿ«öÉUtv¬÷7ïÒqY·äsM]Õ2¿ú®òöNå“+ª›v^Ȭ©K㊯ۧoƒ8ñÆ•u—áòžÒߌ§©KãJ®£0qÙÄóÒŸá¦×VËIÚªº$®Õõq¼=™t®êöÓ5ä$mU]WÚ}tk;.CúáõÏÍÝ[ÙtĪ윑Ò×%qÏ]ÓÆ+W¢ûѧ“T<¶ÔçÊ/ƒô8„wÞ¸2]:ÎeO¹éÀd#©;x®ýåóŽŸ|ðÆuŽš¸1‘†‘¶.‰«¸ÈÐÑÒKo\—½Îûƒ:À:eR¼Ë¹¶.‰ nÙ¯qëë¢Z{þ⇬ã‹õu•\ÃÄÎEu¯G÷å«“Òkmñîm®¾X_—ÂU_ `ü`;®±}½n=é½ÄÕ0‹V[—ÂÕ\~<ºeK®±}ØByŽCš—ƺ.]_ˆ¾ç²Ú_ÚÌÒ|Õ¢¯Kàj/Žùô[\½,Ú¸ôdŠ¨Ká:;æ“[öÉÕyÑ–L¡ú’9]]
+×Ù1×'ï“ëý ì —J•l:ZÇuvÌõ©¾—1ïë9Sš\ùPÔuñ~ùäËãÿc—‹|¶—‡á¤¦ÒLrTu \ÕÉOnÙ?×­µ«^(u@Õ%póÉ-OÁõ.
+‰vÐŒÕ%póÉ-[raß-Z‹ÁX]—x÷b멸n‰×f®^]×Ù1ŸEr”Ë&ûø*™ìŠµ\cu)\o&˜4·õS/\²{ãb§®.…ëM2âúì-¹JÜú¡k¬.…ëØÓUt}Q7®D²­x”KW—Âçð±‰ËæÜM.ý9Õù±º®ê|²&±çÚO¿6ÝyÈJË5V—ÂUßœº7®ªÛŸûÞmz«Kájn³Q.›sˆçkãï·M­–k¬®!ž—Þ¶%ä]A ®qö¶ ©Ÿ,ŽÕ¥puÞ ë«™«õ¹Fê’¸r—Õ¹ìf¤jŸk¤.ƒ«´çZãÒmú¡ÿ VÑ×%qƒßxàjô
+3àj¸ëQ2\ŸÊ(—Õû7䵘_ÚQ.}]—îý¬n¹:}l \Úº$®úêÓǹ¬²Þè!Ón”ØÔµ*–ïí¹Ê×ëMóU|–ïY
+¶X¾ë“kz.óë–—ÉUüÓr•‹ä2/pÉe3aþäšžËjb¹h®õ"¹Ä?-WºH.ólõA¹’ere0ðµü>ŽO®¸ö0ðÅ}Ïn‘\ðA¹ª Z~¯Ù\Åô²×å÷ÐÍULMÞá¾70¸@ªrÃ\ e’ºÒò{9ç*Š+[\Àa
+%°ß{ZÀ‘›ì ù=Å¡­´e&=@~¯thG„â‚¥Âèo‘ßoXÀQã¸÷}ô•±
+-V裥ÈaÜ媗#ˆ(Â-ôDˆ *âƒ-ôéBäpÝåB} Â(»‚-ôÇ¥€VÏP„#C̾:\ÕB")£œ· Å }ºÙHd.„Ð'‹TæB,ÝÄ‹u+_†pd8
+h)‚¸xí\Åú,(ÖCéÑg­¡% ÇÜ ýŽhIÂ1ó
+œéöÔ1GNÀY™8樼A:©Ôï˜gB©œÇ ÜÏ™.H,8Ž¸Ãtºc wàÊêd†kÍÁ…Tú‰:,c=càÔ4Æ €?R§é°ŒÖÛNÓaÓ™_‚& :C¢Åqí±`»0ºk…äªé0‘qƒph­ ±÷UKó„ªY.´v†èuâ,øV­!zÕzìÓÝ ¹ð†èQ:j ›ÖÖãrfWŠØ ¬OKüñÍPË%¢¹-±±’.h­ Ñ“&æV>¬­'BõÊXZ?Þ¹±´°öõ^†˜È,e \<1÷3–½í#×½{&ܸ¥rU¤s©ÍJÉ\"š Ldö
+\EiW°/)áøf08Ô&[õ cœFwžÿf<[l2êR6W›qÀúßtŽ*OôÛø\‹+Š¿S;‹añcq€‰‹ÙaQt_zî¬ñ¹‘‘«ˆ¸ÿ¥ó/9k·6\"â—{”56¼OßYqYtØqœ=šÌñ…Ie˜>˜¹l:ìŒöªÿ짌ý¹K.ê4Li¯ƒ~/ÛÌæqµ¶\Mä¦ÜÝo·Û/¯oåe»}È,?mcÍå¢ÃœSþ#†KȵqÀe'‰³tŽ+¼Û9á
+®ÃÌK_8.±´îBrvm¦(ˆ=$W›…ÄupÇU/«»Ð\¬•O_ºäj‚áÚ´.¹‚ÑzÜöžKdKÑxw gÑ q!Øý^
+—XŒÒ¸°DôÖ!‰kvKÄgиš…X!•kæø—°MäšÕ)¹/T®9½3eû‰Ê5c`OJš#sÍ6Ähi/t®™†1±ŒÁ5Ï#îí2¸fbÔ,%=pÍ`sM®ôT9×ÄÚÁÈ
+·¶øhß"7\o¶è®Ë’CW+Âé,—\ŽºìþІÆÕŠ'küâª-.¹ø©ÕªGw-qËe‘^HÝž“‹Kæ–ʇìþ‹ë6øà"æÅÇ[ï¹ðÃõV^qÙññýw/·÷Æuê5ƒAÞm_}ÝÛ'ש۞¶wê~z|õy_ß\çŽ;žpØ>ÜËývûøúêý–“pÍPàÿ
+ä
+h!¢sô"ò¡endstream
+endobj
+131 0 obj
+6285
+endobj
+132 0 obj<</Type/Page/Parent 128 0 R/Contents 133 0 R/Resources<</ProcSet[/PDF/Text]>>>>endobj
+133 0 obj<</Length 134 0 R/Filter/FlateDecode>>stream
+xÚ+ä2T0
+ár á
+ä
+endobj
+134 0 obj
+31
+endobj
+135 0 obj<</Type/Page/Parent 128 0 R/Contents 136 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 5 0 R/F8 8 0 R/F9 9 0 R/Fc 10 0 R>>>>/Annots 13 0 R>>endobj
+136 0 obj<</Length 137 0 R/Filter/FlateDecode>>stream
+xÚUÉnÛ0½û+99@¤JòÞ[³Â@5vŠ|¡%ÊfB‘*IÙp¿¾3¢/IÑ€!R3ïͼ¡~ubˆðÃ(Þ²²s=ï|¸Ÿ@Á¼€~ÜG0õažwSà –ñËù3"úÇ„<$è%£0!Ô|-,X]¸-3X^
+%¬3Ìic¡dªf*£7"ç„rÜÄV T¡MéŸñ ÜšÃ.K\>}þ$qÏgL)ˆP+˜í¬ã%,º7Oéìbq ?¸±$ãÐóž@<"Þx>‰ÂFW{ìÛ†›àÛS}„zÜü”œç}UÅ ÒƱ¥ä^ a$Û¡NCgÉ`ºâ¤_Ú&€ aê`Í,1¸›w¢0ÂÞÐ_L0 âd”„C(!Ž&È˯$̨q.{¸D²KÎä|Ã%¦Éa¹ƒ¿F=„aq8>
+ûþøIöÎhœ1«=ñ–Û³;˜í-’×™kTNFíÉ.8M•-µC7uLåÌä‡ÂZ-ëW£0)}o6\åd8DSÜ !ðê˜Ú¢/B —ºF&k;ùƒÎÁ57/\òdØtܤPÃÒØ°õáÞM8 íCM §¸;Ø…:i‰ÞÍïƒiš¢E™‡/™Å¹! ˜Š­ßH„g½ldÀ¯š×˜Іgð™x¥í(Ý2N]t?§·WðxÇ£Éâò
+fho|ÿ…[ËV®¥Î^6ûrMo)窚á&r®Zª˜™†XZ}–ÒÖyµn †c qQÔ*£f0)ÜîÝú°<·€eÙjóò:þK£·–$‹T[7ËŒ¨ÜA·Í†h„]¤é-w€%ãGFÐÍ{¬æ_¸"I¼z0¯ÌU•ój•s?–ï3FõØõLÖ~¶³Ú:]Šß˜{Ó^/º€‡¯Oð°>P_t³Ú®œÜ'ª‹·¨A8ˆ—g|© /Á’ÚT¼E§)š„9@‹Ô gb¯Ji•¬-©}{Ýõ#œZ «³ºDbÿsáýû*G„6+¦šz`jݸ¹ÐRêm3ž¼1„ýè³dm–dLó¸O´H†#8¥Ñ!xsÞ2ï?A>X¯ïõ}¦úã(œ "ÜÛëï{ç=!Ãendstream
+endobj
+137 0 obj
+844
+endobj
+138 0 obj<</Type/Page/Parent 128 0 R/Contents 139 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 5 0 R/F8 8 0 R/Fc 10 0 R>>>>>>endobj
+139 0 obj<</Length 140 0 R/Filter/FlateDecode>>stream
+xÚ‘;oƒ0€w~ÅÉ
+?[—& bLÅ9"4Þ&ßá2Uá”#d_¤«Æ¸µjR¥ì ÏNÙ‡è‚endstream
+endobj
+140 0 obj
+244
+endobj
+141 0 obj<</Type/Page/Parent 128 0 R/Contents 142 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F0 3 0 R/F4 5 0 R/F8 8 0 R/F9 9 0 R>>>>/Annots 19 0 R>>endobj
+142 0 obj<</Length 143 0 R/Filter/FlateDecode>>stream
+xÚ•UÉrÛ8½ë+ú6I•Dq‘µä;–GS‰£XtÍR¹@$HaB
+Ù
+9ºÌGÓõ
+Òò
+’Å"º€ùbyù"… \v¢)…ª©6Ê:Ö4t½ºßî^æÿ¢ë ’„\'Áw’¥‹(%÷ü ,Ö:nÀôÑà4ì)¨(BDpWZJ­àþvólPŽòìNÖqÁ¦‚“î€þàãA„ë|G1VEGBÇÝ di­ ËÒh+]"¦pk`G5ÇxÍðŠ@÷B1s‚RXgľsQTFË€
+ …#߃Ž¡5ºà¼¤*è_Ë oî„kx9†ã @<‹‹h9Àóû…„Y¼Djf?´@ûy-žÕûÔÅÔgŠzÍep(y3 „J{ ÷A^DA+”ŽWAˆ:™ÅÿyÔîLÁ¿‰ê½gäÃä"Ž¼øîøçN.¹rö©ÐÐ(£¿u÷ªGQG˜‚×·»Í¤Ð²mS®€žEƒò;‹Î·†bBQöâzmA8 ŠITys;–ÂçÆŒID]r+j`:E¡H^´ºå†yÕ†ÀcÒ;ÿBé„„ªæ¦bHB#ö†(¦)Ú:Е7«Dƒ&ÏÓ’ÕžŸ¸*-Ø®%Œ%…;„¸8^Tc¬N•˜ö(Œ¶ºrÓ‹9ü)T‰CHÏQþǵ…dµ‡pÆáñmß®0¢up·ÙŽ‰¼3íäöÇöúf Û[<òÍz=öNÿ¼Ý\öU"ŸÑyc ª:
+ŒúB¥èÎQ8‹c†âÕ^v¦©:å'5Â"¸fö„rªÜ‘6ÂÖh´ÂJ†ãaƒÆ`YÖg‰‹Î`o,µ½•å€¹W?™æ/I‚—&+œ pûá°M×1¬H½™¦a8*×¾šNñŒ8ÖcêuÚvûéÅ÷‡n°t1ܹ‹ýŽ”짽«j¬¢ßS§£eôðw7·÷P]U€D1¥ÑÒ€¢&À -*¢á7Á¾0Ñ°=)WæP6£%(!YWþòK„ÍV!×#_µê"mjO–ö¿ˆÊ‘‘R-æ÷7¾©*m$sié,Í0Ñö{þîí›÷W`{¥ßCª<ášþ(èš»ÍM×ËÁÖêÛþ+ïÕÙ2Æ5“yÖsúaô]—BKendstream
+endobj
+143 0 obj
+908
+endobj
+144 0 obj<</Type/Page/Parent 128 0 R/Contents 145 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>>>>/Annots 22 0 R>>endobj
+145 0 obj<</Length 146 0 R/Filter/FlateDecode>>stream
+xÚVÛnÛH }÷Wv7¢›£¸q€}ÈÍ^/\7uT(ú2–Fñt¥‘:3²£¿_R[V8H ÄÖ<ç<Òï.þxp1¢ß0ÜgâƒçAãÑ•o_Aý|žß¹ÅfˇXeéõÇà×à!¸¶‹ièâÑe9Åd6_Ž18o<´Gͧž¨F/B=ƒ3qaLEÇhäÖU×Æä׎³ÝnmÎt©·v˜¥ÎÚ¤I”…T|ŸÇÝåƒçS" 3YX3Ýei.!ŸáîÛã…viZÑíWÇð&šk˜.¾+Lf2“ýÏ27k)ûÇ"ÁsLF(J¡BŽG"Ô%SPâg@¸†§6Ýض8–ô.ì!•ì€)s~]Ü+b +x€íì“=,‚‡å!•ŠsK&@ Y‘R–$̈LÂV$ ä…©¨T”wm²ú²­_g=s¸  ¿:Shõê ©Ó?·aýs ÅC“)‚">’£§R-Î9l×"\ײ W[% Ç*%ð¡ õ7WBVÿਤX—±Û‘FŽ×ôÊ¢LfxJA’…µz8Ç4²¬\áL¼ô¹e9E½¯£mŽ¿IL‡*&'»<‹+4‹é9üûø€×`6™ÔÒÿ˜Ïn!+Å*i©ÁȦeÏ#j7k¯•ÑŸg8‹1$å!׎@P!äˆxŸù±æW¼ÄªwôçGêÐ_ØÉ®›ææ˜Èw“ùÍôé…æ÷GOr¹*“)—p[á÷zŽ„+‰T!e=HMÞn"ÂÅÔPÃÖÌÑYÊv®ËmÆØv N ™Ÿ éNŠmÛ''ã‹DSB]aÍ6|Ïõ]#-}¿
+mNø ÝÉB¨ÕªITu¬Ý$»hÝØÝ{ñ¬?’–ž:«wÔ”{ûºóªw oã: Ž>–M«ß…çP×ý2€£û!“€7UÇh#ÆS\­UIè±âÛø«UÑ+!°ÈuöH|Ú‘ð› éèÊàVH¦J¸GWTbUÐB¿Iè;¯¬!,”ÂÅHJôã6ªy`ªNuÒiràfI¼”4e³>|ÎÐ^*ä»AÓ¡¹ÑÍT\6 ºSôýf¹˜-¦×oÂìð«0õž•Ç¿~ ´®>I=á/9G3¤F"ŽEˆ–GÞX¡Ç6ÕÕ¯šêž7DmGãîëA;p¥B’ ÷UÃg& –P¼åùWf}ºåÓw¾ëÛ—àÁw¨¯ƒÿI~Ñ!endstream
+endobj
+146 0 obj
+947
+endobj
+147 0 obj<</Type/Page/Parent 128 0 R/Contents 148 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F0 3 0 R/F4 5 0 R/F8 8 0 R>>>>>>endobj
+148 0 obj<</Length 149 0 R/Filter/FlateDecode>>stream
+xÚmRKSÛ0¾ûWìp‚¬ØŽ †ÐämÁܸ(–ì¨ØZ£™üûj•8ChÇKûí÷ØÕG’C¾® ˜/ ’û:™­JÈs¨[ºZT%« ç¯¿^Àbë¶ÜHàZ€–R€CèÖ¼y§ßzØ ÒNéìÎ:9\R¶ªï]Fø)Ám$õEý'É Íç¬ µ£ÐV¹M?•½¶1jtÑ‘J[Çëq¢ ­Ááàh”†-=ü°ŒÓ0-¬$Íá+Ýiàü¯%ôØuá 4p ÑÁÛ¹@’áά?eÃtP€Ôè»ÍÛƒŸº‰dßò’‰Fo¥¹ãu¼:x
+þQOái*·D1[epsXUZÄlÖøѲ)Êò©^>ï¡ÓZORßµNšà|SJ­ÜÂNÚ™FøðÒ’°ýÏHâ8ÖGƒa´ÂÙf#…ïï„ ã¤øžÚ;B°&´ïØÞdu0™—UxxùUÎÇç÷2I߉Aie]Ø* \{ÞÇ…îÛÒë"‹]8Œª§dÓ+«ŒÝ@,^ÑyY'¿“¿ÅR÷Wendstream
+endobj
+149 0 obj
+423
+endobj
+150 0 obj<</Type/Page/Parent 128 0 R/Contents 151 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F8 8 0 R>>>>>>endobj
+151 0 obj<</Length 152 0 R/Filter/FlateDecode>>stream
+xÚ+ä2T0
+áÒw³P04TIS04±Ð³P072PIÑp VÎO+)O,JUpLÉÍÌË,.)J,É/*VðMÌ+MÌÑ ÉâÒ…hÒ…ê2‰™˜è™*@LÉÏ-ÈÌÉÌKW
+ä
+endobj
+152 0 obj
+123
+endobj
+153 0 obj<</Type/Page/Parent 128 0 R/Contents 154 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
+154 0 obj<</Length 155 0 R/Filter/FlateDecode>>stream
+xÚUQoÚ0~çWœ*Mc”½m“6­S[èÞMì€ÛÄvmĿ߬%´jÅB¾»ï»ï¾;žz)Œñ“Â<ƒÉ òª÷}ÝýX@6†uéb³ùÖ¼?n¬T^X¸­E-àš)¶•PþËú“¦¦””PV2ÉæÃŒ×;é ß1C™\º¼vN8Øéx ŒóTšËâ8
+/À<­à¨k î輨†néœàÆLÇÃË% 4ŒWRÁ•®*¬wJ c³ÙpÚÆÆÇqóØoROSúÇJÀÊR1!âFØBÛ
+¹;Ùb{!_:o™—HÚ3÷è °ºpM¡¤”J„&N:‰*­¤Ê?Jõd¥Cí`¿‘€TÛF¢j
+1] …JÛØö«ê/_Øœ7¢í«‘î<ôí™ãœú¦]ѯƨîíéç!ývr8Ñ«û›U˜®“•)`ëWÕzÇ%éw!í"1  ák[bA±¸gIX|‚VúQkÛÄÄØ,Æö~'ú,!ÙŸr±—¹èÆÝtªÞ‰XþY/ï:û6B­ Ëq7„ˆzÄž
+áÕƒ² p
+MÇ‘„ŽYŽÜÐÑÚÕÆhÛœ!ædtKð°Æ{‰…ÄoÖåò¹óiSâ½?¡éåx¸À ÓïåºwÛû_
+¢endstream
+endobj
+155 0 obj
+728
+endobj
+156 0 obj<</Type/Page/Parent 128 0 R/Contents 157 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/Fc 10 0 R>>>>>>endobj
+157 0 obj<</Length 158 0 R/Filter/FlateDecode>>stream
+xÚµWmoÛ6þî_q0 Åfɲ=;60lÉš`šÁ«]ìC’´HÅl$Rå‹ï×ïHI±¤8^Þ†ECï¹{î¹;ö['‚þ‰`0r?qÖ9[t‹D,w4:'° Ç OÙ4¤lº ’±‹¯t£AÐw¾…ß³9TISÖäÖ€‘`V tÎbžpFÁù
+U ݺ‡•1ù4 ¯¬fÊaMs¢õF*úËÍJj㎮¦¹Tæ&TLK«âv¨<ƒ×æóûl¹âÂ0R‚®™
+`FÍ,fér¢0¼«²„ØÔh1D‡8JsŠVÙ„ß,³oÊáÓìãCÞÙ!prš²tún’¨<‚cyXóø`È1'-ü«Ÿen¸úæ-‘žqì«d Õ.fL,±™xωLS¹áâ¶%QÍ\Η[o˜§VÃõñ÷× ^á§K1-üÇ¥ÿþ[µÛCçáz0ì•aõª
+iüø\ž’%Âð¶Ò‚̸)qÎår´¶%Í Íø£Îe–AK!L –M7#Ï_ª¹A#¢è.eÅq·&|oÅÏŒÀé¥ë*é;Ò%êÌp;fQ^hêCHHŒÒò¼kHeì·.÷&pý‘fµ
+5îÆ6×a&)KÛQ®Xl¤Ú"ëfå¹<êfGeÙ¦•óI%ÿ¾Oø;ðÎS¾ ËD¡›WciRh¬à®á
+î½àÔ« ¥"–ëŒ.›¢¬ hò‰ {ïE´‘6¥NG/ªXì«Ò|ÉáoV¢ÅWfœu¾±uÆ|{Åÿ>³L®]ÿ”ms _$(gÌj4a’¿~™Í}6š#5[PV¼¼¹ë:¾ŽïŸ£ã'eQÔGF?Ž
+6|2órõÀ©ó͵ÁÇ•Ä×=NFK¼»ÑðÄíßq¿çnù'Ò`0&Ð{Þ@:_tþêü å‡$íendstream
+endobj
+158 0 obj
+1096
+endobj
+159 0 obj<</Type/Page/Parent 128 0 R/Contents 160 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
+160 0 obj<</Length 161 0 R/Filter/FlateDecode>>stream
+xÚ­VKoÛ8¾ûW ,ê‘ü¨a'{KÛô°À.ÒÆ‹½ôB‰TÌ€"U>âøßï I9²c“m"ižß÷Í?F3˜âß Vsø°„º}\&_.a¶‚uƒ_–«U¹„5ßX©½°ðI1ç„{¿¾G»ÌfÉ®˜/ËÙ}úûæ˜Rfë`gxwÖ„œl¥bºÈÔÀ ÅYæ8ãüjÊr˜c\Â?A>Á¡…š;|ˆpo*ÊÄ’ã9ø wk›}(çTÛV*•@S ì‚Cµ‹¦´Î{`Xa¥D_#•è7̧¨eŽÖwºÆ|œ÷% õS±sl¸S;°AÇ©›ißêo¥>êjÓ¶LsØJ¿‰ngEwô欨ÏÀt^í~ïƒ]’WÌcI¿Á$8;Q²šäøPtÉvžl{„Œ¡¨­øïm®ÿZ_;¬·ˆ©{<öMîé|‰Fj+˜GøYð¦e^Ö(˜È¤n„Óï<ˆGé| ˆ²­y{lïr…Èð<ö¨¼”Ç7µÛClw1-/(ì­ð^ê»XÅgÑ° <Ü Á}a8°'|tá٥ɢÿe²áÿ~ˆ {dåçÅð,ØSùÌÞ…Và£6¨zÍZ¦‰ÿTr20â™”·Þt=dNNo¬õs”…¦5ð¬J {dÈ¥{É2ÓdRIÑÑa=û^~®)+\'j/„ÚýM•Ô“T¼f ÷üÐúËø "åºßêÕ=ÀiU2‹ÚNÑ9´l‡ÓÖeu-º¸§4&ŸHÏ9TÁÃÖÐijڇ¸ÒNgz‡‹Y M‰aGá–:bH Ž»bƒúÁ.‡&(Ú'CµL5A×qFJhIkez‰rÃí]k‘Žìe°…¶„+,#ñ«?ÊNí¦¸Íšx
+ Rb-‰3ßǹÌïïO+ø*¢ÔKø›¸'qô†?rúת8aþ
+ۘ褈3wä˜,ŸÝDf?²¼oüMʦ”Sü¤²)R®ðWûÊ6‘Ø
+ÏŸóÁI_3M÷šéŽnÁgˆ,-¶€P@nL9C¾vO.Ú‰j
+endobj
+161 0 obj
+973
+endobj
+162 0 obj<</Type/Page/Parent 128 0 R/Contents 163 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F8 8 0 R>>>>>>endobj
+163 0 obj<</Length 164 0 R/Filter/FlateDecode>>stream
+xÚ-‹A‚0÷ÿo) °íV‰ìL0Ô4R’(±­ñúZ5ï­f2bˆÏ»}þ}¡“¦m«À =‚¥ªµ€6Í­ëѯcz™`qç]LÁ¤5D\Œš¹Ð•¿¨üW,2”\W_Ðç“ hf£Ùž5]é ÙÌ%ªendstream
+endobj
+164 0 obj
+126
+endobj
+165 0 obj<</Type/Page/Parent 128 0 R/Contents 166 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
+166 0 obj<</Length 167 0 R/Filter/FlateDecode>>stream
+xÚTÛŽÚ0}ç+F+­JUåJÄ¥oí
+Ô>tKKöLâwÛµ(ß±“
+²FêÀâ_ŽCÎÍŒPG¾òŒ:¥%ÙS¨INØräMZ{2+Å n¾+çgã#Eµ!Ê ­íO<·0ƒÀOOkÐlÇ êSÐèÞÝHY1ì¥3ŤŒ¾ȲŠ|]ôܽïËö7æÄ‹›÷àäDtù˜.¿âàÝ ¤HjË8QG´IŶsžqÄW•nY;²7¼gœ?¿é9˜)Ž“ú
+\É!íºª¹îNeç¶àí¤=¾Ê;™-ûEÑnQn9¥µÊšñçÜ>
+U£;Ç6²1Ò6Ê æÔ¥Í/úöZ^M=ïÃçÂÆíM{vìÓhwöì©­©ªò>£4gæïKâN¹ÿÚ¾XÖ~×¼W¼qi˜gŠÓÇ™ÓÃ…Ú3?Â1áԜꈔJà-à
+ƒ¤ªfZcÈôí ×Ïج·¯ÇA!D°½4®ŽÏ)·vtwyÞÕ"òN~™ÿè¬æ—“Ìÿí7™%H ´ÅQd,ÓÑ·Ñoß¾^endstream
+endobj
+167 0 obj
+621
+endobj
+168 0 obj<</Type/Page/Parent 128 0 R/Contents 169 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F0 3 0 R/F2 4 0 R/F4 5 0 R/F5 6 0 R/F8 8 0 R/F9 9 0 R>>>>>>endobj
+169 0 obj<</Length 170 0 R/Filter/FlateDecode>>stream
+xÚ•VßoÛ6~÷_q{ªIJe;Ž›·¦k±[—¡ÚÀ¼Ð"s•H¤¬ú¿ßIÙ²jœ
+« ýåÕä1›Ì?¯!M!+ðd³]'[Èø4Û ùž©n/ ”J@®•cRIõâŸÝdÿ¢ó":O3QÕ¿JžvWNK#r'¤ºæ–7µå ^] =ööámŽø‹98êæÝAífé*YRº¹Ì þÐÝýžnÀ
+g˵¯&¤C/Ãñt^h=ß13wU}{æ]7‰wþ,+Ëã-a3Î'e…9Ì‚n5‡wåÒX‡çX³V  |(-µ²vÂ$!Î{HïCÃgëEhøWáZm¾ÃGl„|i #÷ˬçœ2ê‹(XS:Båì
+øø÷Ó·.ÉRZ'”…B2V!5ümtX¨[*L±`¹ÀªÔkݬÒA«Ÿ§¾~Çg†Ã—§'où|“À^!;¬‹©Š‘˜!U!"TK³ò"•Vº½Ï›ÕµÑµ‘ˆæ nƒÕ(V¡‰âP3k1ežD£®/_
+ώѾ Š¢˱&K¿ñÔ€=bC*oé«Y .Ý5‚ÎHbéœ:=çk!Kqx![­’ ¥ö„MzÏAØŸíû³dk‘ËB"R,
+Íï„å-,½>§Ê†³Éºæç"Ím³Ã¯·XŽ³mL(0˜C#(©NÀz0 h¤´ëÏþ;;ôùe”cF·V¼u|Ǽú\£}jt‰ò·—ù> îŠJ+‰ûU‰æ؈J;¨‰4Ì4ÈÇ“ôKoƒwå èátßÂ^·‚Ƭ£IÜðƒõB(x¶»˜oŸ
+_ÿÌ>=Œ6á¼³jü£ã‰‹Þ’ºXÙÒүȹ€™=íÿƒ¬
+®§î%rÀoïªã˜VìŒf<gX~G¨}-½IÙ¨E’>×XhjŽ‹n(yÚkë™Ö ô×ýÎçvŠ‚ø’_ÞÝ%½ÿaúk­ÄöRV§”¤B¶Uas;ýÓ@k?‹66|/L×[|ÙlãkŒùoºp-íüÞ 6þ`ªa%ùÏ‚Ûì~¹ ¯tIW÷[Œ¶xõmèS6ùkò?ÄüQåendstream
+endobj
+170 0 obj
+1014
+endobj
+171 0 obj<</Type/Page/Parent 128 0 R/Contents 172 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
+172 0 obj<</Length 173 0 R/Filter/FlateDecode>>stream
+xÚ­V]oÛ: }ϯàcw;±“%é0 èv7¬À>rWØC_Y^´Ù–'ÉMóï/)ÉÙì&‚ É“)‘‡Ôá!Ž˜â/e
+³ðjô:MÞ=‡$¬ O‹Õ<^A–_|úœ½}ñ,ûŽæy0O!Jñœ¬×ìU L h¬¿Áûutû’iœNÕ900í¦ì–Yje!!YŒAiXÁFZ3†FËÚ
+ ­vÎÑÝ]—5Œ[ŠQ“YœRTV–tåœ ZpÑX©ê»g°“h¢;¥Äm1b£Õ¦°¦L°
+6
+ù œÿ€8‰§±Oó’%¥a¢ùÔ—aðÝÞji÷'+òæv}C!ïe. \¯]rU1YG5«l˜ÁÀŒsa pU[­Jwè53’kíVÔVrF)A5êú…rBÏfñ‚BPþÄIlÙV€7NƒñÄ È%–ÕÊ{¹(d© ;``Áe°ßgÙ:\RzO•Ç«²T;zÌƈ6WƒWìNKrŽ
+EËa³G¶p•4Bß È¼t…YÝ¥‹å0­ Ë+ù(©Èÿäÿö˵geÕ"‹ZB‹š#äH®¤tΤ±þI¸ª*|/÷Aý–ÙQD¼dÆóG˜Ât¸‚Ë3|W›¿‚‚üœ:
+x…"”íáeêˆñ ‘nöÆŠª+Mâ…é³Î1¯E½_‘$ n“ÞÑ“ uè™Î{c’.Q£§q28òrÒAÕ—F'à8: ç(ã‘ ÇdÑþöý—Šà=+ÛÀBÓ6ÒVäg ›/¥«Ê˜êö¸“|Å´ø‰Á¬ñT§iܢàpÑÝ4=‡nHÝíV%êž”¬e‰Õ?ðì”C­"õnžN²Ï—'“Tu¹ïšz
+½yýHdü³Oµº3~Rµ8mý'öûTŒ“ØZ…¤’$ÅÓ‹Ë ;nƒ¸Q…ÝQÓ]…)Žb¨p üÈê–9 Q2_%—¸³žXõæKZ‡9™Ñ‡·Ùè¿Ñÿ\Vendstream
+endobj
+173 0 obj
+961
+endobj
+174 0 obj<</Type/Page/Parent 128 0 R/Contents 175 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
+175 0 obj<</Length 176 0 R/Filter/FlateDecode>>stream
+xÚÕ–Ír›0Çï~Š=¶™XF_Î-MšNfÚNš¸ ‚<¡1à"’Lß¾+ bí©ƒñŒ¥¿V?­Ö»ûkFÁÇõ'ÉgV³ÅKX­õHG„Á*}w¹Ù”¯°®ÊHZæ"+HRæïW?gÌ©GâŽè±Tµ“°(
+r6>í(™ Ã×Y¸Èó)Úœì½z¡v'¥ÚŸst蜅Ä3bçýfþ¤Y%“:{‘
+D%!yÎóçÐç JÀu¶ÝH8´{ÒV"
+ø!áYÉê„áI"•‚uY½ÙmÏÉíë;T€ó…¬_ËêIX=m¾h¹Ûº(k„. P[™dëL®n¯ïµ¥\¨§ »"hVPÆÑ1‘uŒ5GcŸèàÄfý¸šyÄÃ}ûºÿ~L"xLB aˆWÄ<äG÷=ôˆ HШcjûí)ª­2ßBy†ÉX7LhN?'˜hŒ«™Œº‡É’x¾½Ú!¡,vañ—1®we±ê!?6>a]’%s"‰ØĨIBnÿ’„M32ÂD¨uæ0êAŸc<á®q‡‚“Ox4!z­z…q³4ìi€NàÐhBàZõ Ž×dæà¤q ¾Œ&D¯Uñ˜ëÝ9ÁÁ÷PMb«îò€ó&íõdÖñ´ŽY[Õ ¶[)*È
+•¥³lÇÄç21™xÐ
+±3K ¾žÁìÏ›P¾–ÅïÃeÞ®zõÐë÷a÷+®+…ȱ¦ÝÞHÓ
+ëÐù^úQÔ)[¨°f5¥
+KWû*Y½Èê¢Ý¾mUæÌo±mmF÷u
+÷nîkYÈÁɳáÆe'rÑŒ7A;]_çr8ë¦pW7@ƒºãþgLêÚþŒFË´îç„)×æÇÜ–ê´@ÿGó± éÚˆZŸØzàï:6!]ñp¶þ‡¦‡³)IšõçèÅMÜÞÑáêûÝ<”ëúUÇêešgE¦êJÔe¥à‹(ž…É>s»hÞ¬¢¦ú!%Üž¤ ^<Ä·Ùq—gendstream
+endobj
+176 0 obj
+764
+endobj
+177 0 obj<</Type/Page/Parent 128 0 R/Contents 178 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
+178 0 obj<</Length 179 0 R/Filter/FlateDecode>>stream
+xÚ½W]oÚ0}çW\í‰J%MòѽÑn­*mhTÚ/nb S°iìñïwÚ¤ „µ«ˆØ÷Þs||®1=l|9p}ˆV½«iïâfŽÓ9xŽ€ºVÓ¸OΦzŽïã“­ŸKbÙú¥Ç¿N{ø ‹í?~ÝÂ0ÔùA€&ØŒÁ¤!˜8žå•Ñ„J4²2\|×ò5¶eY56åÈ^€¹yÑ­<†®5Ô¨añ¼Nš¸æwæRD·r!.éýðÈ”„ŽÐq,Ñ™NÝJÇv ìa ™‚Ða:Þe`Ùé˜è6:^HŒ{]r€¾P
+üLl¢(Ùà¹Ù­é’Ƽ¸±Ënëa|k†v ؇8ÉX¤’'«\* ë5£$\&1Z¯ðMDT%‚·V±ÌÌ%8C=cÃÀ-;j”«åt»fÕT ¾±zá朗¤c6O8“ 0Ya$ˆ9PÌb\%†*(k–ÍE¶úlêDe¢R{øñƒ:þXðWØ‹:„\Š<áíX ³>r£yª¬ÙY×ì”-ÕzEe½F-†»
+endobj
+179 0 obj
+787
+endobj
+180 0 obj<</Type/Page/Parent 128 0 R/Contents 181 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
+181 0 obj<</Length 182 0 R/Filter/FlateDecode>>stream
+xÚWÛRã8}ç+zò²a&6q¶¶¶
+¨¡ŠÛÙ‡­Ê‹b˱[òJr2ùûí–r1Æ É@*`[ÝçôéV«ýßA}üàxDŸ¸8¸ÝœCt
+㟌NOÃŒ“îÈ9Ü(]0kÇÿâ¢!D‘_ Fá]ýýô ¥Vs‘p îo﯃)3<”ìí²rLºkË5]<Θ¦
+":opï:.m‹‰È›ÅžUÂS!1„YGÅk1!í Y27çL䎺’ Êdm–Æò"ô(¨ùp%çñ±×ü=Ö5Å÷ŠÕ7Žù‹T ¹åoB¸&sŠL¥ÛàŒeÚX›¹›”ÒmÄ[”7BKUž«æ|ºÄ¸Ñ†BéºXšÇj&…J‚®rBÇÒþƒeÎ{¨•å?ìQf‹¼-'Þ«X§&f~_ÛŸÓ2¬í`àÛøõL&ë
+endobj
+182 0 obj
+1278
+endobj
+183 0 obj<</Type/Page/Parent 128 0 R/Contents 184 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F0 3 0 R/F4 5 0 R/F6 7 0 R/F8 8 0 R>>>>>>endobj
+184 0 obj<</Length 185 0 R/Filter/FlateDecode>>stream
+xÚUKSÛ0¾çWìä˜Ø‰CHh{¢f80Óé©ô Øk#*KB’ø÷]Ivš˜”arÉHûø»òó ƒ)ý2XÎàly=øºLnæe°*éfq1O/`UŒV'«'º\´—#«“c<ì2FPrp înï®Á½jƒÒŽ+É„x…ÆrYÑí–‹"g¦€RpTÞ6MâãNl³ŽÿF˜V©o3…$;KgÎÐá‹›hÁ¸ŽaÈkVáäÔÿ=heÍ ×nøpÒ%Îéü?<
+´ŽKæ~„ Xr‰t,ì˜3írj^cêãìÛb?
+endobj
+185 0 obj
+743
+endobj
+186 0 obj<</Type/Page/Parent 128 0 R/Contents 187 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F8 8 0 R>>>>>>endobj
+187 0 obj<</Length 188 0 R/Filter/FlateDecode>>stream
+xÚ+ä2T0
+áÒw³P04TIS04±Ð³P072PIÑp VÎO+)O,JUpLÉÍÌË,.)J,É/*VðMÌ+MÌÑ ÉâÒ…hÒ…ê2´
+endobj
+188 0 obj
+124
+endobj
+189 0 obj<</Type/Page/Parent 128 0 R/Contents 190 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
+190 0 obj<</Length 191 0 R/Filter/FlateDecode>>stream
+xÚU]oÚ0}çW\ñÔiÊ'4¼mUQ+uS3MÓ:MÆ1Äkb§¶SÄ¿ß½h;"…äžësî9æa@ŒG“FcàÍàS1ˆf9¤1KH'£0ñ$ƒ¢<û
+Ë\ ®pñm~ µ^ÁRÖ†=PÉ„€b²8<'ˆï•0œ†™T¥¯¼Á¢=oŽ5é8Ìú¶b ë•6 «ë X§(A*Õ#Ä[„³è‘™ˆw­°ú
+y”¼…œÆq¼X¯Æ‘1ÆOó'‰Ž-!jÔYÖõ.UZáiTp=V–¨›…NÕôµÁ°TìQlÓR¾+ì¨X#n´¾ÇhœÎ D04Åé¿œ¨Š_%í-qš5«×lc)šŽIea ÿÜuOö{!ëðŒû)G–ÞŒ$è¥jÄC'¬÷3=
+áz‰Êï^#Û·ÌÚµ6Ñvm‹Q+2¶Ä,îÁѵêw®~-G¤¢ÙùÞîñÆŸ@6ÉЫ±wkN7.‹Á×Á_YaÞëendstream
+endobj
+191 0 obj
+722
+endobj
+192 0 obj<</Type/Page/Parent 128 0 R/Contents 193 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
+193 0 obj<</Length 194 0 R/Filter/FlateDecode>>stream
+xÚ¥V]sÚF}÷¯¸Ã‹í©%$…bà-­íØ™ÐÐ1žN:Eºà—]²»2!¿>wµŸÁ)fFXºŸç½_Nbˆè/†7m÷Mg'¿ Oš7-ˆcNèI»Ó
+;0ÌΆçÃÏô°]><˘ÅÀòúû•ÓL8Š ¸KNÎ
+˜ÌÀY‚š75~ÉÑXàÞiD¹àé#ô‘I:«Ñ ŸBÂ?Fç…;…#krvÙ"â7aâ
+›(=c¶çkˆ ëJ V‚¤åž~ººjö?þÑü›>½ÛÛ^¿ß»¿‡_"úü»^vP8µÃÖžnghUv°ÕÛáp
+endobj
+194 0 obj
+1087
+endobj
+195 0 obj<</Type/Page/Parent 128 0 R/Contents 196 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F0 3 0 R/F4 5 0 R/F6 7 0 R/F8 8 0 R>>>>>>endobj
+196 0 obj<</Length 197 0 R/Filter/FlateDecode>>stream
+xÚT]oÛ0 |Ï¯àžšµ»“×uX€ ¡Þ[A¶èD…-¥½`ÿ~¤?šÖɺÂ/†tGÞi?ÍXð“À*…Û Êfv—Ï>n–$W|“­—ñr=Ï÷×ù#_fÃå¼ èû£?‡Ê`­¡t–”±ˆYV5®êÞ…syýºÝ#ÛãS‹ŒÝEŒ„9m ˆ¼)Z‡k®¨B[4†µ %·q*‚ioË©*çáà•Âñ€J³xù[®ˆŒ~‡1‚m›‚- öj‡P ÷颎áÛ3*€òãÐ{´ø›ę́ÿüs{œÜ²@Êsµ´³Wh58 ÑAGß»µ†ƒüð«ZFdÎS¾àV° ¸¥àÅ°¨?¹ž‚éÇÑ7<£.žnRÖ„‰Y£‘9¥ª%º(S½!ÓؤçeRe‰!üªÝîœUã;ö@lE}®gKÈ1LÐ Œ„.j c–§À†‰I©Ó•î`0¼^¡>=E“ФH ›q·»-â¨KeÁ:âC§ÛÇŠÝž á&G{s)ã·õÃÑÔ5¨ú¨þˆ:Hâ¶`ÉrÍ¿‰$[½ØÞ{WÑQ6þ“nŒ5¼"ÇÁwe[U ?êiÑ*]Œ3@ï—¡Á†§$ åJþ@ MäàK>û1û ]>lüendstream
+endobj
+197 0 obj
+509
+endobj
+198 0 obj<</Type/Page/Parent 128 0 R/Contents 199 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F8 8 0 R>>>>>>endobj
+199 0 obj<</Length 200 0 R/Filter/FlateDecode>>stream
+xÚ-‹±
+Â0E÷÷wÔ¡5‰E³ªØMPg 6­‘´Á׈¿¯Q¹w:œó ñ™Är•hkhQkH ÓAVºÔX+ÓÎvçcƒ&véeÙaÓ~ôSb›"O8ØñiÃÜÜ©øEÅ¿R*ÃJˆRá ÌÍÁ1G¾„Ø£öÁeaoèDoLO&‚endstream
+endobj
+200 0 obj
+128
+endobj
+201 0 obj<</Type/Page/Parent 128 0 R/Contents 202 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F0 3 0 R/F4 5 0 R/F8 8 0 R/F9 9 0 R>>>>/Annots 25 0 R>>endobj
+202 0 obj<</Length 203 0 R/Filter/FlateDecode>>stream
+xÚ…TmOÛ0þÞ_qû“¨IÒª%HÓì­›6bûè$—Æ#‰3Û%ðïwg§¥Tš¦JV›ÜórççúgCDŸ– ÌP´“Ëlrú)…$‚¬¢óL,`±œCV_Àî¬êÖpu÷ýåj¸½øzyñ6ûM˜9Ä1c¦4%K‘0.«•Ù÷Ø•ê J´…Q9Z¨õ
+¸Va '¡Sî²2ˆ»öÄH°V¨";V}""º=¸ɱ 6ÈÓÜÎ.ÌÅnú^g½–€_d½—®Ñ²K+£Ûsü˜M"QØøˆù¸ù q:3:SrÒB’&b9þjà–£x
+endobj
+203 0 obj
+622
+endobj
+204 0 obj<</Type/Page/Parent 128 0 R/Contents 205 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
+205 0 obj<</Length 206 0 R/Filter/FlateDecode>>stream
+xÚ­SÉnÛ0½û+Þ¥€
+TÔbÃK.…“ºhé+è5´HY¬%R%) þû’ÝÖE´@!rÞ2O3ß' b÷$˜Îý›×“ël½_!Y +ÜÍ|± sd,ø z¼S¸Å’…Ø·šãæáó…ÒØ®ï®×o_gßt†$¡a:'3Íò?€Þ@·¶äT/äÞÕÖ5•ìj䋱òtÎ\˜l¯PI„})ò¦Þ™F©êQGu”·‰v4?pÉ"w‡ÍÇlsi-ÈÎæœöYi¼Δ—¨
+endobj
+206 0 obj
+475
+endobj
+207 0 obj<</Type/Page/Parent 128 0 R/Contents 208 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 5 0 R/F5 6 0 R/F8 8 0 R/F9 9 0 R>>>>/Annots 69 0 R>>endobj
+208 0 obj<</Length 209 0 R/Filter/FlateDecode>>stream
+xÚÕ›_sÛ6Àßý)øx÷P…
+v1ØðNSi«Òõ6žx±¾ÆÜf Ëdâź¢¡e"¹}·ýíS<\2‚Ûº<÷€dKùpÑã¤*¾PE…¾?§–ßF¨¢FÇÿÛ!nÖì a!`™<ìf6cÖNÛ˜=ì¬M
+yô@íÎíN“#†Læõ7@z€€ï§þÁ\þƸ:±´¤²)–=1Â[ZR» kâ»ßv t@#9ñžÍãåœà¥¬%„ lf+ZQƒYËll_pYw\'Ï`$'Ü»]{8tnæ”K;Bnâ&+šÚƤ²1ÃŒ o²¢i(Aêæ9~næ»›ÉZ®DÈ"qóŠÜuÆÜûÙk‘œPï»Ûö¸›ã¹·3¡!Ž<÷
+ÝÛÛi|<tn0U³˜«)õ4];P°×ÂSO+¯Üõv;u,óÖ_~ŬŸ’–ämB 理Ê_yÖĺƒK‡­J
+ÃRMââŒ{3ñò€…Rb]o6ãÑrÚ![ÖWAD¢° ±Jb”øùÅ{Ôvjþþµ›ºh&#Î&ê‡B‹™ÇÈ©Í©'Š
+8(¦´%Z‘j¼©~ ¨G5'µèÐNæ—Û•¤¦ŒÂ‚
+Þb
++QŠJ)¯¸å*Pp3—SPê*P›Mw8ü{5®G`äPÄÛÓ°…¤^)[ËÚ† kû™141Ò¶&fm ‚BÂtÓ4NqK{
+Ý{²Ý¯Wÿ¦yˆendstream
+endobj
+209 0 obj
+2336
+endobj
+210 0 obj<</Type/Page/Parent 128 0 R/Contents 211 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 5 0 R/F5 6 0 R/F8 8 0 R/F9 9 0 R>>>>/Annots 75 0 R>>endobj
+211 0 obj<</Length 212 0 R/Filter/FlateDecode>>stream
+xÚÍ–MsÓ0†ïþ:Â!ŠdË:1i!À¡3@ÜáÈš3± ‰;ùû¬¬•½vÇJO-IÆeµ´»ïJùH&à-Y”˜Ï¯*¸ÉƒåZ³P°|Çd¬Y’*–?¼É‹ŸÇ-kv충ÛmÝžß濃y ¸
+ÅlpÂD#4W&6ÁS`hËuŒÉ‡Y`šO!
+ˆãû¡hçE(KC¨ü‰fD•çyñµ}<!¡I]¾ß˜Š¸üo:ùZ곞z{¶ŒÎ$<ë”ô©¹x²=8B¶)hî8ïO®Ohº.±ƒæóÙÓoƒ#T,zÚmç6õ®Ü?ž<÷%
+²vz¾ì11úˆˆz¤÷¢þFÄæÏ̘f'8š_{Ľ×Þf'89%»/ @e6.ú…^$‹$ƒzH¸ÆÃ>y›f×^
+¨Éê¡*ëòÜžŠ¶9Ù]Q?GC\H•AH‹4ìb(KLÑ×à1EA endstream
+endobj
+212 0 obj
+644
+endobj
+213 0 obj<</Count 7/First 214 0 R/Last 259 0 R>>endobj
+214 0 obj<</Parent 213 0 R/Title(Table of Contents)/Dest[207 0 R/XYZ null 756 null]/Next 215 0 R>>endobj
+215 0 obj<</Parent 213 0 R/Count -2/First 216 0 R/Last 217 0 R/Title(Preface)/Dest[135 0 R/XYZ null 746 null]/Prev 214 0 R/Next 218 0 R>>endobj
+216 0 obj<</Parent 215 0 R/Title(System Overview)/Dest[135 0 R/XYZ null 374 null]/Next 217 0 R>>endobj
+217 0 obj<</Parent 215 0 R/Title(Document Overview)/Dest[135 0 R/XYZ null 169 null]/Prev 216 0 R>>endobj
+218 0 obj<</Parent 213 0 R/Count -2/First 219 0 R/Last 224 0 R/Title(2 - Building and Installing CUPS)/Dest[141 0 R/XYZ null 746 null]/Prev 215 0 R/Next 225 0 R>>endobj
+219 0 obj<</Parent 218 0 R/Count -4/First 220 0 R/Last 223 0 R/Title(Installing a Source Distribution)/Dest[141 0 R/XYZ null 374 null]/Next 224 0 R>>endobj
+220 0 obj<</Parent 219 0 R/Title(Requirements)/Dest[141 0 R/XYZ null 309 null]/Next 221 0 R>>endobj
+221 0 obj<</Parent 219 0 R/Title(Compiling CUPS)/Dest[144 0 R/XYZ null 694 null]/Prev 220 0 R/Next 222 0 R>>endobj
+222 0 obj<</Parent 219 0 R/Title(Installing the Software)/Dest[144 0 R/XYZ null 345 null]/Prev 221 0 R/Next 223 0 R>>endobj
+223 0 obj<</Parent 219 0 R/Title(Running the Software)/Dest[144 0 R/XYZ null 261 null]/Prev 222 0 R>>endobj
+224 0 obj<</Parent 218 0 R/Title(Installing a Binary Distribution)/Dest[144 0 R/XYZ null 184 null]/Prev 219 0 R>>endobj
+225 0 obj<</Parent 213 0 R/Count -7/First 226 0 R/Last 233 0 R/Title(3 - Printer Queue Management)/Dest[153 0 R/XYZ null 746 null]/Prev 218 0 R/Next 234 0 R>>endobj
+226 0 obj<</Parent 225 0 R/Title(The lpadmin Command)/Dest[153 0 R/XYZ null 387 null]/Next 227 0 R>>endobj
+227 0 obj<</Parent 225 0 R/Count -1/First 228 0 R/Last 228 0 R/Title(Adding and Modifying Printers)/Dest[153 0 R/XYZ null 293 null]/Prev 226 0 R/Next 229 0 R>>endobj
+228 0 obj<</Parent 227 0 R/Title(Using Standard Printer Drivers)/Dest[156 0 R/XYZ null 348 null]>>endobj
+229 0 obj<</Parent 225 0 R/Title(Removing Printers)/Dest[156 0 R/XYZ null 195 null]/Prev 227 0 R/Next 230 0 R>>endobj
+230 0 obj<</Parent 225 0 R/Title(Printer Classes)/Dest[159 0 R/XYZ null 738 null]/Prev 229 0 R/Next 231 0 R>>endobj
+231 0 obj<</Parent 225 0 R/Title(Setting the Default Printer)/Dest[159 0 R/XYZ null 577 null]/Prev 230 0 R/Next 232 0 R>>endobj
+232 0 obj<</Parent 225 0 R/Title(Starting and Stopping Printers)/Dest[159 0 R/XYZ null 460 null]/Prev 231 0 R/Next 233 0 R>>endobj
+233 0 obj<</Parent 225 0 R/Title(Accepting and Rejecting Print Jobs)/Dest[159 0 R/XYZ null 305 null]/Prev 232 0 R>>endobj
+234 0 obj<</Parent 213 0 R/Count -5/First 235 0 R/Last 251 0 R/Title(4 - Printing System Management)/Dest[165 0 R/XYZ null 746 null]/Prev 225 0 R/Next 254 0 R>>endobj
+235 0 obj<</Parent 234 0 R/Title(Changing the Configuration Files)/Dest[165 0 R/XYZ null 387 null]/Next 236 0 R>>endobj
+236 0 obj<</Parent 234 0 R/Title(Temporary Files)/Dest[165 0 R/XYZ null 230 null]/Prev 235 0 R/Next 237 0 R>>endobj
+237 0 obj<</Parent 234 0 R/Count -4/First 238 0 R/Last 241 0 R/Title(Network Configuration)/Dest[168 0 R/XYZ null 661 null]/Prev 236 0 R/Next 242 0 R>>endobj
+238 0 obj<</Parent 237 0 R/Title(Port)/Dest[168 0 R/XYZ null 534 null]/Next 239 0 R>>endobj
+239 0 obj<</Parent 237 0 R/Title(Listen)/Dest[168 0 R/XYZ null 448 null]/Prev 238 0 R/Next 240 0 R>>endobj
+240 0 obj<</Parent 237 0 R/Title(BrowsePort)/Dest[168 0 R/XYZ null 285 null]/Prev 239 0 R/Next 241 0 R>>endobj
+241 0 obj<</Parent 237 0 R/Title(BrowseAddress)/Dest[168 0 R/XYZ null 159 null]/Prev 240 0 R>>endobj
+242 0 obj<</Parent 234 0 R/Count -8/First 243 0 R/Last 250 0 R/Title(Printer Security)/Dest[171 0 R/XYZ null 685 null]/Prev 237 0 R/Next 251 0 R>>endobj
+243 0 obj<</Parent 242 0 R/Title(Location)/Dest[171 0 R/XYZ null 611 null]/Next 244 0 R>>endobj
+244 0 obj<</Parent 242 0 R/Title(Order)/Dest[171 0 R/XYZ null 306 null]/Prev 243 0 R/Next 245 0 R>>endobj
+245 0 obj<</Parent 242 0 R/Title(Allow)/Dest[171 0 R/XYZ null 167 null]/Prev 244 0 R/Next 246 0 R>>endobj
+246 0 obj<</Parent 242 0 R/Title(Deny)/Dest[174 0 R/XYZ null 370 null]/Prev 245 0 R/Next 247 0 R>>endobj
+247 0 obj<</Parent 242 0 R/Title(AuthType)/Dest[177 0 R/XYZ null 570 null]/Prev 246 0 R/Next 248 0 R>>endobj
+248 0 obj<</Parent 242 0 R/Title(AuthClass)/Dest[177 0 R/XYZ null 444 null]/Prev 247 0 R/Next 249 0 R>>endobj
+249 0 obj<</Parent 242 0 R/Title(AuthGroupName)/Dest[177 0 R/XYZ null 265 null]/Prev 248 0 R/Next 250 0 R>>endobj
+250 0 obj<</Parent 242 0 R/Title(SystemGroup)/Dest[177 0 R/XYZ null 179 null]/Prev 249 0 R>>endobj
+251 0 obj<</Parent 234 0 R/Count -2/First 252 0 R/Last 253 0 R/Title(File Formats)/Dest[180 0 R/XYZ null 738 null]/Prev 242 0 R>>endobj
+252 0 obj<</Parent 251 0 R/Title(mime.types)/Dest[180 0 R/XYZ null 658 null]/Next 253 0 R>>endobj
+253 0 obj<</Parent 251 0 R/Title(mime.convs)/Dest[180 0 R/XYZ null 207 null]/Prev 252 0 R>>endobj
+254 0 obj<</Parent 213 0 R/Count -4/First 255 0 R/Last 258 0 R/Title(5 - Printer Accounting)/Dest[189 0 R/XYZ null 746 null]/Prev 234 0 R/Next 259 0 R>>endobj
+255 0 obj<</Parent 254 0 R/Title(Where to Find the Log Files)/Dest[189 0 R/XYZ null 387 null]/Next 256 0 R>>endobj
+256 0 obj<</Parent 254 0 R/Title(The access_log File)/Dest[189 0 R/XYZ null 307 null]/Prev 255 0 R/Next 257 0 R>>endobj
+257 0 obj<</Parent 254 0 R/Title(The error_log File)/Dest[192 0 R/XYZ null 516 null]/Prev 256 0 R/Next 258 0 R>>endobj
+258 0 obj<</Parent 254 0 R/Title(The page_log File)/Dest[192 0 R/XYZ null 223 null]/Prev 257 0 R>>endobj
+259 0 obj<</Parent 213 0 R/Count -3/First 260 0 R/Last 262 0 R/Title(A - Using CUPS with SAMBA)/Dest[201 0 R/XYZ null 746 null]/Prev 254 0 R>>endobj
+260 0 obj<</Parent 259 0 R/Title(What is SAMBA?)/Dest[201 0 R/XYZ null 387 null]/Next 261 0 R>>endobj
+261 0 obj<</Parent 259 0 R/Title(How Do I Configure SAMBA for CUPS?)/Dest[201 0 R/XYZ null 241 null]/Prev 260 0 R/Next 262 0 R>>endobj
+262 0 obj<</Parent 259 0 R/Title(How Do I Configure CUPS for SAMBA?)/Dest[204 0 R/XYZ null 738 null]/Prev 261 0 R>>endobj
+263 0 obj<</Type/Catalog/Pages 128 0 R/Names 76 0 R/PageLayout/SinglePage/Outlines 213 0 R/OpenAction[135 0 R/XYZ null null null]/PageMode/UseOutlines/PageLabels<</Nums[0<</P(title)>>1<</P(eltit)>>2<</S/r>>4<</S/D>>]>>>>endobj
+xref
+0 264
+0000000000 65535 f
+0000000015 00000 n
+0000000228 00000 n
+0000001794 00000 n
+0000001868 00000 n
+0000001950 00000 n
+0000002028 00000 n
+0000002105 00000 n
+0000002184 00000 n
+0000002260 00000 n
+0000002341 00000 n
+0000002400 00000 n
+0000002452 00000 n
+0000002537 00000 n
+0000002561 00000 n
+0000002666 00000 n
+0000002731 00000 n
+0000002816 00000 n
+0000002874 00000 n
+0000002957 00000 n
+0000002995 00000 n
+0000003055 00000 n
+0000003139 00000 n
+0000003163 00000 n
+0000003214 00000 n
+0000003299 00000 n
+0000003323 00000 n
+0000003427 00000 n
+0000003532 00000 n
+0000003637 00000 n
+0000003741 00000 n
+0000003846 00000 n
+0000003951 00000 n
+0000004056 00000 n
+0000004161 00000 n
+0000004266 00000 n
+0000004371 00000 n
+0000004475 00000 n
+0000004580 00000 n
+0000004685 00000 n
+0000004790 00000 n
+0000004895 00000 n
+0000005000 00000 n
+0000005105 00000 n
+0000005210 00000 n
+0000005315 00000 n
+0000005419 00000 n
+0000005524 00000 n
+0000005629 00000 n
+0000005734 00000 n
+0000005839 00000 n
+0000005944 00000 n
+0000006049 00000 n
+0000006154 00000 n
+0000006259 00000 n
+0000006364 00000 n
+0000006469 00000 n
+0000006574 00000 n
+0000006679 00000 n
+0000006784 00000 n
+0000006889 00000 n
+0000006994 00000 n
+0000007099 00000 n
+0000007204 00000 n
+0000007309 00000 n
+0000007414 00000 n
+0000007518 00000 n
+0000007622 00000 n
+0000007725 00000 n
+0000007828 00000 n
+0000008146 00000 n
+0000008250 00000 n
+0000008354 00000 n
+0000008458 00000 n
+0000008562 00000 n
+0000008666 00000 n
+0000008718 00000 n
+0000008750 00000 n
+0000008782 00000 n
+0000009434 00000 n
+0000009482 00000 n
+0000009530 00000 n
+0000009578 00000 n
+0000009626 00000 n
+0000009674 00000 n
+0000009722 00000 n
+0000009770 00000 n
+0000009818 00000 n
+0000009866 00000 n
+0000009914 00000 n
+0000009962 00000 n
+0000010010 00000 n
+0000010058 00000 n
+0000010106 00000 n
+0000010154 00000 n
+0000010202 00000 n
+0000010250 00000 n
+0000010298 00000 n
+0000010346 00000 n
+0000010394 00000 n
+0000010442 00000 n
+0000010491 00000 n
+0000010540 00000 n
+0000010589 00000 n
+0000010638 00000 n
+0000010687 00000 n
+0000010736 00000 n
+0000010785 00000 n
+0000010834 00000 n
+0000010883 00000 n
+0000010932 00000 n
+0000010981 00000 n
+0000011030 00000 n
+0000011079 00000 n
+0000011128 00000 n
+0000011177 00000 n
+0000011226 00000 n
+0000011275 00000 n
+0000011324 00000 n
+0000011373 00000 n
+0000011422 00000 n
+0000011471 00000 n
+0000011520 00000 n
+0000011569 00000 n
+0000011618 00000 n
+0000011667 00000 n
+0000011716 00000 n
+0000011765 00000 n
+0000011814 00000 n
+0000012107 00000 n
+0000012259 00000 n
+0000018615 00000 n
+0000018637 00000 n
+0000018732 00000 n
+0000018834 00000 n
+0000018854 00000 n
+0000019009 00000 n
+0000019924 00000 n
+0000019945 00000 n
+0000020077 00000 n
+0000020392 00000 n
+0000020413 00000 n
+0000020567 00000 n
+0000021546 00000 n
+0000021567 00000 n
+0000021730 00000 n
+0000022748 00000 n
+0000022769 00000 n
+0000022900 00000 n
+0000023394 00000 n
+0000023415 00000 n
+0000023528 00000 n
+0000023722 00000 n
+0000023743 00000 n
+0000023901 00000 n
+0000024700 00000 n
+0000024721 00000 n
+0000024889 00000 n
+0000026056 00000 n
+0000026078 00000 n
+0000026236 00000 n
+0000027280 00000 n
+0000027301 00000 n
+0000027414 00000 n
+0000027611 00000 n
+0000027632 00000 n
+0000027781 00000 n
+0000028473 00000 n
+0000028494 00000 n
+0000028652 00000 n
+0000029737 00000 n
+0000029759 00000 n
+0000029927 00000 n
+0000030959 00000 n
+0000030980 00000 n
+0000031129 00000 n
+0000031964 00000 n
+0000031985 00000 n
+0000032135 00000 n
+0000032993 00000 n
+0000033014 00000 n
+0000033164 00000 n
+0000034513 00000 n
+0000034535 00000 n
+0000034675 00000 n
+0000035489 00000 n
+0000035510 00000 n
+0000035623 00000 n
+0000035818 00000 n
+0000035839 00000 n
+0000035988 00000 n
+0000036781 00000 n
+0000036802 00000 n
+0000036961 00000 n
+0000038119 00000 n
+0000038141 00000 n
+0000038281 00000 n
+0000038861 00000 n
+0000038882 00000 n
+0000038995 00000 n
+0000039194 00000 n
+0000039215 00000 n
+0000039369 00000 n
+0000040062 00000 n
+0000040083 00000 n
+0000040232 00000 n
+0000040778 00000 n
+0000040799 00000 n
+0000040953 00000 n
+0000043360 00000 n
+0000043382 00000 n
+0000043536 00000 n
+0000044251 00000 n
+0000044272 00000 n
+0000044327 00000 n
+0000044432 00000 n
+0000044576 00000 n
+0000044679 00000 n
+0000044784 00000 n
+0000044953 00000 n
+0000045109 00000 n
+0000045209 00000 n
+0000045324 00000 n
+0000045448 00000 n
+0000045556 00000 n
+0000045676 00000 n
+0000045841 00000 n
+0000045948 00000 n
+0000046114 00000 n
+0000046219 00000 n
+0000046337 00000 n
+0000046453 00000 n
+0000046581 00000 n
+0000046712 00000 n
+0000046834 00000 n
+0000047001 00000 n
+0000047121 00000 n
+0000047237 00000 n
+0000047395 00000 n
+0000047487 00000 n
+0000047594 00000 n
+0000047705 00000 n
+0000047806 00000 n
+0000047959 00000 n
+0000048055 00000 n
+0000048161 00000 n
+0000048267 00000 n
+0000048372 00000 n
+0000048481 00000 n
+0000048591 00000 n
+0000048705 00000 n
+0000048804 00000 n
+0000048940 00000 n
+0000049038 00000 n
+0000049136 00000 n
+0000049295 00000 n
+0000049410 00000 n
+0000049530 00000 n
+0000049649 00000 n
+0000049754 00000 n
+0000049903 00000 n
+0000050005 00000 n
+0000050140 00000 n
+0000050262 00000 n
+trailer
+<</Size 264/Root 263 0 R/Info 1 0 R>>
+startxref
+50489
+%%EOF
diff --git a/doc/sam.shtml b/doc/sam.shtml
new file mode 100644
index 000000000..b472293d1
--- /dev/null
+++ b/doc/sam.shtml
@@ -0,0 +1,960 @@
+<HTML>
+<HEAD>
+ <META NAME="COPYRIGHT" CONTENT="Copyright 1997-2000, All Rights Reserved">
+ <META NAME="DOCNUMBER" CONTENT="CUPS-SAM-1.1">
+ <META NAME="Author" CONTENT="Easy Software Products">
+ <TITLE>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.1.
+
+<EMBED SRC="system-overview.shtml">
+
+<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>
+ <LI>A - Using CUPS with SAMBA</LI>
+</UL>
+
+<EMBED SRC="printing-overview.shtml">
+
+<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-compliant 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>/etc</CODE>, <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
+ <DT>smb://[username:password@]workgroup/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.
+
+<H1 ALIGN="RIGHT">A - Using CUPS with SAMBA</H1>
+
+<P>This appendix describes how to use CUPS with SAMBA.
+
+<H2>What is SAMBA?</H2>
+
+<P>In case you haven't heard of SAMBA, it is basically a software package
+that allows you to configure your UNIX system as a Windows file and printer
+server. It also allows you to access files and printers on a Windows system.
+Like CUPS, SAMBA is free software.
+
+<P>SAMBA version 2.0.6 is the first release of SAMBA that supports CUPS.
+You can download SAMBA from:
+
+<P ALIGN="CENTER"><A HREF="http://www.samba.org">http://www.samba.org</A></P>
+
+<H2>How Do I Configure SAMBA for CUPS?</H2>
+
+<P>To configure SAMBA for CUPS, edit the <CODE>smb.conf</CODE> file and
+replace the existing printing commands and options with the line:
+
+<UL><PRE>
+printing = cups
+</PRE></UL>
+
+<P>That's all there is to it! Remote users will now be able to browse and
+print to printers on your system.
+
+<H2>How Do I Configure CUPS for SAMBA?</H2>
+
+<P>To configure CUPS for SAMBA, run the following command:
+
+<UL><PRE>
+% ln -s `which smbspool` /var/cups/backend/smb ENTER
+</PRE></UL>
+
+<P>The <CODE>smbspool</CODE> program is provided with SAMBA starting with
+SAMBA 2.0.6. Once you have made the link you can use the <CODE>smb</CODE>
+method in the device URI for your printers:
+
+<UL><PRE>
+% lpadmin -p <I>printer</I> -v smb://<I>hostname/printer</I> ... ENTER
+% lpadmin -p <I>printer</I> -v smb://<I>workgroup/hostname/printer</I> ... ENTER
+</PRE></UL>
+
+<P>The second form only needs to be used if the Windows system is in a
+different workgroup.
+
+</BODY>
+</HTML>
diff --git a/doc/sdd.html b/doc/sdd.html
new file mode 100644
index 000000000..0423a9cde
--- /dev/null
+++ b/doc/sdd.html
@@ -0,0 +1,494 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
+<HTML>
+<HEAD>
+<TITLE>CUPS Software Design Description</TITLE>
+<META NAME="AUTHOR" CONTENT="Easy Software Products">
+<META NAME="COPYRIGHT" CONTENT="Copyright 1997-2000, All Rights Reserved">
+<META NAME="DOCNUMBER" CONTENT="CUPS-SDD-1.1">
+<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
+<STYLE>
+BODY { font-family: serif; font-size: 11.0pt }
+H1 { font-family: sans-serif; font-size: 20.0pt }
+H2 { font-family: sans-serif; font-size: 17.0pt }
+H3 { font-family: sans-serif; font-size: 14.0pt }
+H4 { font-family: sans-serif; font-size: 11.0pt }
+H5 { font-family: sans-serif; font-size: 9.0pt }
+H6 { font-family: sans-serif; font-size: 8.0pt }
+SUB { font-size: 8.0pt }
+SUP { font-size: 8.0pt }
+PRE { font-size: 9.0pt }
+A:link { text-decoration: underline }
+A:visited { text-decoration: underline }
+A:active { text-decoration: underline }
+</STYLE>
+</HEAD>
+<BODY>
+<CENTER><A HREF="#CONTENTS"><IMG SRC="images/cups-large.gif" BORDER="0"><BR>
+<H1>CUPS Software Design Description</H1></A><BR>
+CUPS-SDD-1.1<BR>
+Easy Software Products<BR>
+Copyright 1997-2000, 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 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.cgi</A></LI>
+<LI><A HREF="#3_3_2">3.3.2 jobs.cgi</A></LI>
+<LI><A HREF="#3_3_3">3.3.3 printers.cgi</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 imagetoraster</A></LI>
+<LI><A HREF="#3_5_4">3.5.4 pstops</A></LI>
+<LI><A HREF="#3_5_5">3.5.5 pstoraster</A></LI>
+<LI><A HREF="#3_5_6">3.5.6 rastertohp</A></LI>
+<LI><A HREF="#3_5_7">3.5.7 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 Logging</A></LI>
+<LI><A HREF="#3_6_9">3.6.9 Main</A></LI>
+<LI><A HREF="#3_6_10">3.6.10 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">A Glossary</A></B>
+<UL>
+<LI><A HREF="#4_1">A.1 Terms</A></LI>
+<LI><A HREF="#4_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.1.
+<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 <A HREF="http://www.easysw.com">
+Easy Software Products</A> 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 5.50) and an image file RIP that
+is 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>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.1: CUPS Configuration Management Plan </LI>
+<LI>CUPS-IDD-1.1: CUPS System Interface Design Description </LI>
+<LI>CUPS-IPP-1.1: CUPS Implmentation of IPP </LI>
+<LI>CUPS-SAM-1.1.x: CUPS Software Administrators Manual </LI>
+<LI>CUPS-SDD-1.1: CUPS Software Design Description </LI>
+<LI>CUPS-SPM-1.1: CUPS Software Programming Manual </LI>
+<LI>CUPS-SSR-1.1: CUPS Software Security Report </LI>
+<LI>CUPS-STP-1.1: CUPS Software Test Plan </LI>
+<LI>CUPS-SUM-1.1.x: CUPS Software Users Manual </LI>
+<LI>CUPS-SVD-1.1.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>Adobe PostScript Printer Description File Format Specification,
+ Version 4.3. </LI>
+<LI>Adobe PostScript Language Reference, Third Edition. </LI>
+<LI>IPP: Job and Printer Set Operations </LI>
+<LI>IPP/1.1: Encoding and Transport </LI>
+<LI>IPP/1.1: Implementers Guide </LI>
+<LI>IPP/1.1: Model and Semantics </LI>
+<LI>RFC 1179, Line Printer Daemon Protocol </LI>
+<LI>RFC 2567, Design Goals for an Internet Printing Protocol </LI>
+<LI>RFC 2568, Rationale for the Structure of the Model and Protocol
+ for the Internet Printing Protocol </LI>
+<LI>RFC 2569, Mapping between LPD and IPP Protocols </LI>
+<LI>RFC 2616, Hypertext Transfer Protocol -- HTTP/1.1 </LI>
+<LI>RFC 2617, HTTP Authentication: Basic and Digest Access
+ Authentication </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 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.
+<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>quit - Quits the lpc command. </LI>
+<LI>status - Shows the status of printers and jobs in the queue. </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;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.cgi</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.cgi</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.cgi</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, get a list
+of available classes, get the default printer or class, get the default
+server name, get the local username, and get a password string.
+<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 imagetoraster</A></H3>
+ The imagetoraster filter converts image files into CUPS raster data.
+<H3><A NAME="3_5_4">3.5.4 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_5">3.5.5 pstoraster</A></H3>
+ The pstoraster filter converts PostScript program data into CUPS
+raster data.
+<H3><A NAME="3_5_6">3.5.6 rastertohp</A></H3>
+ The rastertohp filter handles converting CUPS raster data to HP PCL
+and supports both color and black-and-white printers.
+<H3><A NAME="3_5_7">3.5.7 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 and IPP/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 Logging</A></H3>
+ The logging module manages the access, error, and page log files that
+are generated by the scheduler.
+<H3><A NAME="3_6_9">3.6.9 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> and <CODE>SIGCHLD</CODE> signals, reloads the server
+configuration files as needed, and handles child process errors and
+exits.
+<H3><A NAME="3_6_10">3.6.10 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 printers and 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), &quot;F&quot; (filter), and &quot;E&quot; (enable and accept) 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 TYPE="A" VALUE="1"><A NAME="4">A Glossary</A></H1>
+<H2><A NAME="4_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="4_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..eec1864ee
--- /dev/null
+++ b/doc/sdd.pdf
@@ -0,0 +1,769 @@
+%PDF-1.2
+%âãÏÓ
+1 0 obj<</Producer(htmldoc 1.8.6 Copyright 1997-2000 Easy Software Products, All Rights Reserved.)/CreationDate(D:20000407154522Z)/Title(CUPS Software Design Description)/Author(Easy Software Products)>>endobj
+2 0 obj<</Type/Encoding/Differences[ 32/space/exclam/quotedbl/numbersign/dollar/percent/ampersand/quotesingle/parenleft/parenright/asterisk/plus/comma/minus/period/slash/zero/one/two/three/four/five/six/seven/eight/nine/colon/semicolon/less/equal/greater/question/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/grave/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 160/space/exclamdown/cent/sterling/currency/yen/brokenbar/section/dieresis/copyright/ordfeminine/guillemotleft/logicalnot/hyphen/registered/macron/degree/plusminus/twosuperior/threesuperior/acute/mu/paragraph/periodcentered/cedilla/onesuperior/ordmasculine/guillemotright/onequarter/onehalf/threequarters/questiondown/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]>>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<</S/URI/URI(http://www.easysw.com)>>endobj
+12 0 obj<</Subtype/Link/Rect[157.2 547.2 262.3 560.2]/Border[0 0 0]/A 11 0 R>>endobj
+13 0 obj[12 0 R
+]endobj
+14 0 obj<</Subtype/Link/Rect[72.0 670.8 107.8 683.8]/Border[0 0 0]/Dest[149 0 R/XYZ null 818 0]>>endobj
+15 0 obj<</Subtype/Link/Rect[108.0 657.6 183.8 670.6]/Border[0 0 0]/Dest[149 0 R/XYZ null 735 0]>>endobj
+16 0 obj<</Subtype/Link/Rect[108.0 644.4 203.0 657.4]/Border[0 0 0]/Dest[149 0 R/XYZ null 675 0]>>endobj
+17 0 obj<</Subtype/Link/Rect[108.0 631.2 216.4 644.2]/Border[0 0 0]/Dest[149 0 R/XYZ null 449 0]>>endobj
+18 0 obj<</Subtype/Link/Rect[72.0 604.8 131.6 617.8]/Border[0 0 0]/Dest[155 0 R/XYZ null 818 0]>>endobj
+19 0 obj<</Subtype/Link/Rect[108.0 591.6 222.6 604.6]/Border[0 0 0]/Dest[155 0 R/XYZ null 735 0]>>endobj
+20 0 obj<</Subtype/Link/Rect[108.0 578.4 202.4 591.4]/Border[0 0 0]/Dest[155 0 R/XYZ null 522 0]>>endobj
+21 0 obj<</Subtype/Link/Rect[72.0 552.0 160.0 565.0]/Border[0 0 0]/Dest[161 0 R/XYZ null 818 0]>>endobj
+22 0 obj<</Subtype/Link/Rect[108.0 538.8 167.3 551.8]/Border[0 0 0]/Dest[161 0 R/XYZ null 623 0]>>endobj
+23 0 obj<</Subtype/Link/Rect[144.0 525.6 182.8 538.6]/Border[0 0 0]/Dest[161 0 R/XYZ null 357 0]>>endobj
+24 0 obj<</Subtype/Link/Rect[144.0 512.4 182.8 525.4]/Border[0 0 0]/Dest[161 0 R/XYZ null 283 0]>>endobj
+25 0 obj<</Subtype/Link/Rect[144.0 499.2 201.7 512.2]/Border[0 0 0]/Dest[161 0 R/XYZ null 186 0]>>endobj
+26 0 obj<</Subtype/Link/Rect[144.0 486.0 192.6 499.0]/Border[0 0 0]/Dest[164 0 R/XYZ null 782 0]>>endobj
+27 0 obj<</Subtype/Link/Rect[144.0 472.8 196.9 485.8]/Border[0 0 0]/Dest[164 0 R/XYZ null 579 0]>>endobj
+28 0 obj<</Subtype/Link/Rect[108.0 459.6 217.1 472.6]/Border[0 0 0]/Dest[164 0 R/XYZ null 469 0]>>endobj
+29 0 obj<</Subtype/Link/Rect[144.0 446.4 182.2 459.4]/Border[0 0 0]/Dest[164 0 R/XYZ null 375 0]>>endobj
+30 0 obj<</Subtype/Link/Rect[144.0 433.2 181.0 446.2]/Border[0 0 0]/Dest[164 0 R/XYZ null 262 0]>>endobj
+31 0 obj<</Subtype/Link/Rect[144.0 420.0 189.5 433.0]/Border[0 0 0]/Dest[164 0 R/XYZ null 189 0]>>endobj
+32 0 obj<</Subtype/Link/Rect[108.0 406.8 143.4 419.8]/Border[0 0 0]/Dest[167 0 R/XYZ null 799 0]>>endobj
+33 0 obj<</Subtype/Link/Rect[144.0 393.6 215.5 406.6]/Border[0 0 0]/Dest[167 0 R/XYZ null 705 0]>>endobj
+34 0 obj<</Subtype/Link/Rect[144.0 380.4 203.3 393.4]/Border[0 0 0]/Dest[167 0 R/XYZ null 631 0]>>endobj
+35 0 obj<</Subtype/Link/Rect[144.0 367.2 218.5 380.2]/Border[0 0 0]/Dest[167 0 R/XYZ null 558 0]>>endobj
+36 0 obj<</Subtype/Link/Rect[108.0 354.0 229.6 367.0]/Border[0 0 0]/Dest[167 0 R/XYZ null 499 0]>>endobj
+37 0 obj<</Subtype/Link/Rect[144.0 340.8 272.3 353.8]/Border[0 0 0]/Dest[167 0 R/XYZ null 405 0]>>endobj
+38 0 obj<</Subtype/Link/Rect[144.0 327.6 242.4 340.6]/Border[0 0 0]/Dest[167 0 R/XYZ null 318 0]>>endobj
+39 0 obj<</Subtype/Link/Rect[144.0 314.4 230.8 327.4]/Border[0 0 0]/Dest[167 0 R/XYZ null 245 0]>>endobj
+40 0 obj<</Subtype/Link/Rect[144.0 301.2 258.3 314.2]/Border[0 0 0]/Dest[170 0 R/XYZ null 782 0]>>endobj
+41 0 obj<</Subtype/Link/Rect[144.0 288.0 244.8 301.0]/Border[0 0 0]/Dest[170 0 R/XYZ null 708 0]>>endobj
+42 0 obj<</Subtype/Link/Rect[144.0 274.8 235.1 287.8]/Border[0 0 0]/Dest[170 0 R/XYZ null 622 0]>>endobj
+43 0 obj<</Subtype/Link/Rect[144.0 261.6 243.0 274.6]/Border[0 0 0]/Dest[170 0 R/XYZ null 549 0]>>endobj
+44 0 obj<</Subtype/Link/Rect[108.0 248.4 152.6 261.4]/Border[0 0 0]/Dest[170 0 R/XYZ null 490 0]>>endobj
+45 0 obj<</Subtype/Link/Rect[144.0 235.2 206.6 248.2]/Border[0 0 0]/Dest[170 0 R/XYZ null 237 0]>>endobj
+46 0 obj<</Subtype/Link/Rect[144.0 222.0 214.0 235.0]/Border[0 0 0]/Dest[170 0 R/XYZ null 177 0]>>endobj
+47 0 obj<</Subtype/Link/Rect[144.0 208.8 228.6 221.8]/Border[0 0 0]/Dest[173 0 R/XYZ null 782 0]>>endobj
+48 0 obj<</Subtype/Link/Rect[144.0 195.6 196.9 208.6]/Border[0 0 0]/Dest[173 0 R/XYZ null 722 0]>>endobj
+49 0 obj<</Subtype/Link/Rect[144.0 182.4 211.5 195.4]/Border[0 0 0]/Dest[173 0 R/XYZ null 648 0]>>endobj
+50 0 obj<</Subtype/Link/Rect[144.0 169.2 212.7 182.2]/Border[0 0 0]/Dest[173 0 R/XYZ null 588 0]>>endobj
+51 0 obj<</Subtype/Link/Rect[144.0 156.0 203.6 169.0]/Border[0 0 0]/Dest[173 0 R/XYZ null 515 0]>>endobj
+52 0 obj<</Subtype/Link/Rect[108.0 142.8 168.5 155.8]/Border[0 0 0]/Dest[173 0 R/XYZ null 469 0]>>endobj
+53 0 obj<</Subtype/Link/Rect[144.0 129.6 229.9 142.6]/Border[0 0 0]/Dest[173 0 R/XYZ null 361 0]>>endobj
+54 0 obj<</Subtype/Link/Rect[144.0 116.4 201.8 129.4]/Border[0 0 0]/Dest[173 0 R/XYZ null 288 0]>>endobj
+55 0 obj<</Subtype/Link/Rect[144.0 103.2 195.6 116.2]/Border[0 0 0]/Dest[173 0 R/XYZ null 215 0]>>endobj
+56 0 obj<</Subtype/Link/Rect[144.0 90.0 230.5 103.0]/Border[0 0 0]/Dest[176 0 R/XYZ null 782 0]>>endobj
+57 0 obj<</Subtype/Link/Rect[144.0 76.8 250.9 89.8]/Border[0 0 0]/Dest[176 0 R/XYZ null 695 0]>>endobj
+58 0 obj<</Subtype/Link/Rect[144.0 63.6 184.6 76.6]/Border[0 0 0]/Dest[176 0 R/XYZ null 582 0]>>endobj
+59 0 obj[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
+]endobj
+60 0 obj<</Subtype/Link/Rect[108.0 670.8 152.3 683.8]/Border[0 0 0]/Dest[176 0 R/XYZ null 496 0]>>endobj
+61 0 obj<</Subtype/Link/Rect[108.0 657.6 170.0 670.6]/Border[0 0 0]/Dest[176 0 R/XYZ null 423 0]>>endobj
+62 0 obj<</Subtype/Link/Rect[108.0 644.4 156.0 657.4]/Border[0 0 0]/Dest[176 0 R/XYZ null 363 0]>>endobj
+63 0 obj<</Subtype/Link/Rect[108.0 631.2 172.5 644.2]/Border[0 0 0]/Dest[176 0 R/XYZ null 276 0]>>endobj
+64 0 obj<</Subtype/Link/Rect[72.0 618.0 184.5 631.0]/Border[0 0 0]/Dest[176 0 R/XYZ null 217 0]>>endobj
+65 0 obj<</Subtype/Link/Rect[108.0 604.8 160.8 617.8]/Border[0 0 0]/Dest[179 0 R/XYZ null 782 0]>>endobj
+66 0 obj<</Subtype/Link/Rect[108.0 591.6 160.8 604.6]/Border[0 0 0]/Dest[179 0 R/XYZ null 722 0]>>endobj
+67 0 obj<</Subtype/Link/Rect[108.0 578.4 163.9 591.4]/Border[0 0 0]/Dest[179 0 R/XYZ null 662 0]>>endobj
+68 0 obj<</Subtype/Link/Rect[108.0 565.2 161.5 578.2]/Border[0 0 0]/Dest[179 0 R/XYZ null 602 0]>>endobj
+69 0 obj<</Subtype/Link/Rect[108.0 552.0 141.3 565.0]/Border[0 0 0]/Dest[179 0 R/XYZ null 542 0]>>endobj
+70 0 obj<</Subtype/Link/Rect[108.0 538.8 168.8 551.8]/Border[0 0 0]/Dest[179 0 R/XYZ null 455 0]>>endobj
+71 0 obj<</Subtype/Link/Rect[108.0 525.6 156.6 538.6]/Border[0 0 0]/Dest[179 0 R/XYZ null 355 0]>>endobj
+72 0 obj<</Subtype/Link/Rect[108.0 512.4 157.2 525.4]/Border[0 0 0]/Dest[179 0 R/XYZ null 295 0]>>endobj
+73 0 obj<</Subtype/Link/Rect[36.0 486.0 88.3 499.0]/Border[0 0 0]/Dest[185 0 R/XYZ null 818 0]>>endobj
+74 0 obj<</Subtype/Link/Rect[72.0 472.8 119.0 485.8]/Border[0 0 0]/Dest[185 0 R/XYZ null 735 0]>>endobj
+75 0 obj<</Subtype/Link/Rect[72.0 459.6 136.8 472.6]/Border[0 0 0]/Dest[185 0 R/XYZ null 549 0]>>endobj
+76 0 obj[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
+]endobj
+77 0 obj<</Dests 78 0 R>>endobj
+78 0 obj<</Kids[79 0 R]>>endobj
+79 0 obj<</Limits[(1)(sdd.shtml)]/Names[(1)80 0 R(1_1)81 0 R(1_2)82 0 R(1_3)83 0 R(2)84 0 R(2_1)85 0 R(2_2)86 0 R(3)87 0 R(3_1)88 0 R(3_1_1)89 0 R(3_1_2)90 0 R(3_1_3)91 0 R(3_1_4)92 0 R(3_1_5)93 0 R(3_2)94 0 R(3_2_1)95 0 R(3_2_2)96 0 R(3_2_3)97 0 R(3_3)98 0 R(3_3_1)99 0 R(3_3_2)100 0 R(3_3_3)101 0 R(3_4)102 0 R(3_4_1)103 0 R(3_4_2)104 0 R(3_4_3)105 0 R(3_4_4)106 0 R(3_4_5)107 0 R(3_4_6)108 0 R(3_4_7)109 0 R(3_5)110 0 R(3_5_1)111 0 R(3_5_2)112 0 R(3_5_3)113 0 R(3_5_4)114 0 R(3_5_5)115 0 R(3_5_6)116 0 R(3_5_7)117 0 R(3_6)118 0 R(3_6_1)119 0 R(3_6_10)120 0 R(3_6_2)121 0 R(3_6_3)122 0 R(3_6_4)123 0 R(3_6_5)124 0 R(3_6_6)125 0 R(3_6_7)126 0 R(3_6_8)127 0 R(3_6_9)128 0 R(3_7)129 0 R(3_7_1)130 0 R(3_7_2)131 0 R(3_7_3)132 0 R(3_7_4)133 0 R(3_7_5)134 0 R(3_7_6)135 0 R(3_7_7)136 0 R(3_7_8)137 0 R(4)138 0 R(4_1)139 0 R(4_2)140 0 R(sdd.shtml)141 0 R]>>endobj
+80 0 obj<</D[149 0 R/XYZ null 818 null]>>endobj
+81 0 obj<</D[149 0 R/XYZ null 735 null]>>endobj
+82 0 obj<</D[149 0 R/XYZ null 675 null]>>endobj
+83 0 obj<</D[149 0 R/XYZ null 449 null]>>endobj
+84 0 obj<</D[155 0 R/XYZ null 818 null]>>endobj
+85 0 obj<</D[155 0 R/XYZ null 735 null]>>endobj
+86 0 obj<</D[155 0 R/XYZ null 522 null]>>endobj
+87 0 obj<</D[161 0 R/XYZ null 818 null]>>endobj
+88 0 obj<</D[161 0 R/XYZ null 623 null]>>endobj
+89 0 obj<</D[161 0 R/XYZ null 357 null]>>endobj
+90 0 obj<</D[161 0 R/XYZ null 283 null]>>endobj
+91 0 obj<</D[161 0 R/XYZ null 186 null]>>endobj
+92 0 obj<</D[164 0 R/XYZ null 782 null]>>endobj
+93 0 obj<</D[164 0 R/XYZ null 579 null]>>endobj
+94 0 obj<</D[164 0 R/XYZ null 469 null]>>endobj
+95 0 obj<</D[164 0 R/XYZ null 375 null]>>endobj
+96 0 obj<</D[164 0 R/XYZ null 262 null]>>endobj
+97 0 obj<</D[164 0 R/XYZ null 189 null]>>endobj
+98 0 obj<</D[167 0 R/XYZ null 799 null]>>endobj
+99 0 obj<</D[167 0 R/XYZ null 705 null]>>endobj
+100 0 obj<</D[167 0 R/XYZ null 631 null]>>endobj
+101 0 obj<</D[167 0 R/XYZ null 558 null]>>endobj
+102 0 obj<</D[167 0 R/XYZ null 499 null]>>endobj
+103 0 obj<</D[167 0 R/XYZ null 405 null]>>endobj
+104 0 obj<</D[167 0 R/XYZ null 318 null]>>endobj
+105 0 obj<</D[167 0 R/XYZ null 245 null]>>endobj
+106 0 obj<</D[170 0 R/XYZ null 782 null]>>endobj
+107 0 obj<</D[170 0 R/XYZ null 708 null]>>endobj
+108 0 obj<</D[170 0 R/XYZ null 622 null]>>endobj
+109 0 obj<</D[170 0 R/XYZ null 549 null]>>endobj
+110 0 obj<</D[170 0 R/XYZ null 490 null]>>endobj
+111 0 obj<</D[170 0 R/XYZ null 237 null]>>endobj
+112 0 obj<</D[170 0 R/XYZ null 177 null]>>endobj
+113 0 obj<</D[173 0 R/XYZ null 782 null]>>endobj
+114 0 obj<</D[173 0 R/XYZ null 722 null]>>endobj
+115 0 obj<</D[173 0 R/XYZ null 648 null]>>endobj
+116 0 obj<</D[173 0 R/XYZ null 588 null]>>endobj
+117 0 obj<</D[173 0 R/XYZ null 515 null]>>endobj
+118 0 obj<</D[173 0 R/XYZ null 469 null]>>endobj
+119 0 obj<</D[173 0 R/XYZ null 361 null]>>endobj
+120 0 obj<</D[176 0 R/XYZ null 276 null]>>endobj
+121 0 obj<</D[173 0 R/XYZ null 288 null]>>endobj
+122 0 obj<</D[173 0 R/XYZ null 215 null]>>endobj
+123 0 obj<</D[176 0 R/XYZ null 782 null]>>endobj
+124 0 obj<</D[176 0 R/XYZ null 695 null]>>endobj
+125 0 obj<</D[176 0 R/XYZ null 582 null]>>endobj
+126 0 obj<</D[176 0 R/XYZ null 496 null]>>endobj
+127 0 obj<</D[176 0 R/XYZ null 423 null]>>endobj
+128 0 obj<</D[176 0 R/XYZ null 363 null]>>endobj
+129 0 obj<</D[176 0 R/XYZ null 217 null]>>endobj
+130 0 obj<</D[179 0 R/XYZ null 782 null]>>endobj
+131 0 obj<</D[179 0 R/XYZ null 722 null]>>endobj
+132 0 obj<</D[179 0 R/XYZ null 662 null]>>endobj
+133 0 obj<</D[179 0 R/XYZ null 602 null]>>endobj
+134 0 obj<</D[179 0 R/XYZ null 542 null]>>endobj
+135 0 obj<</D[179 0 R/XYZ null 455 null]>>endobj
+136 0 obj<</D[179 0 R/XYZ null 355 null]>>endobj
+137 0 obj<</D[179 0 R/XYZ null 295 null]>>endobj
+138 0 obj<</D[185 0 R/XYZ null 818 null]>>endobj
+139 0 obj<</D[185 0 R/XYZ null 735 null]>>endobj
+140 0 obj<</D[185 0 R/XYZ null 549 null]>>endobj
+141 0 obj<</D[149 0 R/XYZ null 698 null]>>endobj
+142 0 obj<</Type/Pages/MediaBox[0 0 595 792]/Count 18/Kids[143 0 R
+146 0 R
+191 0 R
+194 0 R
+149 0 R
+152 0 R
+155 0 R
+158 0 R
+161 0 R
+164 0 R
+167 0 R
+170 0 R
+173 0 R
+176 0 R
+179 0 R
+182 0 R
+185 0 R
+188 0 R
+]>>endobj
+143 0 obj<</Type/Page/Parent 142 0 R/Contents 144 0 R/Resources<</ProcSet[/PDF/Text/ImageB/ImageC/ImageI]/Font<</F4 5 0 R/F8 8 0 R/F9 9 0 R>>>>>>endobj
+144 0 obj<</Length 145 0 R/Filter/FlateDecode>>stream
+xÚìÏsë8rÇIŠºÌ‰öŒî´üt§çyšÝ­ÚJñÙfvçÖ$ÁÊ!•S*Ç6•üÿ±~X"H€èn
+ ‚B¹ü29×$X,0X
+Ú Ñ ê£s5Ѭåà‹+›— 9Ä`™ãÅ`ž‹,öD.1?V{àÊàŠRç\uD98æY\‰c®}HÙ8媣`Jé++D91WTθš°ŒÒË TÔ  LÔ‹Š4ðZåªBÃ2t,OãQ ÔxŒsÆq‰±FãzX¢Æ#F,Ï%£FL×]w÷Ûí—×cy>|ðÛa0IwÅ÷_†ƒüxÂ[‡ÿîŠG’þÅ˃ŸßÝuoœ*=[ í,¸ö~ºªÛk[ça=xì.Bú±àæØ\ìPƒ–.Î$[q¹¸¡==·aMJ&³»XéÔœ.K™\¬@ž› É貘ÇUùÑv‡]¶cqeSÙàÕ›eN¤ܯjؤ½s’  ®|²¡ÅϼJé\Í Xd°˜ÎEùØÍÉÜZ9ƹfÂ"‚­¨\Õ FÈ1Å’È•Ï…EÛи'’ –Y¹0p7ñú¥uZj+Cg‘¼c,Òé« …«rÎX•½ÅÍÁ‘jÄ¥{.B J<—°ÒlKÃ7D=Ì:¸ÎåÛÁÉ %i=•œkˆàBf½ .Ú`Ø`¹Š¹2O”,Á W­Ç’ó ìÍПR4q‡ãÚa…„–¤8®
+¢6(®–KH·Kh%ºÖRÕÓbQ®•–ër›®Îa‡Àà ¯Çé¹ £^ >ÜÌÑ–œáµCrí´\Mçù
+.A5C#WªçÚŸòáªÕr[¬àª©fhäŠõ\盽§5Rì•K,×êZ[Éu6Ž;êÊ12BªWNZ,×:{—'%WG¤(³ÔgO@• ž«x j®Š5K5†ˆëWC6Ãq®ZÂp ÖbI…Ó5 ÊFÒâ¹Ä»Y¨¹®&EZ,ip-â\9%pžê¹*ÖšNè(;
+\n£á¬5;c+\ŒôÅq®æ"
+®K‰kvª‰@“¤¥pµ¥×qgéB™ÐdcMãº(½Ž«àôWòD@“ë¢ô:®Œ3¾êÙwCÅuQz WÍÛÃÈ0’ ¤h#!r]”^ÃUðûMÏ%s Æð2r•^Õñ6g
+ÌÃ’lì¨\g¥Ws5Ì]Œ
+㘴¶ÑR¹ÎJ¯æ½ØER2WÁ^f®“Ò«¹rfæŽÀ8f DQ)ëdÝ…~^¹e¢IàkSYŽ­\§Öç*®³Ý FÈ‘#š ”(ªT?º²k ±ÌukDŸ+?krN7Ä="à
+vt6מÍ•4™¿peM/\œ¥ùp‰'óg®úÃÉ<–k2ßÎ-‡Í‹ó·•Ü:\[æ»uîªßFwÒ²ßmµwpœOm›.Ò,²
+£®[‘"K~­lâŽÃÕ¿(—óM
+-áðì8—Ðpz®”ÃÕ¿¨7RNf™
+E2þW=/WÁá’.ªn»Ê·EÞÙ¹j—t‘趲¾Æ6ÓJ\‚Ã%_”ßö¿ž#Wé“«{œÍ%_T ö+Càª8\òE½H0?qÙL—Q\Ù8WÃá’/’gÆé>®Îq><Wï"©ÃÊbB®ƒ¾‰‡K¾HN#‚—qy#¶äÊ£îíZðS¹zýèN«i¸®oÐ4Qp¸ú=w–"p…S^Nù‡?ßÞ?oäZJ¬èžó2Ûâr{Ï\ù'×'׸V”+]&WöÉõÉõÉõÉõÉ…ãZr}rùçŠì¹Þf>ès¥”ºór=ŸÞa?bšK©;3×Cÿøšü²¡ÎæuÕ»êªHqPJÛ:k®|¾!çéw·§†u\y1Ô×áŒF~™WsÛìø9QeäÚÉÔ—Pì(É/4¬®KüuòÈȵ™‚ TSë¼;Àz齺 ®u+ M?\™j« ºã “Òdúu\©l)^¸š÷§»nÝî«ko»õʺ ®Dºµ®êºQ(¾unÚ‘À[v…².Ÿ«öÉUtv¬÷7ïÒqY·äsM]Õ2¿ú®òöNå“+ª›v^Ȭ©K㊯ۧoƒ8ñÆ•u—áòžÒߌ§©KãJ®£0qÙÄóÒŸá¦×VËIÚªº$®Õõq¼=™t®êöÓ5ä$mU]WÚ}tk;.CúáõÏÍÝ[ÙtĪ윑Ò×%qÏ]ÓÆ+W¢ûѧ“T<¶ÔçÊ/ƒô8„wÞ¸2]:ÎeO¹éÀd#©;x®ýåóŽŸ|ðÆuŽš¸1‘†‘¶.‰«¸ÈÐÑÒKo\—½Îûƒ:À:eR¼Ë¹¶.‰ nÙ¯qëë¢Z{þ⇬ã‹õu•\ÃÄÎEu¯G÷å«“Òkmñîm®¾X_—ÂU_ `ü`;®±}½n=é½ÄÕ0‹V[—ÂÕ\~<ºeK®±}ØByŽCš—ƺ.]_ˆ¾ç²Ú_ÚÌÒ|Õ¢¯Kàj/Žùô[\½,Ú¸ôdŠ¨Ká:;æ“[öÉÕyÑ–L¡ú’9]]
+×Ù1×'ï“ëý ì —J•l:ZÇuvÌõ©¾—1ïë9Sš\ùPÔuñ~ùäËãÿc—‹|¶—‡á¤¦ÒLrTu \ÕÉOnÙ?×­µ«^(u@Õ%póÉ-OÁõ.
+‰vÐŒÕ%póÉ-[raß-Z‹ÁX]—x÷b멸n‰×f®^]×Ù1ŸEr”Ë&ûø*™ìŠµ\cu)\o&˜4·õS/\²{ãb§®.…ëM2âúì-¹JÜú¡k¬.…ëØÓUt}Q7®D²­x”KW—Âçð±‰ËæÜM.ý9Õù±º®ê|²&±çÚO¿6ÝyÈJË5V—ÂUßœº7®ªÛŸûÞmz«Kájn³Q.›sˆçkãï·M­–k¬®!ž—Þ¶%ä]A ®qö¶ ©Ÿ,ŽÕ¥puÞ ë«™«õ¹Fê’¸r—Õ¹ìf¤jŸk¤.ƒ«´çZãÒmú¡ÿ VÑ×%qƒßxàjô
+3àj¸ëQ2\ŸÊ(—Õû7䵘_ÚQ.}]—îý¬n¹:}l \Úº$®úêÓǹ¬²Þè!Ón”ØÔµ*–ïí¹Ê×ëMóU|–ïY
+¶X¾ë“kz.óë–—ÉUüÓr•‹ä2/pÉe3aþäšžËjb¹h®õ"¹Ä?-WºH.ólõA¹’ere0ðµü>ŽO®¸ö0ðÅ}Ïn‘\ðA¹ª Z~¯Ù\Åô²×å÷ÐÍULMÞá¾70¸@ªrÃ\ e’ºÒò{9ç*Š+[\Àa
+%°ß{ZÀ‘›ì ù=Å¡­´e&=@~¯thG„â‚¥Âèo‘ßoXÀQã¸÷}ô•±
+-V裥ÈaÜ媗#ˆ(Â-ôDˆ *âƒ-ôéBäpÝåB} Â(»‚-ôÇ¥€VÏP„#C̾:\ÕB")£œ· Å }ºÙHd.„Ð'‹TæB,ÝÄ‹u+_†pd8
+h)‚¸xí\Åú,(ÖCéÑg­¡% ÇÜ ýŽhIÂ1ó
+œéöÔ1GNÀY™8樼A:©Ôï˜gB©œÇ ÜÏ™.H,8Ž¸Ãtºc wàÊêd†kÍÁ…Tú‰:,c=càÔ4Æ €?R§é°ŒÖÛNÓaÓ™_‚& :C¢Åqí±`»0ºk…äªé0‘qƒph­ ±÷UKó„ªY.´v†èuâ,øV­!zÕzìÓÝ ¹ð†èQ:j ›ÖÖãrfWŠØ ¬OKüñÍPË%¢¹-±±’.h­ Ñ“&æV>¬­'BõÊXZ?Þ¹±´°öõ^†˜È,e \<1÷3–½í#×½{&ܸ¥rU¤s©ÍJÉ\"š Ldö
+\EiW°/)áøf08Ô&[õ cœFwžÿf<[l2êR6W›qÀúßtŽ*OôÛø\‹+Š¿S;‹añcq€‰‹ÙaQt_zî¬ñ¹‘‘«ˆ¸ÿ¥ó/9k·6\"â—{”56¼OßYqYtØqœ=šÌñ…Ie˜>˜¹l:ìŒöªÿ짌ý¹K.ê4Li¯ƒ~/ÛÌæqµ¶\Mä¦ÜÝo·Û/¯oåe»}È,?mcÍå¢ÃœSþ#†KȵqÀe'‰³tŽ+¼Û9á
+®ÃÌK_8.±´îBrvm¦(ˆ=$W›…ÄupÇU/«»Ð\¬•O_ºäj‚áÚ´.¹‚ÑzÜöžKdKÑxw gÑ q!Øý^
+—XŒÒ¸°DôÖ!‰kvKÄgиš…X!•kæø—°MäšÕ)¹/T®9½3eû‰Ê5c`OJš#sÍ6Ähi/t®™†1±ŒÁ5Ï#îí2¸fbÔ,%=pÍ`sM®ôT9×ÄÚÁÈ
+·¶øhß"7\o¶è®Ë’CW+Âé,—\ŽºìþІÆÕŠ'küâª-.¹ø©ÕªGw-qËe‘^HÝž“‹Kæ–ʇìþ‹ë6øà"æÅÇ[ï¹ðÃõV^qÙññýw/·÷Æuê5ƒAÞm_}ÝÛ'ש۞¶wê~z|õy_ß\çŽ;žpØ>ÜËývûøúêý–“pÍPàÿ
+ä
+mµyêÆ£™
+.ÁioñPendstream
+endobj
+145 0 obj
+6280
+endobj
+146 0 obj<</Type/Page/Parent 142 0 R/Contents 147 0 R/Resources<</ProcSet[/PDF/Text]>>>>endobj
+147 0 obj<</Length 148 0 R/Filter/FlateDecode>>stream
+xÚ+ä2T0
+ár á
+ä
+endobj
+148 0 obj
+31
+endobj
+149 0 obj<</Type/Page/Parent 142 0 R/Contents 150 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 5 0 R/F8 8 0 R/F9 9 0 R/Fc 10 0 R>>>>/Annots 13 0 R>>endobj
+150 0 obj<</Length 151 0 R/Filter/FlateDecode>>stream
+xÚ•UÛnÛ8}÷W úä
+CFåöÀ´ L˜b[¹/®‹ÚR¦xS"ÕZí X`µ¬"£¢Ê•.}~Â¿Ý bšï
++¸mUq•Õ–Tî n4l!:çU–ˆ{üºü‡]
+¿´**»…”F½(išÚiÊ:vGZ`„xÈ›Š»a0YØã›ýaYfm9(ýÔ2¦Vã(;‰26õwäÌéâ°`Ód eâBÊÛÑÍ_`H& ueF¬®ewâ 5U&Úµ|1Øcê\6ínóÆXUÿ¢ö¾;1*§û¯t¿;C_÷y£5 <R T寽ÆÁ8\_½ÀëšÀ**J7¦'“– DÂ,A"Ë…8±ªT5¸hY×RóúäBl­?yCZœ®ó;ŽÞ{=ü”Þ²Êw ”×u®¤T¿¨ÂKÃüÙÖâ]­xæ6óTnO¦ô ~¶—?\ü òÔ±·¢”D.0ìêÿ
+"tÑ2}ÞŸwÆBü½TØ3}lM³Î4èØGÝ,ÎÄF³0˜#³{Ûê¿{ÿnZÐendstream
+endobj
+151 0 obj
+900
+endobj
+152 0 obj<</Type/Page/Parent 142 0 R/Contents 153 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F8 8 0 R>>>>>>endobj
+153 0 obj<</Length 154 0 R/Filter/FlateDecode>>stream
+xÚ+ä2T0
+áÒw³P04TIS045×3V072PIÑp VÎO+)O,JUpI-ÎLÏQÉE™%™ùyš!Y\ººP-F 13=30ßP!89¿ $êÂÈ
+endobj
+154 0 obj
+113
+endobj
+155 0 obj<</Type/Page/Parent 142 0 R/Contents 156 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 5 0 R/F8 8 0 R/F9 9 0 R/Fc 10 0 R>>>>>>endobj
+156 0 obj<</Length 157 0 R/Filter/FlateDecode>>stream
+xÚ­–Ks¢@Çï~Š>f«”ºø¸I¶b…’Ó^Fht¶`Æ*ñÛoøܘªÅ¤<¨Ðóë÷þ4\hÓÇ…žâ¼q5®§ðÚ¥tÇïu!J®<˜cŠ
+EŒú[ô»4q{•IËw¯4r\?!Ld\ä( 3\ŠÊ¼ ®»5÷|§dF+„Tf™|åbYLŽ× v^XlÀ¬èÒÎfXã-ØëS{ö/ÏïÁ©ç+ë¡5ž-×q‡•¿±)_ªò7c‚-Ѳ!Ș8åSàn§ÊóCøÃdr7Ú` JYŒ0AÍ—Â~ÅŠ¯µ©å"8Žÿ!_g‡zÉèv}f8šY¦ó¶ \¦æ•)„Q’sÁµ¡úH¥m}
+–]À?-ËŽþåƒÙ9t äR±<·ƒuqÔáü:ĸPÜlh!ÖR™ ¸QpŽ¡¾tìÂçú÷¬ñ3m{™œÇ¾ÕÎÛ»Îí4¡U-c·íô+]ðàɬPí…Aÿ§()Z' Áð•ª0Jä‚Fj–ÉÐìp»±ÇÙÁ”gS©rf \cÌS—;×Ü£ëtœZ5~çø‘‰eAtÚ&D+®¸M¸uVOR0„rL$û¤B4ð´ÆJðt]Þu9··"–‰íŽGŠ ]{ö,+`¥âÚA½+x‚—qf2Á¬ (Äœ ÃãzÉͧcúÙ4á‘ <Ì
+x &¥gêØ]¿EžïúM¸ßÐ|3ÕôѲjµà>ŠÊ©¸„NݲÇaTPUì•U Ó<.˜ð¥UêQLoBú»ŠÙß2·*èµ=çì;T·ßvÝêØÿ·Qãgã/4s¯6endstream
+endobj
+157 0 obj
+706
+endobj
+158 0 obj<</Type/Page/Parent 142 0 R/Contents 159 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F8 8 0 R>>>>>>endobj
+159 0 obj<</Length 160 0 R/Filter/FlateDecode>>stream
+xÚ+ä2T0
+áÒw³P04TIS045×3V072PIÑp VÎO+)O,JUpI-ÎLÏQÉE™%™ùyš!Y\ººP-& 1#C=30ßH!(5-µ(5/9µ$åÂÈ
+endobj
+160 0 obj
+118
+endobj
+161 0 obj<</Type/Page/Parent 142 0 R/Contents 162 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
+162 0 obj<</Length 163 0 R/Filter/FlateDecode>>stream
+xÚµVMoÛ8½ûW rr€Hþ\;Éžšd»ð¢hÓÔéÉZÙl$Q!©ù÷!%GR·n·ð…"çë=>Îø±7‚!ýF0ÃdQÖ»Zöo/`<„eB'³ù–q7hä&‡O¨Ÿ$îN—_Èn
+£‘· ƳÐY^ßß~i RY¡ Æ ˜ƒQ‰Ý `Êu`žÅÌ€Ý
+ ª@-,‚U´[Ô´
+endobj
+163 0 obj
+938
+endobj
+164 0 obj<</Type/Page/Parent 142 0 R/Contents 165 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
+165 0 obj<</Length 166 0 R/Filter/FlateDecode>>stream
+xÚµVQoÛ8 ~ϯ òÔa³;i’8 ·Ý ··íš>u{mzÑbK®$'Ë¿?R²³ÔëÒØ¡EŒH$?ò#ù9÷£¦ô—ÀlÁÿy=z³MÞ]A2‡uI7‹U§°..fqÏÁ¢‘¢z±þJFsH’`¥ º#£õ;ÈD¾EUÐWUXp|Ñ`.K‰|Õ8 *“ic¤rh ×JaîÈ`'ÅÀ¥‹Úhã ÀÌ1BãL?™…$oÿ}Ò‚.½w©M}r§J%F©Ï4Ä»žP¬I)+|­'µº{ž/ã8þü°ÌÈ;Ÿ,-äBA† Ô*¨3©ŸO¢ªô^ª/]&y.]QÊÇxŸÒÅúTƒÁE&Úâp–ögF8|˜ÖDpƒ.PÌ.À6\»?é¸zˆ}ÂØ£ÀÒÙ!ð(àê ´jëŒzHU à¿†Ú#Ýaˆ‹;T?Aå+^o0ßÉ¿RÅOéæÿ
+‹Ávfø÷:} Ta
+ZšÖ¸uF+ù­ëW~a™}ד ç§Dw×,O-p¥h+D¥›_¼J¦Óï/;êæÓxøMá š-Vx€·T-•`Ï}´Î;kfk' ’ °²n*ìo¢J*ÏA)rdîßÞ~¸á§m³Z:ÏÉ$ñÕÉ&÷(,Ú Ñï×Ñ¡^ ¡!]ÊdÅã´—nøMZÇ­´ºt{ao#¼ç†[“kö%hê¤ïÀu qz:„iœ@Õäg‰¡û>VIËXÆú*EQKE ’ši:¢<ü„ uµö2î[l±ŸE"Ps$ÅSàÙµmÃ`Êõ±Êçêöqwï[jTébžÔù,¹8Fêê$õÙ09'¥ÓRôËâià) Áñ&ž„§D"¥¯Ucžè9ö'ÌyQà‰òø¬€žmÏdG9gǾ–Þ«Ê‘È/J ŒåøŒÔü±¿´ˆáÆvÜ¿_ÏOÙŒ‘ê§Ê¨Á`­w”I-¿ÉjÊg¸@“w«.@r¹¤À‹K¿ð¾²›~EþBKÕð#7Ò'È®Qðˆ–é”]|6Ÿq=ÓÇ~;ý½}ýÜÏ[endstream
+endobj
+166 0 obj
+890
+endobj
+167 0 obj<</Type/Page/Parent 142 0 R/Contents 168 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 5 0 R/F8 8 0 R/F9 9 0 R>>>>>>endobj
+168 0 obj<</Length 169 0 R/Filter/FlateDecode>>stream
+xÚ­Vßo›0~Ï_q­”°Xh_—þX¤UÊT¦½ôÅÀAÝb“(ÿýÎ6F»¤“¦H Øßïûîóµ¯æôñ!Z@°„´š|‰'Ÿî®Á ÎigEÞâì"ðXݯ/ãÚÁ÷Ýþl±ôB³?#¬dUI÷Lãž`-4Ö9Kž.(ô鶵,jV)ó°ãƒ=&³„)Ì@i¦¼Ò(ײMÙݾ)€ŽõoaŽ•9å²!j
+iÉ”Bz`"ƒ™(Ï•Kt妆 èéx~à¥?I¬Å äJ+[Û1^²¤Ä®„gÎgâ
+'Vö©I”’W\ÛTWÛ’”6çu)ÜaSˆDï­v§åX8ÉÎiaÓÿ)Äkƒ 5Ъàö¹
+Ž"Å)|ãÍÖú*™(VÐâÃúáv
+›Í»á5S”s¤FÞˆÔõ¹QÎZº+AÉ\ï©û§zÒTX ».Ý_ÑÇÃÉZ‚™•¹IL;¨‹Ä j$(=%õ Z³æ¼$’ÔéKZ¤ŽL¡@
+r—Fæ#ªo¼¥FøûúÑh
+È[tPqZú…mû47®²Ð£àÝ_”ã
+©N–˜jóháŽ
+qäJ5صÞkddL3ÈkYQÝ9ækxíi–¡ÃRÚyšX`»þ1^Ù‘xŸUÅ]‡¡‘\9v¤hÒ9i4v¤Jm)–®šc7bÕr…#»AjõË«¶rÿsDÔ–~èƃ½dí%ƒT¼æ'­¹½&xæbfÑb>ú¯"¼š{×`W#ó~O¾O~T®¹Qendstream
+endobj
+169 0 obj
+792
+endobj
+170 0 obj<</Type/Page/Parent 142 0 R/Contents 171 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 5 0 R/F8 8 0 R/F9 9 0 R/Fc 10 0 R>>>>>>endobj
+171 0 obj<</Length 172 0 R/Filter/FlateDecode>>stream
+xÚµVÛrÛ6}×Wì£<#Ò¦înŸ_ZuâF‰ä7½@ÀRBJ
+~u5+ Dk)˜ f *fœäuÁŒÇ@™³Q„ZhÎ
+Ê ¤Ð¥T>š#@\ƒÜ*®…ßÜô1Ý¥ð¸‚7«›Åb
+ºÉLló:V㯠ó£IH+¥©“UÈ}-‰Aëã ·¬©ãScaSªªv‡–>£û}ÀÆ€·é n2$úx8ܨûÎmeBqBæÃ(;™`º@Ç®@7r?îÍsÇÁÆQ¤þÛÐ Ó‰¸îÏ·äIšÁ¾ÚNWç;VkÔŠ±Ð\ý}™üöîrçŒÿ‚Ñ1u>õ˜R;ÿnîƒÕ7ÉÛ³©çMÈl2KG0]ù ah¬tî¾ø“i‡Âq’†ƒŒÉl\æ~o4¹¦:¸úÞWàݺ÷¡÷7¤‘66endstream
+endobj
+172 0 obj
+982
+endobj
+173 0 obj<</Type/Page/Parent 142 0 R/Contents 174 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 5 0 R/F8 8 0 R/F9 9 0 R>>>>>>endobj
+174 0 obj<</Length 175 0 R/Filter/FlateDecode>>stream
+xÚVÝnÛ< ½ÏSðrf·NÒ¤½Üº¿–¡ù@‘éD›-y’¼.{ú‘úI²´Ÿ; ¹°-‘âáá!•ï“
+.éWÁr
+³Ènòf=¹xÕÖ í,®«r
+ëúŬ¼*g :±Eo¬píËõW²CUEÛbº(çl»Þ៖Ш–Òèh½‹»¼Šô®½ÛÿV÷ŒkáE?)f³r‘Ì¡wÞônA4É¡•v!ro)ÚÂõ(U£$aê:¡k2´¦ƒÕêm‚EkУmŒíÈ+Á%O¥· Xü> Á­E®f‘§Í<(ÚxWà_0y4{DãÊ8/­ê=åe¶Vt¼¡t‘L½Ùõ£pŽfÎŽ¨bÆ,&è<6ž+XÝ~
+¼º¡ï '°1~G~­±¼~Ne+ä·‚Ö‹‡ò˜KçÆY‚ÇŸþYud£GœòÆ©2ã.ÓóËò:Æ]À½Üa=´ÏÓe+P$1h†¶ÝÍ ¥WF‹>®×«‹ª¬Mw«øNZ"l¤+á´JZtAd™W [áÒ »}5wF¥ÒÁÁí©"] wDëÌ¡rœêúac¨%խ؇5ÔÊ¢$ùí%éqXìL® P+ ±eÎâ&\ …† ‚Ÿë­Ð®µo÷±ó_k$qPŽTyA¤¼üÎXõK0s£œ‹SKè óÏô[t½ÑNmè“Ú<w{hð
+ÁU2â{"q*]P§¹qRƒÑì¦p‰Í+“ûÿ±pऔƒË™Þ ¹‹{Q–Ô‘-I‚iÎ2‹5¢Œ/(À©
+¸3á \AiEgꃥ^Žú=¨ÃèFm›˜¥Þ§gFô("õvØd„œCõ’%_ƒNõu¡Ir´ŠxÒL$ásÈÜ<üFÈÐ…$Ÿ> BJMG&ÓCsš¯¯~dˆdO‹¬7vPšpðK€–%Û¡®ië,p2 É1±Ü ÆáÁ7唎P“'8ÁŸ(Ÿ¥J“-O¥ÛwgAÓuãø*Ôˆ5÷´Ésp“HRFšP‡ùÎ ?°D´¢ñBË —ð³–ÇÎÅúU(×]EÓƒñ1c ›1½¤žë$ŒêjI’YÜ,£‚ÂÝtoÿÀ'¼E§¶šaƧYRDŸb9½ùó3¿¾,o XÜð÷»õäËä7´D
+endobj
+175 0 obj
+900
+endobj
+176 0 obj<</Type/Page/Parent 142 0 R/Contents 177 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F0 3 0 R/F4 5 0 R/F8 8 0 R/F9 9 0 R>>>>>>endobj
+177 0 obj<</Length 178 0 R/Filter/FlateDecode>>stream
+xÚVMs"7½ûWôÑ©†ûº&‰ÚT‘ï]h£ÍŒ4+i ä×çµ$0œ²«€Q¼×ýº5?nF4Ä߈ÆSþ—õÍçÕÍÏ¿>ÐhB« N¦÷£Á­ŠÛñ`:˜Ð£5½mÚšŸVßa;¡Ñ(Ùöïض«R‘<·¤Úm¥H{rÊ7Öx½ÆÏuø-
+m¶àóø²Xv7v¤ZTúŸlÌÉ‘r4NðDÓ8Û8-‚¢BA>¸V†é¢ûNT­òºMTÞÂÉ6>ÁðÊí´„÷Z¦:ÇÙI}02á‚gšÄ&(I¾c^
+N£LL2He}mAŒÚ[ð‰æÚ)¬;Ð2C¼Ú‡âd~b”{e
+Ÿ¡J­v8A ãrÔQ0¯N»ÃcAkgE!…ä­üK…}Uµ Ý’ä8)º¬„÷ÜTQ´ÁrD)ªê@¢(TAÁRÔAmœ­c* £¢S ª´GQQ1£Ü9õÿã,¨¤Î”?fìÑt8¾ÌÔXzTÚ½â#º:@ì×ú€UÝVA7,‡¢€|–"‡é¸Øï)=/WŒó#»á+dáGNý€àCJ)$‘Ò:ÖpuÐË×gž ]$²Ì8
+¬¶‰ ¦Ì¢·^åTþ<—¿Nóž¾Øí}½Ê´J6]²¯mè‘rκ„»Á!»ÄýÄV"ÄùÝ*£°¹ õ!µX–Š~ЊúCèëF ƒ+÷DÐ5÷mˆ
+endobj
+178 0 obj
+984
+endobj
+179 0 obj<</Type/Page/Parent 142 0 R/Contents 180 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 5 0 R/F8 8 0 R/F9 9 0 R>>>>>>endobj
+180 0 obj<</Length 181 0 R/Filter/FlateDecode>>stream
+xÚ­•MoÛ0 †ïù„O-xu>»c·¶X2ÄÝ.½(6¨“%WRZäß”â,m{†
+™M⬾_®Nc¾H¥`ƒ­ðÈ›°á'% w×!:Šb`âÐHwȽLáJ93 4+£„¥¥¤J†|a³L†o¢'+òsØdŸ€i¼4Ú…!tôtíÕäFÓˆ–ÝES¢¬¥î©lÐS%#6ØÂÛ8ø.àJ8‡.…ü4“+Nâ–Í›¯‡œ ùÆfõ>»œý÷l~° •Plj6†cãÛ*ì^WAOÍoc©¸Q·H+¡àðp¶\^‡f<œ¿‹Ëë´FÙÑj qþÃس#ÞNç!`cͳ,i˜ù¢0º’›yyFFÇ«BáÉËcIc ´
+endobj
+181 0 obj
+664
+endobj
+182 0 obj<</Type/Page/Parent 142 0 R/Contents 183 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F8 8 0 R>>>>>>endobj
+183 0 obj<</Length 184 0 R/Filter/FlateDecode>>stream
+xÚ+ä2T0
+áÒw³P04TIS045×3V072PIÑp VÎO+)O,JUpI-ÎLÏQÉE™%™ùyš!Y\ººP-†F A#s==@c=3199µ $åÂÈ
+endobj
+184 0 obj
+117
+endobj
+185 0 obj<</Type/Page/Parent 142 0 R/Contents 186 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 5 0 R/F6 7 0 R/F8 8 0 R/F9 9 0 R>>>>>>endobj
+186 0 obj<</Length 187 0 R/Filter/FlateDecode>>stream
+xÚ•ÁrÚ0†ï<ÅÛƒ Ò#q q‚:vfzòâ¨È’+‰Rúô• ¥0S+ÁãíúÛýW¿ÌN=÷ a܇ÁXÙyÈ:wÓ/ÐïA¶v‘ÑxYþi3¡Œ¡zÿ9ûÞ$„ãCB0
+»ý&¥B†º4‡Œ„aá¨AÔm(Ñ!4< …ƒãÓÀTYm-jT[Z`÷œ§WTS!P´S”9—( òŸõœZ
+¥ÒöJ'{Å-Pw奿 ¯Ð×½’ìè¾VQn%gÔr% 0WF¢€Ú¢»SPiUhZ_9ƒšÓ©»A“QlƒÖ§ÊµÛ¨’®}¥7-꺗žœ—‚a¯{pE&L+¹÷c’FIâi¤tã`nÃRKeNu‘ÊÖN}"ݽlúqß}êž
+ô¨Ž^IÚ^*r
+êõ%ùDsiëÁ¦{c±ô0ã4º#íИ¤Ë—ÿ4ß@m<äiæáN¹@È4•f5MYÅ”ðОH0›·óžp'ÐÚ€P¶©iZ½qf`~<’~4‰n`µ»aH«•¸¶ÄóJ<«Õ5ø$ΦíðÆ\î@, .ÑÒ#£fS¥™—KÈؓٮØÆ$]¾GlN'ÓXê‚Jþ» xØsòØΞ;姱>R,½¨E²ˆÛY‹­¨_P9§p‚rñ¯wÏ°×iÄmó-¶">ÅD›2Í+ûO7šfÁ?ÇtñpˆÞŸG_‹ÿ§ÃñÐ-÷ê@8¨â¬óµó÷¾¬endstream
+endobj
+187 0 obj
+573
+endobj
+188 0 obj<</Type/Page/Parent 142 0 R/Contents 189 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 5 0 R/F6 7 0 R/F8 8 0 R>>>>>>endobj
+189 0 obj<</Length 190 0 R/Filter/FlateDecode>>stream
+xÚUÁ‚0 †ï{Šõ0d WQñd‚a>
+endobj
+190 0 obj
+193
+endobj
+191 0 obj<</Type/Page/Parent 142 0 R/Contents 192 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 5 0 R/F5 6 0 R/F8 8 0 R/F9 9 0 R>>>>/Annots 59 0 R>>endobj
+192 0 obj<</Length 193 0 R/Filter/FlateDecode>>stream
+xÚÕ›ÛrÛ6†ïý¼l/ª`q$/;N=“LÝX}
+T3ƒJYÍoš/>®Új{Wo7‡vsØÿ<ÿtöf~ÆfÌ2Ü7pß>¼uA¥…šÕÕºªg_U7Ž~´y_½ºT€û{n7m7µ ªñ ¹®ü‚²H¹Ynw­qeáö?f“ÿ‚ÏÛž”ˆó™¶'®øLa€'þêR¢ŒöJj.lÆé8Q2ƒ¬í¾à´Ì‘˜GÔÕ­½êÝ]·\ºíÆA•±{LHWJ;ÖÌä ðDbÀf2(Æ)ÅS+LÁ éHºyÚÚõÈ@Nû[ù ‡‡²ÏGÚï_ÚþK×~u8 ³fŠ«F[5Qñ$ÅUclì„â‘Ìç„yD]l—k;L_æå ”Òª[
+ð·¸ ŒQÒÀœ²žqÑî»ûÍË lg{Põp„HÂtýŒ'›Iú"Æ;Á\ý¨ôÁ³.ÃdpYA» å†fNÄ4’^/–ŸÛÍí >=õ^–Îp+l˜÷ÀÆuù!Àµ1²`€V°1ɲ]ïËŽQ˜GX·Û9ˆÙ¤$(¡ŒñþÎÅ™P¦æå„â”PHB¡ræ¶ÚÝþˆBiéÛÁp.>È„Ò*´‚N(A …$àõ¥…y„íýbµjW'øDz&J&ÞÙø “I "IJ$ä
+óÛõÝæÐöi0~2È.÷ßà%>È\Š³ ‘¤<
+)~² „tà|Ãgo½
+RÈÒ•Sùn±lGfú3‚›05Ç<Ü
+TÈGØÕõ˜ÄÙÞ 7ˆ°ãñ«§k\ëTCI­M¬ƒ’œ¹‰$ÜÞË
+ó{·ØÜ?.îÇü"CX`­íu.€q$ž^ïšPY‰ô@‚Le5 gjÚ'rÜ¢,A˜÷¨÷WïߌFÚÙ?>¦¸<¹»À¢I•/™À’¥Ê§)‰‘àfi TÈGØõõŘc¤½-K»G’‚…DX©ò){hJh®³òÇuYþ¸IåÏPB# üN
+óû°ØÆÖcs€Ï0f*€~ó\ tWfb#Ú÷Þ²Eï¤Þ›ƒŒ5PÝwäÔn>½à`I—ÝÊ=œàLñTg ¨ÑÉXVÍ|N±š)²Ž$Î2
+æö°»_¶;/œPÓŽP j“ªQ’JP×Q#ªWY ¼Guk[ì£DRL`~€ÒLjTLBéc†UwE¶Ÿ‘þeœ…y„¡F½·:|ò¤C©!$ÒÙ:{2‰ô°Î®È¶1’ÂJVÂ<Âvû8†&´’EÉ$³Uöd2Éa•]‘_$m"T‰Â<ÂœLiì ïh$²öd •<›jÝ" lWeJæô9lv“±$J$ž-¯‡ ‰QMWä¸9]]‚0ïQ‡öïÃPÔ¦2£ÛŒ7LÀLZR Aj˜€ ‹ëšh˜"§ÑÇL×ñííã*ÞgS|YŠTLÞÈ›a‘]“­bÜ,jSpBI¿>¶}÷Ozé”T2B›:[¯ËEñzX×d‹„÷D]rBIça•é¹Û¼>:[ ×åR¸VÂ5Ù !üIôñ:x$¯ºv^b‘Óy4!RÙ¸*—ÀÕpoQÝBÜ”£*(!‹/œlîºûÇ~w0ê}“ì((߀–½5ýTÀ€ýi
+NÈ"é¢ëۥ퇞Ææ‚›<¨ÝëTÓÈ»q/ã§üƒáwW™’—gKä¼>ZœHžN΂áú8¸u^ÿge?öu}=½7:ƒBé¼ØðYÂ߶Í3XoÁÉz¿ðx³½;|]ôm…¯¢Û˾ÛÅ;JøGŽ_LXìP»?Îþ/Úi“endstream
+endobj
+193 0 obj
+2132
+endobj
+194 0 obj<</Type/Page/Parent 142 0 R/Contents 195 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 5 0 R/F5 6 0 R/F8 8 0 R/F9 9 0 R>>>>/Annots 76 0 R>>endobj
+195 0 obj<</Length 196 0 R/Filter/FlateDecode>>stream
+xÚÕ˜Ïo›0€ïù+|Ü¥þ‰áØuk¥i“º%ÛQÈ
+Eoo;ÁW`ëŒ'H<ež¢¸É̘ÔF•áZ:S’‚‰ Ét’.)8l9Û§a¬š×QÁlXÞ·
+‡-ê÷™¥=O„OLfÏóÂq‹¹îš¦h÷öã’Ò|Ë+º¤Ó|Þ]\¬hMý¢aÿ:ã{ãò9Ê{XQ–Õi´·¾Yàk¸õ‹XRé¼…¸ °¤´ßBL>KHBK!
+ÇVmYß—%)æMÄ%)ý&¢á^‰XBƒÜó%
+Ƕ¯ÓMÙÊH× )êHP[õ]6 æw ùE!‰qÓa,P8Ž°ªõŠÞÏJbÚÕk›ˆ KlªI*æ9Œ*SŸCŽ[Ôñd0kéBþÏAݶA`ˆÊ¹n§1GHb¶µX paÇS±o\϶’¦6âHæt®Ú.˜ÉœÍU;v:ò$ÓÔ¦KŽ#ìxÆb\[[ó¤õ\·]xÒÙ\·c$ObÜÔž
+ÇÖWUùŽ<™³ŒT ‹ ¬`n—7
+Ýæ]¶è^kÿ§ù¶EK[Êí±†¢²÷œù
+aEêEƒâüÁ@
+mk’°­»`R迹€#‚™+9WçºÎ
+endobj
+196 0 obj
+869
+endobj
+197 0 obj<</Count 5/First 198 0 R/Last 257 0 R>>endobj
+198 0 obj<</Parent 197 0 R/Title(Table of Contents)/Dest[191 0 R/XYZ null 756 null]/Next 199 0 R>>endobj
+199 0 obj<</Parent 197 0 R/Count -3/First 200 0 R/Last 202 0 R/Title(1 Scope)/Dest[149 0 R/XYZ null 746 null]/Prev 198 0 R/Next 203 0 R>>endobj
+200 0 obj<</Parent 199 0 R/Title(1.1 Identification)/Dest[149 0 R/XYZ null 694 null]/Next 201 0 R>>endobj
+201 0 obj<</Parent 199 0 R/Title(1.2 System Overview)/Dest[149 0 R/XYZ null 613 null]/Prev 200 0 R/Next 202 0 R>>endobj
+202 0 obj<</Parent 199 0 R/Title(1.3 Document Overview)/Dest[149 0 R/XYZ null 408 null]/Prev 201 0 R>>endobj
+203 0 obj<</Parent 197 0 R/Count -2/First 204 0 R/Last 205 0 R/Title(2 References)/Dest[155 0 R/XYZ null 746 null]/Prev 199 0 R/Next 206 0 R>>endobj
+204 0 obj<</Parent 203 0 R/Title(2.1 CUPS Documentation)/Dest[155 0 R/XYZ null 694 null]/Next 205 0 R>>endobj
+205 0 obj<</Parent 203 0 R/Title(2.2 Other Documents)/Dest[155 0 R/XYZ null 481 null]/Prev 204 0 R>>endobj
+206 0 obj<</Parent 197 0 R/Count -7/First 207 0 R/Last 248 0 R/Title(3 Design Overview)/Dest[161 0 R/XYZ null 746 null]/Prev 203 0 R/Next 257 0 R>>endobj
+207 0 obj<</Parent 206 0 R/Count -5/First 208 0 R/Last 212 0 R/Title(3.1 Backends)/Dest[161 0 R/XYZ null 582 null]/Next 213 0 R>>endobj
+208 0 obj<</Parent 207 0 R/Title(3.1.1 ipp)/Dest[161 0 R/XYZ null 323 null]/Next 209 0 R>>endobj
+209 0 obj<</Parent 207 0 R/Title(3.1.2 lpd)/Dest[161 0 R/XYZ null 250 null]/Prev 208 0 R/Next 210 0 R>>endobj
+210 0 obj<</Parent 207 0 R/Title(3.1.3 parallel)/Dest[161 0 R/XYZ null 153 null]/Prev 209 0 R/Next 211 0 R>>endobj
+211 0 obj<</Parent 207 0 R/Title(3.1.4 serial)/Dest[164 0 R/XYZ null 731 null]/Prev 210 0 R/Next 212 0 R>>endobj
+212 0 obj<</Parent 207 0 R/Title(3.1.5 socket)/Dest[164 0 R/XYZ null 545 null]/Prev 211 0 R>>endobj
+213 0 obj<</Parent 206 0 R/Count -3/First 214 0 R/Last 216 0 R/Title(3.2 Berkeley Commands)/Dest[164 0 R/XYZ null 429 null]/Prev 207 0 R/Next 217 0 R>>endobj
+214 0 obj<</Parent 213 0 R/Title(3.2.1 lpc)/Dest[164 0 R/XYZ null 341 null]/Next 215 0 R>>endobj
+215 0 obj<</Parent 213 0 R/Title(3.2.2 lpr)/Dest[164 0 R/XYZ null 228 null]/Prev 214 0 R/Next 216 0 R>>endobj
+216 0 obj<</Parent 213 0 R/Title(3.2.3 lprm)/Dest[164 0 R/XYZ null 155 null]/Prev 215 0 R>>endobj
+217 0 obj<</Parent 206 0 R/Count -3/First 218 0 R/Last 220 0 R/Title(3.3 CGI)/Dest[167 0 R/XYZ null 738 null]/Prev 213 0 R/Next 221 0 R>>endobj
+218 0 obj<</Parent 217 0 R/Title(3.3.1 classes.cgi)/Dest[167 0 R/XYZ null 671 null]/Next 219 0 R>>endobj
+219 0 obj<</Parent 217 0 R/Title(3.3.2 jobs.cgi)/Dest[167 0 R/XYZ null 598 null]/Prev 218 0 R/Next 220 0 R>>endobj
+220 0 obj<</Parent 217 0 R/Title(3.3.3 printers.cgi)/Dest[167 0 R/XYZ null 525 null]/Prev 219 0 R>>endobj
+221 0 obj<</Parent 206 0 R/Count -7/First 222 0 R/Last 228 0 R/Title(3.4 CUPS Interface Library)/Dest[167 0 R/XYZ null 459 null]/Prev 217 0 R/Next 229 0 R>>endobj
+222 0 obj<</Parent 221 0 R/Title(3.4.1 Convenience Functions)/Dest[167 0 R/XYZ null 371 null]/Next 223 0 R>>endobj
+223 0 obj<</Parent 221 0 R/Title(3.4.2 HTTP Functions)/Dest[167 0 R/XYZ null 285 null]/Prev 222 0 R/Next 224 0 R>>endobj
+224 0 obj<</Parent 221 0 R/Title(3.4.3 IPP Functions)/Dest[167 0 R/XYZ null 211 null]/Prev 223 0 R/Next 225 0 R>>endobj
+225 0 obj<</Parent 221 0 R/Title(3.4.4 Language Functions)/Dest[170 0 R/XYZ null 731 null]/Prev 224 0 R/Next 226 0 R>>endobj
+226 0 obj<</Parent 221 0 R/Title(3.4.5 MIME Functions)/Dest[170 0 R/XYZ null 675 null]/Prev 225 0 R/Next 227 0 R>>endobj
+227 0 obj<</Parent 221 0 R/Title(3.4.6 PPD Functions)/Dest[170 0 R/XYZ null 588 null]/Prev 226 0 R/Next 228 0 R>>endobj
+228 0 obj<</Parent 221 0 R/Title(3.4.7 Raster Functions)/Dest[170 0 R/XYZ null 515 null]/Prev 227 0 R>>endobj
+229 0 obj<</Parent 206 0 R/Count -7/First 230 0 R/Last 236 0 R/Title(3.5 Filters)/Dest[170 0 R/XYZ null 449 null]/Prev 221 0 R/Next 237 0 R>>endobj
+230 0 obj<</Parent 229 0 R/Title(3.5.1 hpgltops)/Dest[170 0 R/XYZ null 203 null]/Next 231 0 R>>endobj
+231 0 obj<</Parent 229 0 R/Title(3.5.2 imagetops)/Dest[170 0 R/XYZ null 143 null]/Prev 230 0 R/Next 232 0 R>>endobj
+232 0 obj<</Parent 229 0 R/Title(3.5.3 imagetoraster)/Dest[173 0 R/XYZ null 731 null]/Prev 231 0 R/Next 233 0 R>>endobj
+233 0 obj<</Parent 229 0 R/Title(3.5.4 pstops)/Dest[173 0 R/XYZ null 688 null]/Prev 232 0 R/Next 234 0 R>>endobj
+234 0 obj<</Parent 229 0 R/Title(3.5.5 pstoraster)/Dest[173 0 R/XYZ null 615 null]/Prev 233 0 R/Next 235 0 R>>endobj
+235 0 obj<</Parent 229 0 R/Title(3.5.6 rastertohp)/Dest[173 0 R/XYZ null 555 null]/Prev 234 0 R/Next 236 0 R>>endobj
+236 0 obj<</Parent 229 0 R/Title(3.5.7 texttops)/Dest[173 0 R/XYZ null 481 null]/Prev 235 0 R>>endobj
+237 0 obj<</Parent 206 0 R/Count -10/First 238 0 R/Last 247 0 R/Title(3.6 Scheduler)/Dest[173 0 R/XYZ null 429 null]/Prev 229 0 R/Next 248 0 R>>endobj
+238 0 obj<</Parent 237 0 R/Title(3.6.1 Authorization)/Dest[173 0 R/XYZ null 328 null]/Next 239 0 R>>endobj
+239 0 obj<</Parent 237 0 R/Title(3.6.2 Classes)/Dest[173 0 R/XYZ null 255 null]/Prev 238 0 R/Next 240 0 R>>endobj
+240 0 obj<</Parent 237 0 R/Title(3.6.3 Client)/Dest[173 0 R/XYZ null 181 null]/Prev 239 0 R/Next 241 0 R>>endobj
+241 0 obj<</Parent 237 0 R/Title(3.6.4 Configuration)/Dest[176 0 R/XYZ null 731 null]/Prev 240 0 R/Next 242 0 R>>endobj
+242 0 obj<</Parent 237 0 R/Title(3.6.5 Directory Services)/Dest[176 0 R/XYZ null 661 null]/Prev 241 0 R/Next 243 0 R>>endobj
+243 0 obj<</Parent 237 0 R/Title(3.6.6 IPP)/Dest[176 0 R/XYZ null 549 null]/Prev 242 0 R/Next 244 0 R>>endobj
+244 0 obj<</Parent 237 0 R/Title(3.6.7 Jobs)/Dest[176 0 R/XYZ null 462 null]/Prev 243 0 R/Next 245 0 R>>endobj
+245 0 obj<</Parent 237 0 R/Title(3.6.8 Logging)/Dest[176 0 R/XYZ null 389 null]/Prev 244 0 R/Next 246 0 R>>endobj
+246 0 obj<</Parent 237 0 R/Title(3.6.9 Main)/Dest[176 0 R/XYZ null 329 null]/Prev 245 0 R/Next 247 0 R>>endobj
+247 0 obj<</Parent 237 0 R/Title(3.6.10 Printers)/Dest[176 0 R/XYZ null 243 null]/Prev 246 0 R>>endobj
+248 0 obj<</Parent 206 0 R/Count -8/First 249 0 R/Last 256 0 R/Title(3.7 System V Commands)/Dest[176 0 R/XYZ null 177 null]/Prev 237 0 R>>endobj
+249 0 obj<</Parent 248 0 R/Title(3.7.1 accept)/Dest[179 0 R/XYZ null 731 null]/Next 250 0 R>>endobj
+250 0 obj<</Parent 248 0 R/Title(3.7.2 cancel)/Dest[179 0 R/XYZ null 688 null]/Prev 249 0 R/Next 251 0 R>>endobj
+251 0 obj<</Parent 248 0 R/Title(3.7.3 disable)/Dest[179 0 R/XYZ null 628 null]/Prev 250 0 R/Next 252 0 R>>endobj
+252 0 obj<</Parent 248 0 R/Title(3.7.4 enable)/Dest[179 0 R/XYZ null 568 null]/Prev 251 0 R/Next 253 0 R>>endobj
+253 0 obj<</Parent 248 0 R/Title(3.7.5 lp)/Dest[179 0 R/XYZ null 508 null]/Prev 252 0 R/Next 254 0 R>>endobj
+254 0 obj<</Parent 248 0 R/Title(3.7.6 lpadmin)/Dest[179 0 R/XYZ null 421 null]/Prev 253 0 R/Next 255 0 R>>endobj
+255 0 obj<</Parent 248 0 R/Title(3.7.7 lpstat)/Dest[179 0 R/XYZ null 322 null]/Prev 254 0 R/Next 256 0 R>>endobj
+256 0 obj<</Parent 248 0 R/Title(3.7.8 reject)/Dest[179 0 R/XYZ null 262 null]/Prev 255 0 R>>endobj
+257 0 obj<</Parent 197 0 R/Count -2/First 258 0 R/Last 259 0 R/Title(A Glossary)/Dest[185 0 R/XYZ null 746 null]/Prev 206 0 R>>endobj
+258 0 obj<</Parent 257 0 R/Title(A.1 Terms)/Dest[185 0 R/XYZ null 694 null]/Next 259 0 R>>endobj
+259 0 obj<</Parent 257 0 R/Title(A.2 Acronyms)/Dest[185 0 R/XYZ null 508 null]/Prev 258 0 R>>endobj
+260 0 obj<</Type/Catalog/Pages 142 0 R/Names 77 0 R/PageLayout/SinglePage/Outlines 197 0 R/OpenAction[149 0 R/XYZ null null null]/PageMode/UseOutlines/PageLabels<</Nums[0<</P(title)>>1<</P(eltit)>>2<</S/r>>4<</S/D>>]>>>>endobj
+xref
+0 261
+0000000000 65535 f
+0000000015 00000 n
+0000000225 00000 n
+0000001546 00000 n
+0000001620 00000 n
+0000001702 00000 n
+0000001780 00000 n
+0000001857 00000 n
+0000001936 00000 n
+0000002012 00000 n
+0000002093 00000 n
+0000002152 00000 n
+0000002204 00000 n
+0000002289 00000 n
+0000002313 00000 n
+0000002417 00000 n
+0000002522 00000 n
+0000002627 00000 n
+0000002732 00000 n
+0000002836 00000 n
+0000002941 00000 n
+0000003046 00000 n
+0000003150 00000 n
+0000003255 00000 n
+0000003360 00000 n
+0000003465 00000 n
+0000003570 00000 n
+0000003675 00000 n
+0000003780 00000 n
+0000003885 00000 n
+0000003990 00000 n
+0000004095 00000 n
+0000004200 00000 n
+0000004305 00000 n
+0000004410 00000 n
+0000004515 00000 n
+0000004620 00000 n
+0000004725 00000 n
+0000004830 00000 n
+0000004935 00000 n
+0000005040 00000 n
+0000005145 00000 n
+0000005250 00000 n
+0000005355 00000 n
+0000005460 00000 n
+0000005565 00000 n
+0000005670 00000 n
+0000005775 00000 n
+0000005880 00000 n
+0000005985 00000 n
+0000006090 00000 n
+0000006195 00000 n
+0000006300 00000 n
+0000006405 00000 n
+0000006510 00000 n
+0000006615 00000 n
+0000006720 00000 n
+0000006824 00000 n
+0000006927 00000 n
+0000007030 00000 n
+0000007362 00000 n
+0000007467 00000 n
+0000007572 00000 n
+0000007677 00000 n
+0000007782 00000 n
+0000007886 00000 n
+0000007991 00000 n
+0000008096 00000 n
+0000008201 00000 n
+0000008306 00000 n
+0000008411 00000 n
+0000008516 00000 n
+0000008621 00000 n
+0000008726 00000 n
+0000008829 00000 n
+0000008933 00000 n
+0000009037 00000 n
+0000009166 00000 n
+0000009198 00000 n
+0000009230 00000 n
+0000010089 00000 n
+0000010137 00000 n
+0000010185 00000 n
+0000010233 00000 n
+0000010281 00000 n
+0000010329 00000 n
+0000010377 00000 n
+0000010425 00000 n
+0000010473 00000 n
+0000010521 00000 n
+0000010569 00000 n
+0000010617 00000 n
+0000010665 00000 n
+0000010713 00000 n
+0000010761 00000 n
+0000010809 00000 n
+0000010857 00000 n
+0000010905 00000 n
+0000010953 00000 n
+0000011001 00000 n
+0000011049 00000 n
+0000011098 00000 n
+0000011147 00000 n
+0000011196 00000 n
+0000011245 00000 n
+0000011294 00000 n
+0000011343 00000 n
+0000011392 00000 n
+0000011441 00000 n
+0000011490 00000 n
+0000011539 00000 n
+0000011588 00000 n
+0000011637 00000 n
+0000011686 00000 n
+0000011735 00000 n
+0000011784 00000 n
+0000011833 00000 n
+0000011882 00000 n
+0000011931 00000 n
+0000011980 00000 n
+0000012029 00000 n
+0000012078 00000 n
+0000012127 00000 n
+0000012176 00000 n
+0000012225 00000 n
+0000012274 00000 n
+0000012323 00000 n
+0000012372 00000 n
+0000012421 00000 n
+0000012470 00000 n
+0000012519 00000 n
+0000012568 00000 n
+0000012617 00000 n
+0000012666 00000 n
+0000012715 00000 n
+0000012764 00000 n
+0000012813 00000 n
+0000012862 00000 n
+0000012911 00000 n
+0000012960 00000 n
+0000013009 00000 n
+0000013058 00000 n
+0000013107 00000 n
+0000013320 00000 n
+0000013472 00000 n
+0000019823 00000 n
+0000019845 00000 n
+0000019940 00000 n
+0000020042 00000 n
+0000020062 00000 n
+0000020217 00000 n
+0000021188 00000 n
+0000021209 00000 n
+0000021322 00000 n
+0000021506 00000 n
+0000021527 00000 n
+0000021668 00000 n
+0000022445 00000 n
+0000022466 00000 n
+0000022579 00000 n
+0000022768 00000 n
+0000022789 00000 n
+0000022939 00000 n
+0000023948 00000 n
+0000023969 00000 n
+0000024128 00000 n
+0000025089 00000 n
+0000025110 00000 n
+0000025241 00000 n
+0000026104 00000 n
+0000026125 00000 n
+0000026266 00000 n
+0000027319 00000 n
+0000027340 00000 n
+0000027471 00000 n
+0000028442 00000 n
+0000028463 00000 n
+0000028603 00000 n
+0000029658 00000 n
+0000029679 00000 n
+0000029810 00000 n
+0000030545 00000 n
+0000030566 00000 n
+0000030679 00000 n
+0000030867 00000 n
+0000030888 00000 n
+0000031028 00000 n
+0000031672 00000 n
+0000031693 00000 n
+0000031824 00000 n
+0000032088 00000 n
+0000032109 00000 n
+0000032263 00000 n
+0000034466 00000 n
+0000034488 00000 n
+0000034642 00000 n
+0000035582 00000 n
+0000035603 00000 n
+0000035658 00000 n
+0000035763 00000 n
+0000035907 00000 n
+0000036013 00000 n
+0000036133 00000 n
+0000036242 00000 n
+0000036391 00000 n
+0000036501 00000 n
+0000036608 00000 n
+0000036762 00000 n
+0000036898 00000 n
+0000036995 00000 n
+0000037105 00000 n
+0000037220 00000 n
+0000037333 00000 n
+0000037433 00000 n
+0000037591 00000 n
+0000037688 00000 n
+0000037798 00000 n
+0000037896 00000 n
+0000038040 00000 n
+0000038145 00000 n
+0000038260 00000 n
+0000038366 00000 n
+0000038529 00000 n
+0000038644 00000 n
+0000038765 00000 n
+0000038885 00000 n
+0000039010 00000 n
+0000039131 00000 n
+0000039251 00000 n
+0000039361 00000 n
+0000039509 00000 n
+0000039611 00000 n
+0000039727 00000 n
+0000039847 00000 n
+0000039960 00000 n
+0000040077 00000 n
+0000040194 00000 n
+0000040296 00000 n
+0000040447 00000 n
+0000040554 00000 n
+0000040668 00000 n
+0000040781 00000 n
+0000040901 00000 n
+0000041026 00000 n
+0000041136 00000 n
+0000041247 00000 n
+0000041361 00000 n
+0000041472 00000 n
+0000041575 00000 n
+0000041720 00000 n
+0000041820 00000 n
+0000041933 00000 n
+0000042047 00000 n
+0000042160 00000 n
+0000042269 00000 n
+0000042383 00000 n
+0000042496 00000 n
+0000042596 00000 n
+0000042730 00000 n
+0000042827 00000 n
+0000042927 00000 n
+trailer
+<</Size 261/Root 260 0 R/Info 1 0 R>>
+startxref
+43154
+%%EOF
diff --git a/doc/sdd.shtml b/doc/sdd.shtml
new file mode 100644
index 000000000..2ef7a7320
--- /dev/null
+++ b/doc/sdd.shtml
@@ -0,0 +1,435 @@
+<HTML>
+<HEAD>
+ <META NAME="COPYRIGHT" CONTENT="Copyright 1997-2000, All Rights Reserved">
+ <META NAME="DOCNUMBER" CONTENT="CUPS-SDD-1.1">
+ <META NAME="Author" CONTENT="Easy Software Products">
+ <TITLE>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.1.
+
+<EMBED SRC="system-overview.shtml">
+
+<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>A - Glossary
+
+</UL>
+
+<EMBED SRC="references.shtml">
+
+<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>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.
+
+<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>quit - Quits the lpc command.
+
+ <LI>status - Shows the status of printers and jobs in the queue.
+
+</UL>
+
+<H3>lpr</H3>
+
+The lpr command submits a job for printing. The CUPS version of lpr silently
+ignores the "i", "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.cgi</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.cgi</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.cgi</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, get a list of available
+classes, get the default printer or class, get the default server name, get
+the local username, and get a password string.
+
+<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>imagetoraster</H3>
+
+The imagetoraster filter converts image files into CUPS raster data.
+
+<H3>pstops</H3>
+
+The pstops filter inserts printer-specific commands from PPD files and
+performs page filtering as requested by the user.
+
+<H3>pstoraster</H3>
+
+The pstoraster filter converts PostScript program data into CUPS raster data.
+
+<H3>rastertohp</H3>
+
+The rastertohp filter handles converting CUPS raster data to HP PCL and
+supports both color and black-and-white printers.
+
+<H3>texttops</H3>
+
+The texttops filter converts text files into PostScript.
+
+<H2>Scheduler</H2>
+
+The scheduler is a fully-functional HTTP/1.1 and IPP/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>Logging</H3>
+
+The logging module manages the access, error, and page log files that are
+generated by the scheduler.
+
+<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>
+and <CODE>SIGCHLD</CODE> signals, reloads the server configuration files as
+needed, and handles child process errors and exits.
+
+<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 printers and 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), "F" (filter), and "E" (enable and accept) 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.
+
+<EMBED SRC="glossary.shtml">
+
+</BODY>
+</HTML>
diff --git a/doc/spm.html b/doc/spm.html
new file mode 100644
index 000000000..fcdfb032e
--- /dev/null
+++ b/doc/spm.html
@@ -0,0 +1,3709 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
+<HTML>
+<HEAD>
+<TITLE>DRAFT - CUPS Software Programmers Manual</TITLE>
+<META NAME="AUTHOR" CONTENT="Easy Software Products">
+<META NAME="COPYRIGHT" CONTENT="Copyright 1997-2000, All Rights Reserved">
+<META NAME="DOCNUMBER" CONTENT="CUPS-SPM-1.1">
+<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
+<STYLE>
+BODY { font-family: serif; font-size: 11.0pt }
+H1 { font-family: sans-serif; font-size: 20.0pt }
+H2 { font-family: sans-serif; font-size: 17.0pt }
+H3 { font-family: sans-serif; font-size: 14.0pt }
+H4 { font-family: sans-serif; font-size: 11.0pt }
+H5 { font-family: sans-serif; font-size: 9.0pt }
+H6 { font-family: sans-serif; font-size: 8.0pt }
+SUB { font-size: 8.0pt }
+SUP { font-size: 8.0pt }
+PRE { font-size: 9.0pt }
+A:link { text-decoration: underline }
+A:visited { text-decoration: underline }
+A:active { text-decoration: underline }
+</STYLE>
+</HEAD>
+<BODY>
+<CENTER><A HREF="#CONTENTS"><IMG SRC="images/cups-large.gif" BORDER="0"><BR>
+<H1>DRAFT - CUPS Software Programmers Manual</H1></A><BR>
+CUPS-SPM-1.1<BR>
+Easy Software Products<BR>
+Copyright 1997-2000, 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 - The CUPS API</A></B>
+<UL>
+<LI><A HREF="#3_1">The CUPS Library</A></LI>
+<UL>
+<LI><A HREF="#3_1_1">Detecting the CUPS Library in Autoconf</A></LI>
+</UL>
+<LI><A HREF="#3_2">Basic Services</A></LI>
+<UL>
+<LI><A HREF="#3_2_1">Include Files</A></LI>
+<LI><A HREF="#3_2_2">Getting the Available Printers and Classes</A></LI>
+<LI><A HREF="#3_2_3">Printing Files</A></LI>
+<LI><A HREF="#3_2_4">Setting Printer Options</A></LI>
+<LI><A HREF="#3_2_5">Cancelling Jobs</A></LI>
+</UL>
+<LI><A HREF="#3_3">HTTP Services</A></LI>
+<UL>
+<LI><A HREF="#3_3_1">Include Files</A></LI>
+<LI><A HREF="#3_3_2">Connecting to a Server</A></LI>
+<LI><A HREF="#3_3_3">Setting Request Fields</A></LI>
+<LI><A HREF="#3_3_4">Issuing a Request</A></LI>
+<LI><A HREF="#3_3_5">Getting the Request Status</A></LI>
+<LI><A HREF="#3_3_6">Sending Request Data</A></LI>
+<LI><A HREF="#3_3_7">Reading Request Data</A></LI>
+</UL>
+<LI><A HREF="#3_4">IPP Services</A></LI>
+<UL>
+<LI><A HREF="#3_4_1">Include Files</A></LI>
+<LI><A HREF="#3_4_2">Creating an IPP Request</A></LI>
+<LI><A HREF="#3_4_3">Adding Attributes</A></LI>
+<LI><A HREF="#3_4_4">Sending an IPP Request</A></LI>
+<LI><A HREF="#3_4_5">Reading an IPP Response</A></LI>
+<LI><A HREF="#3_4_6">Finding Attributes</A></LI>
+<LI><A HREF="#3_4_7">Looping Through Attributes</A></LI>
+<LI><A HREF="#3_4_8">IPP Standard Operations</A></LI>
+<LI><A HREF="#3_4_9">IPP Extension Operations</A></LI>
+<LI><A HREF="#3_4_10">CUPS Extension Operations</A></LI>
+</UL>
+<LI><A HREF="#3_5">Language Services</A></LI>
+<UL>
+<LI><A HREF="#3_5_1">Include Files</A></LI>
+<LI><A HREF="#3_5_2">Getting the Default Language</A></LI>
+<LI><A HREF="#3_5_3">Getting the Language Encoding</A></LI>
+<LI><A HREF="#3_5_4">Getting a Language String</A></LI>
+</UL>
+<LI><A HREF="#3_6">PPD Services</A></LI>
+<UL>
+<LI><A HREF="#3_6_1">Include Files</A></LI>
+<LI><A HREF="#3_6_2">Loading a PPD File</A></LI>
+<LI><A HREF="#3_6_3">Options and Groups</A></LI>
+<LI><A HREF="#3_6_4">Finding an Option</A></LI>
+<LI><A HREF="#3_6_5">Finding a Page Size</A></LI>
+<LI><A HREF="#3_6_6">Marking Options</A></LI>
+<LI><A HREF="#3_6_7">Checking for Conflicts</A></LI>
+<LI><A HREF="#3_6_8">Sending Options</A></LI>
+</UL>
+</UL>
+<B><A HREF="#4">3 - Writing Filters</A></B>
+<UL>
+<LI><A HREF="#4_1">Overview</A></LI>
+<UL>
+<LI><A HREF="#4_1_1">Security Considerations</A></LI>
+<LI><A HREF="#4_1_2">Temporary Files</A></LI>
+<LI><A HREF="#4_1_3">Page Accounting</A></LI>
+</UL>
+<LI><A HREF="#4_2">Command-Line Arguments</A></LI>
+<UL>
+<LI><A HREF="#4_2_1">Copy Generation</A></LI>
+</UL>
+<LI><A HREF="#4_3">Environment Variables</A></LI>
+<LI><A HREF="#4_4">Writing a HTML Filter</A></LI>
+</UL>
+<B><A HREF="#5">4 - Writing Printer Drivers</A></B>
+<UL>
+<LI><A HREF="#5_1">Overview</A></LI>
+<UL>
+<LI><A HREF="#5_1_1">Page Accounting</A></LI>
+<LI><A HREF="#5_1_2">Color Management</A></LI>
+</UL>
+<LI><A HREF="#5_2">Raster Functions</A></LI>
+<UL>
+<LI><A HREF="#5_2_1">cupsRasterOpen()</A></LI>
+<LI><A HREF="#5_2_2">cupsRasterReadHeader()</A></LI>
+<LI><A HREF="#5_2_3">cupsRasterReadPixels()</A></LI>
+<LI><A HREF="#5_2_4">cupsRasterClose()</A></LI>
+</UL>
+<LI><A HREF="#5_3">Writing a HP-PCL Driver</A></LI>
+</UL>
+<B><A HREF="#6">5 - Writing Backends</A></B>
+<UL>
+<LI><A HREF="#6_1">Overview</A></LI>
+<UL>
+<LI><A HREF="#6_1_1">Security Considerations</A></LI>
+<LI><A HREF="#6_1_2">Temporary Files</A></LI>
+<LI><A HREF="#6_1_3">Page Accounting</A></LI>
+<LI><A HREF="#6_1_4">Retries</A></LI>
+</UL>
+<LI><A HREF="#6_2">Command-Line Arguments</A></LI>
+<UL>
+<LI><A HREF="#6_2_1">Copy Generation</A></LI>
+</UL>
+<LI><A HREF="#6_3">Environment Variables</A></LI>
+<LI><A HREF="#6_4">Writing a Serial Port Backend</A></LI>
+</UL>
+<B><A HREF="#7">A - Constants</A></B>
+<UL>
+<LI><A HREF="#7_1">CUPS Constants</A></LI>
+<LI><A HREF="#7_2">HTTP Constants</A></LI>
+<LI><A HREF="#7_3">IPP Constants</A></LI>
+<LI><A HREF="#7_4">Language Constants</A></LI>
+<LI><A HREF="#7_5">PPD Constants</A></LI>
+<LI><A HREF="#7_6">Raster Constants</A></LI>
+</UL>
+<B><A HREF="#8">B - Structures</A></B>
+<UL>
+<LI><A HREF="#8_1"></LI>
+</UL>
+<B><A HREF="#9">C - Functions</A></B>
+<UL>
+<LI><A HREF="#cupsAddOption">cupsAddOption()</A></LI>
+<UL>
+<LI><A HREF="#9_1_1">Usage</A></LI>
+<LI><A HREF="#9_1_2">Arguments</A></LI>
+<LI><A HREF="#9_1_3">Returns</A></LI>
+<LI><A HREF="#9_1_4">Description</A></LI>
+<LI><A HREF="#9_1_5">Example</A></LI>
+<LI><A HREF="#9_1_6">See Also</A></LI>
+</UL>
+<LI><A HREF="#cupsCancelJob">cupsCancelJob()</A></LI>
+<UL>
+<LI><A HREF="#9_2_1">Usage</A></LI>
+<LI><A HREF="#9_2_2">Arguments</A></LI>
+<LI><A HREF="#9_2_3">Returns</A></LI>
+<LI><A HREF="#9_2_4">Description</A></LI>
+<LI><A HREF="#9_2_5">Example</A></LI>
+<LI><A HREF="#9_2_6">See Also</A></LI>
+</UL>
+<LI><A HREF="#cupsDoFileRequest">cupsDoFileRequest()</A></LI>
+<UL>
+<LI><A HREF="#9_3_1">Usage</A></LI>
+<LI><A HREF="#9_3_2">Arguments</A></LI>
+<LI><A HREF="#9_3_3">Returns</A></LI>
+<LI><A HREF="#9_3_4">Description</A></LI>
+<LI><A HREF="#9_3_5">Example</A></LI>
+<LI><A HREF="#9_3_6">See Also</A></LI>
+</UL>
+<LI><A HREF="#cupsDoRequest">cupsDoRequest()</A></LI>
+<UL>
+<LI><A HREF="#9_4_1">Usage</A></LI>
+<LI><A HREF="#9_4_2">Arguments</A></LI>
+<LI><A HREF="#9_4_3">Returns</A></LI>
+<LI><A HREF="#9_4_4">Description</A></LI>
+<LI><A HREF="#9_4_5">Example</A></LI>
+<LI><A HREF="#9_4_6">See Also</A></LI>
+</UL>
+<LI><A HREF="#cupsFreeOptions">cupsFreeOptions()</A></LI>
+<UL>
+<LI><A HREF="#9_5_1">Usage</A></LI>
+<LI><A HREF="#9_5_2">Arguments</A></LI>
+<LI><A HREF="#9_5_3">Description</A></LI>
+<LI><A HREF="#9_5_4">Example</A></LI>
+<LI><A HREF="#9_5_5">See Also</A></LI>
+</UL>
+<LI><A HREF="#cupsGetClasses">cupsGetClasses()</A></LI>
+<UL>
+<LI><A HREF="#9_6_1">Usage</A></LI>
+<LI><A HREF="#9_6_2">Arguments</A></LI>
+<LI><A HREF="#9_6_3">Returns</A></LI>
+<LI><A HREF="#9_6_4">Description</A></LI>
+<LI><A HREF="#9_6_5">Example</A></LI>
+<LI><A HREF="#9_6_6">See Also</A></LI>
+</UL>
+<LI><A HREF="#cupsGetDefault">cupsGetDefault()</A></LI>
+<UL>
+<LI><A HREF="#9_7_1">Usage</A></LI>
+<LI><A HREF="#9_7_2">Returns</A></LI>
+<LI><A HREF="#9_7_3">Description</A></LI>
+<LI><A HREF="#9_7_4">Example</A></LI>
+<LI><A HREF="#9_7_5">See Also</A></LI>
+</UL>
+<LI><A HREF="#cupsGetOption">cupsGetOption()</A></LI>
+<UL>
+<LI><A HREF="#9_8_1">Usage</A></LI>
+<LI><A HREF="#9_8_2">Arguments</A></LI>
+<LI><A HREF="#9_8_3">Returns</A></LI>
+<LI><A HREF="#9_8_4">Description</A></LI>
+<LI><A HREF="#9_8_5">See Also</A></LI>
+</UL>
+<LI><A HREF="#cupsGetPassword">cupsGetPassword()</A></LI>
+<UL>
+<LI><A HREF="#9_9_1">Usage</A></LI>
+<LI><A HREF="#9_9_2">Arguments</A></LI>
+<LI><A HREF="#9_9_3">Returns</A></LI>
+<LI><A HREF="#9_9_4">Description</A></LI>
+<LI><A HREF="#9_9_5">Example</A></LI>
+<LI><A HREF="#9_9_6">See Also</A></LI>
+</UL>
+<LI><A HREF="#cupsGetPPD">cupsGetPPD()</A></LI>
+<UL>
+<LI><A HREF="#9_10_1">Usage</A></LI>
+<LI><A HREF="#9_10_2">Arguments</A></LI>
+<LI><A HREF="#9_10_3">Returns</A></LI>
+<LI><A HREF="#9_10_4">Description</A></LI>
+<LI><A HREF="#9_10_5">Example</A></LI>
+</UL>
+<LI><A HREF="#cupsGetPrinters">cupsGetPrinters()</A></LI>
+<UL>
+<LI><A HREF="#9_11_1">Usage</A></LI>
+<LI><A HREF="#9_11_2">Arguments</A></LI>
+<LI><A HREF="#9_11_3">Returns</A></LI>
+<LI><A HREF="#9_11_4">Description</A></LI>
+<LI><A HREF="#9_11_5">Example</A></LI>
+<LI><A HREF="#9_11_6">See Also</A></LI>
+</UL>
+<LI><A HREF="#cupsLangDefault">cupsLangDefault()</A></LI>
+<UL>
+<LI><A HREF="#9_12_1">Usage</A></LI>
+<LI><A HREF="#9_12_2">Returns</A></LI>
+<LI><A HREF="#9_12_3">Description</A></LI>
+<LI><A HREF="#9_12_4">Example</A></LI>
+<LI><A HREF="#9_12_5">See Also</A></LI>
+</UL>
+<LI><A HREF="#cupsLangEncoding">cupsLangEncoding()</A></LI>
+<UL>
+<LI><A HREF="#9_13_1">Usage</A></LI>
+<LI><A HREF="#9_13_2">Arguments</A></LI>
+<LI><A HREF="#9_13_3">Returns</A></LI>
+<LI><A HREF="#9_13_4">Description</A></LI>
+<LI><A HREF="#9_13_5">Example</A></LI>
+<LI><A HREF="#9_13_6">See Also</A></LI>
+</UL>
+<LI><A HREF="#cupsLangFlush">cupsLangFlush()</A></LI>
+<UL>
+<LI><A HREF="#9_14_1">Usage</A></LI>
+<LI><A HREF="#9_14_2">Description</A></LI>
+<LI><A HREF="#9_14_3">Example</A></LI>
+<LI><A HREF="#9_14_4">See Also</A></LI>
+</UL>
+<LI><A HREF="#cupsLangFree">cupsLangFree()</A></LI>
+<UL>
+<LI><A HREF="#9_15_1">Usage</A></LI>
+<LI><A HREF="#9_15_2">Arguments</A></LI>
+<LI><A HREF="#9_15_3">Description</A></LI>
+<LI><A HREF="#9_15_4">Example</A></LI>
+<LI><A HREF="#9_15_5">See Also</A></LI>
+</UL>
+<LI><A HREF="#cupsLangGet">cupsLangGet()</A></LI>
+<UL>
+<LI><A HREF="#9_16_1">Usage</A></LI>
+<LI><A HREF="#9_16_2">Arguments</A></LI>
+<LI><A HREF="#9_16_3">Returns</A></LI>
+<LI><A HREF="#9_16_4">Description</A></LI>
+<LI><A HREF="#9_16_5">Example</A></LI>
+<LI><A HREF="#9_16_6">See Also</A></LI>
+</UL>
+<LI><A HREF="#cupsLangString">cupsLangString()</A></LI>
+<UL>
+<LI><A HREF="#9_17_1">Usage</A></LI>
+<LI><A HREF="#9_17_2">Arguments</A></LI>
+<LI><A HREF="#9_17_3">Returns</A></LI>
+<LI><A HREF="#9_17_4">Description</A></LI>
+<LI><A HREF="#9_17_5">Example</A></LI>
+<LI><A HREF="#9_17_6">See Also</A></LI>
+</UL>
+<LI><A HREF="#cupsLastError">cupsLastError()</A></LI>
+<UL>
+<LI><A HREF="#9_18_1">Usage</A></LI>
+<LI><A HREF="#9_18_2">Returns</A></LI>
+<LI><A HREF="#9_18_3">Description</A></LI>
+<LI><A HREF="#9_18_4">Example</A></LI>
+<LI><A HREF="#9_18_5">See Also</A></LI>
+</UL>
+<LI><A HREF="#cupsMarkOptions">cupsMarkOptions()</A></LI>
+<UL>
+<LI><A HREF="#9_19_1">Usage</A></LI>
+<LI><A HREF="#9_19_2">Arguments</A></LI>
+<LI><A HREF="#9_19_3">Returns</A></LI>
+<LI><A HREF="#9_19_4">Description</A></LI>
+<LI><A HREF="#9_19_5">Example</A></LI>
+<LI><A HREF="#9_19_6">See Also</A></LI>
+</UL>
+<LI><A HREF="#cupsParseOptions">cupsParseOptions()</A></LI>
+<UL>
+<LI><A HREF="#9_20_1">Usage</A></LI>
+<LI><A HREF="#9_20_2">Arguments</A></LI>
+<LI><A HREF="#9_20_3">Returns</A></LI>
+<LI><A HREF="#9_20_4">Description</A></LI>
+<LI><A HREF="#9_20_5">Example</A></LI>
+<LI><A HREF="#9_20_6">See Also</A></LI>
+</UL>
+<LI><A HREF="#cupsPrintFile">cupsPrintFile()</A></LI>
+<UL>
+<LI><A HREF="#9_21_1">Usage</A></LI>
+<LI><A HREF="#9_21_2">Arguments</A></LI>
+<LI><A HREF="#9_21_3">Returns</A></LI>
+<LI><A HREF="#9_21_4">Description</A></LI>
+<LI><A HREF="#9_21_5">Example</A></LI>
+<LI><A HREF="#9_21_6">See Also</A></LI>
+</UL>
+<LI><A HREF="#cupsRasterClose">cupsRasterClose()</A></LI>
+<UL>
+<LI><A HREF="#9_22_1">Usage</A></LI>
+<LI><A HREF="#9_22_2">Arguments</A></LI>
+<LI><A HREF="#9_22_3">Description</A></LI>
+<LI><A HREF="#9_22_4">Example</A></LI>
+<LI><A HREF="#9_22_5">See Also</A></LI>
+</UL>
+<LI><A HREF="#cupsRasterOpen">cupsRasterOpen()</A></LI>
+<UL>
+<LI><A HREF="#9_23_1">Usage</A></LI>
+<LI><A HREF="#9_23_2">Arguments</A></LI>
+<LI><A HREF="#9_23_3">Returns</A></LI>
+<LI><A HREF="#9_23_4">Description</A></LI>
+<LI><A HREF="#9_23_5">Example</A></LI>
+<LI><A HREF="#9_23_6">See Also</A></LI>
+</UL>
+<LI><A HREF="#cupsRasterReadHeader">cupsRasterReadHeader()</A></LI>
+<UL>
+<LI><A HREF="#9_24_1">Usage</A></LI>
+<LI><A HREF="#9_24_2">Arguments</A></LI>
+<LI><A HREF="#9_24_3">Returns</A></LI>
+<LI><A HREF="#9_24_4">Description</A></LI>
+<LI><A HREF="#9_24_5">Example</A></LI>
+<LI><A HREF="#9_24_6">See Also</A></LI>
+</UL>
+<LI><A HREF="#cupsRasterReadPixels">cupsRasterReadPixels()</A></LI>
+<UL>
+<LI><A HREF="#9_25_1">Usage</A></LI>
+<LI><A HREF="#9_25_2">Arguments</A></LI>
+<LI><A HREF="#9_25_3">Returns</A></LI>
+<LI><A HREF="#9_25_4">Description</A></LI>
+<LI><A HREF="#9_25_5">Example</A></LI>
+<LI><A HREF="#9_25_6">See Also</A></LI>
+</UL>
+<LI><A HREF="#cupsRasterWriteHeader">cupsRasterWriteHeader()</A></LI>
+<UL>
+<LI><A HREF="#9_26_1">Usage</A></LI>
+<LI><A HREF="#9_26_2">Arguments</A></LI>
+<LI><A HREF="#9_26_3">Returns</A></LI>
+<LI><A HREF="#9_26_4">Description</A></LI>
+<LI><A HREF="#9_26_5">Example</A></LI>
+<LI><A HREF="#9_26_6">See Also</A></LI>
+</UL>
+<LI><A HREF="#cupsRasterWritePixels">cupsRasterWritePixels()</A></LI>
+<UL>
+<LI><A HREF="#9_27_1">Usage</A></LI>
+<LI><A HREF="#9_27_2">Arguments</A></LI>
+<LI><A HREF="#9_27_3">Returns</A></LI>
+<LI><A HREF="#9_27_4">Description</A></LI>
+<LI><A HREF="#9_27_5">Example</A></LI>
+<LI><A HREF="#9_27_6">See Also</A></LI>
+</UL>
+<LI><A HREF="#cupsServer">cupsServer()</A></LI>
+<UL>
+<LI><A HREF="#9_28_1">Usage</A></LI>
+<LI><A HREF="#9_28_2">Returns</A></LI>
+<LI><A HREF="#9_28_3">Description</A></LI>
+<LI><A HREF="#9_28_4">Example</A></LI>
+<LI><A HREF="#9_28_5">See Also</A></LI>
+</UL>
+<LI><A HREF="#cupsTempFile">cupsTempFile()</A></LI>
+<UL>
+<LI><A HREF="#9_29_1">Usage</A></LI>
+<LI><A HREF="#9_29_2">Arguments</A></LI>
+<LI><A HREF="#9_29_3">Returns</A></LI>
+<LI><A HREF="#9_29_4">Description</A></LI>
+<LI><A HREF="#9_29_5">Example</A></LI>
+</UL>
+<LI><A HREF="#cupsUser">cupsUser()</A></LI>
+<UL>
+<LI><A HREF="#9_30_1">Usage</A></LI>
+<LI><A HREF="#9_30_2">Returns</A></LI>
+<LI><A HREF="#9_30_3">Description</A></LI>
+<LI><A HREF="#9_30_4">Example</A></LI>
+<LI><A HREF="#9_30_5">See Also</A></LI>
+</UL>
+<LI><A HREF="#httpBlocking">httpBlocking()</A></LI>
+<UL>
+<LI><A HREF="#9_31_1">Usage</A></LI>
+<LI><A HREF="#9_31_2">Arguments</A></LI>
+<LI><A HREF="#9_31_3">Returns</A></LI>
+<LI><A HREF="#9_31_4">Description</A></LI>
+<LI><A HREF="#9_31_5">Example</A></LI>
+<LI><A HREF="#9_31_6">See Also</A></LI>
+</UL>
+<LI><A HREF="#httpCheck">httpCheck()</A></LI>
+<UL>
+<LI><A HREF="#9_32_1">Usage</A></LI>
+<LI><A HREF="#9_32_2">Arguments</A></LI>
+<LI><A HREF="#9_32_3">Returns</A></LI>
+<LI><A HREF="#9_32_4">Description</A></LI>
+<LI><A HREF="#9_32_5">Example</A></LI>
+<LI><A HREF="#9_32_6">See Also</A></LI>
+</UL>
+<LI><A HREF="#httpClearFields">httpClearFields()</A></LI>
+<UL>
+<LI><A HREF="#9_33_1">Usage</A></LI>
+<LI><A HREF="#9_33_2">Arguments</A></LI>
+<LI><A HREF="#9_33_3">Returns</A></LI>
+<LI><A HREF="#9_33_4">Description</A></LI>
+<LI><A HREF="#9_33_5">Example</A></LI>
+<LI><A HREF="#9_33_6">See Also</A></LI>
+</UL>
+<LI><A HREF="#httpClose">httpClose()</A></LI>
+<UL>
+<LI><A HREF="#9_34_1">Usage</A></LI>
+<LI><A HREF="#9_34_2">Arguments</A></LI>
+<LI><A HREF="#9_34_3">Returns</A></LI>
+<LI><A HREF="#9_34_4">Description</A></LI>
+<LI><A HREF="#9_34_5">Example</A></LI>
+<LI><A HREF="#9_34_6">See Also</A></LI>
+</UL>
+<LI><A HREF="#httpConnect">httpConnect()</A></LI>
+<UL>
+<LI><A HREF="#9_35_1">Usage</A></LI>
+<LI><A HREF="#9_35_2">Arguments</A></LI>
+<LI><A HREF="#9_35_3">Returns</A></LI>
+<LI><A HREF="#9_35_4">Description</A></LI>
+<LI><A HREF="#9_35_5">Example</A></LI>
+<LI><A HREF="#9_35_6">See Also</A></LI>
+</UL>
+<LI><A HREF="#httpDecode64">httpDecode64()</A></LI>
+<UL>
+<LI><A HREF="#9_36_1">Usage</A></LI>
+<LI><A HREF="#9_36_2">Arguments</A></LI>
+<LI><A HREF="#9_36_3">Returns</A></LI>
+<LI><A HREF="#9_36_4">Description</A></LI>
+<LI><A HREF="#9_36_5">Example</A></LI>
+<LI><A HREF="#9_36_6">See Also</A></LI>
+</UL>
+<LI><A HREF="#httpDelete">httpDelete()</A></LI>
+<UL>
+<LI><A HREF="#9_37_1">Usage</A></LI>
+<LI><A HREF="#9_37_2">Arguments</A></LI>
+<LI><A HREF="#9_37_3">Returns</A></LI>
+<LI><A HREF="#9_37_4">Description</A></LI>
+<LI><A HREF="#9_37_5">Example</A></LI>
+<LI><A HREF="#9_37_6">See Also</A></LI>
+</UL>
+<LI><A HREF="#httpEncode64">httpEncode64()</A></LI>
+<UL>
+<LI><A HREF="#9_38_1">Usage</A></LI>
+<LI><A HREF="#9_38_2">Arguments</A></LI>
+<LI><A HREF="#9_38_3">Returns</A></LI>
+<LI><A HREF="#9_38_4">Description</A></LI>
+<LI><A HREF="#9_38_5">Example</A></LI>
+<LI><A HREF="#9_38_6">See Also</A></LI>
+</UL>
+<LI><A HREF="#httpError">httpError()</A></LI>
+<UL>
+<LI><A HREF="#9_39_1">Usage</A></LI>
+<LI><A HREF="#9_39_2">Arguments</A></LI>
+<LI><A HREF="#9_39_3">Returns</A></LI>
+<LI><A HREF="#9_39_4">Description</A></LI>
+<LI><A HREF="#9_39_5">Example</A></LI>
+<LI><A HREF="#9_39_6">See Also</A></LI>
+</UL>
+<LI><A HREF="#httpFlush">httpFlush()</A></LI>
+<UL>
+<LI><A HREF="#9_40_1">Usage</A></LI>
+<LI><A HREF="#9_40_2">Arguments</A></LI>
+<LI><A HREF="#9_40_3">Returns</A></LI>
+<LI><A HREF="#9_40_4">Description</A></LI>
+<LI><A HREF="#9_40_5">Example</A></LI>
+<LI><A HREF="#9_40_6">See Also</A></LI>
+</UL>
+<LI><A HREF="#httpGet">httpGet()</A></LI>
+<UL>
+<LI><A HREF="#9_41_1">Usage</A></LI>
+<LI><A HREF="#9_41_2">Arguments</A></LI>
+<LI><A HREF="#9_41_3">Returns</A></LI>
+<LI><A HREF="#9_41_4">Description</A></LI>
+<LI><A HREF="#9_41_5">Example</A></LI>
+<LI><A HREF="#9_41_6">See Also</A></LI>
+</UL>
+<LI><A HREF="#httpGets">httpGets()</A></LI>
+<UL>
+<LI><A HREF="#9_42_1">Usage</A></LI>
+<LI><A HREF="#9_42_2">Arguments</A></LI>
+<LI><A HREF="#9_42_3">Returns</A></LI>
+<LI><A HREF="#9_42_4">Description</A></LI>
+<LI><A HREF="#9_42_5">Example</A></LI>
+<LI><A HREF="#9_42_6">See Also</A></LI>
+</UL>
+<LI><A HREF="#httpGetDateString">httpGetDateString()</A></LI>
+<UL>
+<LI><A HREF="#9_43_1">Usage</A></LI>
+<LI><A HREF="#9_43_2">Arguments</A></LI>
+<LI><A HREF="#9_43_3">Returns</A></LI>
+<LI><A HREF="#9_43_4">Description</A></LI>
+<LI><A HREF="#9_43_5">Example</A></LI>
+<LI><A HREF="#9_43_6">See Also</A></LI>
+</UL>
+<LI><A HREF="#httpGetDateTime">httpGetDateTime()</A></LI>
+<UL>
+<LI><A HREF="#9_44_1">Usage</A></LI>
+<LI><A HREF="#9_44_2">Arguments</A></LI>
+<LI><A HREF="#9_44_3">Returns</A></LI>
+<LI><A HREF="#9_44_4">Description</A></LI>
+<LI><A HREF="#9_44_5">Example</A></LI>
+<LI><A HREF="#9_44_6">See Also</A></LI>
+</UL>
+<LI><A HREF="#httpGetField">httpGetField()</A></LI>
+<UL>
+<LI><A HREF="#9_45_1">Usage</A></LI>
+<LI><A HREF="#9_45_2">Arguments</A></LI>
+<LI><A HREF="#9_45_3">Returns</A></LI>
+<LI><A HREF="#9_45_4">Description</A></LI>
+<LI><A HREF="#9_45_5">Example</A></LI>
+<LI><A HREF="#9_45_6">See Also</A></LI>
+</UL>
+<LI><A HREF="#httpGetLength">httpGetLength()</A></LI>
+<UL>
+<LI><A HREF="#9_46_1">Usage</A></LI>
+<LI><A HREF="#9_46_2">Arguments</A></LI>
+<LI><A HREF="#9_46_3">Returns</A></LI>
+<LI><A HREF="#9_46_4">Description</A></LI>
+<LI><A HREF="#9_46_5">Example</A></LI>
+<LI><A HREF="#9_46_6">See Also</A></LI>
+</UL>
+<LI><A HREF="#httpHead">httpHead()</A></LI>
+<UL>
+<LI><A HREF="#9_47_1">Usage</A></LI>
+<LI><A HREF="#9_47_2">Arguments</A></LI>
+<LI><A HREF="#9_47_3">Returns</A></LI>
+<LI><A HREF="#9_47_4">Description</A></LI>
+<LI><A HREF="#9_47_5">Example</A></LI>
+<LI><A HREF="#9_47_6">See Also</A></LI>
+</UL>
+<LI><A HREF="#httpInitialize">httpInitialize()</A></LI>
+<UL>
+<LI><A HREF="#9_48_1">Usage</A></LI>
+<LI><A HREF="#9_48_2">Arguments</A></LI>
+<LI><A HREF="#9_48_3">Returns</A></LI>
+<LI><A HREF="#9_48_4">Description</A></LI>
+<LI><A HREF="#9_48_5">Example</A></LI>
+<LI><A HREF="#9_48_6">See Also</A></LI>
+</UL>
+<LI><A HREF="#httpOptions">httpOptions()</A></LI>
+<UL>
+<LI><A HREF="#9_49_1">Usage</A></LI>
+<LI><A HREF="#9_49_2">Arguments</A></LI>
+<LI><A HREF="#9_49_3">Returns</A></LI>
+<LI><A HREF="#9_49_4">Description</A></LI>
+<LI><A HREF="#9_49_5">Example</A></LI>
+<LI><A HREF="#9_49_6">See Also</A></LI>
+</UL>
+<LI><A HREF="#httpPost">httpPost()</A></LI>
+<UL>
+<LI><A HREF="#9_50_1">Usage</A></LI>
+<LI><A HREF="#9_50_2">Arguments</A></LI>
+<LI><A HREF="#9_50_3">Returns</A></LI>
+<LI><A HREF="#9_50_4">Description</A></LI>
+<LI><A HREF="#9_50_5">Example</A></LI>
+<LI><A HREF="#9_50_6">See Also</A></LI>
+</UL>
+<LI><A HREF="#httpPrintf">httpPrintf()</A></LI>
+<UL>
+<LI><A HREF="#9_51_1">Usage</A></LI>
+<LI><A HREF="#9_51_2">Arguments</A></LI>
+<LI><A HREF="#9_51_3">Returns</A></LI>
+<LI><A HREF="#9_51_4">Description</A></LI>
+<LI><A HREF="#9_51_5">Example</A></LI>
+<LI><A HREF="#9_51_6">See Also</A></LI>
+</UL>
+<LI><A HREF="#httpPut">httpPut()</A></LI>
+<UL>
+<LI><A HREF="#9_52_1">Usage</A></LI>
+<LI><A HREF="#9_52_2">Arguments</A></LI>
+<LI><A HREF="#9_52_3">Returns</A></LI>
+<LI><A HREF="#9_52_4">Description</A></LI>
+<LI><A HREF="#9_52_5">Example</A></LI>
+<LI><A HREF="#9_52_6">See Also</A></LI>
+</UL>
+<LI><A HREF="#httpRead">httpRead()</A></LI>
+<UL>
+<LI><A HREF="#9_53_1">Usage</A></LI>
+<LI><A HREF="#9_53_2">Arguments</A></LI>
+<LI><A HREF="#9_53_3">Returns</A></LI>
+<LI><A HREF="#9_53_4">Description</A></LI>
+<LI><A HREF="#9_53_5">Example</A></LI>
+<LI><A HREF="#9_53_6">See Also</A></LI>
+</UL>
+<LI><A HREF="#httpReconnect">httpReconnect()</A></LI>
+<UL>
+<LI><A HREF="#9_54_1">Usage</A></LI>
+<LI><A HREF="#9_54_2">Arguments</A></LI>
+<LI><A HREF="#9_54_3">Returns</A></LI>
+<LI><A HREF="#9_54_4">Description</A></LI>
+<LI><A HREF="#9_54_5">Example</A></LI>
+<LI><A HREF="#9_54_6">See Also</A></LI>
+</UL>
+<LI><A HREF="#httpSeparate">httpSeparate()</A></LI>
+<UL>
+<LI><A HREF="#9_55_1">Usage</A></LI>
+<LI><A HREF="#9_55_2">Arguments</A></LI>
+<LI><A HREF="#9_55_3">Returns</A></LI>
+<LI><A HREF="#9_55_4">Description</A></LI>
+<LI><A HREF="#9_55_5">Example</A></LI>
+<LI><A HREF="#9_55_6">See Also</A></LI>
+</UL>
+<LI><A HREF="#httpSetField">httpSetField()</A></LI>
+<UL>
+<LI><A HREF="#9_56_1">Usage</A></LI>
+<LI><A HREF="#9_56_2">Arguments</A></LI>
+<LI><A HREF="#9_56_3">Returns</A></LI>
+<LI><A HREF="#9_56_4">Description</A></LI>
+<LI><A HREF="#9_56_5">Example</A></LI>
+<LI><A HREF="#9_56_6">See Also</A></LI>
+</UL>
+<LI><A HREF="#httpTrace">httpTrace()</A></LI>
+<UL>
+<LI><A HREF="#9_57_1">Usage</A></LI>
+<LI><A HREF="#9_57_2">Arguments</A></LI>
+<LI><A HREF="#9_57_3">Returns</A></LI>
+<LI><A HREF="#9_57_4">Description</A></LI>
+<LI><A HREF="#9_57_5">Example</A></LI>
+<LI><A HREF="#9_57_6">See Also</A></LI>
+</UL>
+<LI><A HREF="#httpUpdate">httpUpdate()</A></LI>
+<UL>
+<LI><A HREF="#9_58_1">Usage</A></LI>
+<LI><A HREF="#9_58_2">Arguments</A></LI>
+<LI><A HREF="#9_58_3">Returns</A></LI>
+<LI><A HREF="#9_58_4">Description</A></LI>
+<LI><A HREF="#9_58_5">Example</A></LI>
+<LI><A HREF="#9_58_6">See Also</A></LI>
+</UL>
+<LI><A HREF="#httpWrite">httpWrite()</A></LI>
+<UL>
+<LI><A HREF="#9_59_1">Usage</A></LI>
+<LI><A HREF="#9_59_2">Arguments</A></LI>
+<LI><A HREF="#9_59_3">Returns</A></LI>
+<LI><A HREF="#9_59_4">Description</A></LI>
+<LI><A HREF="#9_59_5">Example</A></LI>
+<LI><A HREF="#9_59_6">See Also</A></LI>
+</UL>
+<LI><A HREF="#ippAddBoolean">ippAddBoolean()</A></LI>
+<UL>
+<LI><A HREF="#9_60_1">Usage</A></LI>
+<LI><A HREF="#9_60_2">Arguments</A></LI>
+<LI><A HREF="#9_60_3">Returns</A></LI>
+<LI><A HREF="#9_60_4">Description</A></LI>
+<LI><A HREF="#9_60_5">Example</A></LI>
+<LI><A HREF="#9_60_6">See Also</A></LI>
+</UL>
+<LI><A HREF="#ippAddBooleans">ippAddBooleans()</A></LI>
+<UL>
+<LI><A HREF="#9_61_1">Usage</A></LI>
+<LI><A HREF="#9_61_2">Arguments</A></LI>
+<LI><A HREF="#9_61_3">Returns</A></LI>
+<LI><A HREF="#9_61_4">Description</A></LI>
+<LI><A HREF="#9_61_5">Example</A></LI>
+<LI><A HREF="#9_61_6">See Also</A></LI>
+</UL>
+<LI><A HREF="#ippAddDate">ippAddDate()</A></LI>
+<UL>
+<LI><A HREF="#9_62_1">Usage</A></LI>
+<LI><A HREF="#9_62_2">Arguments</A></LI>
+<LI><A HREF="#9_62_3">Returns</A></LI>
+<LI><A HREF="#9_62_4">Description</A></LI>
+<LI><A HREF="#9_62_5">Example</A></LI>
+<LI><A HREF="#9_62_6">See Also</A></LI>
+</UL>
+<LI><A HREF="#ippAddInteger">ippAddInteger()</A></LI>
+<UL>
+<LI><A HREF="#9_63_1">Usage</A></LI>
+<LI><A HREF="#9_63_2">Arguments</A></LI>
+<LI><A HREF="#9_63_3">Returns</A></LI>
+<LI><A HREF="#9_63_4">Description</A></LI>
+<LI><A HREF="#9_63_5">Example</A></LI>
+<LI><A HREF="#9_63_6">See Also</A></LI>
+</UL>
+<LI><A HREF="#ippAddIntegers">ippAddIntegers()</A></LI>
+<UL>
+<LI><A HREF="#9_64_1">Usage</A></LI>
+<LI><A HREF="#9_64_2">Arguments</A></LI>
+<LI><A HREF="#9_64_3">Returns</A></LI>
+<LI><A HREF="#9_64_4">Description</A></LI>
+<LI><A HREF="#9_64_5">Example</A></LI>
+<LI><A HREF="#9_64_6">See Also</A></LI>
+</UL>
+<LI><A HREF="#ippAddRange">ippAddRange()</A></LI>
+<UL>
+<LI><A HREF="#9_65_1">Usage</A></LI>
+<LI><A HREF="#9_65_2">Arguments</A></LI>
+<LI><A HREF="#9_65_3">Returns</A></LI>
+<LI><A HREF="#9_65_4">Description</A></LI>
+<LI><A HREF="#9_65_5">Example</A></LI>
+<LI><A HREF="#9_65_6">See Also</A></LI>
+</UL>
+<LI><A HREF="#ippAddRanges">ippAddRanges()</A></LI>
+<UL>
+<LI><A HREF="#9_66_1">Usage</A></LI>
+<LI><A HREF="#9_66_2">Arguments</A></LI>
+<LI><A HREF="#9_66_3">Returns</A></LI>
+<LI><A HREF="#9_66_4">Description</A></LI>
+<LI><A HREF="#9_66_5">Example</A></LI>
+<LI><A HREF="#9_66_6">See Also</A></LI>
+</UL>
+<LI><A HREF="#ippAddResolution">ippAddResolution()</A></LI>
+<UL>
+<LI><A HREF="#9_67_1">Usage</A></LI>
+<LI><A HREF="#9_67_2">Arguments</A></LI>
+<LI><A HREF="#9_67_3">Returns</A></LI>
+<LI><A HREF="#9_67_4">Description</A></LI>
+<LI><A HREF="#9_67_5">Example</A></LI>
+<LI><A HREF="#9_67_6">See Also</A></LI>
+</UL>
+<LI><A HREF="#ippAddResolutions">ippAddResolutions()</A></LI>
+<UL>
+<LI><A HREF="#9_68_1">Usage</A></LI>
+<LI><A HREF="#9_68_2">Arguments</A></LI>
+<LI><A HREF="#9_68_3">Returns</A></LI>
+<LI><A HREF="#9_68_4">Description</A></LI>
+<LI><A HREF="#9_68_5">Example</A></LI>
+<LI><A HREF="#9_68_6">See Also</A></LI>
+</UL>
+<LI><A HREF="#ippAddSeparator">ippAddSeparator()</A></LI>
+<UL>
+<LI><A HREF="#9_69_1">Usage</A></LI>
+<LI><A HREF="#9_69_2">Arguments</A></LI>
+<LI><A HREF="#9_69_3">Returns</A></LI>
+<LI><A HREF="#9_69_4">Description</A></LI>
+<LI><A HREF="#9_69_5">Example</A></LI>
+<LI><A HREF="#9_69_6">See Also</A></LI>
+</UL>
+<LI><A HREF="#ippAddString">ippAddString()</A></LI>
+<UL>
+<LI><A HREF="#9_70_1">Usage</A></LI>
+<LI><A HREF="#9_70_2">Arguments</A></LI>
+<LI><A HREF="#9_70_3">Returns</A></LI>
+<LI><A HREF="#9_70_4">Description</A></LI>
+<LI><A HREF="#9_70_5">Example</A></LI>
+<LI><A HREF="#9_70_6">See Also</A></LI>
+</UL>
+<LI><A HREF="#ippAddStrings">ippAddStrings()</A></LI>
+<UL>
+<LI><A HREF="#9_71_1">Usage</A></LI>
+<LI><A HREF="#9_71_2">Arguments</A></LI>
+<LI><A HREF="#9_71_3">Returns</A></LI>
+<LI><A HREF="#9_71_4">Description</A></LI>
+<LI><A HREF="#9_71_5">Example</A></LI>
+<LI><A HREF="#9_71_6">See Also</A></LI>
+</UL>
+<LI><A HREF="#ippDateToTime">ippDateToTime()</A></LI>
+<UL>
+<LI><A HREF="#9_72_1">Usage</A></LI>
+<LI><A HREF="#9_72_2">Arguments</A></LI>
+<LI><A HREF="#9_72_3">Returns</A></LI>
+<LI><A HREF="#9_72_4">Description</A></LI>
+<LI><A HREF="#9_72_5">Example</A></LI>
+<LI><A HREF="#9_72_6">See Also</A></LI>
+</UL>
+<LI><A HREF="#ippDelete">ippDelete()</A></LI>
+<UL>
+<LI><A HREF="#9_73_1">Usage</A></LI>
+<LI><A HREF="#9_73_2">Arguments</A></LI>
+<LI><A HREF="#9_73_3">Returns</A></LI>
+<LI><A HREF="#9_73_4">Description</A></LI>
+<LI><A HREF="#9_73_5">Example</A></LI>
+<LI><A HREF="#9_73_6">See Also</A></LI>
+</UL>
+<LI><A HREF="#ippFindAttribute">ippFindAttribute()</A></LI>
+<UL>
+<LI><A HREF="#9_74_1">Usage</A></LI>
+<LI><A HREF="#9_74_2">Arguments</A></LI>
+<LI><A HREF="#9_74_3">Returns</A></LI>
+<LI><A HREF="#9_74_4">Description</A></LI>
+<LI><A HREF="#9_74_5">Example</A></LI>
+<LI><A HREF="#9_74_6">See Also</A></LI>
+</UL>
+<LI><A HREF="#ippLength">ippLength()</A></LI>
+<UL>
+<LI><A HREF="#9_75_1">Usage</A></LI>
+<LI><A HREF="#9_75_2">Arguments</A></LI>
+<LI><A HREF="#9_75_3">Returns</A></LI>
+<LI><A HREF="#9_75_4">Description</A></LI>
+<LI><A HREF="#9_75_5">Example</A></LI>
+<LI><A HREF="#9_75_6">See Also</A></LI>
+</UL>
+<LI><A HREF="#ippNew">ippNew()</A></LI>
+<UL>
+<LI><A HREF="#9_76_1">Usage</A></LI>
+<LI><A HREF="#9_76_2">Arguments</A></LI>
+<LI><A HREF="#9_76_3">Returns</A></LI>
+<LI><A HREF="#9_76_4">Description</A></LI>
+<LI><A HREF="#9_76_5">Example</A></LI>
+<LI><A HREF="#9_76_6">See Also</A></LI>
+</UL>
+<LI><A HREF="#ippPort">ippPort()</A></LI>
+<UL>
+<LI><A HREF="#9_77_1">Usage</A></LI>
+<LI><A HREF="#9_77_2">Arguments</A></LI>
+<LI><A HREF="#9_77_3">Returns</A></LI>
+<LI><A HREF="#9_77_4">Description</A></LI>
+<LI><A HREF="#9_77_5">Example</A></LI>
+<LI><A HREF="#9_77_6">See Also</A></LI>
+</UL>
+<LI><A HREF="#ippRead">ippRead()</A></LI>
+<UL>
+<LI><A HREF="#9_78_1">Usage</A></LI>
+<LI><A HREF="#9_78_2">Arguments</A></LI>
+<LI><A HREF="#9_78_3">Returns</A></LI>
+<LI><A HREF="#9_78_4">Description</A></LI>
+<LI><A HREF="#9_78_5">Example</A></LI>
+<LI><A HREF="#9_78_6">See Also</A></LI>
+</UL>
+<LI><A HREF="#ippTimeToDate">ippTimeToDate()</A></LI>
+<UL>
+<LI><A HREF="#9_79_1">Usage</A></LI>
+<LI><A HREF="#9_79_2">Arguments</A></LI>
+<LI><A HREF="#9_79_3">Returns</A></LI>
+<LI><A HREF="#9_79_4">Description</A></LI>
+<LI><A HREF="#9_79_5">Example</A></LI>
+<LI><A HREF="#9_79_6">See Also</A></LI>
+</UL>
+<LI><A HREF="#ippWrite">ippWrite()</A></LI>
+<UL>
+<LI><A HREF="#9_80_1">Usage</A></LI>
+<LI><A HREF="#9_80_2">Arguments</A></LI>
+<LI><A HREF="#9_80_3">Returns</A></LI>
+<LI><A HREF="#9_80_4">Description</A></LI>
+<LI><A HREF="#9_80_5">Example</A></LI>
+<LI><A HREF="#9_80_6">See Also</A></LI>
+</UL>
+<LI><A HREF="#ppdClose">ppdClose()</A></LI>
+<UL>
+<LI><A HREF="#9_81_1">Usage</A></LI>
+<LI><A HREF="#9_81_2">Arguments</A></LI>
+<LI><A HREF="#9_81_3">Returns</A></LI>
+<LI><A HREF="#9_81_4">Description</A></LI>
+<LI><A HREF="#9_81_5">Example</A></LI>
+<LI><A HREF="#9_81_6">See Also</A></LI>
+</UL>
+<LI><A HREF="#ppdConflicts">ppdConflicts()</A></LI>
+<UL>
+<LI><A HREF="#9_82_1">Usage</A></LI>
+<LI><A HREF="#9_82_2">Arguments</A></LI>
+<LI><A HREF="#9_82_3">Returns</A></LI>
+<LI><A HREF="#9_82_4">Description</A></LI>
+<LI><A HREF="#9_82_5">Example</A></LI>
+<LI><A HREF="#9_82_6">See Also</A></LI>
+</UL>
+<LI><A HREF="#pddEmitFd">pddEmitFd()</A></LI>
+<UL>
+<LI><A HREF="#9_83_1">Usage</A></LI>
+<LI><A HREF="#9_83_2">Arguments</A></LI>
+<LI><A HREF="#9_83_3">Returns</A></LI>
+<LI><A HREF="#9_83_4">Description</A></LI>
+<LI><A HREF="#9_83_5">Example</A></LI>
+<LI><A HREF="#9_83_6">See Also</A></LI>
+</UL>
+<LI><A HREF="#ppdEmit">ppdEmit()</A></LI>
+<UL>
+<LI><A HREF="#9_84_1">Usage</A></LI>
+<LI><A HREF="#9_84_2">Arguments</A></LI>
+<LI><A HREF="#9_84_3">Returns</A></LI>
+<LI><A HREF="#9_84_4">Description</A></LI>
+<LI><A HREF="#9_84_5">Example</A></LI>
+<LI><A HREF="#9_84_6">See Also</A></LI>
+</UL>
+<LI><A HREF="#ppdFindChoice">ppdFindChoice()</A></LI>
+<UL>
+<LI><A HREF="#9_85_1">Usage</A></LI>
+<LI><A HREF="#9_85_2">Arguments</A></LI>
+<LI><A HREF="#9_85_3">Returns</A></LI>
+<LI><A HREF="#9_85_4">Description</A></LI>
+<LI><A HREF="#9_85_5">Example</A></LI>
+<LI><A HREF="#9_85_6">See Also</A></LI>
+</UL>
+<LI><A HREF="#ppdFindMarkedChoice">ppdFindMarkedChoice()</A></LI>
+<UL>
+<LI><A HREF="#9_86_1">Usage</A></LI>
+<LI><A HREF="#9_86_2">Arguments</A></LI>
+<LI><A HREF="#9_86_3">Returns</A></LI>
+<LI><A HREF="#9_86_4">Description</A></LI>
+<LI><A HREF="#9_86_5">Example</A></LI>
+<LI><A HREF="#9_86_6">See Also</A></LI>
+</UL>
+<LI><A HREF="#ppdFindOption">ppdFindOption()</A></LI>
+<UL>
+<LI><A HREF="#9_87_1">Usage</A></LI>
+<LI><A HREF="#9_87_2">Arguments</A></LI>
+<LI><A HREF="#9_87_3">Returns</A></LI>
+<LI><A HREF="#9_87_4">Description</A></LI>
+<LI><A HREF="#9_87_5">Example</A></LI>
+<LI><A HREF="#9_87_6">See Also</A></LI>
+</UL>
+<LI><A HREF="#ppdIsMarked">ppdIsMarked()</A></LI>
+<UL>
+<LI><A HREF="#9_88_1">Usage</A></LI>
+<LI><A HREF="#9_88_2">Arguments</A></LI>
+<LI><A HREF="#9_88_3">Returns</A></LI>
+<LI><A HREF="#9_88_4">Description</A></LI>
+<LI><A HREF="#9_88_5">Example</A></LI>
+<LI><A HREF="#9_88_6">See Also</A></LI>
+</UL>
+<LI><A HREF="#ppdMarkDefaults">ppdMarkDefaults()</A></LI>
+<UL>
+<LI><A HREF="#9_89_1">Usage</A></LI>
+<LI><A HREF="#9_89_2">Arguments</A></LI>
+<LI><A HREF="#9_89_3">Returns</A></LI>
+<LI><A HREF="#9_89_4">Description</A></LI>
+<LI><A HREF="#9_89_5">Example</A></LI>
+<LI><A HREF="#9_89_6">See Also</A></LI>
+</UL>
+<LI><A HREF="#ppdMarkOption">ppdMarkOption()</A></LI>
+<UL>
+<LI><A HREF="#9_90_1">Usage</A></LI>
+<LI><A HREF="#9_90_2">Arguments</A></LI>
+<LI><A HREF="#9_90_3">Returns</A></LI>
+<LI><A HREF="#9_90_4">Description</A></LI>
+<LI><A HREF="#9_90_5">Example</A></LI>
+<LI><A HREF="#9_90_6">See Also</A></LI>
+</UL>
+<LI><A HREF="#ppdOpenFd">ppdOpenFd()</A></LI>
+<UL>
+<LI><A HREF="#9_91_1">Usage</A></LI>
+<LI><A HREF="#9_91_2">Arguments</A></LI>
+<LI><A HREF="#9_91_3">Returns</A></LI>
+<LI><A HREF="#9_91_4">Description</A></LI>
+<LI><A HREF="#9_91_5">Example</A></LI>
+<LI><A HREF="#9_91_6">See Also</A></LI>
+</UL>
+<LI><A HREF="#ppdOpenFile">ppdOpenFile()</A></LI>
+<UL>
+<LI><A HREF="#9_92_1">Usage</A></LI>
+<LI><A HREF="#9_92_2">Arguments</A></LI>
+<LI><A HREF="#9_92_3">Returns</A></LI>
+<LI><A HREF="#9_92_4">Description</A></LI>
+<LI><A HREF="#9_92_5">Example</A></LI>
+<LI><A HREF="#9_92_6">See Also</A></LI>
+</UL>
+<LI><A HREF="#ppdOpen">ppdOpen()</A></LI>
+<UL>
+<LI><A HREF="#9_93_1">Usage</A></LI>
+<LI><A HREF="#9_93_2">Arguments</A></LI>
+<LI><A HREF="#9_93_3">Returns</A></LI>
+<LI><A HREF="#9_93_4">Description</A></LI>
+<LI><A HREF="#9_93_5">Example</A></LI>
+<LI><A HREF="#9_93_6">See Also</A></LI>
+</UL>
+<LI><A HREF="#ppdPageLength">ppdPageLength()</A></LI>
+<UL>
+<LI><A HREF="#9_94_1">Usage</A></LI>
+<LI><A HREF="#9_94_2">Arguments</A></LI>
+<LI><A HREF="#9_94_3">Returns</A></LI>
+<LI><A HREF="#9_94_4">Description</A></LI>
+<LI><A HREF="#9_94_5">Example</A></LI>
+<LI><A HREF="#9_94_6">See Also</A></LI>
+</UL>
+<LI><A HREF="#ppdPageSize">ppdPageSize()</A></LI>
+<UL>
+<LI><A HREF="#9_95_1">Usage</A></LI>
+<LI><A HREF="#9_95_2">Arguments</A></LI>
+<LI><A HREF="#9_95_3">Returns</A></LI>
+<LI><A HREF="#9_95_4">Description</A></LI>
+<LI><A HREF="#9_95_5">Example</A></LI>
+<LI><A HREF="#9_95_6">See Also</A></LI>
+</UL>
+<LI><A HREF="#ppdPageWidth">ppdPageWidth()</A></LI>
+<UL>
+<LI><A HREF="#9_96_1">Usage</A></LI>
+<LI><A HREF="#9_96_2">Arguments</A></LI>
+<LI><A HREF="#9_96_3">Returns</A></LI>
+<LI><A HREF="#9_96_4">Description</A></LI>
+<LI><A HREF="#9_96_5">Example</A></LI>
+<LI><A HREF="#9_96_6">See Also</A></LI>
+</UL>
+</UL>
+<HR>
+<H1 ALIGN="RIGHT"><A NAME="1">Preface</A></H1>
+ This software programmers manual provides software programming
+information for the Common UNIX Printing System (&quot;CUPS&quot;) Version 1.1.
+<H2><A NAME="1_1">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 <A HREF="http://www.easysw.com">
+Easy Software Products</A> 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 5.50) and an image file RIP that
+is 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 - The CUPS API</LI>
+<LI>3 - Writing Filters</LI>
+<LI>4 - Writing Printer Drivers</LI>
+<LI>5 - Writing Backends</LI>
+<LI>A - Constants</LI>
+<LI>B - Structures</LI>
+<LI>C - Functions</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 - The CUPS API</A></H1>
+<P>This chapter describes the CUPS Application Programmers Interface
+(&quot;API&quot;). </P>
+<H2><A NAME="3_1">The CUPS Library</A></H2>
+<H3><A NAME="3_1_1">Detecting the CUPS Library in Autoconf</A></H3>
+<H2><A NAME="3_2">Basic Services</A></H2>
+<H3><A NAME="3_2_1">Include Files</A></H3>
+<H3><A NAME="3_2_2">Getting the Available Printers and Classes</A></H3>
+<H3><A NAME="3_2_3">Printing Files</A></H3>
+<H3><A NAME="3_2_4">Setting Printer Options</A></H3>
+<H3><A NAME="3_2_5">Cancelling Jobs</A></H3>
+<H2><A NAME="3_3">HTTP Services</A></H2>
+<H3><A NAME="3_3_1">Include Files</A></H3>
+<H3><A NAME="3_3_2">Connecting to a Server</A></H3>
+<H3><A NAME="3_3_3">Setting Request Fields</A></H3>
+<H3><A NAME="3_3_4">Issuing a Request</A></H3>
+<H3><A NAME="3_3_5">Getting the Request Status</A></H3>
+<H3><A NAME="3_3_6">Sending Request Data</A></H3>
+<H3><A NAME="3_3_7">Reading Request Data</A></H3>
+<H2><A NAME="3_4">IPP Services</A></H2>
+<H3><A NAME="3_4_1">Include Files</A></H3>
+<H3><A NAME="3_4_2">Creating an IPP Request</A></H3>
+<H3><A NAME="3_4_3">Adding Attributes</A></H3>
+<H3><A NAME="3_4_4">Sending an IPP Request</A></H3>
+<H3><A NAME="3_4_5">Reading an IPP Response</A></H3>
+<H3><A NAME="3_4_6">Finding Attributes</A></H3>
+<H3><A NAME="3_4_7">Looping Through Attributes</A></H3>
+<H3><A NAME="3_4_8">IPP Standard Operations</A></H3>
+<H3><A NAME="3_4_9">IPP Extension Operations</A></H3>
+<H3><A NAME="3_4_10">CUPS Extension Operations</A></H3>
+<H2><A NAME="3_5">Language Services</A></H2>
+<H3><A NAME="3_5_1">Include Files</A></H3>
+<H3><A NAME="3_5_2">Getting the Default Language</A></H3>
+<H3><A NAME="3_5_3">Getting the Language Encoding</A></H3>
+<H3><A NAME="3_5_4">Getting a Language String</A></H3>
+<H2><A NAME="3_6">PPD Services</A></H2>
+<H3><A NAME="3_6_1">Include Files</A></H3>
+<H3><A NAME="3_6_2">Loading a PPD File</A></H3>
+<H3><A NAME="3_6_3">Options and Groups</A></H3>
+<H3><A NAME="3_6_4">Finding an Option</A></H3>
+<H3><A NAME="3_6_5">Finding a Page Size</A></H3>
+<H3><A NAME="3_6_6">Marking Options</A></H3>
+<H3><A NAME="3_6_7">Checking for Conflicts</A></H3>
+<H3><A NAME="3_6_8">Sending Options</A></H3>
+<H1 ALIGN="RIGHT"><A NAME="4">3 - Writing Filters</A></H1>
+<P>This chapter describes how to write a file filter for CUPS. </P>
+<H2><A NAME="4_1">Overview</A></H2>
+<H3><A NAME="4_1_1">Security Considerations</A></H3>
+ Users and Groups
+<H3><A NAME="4_1_2">Temporary Files</A></H3>
+<H3><A NAME="4_1_3">Page Accounting</A></H3>
+<H2><A NAME="4_2">Command-Line Arguments</A></H2>
+<H3><A NAME="4_2_1">Copy Generation</A></H3>
+<H2><A NAME="4_3">Environment Variables</A></H2>
+<H2><A NAME="4_4">Writing a HTML Filter</A></H2>
+<H1 ALIGN="RIGHT"><A NAME="5">4 - Writing Printer Drivers</A></H1>
+<P>This chapter discusses how to write a printer driver, which is a
+special filter program that converts CUPS raster data into the
+appropriate commands and data required for a printer. </P>
+<H2><A NAME="5_1">Overview</A></H2>
+<H3><A NAME="5_1_1">Page Accounting</A></H3>
+<H3><A NAME="5_1_2">Color Management</A></H3>
+<H2><A NAME="5_2">Raster Functions</A></H2>
+<H3><A NAME="5_2_1">cupsRasterOpen()</A></H3>
+<H3><A NAME="5_2_2">cupsRasterReadHeader()</A></H3>
+<H3><A NAME="5_2_3">cupsRasterReadPixels()</A></H3>
+<H3><A NAME="5_2_4">cupsRasterClose()</A></H3>
+<H2><A NAME="5_3">Writing a HP-PCL Driver</A></H2>
+<H1 ALIGN="RIGHT"><A NAME="6">5 - Writing Backends</A></H1>
+<P>This chapter describes how to write a backend for CUPS. Backends
+communicate directly with printers and allow printer drivers and
+filters to send data using any type of connection transparently. </P>
+<H2><A NAME="6_1">Overview</A></H2>
+<H3><A NAME="6_1_1">Security Considerations</A></H3>
+ Users and Groups
+<H3><A NAME="6_1_2">Temporary Files</A></H3>
+<H3><A NAME="6_1_3">Page Accounting</A></H3>
+<H3><A NAME="6_1_4">Retries</A></H3>
+<H2><A NAME="6_2">Command-Line Arguments</A></H2>
+<H3><A NAME="6_2_1">Copy Generation</A></H3>
+<H2><A NAME="6_3">Environment Variables</A></H2>
+<H2><A NAME="6_4">Writing a Serial Port Backend</A></H2>
+<H1 ALIGN="RIGHT"><A NAME="7">A - Constants</A></H1>
+<P>This appendix lists all of the constants that are defined by the
+CUPS API. </P>
+<H2><A NAME="7_1">CUPS Constants</A></H2>
+<H2><A NAME="7_2">HTTP Constants</A></H2>
+<H2><A NAME="7_3">IPP Constants</A></H2>
+<H2><A NAME="7_4">Language Constants</A></H2>
+<H2><A NAME="7_5">PPD Constants</A></H2>
+<H2><A NAME="7_6">Raster Constants</A></H2>
+<H1 ALIGN="RIGHT"><A NAME="8">B - Structures</A></H1>
+<P>This appendix describes all of the structures that are defined by
+the CUPS API. </P>
+<H2><A NAME="8_1"></H2>
+<H1 ALIGN="RIGHT"><A NAME="9">C - Functions</A></H1>
+<P>This appendix provides a reference for all of the CUPS API
+functions.
+<!-- NEW PAGE ---->
+</P>
+<H2><A NAME="cupsAddOption">cupsAddOption()</A></H2>
+<H3><A NAME="9_1_1">Usage</A></H3>
+<PRE>
+int
+cupsAddOption(const char *name,
+ const char *value,
+ int num_options,
+ cups_option_t **options);
+</PRE>
+<H3><A NAME="9_1_2">Arguments</A></H3>
+<CENTER>
+<TABLE BORDER WIDTH="80%">
+<TR><TH>Argument</TH><TH>Description</TH></TR>
+<TR><TD>name</TD><TD>The name of the option.</TD></TR>
+<TR><TD>value</TD><TD>The value of the option.</TD></TR>
+<TR><TD>num_options</TD><TD>Number of options currently in the array.</TD>
+</TR>
+<TR><TD>options</TD><TD>Pointer to the options array.</TD></TR>
+</TABLE>
+</CENTER>
+<H3><A NAME="9_1_3">Returns</A></H3>
+<P>The new number of options. </P>
+<H3><A NAME="9_1_4">Description</A></H3>
+<P><CODE>cupsAddOption()</CODE> adds an option to the specified array. </P>
+<H3><A NAME="9_1_5">Example</A></H3>
+<PRE>
+#include &lt;cups.h&gt;
+
+...
+
+/* Declare the options array */
+int num_options;
+<A HREF="#cups_option_t">cups_option_t</A> *options;
+
+/* Initialize the options array */
+num_options = 0;
+options = (cups_option_t *)0;
+
+/* Add options using cupsAddOption() */
+num_options = cupsAddOption(&quot;media&quot;, &quot;letter&quot;, num_options, &amp;options);
+num_options = cupsAddOption(&quot;resolution&quot;, &quot;300dpi&quot;, num_options, &amp;options);
+</PRE>
+<H3><A NAME="9_1_6">See Also</A></H3>
+<A HREF="#cupsFreeOptions"><CODE>cupsFreeOptions()</CODE></A>, <A HREF="#cupsGetOption">
+<CODE>cupsGetOption()</CODE></A>, <A HREF="#cupsParseOptions"><CODE>
+cupsParseOptions()</CODE></A>
+<!-- NEW PAGE ---->
+
+<H2><A NAME="cupsCancelJob">cupsCancelJob()</A></H2>
+<H3><A NAME="9_2_1">Usage</A></H3>
+<PRE>
+int
+cupsCancelJob(const char *dest,
+ int job);
+</PRE>
+<H3><A NAME="9_2_2">Arguments</A></H3>
+<CENTER>
+<TABLE BORDER WIDTH="80%">
+<TR><TH>Argument</TH><TH>Description</TH></TR>
+<TR><TD>dest</TD><TD>Printer or class name</TD></TR>
+<TR><TD>job</TD><TD>Job ID</TD></TR>
+</TABLE>
+</CENTER>
+<H3><A NAME="9_2_3">Returns</A></H3>
+<P>1 on success, 0 on failure. On failure the error can be found by
+calling <A HREF="#cupsLastError"><CODE>cupsLastError()</CODE></A>. </P>
+<H3><A NAME="9_2_4">Description</A></H3>
+<P><CODE>cupsCancelJob()</CODE> cancels the specifies job. </P>
+<H3><A NAME="9_2_5">Example</A></H3>
+<PRE>
+#include &lt;cups.h&gt;
+
+cupsCancelJob(&quot;LaserJet&quot;, 1);
+</PRE>
+<H3><A NAME="9_2_6">See Also</A></H3>
+<P><A HREF="#cupsLastError"><CODE>cupsLastError()</CODE></A>, <A HREF="#cupsPrintFile">
+<CODE>cupsPrintFile()</CODE></A>
+<!-- NEW PAGE ---->
+</P>
+<H2><A NAME="cupsDoFileRequest">cupsDoFileRequest()</A></H2>
+<H3><A NAME="9_3_1">Usage</A></H3>
+<PRE>
+ipp_t *
+cupsDoFileRequest(http_t *http,
+ ipp_t *request,
+ const char *resource,
+ const char *filename);
+</PRE>
+<H3><A NAME="9_3_2">Arguments</A></H3>
+<CENTER>
+<TABLE BORDER WIDTH="80%">
+<TR><TH>Argument</TH><TH>Description</TH></TR>
+<TR><TD>http</TD><TD>HTTP connection to server.</TD></TR>
+<TR><TD>request</TD><TD>IPP request data.</TD></TR>
+<TR><TD>resource</TD><TD>HTTP resource name for POST.</TD></TR>
+<TR><TD>filename</TD><TD>File to send with POST request (<CODE>NULL</CODE>
+ pointer if none.)</TD></TR>
+</TABLE>
+</CENTER>
+<H3><A NAME="9_3_3">Returns</A></H3>
+<P>IPP response data or <CODE>NULL</CODE> if the request fails. On
+failure the error can be found by calling <A HREF="#cupsLastError"><CODE>
+cupsLastError()</CODE></A>. </P>
+<H3><A NAME="9_3_4">Description</A></H3>
+<P><CODE>cupsDoFileRequest()</CODE> does a HTTP POST request and
+provides the IPP request and optionally the contents of a file to the
+IPP server. It also handles resubmitting the request and performing
+password authentication as needed. </P>
+<H3><A NAME="9_3_5">Example</A></H3>
+<PRE>
+#include &lt;cups.h&gt;
+
+<A HREF="#http_t">http_t</A> *http;
+<A HREF="#cups_lang_t">cups_lang_t</A> *language;
+<A HREF="#ipp_t">ipp_t</A> *request;
+ipp_t *response;
+
+...
+
+/* Get the default language */
+language = <A HREF="#cupsLangDefault">cupsLangDefault()</A>;
+
+/* Create a new IPP request */
+request = <A HREF="#ippNew">ippNew()</A>;
+
+request-&gt;request.op.operation_id = IPP_PRINT_FILE;
+request-&gt;request.op.request_id = 1;
+
+/* Add required attributes */
+<A HREF="#ippAddString">ippAddString</A>(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
+ &quot;attributes-charset&quot;, NULL, <A HREF="#cupsLangEncoding">cupsLangEncoding</A>(language));
+
+ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
+ &quot;attributes-natural-language&quot;, NULL,
+ language != NULL ? language-&gt;language : &quot;C&quot;);
+
+ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, &quot;printer-uri&quot;,
+ NULL, &quot;ipp://hostname/resource&quot;);
+
+ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, &quot;requesting-user-name&quot;,
+ NULL, <A HREF="#cupsUser">cupsUser()</A>);
+
+/* Do the request... */
+response = cupsDoFileRequest(http, request, &quot;/resource&quot;, &quot;filename.txt&quot;);
+</PRE>
+<H3><A NAME="9_3_6">See Also</A></H3>
+<P><A HREF="#cupsLangDefault"><CODE>cupsLangDefault()</CODE></A>, <A HREF="#cupsLangEncoding">
+<CODE>cupsLangEncoding()</CODE></A>, <A HREF="#cupsUser"><CODE>
+cupsUser()</CODE></A>, <A HREF="#httpConnect"><CODE>httpConnect()</CODE></A>
+, <A HREF="#ippAddString"><CODE>ippAddString()</CODE></A>, <A HREF="#ippNew">
+<CODE>ippNew()</CODE></A>
+<!-- NEW PAGE ---->
+</P>
+<H2><A NAME="cupsDoRequest">cupsDoRequest()</A></H2>
+<H3><A NAME="9_4_1">Usage</A></H3>
+<PRE>
+ipp_t *
+cupsDoRequest(http_t *http,
+ ipp_t *request,
+ const char *resource);
+</PRE>
+<H3><A NAME="9_4_2">Arguments</A></H3>
+<CENTER>
+<TABLE BORDER WIDTH="80%">
+<TR><TH>Argument</TH><TH>Description</TH></TR>
+<TR><TD>http</TD><TD>HTTP connection to server.</TD></TR>
+<TR><TD>request</TD><TD>IPP request data.</TD></TR>
+<TR><TD>resource</TD><TD>HTTP resource name for POST.</TD></TR>
+</TABLE>
+</CENTER>
+<H3><A NAME="9_4_3">Returns</A></H3>
+<P>IPP response data or <CODE>NULL</CODE> if the request fails. On
+failure the error can be found by calling <A HREF="#cupsLastError"><CODE>
+cupsLastError()</CODE></A>. </P>
+<H3><A NAME="9_4_4">Description</A></H3>
+<P><CODE>cupsDoRequest()</CODE> does a HTTP POST request and provides
+the IPP request to the IPP server. It also handles resubmitting the
+request and performing password authentication as needed. </P>
+<H3><A NAME="9_4_5">Example</A></H3>
+<PRE>
+#include &lt;cups.h&gt;
+
+<A HREF="#http_t">http_t</A> *http;
+<A HREF="#cups_lang_t">cups_lang_t</A> *language;
+<A HREF="#ipp_t">ipp_t</A> *request;
+ipp_t *response;
+
+...
+
+/* Get the default language */
+language = <A HREF="#cupsLangDefault">cupsLangDefault()</A>;
+
+/* Create a new IPP request */
+request = <A HREF="#ippNew">ippNew()</A>;
+
+request-&gt;request.op.operation_id = IPP_GET_PRINTER_ATTRIBUTES;
+request-&gt;request.op.request_id = 1;
+
+/* Add required attributes */
+<A HREF="#ippAddString">ippAddString</A>(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
+ &quot;attributes-charset&quot;, NULL, <A HREF="#cupsLangEncoding">cupsLangEncoding</A>(language));
+
+ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
+ &quot;attributes-natural-language&quot;, NULL,
+ language != NULL ? language-&gt;language : &quot;C&quot;);
+
+ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, &quot;printer-uri&quot;,
+ NULL, &quot;ipp://hostname/resource&quot;);
+
+/* Do the request... */
+response = cupsDoRequest(http, request, &quot;/resource&quot;);
+</PRE>
+<H3><A NAME="9_4_6">See Also</A></H3>
+<P><A HREF="#cupsLangDefault"><CODE>cupsLangDefault()</CODE></A>, <A HREF="#cupsLangEncoding">
+<CODE>cupsLangEncoding()</CODE></A>, <A HREF="#cupsUser"><CODE>
+cupsUser()</CODE></A>, <A HREF="#httpConnect"><CODE>httpConnect()</CODE></A>
+, <A HREF="#ippAddString"><CODE>ippAddString()</CODE></A>, <A HREF="#ippNew">
+<CODE>ippNew()</CODE></A>
+<!-- NEW PAGE ---->
+</P>
+<H2><A NAME="cupsFreeOptions">cupsFreeOptions()</A></H2>
+<H3><A NAME="9_5_1">Usage</A></H3>
+<PRE>
+void
+cupsFreeOptions(int num_options,
+ cups_option_t *options);
+
+</PRE>
+<H3><A NAME="9_5_2">Arguments</A></H3>
+<CENTER>
+<TABLE BORDER WIDTH="80%">
+<TR><TH>Argument</TH><TH>Description</TH></TR>
+<TR><TD>num_options</TD><TD>Number of options in array.</TD></TR>
+<TR><TD>options</TD><TD>Pointer to options array.</TD></TR>
+</TABLE>
+</CENTER>
+<H3><A NAME="9_5_3">Description</A></H3>
+<P><CODE>cupsFreeOptions()</CODE> frees all memory associated with the
+option array specified. </P>
+<H3><A NAME="9_5_4">Example</A></H3>
+<PRE>
+#include &lt;cups/cups.h&gt;
+
+int num_options;
+cups_option_t *options;
+
+...
+
+cupsFreeOptions(num_options, options);
+</PRE>
+<H3><A NAME="9_5_5">See Also</A></H3>
+<P><A HREF="#cupsAddOption">cupsAddOption()</A>, <A HREF="#cupsGetOption">
+cupsGetOption()</A>, <A HREF="#cupsMarkOptions">cupsMarkOptions()</A>, <A
+HREF="#cupsParseOptions">cupsParseOptions()</A>
+<!-- NEW PAGE ---->
+</P>
+<H2><A NAME="cupsGetClasses">cupsGetClasses()</A></H2>
+<H3><A NAME="9_6_1">Usage</A></H3>
+<PRE>
+int
+cupsGetClasses(char ***classes);
+</PRE>
+<H3><A NAME="9_6_2">Arguments</A></H3>
+<CENTER>
+<TABLE BORDER WIDTH="80%">
+<TR><TH>Argument</TH><TH>Description</TH></TR>
+<TR><TD>classes</TD><TD>Pointer to character pointer array.</TD></TR>
+</TABLE>
+</CENTER>
+<H3><A NAME="9_6_3">Returns</A></H3>
+<P>The number of printer classes available. </P>
+<H3><A NAME="9_6_4">Description</A></H3>
+<P><CODE>cupsGetClasses()</CODE> gets a list of the available printer
+classes. The returned array should be freed using the <CODE>free()</CODE>
+ when it is no longer needed. </P>
+<H3><A NAME="9_6_5">Example</A></H3>
+<PRE>
+#include &lt;cups/cups.h&gt;
+
+int i;
+int num_classes;
+char **classes;
+
+...
+
+num_classes = cupsGetClasses(;
+
+...
+
+if (num_classes &gt; 0)
+{
+ for (i = 0; i num_classes; i ++)
+ free(classes[i]);
+
+ free(classes);
+}
+</PRE>
+<H3><A NAME="9_6_6">See Also</A></H3>
+<P><A HREF="#cupsGetDefault">cupsGetDefault(), <A HREF="#cupsGetPrinters">
+cupsGetPrinters()
+<!-- NEW PAGE ---->
+</A></A></P>
+<H2><A NAME="cupsGetDefault">cupsGetDefault()</A></H2>
+<H3><A NAME="9_7_1">Usage</A></H3>
+<PRE>
+const char *
+cupsGetDefault(void);
+</PRE>
+<H3><A NAME="9_7_2">Returns</A></H3>
+<P>A pointer to the default destination. </P>
+<H3><A NAME="9_7_3">Description</A></H3>
+<P><CODE>cupsGetDefault()</CODE> gets the default destination printer
+or class. The default destination is stored in a static string and will
+be overwritten (usually with the same value) after each call. </P>
+<H3><A NAME="9_7_4">Example</A></H3>
+<PRE>
+#include &lt;cups/cups.h&gt;
+
+printf(&quot;The default destination is %s\n&quot;, cupsGetDefault());
+</PRE>
+<H3><A NAME="9_7_5">See Also</A></H3>
+<P><A HREF="#cupsGetClasses">cupsGetClasses(), <A HREF="#cupsGetPrinters">
+cupsGetPrinters()
+<!-- NEW PAGE ---->
+</A></A></P>
+<H2><A NAME="cupsGetOption">cupsGetOption()</A></H2>
+<H3><A NAME="9_8_1">Usage</A></H3>
+<PRE>
+const char *
+cupsGetOption(const char *name,
+ int num_options,
+ cups_option_t *options);
+</PRE>
+<H3><A NAME="9_8_2">Arguments</A></H3>
+<CENTER>
+<TABLE BORDER WIDTH="80%">
+<TR><TH>Argument</TH><TH>Description</TH></TR>
+<TR><TD>name</TD><TD>The name of the option.</TD></TR>
+<TR><TD>num_options</TD><TD>The number of options in the array.</TD></TR>
+<TR><TD>options</TD><TD>The options array.</TD></TR>
+</TABLE>
+</CENTER>
+<H3><A NAME="9_8_3">Returns</A></H3>
+<P>A pointer to the option values or <CODE>NULL</CODE> if the option is
+not defined. </P>
+<H3><A NAME="9_8_4">Description</A></H3>
+<P><CODE>cupsGetOption()</CODE> returns the first occurrence of the
+named option. If the option is not included in the options array then a <CODE>
+NULL</CODE> pointer is returned. </P>
+<PRE>
+#include &lt;cups/cups.h&gt;
+
+int num_options;
+cups_option_t *options;
+const char *media;
+
+...
+
+media = cupsGetOption(&quot;media&quot;, num_options, options);
+</PRE>
+<H3><A NAME="9_8_5">See Also</A></H3>
+<P><A HREF="#cupsAddOption">cupsAddOption()</A>, <A HREF="#cupsFreeOptions">
+cupsFreeOptions()</A>, <A HREF="#cupsMarkOptions">cupsMarkOptions()</A>
+, <A HREF="#cupsParseOptions">cupsParseOptions()</A>
+<!-- NEW PAGE ---->
+</P>
+<H2><A NAME="cupsGetPassword">cupsGetPassword()</A></H2>
+<H3><A NAME="9_9_1">Usage</A></H3>
+<PRE>
+const char *
+cupsGetPassword(const char *prompt);
+</PRE>
+<H3><A NAME="9_9_2">Arguments</A></H3>
+<CENTER>
+<TABLE BORDER WIDTH="80%">
+<TR><TH>Argument</TH><TH>Description</TH></TR>
+<TR><TD>prompt</TD><TD>The prompt to display to the user.</TD></TR>
+</TABLE>
+</CENTER>
+<H3><A NAME="9_9_3">Returns</A></H3>
+<P>A pointer to the password that was entered or <CODE>NULL</CODE> if
+no password was entered. </P>
+<H3><A NAME="9_9_4">Description</A></H3>
+<P><CODE>cupsGetPassword()</CODE> displays the prompt string and asks
+the user for a password. The password text is not echoed to the user. </P>
+<H3><A NAME="9_9_5">Example</A></H3>
+<PRE>
+#include &lt;cups/cups.h&gt;
+
+char *password;
+
+...
+
+password = cupsGetPassword(&quot;Please enter a password:&quot;);
+</PRE>
+<H3><A NAME="9_9_6">See Also</A></H3>
+<P><A HREF="#cupsServer">cupsServer()</A>, <A HREF="#cupsUser()">
+cupsUser()</A>
+<!-- NEW PAGE ---->
+</P>
+<H2><A NAME="cupsGetPPD">cupsGetPPD()</A></H2>
+<H3><A NAME="9_10_1">Usage</A></H3>
+<PRE>
+const char *
+cupsGetPPD(const char *printer);
+</PRE>
+<H3><A NAME="9_10_2">Arguments</A></H3>
+<CENTER>
+<TABLE BORDER WIDTH="80%">
+<TR><TH>Argument</TH><TH>Description</TH></TR>
+<TR><TD>printer</TD><TD>The name of the printer.</TD></TR>
+</TABLE>
+</CENTER>
+<H3><A NAME="9_10_3">Returns</A></H3>
+<P>The name of a temporary file containing the PPD file or <CODE>NULL</CODE>
+ if the printer cannot be located or does not have a PPD file. </P>
+<H3><A NAME="9_10_4">Description</A></H3>
+<P><CODE>cupsGetPPD()</CODE> gets a copy of the PPD file for the named
+printer. The printer name can be of the form &quot;printer&quot; or
+&quot;printer@hostname&quot;. </P>
+<P>You should remove (unlink) the PPD file after you are done using it.
+The filename is stored in a static buffer and will be overwritten with
+each call to <CODE>cupsGetPPD()</CODE>. </P>
+<H3><A NAME="9_10_5">Example</A></H3>
+<PRE>
+#include &lt;cups/cups.h&gt;
+
+char *ppd;
+
+...
+
+ppd = cupsGetPPD(&quot;printer@hostname&quot;);
+
+...
+
+unlink(ppd);
+</PRE>
+
+<!-- NEW PAGE ---->
+<H2><A NAME="cupsGetPrinters">cupsGetPrinters()</A></H2>
+<H3><A NAME="9_11_1">Usage</A></H3>
+<PRE>
+int
+cupsGetPrinters(char ***printers);
+</PRE>
+<H3><A NAME="9_11_2">Arguments</A></H3>
+<CENTER>
+<TABLE BORDER WIDTH="80%">
+<TR><TH>Argument</TH><TH>Description</TH></TR>
+<TR><TD>printers</TD><TD>Pointer to character pointer array.</TD></TR>
+</TABLE>
+</CENTER>
+<H3><A NAME="9_11_3">Returns</A></H3>
+<P>The number of printer printers available. </P>
+<H3><A NAME="9_11_4">Description</A></H3>
+<P><CODE>cupsGetPrinters()</CODE> gets a list of the available
+printers. The returned array should be freed using the <CODE>free()</CODE>
+ when it is no longer needed. </P>
+<H3><A NAME="9_11_5">Example</A></H3>
+<PRE>
+#include &lt;cups/cups.h&gt;
+
+int i;
+int num_printers;
+char **printers;
+
+...
+
+num_printers = cupsGetPrinters(;
+
+...
+
+if (num_printers &gt; 0)
+{
+ for (i = 0; i num_printers; i ++)
+ free(printers[i]);
+
+ free(printers);
+}
+</PRE>
+<H3><A NAME="9_11_6">See Also</A></H3>
+<P><A HREF="#cupsGetClasses">cupsGetClasses(), <A HREF="#cupsGetDefault">
+cupsGetDefault()
+<!-- NEW PAGE ---->
+</A></A></P>
+<H2><A NAME="cupsLangDefault">cupsLangDefault()</A></H2>
+<H3><A NAME="9_12_1">Usage</A></H3>
+<PRE>
+const char *
+cupsLangDefault(void);
+</PRE>
+<H3><A NAME="9_12_2">Returns</A></H3>
+<P>A pointer to the default language structure. </P>
+<H3><A NAME="9_12_3">Description</A></H3>
+<P><CODE>cupsLangDefault()</CODE> returns a language structure for the
+default language. The default language is defined by the <CODE>LANG</CODE>
+ environment variable. If the specified language cannot be located then
+the POSIX (English) locale is used. </P>
+<P>Call <CODE>cupsLangFree()</CODE> to free any memory associated with
+the language structure when you are done. </P>
+<H3><A NAME="9_12_4">Example</A></H3>
+<PRE>
+#include &lt;cups/language.h&gt;
+
+cups_lang_t *language;
+...
+
+language = cupsLangDefault();
+
+...
+
+cupsLangFree(language);
+</PRE>
+<H3><A NAME="9_12_5">See Also</A></H3>
+<P><A HREF="#cupsLangEncoding">cupsLangEncoding()</A>, <A HREF="#cupsLangFlush">
+cupsLangFlush()</A>, <A HREF="#cupsLangFree">cupsLangFree()</A>, <A HREF="#cupsLangGet">
+cupsLangGet()</A>, <A HREF="#cupsLangString">cupsLangString()</A>
+<!-- NEW PAGE ---->
+</P>
+<H2><A NAME="cupsLangEncoding">cupsLangEncoding()</A></H2>
+<H3><A NAME="9_13_1">Usage</A></H3>
+<PRE>
+char *
+cupsLangEncoding(cups_lang_t *language);
+</PRE>
+<H3><A NAME="9_13_2">Arguments</A></H3>
+<CENTER>
+<TABLE BORDER WIDTH="80%">
+<TR><TH>Argument</TH><TH>Description</TH></TR>
+<TR><TD>language</TD><TD>The language structure.</TD></TR>
+</TABLE>
+</CENTER>
+<H3><A NAME="9_13_3">Returns</A></H3>
+<P>A pointer to the encoding string. </P>
+<H3><A NAME="9_13_4">Description</A></H3>
+<P><CODE>cupsLangEncoding()</CODE> returns the language encoding used
+for the specified language, e.g. &quot;iso-8859-1&quot;, &quot;utf-8&quot;, etc. </P>
+<H3><A NAME="9_13_5">Example</A></H3>
+<PRE>
+#include &lt;cups/language.h&gt;
+
+cups_lang_t *language;
+char *encoding;
+...
+
+language = cupsLangDefault();
+encoding = cupsLangEncoding(language);
+...
+
+cupsLangFree(language);
+</PRE>
+<H3><A NAME="9_13_6">See Also</A></H3>
+<P><A HREF="#cupsLangDefault">cupsLangDefault()</A>, <A HREF="#cupsLangFlush">
+cupsLangFlush()</A>, <A HREF="#cupsLangFree">cupsLangFree()</A>, <A HREF="#cupsLangGet">
+cupsLangGet()</A>, <A HREF="#cupsLangString">cupsLangString()</A>
+<!-- NEW PAGE ---->
+</P>
+<H2><A NAME="cupsLangFlush">cupsLangFlush()</A></H2>
+<H3><A NAME="9_14_1">Usage</A></H3>
+<PRE>
+void
+cupsLangFlush(void);
+</PRE>
+<H3><A NAME="9_14_2">Description</A></H3>
+<P><CODE>cupsLangFlush()</CODE> frees all language structures that have
+been allocated. </P>
+<H3><A NAME="9_14_3">Example</A></H3>
+<PRE>
+#include &lt;cups/language.h&gt;
+
+...
+
+cupsLangFlush();
+</PRE>
+<H3><A NAME="9_14_4">See Also</A></H3>
+<P><A HREF="#cupsLangDefault">cupsLangDefault()</A>, <A HREF="#cupsLangEncoding">
+cupsLangEncoding()</A>, <A HREF="#cupsLangFree">cupsLangFree()</A>, <A HREF="#cupsLangGet">
+cupsLangGet()</A>, <A HREF="#cupsLangString">cupsLangString()</A>
+<!-- NEW PAGE ---->
+</P>
+<H2><A NAME="cupsLangFree">cupsLangFree()</A></H2>
+<H3><A NAME="9_15_1">Usage</A></H3>
+<PRE>
+void
+cupsLangFree(cups_lang_t *language);
+</PRE>
+<H3><A NAME="9_15_2">Arguments</A></H3>
+<CENTER>
+<TABLE BORDER WIDTH="80%">
+<TR><TH>Argument</TH><TH>Description</TH></TR>
+<TR><TD>language</TD><TD>The language structure to free.</TD></TR>
+</TABLE>
+</CENTER>
+<H3><A NAME="9_15_3">Description</A></H3>
+<P><CODE>cupsLangFree()</CODE> frees the specified language structure. </P>
+<H3><A NAME="9_15_4">Example</A></H3>
+<PRE>
+#include &lt;cups/language.h&gt;
+
+cups_lang_t *language;
+...
+
+cupsLangFree(language);
+</PRE>
+<H3><A NAME="9_15_5">See Also</A></H3>
+<P><A HREF="#cupsLangDefault">cupsLangDefault()</A>, <A HREF="#cupsLangEncoding">
+cupsLangEncoding()</A>, <A HREF="#cupsLangFlush">cupsLangFlush()</A>, <A HREF="#cupsLangGet">
+cupsLangGet()</A>, <A HREF="#cupsLangString">cupsLangString()</A>
+<!-- NEW PAGE ---->
+</P>
+<H2><A NAME="cupsLangGet">cupsLangGet()</A></H2>
+<H3><A NAME="9_16_1">Usage</A></H3>
+<PRE>
+cups_lang_t *
+cupsLangGet(const char *name);
+</PRE>
+<H3><A NAME="9_16_2">Arguments</A></H3>
+<CENTER>
+<TABLE BORDER WIDTH="80%">
+<TR><TH>Argument</TH><TH>Description</TH></TR>
+<TR><TD>name</TD><TD>The name of the locale.</TD></TR>
+</TABLE>
+</CENTER>
+<H3><A NAME="9_16_3">Returns</A></H3>
+<P>A pointer to a language structure. </P>
+<H3><A NAME="9_16_4">Description</A></H3>
+<P><CODE>cupsLangGet()</CODE> returns a language structure for the
+specified locale. If the locale is not defined then the POSIX (English)
+locale is substituted. </P>
+<H3><A NAME="9_16_5">Example</A></H3>
+<PRE>
+#include &lt;cups/language.h&gt;
+
+cups_lang_t *language;
+
+...
+
+language = cupsLangGet(&quot;fr&quot;);
+
+...
+
+cupsLangFree(language);
+</PRE>
+<H3><A NAME="9_16_6">See Also</A></H3>
+<P><A HREF="#cupsLangDefault">cupsLangDefault()</A>, <A HREF="#cupsLangEncoding">
+cupsLangEncoding()</A>, <A HREF="#cupsLangFlush">cupsLangFlush()</A>, <A HREF="#cupsLangFree">
+cupsLangFree()</A>, <A HREF="#cupsLangString">cupsLangString()</A>
+<!-- NEW PAGE ---->
+</P>
+<H2><A NAME="cupsLangString">cupsLangString()</A></H2>
+<H3><A NAME="9_17_1">Usage</A></H3>
+<PRE>
+char *
+cupsLangString(cups_lang_t *language,
+ int message);
+</PRE>
+<H3><A NAME="9_17_2">Arguments</A></H3>
+<CENTER>
+<TABLE BORDER WIDTH="80%">
+<TR><TH>Argument</TH><TH>Description</TH></TR>
+<TR><TD>language</TD><TD>The language to query.</TD></TR>
+<TR><TD>message</TD><TD>The message number.</TD></TR>
+</TABLE>
+</CENTER>
+<H3><A NAME="9_17_3">Returns</A></H3>
+<P>A pointer to the message string or <CODE>NULL</CODE> if the message
+is not defined. </P>
+<H3><A NAME="9_17_4">Description</A></H3>
+<P><CODE>cupsLangString()</CODE> returns a pointer to the specified
+message string in the specified language. </P>
+<H3><A NAME="9_17_5">Example</A></H3>
+<PRE>
+#include &lt;cups/language.h&gt;
+
+cups_lang_t *language;
+char *s;
+...
+
+language = cupsLangGet(&quot;fr&quot;);
+
+s = cupsLangString(language, CUPS_MSG_YES);
+
+...
+
+cupsLangFree(language);
+</PRE>
+<H3><A NAME="9_17_6">See Also</A></H3>
+<P><A HREF="#cupsLangDefault">cupsLangDefault()</A>, <A HREF="#cupsLangEncoding">
+cupsLangEncoding()</A>, <A HREF="#cupsLangFlush">cupsLangFlush()</A>, <A HREF="#cupsLangFree">
+cupsLangFree()</A>, <A HREF="#cupsLangGet">cupsLangGet()</A>
+<!-- NEW PAGE ---->
+</P>
+<H2><A NAME="cupsLastError">cupsLastError()</A></H2>
+<H3><A NAME="9_18_1">Usage</A></H3>
+<PRE>
+ipp_status_t
+cupsLastError(void);
+</PRE>
+<H3><A NAME="9_18_2">Returns</A></H3>
+<P>An enumeration containing the last IPP error. </P>
+<H3><A NAME="9_18_3">Description</A></H3>
+<P><CODE>cupsLastError()</CODE> returns the last IPP error that
+occurred. If no error occurred then it will return <CODE>IPP_OK</CODE>
+ or <CODE>IPP_OK_CONFLICT</CODE>. </P>
+<H3><A NAME="9_18_4">Example</A></H3>
+<PRE>
+#include &lt;cups/cups.h&gt;
+
+ipp_status_t status;
+
+...
+
+status = cupsLastError();
+</PRE>
+<H3><A NAME="9_18_5">See Also</A></H3>
+<P><A HREF="#cupsCancelJob">cupsCancelJob()</A>, <A HREF="#cupsPrintFile">
+cupsPrintFile()</A>
+<!-- NEW PAGE ---->
+</P>
+<H2><A NAME="cupsMarkOptions">cupsMarkOptions()</A></H2>
+<H3><A NAME="9_19_1">Usage</A></H3>
+<PRE>
+int
+cupsMarkOptions(ppd_file_t *ppd,
+ int num_options,
+ cups_option_t *options);
+</PRE>
+<H3><A NAME="9_19_2">Arguments</A></H3>
+<CENTER>
+<TABLE BORDER WIDTH="80%">
+<TR><TH>Argument</TH><TH>Description</TH></TR>
+<TR><TD>ppd</TD><TD>The PPD file to mark.</TD></TR>
+<TR><TD>num_options</TD><TD>The number of options in the options array.</TD>
+</TR>
+<TR><TD>options</TD><TD>A pointer to the options array.</TD></TR>
+</TABLE>
+</CENTER>
+<H3><A NAME="9_19_3">Returns</A></H3>
+<P>The number of conflicts found. </P>
+<H3><A NAME="9_19_4">Description</A></H3>
+<P><CODE>cupsMarkOptions()</CODE> marks options in the PPD file. It
+also handles mapping of IPP option names and values to PPD option
+names. </P>
+<H3><A NAME="9_19_5">Example</A></H3>
+<PRE>
+#include &lt;cups/cups.h&gt;
+
+int num_options;
+cups_option_t *options;
+ppd_file_t *ppd;
+
+...
+
+cupsMarkOptions(ppd, num_options, options);
+</PRE>
+<H3><A NAME="9_19_6">See Also</A></H3>
+<P><A HREF="#cupsAddOption">cupsAddOption()</A>, <A HREF="#cupsFreeOptions">
+cupsFreeOptions()</A>, <A HREF="#cupsGetOption">cupsGetOption()</A>, <A HREF="#cupsParseOptions">
+cupsParseOptions()</A>
+<!-- NEW PAGE ---->
+</P>
+<H2><A NAME="cupsParseOptions">cupsParseOptions()</A></H2>
+<H3><A NAME="9_20_1">Usage</A></H3>
+<PRE>
+int
+cupsParseOptions(const char *arg,
+ int num_options,
+ cups_option_t **options);
+</PRE>
+<H3><A NAME="9_20_2">Arguments</A></H3>
+<CENTER>
+<TABLE BORDER WIDTH="80%">
+<TR><TH>Argument</TH><TH>Description</TH></TR>
+<TR><TD>arg</TD><TD>The string containing one or more options.</TD></TR>
+<TR><TD>num_options</TD><TD>The number of options in the options array.</TD>
+</TR>
+<TR><TD>options</TD><TD>A pointer to the options array pointer.</TD></TR>
+</TABLE>
+</CENTER>
+<H3><A NAME="9_20_3">Returns</A></H3>
+<P>The new number of options in the array. </P>
+<H3><A NAME="9_20_4">Description</A></H3>
+<P><CODE>cupsParseOptions()</CODE> parses the specifies string for one
+or more options of the form &quot;name=value&quot;, &quot;name&quot;, or &quot;noname&quot;. It can
+be called multiple times to combine the options from several strings. </P>
+<H3><A NAME="9_20_5">Example</A></H3>
+<PRE>
+#include &lt;cups/cups.h&gt;
+
+int num_options;
+cups_option_t *options;
+
+...
+
+num_options = 0;
+options = (cups_option_t *)0;
+num_options = cupsParseOptions(argv[5], num_options, &amp;options);
+</PRE>
+<H3><A NAME="9_20_6">See Also</A></H3>
+<P><A HREF="#cupsAddOption">cupsAddOption()</A>, <A HREF="#cupsFreeOptions">
+cupsFreeOptions()</A>, <A HREF="#cupsGetOption">cupsGetOption()</A>, <A HREF="#cupsMarkOptions">
+cupsMarkOptions()</A>
+<!-- NEW PAGE ---->
+</P>
+<H2><A NAME="cupsPrintFile">cupsPrintFile()</A></H2>
+<H3><A NAME="9_21_1">Usage</A></H3>
+<PRE>
+int
+cupsPrintFile(const char *printer,
+ const char *filename,
+ const char *title,
+ int num_options,
+ cups_option_t *options);
+</PRE>
+<H3><A NAME="9_21_2">Arguments</A></H3>
+<CENTER>
+<TABLE BORDER WIDTH="80%">
+<TR><TH>Argument</TH><TH>Description</TH></TR>
+<TR><TD>printer</TD><TD>The printer or class to print to.</TD></TR>
+<TR><TD>filename</TD><TD>The file to print.</TD></TR>
+<TR><TD>title</TD><TD>The job title.</TD></TR>
+<TR><TD>num_options</TD><TD>The number of options in the options array.</TD>
+</TR>
+<TR><TD>options</TD><TD>A pointer to the options array.</TD></TR>
+</TABLE>
+</CENTER>
+<H3><A NAME="9_21_3">Returns</A></H3>
+<P>The new job ID number or 0 on error. </P>
+<H3><A NAME="9_21_4">Description</A></H3>
+<P><CODE>cupsPrintFile()</CODE> sends a file to the specified printer
+or class for printing. If the job cannot be printed the error code can
+be found by calling <CODE>cupsLastError()</CODE>. </P>
+<H3><A NAME="9_21_5">Example</A></H3>
+<PRE>
+#include &lt;cups/cups.h&gt;
+
+int num_options;
+cups_option_t *options;
+
+...
+
+cupsPrintFile(&quot;printer@hostname&quot;, &quot;filename.ps&quot;, &quot;Job Title&quot;, num_options,
+ options);
+</PRE>
+<H3><A NAME="9_21_6">See Also</A></H3>
+<P><A HREF="#cupsCancelJob">cupsCancelJob()</A>, <A HREF="#cupsLastError">
+cupsLastError()</A>
+<!-- NEW PAGE ---->
+</P>
+<H2><A NAME="cupsRasterClose">cupsRasterClose()</A></H2>
+<H3><A NAME="9_22_1">Usage</A></H3>
+<PRE>
+void
+cupsRasterClose(cups_raster_t *ras);
+</PRE>
+<H3><A NAME="9_22_2">Arguments</A></H3>
+<CENTER>
+<TABLE BORDER WIDTH="80%">
+<TR><TH>Argument</TH><TH>Description</TH></TR>
+<TR><TD>ras</TD><TD>The raster stream to close.</TD></TR>
+</TABLE>
+</CENTER>
+<H3><A NAME="9_22_3">Description</A></H3>
+<P><CODE>cupsRasterClose()</CODE> closes the specified raster stream. </P>
+<H3><A NAME="9_22_4">Example</A></H3>
+<PRE>
+#include &lt;cups/raster.h&gt;
+
+cups_raster_t *ras;
+
+...
+
+cupsRasterClose(ras);
+</PRE>
+<H3><A NAME="9_22_5">See Also</A></H3>
+<P><A HREF="#cupsRasterOpen">cupsRasterOpen()</A>, <A HREF="#cupsRasterReadHeader">
+cupsRasterReadHeader()</A>, <A HREF="#cupsRasterReadPixels">
+cupsRasterReadPixels()</A>, <A HREF="#cupsRasterWriteHeader">
+cupsRasterWriteHeader()</A>, <A HREF="#cupsRasterWritePixels">
+cupsRasterWritePixels()</A>
+<!-- NEW PAGE ---->
+</P>
+<H2><A NAME="cupsRasterOpen">cupsRasterOpen()</A></H2>
+<H3><A NAME="9_23_1">Usage</A></H3>
+<PRE>
+cups_raster_t *
+cupsRasterOpen(int fd,
+ cups_mode_t mode);
+</PRE>
+<H3><A NAME="9_23_2">Arguments</A></H3>
+<CENTER>
+<TABLE BORDER WIDTH="80%">
+<TR><TH>Argument</TH><TH>Description</TH></TR>
+<TR><TD>fd</TD><TD>The file descriptor to use.</TD></TR>
+<TR><TD>mode</TD><TD>The mode to use; <CODE>CUPS_RASTER_READ</CODE> or <CODE>
+CUPS_RASTER_WRITE</CODE>.</TD></TR>
+</TABLE>
+</CENTER>
+<H3><A NAME="9_23_3">Returns</A></H3>
+<P>A pointer to a raster stream or <CODE>NULL</CODE> if there was an
+error. </P>
+<H3><A NAME="9_23_4">Description</A></H3>
+<P><CODE>cupsRasterOpen()</CODE> opens a raster stream for reading or
+writing. </P>
+<H3><A NAME="9_23_5">Example</A></H3>
+<PRE>
+#include &lt;cups/raster.h&gt;
+
+cups_raster_t *ras;
+
+...
+
+ras = cupsRasterOpen(0, CUPS_RASTER_READ);
+</PRE>
+<H3><A NAME="9_23_6">See Also</A></H3>
+<P><A HREF="#cupsRasterClose">cupsRasterClose()</A>, <A HREF="#cupsRasterReadHeader">
+cupsRasterReadHeader()</A>, <A HREF="#cupsRasterReadPixels">
+cupsRasterReadPixels()</A>, <A HREF="#cupsRasterWriteHeader">
+cupsRasterWriteHeader()</A>, <A HREF="#cupsRasterWritePixels">
+cupsRasterWritePixels()</A>
+<!-- NEW PAGE ---->
+</P>
+<H2><A NAME="cupsRasterReadHeader">cupsRasterReadHeader()</A></H2>
+<H3><A NAME="9_24_1">Usage</A></H3>
+<PRE>
+unsigned
+cupsRasterReadHeader(cups_raster_t *ras,
+ cups_page_header_t *header);
+</PRE>
+<H3><A NAME="9_24_2">Arguments</A></H3>
+<CENTER>
+<TABLE BORDER WIDTH="80%">
+<TR><TH>Argument</TH><TH>Description</TH></TR>
+<TR><TD>ras</TD><TD>The raster stream to read from.</TD></TR>
+<TR><TD>header</TD><TD>A pointer to a page header structure to read
+into.</TD></TR>
+</TABLE>
+</CENTER>
+<H3><A NAME="9_24_3">Returns</A></H3>
+<P>1 on success, 0 on EOF or error. </P>
+<H3><A NAME="9_24_4">Description</A></H3>
+<P><CODE>cupsRasterReadHeader()</CODE> reads a page header from the
+specified raster stream. </P>
+<H3><A NAME="9_24_5">Example</A></H3>
+<PRE>
+#include &lt;cups/raster.h&gt;
+
+int line;
+cups_raster_t *ras;
+cups_raster_header_t header;
+unsigned char pixels[8192];
+...
+
+while (cupsRasterReadHeader(ras, &amp;header))
+{
+ ...
+
+ for (line = 0; line &lt; header.cupsHeight; line ++)
+ {
+ cupsRasterReadPixels(ras, pixels, header.cupsBytesPerLine);
+
+ ...
+ }
+}
+</PRE>
+<H3><A NAME="9_24_6">See Also</A></H3>
+<P><A HREF="#cupsRasterClose">cupsRasterClose()</A>, <A HREF="#cupsRasterOpen">
+cupsRasterOpen()</A>, <A HREF="#cupsRasterReadPixels">
+cupsRasterReadPixels()</A>, <A HREF="#cupsRasterWriteHeader">
+cupsRasterWriteHeader()</A>, <A HREF="#cupsRasterWritePixels">
+cupsRasterWritePixels()</A>
+<!-- NEW PAGE ---->
+</P>
+<H2><A NAME="cupsRasterReadPixels">cupsRasterReadPixels()</A></H2>
+<H3><A NAME="9_25_1">Usage</A></H3>
+<PRE>
+unsigned
+cupsRasterReadPixels(cups_raster_t *ras,
+ unsigned char *pixels,
+ unsigned length);
+</PRE>
+<H3><A NAME="9_25_2">Arguments</A></H3>
+<CENTER>
+<TABLE BORDER WIDTH="80%">
+<TR><TH>Argument</TH><TH>Description</TH></TR>
+<TR><TD>ras</TD><TD>The raster stream to read from.</TD></TR>
+<TR><TD>pixels</TD><TD>The pointer to a pixel buffer.</TD></TR>
+<TR><TD>length</TD><TD>The number of bytes of pixel data to read.</TD></TR>
+</TABLE>
+</CENTER>
+<H3><A NAME="9_25_3">Returns</A></H3>
+<P>The number of bytes read or 0 on EOF or error. </P>
+<H3><A NAME="9_25_4">Description</A></H3>
+<P><CODE>cupsRasterReadPixels()</CODE> reads pixel data from the
+specified raster stream. </P>
+<H3><A NAME="9_25_5">Example</A></H3>
+<PRE>
+#include &lt;cups/raster.h&gt;
+
+int line;
+cups_raster_t *ras;
+cups_raster_header_t header;
+unsigned char pixels[8192];
+...
+
+while (cupsRasterReadHeader(ras, &amp;header))
+{
+ ...
+
+ for (line = 0; line &lt; header.cupsHeight; line ++)
+ {
+ cupsRasterReadPixels(ras, pixels, header.cupsBytesPerLine);
+
+ ...
+ }
+}
+</PRE>
+<H3><A NAME="9_25_6">See Also</A></H3>
+<P><A HREF="#cupsRasterClose">cupsRasterClose()</A>, <A HREF="#cupsRasterOpen">
+cupsRasterOpen()</A>, <A HREF="#cupsRasterReadHeader">
+cupsRasterReadHeader()</A>, <A HREF="#cupsRasterWriteHeader">
+cupsRasterWriteHeader()</A>, <A HREF="#cupsRasterWritePixels">
+cupsRasterWritePixels()</A>
+<!-- NEW PAGE ---->
+</P>
+<H2><A NAME="cupsRasterWriteHeader">cupsRasterWriteHeader()</A></H2>
+<H3><A NAME="9_26_1">Usage</A></H3>
+<PRE>
+unsigned
+cupsRasterWriteHeader(cups_raster_t *ras,
+ cups_page_header_t *header);
+</PRE>
+<H3><A NAME="9_26_2">Arguments</A></H3>
+<CENTER>
+<TABLE BORDER WIDTH="80%">
+<TR><TH>Argument</TH><TH>Description</TH></TR>
+<TR><TD>ras</TD><TD>The raster stream to write to.</TD></TR>
+<TR><TD>header</TD><TD>A pointer to the page header to write.</TD></TR>
+</TABLE>
+</CENTER>
+<H3><A NAME="9_26_3">Returns</A></H3>
+<P>1 on success, 0 on error. </P>
+<H3><A NAME="9_26_4">Description</A></H3>
+<P><CODE>cupsRasterWriteHeader()</CODE> writes the specified page
+header to a raster stream. </P>
+<H3><A NAME="9_26_5">Example</A></H3>
+<PRE>
+#include &lt;cups/raster.h&gt;
+
+int line;
+cups_raster_t *ras;
+cups_raster_header_t header;
+unsigned char pixels[8192];
+...
+
+cupsRasterWriteHeader(ras, &amp;header);
+
+for (line = 0; line &lt; header.cupsHeight; line ++)
+{
+ ...
+
+ cupsRasterWritePixels(ras, pixels, header.cupsBytesPerLine);
+}
+</PRE>
+<H3><A NAME="9_26_6">See Also</A></H3>
+<P><A HREF="#cupsRasterClose">cupsRasterClose()</A>, <A HREF="#cupsRasterOpen">
+cupsRasterOpen()</A>, <A HREF="#cupsRasterReadHeader">
+cupsRasterReadHeader()</A>, <A HREF="#cupsRasterReadPixels">
+cupsRasterReadPixels()</A>, <A HREF="#cupsRasterWritePixels">
+cupsRasterWritePixels()</A>
+<!-- NEW PAGE ---->
+</P>
+<H2><A NAME="cupsRasterWritePixels">cupsRasterWritePixels()</A></H2>
+<H3><A NAME="9_27_1">Usage</A></H3>
+<PRE>
+unsigned
+cupsRasterWritePixels(cups_raster_t *ras,
+ unsigned char *pixels,
+ unsigned length);
+</PRE>
+<H3><A NAME="9_27_2">Arguments</A></H3>
+<CENTER>
+<TABLE BORDER WIDTH="80%">
+<TR><TH>Argument</TH><TH>Description</TH></TR>
+<TR><TD>ras</TD><TD>The raster stream to write to.</TD></TR>
+<TR><TD>pixels</TD><TD>The pixel data to write.</TD></TR>
+<TR><TD>length</TD><TD>The number of bytes to write.</TD></TR>
+</TABLE>
+</CENTER>
+<H3><A NAME="9_27_3">Returns</A></H3>
+<P>The number of bytes written. </P>
+<H3><A NAME="9_27_4">Description</A></H3>
+<P><CODE>cupsRasterWritePixels()</CODE> writes the specified pixel data
+to a raster stream. </P>
+<H3><A NAME="9_27_5">Example</A></H3>
+<PRE>
+#include &lt;cups/raster.h&gt;
+
+int line;
+cups_raster_t *ras;
+cups_raster_header_t header;
+unsigned char pixels[8192];
+...
+
+cupsRasterWriteHeader(ras, &amp;header);
+
+for (line = 0; line &lt; header.cupsHeight; line ++)
+{
+ ...
+
+ cupsRasterWritePixels(ras, pixels, header.cupsBytesPerLine);
+}
+</PRE>
+<H3><A NAME="9_27_6">See Also</A></H3>
+<P><A HREF="#cupsRasterClose">cupsRasterClose()</A>, <A HREF="#cupsRasterOpen">
+cupsRasterOpen()</A>, <A HREF="#cupsRasterReadHeader">
+cupsRasterReadHeader()</A>, <A HREF="#cupsRasterReadPixels">
+cupsRasterReadPixels()</A>, <A HREF="#cupsRasterWriteHeader">
+cupsRasterWriteHeader()</A>
+<!-- NEW PAGE ---->
+</P>
+<H2><A NAME="cupsServer">cupsServer()</A></H2>
+<H3><A NAME="9_28_1">Usage</A></H3>
+<PRE>
+const char *
+cupsServer(void);
+</PRE>
+<H3><A NAME="9_28_2">Returns</A></H3>
+<P>A pointer to the default server name. </P>
+<H3><A NAME="9_28_3">Description</A></H3>
+<P><CODE>cupsServer()</CODE> returns a pointer to the default server
+name. The server name is stored in a static location and will be
+overwritten with every call to <CODE>cupsServer()</CODE></P>
+<P>The default server is determined from the following locations: </P>
+<OL>
+<LI>The <CODE>CUPS_SERVER</CODE> environment variable, </LI>
+<LI>The <CODE>ServerName</CODE> directive in the <VAR>cupsd.conf</VAR>
+ file, </LI>
+<LI>The default host, &quot;localhost&quot;. </LI>
+</OL>
+<H3><A NAME="9_28_4">Example</A></H3>
+<PRE>
+#include &lt;cups/cups.h&gt;
+
+const char *server;
+
+server = cupsServer();
+</PRE>
+<H3><A NAME="9_28_5">See Also</A></H3>
+<P><A HREF="#cupsGetPassword">cupsGetPassword()</A>, <A HREF="#cupsUser">
+cupsUser()</A>
+<!-- NEW PAGE ---->
+</P>
+<H2><A NAME="cupsTempFile">cupsTempFile()</A></H2>
+<H3><A NAME="9_29_1">Usage</A></H3>
+<PRE>
+char *
+cupsTempFile(char *filename,
+ int length);
+</PRE>
+<H3><A NAME="9_29_2">Arguments</A></H3>
+<CENTER>
+<TABLE BORDER WIDTH="80%">
+<TR><TH>Argument</TH><TH>Description</TH></TR>
+<TR><TD>filename</TD><TD>The character string to hold the temporary
+filename.</TD></TR>
+<TR><TD>length</TD><TD>The size of the filename string in bytes.</TD></TR>
+</TABLE>
+</CENTER>
+<H3><A NAME="9_29_3">Returns</A></H3>
+<P>A pointer to <CODE>filename</CODE>. </P>
+<H3><A NAME="9_29_4">Description</A></H3>
+<P><CODE>cupsTempFile()</CODE> generates a temporary filename for the <VAR>
+/var/tmp</VAR> directory or the directory specified by the <CODE>TMPDIR</CODE>
+ environment variable. </P>
+<H3><A NAME="9_29_5">Example</A></H3>
+<PRE>
+#include &lt;cups/cups.h&gt;
+
+char filename[256];
+
+cupsTempFile(filename, sizeof(filename));
+</PRE>
+
+<!-- NEW PAGE ---->
+<H2><A NAME="cupsUser">cupsUser()</A></H2>
+<H3><A NAME="9_30_1">Usage</A></H3>
+<PRE>
+const char *
+cupsUser(void);
+</PRE>
+<H3><A NAME="9_30_2">Returns</A></H3>
+<P>A pointer to the current username or <CODE>NULL</CODE> if the user
+ID is undefined. </P>
+<H3><A NAME="9_30_3">Description</A></H3>
+<P><CODE>cupsUser()</CODE> returns the name associated with the current
+user ID as reported by the <CODE>getuid()</CODE> system call. </P>
+<H3><A NAME="9_30_4">Example</A></H3>
+<PRE>
+#include &lt;cups/cups.h&gt;
+
+const char *user;
+
+user = cupsUser();
+</PRE>
+<H3><A NAME="9_30_5">See Also</A></H3>
+<P><A HREF="#cupsGetPassword">cupsGetPassword()</A>, <A HREF="#cupsServer">
+cupsServer()</A>
+<!-- NEW PAGE ---->
+</P>
+<H2><A NAME="httpBlocking">httpBlocking()</A></H2>
+<H3><A NAME="9_31_1">Usage</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_31_2">Arguments</A></H3>
+<CENTER>
+<TABLE BORDER WIDTH="80%">
+<TR><TH>Argument</TH><TH>Description</TH></TR>
+<TR><TD><TD></TR>
+</TABLE>
+</CENTER>
+<H3><A NAME="9_31_3">Returns</A></H3>
+<H3><A NAME="9_31_4">Description</A></H3>
+<H3><A NAME="9_31_5">Example</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_31_6">See Also</A></H3>
+
+<!-- NEW PAGE ---->
+<H2><A NAME="httpCheck">httpCheck()</A></H2>
+<H3><A NAME="9_32_1">Usage</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_32_2">Arguments</A></H3>
+<CENTER>
+<TABLE BORDER WIDTH="80%">
+<TR><TH>Argument</TH><TH>Description</TH></TR>
+<TR><TD><TD></TR>
+</TABLE>
+</CENTER>
+<H3><A NAME="9_32_3">Returns</A></H3>
+<H3><A NAME="9_32_4">Description</A></H3>
+<H3><A NAME="9_32_5">Example</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_32_6">See Also</A></H3>
+
+<!-- NEW PAGE ---->
+<H2><A NAME="httpClearFields">httpClearFields()</A></H2>
+<H3><A NAME="9_33_1">Usage</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_33_2">Arguments</A></H3>
+<CENTER>
+<TABLE BORDER WIDTH="80%">
+<TR><TH>Argument</TH><TH>Description</TH></TR>
+<TR><TD><TD></TR>
+</TABLE>
+</CENTER>
+<H3><A NAME="9_33_3">Returns</A></H3>
+<H3><A NAME="9_33_4">Description</A></H3>
+<H3><A NAME="9_33_5">Example</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_33_6">See Also</A></H3>
+
+<!-- NEW PAGE ---->
+<H2><A NAME="httpClose">httpClose()</A></H2>
+<H3><A NAME="9_34_1">Usage</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_34_2">Arguments</A></H3>
+<CENTER>
+<TABLE BORDER WIDTH="80%">
+<TR><TH>Argument</TH><TH>Description</TH></TR>
+<TR><TD><TD></TR>
+</TABLE>
+</CENTER>
+<H3><A NAME="9_34_3">Returns</A></H3>
+<H3><A NAME="9_34_4">Description</A></H3>
+<H3><A NAME="9_34_5">Example</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_34_6">See Also</A></H3>
+
+<!-- NEW PAGE ---->
+<H2><A NAME="httpConnect">httpConnect()</A></H2>
+<H3><A NAME="9_35_1">Usage</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_35_2">Arguments</A></H3>
+<CENTER>
+<TABLE BORDER WIDTH="80%">
+<TR><TH>Argument</TH><TH>Description</TH></TR>
+<TR><TD><TD></TR>
+</TABLE>
+</CENTER>
+<H3><A NAME="9_35_3">Returns</A></H3>
+<H3><A NAME="9_35_4">Description</A></H3>
+<H3><A NAME="9_35_5">Example</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_35_6">See Also</A></H3>
+
+<!-- NEW PAGE ---->
+<H2><A NAME="httpDecode64">httpDecode64()</A></H2>
+<H3><A NAME="9_36_1">Usage</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_36_2">Arguments</A></H3>
+<CENTER>
+<TABLE BORDER WIDTH="80%">
+<TR><TH>Argument</TH><TH>Description</TH></TR>
+<TR><TD><TD></TR>
+</TABLE>
+</CENTER>
+<H3><A NAME="9_36_3">Returns</A></H3>
+<H3><A NAME="9_36_4">Description</A></H3>
+<H3><A NAME="9_36_5">Example</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_36_6">See Also</A></H3>
+
+<!-- NEW PAGE ---->
+<H2><A NAME="httpDelete">httpDelete()</A></H2>
+<H3><A NAME="9_37_1">Usage</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_37_2">Arguments</A></H3>
+<CENTER>
+<TABLE BORDER WIDTH="80%">
+<TR><TH>Argument</TH><TH>Description</TH></TR>
+<TR><TD><TD></TR>
+</TABLE>
+</CENTER>
+<H3><A NAME="9_37_3">Returns</A></H3>
+<H3><A NAME="9_37_4">Description</A></H3>
+<H3><A NAME="9_37_5">Example</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_37_6">See Also</A></H3>
+
+<!-- NEW PAGE ---->
+<H2><A NAME="httpEncode64">httpEncode64()</A></H2>
+<H3><A NAME="9_38_1">Usage</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_38_2">Arguments</A></H3>
+<CENTER>
+<TABLE BORDER WIDTH="80%">
+<TR><TH>Argument</TH><TH>Description</TH></TR>
+<TR><TD><TD></TR>
+</TABLE>
+</CENTER>
+<H3><A NAME="9_38_3">Returns</A></H3>
+<H3><A NAME="9_38_4">Description</A></H3>
+<H3><A NAME="9_38_5">Example</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_38_6">See Also</A></H3>
+
+<!-- NEW PAGE ---->
+<H2><A NAME="httpError">httpError()</A></H2>
+<H3><A NAME="9_39_1">Usage</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_39_2">Arguments</A></H3>
+<CENTER>
+<TABLE BORDER WIDTH="80%">
+<TR><TH>Argument</TH><TH>Description</TH></TR>
+<TR><TD><TD></TR>
+</TABLE>
+</CENTER>
+<H3><A NAME="9_39_3">Returns</A></H3>
+<H3><A NAME="9_39_4">Description</A></H3>
+<H3><A NAME="9_39_5">Example</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_39_6">See Also</A></H3>
+
+<!-- NEW PAGE ---->
+<H2><A NAME="httpFlush">httpFlush()</A></H2>
+<H3><A NAME="9_40_1">Usage</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_40_2">Arguments</A></H3>
+<CENTER>
+<TABLE BORDER WIDTH="80%">
+<TR><TH>Argument</TH><TH>Description</TH></TR>
+<TR><TD><TD></TR>
+</TABLE>
+</CENTER>
+<H3><A NAME="9_40_3">Returns</A></H3>
+<H3><A NAME="9_40_4">Description</A></H3>
+<H3><A NAME="9_40_5">Example</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_40_6">See Also</A></H3>
+
+<!-- NEW PAGE ---->
+<H2><A NAME="httpGet">httpGet()</A></H2>
+<H3><A NAME="9_41_1">Usage</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_41_2">Arguments</A></H3>
+<CENTER>
+<TABLE BORDER WIDTH="80%">
+<TR><TH>Argument</TH><TH>Description</TH></TR>
+<TR><TD><TD></TR>
+</TABLE>
+</CENTER>
+<H3><A NAME="9_41_3">Returns</A></H3>
+<H3><A NAME="9_41_4">Description</A></H3>
+<H3><A NAME="9_41_5">Example</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_41_6">See Also</A></H3>
+
+<!-- NEW PAGE ---->
+<H2><A NAME="httpGets">httpGets()</A></H2>
+<H3><A NAME="9_42_1">Usage</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_42_2">Arguments</A></H3>
+<CENTER>
+<TABLE BORDER WIDTH="80%">
+<TR><TH>Argument</TH><TH>Description</TH></TR>
+<TR><TD><TD></TR>
+</TABLE>
+</CENTER>
+<H3><A NAME="9_42_3">Returns</A></H3>
+<H3><A NAME="9_42_4">Description</A></H3>
+<H3><A NAME="9_42_5">Example</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_42_6">See Also</A></H3>
+
+<!-- NEW PAGE ---->
+<H2><A NAME="httpGetDateString">httpGetDateString()</A></H2>
+<H3><A NAME="9_43_1">Usage</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_43_2">Arguments</A></H3>
+<CENTER>
+<TABLE BORDER WIDTH="80%">
+<TR><TH>Argument</TH><TH>Description</TH></TR>
+<TR><TD><TD></TR>
+</TABLE>
+</CENTER>
+<H3><A NAME="9_43_3">Returns</A></H3>
+<H3><A NAME="9_43_4">Description</A></H3>
+<H3><A NAME="9_43_5">Example</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_43_6">See Also</A></H3>
+
+<!-- NEW PAGE ---->
+<H2><A NAME="httpGetDateTime">httpGetDateTime()</A></H2>
+<H3><A NAME="9_44_1">Usage</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_44_2">Arguments</A></H3>
+<CENTER>
+<TABLE BORDER WIDTH="80%">
+<TR><TH>Argument</TH><TH>Description</TH></TR>
+<TR><TD><TD></TR>
+</TABLE>
+</CENTER>
+<H3><A NAME="9_44_3">Returns</A></H3>
+<H3><A NAME="9_44_4">Description</A></H3>
+<H3><A NAME="9_44_5">Example</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_44_6">See Also</A></H3>
+
+<!-- NEW PAGE ---->
+<H2><A NAME="httpGetField">httpGetField()</A></H2>
+<H3><A NAME="9_45_1">Usage</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_45_2">Arguments</A></H3>
+<CENTER>
+<TABLE BORDER WIDTH="80%">
+<TR><TH>Argument</TH><TH>Description</TH></TR>
+<TR><TD><TD></TR>
+</TABLE>
+</CENTER>
+<H3><A NAME="9_45_3">Returns</A></H3>
+<H3><A NAME="9_45_4">Description</A></H3>
+<H3><A NAME="9_45_5">Example</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_45_6">See Also</A></H3>
+
+<!-- NEW PAGE ---->
+<H2><A NAME="httpGetLength">httpGetLength()</A></H2>
+<H3><A NAME="9_46_1">Usage</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_46_2">Arguments</A></H3>
+<CENTER>
+<TABLE BORDER WIDTH="80%">
+<TR><TH>Argument</TH><TH>Description</TH></TR>
+<TR><TD><TD></TR>
+</TABLE>
+</CENTER>
+<H3><A NAME="9_46_3">Returns</A></H3>
+<H3><A NAME="9_46_4">Description</A></H3>
+<H3><A NAME="9_46_5">Example</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_46_6">See Also</A></H3>
+
+<!-- NEW PAGE ---->
+<H2><A NAME="httpHead">httpHead()</A></H2>
+<H3><A NAME="9_47_1">Usage</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_47_2">Arguments</A></H3>
+<CENTER>
+<TABLE BORDER WIDTH="80%">
+<TR><TH>Argument</TH><TH>Description</TH></TR>
+<TR><TD><TD></TR>
+</TABLE>
+</CENTER>
+<H3><A NAME="9_47_3">Returns</A></H3>
+<H3><A NAME="9_47_4">Description</A></H3>
+<H3><A NAME="9_47_5">Example</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_47_6">See Also</A></H3>
+
+<!-- NEW PAGE ---->
+<H2><A NAME="httpInitialize">httpInitialize()</A></H2>
+<H3><A NAME="9_48_1">Usage</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_48_2">Arguments</A></H3>
+<CENTER>
+<TABLE BORDER WIDTH="80%">
+<TR><TH>Argument</TH><TH>Description</TH></TR>
+<TR><TD><TD></TR>
+</TABLE>
+</CENTER>
+<H3><A NAME="9_48_3">Returns</A></H3>
+<H3><A NAME="9_48_4">Description</A></H3>
+<H3><A NAME="9_48_5">Example</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_48_6">See Also</A></H3>
+
+<!-- NEW PAGE ---->
+<H2><A NAME="httpOptions">httpOptions()</A></H2>
+<H3><A NAME="9_49_1">Usage</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_49_2">Arguments</A></H3>
+<CENTER>
+<TABLE BORDER WIDTH="80%">
+<TR><TH>Argument</TH><TH>Description</TH></TR>
+<TR><TD><TD></TR>
+</TABLE>
+</CENTER>
+<H3><A NAME="9_49_3">Returns</A></H3>
+<H3><A NAME="9_49_4">Description</A></H3>
+<H3><A NAME="9_49_5">Example</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_49_6">See Also</A></H3>
+
+<!-- NEW PAGE ---->
+<H2><A NAME="httpPost">httpPost()</A></H2>
+<H3><A NAME="9_50_1">Usage</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_50_2">Arguments</A></H3>
+<CENTER>
+<TABLE BORDER WIDTH="80%">
+<TR><TH>Argument</TH><TH>Description</TH></TR>
+<TR><TD><TD></TR>
+</TABLE>
+</CENTER>
+<H3><A NAME="9_50_3">Returns</A></H3>
+<H3><A NAME="9_50_4">Description</A></H3>
+<H3><A NAME="9_50_5">Example</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_50_6">See Also</A></H3>
+
+<!-- NEW PAGE ---->
+<H2><A NAME="httpPrintf">httpPrintf()</A></H2>
+<H3><A NAME="9_51_1">Usage</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_51_2">Arguments</A></H3>
+<CENTER>
+<TABLE BORDER WIDTH="80%">
+<TR><TH>Argument</TH><TH>Description</TH></TR>
+<TR><TD><TD></TR>
+</TABLE>
+</CENTER>
+<H3><A NAME="9_51_3">Returns</A></H3>
+<H3><A NAME="9_51_4">Description</A></H3>
+<H3><A NAME="9_51_5">Example</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_51_6">See Also</A></H3>
+
+<!-- NEW PAGE ---->
+<H2><A NAME="httpPut">httpPut()</A></H2>
+<H3><A NAME="9_52_1">Usage</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_52_2">Arguments</A></H3>
+<CENTER>
+<TABLE BORDER WIDTH="80%">
+<TR><TH>Argument</TH><TH>Description</TH></TR>
+<TR><TD><TD></TR>
+</TABLE>
+</CENTER>
+<H3><A NAME="9_52_3">Returns</A></H3>
+<H3><A NAME="9_52_4">Description</A></H3>
+<H3><A NAME="9_52_5">Example</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_52_6">See Also</A></H3>
+
+<!-- NEW PAGE ---->
+<H2><A NAME="httpRead">httpRead()</A></H2>
+<H3><A NAME="9_53_1">Usage</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_53_2">Arguments</A></H3>
+<CENTER>
+<TABLE BORDER WIDTH="80%">
+<TR><TH>Argument</TH><TH>Description</TH></TR>
+<TR><TD><TD></TR>
+</TABLE>
+</CENTER>
+<H3><A NAME="9_53_3">Returns</A></H3>
+<H3><A NAME="9_53_4">Description</A></H3>
+<H3><A NAME="9_53_5">Example</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_53_6">See Also</A></H3>
+
+<!-- NEW PAGE ---->
+<H2><A NAME="httpReconnect">httpReconnect()</A></H2>
+<H3><A NAME="9_54_1">Usage</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_54_2">Arguments</A></H3>
+<CENTER>
+<TABLE BORDER WIDTH="80%">
+<TR><TH>Argument</TH><TH>Description</TH></TR>
+<TR><TD><TD></TR>
+</TABLE>
+</CENTER>
+<H3><A NAME="9_54_3">Returns</A></H3>
+<H3><A NAME="9_54_4">Description</A></H3>
+<H3><A NAME="9_54_5">Example</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_54_6">See Also</A></H3>
+
+<!-- NEW PAGE ---->
+<H2><A NAME="httpSeparate">httpSeparate()</A></H2>
+<H3><A NAME="9_55_1">Usage</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_55_2">Arguments</A></H3>
+<CENTER>
+<TABLE BORDER WIDTH="80%">
+<TR><TH>Argument</TH><TH>Description</TH></TR>
+<TR><TD><TD></TR>
+</TABLE>
+</CENTER>
+<H3><A NAME="9_55_3">Returns</A></H3>
+<H3><A NAME="9_55_4">Description</A></H3>
+<H3><A NAME="9_55_5">Example</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_55_6">See Also</A></H3>
+
+<!-- NEW PAGE ---->
+<H2><A NAME="httpSetField">httpSetField()</A></H2>
+<H3><A NAME="9_56_1">Usage</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_56_2">Arguments</A></H3>
+<CENTER>
+<TABLE BORDER WIDTH="80%">
+<TR><TH>Argument</TH><TH>Description</TH></TR>
+<TR><TD><TD></TR>
+</TABLE>
+</CENTER>
+<H3><A NAME="9_56_3">Returns</A></H3>
+<H3><A NAME="9_56_4">Description</A></H3>
+<H3><A NAME="9_56_5">Example</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_56_6">See Also</A></H3>
+
+<!-- NEW PAGE ---->
+<H2><A NAME="httpTrace">httpTrace()</A></H2>
+<H3><A NAME="9_57_1">Usage</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_57_2">Arguments</A></H3>
+<CENTER>
+<TABLE BORDER WIDTH="80%">
+<TR><TH>Argument</TH><TH>Description</TH></TR>
+<TR><TD><TD></TR>
+</TABLE>
+</CENTER>
+<H3><A NAME="9_57_3">Returns</A></H3>
+<H3><A NAME="9_57_4">Description</A></H3>
+<H3><A NAME="9_57_5">Example</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_57_6">See Also</A></H3>
+
+<!-- NEW PAGE ---->
+<H2><A NAME="httpUpdate">httpUpdate()</A></H2>
+<H3><A NAME="9_58_1">Usage</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_58_2">Arguments</A></H3>
+<CENTER>
+<TABLE BORDER WIDTH="80%">
+<TR><TH>Argument</TH><TH>Description</TH></TR>
+<TR><TD><TD></TR>
+</TABLE>
+</CENTER>
+<H3><A NAME="9_58_3">Returns</A></H3>
+<H3><A NAME="9_58_4">Description</A></H3>
+<H3><A NAME="9_58_5">Example</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_58_6">See Also</A></H3>
+
+<!-- NEW PAGE ---->
+<H2><A NAME="httpWrite">httpWrite()</A></H2>
+<H3><A NAME="9_59_1">Usage</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_59_2">Arguments</A></H3>
+<CENTER>
+<TABLE BORDER WIDTH="80%">
+<TR><TH>Argument</TH><TH>Description</TH></TR>
+<TR><TD><TD></TR>
+</TABLE>
+</CENTER>
+<H3><A NAME="9_59_3">Returns</A></H3>
+<H3><A NAME="9_59_4">Description</A></H3>
+<H3><A NAME="9_59_5">Example</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_59_6">See Also</A></H3>
+
+<!-- NEW PAGE ---->
+<H2><A NAME="ippAddBoolean">ippAddBoolean()</A></H2>
+<H3><A NAME="9_60_1">Usage</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_60_2">Arguments</A></H3>
+<CENTER>
+<TABLE BORDER WIDTH="80%">
+<TR><TH>Argument</TH><TH>Description</TH></TR>
+<TR><TD><TD></TR>
+</TABLE>
+</CENTER>
+<H3><A NAME="9_60_3">Returns</A></H3>
+<H3><A NAME="9_60_4">Description</A></H3>
+<H3><A NAME="9_60_5">Example</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_60_6">See Also</A></H3>
+
+<!-- NEW PAGE ---->
+<H2><A NAME="ippAddBooleans">ippAddBooleans()</A></H2>
+<H3><A NAME="9_61_1">Usage</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_61_2">Arguments</A></H3>
+<CENTER>
+<TABLE BORDER WIDTH="80%">
+<TR><TH>Argument</TH><TH>Description</TH></TR>
+<TR><TD><TD></TR>
+</TABLE>
+</CENTER>
+<H3><A NAME="9_61_3">Returns</A></H3>
+<H3><A NAME="9_61_4">Description</A></H3>
+<H3><A NAME="9_61_5">Example</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_61_6">See Also</A></H3>
+
+<!-- NEW PAGE ---->
+<H2><A NAME="ippAddDate">ippAddDate()</A></H2>
+<H3><A NAME="9_62_1">Usage</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_62_2">Arguments</A></H3>
+<CENTER>
+<TABLE BORDER WIDTH="80%">
+<TR><TH>Argument</TH><TH>Description</TH></TR>
+<TR><TD><TD></TR>
+</TABLE>
+</CENTER>
+<H3><A NAME="9_62_3">Returns</A></H3>
+<H3><A NAME="9_62_4">Description</A></H3>
+<H3><A NAME="9_62_5">Example</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_62_6">See Also</A></H3>
+
+<!-- NEW PAGE ---->
+<H2><A NAME="ippAddInteger">ippAddInteger()</A></H2>
+<H3><A NAME="9_63_1">Usage</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_63_2">Arguments</A></H3>
+<CENTER>
+<TABLE BORDER WIDTH="80%">
+<TR><TH>Argument</TH><TH>Description</TH></TR>
+<TR><TD><TD></TR>
+</TABLE>
+</CENTER>
+<H3><A NAME="9_63_3">Returns</A></H3>
+<H3><A NAME="9_63_4">Description</A></H3>
+<H3><A NAME="9_63_5">Example</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_63_6">See Also</A></H3>
+
+<!-- NEW PAGE ---->
+<H2><A NAME="ippAddIntegers">ippAddIntegers()</A></H2>
+<H3><A NAME="9_64_1">Usage</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_64_2">Arguments</A></H3>
+<CENTER>
+<TABLE BORDER WIDTH="80%">
+<TR><TH>Argument</TH><TH>Description</TH></TR>
+<TR><TD><TD></TR>
+</TABLE>
+</CENTER>
+<H3><A NAME="9_64_3">Returns</A></H3>
+<H3><A NAME="9_64_4">Description</A></H3>
+<H3><A NAME="9_64_5">Example</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_64_6">See Also</A></H3>
+
+<!-- NEW PAGE ---->
+<H2><A NAME="ippAddRange">ippAddRange()</A></H2>
+<H3><A NAME="9_65_1">Usage</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_65_2">Arguments</A></H3>
+<CENTER>
+<TABLE BORDER WIDTH="80%">
+<TR><TH>Argument</TH><TH>Description</TH></TR>
+<TR><TD><TD></TR>
+</TABLE>
+</CENTER>
+<H3><A NAME="9_65_3">Returns</A></H3>
+<H3><A NAME="9_65_4">Description</A></H3>
+<H3><A NAME="9_65_5">Example</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_65_6">See Also</A></H3>
+
+<!-- NEW PAGE ---->
+<H2><A NAME="ippAddRanges">ippAddRanges()</A></H2>
+<H3><A NAME="9_66_1">Usage</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_66_2">Arguments</A></H3>
+<CENTER>
+<TABLE BORDER WIDTH="80%">
+<TR><TH>Argument</TH><TH>Description</TH></TR>
+<TR><TD><TD></TR>
+</TABLE>
+</CENTER>
+<H3><A NAME="9_66_3">Returns</A></H3>
+<H3><A NAME="9_66_4">Description</A></H3>
+<H3><A NAME="9_66_5">Example</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_66_6">See Also</A></H3>
+
+<!-- NEW PAGE ---->
+<H2><A NAME="ippAddResolution">ippAddResolution()</A></H2>
+<H3><A NAME="9_67_1">Usage</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_67_2">Arguments</A></H3>
+<CENTER>
+<TABLE BORDER WIDTH="80%">
+<TR><TH>Argument</TH><TH>Description</TH></TR>
+<TR><TD><TD></TR>
+</TABLE>
+</CENTER>
+<H3><A NAME="9_67_3">Returns</A></H3>
+<H3><A NAME="9_67_4">Description</A></H3>
+<H3><A NAME="9_67_5">Example</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_67_6">See Also</A></H3>
+
+<!-- NEW PAGE ---->
+<H2><A NAME="ippAddResolutions">ippAddResolutions()</A></H2>
+<H3><A NAME="9_68_1">Usage</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_68_2">Arguments</A></H3>
+<CENTER>
+<TABLE BORDER WIDTH="80%">
+<TR><TH>Argument</TH><TH>Description</TH></TR>
+<TR><TD><TD></TR>
+</TABLE>
+</CENTER>
+<H3><A NAME="9_68_3">Returns</A></H3>
+<H3><A NAME="9_68_4">Description</A></H3>
+<H3><A NAME="9_68_5">Example</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_68_6">See Also</A></H3>
+
+<!-- NEW PAGE ---->
+<H2><A NAME="ippAddSeparator">ippAddSeparator()</A></H2>
+<H3><A NAME="9_69_1">Usage</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_69_2">Arguments</A></H3>
+<CENTER>
+<TABLE BORDER WIDTH="80%">
+<TR><TH>Argument</TH><TH>Description</TH></TR>
+<TR><TD><TD></TR>
+</TABLE>
+</CENTER>
+<H3><A NAME="9_69_3">Returns</A></H3>
+<H3><A NAME="9_69_4">Description</A></H3>
+<H3><A NAME="9_69_5">Example</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_69_6">See Also</A></H3>
+
+<!-- NEW PAGE ---->
+<H2><A NAME="ippAddString">ippAddString()</A></H2>
+<H3><A NAME="9_70_1">Usage</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_70_2">Arguments</A></H3>
+<CENTER>
+<TABLE BORDER WIDTH="80%">
+<TR><TH>Argument</TH><TH>Description</TH></TR>
+<TR><TD><TD></TR>
+</TABLE>
+</CENTER>
+<H3><A NAME="9_70_3">Returns</A></H3>
+<H3><A NAME="9_70_4">Description</A></H3>
+<H3><A NAME="9_70_5">Example</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_70_6">See Also</A></H3>
+
+<!-- NEW PAGE ---->
+<H2><A NAME="ippAddStrings">ippAddStrings()</A></H2>
+<H3><A NAME="9_71_1">Usage</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_71_2">Arguments</A></H3>
+<CENTER>
+<TABLE BORDER WIDTH="80%">
+<TR><TH>Argument</TH><TH>Description</TH></TR>
+<TR><TD><TD></TR>
+</TABLE>
+</CENTER>
+<H3><A NAME="9_71_3">Returns</A></H3>
+<H3><A NAME="9_71_4">Description</A></H3>
+<H3><A NAME="9_71_5">Example</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_71_6">See Also</A></H3>
+
+<!-- NEW PAGE ---->
+<H2><A NAME="ippDateToTime">ippDateToTime()</A></H2>
+<H3><A NAME="9_72_1">Usage</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_72_2">Arguments</A></H3>
+<CENTER>
+<TABLE BORDER WIDTH="80%">
+<TR><TH>Argument</TH><TH>Description</TH></TR>
+<TR><TD><TD></TR>
+</TABLE>
+</CENTER>
+<H3><A NAME="9_72_3">Returns</A></H3>
+<H3><A NAME="9_72_4">Description</A></H3>
+<H3><A NAME="9_72_5">Example</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_72_6">See Also</A></H3>
+
+<!-- NEW PAGE ---->
+<H2><A NAME="ippDelete">ippDelete()</A></H2>
+<H3><A NAME="9_73_1">Usage</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_73_2">Arguments</A></H3>
+<CENTER>
+<TABLE BORDER WIDTH="80%">
+<TR><TH>Argument</TH><TH>Description</TH></TR>
+<TR><TD><TD></TR>
+</TABLE>
+</CENTER>
+<H3><A NAME="9_73_3">Returns</A></H3>
+<H3><A NAME="9_73_4">Description</A></H3>
+<H3><A NAME="9_73_5">Example</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_73_6">See Also</A></H3>
+
+<!-- NEW PAGE ---->
+<H2><A NAME="ippFindAttribute">ippFindAttribute()</A></H2>
+<H3><A NAME="9_74_1">Usage</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_74_2">Arguments</A></H3>
+<CENTER>
+<TABLE BORDER WIDTH="80%">
+<TR><TH>Argument</TH><TH>Description</TH></TR>
+<TR><TD><TD></TR>
+</TABLE>
+</CENTER>
+<H3><A NAME="9_74_3">Returns</A></H3>
+<H3><A NAME="9_74_4">Description</A></H3>
+<H3><A NAME="9_74_5">Example</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_74_6">See Also</A></H3>
+
+<!-- NEW PAGE ---->
+<H2><A NAME="ippLength">ippLength()</A></H2>
+<H3><A NAME="9_75_1">Usage</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_75_2">Arguments</A></H3>
+<CENTER>
+<TABLE BORDER WIDTH="80%">
+<TR><TH>Argument</TH><TH>Description</TH></TR>
+<TR><TD><TD></TR>
+</TABLE>
+</CENTER>
+<H3><A NAME="9_75_3">Returns</A></H3>
+<H3><A NAME="9_75_4">Description</A></H3>
+<H3><A NAME="9_75_5">Example</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_75_6">See Also</A></H3>
+
+<!-- NEW PAGE ---->
+<H2><A NAME="ippNew">ippNew()</A></H2>
+<H3><A NAME="9_76_1">Usage</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_76_2">Arguments</A></H3>
+<CENTER>
+<TABLE BORDER WIDTH="80%">
+<TR><TH>Argument</TH><TH>Description</TH></TR>
+<TR><TD><TD></TR>
+</TABLE>
+</CENTER>
+<H3><A NAME="9_76_3">Returns</A></H3>
+<H3><A NAME="9_76_4">Description</A></H3>
+<H3><A NAME="9_76_5">Example</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_76_6">See Also</A></H3>
+
+<!-- NEW PAGE ---->
+<H2><A NAME="ippPort">ippPort()</A></H2>
+<H3><A NAME="9_77_1">Usage</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_77_2">Arguments</A></H3>
+<CENTER>
+<TABLE BORDER WIDTH="80%">
+<TR><TH>Argument</TH><TH>Description</TH></TR>
+<TR><TD><TD></TR>
+</TABLE>
+</CENTER>
+<H3><A NAME="9_77_3">Returns</A></H3>
+<H3><A NAME="9_77_4">Description</A></H3>
+<H3><A NAME="9_77_5">Example</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_77_6">See Also</A></H3>
+
+<!-- NEW PAGE ---->
+<H2><A NAME="ippRead">ippRead()</A></H2>
+<H3><A NAME="9_78_1">Usage</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_78_2">Arguments</A></H3>
+<CENTER>
+<TABLE BORDER WIDTH="80%">
+<TR><TH>Argument</TH><TH>Description</TH></TR>
+<TR><TD><TD></TR>
+</TABLE>
+</CENTER>
+<H3><A NAME="9_78_3">Returns</A></H3>
+<H3><A NAME="9_78_4">Description</A></H3>
+<H3><A NAME="9_78_5">Example</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_78_6">See Also</A></H3>
+
+<!-- NEW PAGE ---->
+<H2><A NAME="ippTimeToDate">ippTimeToDate()</A></H2>
+<H3><A NAME="9_79_1">Usage</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_79_2">Arguments</A></H3>
+<CENTER>
+<TABLE BORDER WIDTH="80%">
+<TR><TH>Argument</TH><TH>Description</TH></TR>
+<TR><TD><TD></TR>
+</TABLE>
+</CENTER>
+<H3><A NAME="9_79_3">Returns</A></H3>
+<H3><A NAME="9_79_4">Description</A></H3>
+<H3><A NAME="9_79_5">Example</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_79_6">See Also</A></H3>
+
+<!-- NEW PAGE ---->
+<H2><A NAME="ippWrite">ippWrite()</A></H2>
+<H3><A NAME="9_80_1">Usage</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_80_2">Arguments</A></H3>
+<CENTER>
+<TABLE BORDER WIDTH="80%">
+<TR><TH>Argument</TH><TH>Description</TH></TR>
+<TR><TD><TD></TR>
+</TABLE>
+</CENTER>
+<H3><A NAME="9_80_3">Returns</A></H3>
+<H3><A NAME="9_80_4">Description</A></H3>
+<H3><A NAME="9_80_5">Example</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_80_6">See Also</A></H3>
+
+<!-- NEW PAGE ---->
+<H2><A NAME="ppdClose">ppdClose()</A></H2>
+<H3><A NAME="9_81_1">Usage</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_81_2">Arguments</A></H3>
+<CENTER>
+<TABLE BORDER WIDTH="80%">
+<TR><TH>Argument</TH><TH>Description</TH></TR>
+<TR><TD><TD></TR>
+</TABLE>
+</CENTER>
+<H3><A NAME="9_81_3">Returns</A></H3>
+<H3><A NAME="9_81_4">Description</A></H3>
+<H3><A NAME="9_81_5">Example</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_81_6">See Also</A></H3>
+
+<!-- NEW PAGE ---->
+<H2><A NAME="ppdConflicts">ppdConflicts()</A></H2>
+<H3><A NAME="9_82_1">Usage</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_82_2">Arguments</A></H3>
+<CENTER>
+<TABLE BORDER WIDTH="80%">
+<TR><TH>Argument</TH><TH>Description</TH></TR>
+<TR><TD><TD></TR>
+</TABLE>
+</CENTER>
+<H3><A NAME="9_82_3">Returns</A></H3>
+<H3><A NAME="9_82_4">Description</A></H3>
+<H3><A NAME="9_82_5">Example</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_82_6">See Also</A></H3>
+
+<!-- NEW PAGE ---->
+<H2><A NAME="pddEmitFd">pddEmitFd()</A></H2>
+<H3><A NAME="9_83_1">Usage</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_83_2">Arguments</A></H3>
+<CENTER>
+<TABLE BORDER WIDTH="80%">
+<TR><TH>Argument</TH><TH>Description</TH></TR>
+<TR><TD><TD></TR>
+</TABLE>
+</CENTER>
+<H3><A NAME="9_83_3">Returns</A></H3>
+<H3><A NAME="9_83_4">Description</A></H3>
+<H3><A NAME="9_83_5">Example</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_83_6">See Also</A></H3>
+
+<!-- NEW PAGE ---->
+<H2><A NAME="ppdEmit">ppdEmit()</A></H2>
+<H3><A NAME="9_84_1">Usage</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_84_2">Arguments</A></H3>
+<CENTER>
+<TABLE BORDER WIDTH="80%">
+<TR><TH>Argument</TH><TH>Description</TH></TR>
+<TR><TD><TD></TR>
+</TABLE>
+</CENTER>
+<H3><A NAME="9_84_3">Returns</A></H3>
+<H3><A NAME="9_84_4">Description</A></H3>
+<H3><A NAME="9_84_5">Example</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_84_6">See Also</A></H3>
+
+<!-- NEW PAGE ---->
+<H2><A NAME="ppdFindChoice">ppdFindChoice()</A></H2>
+<H3><A NAME="9_85_1">Usage</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_85_2">Arguments</A></H3>
+<CENTER>
+<TABLE BORDER WIDTH="80%">
+<TR><TH>Argument</TH><TH>Description</TH></TR>
+<TR><TD><TD></TR>
+</TABLE>
+</CENTER>
+<H3><A NAME="9_85_3">Returns</A></H3>
+<H3><A NAME="9_85_4">Description</A></H3>
+<H3><A NAME="9_85_5">Example</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_85_6">See Also</A></H3>
+
+<!-- NEW PAGE ---->
+<H2><A NAME="ppdFindMarkedChoice">ppdFindMarkedChoice()</A></H2>
+<H3><A NAME="9_86_1">Usage</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_86_2">Arguments</A></H3>
+<CENTER>
+<TABLE BORDER WIDTH="80%">
+<TR><TH>Argument</TH><TH>Description</TH></TR>
+<TR><TD><TD></TR>
+</TABLE>
+</CENTER>
+<H3><A NAME="9_86_3">Returns</A></H3>
+<H3><A NAME="9_86_4">Description</A></H3>
+<H3><A NAME="9_86_5">Example</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_86_6">See Also</A></H3>
+
+<!-- NEW PAGE ---->
+<H2><A NAME="ppdFindOption">ppdFindOption()</A></H2>
+<H3><A NAME="9_87_1">Usage</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_87_2">Arguments</A></H3>
+<CENTER>
+<TABLE BORDER WIDTH="80%">
+<TR><TH>Argument</TH><TH>Description</TH></TR>
+<TR><TD><TD></TR>
+</TABLE>
+</CENTER>
+<H3><A NAME="9_87_3">Returns</A></H3>
+<H3><A NAME="9_87_4">Description</A></H3>
+<H3><A NAME="9_87_5">Example</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_87_6">See Also</A></H3>
+
+<!-- NEW PAGE ---->
+<H2><A NAME="ppdIsMarked">ppdIsMarked()</A></H2>
+<H3><A NAME="9_88_1">Usage</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_88_2">Arguments</A></H3>
+<CENTER>
+<TABLE BORDER WIDTH="80%">
+<TR><TH>Argument</TH><TH>Description</TH></TR>
+<TR><TD><TD></TR>
+</TABLE>
+</CENTER>
+<H3><A NAME="9_88_3">Returns</A></H3>
+<H3><A NAME="9_88_4">Description</A></H3>
+<H3><A NAME="9_88_5">Example</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_88_6">See Also</A></H3>
+
+<!-- NEW PAGE ---->
+<H2><A NAME="ppdMarkDefaults">ppdMarkDefaults()</A></H2>
+<H3><A NAME="9_89_1">Usage</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_89_2">Arguments</A></H3>
+<CENTER>
+<TABLE BORDER WIDTH="80%">
+<TR><TH>Argument</TH><TH>Description</TH></TR>
+<TR><TD><TD></TR>
+</TABLE>
+</CENTER>
+<H3><A NAME="9_89_3">Returns</A></H3>
+<H3><A NAME="9_89_4">Description</A></H3>
+<H3><A NAME="9_89_5">Example</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_89_6">See Also</A></H3>
+
+<!-- NEW PAGE ---->
+<H2><A NAME="ppdMarkOption">ppdMarkOption()</A></H2>
+<H3><A NAME="9_90_1">Usage</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_90_2">Arguments</A></H3>
+<CENTER>
+<TABLE BORDER WIDTH="80%">
+<TR><TH>Argument</TH><TH>Description</TH></TR>
+<TR><TD><TD></TR>
+</TABLE>
+</CENTER>
+<H3><A NAME="9_90_3">Returns</A></H3>
+<H3><A NAME="9_90_4">Description</A></H3>
+<H3><A NAME="9_90_5">Example</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_90_6">See Also</A></H3>
+
+<!-- NEW PAGE ---->
+<H2><A NAME="ppdOpenFd">ppdOpenFd()</A></H2>
+<H3><A NAME="9_91_1">Usage</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_91_2">Arguments</A></H3>
+<CENTER>
+<TABLE BORDER WIDTH="80%">
+<TR><TH>Argument</TH><TH>Description</TH></TR>
+<TR><TD><TD></TR>
+</TABLE>
+</CENTER>
+<H3><A NAME="9_91_3">Returns</A></H3>
+<H3><A NAME="9_91_4">Description</A></H3>
+<H3><A NAME="9_91_5">Example</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_91_6">See Also</A></H3>
+
+<!-- NEW PAGE ---->
+<H2><A NAME="ppdOpenFile">ppdOpenFile()</A></H2>
+<H3><A NAME="9_92_1">Usage</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_92_2">Arguments</A></H3>
+<CENTER>
+<TABLE BORDER WIDTH="80%">
+<TR><TH>Argument</TH><TH>Description</TH></TR>
+<TR><TD><TD></TR>
+</TABLE>
+</CENTER>
+<H3><A NAME="9_92_3">Returns</A></H3>
+<H3><A NAME="9_92_4">Description</A></H3>
+<H3><A NAME="9_92_5">Example</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_92_6">See Also</A></H3>
+
+<!-- NEW PAGE ---->
+<H2><A NAME="ppdOpen">ppdOpen()</A></H2>
+<H3><A NAME="9_93_1">Usage</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_93_2">Arguments</A></H3>
+<CENTER>
+<TABLE BORDER WIDTH="80%">
+<TR><TH>Argument</TH><TH>Description</TH></TR>
+<TR><TD><TD></TR>
+</TABLE>
+</CENTER>
+<H3><A NAME="9_93_3">Returns</A></H3>
+<H3><A NAME="9_93_4">Description</A></H3>
+<H3><A NAME="9_93_5">Example</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_93_6">See Also</A></H3>
+
+<!-- NEW PAGE ---->
+<H2><A NAME="ppdPageLength">ppdPageLength()</A></H2>
+<H3><A NAME="9_94_1">Usage</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_94_2">Arguments</A></H3>
+<CENTER>
+<TABLE BORDER WIDTH="80%">
+<TR><TH>Argument</TH><TH>Description</TH></TR>
+<TR><TD><TD></TR>
+</TABLE>
+</CENTER>
+<H3><A NAME="9_94_3">Returns</A></H3>
+<H3><A NAME="9_94_4">Description</A></H3>
+<H3><A NAME="9_94_5">Example</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_94_6">See Also</A></H3>
+
+<!-- NEW PAGE ---->
+<H2><A NAME="ppdPageSize">ppdPageSize()</A></H2>
+<H3><A NAME="9_95_1">Usage</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_95_2">Arguments</A></H3>
+<CENTER>
+<TABLE BORDER WIDTH="80%">
+<TR><TH>Argument</TH><TH>Description</TH></TR>
+<TR><TD><TD></TR>
+</TABLE>
+</CENTER>
+<H3><A NAME="9_95_3">Returns</A></H3>
+<H3><A NAME="9_95_4">Description</A></H3>
+<H3><A NAME="9_95_5">Example</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_95_6">See Also</A></H3>
+
+<!-- NEW PAGE ---->
+<H2><A NAME="ppdPageWidth">ppdPageWidth()</A></H2>
+<H3><A NAME="9_96_1">Usage</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_96_2">Arguments</A></H3>
+<CENTER>
+<TABLE BORDER WIDTH="80%">
+<TR><TH>Argument</TH><TH>Description</TH></TR>
+<TR><TD><TD></TR>
+</TABLE>
+</CENTER>
+<H3><A NAME="9_96_3">Returns</A></H3>
+<H3><A NAME="9_96_4">Description</A></H3>
+<H3><A NAME="9_96_5">Example</A></H3>
+<PRE>
+</PRE>
+<H3><A NAME="9_96_6">See Also</A></H3>
+</BODY>
+</HTML>
diff --git a/doc/spm.pdf b/doc/spm.pdf
new file mode 100644
index 000000000..3c44264cc
--- /dev/null
+++ b/doc/spm.pdf
@@ -0,0 +1,7676 @@
+%PDF-1.2
+%âãÏÓ
+1 0 obj<</Producer(htmldoc 1.8.6 Copyright 1997-2000 Easy Software Products, All Rights Reserved.)/CreationDate(D:20000407154536Z)/Title(DRAFT - CUPS Software Programmers Manual)/Author(Easy Software Products)>>endobj
+2 0 obj<</Type/Encoding/Differences[ 32/space/exclam/quotedbl/numbersign/dollar/percent/ampersand/quotesingle/parenleft/parenright/asterisk/plus/comma/minus/period/slash/zero/one/two/three/four/five/six/seven/eight/nine/colon/semicolon/less/equal/greater/question/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/grave/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 160/space/exclamdown/cent/sterling/currency/yen/brokenbar/section/dieresis/copyright/ordfeminine/guillemotleft/logicalnot/hyphen/registered/macron/degree/plusminus/twosuperior/threesuperior/acute/mu/paragraph/periodcentered/cedilla/onesuperior/ordmasculine/guillemotright/onequarter/onehalf/threequarters/questiondown/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]>>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/Helvetica-Oblique/Encoding 2 0 R>>endobj
+10 0 obj<</Type/Font/Subtype/Type1/BaseFont/Symbol>>endobj
+11 0 obj<</S/URI/URI(http://www.easysw.com)>>endobj
+12 0 obj<</Subtype/Link/Rect[157.2 307.6 262.3 320.6]/Border[0 0 0]/A 11 0 R>>endobj
+13 0 obj[12 0 R
+]endobj
+14 0 obj<</S/URI/URI(#cups_option_t)>>endobj
+15 0 obj<</Subtype/Link/Rect[36.0 197.4 106.2 206.4]/Border[0 0 0]/A 14 0 R>>endobj
+16 0 obj[15 0 R
+]endobj
+17 0 obj<</Subtype/Link/Rect[72.0 688.8 184.2 701.8]/Border[0 0 0]/Dest[1794 0 R/XYZ null 799 0]>>endobj
+18 0 obj<</Subtype/Link/Rect[189.7 688.8 288.7 701.8]/Border[0 0 0]/Dest[1803 0 R/XYZ null 799 0]>>endobj
+19 0 obj<</Subtype/Link/Rect[294.2 688.8 413.0 701.8]/Border[0 0 0]/Dest[1839 0 R/XYZ null 799 0]>>endobj
+20 0 obj[17 0 R
+18 0 R
+19 0 R
+]endobj
+21 0 obj<</Subtype/Link/Rect[346.7 437.4 445.7 450.4]/Border[0 0 0]/Dest[1833 0 R/XYZ null 799 0]>>endobj
+22 0 obj<</Subtype/Link/Rect[36.0 240.6 135.0 253.6]/Border[0 0 0]/Dest[1833 0 R/XYZ null 799 0]>>endobj
+23 0 obj<</Subtype/Link/Rect[140.5 240.6 239.5 253.6]/Border[0 0 0]/Dest[1842 0 R/XYZ null 799 0]>>endobj
+24 0 obj[21 0 R
+22 0 R
+23 0 R
+]endobj
+25 0 obj<</Subtype/Link/Rect[72.0 362.2 171.0 375.2]/Border[0 0 0]/Dest[1833 0 R/XYZ null 799 0]>>endobj
+26 0 obj<</S/URI/URI(#http_t)>>endobj
+27 0 obj<</Subtype/Link/Rect[72.0 199.0 104.4 210.0]/Border[0 0 0]/A 26 0 R>>endobj
+28 0 obj<</S/URI/URI(#cups_lang_t)>>endobj
+29 0 obj<</Subtype/Link/Rect[72.0 188.2 131.4 199.2]/Border[0 0 0]/A 28 0 R>>endobj
+30 0 obj<</S/URI/URI(#ipp_t)>>endobj
+31 0 obj<</Subtype/Link/Rect[72.0 177.4 99.0 188.4]/Border[0 0 0]/A 30 0 R>>endobj
+32 0 obj<</Subtype/Link/Rect[131.4 112.6 223.2 123.6]/Border[0 0 0]/Dest[1815 0 R/XYZ null 799 0]>>endobj
+33 0 obj<</Subtype/Link/Rect[131.4 80.2 174.6 91.2]/Border[0 0 0]/Dest[2019 0 R/XYZ null 799 0]>>endobj
+34 0 obj[25 0 R
+27 0 R
+29 0 R
+31 0 R
+32 0 R
+33 0 R
+]endobj
+35 0 obj<</Subtype/Link/Rect[36.0 688.8 100.8 699.8]/Border[0 0 0]/Dest[2001 0 R/XYZ null 799 0]>>endobj
+36 0 obj<</Subtype/Link/Rect[257.4 678.0 343.8 689.0]/Border[0 0 0]/Dest[1818 0 R/XYZ null 799 0]>>endobj
+37 0 obj<</Subtype/Link/Rect[138.6 570.0 192.6 581.0]/Border[0 0 0]/Dest[1881 0 R/XYZ null 799 0]>>endobj
+38 0 obj<</Subtype/Link/Rect[36.0 477.6 148.2 490.6]/Border[0 0 0]/Dest[1815 0 R/XYZ null 799 0]>>endobj
+39 0 obj<</Subtype/Link/Rect[153.7 477.6 272.5 490.6]/Border[0 0 0]/Dest[1818 0 R/XYZ null 799 0]>>endobj
+40 0 obj<</Subtype/Link/Rect[278.0 477.6 344.0 490.6]/Border[0 0 0]/Dest[1881 0 R/XYZ null 799 0]>>endobj
+41 0 obj<</Subtype/Link/Rect[349.5 477.6 435.3 490.6]/Border[0 0 0]/Dest[1896 0 R/XYZ null 799 0]>>endobj
+42 0 obj<</Subtype/Link/Rect[36.0 464.4 128.4 477.4]/Border[0 0 0]/Dest[2001 0 R/XYZ null 799 0]>>endobj
+43 0 obj<</Subtype/Link/Rect[133.9 464.4 186.7 477.4]/Border[0 0 0]/Dest[2019 0 R/XYZ null 799 0]>>endobj
+44 0 obj[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
+]endobj
+45 0 obj<</Subtype/Link/Rect[72.0 393.2 171.0 406.2]/Border[0 0 0]/Dest[1833 0 R/XYZ null 799 0]>>endobj
+46 0 obj<</S/URI/URI(#http_t)>>endobj
+47 0 obj<</Subtype/Link/Rect[72.0 243.2 104.4 254.2]/Border[0 0 0]/A 46 0 R>>endobj
+48 0 obj<</S/URI/URI(#cups_lang_t)>>endobj
+49 0 obj<</Subtype/Link/Rect[72.0 232.4 131.4 243.4]/Border[0 0 0]/A 48 0 R>>endobj
+50 0 obj<</S/URI/URI(#ipp_t)>>endobj
+51 0 obj<</Subtype/Link/Rect[72.0 221.6 99.0 232.6]/Border[0 0 0]/A 50 0 R>>endobj
+52 0 obj<</Subtype/Link/Rect[131.4 156.8 223.2 167.8]/Border[0 0 0]/Dest[1815 0 R/XYZ null 799 0]>>endobj
+53 0 obj<</Subtype/Link/Rect[131.4 124.4 174.6 135.4]/Border[0 0 0]/Dest[2019 0 R/XYZ null 799 0]>>endobj
+54 0 obj<</Subtype/Link/Rect[72.0 59.6 136.8 70.6]/Border[0 0 0]/Dest[2001 0 R/XYZ null 799 0]>>endobj
+55 0 obj[45 0 R
+47 0 R
+49 0 R
+51 0 R
+52 0 R
+53 0 R
+54 0 R
+]endobj
+56 0 obj<</Subtype/Link/Rect[257.4 721.2 343.8 732.2]/Border[0 0 0]/Dest[1818 0 R/XYZ null 799 0]>>endobj
+57 0 obj<</Subtype/Link/Rect[36.0 553.2 148.2 566.2]/Border[0 0 0]/Dest[1815 0 R/XYZ null 799 0]>>endobj
+58 0 obj<</Subtype/Link/Rect[153.7 553.2 272.5 566.2]/Border[0 0 0]/Dest[1818 0 R/XYZ null 799 0]>>endobj
+59 0 obj<</Subtype/Link/Rect[278.0 553.2 344.0 566.2]/Border[0 0 0]/Dest[1881 0 R/XYZ null 799 0]>>endobj
+60 0 obj<</Subtype/Link/Rect[349.5 553.2 435.3 566.2]/Border[0 0 0]/Dest[1896 0 R/XYZ null 799 0]>>endobj
+61 0 obj<</Subtype/Link/Rect[36.0 540.0 128.4 553.0]/Border[0 0 0]/Dest[2001 0 R/XYZ null 799 0]>>endobj
+62 0 obj<</Subtype/Link/Rect[133.9 540.0 186.7 553.0]/Border[0 0 0]/Dest[2019 0 R/XYZ null 799 0]>>endobj
+63 0 obj[56 0 R
+57 0 R
+58 0 R
+59 0 R
+60 0 R
+61 0 R
+62 0 R
+]endobj
+64 0 obj<</Subtype/Link/Rect[72.0 235.8 149.0 248.8]/Border[0 0 0]/Dest[1773 0 R/XYZ null 799 0]>>endobj
+65 0 obj<</Subtype/Link/Rect[154.5 235.8 228.4 248.8]/Border[0 0 0]/Dest[1803 0 R/XYZ null 799 0]>>endobj
+66 0 obj<</Subtype/Link/Rect[233.9 235.8 320.1 248.8]/Border[0 0 0]/Dest[1836 0 R/XYZ null 799 0]>>endobj
+67 0 obj<</Subtype/Link/Rect[325.6 235.8 411.7 248.8]/Border[0 0 0]/Dest[1839 0 R/XYZ null 799 0]>>endobj
+68 0 obj[64 0 R
+65 0 R
+66 0 R
+67 0 R
+]endobj
+69 0 obj<</Subtype/Link/Rect[36.0 85.6 117.9 98.6]/Border[0 0 0]/Dest[1800 0 R/XYZ null 799 0]>>endobj
+70 0 obj<</Subtype/Link/Rect[117.9 85.6 195.5 98.6]/Border[0 0 0]/Dest[1812 0 R/XYZ null 799 0]>>endobj
+71 0 obj[69 0 R
+70 0 R
+]endobj
+72 0 obj<</Subtype/Link/Rect[72.0 358.8 153.9 371.8]/Border[0 0 0]/Dest[1797 0 R/XYZ null 799 0]>>endobj
+73 0 obj<</Subtype/Link/Rect[153.9 358.8 231.5 371.8]/Border[0 0 0]/Dest[1812 0 R/XYZ null 799 0]>>endobj
+74 0 obj[72 0 R
+73 0 R
+]endobj
+75 0 obj<</Subtype/Link/Rect[36.0 165.2 113.0 178.2]/Border[0 0 0]/Dest[1773 0 R/XYZ null 799 0]>>endobj
+76 0 obj<</Subtype/Link/Rect[118.5 165.2 200.4 178.2]/Border[0 0 0]/Dest[1794 0 R/XYZ null 799 0]>>endobj
+77 0 obj<</Subtype/Link/Rect[205.9 165.2 292.0 178.2]/Border[0 0 0]/Dest[1836 0 R/XYZ null 799 0]>>endobj
+78 0 obj<</Subtype/Link/Rect[297.5 165.2 383.7 178.2]/Border[0 0 0]/Dest[1839 0 R/XYZ null 799 0]>>endobj
+79 0 obj[75 0 R
+76 0 R
+77 0 R
+78 0 R
+]endobj
+80 0 obj<</Subtype/Link/Rect[72.0 215.2 128.2 228.2]/Border[0 0 0]/Dest[1875 0 R/XYZ null 799 0]>>endobj
+81 0 obj<</S/URI/URI(#cupsUser(\))>>endobj
+82 0 obj<</Subtype/Link/Rect[133.7 215.2 182.0 228.2]/Border[0 0 0]/A 81 0 R>>endobj
+83 0 obj[80 0 R
+82 0 R
+]endobj
+84 0 obj<</Subtype/Link/Rect[72.0 85.6 153.9 98.6]/Border[0 0 0]/Dest[1797 0 R/XYZ null 799 0]>>endobj
+85 0 obj<</Subtype/Link/Rect[153.9 85.6 230.2 98.6]/Border[0 0 0]/Dest[1800 0 R/XYZ null 799 0]>>endobj
+86 0 obj[84 0 R
+85 0 R
+]endobj
+87 0 obj<</Subtype/Link/Rect[36.0 243.6 128.3 256.6]/Border[0 0 0]/Dest[1818 0 R/XYZ null 799 0]>>endobj
+88 0 obj<</Subtype/Link/Rect[133.8 243.6 208.3 256.6]/Border[0 0 0]/Dest[1821 0 R/XYZ null 799 0]>>endobj
+89 0 obj<</Subtype/Link/Rect[213.8 243.6 283.4 256.6]/Border[0 0 0]/Dest[1824 0 R/XYZ null 799 0]>>endobj
+90 0 obj<</Subtype/Link/Rect[288.9 243.6 354.9 256.6]/Border[0 0 0]/Dest[1827 0 R/XYZ null 799 0]>>endobj
+91 0 obj<</Subtype/Link/Rect[360.4 243.6 437.4 256.6]/Border[0 0 0]/Dest[1830 0 R/XYZ null 799 0]>>endobj
+92 0 obj[87 0 R
+88 0 R
+89 0 R
+90 0 R
+91 0 R
+]endobj
+93 0 obj<</Subtype/Link/Rect[72.0 172.0 155.1 185.0]/Border[0 0 0]/Dest[1815 0 R/XYZ null 799 0]>>endobj
+94 0 obj<</Subtype/Link/Rect[160.6 172.0 235.1 185.0]/Border[0 0 0]/Dest[1821 0 R/XYZ null 799 0]>>endobj
+95 0 obj<</Subtype/Link/Rect[240.6 172.0 310.3 185.0]/Border[0 0 0]/Dest[1824 0 R/XYZ null 799 0]>>endobj
+96 0 obj<</Subtype/Link/Rect[315.8 172.0 381.7 185.0]/Border[0 0 0]/Dest[1827 0 R/XYZ null 799 0]>>endobj
+97 0 obj<</Subtype/Link/Rect[387.2 172.0 464.2 185.0]/Border[0 0 0]/Dest[1830 0 R/XYZ null 799 0]>>endobj
+98 0 obj[93 0 R
+94 0 R
+95 0 R
+96 0 R
+97 0 R
+]endobj
+99 0 obj<</Subtype/Link/Rect[36.0 410.4 119.1 423.4]/Border[0 0 0]/Dest[1815 0 R/XYZ null 799 0]>>endobj
+100 0 obj<</Subtype/Link/Rect[124.6 410.4 216.8 423.4]/Border[0 0 0]/Dest[1818 0 R/XYZ null 799 0]>>endobj
+101 0 obj<</Subtype/Link/Rect[222.3 410.4 292.0 423.4]/Border[0 0 0]/Dest[1824 0 R/XYZ null 799 0]>>endobj
+102 0 obj<</Subtype/Link/Rect[297.5 410.4 363.5 423.4]/Border[0 0 0]/Dest[1827 0 R/XYZ null 799 0]>>endobj
+103 0 obj<</Subtype/Link/Rect[369.0 410.4 445.9 423.4]/Border[0 0 0]/Dest[1830 0 R/XYZ null 799 0]>>endobj
+104 0 obj[99 0 R
+100 0 R
+101 0 R
+102 0 R
+103 0 R
+]endobj
+105 0 obj<</Subtype/Link/Rect[72.0 299.2 155.1 312.2]/Border[0 0 0]/Dest[1815 0 R/XYZ null 799 0]>>endobj
+106 0 obj<</Subtype/Link/Rect[160.6 299.2 252.8 312.2]/Border[0 0 0]/Dest[1818 0 R/XYZ null 799 0]>>endobj
+107 0 obj<</Subtype/Link/Rect[258.3 299.2 332.9 312.2]/Border[0 0 0]/Dest[1821 0 R/XYZ null 799 0]>>endobj
+108 0 obj<</Subtype/Link/Rect[338.4 299.2 404.4 312.2]/Border[0 0 0]/Dest[1827 0 R/XYZ null 799 0]>>endobj
+109 0 obj<</Subtype/Link/Rect[409.9 299.2 486.9 312.2]/Border[0 0 0]/Dest[1830 0 R/XYZ null 799 0]>>endobj
+110 0 obj[105 0 R
+106 0 R
+107 0 R
+108 0 R
+109 0 R
+]endobj
+111 0 obj<</Subtype/Link/Rect[36.0 172.0 119.1 185.0]/Border[0 0 0]/Dest[1815 0 R/XYZ null 799 0]>>endobj
+112 0 obj<</Subtype/Link/Rect[124.6 172.0 216.8 185.0]/Border[0 0 0]/Dest[1818 0 R/XYZ null 799 0]>>endobj
+113 0 obj<</Subtype/Link/Rect[222.3 172.0 296.9 185.0]/Border[0 0 0]/Dest[1821 0 R/XYZ null 799 0]>>endobj
+114 0 obj<</Subtype/Link/Rect[302.4 172.0 372.0 185.0]/Border[0 0 0]/Dest[1824 0 R/XYZ null 799 0]>>endobj
+115 0 obj<</Subtype/Link/Rect[377.5 172.0 454.5 185.0]/Border[0 0 0]/Dest[1830 0 R/XYZ null 799 0]>>endobj
+116 0 obj[111 0 R
+112 0 R
+113 0 R
+114 0 R
+115 0 R
+]endobj
+117 0 obj<</Subtype/Link/Rect[72.0 132.6 155.1 145.6]/Border[0 0 0]/Dest[1815 0 R/XYZ null 799 0]>>endobj
+118 0 obj<</Subtype/Link/Rect[160.6 132.6 252.8 145.6]/Border[0 0 0]/Dest[1818 0 R/XYZ null 799 0]>>endobj
+119 0 obj<</Subtype/Link/Rect[258.3 132.6 332.9 145.6]/Border[0 0 0]/Dest[1821 0 R/XYZ null 799 0]>>endobj
+120 0 obj<</Subtype/Link/Rect[338.4 132.6 408.0 145.6]/Border[0 0 0]/Dest[1824 0 R/XYZ null 799 0]>>endobj
+121 0 obj<</Subtype/Link/Rect[413.5 132.6 479.5 145.6]/Border[0 0 0]/Dest[1827 0 R/XYZ null 799 0]>>endobj
+122 0 obj[117 0 R
+118 0 R
+119 0 R
+120 0 R
+121 0 R
+]endobj
+123 0 obj<</Subtype/Link/Rect[36.0 315.6 109.3 328.6]/Border[0 0 0]/Dest[1779 0 R/XYZ null 799 0]>>endobj
+124 0 obj<</Subtype/Link/Rect[114.8 315.6 180.8 328.6]/Border[0 0 0]/Dest[1842 0 R/XYZ null 799 0]>>endobj
+125 0 obj[123 0 R
+124 0 R
+]endobj
+126 0 obj<</Subtype/Link/Rect[72.0 131.6 149.0 144.6]/Border[0 0 0]/Dest[1773 0 R/XYZ null 799 0]>>endobj
+127 0 obj<</Subtype/Link/Rect[154.5 131.6 236.4 144.6]/Border[0 0 0]/Dest[1794 0 R/XYZ null 799 0]>>endobj
+128 0 obj<</Subtype/Link/Rect[241.9 131.6 315.8 144.6]/Border[0 0 0]/Dest[1803 0 R/XYZ null 799 0]>>endobj
+129 0 obj<</Subtype/Link/Rect[321.3 131.6 407.4 144.6]/Border[0 0 0]/Dest[1839 0 R/XYZ null 799 0]>>endobj
+130 0 obj[126 0 R
+127 0 R
+128 0 R
+129 0 R
+]endobj
+131 0 obj<</Subtype/Link/Rect[36.0 120.8 113.0 133.8]/Border[0 0 0]/Dest[1773 0 R/XYZ null 799 0]>>endobj
+132 0 obj<</Subtype/Link/Rect[118.5 120.8 200.4 133.8]/Border[0 0 0]/Dest[1794 0 R/XYZ null 799 0]>>endobj
+133 0 obj<</Subtype/Link/Rect[205.9 120.8 279.8 133.8]/Border[0 0 0]/Dest[1803 0 R/XYZ null 799 0]>>endobj
+134 0 obj<</Subtype/Link/Rect[285.3 120.8 371.4 133.8]/Border[0 0 0]/Dest[1836 0 R/XYZ null 799 0]>>endobj
+135 0 obj[131 0 R
+132 0 R
+133 0 R
+134 0 R
+]endobj
+136 0 obj<</Subtype/Link/Rect[36.0 688.8 109.3 701.8]/Border[0 0 0]/Dest[1779 0 R/XYZ null 799 0]>>endobj
+137 0 obj<</Subtype/Link/Rect[114.8 688.8 184.5 701.8]/Border[0 0 0]/Dest[1833 0 R/XYZ null 799 0]>>endobj
+138 0 obj[136 0 R
+137 0 R
+]endobj
+139 0 obj<</Subtype/Link/Rect[72.0 288.4 151.4 301.4]/Border[0 0 0]/Dest[1851 0 R/XYZ null 799 0]>>endobj
+140 0 obj<</Subtype/Link/Rect[156.9 288.4 266.9 301.4]/Border[0 0 0]/Dest[1854 0 R/XYZ null 799 0]>>endobj
+141 0 obj<</Subtype/Link/Rect[272.4 288.4 377.5 301.4]/Border[0 0 0]/Dest[1860 0 R/XYZ null 799 0]>>endobj
+142 0 obj<</Subtype/Link/Rect[383.0 288.4 495.4 301.4]/Border[0 0 0]/Dest[1866 0 R/XYZ null 799 0]>>endobj
+143 0 obj<</Subtype/Link/Rect[72.0 275.2 179.5 288.2]/Border[0 0 0]/Dest[1869 0 R/XYZ null 799 0]>>endobj
+144 0 obj[139 0 R
+140 0 R
+141 0 R
+142 0 R
+143 0 R
+]endobj
+145 0 obj<</Subtype/Link/Rect[36.0 197.4 116.7 210.4]/Border[0 0 0]/Dest[1848 0 R/XYZ null 799 0]>>endobj
+146 0 obj<</Subtype/Link/Rect[122.2 197.4 232.1 210.4]/Border[0 0 0]/Dest[1854 0 R/XYZ null 799 0]>>endobj
+147 0 obj<</Subtype/Link/Rect[237.6 197.4 342.7 210.4]/Border[0 0 0]/Dest[1860 0 R/XYZ null 799 0]>>endobj
+148 0 obj<</Subtype/Link/Rect[348.2 197.4 460.6 210.4]/Border[0 0 0]/Dest[1866 0 R/XYZ null 799 0]>>endobj
+149 0 obj<</Subtype/Link/Rect[36.0 184.2 143.5 197.2]/Border[0 0 0]/Dest[1869 0 R/XYZ null 799 0]>>endobj
+150 0 obj[145 0 R
+146 0 R
+147 0 R
+148 0 R
+149 0 R
+]endobj
+151 0 obj<</Subtype/Link/Rect[36.0 688.8 116.7 701.8]/Border[0 0 0]/Dest[1848 0 R/XYZ null 799 0]>>endobj
+152 0 obj<</Subtype/Link/Rect[122.2 688.8 201.6 701.8]/Border[0 0 0]/Dest[1851 0 R/XYZ null 799 0]>>endobj
+153 0 obj<</Subtype/Link/Rect[207.1 688.8 312.2 701.8]/Border[0 0 0]/Dest[1860 0 R/XYZ null 799 0]>>endobj
+154 0 obj<</Subtype/Link/Rect[317.7 688.8 430.1 701.8]/Border[0 0 0]/Dest[1866 0 R/XYZ null 799 0]>>endobj
+155 0 obj<</Subtype/Link/Rect[36.0 675.6 143.5 688.6]/Border[0 0 0]/Dest[1869 0 R/XYZ null 799 0]>>endobj
+156 0 obj[151 0 R
+152 0 R
+153 0 R
+154 0 R
+155 0 R
+]endobj
+157 0 obj<</Subtype/Link/Rect[36.0 688.8 116.7 701.8]/Border[0 0 0]/Dest[1848 0 R/XYZ null 799 0]>>endobj
+158 0 obj<</Subtype/Link/Rect[122.2 688.8 201.6 701.8]/Border[0 0 0]/Dest[1851 0 R/XYZ null 799 0]>>endobj
+159 0 obj<</Subtype/Link/Rect[207.1 688.8 317.0 701.8]/Border[0 0 0]/Dest[1854 0 R/XYZ null 799 0]>>endobj
+160 0 obj<</Subtype/Link/Rect[322.5 688.8 434.9 701.8]/Border[0 0 0]/Dest[1866 0 R/XYZ null 799 0]>>endobj
+161 0 obj<</Subtype/Link/Rect[36.0 675.6 143.5 688.6]/Border[0 0 0]/Dest[1869 0 R/XYZ null 799 0]>>endobj
+162 0 obj[157 0 R
+158 0 R
+159 0 R
+160 0 R
+161 0 R
+]endobj
+163 0 obj<</Subtype/Link/Rect[72.0 100.2 152.7 113.2]/Border[0 0 0]/Dest[1848 0 R/XYZ null 799 0]>>endobj
+164 0 obj<</Subtype/Link/Rect[158.2 100.2 237.6 113.2]/Border[0 0 0]/Dest[1851 0 R/XYZ null 799 0]>>endobj
+165 0 obj<</Subtype/Link/Rect[243.1 100.2 353.0 113.2]/Border[0 0 0]/Dest[1854 0 R/XYZ null 799 0]>>endobj
+166 0 obj<</Subtype/Link/Rect[358.5 100.2 463.6 113.2]/Border[0 0 0]/Dest[1860 0 R/XYZ null 799 0]>>endobj
+167 0 obj<</Subtype/Link/Rect[72.0 87.0 179.5 100.0]/Border[0 0 0]/Dest[1869 0 R/XYZ null 799 0]>>endobj
+168 0 obj[163 0 R
+164 0 R
+165 0 R
+166 0 R
+167 0 R
+]endobj
+169 0 obj<</Subtype/Link/Rect[72.0 688.8 152.7 701.8]/Border[0 0 0]/Dest[1848 0 R/XYZ null 799 0]>>endobj
+170 0 obj<</Subtype/Link/Rect[158.2 688.8 237.6 701.8]/Border[0 0 0]/Dest[1851 0 R/XYZ null 799 0]>>endobj
+171 0 obj<</Subtype/Link/Rect[243.1 688.8 353.0 701.8]/Border[0 0 0]/Dest[1854 0 R/XYZ null 799 0]>>endobj
+172 0 obj<</Subtype/Link/Rect[358.5 688.8 463.6 701.8]/Border[0 0 0]/Dest[1860 0 R/XYZ null 799 0]>>endobj
+173 0 obj<</Subtype/Link/Rect[72.0 675.6 184.4 688.6]/Border[0 0 0]/Dest[1866 0 R/XYZ null 799 0]>>endobj
+174 0 obj[169 0 R
+170 0 R
+171 0 R
+172 0 R
+173 0 R
+]endobj
+175 0 obj<</Subtype/Link/Rect[36.0 258.0 121.5 271.0]/Border[0 0 0]/Dest[1806 0 R/XYZ null 799 0]>>endobj
+176 0 obj<</Subtype/Link/Rect[127.0 258.0 175.3 271.0]/Border[0 0 0]/Dest[1881 0 R/XYZ null 799 0]>>endobj
+177 0 obj[175 0 R
+176 0 R
+]endobj
+178 0 obj<</Subtype/Link/Rect[36.0 337.2 121.5 350.2]/Border[0 0 0]/Dest[1806 0 R/XYZ null 799 0]>>endobj
+179 0 obj<</Subtype/Link/Rect[127.0 337.2 183.2 350.2]/Border[0 0 0]/Dest[1875 0 R/XYZ null 799 0]>>endobj
+180 0 obj[178 0 R
+179 0 R
+]endobj
+181 0 obj<</Subtype/Link/Rect[72.0 670.8 107.4 683.8]/Border[0 0 0]/Dest[1710 0 R/XYZ null 818 0]>>endobj
+182 0 obj<</Subtype/Link/Rect[108.0 657.6 186.5 670.6]/Border[0 0 0]/Dest[1710 0 R/XYZ null 435 0]>>endobj
+183 0 obj<</Subtype/Link/Rect[108.0 644.4 199.9 657.4]/Border[0 0 0]/Dest[1710 0 R/XYZ null 209 0]>>endobj
+184 0 obj<</Subtype/Link/Rect[72.0 618.0 212.7 631.0]/Border[0 0 0]/Dest[1716 0 R/XYZ null 818 0]>>endobj
+185 0 obj<</Subtype/Link/Rect[108.0 604.8 203.3 617.8]/Border[0 0 0]/Dest[1716 0 R/XYZ null 428 0]>>endobj
+186 0 obj<</Subtype/Link/Rect[108.0 591.6 180.4 604.6]/Border[0 0 0]/Dest[1719 0 R/XYZ null 799 0]>>endobj
+187 0 obj<</Subtype/Link/Rect[108.0 578.4 127.6 591.4]/Border[0 0 0]/Dest[1719 0 R/XYZ null 534 0]>>endobj
+188 0 obj<</Subtype/Link/Rect[108.0 565.2 141.0 578.2]/Border[0 0 0]/Dest[1719 0 R/XYZ null 441 0]>>endobj
+189 0 obj<</Subtype/Link/Rect[108.0 552.0 136.1 565.0]/Border[0 0 0]/Dest[1719 0 R/XYZ null 360 0]>>endobj
+190 0 obj<</Subtype/Link/Rect[108.0 538.8 173.7 551.8]/Border[0 0 0]/Dest[1722 0 R/XYZ null 799 0]>>endobj
+191 0 obj<</Subtype/Link/Rect[108.0 525.6 160.5 538.6]/Border[0 0 0]/Dest[1722 0 R/XYZ null 693 0]>>endobj
+192 0 obj<</Subtype/Link/Rect[72.0 499.2 160.8 512.2]/Border[0 0 0]/Dest[1728 0 R/XYZ null 818 0]>>endobj
+193 0 obj<</Subtype/Link/Rect[108.0 486.0 191.1 499.0]/Border[0 0 0]/Dest[1728 0 R/XYZ null 428 0]>>endobj
+194 0 obj<</Subtype/Link/Rect[144.0 472.8 324.6 485.8]/Border[0 0 0]/Dest[1728 0 R/XYZ null 356 0]>>endobj
+195 0 obj<</Subtype/Link/Rect[108.0 459.6 172.5 472.6]/Border[0 0 0]/Dest[1728 0 R/XYZ null 316 0]>>endobj
+196 0 obj<</Subtype/Link/Rect[144.0 446.4 201.1 459.4]/Border[0 0 0]/Dest[1728 0 R/XYZ null 244 0]>>endobj
+197 0 obj<</Subtype/Link/Rect[144.0 433.2 330.1 446.2]/Border[0 0 0]/Dest[1728 0 R/XYZ null 194 0]>>endobj
+198 0 obj<</Subtype/Link/Rect[144.0 420.0 203.6 433.0]/Border[0 0 0]/Dest[1731 0 R/XYZ null 782 0]>>endobj
+199 0 obj<</Subtype/Link/Rect[144.0 406.8 245.5 419.8]/Border[0 0 0]/Dest[1731 0 R/XYZ null 731 0]>>endobj
+200 0 obj<</Subtype/Link/Rect[144.0 393.6 214.0 406.6]/Border[0 0 0]/Dest[1731 0 R/XYZ null 681 0]>>endobj
+201 0 obj<</Subtype/Link/Rect[108.0 380.4 175.5 393.4]/Border[0 0 0]/Dest[1731 0 R/XYZ null 641 0]>>endobj
+202 0 obj<</Subtype/Link/Rect[144.0 367.2 201.1 380.2]/Border[0 0 0]/Dest[1731 0 R/XYZ null 569 0]>>endobj
+203 0 obj<</Subtype/Link/Rect[144.0 354.0 245.1 367.0]/Border[0 0 0]/Dest[1731 0 R/XYZ null 519 0]>>endobj
+204 0 obj<</Subtype/Link/Rect[144.0 340.8 243.0 353.8]/Border[0 0 0]/Dest[1731 0 R/XYZ null 468 0]>>endobj
+205 0 obj<</Subtype/Link/Rect[144.0 327.6 221.6 340.6]/Border[0 0 0]/Dest[1731 0 R/XYZ null 418 0]>>endobj
+206 0 obj<</Subtype/Link/Rect[144.0 314.4 261.0 327.4]/Border[0 0 0]/Dest[1731 0 R/XYZ null 368 0]>>endobj
+207 0 obj<</Subtype/Link/Rect[144.0 301.2 241.8 314.2]/Border[0 0 0]/Dest[1731 0 R/XYZ null 317 0]>>endobj
+208 0 obj<</Subtype/Link/Rect[144.0 288.0 242.4 301.0]/Border[0 0 0]/Dest[1731 0 R/XYZ null 267 0]>>endobj
+209 0 obj<</Subtype/Link/Rect[108.0 274.8 163.9 287.8]/Border[0 0 0]/Dest[1731 0 R/XYZ null 227 0]>>endobj
+210 0 obj<</Subtype/Link/Rect[144.0 261.6 201.1 274.6]/Border[0 0 0]/Dest[1734 0 R/XYZ null 782 0]>>endobj
+211 0 obj<</Subtype/Link/Rect[144.0 248.4 251.9 261.4]/Border[0 0 0]/Dest[1734 0 R/XYZ null 731 0]>>endobj
+212 0 obj<</Subtype/Link/Rect[144.0 235.2 223.8 248.2]/Border[0 0 0]/Dest[1734 0 R/XYZ null 681 0]>>endobj
+213 0 obj<</Subtype/Link/Rect[144.0 222.0 250.0 235.0]/Border[0 0 0]/Dest[1734 0 R/XYZ null 630 0]>>endobj
+214 0 obj<</Subtype/Link/Rect[144.0 208.8 257.4 221.8]/Border[0 0 0]/Dest[1734 0 R/XYZ null 580 0]>>endobj
+215 0 obj<</Subtype/Link/Rect[144.0 195.6 225.0 208.6]/Border[0 0 0]/Dest[1734 0 R/XYZ null 530 0]>>endobj
+216 0 obj<</Subtype/Link/Rect[144.0 182.4 268.7 195.4]/Border[0 0 0]/Dest[1734 0 R/XYZ null 479 0]>>endobj
+217 0 obj<</Subtype/Link/Rect[144.0 169.2 252.8 182.2]/Border[0 0 0]/Dest[1734 0 R/XYZ null 429 0]>>endobj
+218 0 obj<</Subtype/Link/Rect[144.0 156.0 257.7 169.0]/Border[0 0 0]/Dest[1734 0 R/XYZ null 378 0]>>endobj
+219 0 obj<</Subtype/Link/Rect[144.0 142.8 269.3 155.8]/Border[0 0 0]/Dest[1734 0 R/XYZ null 328 0]>>endobj
+220 0 obj<</Subtype/Link/Rect[108.0 129.6 191.4 142.6]/Border[0 0 0]/Dest[1734 0 R/XYZ null 288 0]>>endobj
+221 0 obj<</Subtype/Link/Rect[144.0 116.4 201.1 129.4]/Border[0 0 0]/Dest[1734 0 R/XYZ null 216 0]>>endobj
+222 0 obj<</Subtype/Link/Rect[144.0 103.2 275.1 116.2]/Border[0 0 0]/Dest[1737 0 R/XYZ null 782 0]>>endobj
+223 0 obj<</Subtype/Link/Rect[144.0 90.0 284.2 103.0]/Border[0 0 0]/Dest[1737 0 R/XYZ null 731 0]>>endobj
+224 0 obj<</Subtype/Link/Rect[144.0 76.8 260.4 89.8]/Border[0 0 0]/Dest[1737 0 R/XYZ null 681 0]>>endobj
+225 0 obj<</Subtype/Link/Rect[108.0 63.6 168.2 76.6]/Border[0 0 0]/Dest[1737 0 R/XYZ null 641 0]>>endobj
+226 0 obj[181 0 R
+182 0 R
+183 0 R
+184 0 R
+185 0 R
+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
+]endobj
+227 0 obj<</Subtype/Link/Rect[108.0 670.8 165.1 683.8]/Border[0 0 0]/Dest[1737 0 R/XYZ null 569 0]>>endobj
+228 0 obj<</Subtype/Link/Rect[108.0 657.6 195.1 670.6]/Border[0 0 0]/Dest[1737 0 R/XYZ null 519 0]>>endobj
+229 0 obj<</Subtype/Link/Rect[108.0 644.4 196.6 657.4]/Border[0 0 0]/Dest[1737 0 R/XYZ null 468 0]>>endobj
+230 0 obj<</Subtype/Link/Rect[108.0 631.2 188.7 644.2]/Border[0 0 0]/Dest[1737 0 R/XYZ null 418 0]>>endobj
+231 0 obj<</Subtype/Link/Rect[108.0 618.0 195.7 631.0]/Border[0 0 0]/Dest[1737 0 R/XYZ null 368 0]>>endobj
+232 0 obj<</Subtype/Link/Rect[108.0 604.8 183.5 617.8]/Border[0 0 0]/Dest[1737 0 R/XYZ null 317 0]>>endobj
+233 0 obj<</Subtype/Link/Rect[108.0 591.6 208.8 604.6]/Border[0 0 0]/Dest[1737 0 R/XYZ null 267 0]>>endobj
+234 0 obj<</Subtype/Link/Rect[108.0 578.4 181.6 591.4]/Border[0 0 0]/Dest[1737 0 R/XYZ null 216 0]>>endobj
+235 0 obj<</Subtype/Link/Rect[36.0 552.0 123.8 565.0]/Border[0 0 0]/Dest[1740 0 R/XYZ null 818 0]>>endobj
+236 0 obj<</Subtype/Link/Rect[72.0 538.8 115.4 551.8]/Border[0 0 0]/Dest[1740 0 R/XYZ null 428 0]>>endobj
+237 0 obj<</Subtype/Link/Rect[108.0 525.6 213.4 538.6]/Border[0 0 0]/Dest[1740 0 R/XYZ null 356 0]>>endobj
+238 0 obj<</Subtype/Link/Rect[108.0 512.4 181.0 525.4]/Border[0 0 0]/Dest[1740 0 R/XYZ null 296 0]>>endobj
+239 0 obj<</Subtype/Link/Rect[108.0 499.2 183.5 512.2]/Border[0 0 0]/Dest[1740 0 R/XYZ null 246 0]>>endobj
+240 0 obj<</Subtype/Link/Rect[72.0 486.0 195.8 499.0]/Border[0 0 0]/Dest[1740 0 R/XYZ null 206 0]>>endobj
+241 0 obj<</Subtype/Link/Rect[108.0 472.8 183.5 485.8]/Border[0 0 0]/Dest[1743 0 R/XYZ null 782 0]>>endobj
+242 0 obj<</Subtype/Link/Rect[72.0 459.6 174.3 472.6]/Border[0 0 0]/Dest[1743 0 R/XYZ null 742 0]>>endobj
+243 0 obj<</Subtype/Link/Rect[72.0 446.4 174.4 459.4]/Border[0 0 0]/Dest[1743 0 R/XYZ null 681 0]>>endobj
+244 0 obj<</Subtype/Link/Rect[36.0 420.0 165.7 433.0]/Border[0 0 0]/Dest[1746 0 R/XYZ null 818 0]>>endobj
+245 0 obj<</Subtype/Link/Rect[72.0 406.8 115.4 419.8]/Border[0 0 0]/Dest[1746 0 R/XYZ null 415 0]>>endobj
+246 0 obj<</Subtype/Link/Rect[108.0 393.6 183.5 406.6]/Border[0 0 0]/Dest[1746 0 R/XYZ null 343 0]>>endobj
+247 0 obj<</Subtype/Link/Rect[108.0 380.4 193.2 393.4]/Border[0 0 0]/Dest[1746 0 R/XYZ null 292 0]>>endobj
+248 0 obj<</Subtype/Link/Rect[72.0 367.2 146.2 380.2]/Border[0 0 0]/Dest[1746 0 R/XYZ null 253 0]>>endobj
+249 0 obj<</Subtype/Link/Rect[108.0 354.0 187.4 367.0]/Border[0 0 0]/Dest[1746 0 R/XYZ null 181 0]>>endobj
+250 0 obj<</Subtype/Link/Rect[108.0 340.8 218.0 353.8]/Border[0 0 0]/Dest[1749 0 R/XYZ null 782 0]>>endobj
+251 0 obj<</Subtype/Link/Rect[108.0 327.6 213.1 340.6]/Border[0 0 0]/Dest[1749 0 R/XYZ null 731 0]>>endobj
+252 0 obj<</Subtype/Link/Rect[108.0 314.4 188.7 327.4]/Border[0 0 0]/Dest[1749 0 R/XYZ null 681 0]>>endobj
+253 0 obj<</Subtype/Link/Rect[72.0 301.2 188.5 314.2]/Border[0 0 0]/Dest[1749 0 R/XYZ null 641 0]>>endobj
+254 0 obj<</Subtype/Link/Rect[36.0 274.8 138.5 287.8]/Border[0 0 0]/Dest[1752 0 R/XYZ null 818 0]>>endobj
+255 0 obj<</Subtype/Link/Rect[72.0 261.6 115.4 274.6]/Border[0 0 0]/Dest[1752 0 R/XYZ null 415 0]>>endobj
+256 0 obj<</Subtype/Link/Rect[108.0 248.4 213.4 261.4]/Border[0 0 0]/Dest[1752 0 R/XYZ null 343 0]>>endobj
+257 0 obj<</Subtype/Link/Rect[108.0 235.2 181.0 248.2]/Border[0 0 0]/Dest[1752 0 R/XYZ null 283 0]>>endobj
+258 0 obj<</Subtype/Link/Rect[108.0 222.0 183.5 235.0]/Border[0 0 0]/Dest[1752 0 R/XYZ null 232 0]>>endobj
+259 0 obj<</Subtype/Link/Rect[108.0 208.8 139.2 221.8]/Border[0 0 0]/Dest[1752 0 R/XYZ null 182 0]>>endobj
+260 0 obj<</Subtype/Link/Rect[72.0 195.6 195.8 208.6]/Border[0 0 0]/Dest[1755 0 R/XYZ null 799 0]>>endobj
+261 0 obj<</Subtype/Link/Rect[108.0 182.4 183.5 195.4]/Border[0 0 0]/Dest[1755 0 R/XYZ null 728 0]>>endobj
+262 0 obj<</Subtype/Link/Rect[72.0 169.2 174.3 182.2]/Border[0 0 0]/Dest[1755 0 R/XYZ null 688 0]>>endobj
+263 0 obj<</Subtype/Link/Rect[72.0 156.0 204.6 169.0]/Border[0 0 0]/Dest[1755 0 R/XYZ null 627 0]>>endobj
+264 0 obj<</Subtype/Link/Rect[36.0 129.6 102.8 142.6]/Border[0 0 0]/Dest[1758 0 R/XYZ null 818 0]>>endobj
+265 0 obj<</Subtype/Link/Rect[72.0 116.4 145.7 129.4]/Border[0 0 0]/Dest[1758 0 R/XYZ null 428 0]>>endobj
+266 0 obj<</Subtype/Link/Rect[72.0 103.2 145.6 116.2]/Border[0 0 0]/Dest[1758 0 R/XYZ null 367 0]>>endobj
+267 0 obj<</Subtype/Link/Rect[72.0 90.0 134.0 103.0]/Border[0 0 0]/Dest[1758 0 R/XYZ null 306 0]>>endobj
+268 0 obj<</Subtype/Link/Rect[72.0 76.8 161.5 89.8]/Border[0 0 0]/Dest[1758 0 R/XYZ null 244 0]>>endobj
+269 0 obj<</Subtype/Link/Rect[72.0 63.6 138.3 76.6]/Border[0 0 0]/Dest[1761 0 R/XYZ null 799 0]>>endobj
+270 0 obj[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
+]endobj
+271 0 obj<</Subtype/Link/Rect[108.0 670.8 182.2 683.8]/Border[0 0 0]/Dest[1761 0 R/XYZ null 738 0]>>endobj
+272 0 obj<</Subtype/Link/Rect[72.0 644.4 140.6 657.4]/Border[0 0 0]/Dest[1764 0 R/XYZ null 818 0]>>endobj
+273 0 obj<</Subtype/Link/Rect[72.0 618.0 138.2 631.0]/Border[0 0 0]/Dest[1770 0 R/XYZ null 818 0]>>endobj
+274 0 obj<</Subtype/Link/Rect[108.0 604.8 185.0 617.8]/Border[0 0 0]/Dest[1773 0 R/XYZ null 799 0]>>endobj
+275 0 obj<</Subtype/Link/Rect[144.0 591.6 171.5 604.6]/Border[0 0 0]/Dest[1773 0 R/XYZ null 728 0]>>endobj
+276 0 obj<</Subtype/Link/Rect[144.0 578.4 192.9 591.4]/Border[0 0 0]/Dest[1773 0 R/XYZ null 629 0]>>endobj
+277 0 obj<</Subtype/Link/Rect[144.0 565.2 178.2 578.2]/Border[0 0 0]/Dest[1773 0 R/XYZ null 468 0]>>endobj
+278 0 obj<</Subtype/Link/Rect[144.0 552.0 195.3 565.0]/Border[0 0 0]/Dest[1773 0 R/XYZ null 408 0]>>endobj
+279 0 obj<</Subtype/Link/Rect[144.0 538.8 183.1 551.8]/Border[0 0 0]/Dest[1773 0 R/XYZ null 348 0]>>endobj
+280 0 obj<</Subtype/Link/Rect[144.0 525.6 183.4 538.6]/Border[0 0 0]/Dest[1776 0 R/XYZ null 782 0]>>endobj
+281 0 obj<</Subtype/Link/Rect[108.0 512.4 181.3 525.4]/Border[0 0 0]/Dest[1779 0 R/XYZ null 799 0]>>endobj
+282 0 obj<</Subtype/Link/Rect[144.0 499.2 171.5 512.2]/Border[0 0 0]/Dest[1779 0 R/XYZ null 728 0]>>endobj
+283 0 obj<</Subtype/Link/Rect[144.0 486.0 192.9 499.0]/Border[0 0 0]/Dest[1779 0 R/XYZ null 651 0]>>endobj
+284 0 obj<</Subtype/Link/Rect[144.0 472.8 178.2 485.8]/Border[0 0 0]/Dest[1779 0 R/XYZ null 530 0]>>endobj
+285 0 obj<</Subtype/Link/Rect[144.0 459.6 195.3 472.6]/Border[0 0 0]/Dest[1779 0 R/XYZ null 470 0]>>endobj
+286 0 obj<</Subtype/Link/Rect[144.0 446.4 183.1 459.4]/Border[0 0 0]/Dest[1779 0 R/XYZ null 410 0]>>endobj
+287 0 obj<</Subtype/Link/Rect[144.0 433.2 183.4 446.2]/Border[0 0 0]/Dest[1779 0 R/XYZ null 333 0]>>endobj
+288 0 obj<</Subtype/Link/Rect[108.0 420.0 201.5 433.0]/Border[0 0 0]/Dest[1782 0 R/XYZ null 799 0]>>endobj
+289 0 obj<</Subtype/Link/Rect[144.0 406.8 171.5 419.8]/Border[0 0 0]/Dest[1782 0 R/XYZ null 728 0]>>endobj
+290 0 obj<</Subtype/Link/Rect[144.0 393.6 192.9 406.6]/Border[0 0 0]/Dest[1782 0 R/XYZ null 629 0]>>endobj
+291 0 obj<</Subtype/Link/Rect[144.0 380.4 178.2 393.4]/Border[0 0 0]/Dest[1782 0 R/XYZ null 468 0]>>endobj
+292 0 obj<</Subtype/Link/Rect[144.0 367.2 195.3 380.2]/Border[0 0 0]/Dest[1782 0 R/XYZ null 395 0]>>endobj
+293 0 obj<</Subtype/Link/Rect[144.0 354.0 183.1 367.0]/Border[0 0 0]/Dest[1782 0 R/XYZ null 309 0]>>endobj
+294 0 obj<</Subtype/Link/Rect[144.0 340.8 183.4 353.8]/Border[0 0 0]/Dest[1785 0 R/XYZ null 570 0]>>endobj
+295 0 obj<</Subtype/Link/Rect[108.0 327.6 184.4 340.6]/Border[0 0 0]/Dest[1788 0 R/XYZ null 799 0]>>endobj
+296 0 obj<</Subtype/Link/Rect[144.0 314.4 171.5 327.4]/Border[0 0 0]/Dest[1788 0 R/XYZ null 728 0]>>endobj
+297 0 obj<</Subtype/Link/Rect[144.0 301.2 192.9 314.2]/Border[0 0 0]/Dest[1788 0 R/XYZ null 640 0]>>endobj
+298 0 obj<</Subtype/Link/Rect[144.0 288.0 178.2 301.0]/Border[0 0 0]/Dest[1788 0 R/XYZ null 499 0]>>endobj
+299 0 obj<</Subtype/Link/Rect[144.0 274.8 195.3 287.8]/Border[0 0 0]/Dest[1788 0 R/XYZ null 426 0]>>endobj
+300 0 obj<</Subtype/Link/Rect[144.0 261.6 183.1 274.6]/Border[0 0 0]/Dest[1788 0 R/XYZ null 353 0]>>endobj
+301 0 obj<</Subtype/Link/Rect[144.0 248.4 183.4 261.4]/Border[0 0 0]/Dest[1791 0 R/XYZ null 646 0]>>endobj
+302 0 obj<</Subtype/Link/Rect[108.0 235.2 189.9 248.2]/Border[0 0 0]/Dest[1794 0 R/XYZ null 799 0]>>endobj
+303 0 obj<</Subtype/Link/Rect[144.0 222.0 171.5 235.0]/Border[0 0 0]/Dest[1794 0 R/XYZ null 728 0]>>endobj
+304 0 obj<</Subtype/Link/Rect[144.0 208.8 192.9 221.8]/Border[0 0 0]/Dest[1794 0 R/XYZ null 640 0]>>endobj
+305 0 obj<</Subtype/Link/Rect[144.0 195.6 195.3 208.6]/Border[0 0 0]/Dest[1794 0 R/XYZ null 519 0]>>endobj
+306 0 obj<</Subtype/Link/Rect[144.0 182.4 183.1 195.4]/Border[0 0 0]/Dest[1794 0 R/XYZ null 459 0]>>endobj
+307 0 obj<</Subtype/Link/Rect[144.0 169.2 183.4 182.2]/Border[0 0 0]/Dest[1794 0 R/XYZ null 329 0]>>endobj
+308 0 obj<</Subtype/Link/Rect[108.0 156.0 184.4 169.0]/Border[0 0 0]/Dest[1797 0 R/XYZ null 799 0]>>endobj
+309 0 obj<</Subtype/Link/Rect[144.0 142.8 171.5 155.8]/Border[0 0 0]/Dest[1797 0 R/XYZ null 728 0]>>endobj
+310 0 obj<</Subtype/Link/Rect[144.0 129.6 192.9 142.6]/Border[0 0 0]/Dest[1797 0 R/XYZ null 662 0]>>endobj
+311 0 obj<</Subtype/Link/Rect[144.0 116.4 178.2 129.4]/Border[0 0 0]/Dest[1797 0 R/XYZ null 561 0]>>endobj
+312 0 obj<</Subtype/Link/Rect[144.0 103.2 195.3 116.2]/Border[0 0 0]/Dest[1797 0 R/XYZ null 501 0]>>endobj
+313 0 obj<</Subtype/Link/Rect[144.0 90.0 183.1 103.0]/Border[0 0 0]/Dest[1797 0 R/XYZ null 428 0]>>endobj
+314 0 obj<</Subtype/Link/Rect[144.0 76.8 183.4 89.8]/Border[0 0 0]/Dest[1797 0 R/XYZ null 178 0]>>endobj
+315 0 obj<</Subtype/Link/Rect[108.0 63.6 184.4 76.6]/Border[0 0 0]/Dest[1800 0 R/XYZ null 799 0]>>endobj
+316 0 obj[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
+]endobj
+317 0 obj<</Subtype/Link/Rect[108.0 670.8 135.5 683.8]/Border[0 0 0]/Dest[1800 0 R/XYZ null 728 0]>>endobj
+318 0 obj<</Subtype/Link/Rect[108.0 657.6 142.2 670.6]/Border[0 0 0]/Dest[1800 0 R/XYZ null 662 0]>>endobj
+319 0 obj<</Subtype/Link/Rect[108.0 644.4 159.3 657.4]/Border[0 0 0]/Dest[1800 0 R/XYZ null 602 0]>>endobj
+320 0 obj<</Subtype/Link/Rect[108.0 631.2 147.1 644.2]/Border[0 0 0]/Dest[1800 0 R/XYZ null 528 0]>>endobj
+321 0 obj<</Subtype/Link/Rect[108.0 618.0 147.4 631.0]/Border[0 0 0]/Dest[1800 0 R/XYZ null 452 0]>>endobj
+322 0 obj<</Subtype/Link/Rect[72.0 604.8 145.9 617.8]/Border[0 0 0]/Dest[1803 0 R/XYZ null 799 0]>>endobj
+323 0 obj<</Subtype/Link/Rect[108.0 591.6 135.5 604.6]/Border[0 0 0]/Dest[1803 0 R/XYZ null 728 0]>>endobj
+324 0 obj<</Subtype/Link/Rect[108.0 578.4 156.9 591.4]/Border[0 0 0]/Dest[1803 0 R/XYZ null 640 0]>>endobj
+325 0 obj<</Subtype/Link/Rect[108.0 565.2 142.2 578.2]/Border[0 0 0]/Dest[1803 0 R/XYZ null 499 0]>>endobj
+326 0 obj<</Subtype/Link/Rect[108.0 552.0 159.3 565.0]/Border[0 0 0]/Dest[1803 0 R/XYZ null 439 0]>>endobj
+327 0 obj<</Subtype/Link/Rect[108.0 538.8 147.4 551.8]/Border[0 0 0]/Dest[1803 0 R/XYZ null 258 0]>>endobj
+328 0 obj<</Subtype/Link/Rect[72.0 525.6 157.5 538.6]/Border[0 0 0]/Dest[1806 0 R/XYZ null 799 0]>>endobj
+329 0 obj<</Subtype/Link/Rect[108.0 512.4 135.5 525.4]/Border[0 0 0]/Dest[1806 0 R/XYZ null 728 0]>>endobj
+330 0 obj<</Subtype/Link/Rect[108.0 499.2 156.9 512.2]/Border[0 0 0]/Dest[1806 0 R/XYZ null 662 0]>>endobj
+331 0 obj<</Subtype/Link/Rect[108.0 486.0 142.2 499.0]/Border[0 0 0]/Dest[1806 0 R/XYZ null 561 0]>>endobj
+332 0 obj<</Subtype/Link/Rect[108.0 472.8 159.3 485.8]/Border[0 0 0]/Dest[1806 0 R/XYZ null 501 0]>>endobj
+333 0 obj<</Subtype/Link/Rect[108.0 459.6 147.1 472.6]/Border[0 0 0]/Dest[1806 0 R/XYZ null 428 0]>>endobj
+334 0 obj<</Subtype/Link/Rect[108.0 446.4 147.4 459.4]/Border[0 0 0]/Dest[1806 0 R/XYZ null 308 0]>>endobj
+335 0 obj<</Subtype/Link/Rect[72.0 433.2 135.5 446.2]/Border[0 0 0]/Dest[1809 0 R/XYZ null 799 0]>>endobj
+336 0 obj<</Subtype/Link/Rect[108.0 420.0 135.5 433.0]/Border[0 0 0]/Dest[1809 0 R/XYZ null 728 0]>>endobj
+337 0 obj<</Subtype/Link/Rect[108.0 406.8 156.9 419.8]/Border[0 0 0]/Dest[1809 0 R/XYZ null 662 0]>>endobj
+338 0 obj<</Subtype/Link/Rect[108.0 393.6 142.2 406.6]/Border[0 0 0]/Dest[1809 0 R/XYZ null 561 0]>>endobj
+339 0 obj<</Subtype/Link/Rect[108.0 380.4 159.3 393.4]/Border[0 0 0]/Dest[1809 0 R/XYZ null 488 0]>>endobj
+340 0 obj<</Subtype/Link/Rect[108.0 367.2 147.1 380.2]/Border[0 0 0]/Dest[1809 0 R/XYZ null 375 0]>>endobj
+341 0 obj<</Subtype/Link/Rect[72.0 354.0 149.6 367.0]/Border[0 0 0]/Dest[1812 0 R/XYZ null 799 0]>>endobj
+342 0 obj<</Subtype/Link/Rect[108.0 340.8 135.5 353.8]/Border[0 0 0]/Dest[1812 0 R/XYZ null 728 0]>>endobj
+343 0 obj<</Subtype/Link/Rect[108.0 327.6 156.9 340.6]/Border[0 0 0]/Dest[1812 0 R/XYZ null 662 0]>>endobj
+344 0 obj<</Subtype/Link/Rect[108.0 314.4 142.2 327.4]/Border[0 0 0]/Dest[1812 0 R/XYZ null 561 0]>>endobj
+345 0 obj<</Subtype/Link/Rect[108.0 301.2 159.3 314.2]/Border[0 0 0]/Dest[1812 0 R/XYZ null 501 0]>>endobj
+346 0 obj<</Subtype/Link/Rect[108.0 288.0 147.1 301.0]/Border[0 0 0]/Dest[1812 0 R/XYZ null 428 0]>>endobj
+347 0 obj<</Subtype/Link/Rect[108.0 274.8 147.4 287.8]/Border[0 0 0]/Dest[1812 0 R/XYZ null 178 0]>>endobj
+348 0 obj<</Subtype/Link/Rect[72.0 261.6 155.1 274.6]/Border[0 0 0]/Dest[1815 0 R/XYZ null 799 0]>>endobj
+349 0 obj<</Subtype/Link/Rect[108.0 248.4 135.5 261.4]/Border[0 0 0]/Dest[1815 0 R/XYZ null 728 0]>>endobj
+350 0 obj<</Subtype/Link/Rect[108.0 235.2 142.2 248.2]/Border[0 0 0]/Dest[1815 0 R/XYZ null 662 0]>>endobj
+351 0 obj<</Subtype/Link/Rect[108.0 222.0 159.3 235.0]/Border[0 0 0]/Dest[1815 0 R/XYZ null 602 0]>>endobj
+352 0 obj<</Subtype/Link/Rect[108.0 208.8 147.1 221.8]/Border[0 0 0]/Dest[1815 0 R/XYZ null 489 0]>>endobj
+353 0 obj<</Subtype/Link/Rect[108.0 195.6 147.4 208.6]/Border[0 0 0]/Dest[1815 0 R/XYZ null 336 0]>>endobj
+354 0 obj<</Subtype/Link/Rect[72.0 182.4 164.3 195.4]/Border[0 0 0]/Dest[1818 0 R/XYZ null 799 0]>>endobj
+355 0 obj<</Subtype/Link/Rect[108.0 169.2 135.5 182.2]/Border[0 0 0]/Dest[1818 0 R/XYZ null 728 0]>>endobj
+356 0 obj<</Subtype/Link/Rect[108.0 156.0 156.9 169.0]/Border[0 0 0]/Dest[1818 0 R/XYZ null 662 0]>>endobj
+357 0 obj<</Subtype/Link/Rect[108.0 142.8 142.2 155.8]/Border[0 0 0]/Dest[1818 0 R/XYZ null 561 0]>>endobj
+358 0 obj<</Subtype/Link/Rect[108.0 129.6 159.3 142.6]/Border[0 0 0]/Dest[1818 0 R/XYZ null 501 0]>>endobj
+359 0 obj<</Subtype/Link/Rect[108.0 116.4 147.1 129.4]/Border[0 0 0]/Dest[1818 0 R/XYZ null 428 0]>>endobj
+360 0 obj<</Subtype/Link/Rect[108.0 103.2 147.4 116.2]/Border[0 0 0]/Dest[1818 0 R/XYZ null 265 0]>>endobj
+361 0 obj<</Subtype/Link/Rect[72.0 90.0 146.5 103.0]/Border[0 0 0]/Dest[1821 0 R/XYZ null 799 0]>>endobj
+362 0 obj<</Subtype/Link/Rect[108.0 76.8 135.5 89.8]/Border[0 0 0]/Dest[1821 0 R/XYZ null 728 0]>>endobj
+363 0 obj<</Subtype/Link/Rect[108.0 63.6 159.3 76.6]/Border[0 0 0]/Dest[1821 0 R/XYZ null 662 0]>>endobj
+364 0 obj[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
+355 0 R
+356 0 R
+357 0 R
+358 0 R
+359 0 R
+360 0 R
+361 0 R
+362 0 R
+363 0 R
+]endobj
+365 0 obj<</Subtype/Link/Rect[144.0 670.8 183.1 683.8]/Border[0 0 0]/Dest[1821 0 R/XYZ null 602 0]>>endobj
+366 0 obj<</Subtype/Link/Rect[144.0 657.6 183.4 670.6]/Border[0 0 0]/Dest[1821 0 R/XYZ null 503 0]>>endobj
+367 0 obj<</Subtype/Link/Rect[108.0 644.4 177.6 657.4]/Border[0 0 0]/Dest[1824 0 R/XYZ null 799 0]>>endobj
+368 0 obj<</Subtype/Link/Rect[144.0 631.2 171.5 644.2]/Border[0 0 0]/Dest[1824 0 R/XYZ null 728 0]>>endobj
+369 0 obj<</Subtype/Link/Rect[144.0 618.0 192.9 631.0]/Border[0 0 0]/Dest[1824 0 R/XYZ null 662 0]>>endobj
+370 0 obj<</Subtype/Link/Rect[144.0 604.8 195.3 617.8]/Border[0 0 0]/Dest[1824 0 R/XYZ null 561 0]>>endobj
+371 0 obj<</Subtype/Link/Rect[144.0 591.6 183.1 604.6]/Border[0 0 0]/Dest[1824 0 R/XYZ null 501 0]>>endobj
+372 0 obj<</Subtype/Link/Rect[144.0 578.4 183.4 591.4]/Border[0 0 0]/Dest[1824 0 R/XYZ null 392 0]>>endobj
+373 0 obj<</Subtype/Link/Rect[108.0 565.2 174.0 578.2]/Border[0 0 0]/Dest[1827 0 R/XYZ null 799 0]>>endobj
+374 0 obj<</Subtype/Link/Rect[144.0 552.0 171.5 565.0]/Border[0 0 0]/Dest[1827 0 R/XYZ null 728 0]>>endobj
+375 0 obj<</Subtype/Link/Rect[144.0 538.8 192.9 551.8]/Border[0 0 0]/Dest[1827 0 R/XYZ null 662 0]>>endobj
+376 0 obj<</Subtype/Link/Rect[144.0 525.6 178.2 538.6]/Border[0 0 0]/Dest[1827 0 R/XYZ null 561 0]>>endobj
+377 0 obj<</Subtype/Link/Rect[144.0 512.4 195.3 525.4]/Border[0 0 0]/Dest[1827 0 R/XYZ null 501 0]>>endobj
+378 0 obj<</Subtype/Link/Rect[144.0 499.2 183.1 512.2]/Border[0 0 0]/Dest[1827 0 R/XYZ null 428 0]>>endobj
+379 0 obj<</Subtype/Link/Rect[144.0 486.0 183.4 499.0]/Border[0 0 0]/Dest[1827 0 R/XYZ null 265 0]>>endobj
+380 0 obj<</Subtype/Link/Rect[108.0 472.8 185.0 485.8]/Border[0 0 0]/Dest[1830 0 R/XYZ null 799 0]>>endobj
+381 0 obj<</Subtype/Link/Rect[144.0 459.6 171.5 472.6]/Border[0 0 0]/Dest[1830 0 R/XYZ null 728 0]>>endobj
+382 0 obj<</Subtype/Link/Rect[144.0 446.4 192.9 459.4]/Border[0 0 0]/Dest[1830 0 R/XYZ null 651 0]>>endobj
+383 0 obj<</Subtype/Link/Rect[144.0 433.2 178.2 446.2]/Border[0 0 0]/Dest[1830 0 R/XYZ null 530 0]>>endobj
+384 0 obj<</Subtype/Link/Rect[144.0 420.0 195.3 433.0]/Border[0 0 0]/Dest[1830 0 R/XYZ null 470 0]>>endobj
+385 0 obj<</Subtype/Link/Rect[144.0 406.8 183.1 419.8]/Border[0 0 0]/Dest[1830 0 R/XYZ null 410 0]>>endobj
+386 0 obj<</Subtype/Link/Rect[144.0 393.6 183.4 406.6]/Border[0 0 0]/Dest[1830 0 R/XYZ null 225 0]>>endobj
+387 0 obj<</Subtype/Link/Rect[108.0 380.4 177.6 393.4]/Border[0 0 0]/Dest[1833 0 R/XYZ null 799 0]>>endobj
+388 0 obj<</Subtype/Link/Rect[144.0 367.2 171.5 380.2]/Border[0 0 0]/Dest[1833 0 R/XYZ null 728 0]>>endobj
+389 0 obj<</Subtype/Link/Rect[144.0 354.0 178.2 367.0]/Border[0 0 0]/Dest[1833 0 R/XYZ null 662 0]>>endobj
+390 0 obj<</Subtype/Link/Rect[144.0 340.8 195.3 353.8]/Border[0 0 0]/Dest[1833 0 R/XYZ null 602 0]>>endobj
+391 0 obj<</Subtype/Link/Rect[144.0 327.6 183.1 340.6]/Border[0 0 0]/Dest[1833 0 R/XYZ null 528 0]>>endobj
+392 0 obj<</Subtype/Link/Rect[144.0 314.4 183.4 327.4]/Border[0 0 0]/Dest[1833 0 R/XYZ null 408 0]>>endobj
+393 0 obj<</Subtype/Link/Rect[108.0 301.2 194.2 314.2]/Border[0 0 0]/Dest[1836 0 R/XYZ null 799 0]>>endobj
+394 0 obj<</Subtype/Link/Rect[144.0 288.0 171.5 301.0]/Border[0 0 0]/Dest[1836 0 R/XYZ null 728 0]>>endobj
+395 0 obj<</Subtype/Link/Rect[144.0 274.8 192.9 287.8]/Border[0 0 0]/Dest[1836 0 R/XYZ null 640 0]>>endobj
+396 0 obj<</Subtype/Link/Rect[144.0 261.6 178.2 274.6]/Border[0 0 0]/Dest[1836 0 R/XYZ null 499 0]>>endobj
+397 0 obj<</Subtype/Link/Rect[144.0 248.4 195.3 261.4]/Border[0 0 0]/Dest[1836 0 R/XYZ null 439 0]>>endobj
+398 0 obj<</Subtype/Link/Rect[144.0 235.2 183.1 248.2]/Border[0 0 0]/Dest[1836 0 R/XYZ null 366 0]>>endobj
+399 0 obj<</Subtype/Link/Rect[144.0 222.0 183.4 235.0]/Border[0 0 0]/Dest[1836 0 R/XYZ null 224 0]>>endobj
+400 0 obj<</Subtype/Link/Rect[108.0 208.8 194.2 221.8]/Border[0 0 0]/Dest[1839 0 R/XYZ null 799 0]>>endobj
+401 0 obj<</Subtype/Link/Rect[144.0 195.6 171.5 208.6]/Border[0 0 0]/Dest[1839 0 R/XYZ null 728 0]>>endobj
+402 0 obj<</Subtype/Link/Rect[144.0 182.4 192.9 195.4]/Border[0 0 0]/Dest[1839 0 R/XYZ null 640 0]>>endobj
+403 0 obj<</Subtype/Link/Rect[144.0 169.2 178.2 182.2]/Border[0 0 0]/Dest[1839 0 R/XYZ null 499 0]>>endobj
+404 0 obj<</Subtype/Link/Rect[144.0 156.0 195.3 169.0]/Border[0 0 0]/Dest[1839 0 R/XYZ null 439 0]>>endobj
+405 0 obj<</Subtype/Link/Rect[144.0 142.8 183.1 155.8]/Border[0 0 0]/Dest[1839 0 R/XYZ null 366 0]>>endobj
+406 0 obj<</Subtype/Link/Rect[144.0 129.6 183.4 142.6]/Border[0 0 0]/Dest[1839 0 R/XYZ null 214 0]>>endobj
+407 0 obj<</Subtype/Link/Rect[108.0 116.4 174.0 129.4]/Border[0 0 0]/Dest[1842 0 R/XYZ null 799 0]>>endobj
+408 0 obj<</Subtype/Link/Rect[144.0 103.2 171.5 116.2]/Border[0 0 0]/Dest[1842 0 R/XYZ null 728 0]>>endobj
+409 0 obj<</Subtype/Link/Rect[144.0 90.0 192.9 103.0]/Border[0 0 0]/Dest[1842 0 R/XYZ null 618 0]>>endobj
+410 0 obj<</Subtype/Link/Rect[144.0 76.8 178.2 89.8]/Border[0 0 0]/Dest[1842 0 R/XYZ null 437 0]>>endobj
+411 0 obj<</Subtype/Link/Rect[144.0 63.6 195.3 76.6]/Border[0 0 0]/Dest[1842 0 R/XYZ null 377 0]>>endobj
+412 0 obj[365 0 R
+366 0 R
+367 0 R
+368 0 R
+369 0 R
+370 0 R
+371 0 R
+372 0 R
+373 0 R
+374 0 R
+375 0 R
+376 0 R
+377 0 R
+378 0 R
+379 0 R
+380 0 R
+381 0 R
+382 0 R
+383 0 R
+384 0 R
+385 0 R
+386 0 R
+387 0 R
+388 0 R
+389 0 R
+390 0 R
+391 0 R
+392 0 R
+393 0 R
+394 0 R
+395 0 R
+396 0 R
+397 0 R
+398 0 R
+399 0 R
+400 0 R
+401 0 R
+402 0 R
+403 0 R
+404 0 R
+405 0 R
+406 0 R
+407 0 R
+408 0 R
+409 0 R
+410 0 R
+411 0 R
+]endobj
+413 0 obj<</Subtype/Link/Rect[108.0 670.8 147.1 683.8]/Border[0 0 0]/Dest[1842 0 R/XYZ null 304 0]>>endobj
+414 0 obj<</Subtype/Link/Rect[108.0 657.6 147.4 670.6]/Border[0 0 0]/Dest[1845 0 R/XYZ null 782 0]>>endobj
+415 0 obj<</Subtype/Link/Rect[72.0 644.4 152.7 657.4]/Border[0 0 0]/Dest[1848 0 R/XYZ null 799 0]>>endobj
+416 0 obj<</Subtype/Link/Rect[108.0 631.2 135.5 644.2]/Border[0 0 0]/Dest[1848 0 R/XYZ null 728 0]>>endobj
+417 0 obj<</Subtype/Link/Rect[108.0 618.0 156.9 631.0]/Border[0 0 0]/Dest[1848 0 R/XYZ null 662 0]>>endobj
+418 0 obj<</Subtype/Link/Rect[108.0 604.8 159.3 617.8]/Border[0 0 0]/Dest[1848 0 R/XYZ null 561 0]>>endobj
+419 0 obj<</Subtype/Link/Rect[108.0 591.6 147.1 604.6]/Border[0 0 0]/Dest[1848 0 R/XYZ null 501 0]>>endobj
+420 0 obj<</Subtype/Link/Rect[108.0 578.4 147.4 591.4]/Border[0 0 0]/Dest[1848 0 R/XYZ null 381 0]>>endobj
+421 0 obj<</Subtype/Link/Rect[72.0 565.2 151.4 578.2]/Border[0 0 0]/Dest[1851 0 R/XYZ null 799 0]>>endobj
+422 0 obj<</Subtype/Link/Rect[108.0 552.0 135.5 565.0]/Border[0 0 0]/Dest[1851 0 R/XYZ null 728 0]>>endobj
+423 0 obj<</Subtype/Link/Rect[108.0 538.8 156.9 551.8]/Border[0 0 0]/Dest[1851 0 R/XYZ null 651 0]>>endobj
+424 0 obj<</Subtype/Link/Rect[108.0 525.6 142.2 538.6]/Border[0 0 0]/Dest[1851 0 R/XYZ null 530 0]>>endobj
+425 0 obj<</Subtype/Link/Rect[108.0 512.4 159.3 525.4]/Border[0 0 0]/Dest[1851 0 R/XYZ null 470 0]>>endobj
+426 0 obj<</Subtype/Link/Rect[108.0 499.2 147.1 512.2]/Border[0 0 0]/Dest[1851 0 R/XYZ null 410 0]>>endobj
+427 0 obj<</Subtype/Link/Rect[108.0 486.0 147.4 499.0]/Border[0 0 0]/Dest[1851 0 R/XYZ null 290 0]>>endobj
+428 0 obj<</Subtype/Link/Rect[72.0 472.8 182.0 485.8]/Border[0 0 0]/Dest[1854 0 R/XYZ null 799 0]>>endobj
+429 0 obj<</Subtype/Link/Rect[108.0 459.6 135.5 472.6]/Border[0 0 0]/Dest[1854 0 R/XYZ null 728 0]>>endobj
+430 0 obj<</Subtype/Link/Rect[108.0 446.4 156.9 459.4]/Border[0 0 0]/Dest[1854 0 R/XYZ null 651 0]>>endobj
+431 0 obj<</Subtype/Link/Rect[108.0 433.2 142.2 446.2]/Border[0 0 0]/Dest[1854 0 R/XYZ null 530 0]>>endobj
+432 0 obj<</Subtype/Link/Rect[108.0 420.0 159.3 433.0]/Border[0 0 0]/Dest[1854 0 R/XYZ null 470 0]>>endobj
+433 0 obj<</Subtype/Link/Rect[108.0 406.8 147.1 419.8]/Border[0 0 0]/Dest[1854 0 R/XYZ null 410 0]>>endobj
+434 0 obj<</Subtype/Link/Rect[108.0 393.6 147.4 406.6]/Border[0 0 0]/Dest[1857 0 R/XYZ null 782 0]>>endobj
+435 0 obj<</Subtype/Link/Rect[72.0 380.4 177.1 393.4]/Border[0 0 0]/Dest[1860 0 R/XYZ null 799 0]>>endobj
+436 0 obj<</Subtype/Link/Rect[108.0 367.2 135.5 380.2]/Border[0 0 0]/Dest[1860 0 R/XYZ null 728 0]>>endobj
+437 0 obj<</Subtype/Link/Rect[108.0 354.0 156.9 367.0]/Border[0 0 0]/Dest[1860 0 R/XYZ null 640 0]>>endobj
+438 0 obj<</Subtype/Link/Rect[108.0 340.8 142.2 353.8]/Border[0 0 0]/Dest[1860 0 R/XYZ null 499 0]>>endobj
+439 0 obj<</Subtype/Link/Rect[108.0 327.6 159.3 340.6]/Border[0 0 0]/Dest[1860 0 R/XYZ null 439 0]>>endobj
+440 0 obj<</Subtype/Link/Rect[108.0 314.4 147.1 327.4]/Border[0 0 0]/Dest[1860 0 R/XYZ null 379 0]>>endobj
+441 0 obj<</Subtype/Link/Rect[108.0 301.2 147.4 314.2]/Border[0 0 0]/Dest[1863 0 R/XYZ null 782 0]>>endobj
+442 0 obj<</Subtype/Link/Rect[72.0 288.0 184.4 301.0]/Border[0 0 0]/Dest[1866 0 R/XYZ null 799 0]>>endobj
+443 0 obj<</Subtype/Link/Rect[108.0 274.8 135.5 287.8]/Border[0 0 0]/Dest[1866 0 R/XYZ null 728 0]>>endobj
+444 0 obj<</Subtype/Link/Rect[108.0 261.6 156.9 274.6]/Border[0 0 0]/Dest[1866 0 R/XYZ null 651 0]>>endobj
+445 0 obj<</Subtype/Link/Rect[108.0 248.4 142.2 261.4]/Border[0 0 0]/Dest[1866 0 R/XYZ null 530 0]>>endobj
+446 0 obj<</Subtype/Link/Rect[108.0 235.2 159.3 248.2]/Border[0 0 0]/Dest[1866 0 R/XYZ null 470 0]>>endobj
+447 0 obj<</Subtype/Link/Rect[108.0 222.0 147.1 235.0]/Border[0 0 0]/Dest[1866 0 R/XYZ null 410 0]>>endobj
+448 0 obj<</Subtype/Link/Rect[108.0 208.8 147.4 221.8]/Border[0 0 0]/Dest[1866 0 R/XYZ null 193 0]>>endobj
+449 0 obj<</Subtype/Link/Rect[72.0 195.6 179.5 208.6]/Border[0 0 0]/Dest[1869 0 R/XYZ null 799 0]>>endobj
+450 0 obj<</Subtype/Link/Rect[108.0 182.4 135.5 195.4]/Border[0 0 0]/Dest[1869 0 R/XYZ null 728 0]>>endobj
+451 0 obj<</Subtype/Link/Rect[108.0 169.2 156.9 182.2]/Border[0 0 0]/Dest[1869 0 R/XYZ null 640 0]>>endobj
+452 0 obj<</Subtype/Link/Rect[108.0 156.0 142.2 169.0]/Border[0 0 0]/Dest[1869 0 R/XYZ null 499 0]>>endobj
+453 0 obj<</Subtype/Link/Rect[108.0 142.8 159.3 155.8]/Border[0 0 0]/Dest[1869 0 R/XYZ null 439 0]>>endobj
+454 0 obj<</Subtype/Link/Rect[108.0 129.6 147.1 142.6]/Border[0 0 0]/Dest[1869 0 R/XYZ null 379 0]>>endobj
+455 0 obj<</Subtype/Link/Rect[108.0 116.4 147.4 129.4]/Border[0 0 0]/Dest[1872 0 R/XYZ null 782 0]>>endobj
+456 0 obj<</Subtype/Link/Rect[72.0 103.2 128.2 116.2]/Border[0 0 0]/Dest[1875 0 R/XYZ null 799 0]>>endobj
+457 0 obj<</Subtype/Link/Rect[108.0 90.0 135.5 103.0]/Border[0 0 0]/Dest[1875 0 R/XYZ null 728 0]>>endobj
+458 0 obj<</Subtype/Link/Rect[108.0 76.8 142.2 89.8]/Border[0 0 0]/Dest[1875 0 R/XYZ null 662 0]>>endobj
+459 0 obj<</Subtype/Link/Rect[108.0 63.6 159.3 76.6]/Border[0 0 0]/Dest[1875 0 R/XYZ null 602 0]>>endobj
+460 0 obj[413 0 R
+414 0 R
+415 0 R
+416 0 R
+417 0 R
+418 0 R
+419 0 R
+420 0 R
+421 0 R
+422 0 R
+423 0 R
+424 0 R
+425 0 R
+426 0 R
+427 0 R
+428 0 R
+429 0 R
+430 0 R
+431 0 R
+432 0 R
+433 0 R
+434 0 R
+435 0 R
+436 0 R
+437 0 R
+438 0 R
+439 0 R
+440 0 R
+441 0 R
+442 0 R
+443 0 R
+444 0 R
+445 0 R
+446 0 R
+447 0 R
+448 0 R
+449 0 R
+450 0 R
+451 0 R
+452 0 R
+453 0 R
+454 0 R
+455 0 R
+456 0 R
+457 0 R
+458 0 R
+459 0 R
+]endobj
+461 0 obj<</Subtype/Link/Rect[144.0 670.8 183.1 683.8]/Border[0 0 0]/Dest[1875 0 R/XYZ null 449 0]>>endobj
+462 0 obj<</Subtype/Link/Rect[144.0 657.6 183.4 670.6]/Border[0 0 0]/Dest[1875 0 R/XYZ null 351 0]>>endobj
+463 0 obj<</Subtype/Link/Rect[108.0 644.4 178.3 657.4]/Border[0 0 0]/Dest[1878 0 R/XYZ null 799 0]>>endobj
+464 0 obj<</Subtype/Link/Rect[144.0 631.2 171.5 644.2]/Border[0 0 0]/Dest[1878 0 R/XYZ null 728 0]>>endobj
+465 0 obj<</Subtype/Link/Rect[144.0 618.0 192.9 631.0]/Border[0 0 0]/Dest[1878 0 R/XYZ null 651 0]>>endobj
+466 0 obj<</Subtype/Link/Rect[144.0 604.8 178.2 617.8]/Border[0 0 0]/Dest[1878 0 R/XYZ null 530 0]>>endobj
+467 0 obj<</Subtype/Link/Rect[144.0 591.6 195.3 604.6]/Border[0 0 0]/Dest[1878 0 R/XYZ null 470 0]>>endobj
+468 0 obj<</Subtype/Link/Rect[144.0 578.4 183.1 591.4]/Border[0 0 0]/Dest[1878 0 R/XYZ null 397 0]>>endobj
+469 0 obj<</Subtype/Link/Rect[108.0 565.2 156.3 578.2]/Border[0 0 0]/Dest[1881 0 R/XYZ null 799 0]>>endobj
+470 0 obj<</Subtype/Link/Rect[144.0 552.0 171.5 565.0]/Border[0 0 0]/Dest[1881 0 R/XYZ null 728 0]>>endobj
+471 0 obj<</Subtype/Link/Rect[144.0 538.8 178.2 551.8]/Border[0 0 0]/Dest[1881 0 R/XYZ null 662 0]>>endobj
+472 0 obj<</Subtype/Link/Rect[144.0 525.6 195.3 538.6]/Border[0 0 0]/Dest[1881 0 R/XYZ null 602 0]>>endobj
+473 0 obj<</Subtype/Link/Rect[144.0 512.4 183.1 525.4]/Border[0 0 0]/Dest[1881 0 R/XYZ null 528 0]>>endobj
+474 0 obj<</Subtype/Link/Rect[144.0 499.2 183.4 512.2]/Border[0 0 0]/Dest[1881 0 R/XYZ null 430 0]>>endobj
+475 0 obj<</Subtype/Link/Rect[108.0 486.0 172.8 499.0]/Border[0 0 0]/Dest[1884 0 R/XYZ null 799 0]>>endobj
+476 0 obj<</Subtype/Link/Rect[144.0 472.8 171.5 485.8]/Border[0 0 0]/Dest[1884 0 R/XYZ null 728 0]>>endobj
+477 0 obj<</Subtype/Link/Rect[144.0 459.6 192.9 472.6]/Border[0 0 0]/Dest[1884 0 R/XYZ null 683 0]>>endobj
+478 0 obj<</Subtype/Link/Rect[144.0 446.4 178.2 459.4]/Border[0 0 0]/Dest[1884 0 R/XYZ null 596 0]>>endobj
+479 0 obj<</Subtype/Link/Rect[144.0 433.2 195.3 446.2]/Border[0 0 0]/Dest[1884 0 R/XYZ null 546 0]>>endobj
+480 0 obj<</Subtype/Link/Rect[144.0 420.0 183.1 433.0]/Border[0 0 0]/Dest[1884 0 R/XYZ null 495 0]>>endobj
+481 0 obj<</Subtype/Link/Rect[144.0 406.8 183.4 419.8]/Border[0 0 0]/Dest[1884 0 R/XYZ null 451 0]>>endobj
+482 0 obj<</Subtype/Link/Rect[108.0 393.6 160.5 406.6]/Border[0 0 0]/Dest[1887 0 R/XYZ null 799 0]>>endobj
+483 0 obj<</Subtype/Link/Rect[144.0 380.4 171.5 393.4]/Border[0 0 0]/Dest[1887 0 R/XYZ null 728 0]>>endobj
+484 0 obj<</Subtype/Link/Rect[144.0 367.2 192.9 380.2]/Border[0 0 0]/Dest[1887 0 R/XYZ null 683 0]>>endobj
+485 0 obj<</Subtype/Link/Rect[144.0 354.0 178.2 367.0]/Border[0 0 0]/Dest[1887 0 R/XYZ null 596 0]>>endobj
+486 0 obj<</Subtype/Link/Rect[144.0 340.8 195.3 353.8]/Border[0 0 0]/Dest[1887 0 R/XYZ null 546 0]>>endobj
+487 0 obj<</Subtype/Link/Rect[144.0 327.6 183.1 340.6]/Border[0 0 0]/Dest[1887 0 R/XYZ null 495 0]>>endobj
+488 0 obj<</Subtype/Link/Rect[144.0 314.4 183.4 327.4]/Border[0 0 0]/Dest[1887 0 R/XYZ null 451 0]>>endobj
+489 0 obj<</Subtype/Link/Rect[108.0 301.2 183.2 314.2]/Border[0 0 0]/Dest[1890 0 R/XYZ null 799 0]>>endobj
+490 0 obj<</Subtype/Link/Rect[144.0 288.0 171.5 301.0]/Border[0 0 0]/Dest[1890 0 R/XYZ null 728 0]>>endobj
+491 0 obj<</Subtype/Link/Rect[144.0 274.8 192.9 287.8]/Border[0 0 0]/Dest[1890 0 R/XYZ null 683 0]>>endobj
+492 0 obj<</Subtype/Link/Rect[144.0 261.6 178.2 274.6]/Border[0 0 0]/Dest[1890 0 R/XYZ null 596 0]>>endobj
+493 0 obj<</Subtype/Link/Rect[144.0 248.4 195.3 261.4]/Border[0 0 0]/Dest[1890 0 R/XYZ null 546 0]>>endobj
+494 0 obj<</Subtype/Link/Rect[144.0 235.2 183.1 248.2]/Border[0 0 0]/Dest[1890 0 R/XYZ null 495 0]>>endobj
+495 0 obj<</Subtype/Link/Rect[144.0 222.0 183.4 235.0]/Border[0 0 0]/Dest[1890 0 R/XYZ null 451 0]>>endobj
+496 0 obj<</Subtype/Link/Rect[108.0 208.8 157.5 221.8]/Border[0 0 0]/Dest[1893 0 R/XYZ null 799 0]>>endobj
+497 0 obj<</Subtype/Link/Rect[144.0 195.6 171.5 208.6]/Border[0 0 0]/Dest[1893 0 R/XYZ null 728 0]>>endobj
+498 0 obj<</Subtype/Link/Rect[144.0 182.4 192.9 195.4]/Border[0 0 0]/Dest[1893 0 R/XYZ null 683 0]>>endobj
+499 0 obj<</Subtype/Link/Rect[144.0 169.2 178.2 182.2]/Border[0 0 0]/Dest[1893 0 R/XYZ null 596 0]>>endobj
+500 0 obj<</Subtype/Link/Rect[144.0 156.0 195.3 169.0]/Border[0 0 0]/Dest[1893 0 R/XYZ null 546 0]>>endobj
+501 0 obj<</Subtype/Link/Rect[144.0 142.8 183.1 155.8]/Border[0 0 0]/Dest[1893 0 R/XYZ null 495 0]>>endobj
+502 0 obj<</Subtype/Link/Rect[144.0 129.6 183.4 142.6]/Border[0 0 0]/Dest[1893 0 R/XYZ null 451 0]>>endobj
+503 0 obj<</Subtype/Link/Rect[108.0 116.4 169.1 129.4]/Border[0 0 0]/Dest[1896 0 R/XYZ null 799 0]>>endobj
+504 0 obj<</Subtype/Link/Rect[144.0 103.2 171.5 116.2]/Border[0 0 0]/Dest[1896 0 R/XYZ null 728 0]>>endobj
+505 0 obj<</Subtype/Link/Rect[144.0 90.0 192.9 103.0]/Border[0 0 0]/Dest[1896 0 R/XYZ null 683 0]>>endobj
+506 0 obj<</Subtype/Link/Rect[144.0 76.8 178.2 89.8]/Border[0 0 0]/Dest[1896 0 R/XYZ null 596 0]>>endobj
+507 0 obj<</Subtype/Link/Rect[144.0 63.6 195.3 76.6]/Border[0 0 0]/Dest[1896 0 R/XYZ null 546 0]>>endobj
+508 0 obj[461 0 R
+462 0 R
+463 0 R
+464 0 R
+465 0 R
+466 0 R
+467 0 R
+468 0 R
+469 0 R
+470 0 R
+471 0 R
+472 0 R
+473 0 R
+474 0 R
+475 0 R
+476 0 R
+477 0 R
+478 0 R
+479 0 R
+480 0 R
+481 0 R
+482 0 R
+483 0 R
+484 0 R
+485 0 R
+486 0 R
+487 0 R
+488 0 R
+489 0 R
+490 0 R
+491 0 R
+492 0 R
+493 0 R
+494 0 R
+495 0 R
+496 0 R
+497 0 R
+498 0 R
+499 0 R
+500 0 R
+501 0 R
+502 0 R
+503 0 R
+504 0 R
+505 0 R
+506 0 R
+507 0 R
+]endobj
+509 0 obj<</Subtype/Link/Rect[108.0 670.8 147.1 683.8]/Border[0 0 0]/Dest[1896 0 R/XYZ null 495 0]>>endobj
+510 0 obj<</Subtype/Link/Rect[108.0 657.6 147.4 670.6]/Border[0 0 0]/Dest[1896 0 R/XYZ null 451 0]>>endobj
+511 0 obj<</Subtype/Link/Rect[72.0 644.4 141.0 657.4]/Border[0 0 0]/Dest[1899 0 R/XYZ null 799 0]>>endobj
+512 0 obj<</Subtype/Link/Rect[108.0 631.2 135.5 644.2]/Border[0 0 0]/Dest[1899 0 R/XYZ null 728 0]>>endobj
+513 0 obj<</Subtype/Link/Rect[108.0 618.0 156.9 631.0]/Border[0 0 0]/Dest[1899 0 R/XYZ null 683 0]>>endobj
+514 0 obj<</Subtype/Link/Rect[108.0 604.8 142.2 617.8]/Border[0 0 0]/Dest[1899 0 R/XYZ null 596 0]>>endobj
+515 0 obj<</Subtype/Link/Rect[108.0 591.6 159.3 604.6]/Border[0 0 0]/Dest[1899 0 R/XYZ null 546 0]>>endobj
+516 0 obj<</Subtype/Link/Rect[108.0 578.4 147.1 591.4]/Border[0 0 0]/Dest[1899 0 R/XYZ null 495 0]>>endobj
+517 0 obj<</Subtype/Link/Rect[108.0 565.2 147.4 578.2]/Border[0 0 0]/Dest[1899 0 R/XYZ null 451 0]>>endobj
+518 0 obj<</Subtype/Link/Rect[72.0 552.0 125.2 565.0]/Border[0 0 0]/Dest[1902 0 R/XYZ null 799 0]>>endobj
+519 0 obj<</Subtype/Link/Rect[108.0 538.8 135.5 551.8]/Border[0 0 0]/Dest[1902 0 R/XYZ null 728 0]>>endobj
+520 0 obj<</Subtype/Link/Rect[108.0 525.6 156.9 538.6]/Border[0 0 0]/Dest[1902 0 R/XYZ null 683 0]>>endobj
+521 0 obj<</Subtype/Link/Rect[108.0 512.4 142.2 525.4]/Border[0 0 0]/Dest[1902 0 R/XYZ null 596 0]>>endobj
+522 0 obj<</Subtype/Link/Rect[108.0 499.2 159.3 512.2]/Border[0 0 0]/Dest[1902 0 R/XYZ null 546 0]>>endobj
+523 0 obj<</Subtype/Link/Rect[108.0 486.0 147.1 499.0]/Border[0 0 0]/Dest[1902 0 R/XYZ null 495 0]>>endobj
+524 0 obj<</Subtype/Link/Rect[108.0 472.8 147.4 485.8]/Border[0 0 0]/Dest[1902 0 R/XYZ null 451 0]>>endobj
+525 0 obj<</Subtype/Link/Rect[72.0 459.6 140.4 472.6]/Border[0 0 0]/Dest[1905 0 R/XYZ null 799 0]>>endobj
+526 0 obj<</Subtype/Link/Rect[108.0 446.4 135.5 459.4]/Border[0 0 0]/Dest[1905 0 R/XYZ null 728 0]>>endobj
+527 0 obj<</Subtype/Link/Rect[108.0 433.2 156.9 446.2]/Border[0 0 0]/Dest[1905 0 R/XYZ null 683 0]>>endobj
+528 0 obj<</Subtype/Link/Rect[108.0 420.0 142.2 433.0]/Border[0 0 0]/Dest[1905 0 R/XYZ null 596 0]>>endobj
+529 0 obj<</Subtype/Link/Rect[108.0 406.8 159.3 419.8]/Border[0 0 0]/Dest[1905 0 R/XYZ null 546 0]>>endobj
+530 0 obj<</Subtype/Link/Rect[108.0 393.6 147.1 406.6]/Border[0 0 0]/Dest[1905 0 R/XYZ null 495 0]>>endobj
+531 0 obj<</Subtype/Link/Rect[108.0 380.4 147.4 393.4]/Border[0 0 0]/Dest[1905 0 R/XYZ null 451 0]>>endobj
+532 0 obj<</Subtype/Link/Rect[72.0 367.2 119.7 380.2]/Border[0 0 0]/Dest[1908 0 R/XYZ null 799 0]>>endobj
+533 0 obj<</Subtype/Link/Rect[108.0 354.0 135.5 367.0]/Border[0 0 0]/Dest[1908 0 R/XYZ null 728 0]>>endobj
+534 0 obj<</Subtype/Link/Rect[108.0 340.8 156.9 353.8]/Border[0 0 0]/Dest[1908 0 R/XYZ null 683 0]>>endobj
+535 0 obj<</Subtype/Link/Rect[108.0 327.6 142.2 340.6]/Border[0 0 0]/Dest[1908 0 R/XYZ null 596 0]>>endobj
+536 0 obj<</Subtype/Link/Rect[108.0 314.4 159.3 327.4]/Border[0 0 0]/Dest[1908 0 R/XYZ null 546 0]>>endobj
+537 0 obj<</Subtype/Link/Rect[108.0 301.2 147.1 314.2]/Border[0 0 0]/Dest[1908 0 R/XYZ null 495 0]>>endobj
+538 0 obj<</Subtype/Link/Rect[108.0 288.0 147.4 301.0]/Border[0 0 0]/Dest[1908 0 R/XYZ null 451 0]>>endobj
+539 0 obj<</Subtype/Link/Rect[72.0 274.8 120.9 287.8]/Border[0 0 0]/Dest[1911 0 R/XYZ null 799 0]>>endobj
+540 0 obj<</Subtype/Link/Rect[108.0 261.6 135.5 274.6]/Border[0 0 0]/Dest[1911 0 R/XYZ null 728 0]>>endobj
+541 0 obj<</Subtype/Link/Rect[108.0 248.4 156.9 261.4]/Border[0 0 0]/Dest[1911 0 R/XYZ null 683 0]>>endobj
+542 0 obj<</Subtype/Link/Rect[108.0 235.2 142.2 248.2]/Border[0 0 0]/Dest[1911 0 R/XYZ null 596 0]>>endobj
+543 0 obj<</Subtype/Link/Rect[108.0 222.0 159.3 235.0]/Border[0 0 0]/Dest[1911 0 R/XYZ null 546 0]>>endobj
+544 0 obj<</Subtype/Link/Rect[108.0 208.8 147.1 221.8]/Border[0 0 0]/Dest[1911 0 R/XYZ null 495 0]>>endobj
+545 0 obj<</Subtype/Link/Rect[108.0 195.6 147.4 208.6]/Border[0 0 0]/Dest[1911 0 R/XYZ null 451 0]>>endobj
+546 0 obj<</Subtype/Link/Rect[72.0 182.4 112.3 195.4]/Border[0 0 0]/Dest[1914 0 R/XYZ null 799 0]>>endobj
+547 0 obj<</Subtype/Link/Rect[108.0 169.2 135.5 182.2]/Border[0 0 0]/Dest[1914 0 R/XYZ null 728 0]>>endobj
+548 0 obj<</Subtype/Link/Rect[108.0 156.0 156.9 169.0]/Border[0 0 0]/Dest[1914 0 R/XYZ null 683 0]>>endobj
+549 0 obj<</Subtype/Link/Rect[108.0 142.8 142.2 155.8]/Border[0 0 0]/Dest[1914 0 R/XYZ null 596 0]>>endobj
+550 0 obj<</Subtype/Link/Rect[108.0 129.6 159.3 142.6]/Border[0 0 0]/Dest[1914 0 R/XYZ null 546 0]>>endobj
+551 0 obj<</Subtype/Link/Rect[108.0 116.4 147.1 129.4]/Border[0 0 0]/Dest[1914 0 R/XYZ null 495 0]>>endobj
+552 0 obj<</Subtype/Link/Rect[108.0 103.2 147.4 116.2]/Border[0 0 0]/Dest[1914 0 R/XYZ null 451 0]>>endobj
+553 0 obj<</Subtype/Link/Rect[72.0 90.0 116.6 103.0]/Border[0 0 0]/Dest[1917 0 R/XYZ null 799 0]>>endobj
+554 0 obj<</Subtype/Link/Rect[108.0 76.8 135.5 89.8]/Border[0 0 0]/Dest[1917 0 R/XYZ null 728 0]>>endobj
+555 0 obj<</Subtype/Link/Rect[108.0 63.6 156.9 76.6]/Border[0 0 0]/Dest[1917 0 R/XYZ null 683 0]>>endobj
+556 0 obj[509 0 R
+510 0 R
+511 0 R
+512 0 R
+513 0 R
+514 0 R
+515 0 R
+516 0 R
+517 0 R
+518 0 R
+519 0 R
+520 0 R
+521 0 R
+522 0 R
+523 0 R
+524 0 R
+525 0 R
+526 0 R
+527 0 R
+528 0 R
+529 0 R
+530 0 R
+531 0 R
+532 0 R
+533 0 R
+534 0 R
+535 0 R
+536 0 R
+537 0 R
+538 0 R
+539 0 R
+540 0 R
+541 0 R
+542 0 R
+543 0 R
+544 0 R
+545 0 R
+546 0 R
+547 0 R
+548 0 R
+549 0 R
+550 0 R
+551 0 R
+552 0 R
+553 0 R
+554 0 R
+555 0 R
+]endobj
+557 0 obj<</Subtype/Link/Rect[144.0 670.8 178.2 683.8]/Border[0 0 0]/Dest[1917 0 R/XYZ null 596 0]>>endobj
+558 0 obj<</Subtype/Link/Rect[144.0 657.6 195.3 670.6]/Border[0 0 0]/Dest[1917 0 R/XYZ null 546 0]>>endobj
+559 0 obj<</Subtype/Link/Rect[144.0 644.4 183.1 657.4]/Border[0 0 0]/Dest[1917 0 R/XYZ null 495 0]>>endobj
+560 0 obj<</Subtype/Link/Rect[144.0 631.2 183.4 644.2]/Border[0 0 0]/Dest[1917 0 R/XYZ null 451 0]>>endobj
+561 0 obj<</Subtype/Link/Rect[108.0 618.0 196.0 631.0]/Border[0 0 0]/Dest[1920 0 R/XYZ null 799 0]>>endobj
+562 0 obj<</Subtype/Link/Rect[144.0 604.8 171.5 617.8]/Border[0 0 0]/Dest[1920 0 R/XYZ null 728 0]>>endobj
+563 0 obj<</Subtype/Link/Rect[144.0 591.6 192.9 604.6]/Border[0 0 0]/Dest[1920 0 R/XYZ null 683 0]>>endobj
+564 0 obj<</Subtype/Link/Rect[144.0 578.4 178.2 591.4]/Border[0 0 0]/Dest[1920 0 R/XYZ null 596 0]>>endobj
+565 0 obj<</Subtype/Link/Rect[144.0 565.2 195.3 578.2]/Border[0 0 0]/Dest[1920 0 R/XYZ null 546 0]>>endobj
+566 0 obj<</Subtype/Link/Rect[144.0 552.0 183.1 565.0]/Border[0 0 0]/Dest[1920 0 R/XYZ null 495 0]>>endobj
+567 0 obj<</Subtype/Link/Rect[144.0 538.8 183.4 551.8]/Border[0 0 0]/Dest[1920 0 R/XYZ null 451 0]>>endobj
+568 0 obj<</Subtype/Link/Rect[108.0 525.6 192.3 538.6]/Border[0 0 0]/Dest[1923 0 R/XYZ null 799 0]>>endobj
+569 0 obj<</Subtype/Link/Rect[144.0 512.4 171.5 525.4]/Border[0 0 0]/Dest[1923 0 R/XYZ null 728 0]>>endobj
+570 0 obj<</Subtype/Link/Rect[144.0 499.2 192.9 512.2]/Border[0 0 0]/Dest[1923 0 R/XYZ null 683 0]>>endobj
+571 0 obj<</Subtype/Link/Rect[144.0 486.0 178.2 499.0]/Border[0 0 0]/Dest[1923 0 R/XYZ null 596 0]>>endobj
+572 0 obj<</Subtype/Link/Rect[144.0 472.8 195.3 485.8]/Border[0 0 0]/Dest[1923 0 R/XYZ null 546 0]>>endobj
+573 0 obj<</Subtype/Link/Rect[144.0 459.6 183.1 472.6]/Border[0 0 0]/Dest[1923 0 R/XYZ null 495 0]>>endobj
+574 0 obj<</Subtype/Link/Rect[144.0 446.4 183.4 459.4]/Border[0 0 0]/Dest[1923 0 R/XYZ null 451 0]>>endobj
+575 0 obj<</Subtype/Link/Rect[108.0 433.2 170.9 446.2]/Border[0 0 0]/Dest[1926 0 R/XYZ null 799 0]>>endobj
+576 0 obj<</Subtype/Link/Rect[144.0 420.0 171.5 433.0]/Border[0 0 0]/Dest[1926 0 R/XYZ null 728 0]>>endobj
+577 0 obj<</Subtype/Link/Rect[144.0 406.8 192.9 419.8]/Border[0 0 0]/Dest[1926 0 R/XYZ null 683 0]>>endobj
+578 0 obj<</Subtype/Link/Rect[144.0 393.6 178.2 406.6]/Border[0 0 0]/Dest[1926 0 R/XYZ null 596 0]>>endobj
+579 0 obj<</Subtype/Link/Rect[144.0 380.4 195.3 393.4]/Border[0 0 0]/Dest[1926 0 R/XYZ null 546 0]>>endobj
+580 0 obj<</Subtype/Link/Rect[144.0 367.2 183.1 380.2]/Border[0 0 0]/Dest[1926 0 R/XYZ null 495 0]>>endobj
+581 0 obj<</Subtype/Link/Rect[144.0 354.0 183.4 367.0]/Border[0 0 0]/Dest[1926 0 R/XYZ null 451 0]>>endobj
+582 0 obj<</Subtype/Link/Rect[108.0 340.8 179.5 353.8]/Border[0 0 0]/Dest[1929 0 R/XYZ null 799 0]>>endobj
+583 0 obj<</Subtype/Link/Rect[144.0 327.6 171.5 340.6]/Border[0 0 0]/Dest[1929 0 R/XYZ null 728 0]>>endobj
+584 0 obj<</Subtype/Link/Rect[144.0 314.4 192.9 327.4]/Border[0 0 0]/Dest[1929 0 R/XYZ null 683 0]>>endobj
+585 0 obj<</Subtype/Link/Rect[144.0 301.2 178.2 314.2]/Border[0 0 0]/Dest[1929 0 R/XYZ null 596 0]>>endobj
+586 0 obj<</Subtype/Link/Rect[144.0 288.0 195.3 301.0]/Border[0 0 0]/Dest[1929 0 R/XYZ null 546 0]>>endobj
+587 0 obj<</Subtype/Link/Rect[144.0 274.8 183.1 287.8]/Border[0 0 0]/Dest[1929 0 R/XYZ null 495 0]>>endobj
+588 0 obj<</Subtype/Link/Rect[144.0 261.6 183.4 274.6]/Border[0 0 0]/Dest[1929 0 R/XYZ null 451 0]>>endobj
+589 0 obj<</Subtype/Link/Rect[108.0 248.4 155.7 261.4]/Border[0 0 0]/Dest[1932 0 R/XYZ null 799 0]>>endobj
+590 0 obj<</Subtype/Link/Rect[144.0 235.2 171.5 248.2]/Border[0 0 0]/Dest[1932 0 R/XYZ null 728 0]>>endobj
+591 0 obj<</Subtype/Link/Rect[144.0 222.0 192.9 235.0]/Border[0 0 0]/Dest[1932 0 R/XYZ null 683 0]>>endobj
+592 0 obj<</Subtype/Link/Rect[144.0 208.8 178.2 221.8]/Border[0 0 0]/Dest[1932 0 R/XYZ null 596 0]>>endobj
+593 0 obj<</Subtype/Link/Rect[144.0 195.6 195.3 208.6]/Border[0 0 0]/Dest[1932 0 R/XYZ null 546 0]>>endobj
+594 0 obj<</Subtype/Link/Rect[144.0 182.4 183.1 195.4]/Border[0 0 0]/Dest[1932 0 R/XYZ null 495 0]>>endobj
+595 0 obj<</Subtype/Link/Rect[144.0 169.2 183.4 182.2]/Border[0 0 0]/Dest[1932 0 R/XYZ null 451 0]>>endobj
+596 0 obj<</Subtype/Link/Rect[108.0 156.0 171.5 169.0]/Border[0 0 0]/Dest[1935 0 R/XYZ null 799 0]>>endobj
+597 0 obj<</Subtype/Link/Rect[144.0 142.8 171.5 155.8]/Border[0 0 0]/Dest[1935 0 R/XYZ null 728 0]>>endobj
+598 0 obj<</Subtype/Link/Rect[144.0 129.6 192.9 142.6]/Border[0 0 0]/Dest[1935 0 R/XYZ null 683 0]>>endobj
+599 0 obj<</Subtype/Link/Rect[144.0 116.4 178.2 129.4]/Border[0 0 0]/Dest[1935 0 R/XYZ null 596 0]>>endobj
+600 0 obj<</Subtype/Link/Rect[144.0 103.2 195.3 116.2]/Border[0 0 0]/Dest[1935 0 R/XYZ null 546 0]>>endobj
+601 0 obj<</Subtype/Link/Rect[144.0 90.0 183.1 103.0]/Border[0 0 0]/Dest[1935 0 R/XYZ null 495 0]>>endobj
+602 0 obj<</Subtype/Link/Rect[144.0 76.8 183.4 89.8]/Border[0 0 0]/Dest[1935 0 R/XYZ null 451 0]>>endobj
+603 0 obj<</Subtype/Link/Rect[108.0 63.6 167.3 76.6]/Border[0 0 0]/Dest[1938 0 R/XYZ null 799 0]>>endobj
+604 0 obj[557 0 R
+558 0 R
+559 0 R
+560 0 R
+561 0 R
+562 0 R
+563 0 R
+564 0 R
+565 0 R
+566 0 R
+567 0 R
+568 0 R
+569 0 R
+570 0 R
+571 0 R
+572 0 R
+573 0 R
+574 0 R
+575 0 R
+576 0 R
+577 0 R
+578 0 R
+579 0 R
+580 0 R
+581 0 R
+582 0 R
+583 0 R
+584 0 R
+585 0 R
+586 0 R
+587 0 R
+588 0 R
+589 0 R
+590 0 R
+591 0 R
+592 0 R
+593 0 R
+594 0 R
+595 0 R
+596 0 R
+597 0 R
+598 0 R
+599 0 R
+600 0 R
+601 0 R
+602 0 R
+603 0 R
+]endobj
+605 0 obj<</Subtype/Link/Rect[108.0 670.8 135.5 683.8]/Border[0 0 0]/Dest[1938 0 R/XYZ null 728 0]>>endobj
+606 0 obj<</Subtype/Link/Rect[108.0 657.6 156.9 670.6]/Border[0 0 0]/Dest[1938 0 R/XYZ null 683 0]>>endobj
+607 0 obj<</Subtype/Link/Rect[108.0 644.4 142.2 657.4]/Border[0 0 0]/Dest[1938 0 R/XYZ null 596 0]>>endobj
+608 0 obj<</Subtype/Link/Rect[108.0 631.2 159.3 644.2]/Border[0 0 0]/Dest[1938 0 R/XYZ null 546 0]>>endobj
+609 0 obj<</Subtype/Link/Rect[108.0 618.0 147.1 631.0]/Border[0 0 0]/Dest[1938 0 R/XYZ null 495 0]>>endobj
+610 0 obj<</Subtype/Link/Rect[108.0 604.8 147.4 617.8]/Border[0 0 0]/Dest[1938 0 R/XYZ null 451 0]>>endobj
+611 0 obj<</Subtype/Link/Rect[72.0 591.6 115.4 604.6]/Border[0 0 0]/Dest[1941 0 R/XYZ null 799 0]>>endobj
+612 0 obj<</Subtype/Link/Rect[108.0 578.4 135.5 591.4]/Border[0 0 0]/Dest[1941 0 R/XYZ null 728 0]>>endobj
+613 0 obj<</Subtype/Link/Rect[108.0 565.2 156.9 578.2]/Border[0 0 0]/Dest[1941 0 R/XYZ null 683 0]>>endobj
+614 0 obj<</Subtype/Link/Rect[108.0 552.0 142.2 565.0]/Border[0 0 0]/Dest[1941 0 R/XYZ null 596 0]>>endobj
+615 0 obj<</Subtype/Link/Rect[108.0 538.8 159.3 551.8]/Border[0 0 0]/Dest[1941 0 R/XYZ null 546 0]>>endobj
+616 0 obj<</Subtype/Link/Rect[108.0 525.6 147.1 538.6]/Border[0 0 0]/Dest[1941 0 R/XYZ null 495 0]>>endobj
+617 0 obj<</Subtype/Link/Rect[108.0 512.4 147.4 525.4]/Border[0 0 0]/Dest[1941 0 R/XYZ null 451 0]>>endobj
+618 0 obj<</Subtype/Link/Rect[72.0 499.2 121.5 512.2]/Border[0 0 0]/Dest[1944 0 R/XYZ null 799 0]>>endobj
+619 0 obj<</Subtype/Link/Rect[108.0 486.0 135.5 499.0]/Border[0 0 0]/Dest[1944 0 R/XYZ null 728 0]>>endobj
+620 0 obj<</Subtype/Link/Rect[108.0 472.8 156.9 485.8]/Border[0 0 0]/Dest[1944 0 R/XYZ null 683 0]>>endobj
+621 0 obj<</Subtype/Link/Rect[108.0 459.6 142.2 472.6]/Border[0 0 0]/Dest[1944 0 R/XYZ null 596 0]>>endobj
+622 0 obj<</Subtype/Link/Rect[108.0 446.4 159.3 459.4]/Border[0 0 0]/Dest[1944 0 R/XYZ null 546 0]>>endobj
+623 0 obj<</Subtype/Link/Rect[108.0 433.2 147.1 446.2]/Border[0 0 0]/Dest[1944 0 R/XYZ null 495 0]>>endobj
+624 0 obj<</Subtype/Link/Rect[108.0 420.0 147.4 433.0]/Border[0 0 0]/Dest[1944 0 R/XYZ null 451 0]>>endobj
+625 0 obj<</Subtype/Link/Rect[72.0 406.8 111.1 419.8]/Border[0 0 0]/Dest[1947 0 R/XYZ null 799 0]>>endobj
+626 0 obj<</Subtype/Link/Rect[108.0 393.6 135.5 406.6]/Border[0 0 0]/Dest[1947 0 R/XYZ null 728 0]>>endobj
+627 0 obj<</Subtype/Link/Rect[108.0 380.4 156.9 393.4]/Border[0 0 0]/Dest[1947 0 R/XYZ null 683 0]>>endobj
+628 0 obj<</Subtype/Link/Rect[108.0 367.2 142.2 380.2]/Border[0 0 0]/Dest[1947 0 R/XYZ null 596 0]>>endobj
+629 0 obj<</Subtype/Link/Rect[108.0 354.0 159.3 367.0]/Border[0 0 0]/Dest[1947 0 R/XYZ null 546 0]>>endobj
+630 0 obj<</Subtype/Link/Rect[108.0 340.8 147.1 353.8]/Border[0 0 0]/Dest[1947 0 R/XYZ null 495 0]>>endobj
+631 0 obj<</Subtype/Link/Rect[108.0 327.6 147.4 340.6]/Border[0 0 0]/Dest[1947 0 R/XYZ null 451 0]>>endobj
+632 0 obj<</Subtype/Link/Rect[72.0 314.4 119.0 327.4]/Border[0 0 0]/Dest[1950 0 R/XYZ null 799 0]>>endobj
+633 0 obj<</Subtype/Link/Rect[108.0 301.2 135.5 314.2]/Border[0 0 0]/Dest[1950 0 R/XYZ null 728 0]>>endobj
+634 0 obj<</Subtype/Link/Rect[108.0 288.0 156.9 301.0]/Border[0 0 0]/Dest[1950 0 R/XYZ null 683 0]>>endobj
+635 0 obj<</Subtype/Link/Rect[108.0 274.8 142.2 287.8]/Border[0 0 0]/Dest[1950 0 R/XYZ null 596 0]>>endobj
+636 0 obj<</Subtype/Link/Rect[108.0 261.6 159.3 274.6]/Border[0 0 0]/Dest[1950 0 R/XYZ null 546 0]>>endobj
+637 0 obj<</Subtype/Link/Rect[108.0 248.4 147.1 261.4]/Border[0 0 0]/Dest[1950 0 R/XYZ null 495 0]>>endobj
+638 0 obj<</Subtype/Link/Rect[108.0 235.2 147.4 248.2]/Border[0 0 0]/Dest[1950 0 R/XYZ null 451 0]>>endobj
+639 0 obj<</Subtype/Link/Rect[72.0 222.0 142.9 235.0]/Border[0 0 0]/Dest[1953 0 R/XYZ null 799 0]>>endobj
+640 0 obj<</Subtype/Link/Rect[108.0 208.8 135.5 221.8]/Border[0 0 0]/Dest[1953 0 R/XYZ null 728 0]>>endobj
+641 0 obj<</Subtype/Link/Rect[108.0 195.6 156.9 208.6]/Border[0 0 0]/Dest[1953 0 R/XYZ null 683 0]>>endobj
+642 0 obj<</Subtype/Link/Rect[108.0 182.4 142.2 195.4]/Border[0 0 0]/Dest[1953 0 R/XYZ null 596 0]>>endobj
+643 0 obj<</Subtype/Link/Rect[108.0 169.2 159.3 182.2]/Border[0 0 0]/Dest[1953 0 R/XYZ null 546 0]>>endobj
+644 0 obj<</Subtype/Link/Rect[108.0 156.0 147.1 169.0]/Border[0 0 0]/Dest[1953 0 R/XYZ null 495 0]>>endobj
+645 0 obj<</Subtype/Link/Rect[108.0 142.8 147.4 155.8]/Border[0 0 0]/Dest[1953 0 R/XYZ null 451 0]>>endobj
+646 0 obj<</Subtype/Link/Rect[72.0 129.6 134.3 142.6]/Border[0 0 0]/Dest[1956 0 R/XYZ null 799 0]>>endobj
+647 0 obj<</Subtype/Link/Rect[108.0 116.4 135.5 129.4]/Border[0 0 0]/Dest[1956 0 R/XYZ null 728 0]>>endobj
+648 0 obj<</Subtype/Link/Rect[108.0 103.2 156.9 116.2]/Border[0 0 0]/Dest[1956 0 R/XYZ null 683 0]>>endobj
+649 0 obj<</Subtype/Link/Rect[108.0 90.0 142.2 103.0]/Border[0 0 0]/Dest[1956 0 R/XYZ null 596 0]>>endobj
+650 0 obj<</Subtype/Link/Rect[108.0 76.8 159.3 89.8]/Border[0 0 0]/Dest[1956 0 R/XYZ null 546 0]>>endobj
+651 0 obj<</Subtype/Link/Rect[108.0 63.6 147.1 76.6]/Border[0 0 0]/Dest[1956 0 R/XYZ null 495 0]>>endobj
+652 0 obj[605 0 R
+606 0 R
+607 0 R
+608 0 R
+609 0 R
+610 0 R
+611 0 R
+612 0 R
+613 0 R
+614 0 R
+615 0 R
+616 0 R
+617 0 R
+618 0 R
+619 0 R
+620 0 R
+621 0 R
+622 0 R
+623 0 R
+624 0 R
+625 0 R
+626 0 R
+627 0 R
+628 0 R
+629 0 R
+630 0 R
+631 0 R
+632 0 R
+633 0 R
+634 0 R
+635 0 R
+636 0 R
+637 0 R
+638 0 R
+639 0 R
+640 0 R
+641 0 R
+642 0 R
+643 0 R
+644 0 R
+645 0 R
+646 0 R
+647 0 R
+648 0 R
+649 0 R
+650 0 R
+651 0 R
+]endobj
+653 0 obj<</Subtype/Link/Rect[144.0 670.8 183.4 683.8]/Border[0 0 0]/Dest[1956 0 R/XYZ null 451 0]>>endobj
+654 0 obj<</Subtype/Link/Rect[108.0 657.6 169.1 670.6]/Border[0 0 0]/Dest[1959 0 R/XYZ null 799 0]>>endobj
+655 0 obj<</Subtype/Link/Rect[144.0 644.4 171.5 657.4]/Border[0 0 0]/Dest[1959 0 R/XYZ null 728 0]>>endobj
+656 0 obj<</Subtype/Link/Rect[144.0 631.2 192.9 644.2]/Border[0 0 0]/Dest[1959 0 R/XYZ null 683 0]>>endobj
+657 0 obj<</Subtype/Link/Rect[144.0 618.0 178.2 631.0]/Border[0 0 0]/Dest[1959 0 R/XYZ null 596 0]>>endobj
+658 0 obj<</Subtype/Link/Rect[144.0 604.8 195.3 617.8]/Border[0 0 0]/Dest[1959 0 R/XYZ null 546 0]>>endobj
+659 0 obj<</Subtype/Link/Rect[144.0 591.6 183.1 604.6]/Border[0 0 0]/Dest[1959 0 R/XYZ null 495 0]>>endobj
+660 0 obj<</Subtype/Link/Rect[144.0 578.4 183.4 591.4]/Border[0 0 0]/Dest[1959 0 R/XYZ null 451 0]>>endobj
+661 0 obj<</Subtype/Link/Rect[108.0 565.2 157.5 578.2]/Border[0 0 0]/Dest[1962 0 R/XYZ null 799 0]>>endobj
+662 0 obj<</Subtype/Link/Rect[144.0 552.0 171.5 565.0]/Border[0 0 0]/Dest[1962 0 R/XYZ null 728 0]>>endobj
+663 0 obj<</Subtype/Link/Rect[144.0 538.8 192.9 551.8]/Border[0 0 0]/Dest[1962 0 R/XYZ null 683 0]>>endobj
+664 0 obj<</Subtype/Link/Rect[144.0 525.6 178.2 538.6]/Border[0 0 0]/Dest[1962 0 R/XYZ null 596 0]>>endobj
+665 0 obj<</Subtype/Link/Rect[144.0 512.4 195.3 525.4]/Border[0 0 0]/Dest[1962 0 R/XYZ null 546 0]>>endobj
+666 0 obj<</Subtype/Link/Rect[144.0 499.2 183.1 512.2]/Border[0 0 0]/Dest[1962 0 R/XYZ null 495 0]>>endobj
+667 0 obj<</Subtype/Link/Rect[144.0 486.0 183.4 499.0]/Border[0 0 0]/Dest[1962 0 R/XYZ null 451 0]>>endobj
+668 0 obj<</Subtype/Link/Rect[108.0 472.8 164.2 485.8]/Border[0 0 0]/Dest[1965 0 R/XYZ null 799 0]>>endobj
+669 0 obj<</Subtype/Link/Rect[144.0 459.6 171.5 472.6]/Border[0 0 0]/Dest[1965 0 R/XYZ null 728 0]>>endobj
+670 0 obj<</Subtype/Link/Rect[144.0 446.4 192.9 459.4]/Border[0 0 0]/Dest[1965 0 R/XYZ null 683 0]>>endobj
+671 0 obj<</Subtype/Link/Rect[144.0 433.2 178.2 446.2]/Border[0 0 0]/Dest[1965 0 R/XYZ null 596 0]>>endobj
+672 0 obj<</Subtype/Link/Rect[144.0 420.0 195.3 433.0]/Border[0 0 0]/Dest[1965 0 R/XYZ null 546 0]>>endobj
+673 0 obj<</Subtype/Link/Rect[144.0 406.8 183.1 419.8]/Border[0 0 0]/Dest[1965 0 R/XYZ null 495 0]>>endobj
+674 0 obj<</Subtype/Link/Rect[144.0 393.6 183.4 406.6]/Border[0 0 0]/Dest[1965 0 R/XYZ null 451 0]>>endobj
+675 0 obj<</Subtype/Link/Rect[108.0 380.4 157.5 393.4]/Border[0 0 0]/Dest[1968 0 R/XYZ null 799 0]>>endobj
+676 0 obj<</Subtype/Link/Rect[144.0 367.2 171.5 380.2]/Border[0 0 0]/Dest[1968 0 R/XYZ null 728 0]>>endobj
+677 0 obj<</Subtype/Link/Rect[144.0 354.0 192.9 367.0]/Border[0 0 0]/Dest[1968 0 R/XYZ null 683 0]>>endobj
+678 0 obj<</Subtype/Link/Rect[144.0 340.8 178.2 353.8]/Border[0 0 0]/Dest[1968 0 R/XYZ null 596 0]>>endobj
+679 0 obj<</Subtype/Link/Rect[144.0 327.6 195.3 340.6]/Border[0 0 0]/Dest[1968 0 R/XYZ null 546 0]>>endobj
+680 0 obj<</Subtype/Link/Rect[144.0 314.4 183.1 327.4]/Border[0 0 0]/Dest[1968 0 R/XYZ null 495 0]>>endobj
+681 0 obj<</Subtype/Link/Rect[144.0 301.2 183.4 314.2]/Border[0 0 0]/Dest[1968 0 R/XYZ null 451 0]>>endobj
+682 0 obj<</Subtype/Link/Rect[108.0 288.0 185.0 301.0]/Border[0 0 0]/Dest[1971 0 R/XYZ null 799 0]>>endobj
+683 0 obj<</Subtype/Link/Rect[144.0 274.8 171.5 287.8]/Border[0 0 0]/Dest[1971 0 R/XYZ null 728 0]>>endobj
+684 0 obj<</Subtype/Link/Rect[144.0 261.6 192.9 274.6]/Border[0 0 0]/Dest[1971 0 R/XYZ null 683 0]>>endobj
+685 0 obj<</Subtype/Link/Rect[144.0 248.4 178.2 261.4]/Border[0 0 0]/Dest[1971 0 R/XYZ null 596 0]>>endobj
+686 0 obj<</Subtype/Link/Rect[144.0 235.2 195.3 248.2]/Border[0 0 0]/Dest[1971 0 R/XYZ null 546 0]>>endobj
+687 0 obj<</Subtype/Link/Rect[144.0 222.0 183.1 235.0]/Border[0 0 0]/Dest[1971 0 R/XYZ null 495 0]>>endobj
+688 0 obj<</Subtype/Link/Rect[144.0 208.8 183.4 221.8]/Border[0 0 0]/Dest[1971 0 R/XYZ null 451 0]>>endobj
+689 0 obj<</Subtype/Link/Rect[108.0 195.6 189.3 208.6]/Border[0 0 0]/Dest[1974 0 R/XYZ null 799 0]>>endobj
+690 0 obj<</Subtype/Link/Rect[144.0 182.4 171.5 195.4]/Border[0 0 0]/Dest[1974 0 R/XYZ null 728 0]>>endobj
+691 0 obj<</Subtype/Link/Rect[144.0 169.2 192.9 182.2]/Border[0 0 0]/Dest[1974 0 R/XYZ null 683 0]>>endobj
+692 0 obj<</Subtype/Link/Rect[144.0 156.0 178.2 169.0]/Border[0 0 0]/Dest[1974 0 R/XYZ null 596 0]>>endobj
+693 0 obj<</Subtype/Link/Rect[144.0 142.8 195.3 155.8]/Border[0 0 0]/Dest[1974 0 R/XYZ null 546 0]>>endobj
+694 0 obj<</Subtype/Link/Rect[144.0 129.6 183.1 142.6]/Border[0 0 0]/Dest[1974 0 R/XYZ null 495 0]>>endobj
+695 0 obj<</Subtype/Link/Rect[144.0 116.4 183.4 129.4]/Border[0 0 0]/Dest[1974 0 R/XYZ null 451 0]>>endobj
+696 0 obj<</Subtype/Link/Rect[108.0 103.2 169.1 116.2]/Border[0 0 0]/Dest[1977 0 R/XYZ null 799 0]>>endobj
+697 0 obj<</Subtype/Link/Rect[144.0 90.0 171.5 103.0]/Border[0 0 0]/Dest[1977 0 R/XYZ null 728 0]>>endobj
+698 0 obj<</Subtype/Link/Rect[144.0 76.8 192.9 89.8]/Border[0 0 0]/Dest[1977 0 R/XYZ null 683 0]>>endobj
+699 0 obj<</Subtype/Link/Rect[144.0 63.6 178.2 76.6]/Border[0 0 0]/Dest[1977 0 R/XYZ null 596 0]>>endobj
+700 0 obj[653 0 R
+654 0 R
+655 0 R
+656 0 R
+657 0 R
+658 0 R
+659 0 R
+660 0 R
+661 0 R
+662 0 R
+663 0 R
+664 0 R
+665 0 R
+666 0 R
+667 0 R
+668 0 R
+669 0 R
+670 0 R
+671 0 R
+672 0 R
+673 0 R
+674 0 R
+675 0 R
+676 0 R
+677 0 R
+678 0 R
+679 0 R
+680 0 R
+681 0 R
+682 0 R
+683 0 R
+684 0 R
+685 0 R
+686 0 R
+687 0 R
+688 0 R
+689 0 R
+690 0 R
+691 0 R
+692 0 R
+693 0 R
+694 0 R
+695 0 R
+696 0 R
+697 0 R
+698 0 R
+699 0 R
+]endobj
+701 0 obj<</Subtype/Link/Rect[108.0 670.8 159.3 683.8]/Border[0 0 0]/Dest[1977 0 R/XYZ null 546 0]>>endobj
+702 0 obj<</Subtype/Link/Rect[108.0 657.6 147.1 670.6]/Border[0 0 0]/Dest[1977 0 R/XYZ null 495 0]>>endobj
+703 0 obj<</Subtype/Link/Rect[108.0 644.4 147.4 657.4]/Border[0 0 0]/Dest[1977 0 R/XYZ null 451 0]>>endobj
+704 0 obj<</Subtype/Link/Rect[72.0 631.2 143.5 644.2]/Border[0 0 0]/Dest[1980 0 R/XYZ null 799 0]>>endobj
+705 0 obj<</Subtype/Link/Rect[108.0 618.0 135.5 631.0]/Border[0 0 0]/Dest[1980 0 R/XYZ null 728 0]>>endobj
+706 0 obj<</Subtype/Link/Rect[108.0 604.8 156.9 617.8]/Border[0 0 0]/Dest[1980 0 R/XYZ null 683 0]>>endobj
+707 0 obj<</Subtype/Link/Rect[108.0 591.6 142.2 604.6]/Border[0 0 0]/Dest[1980 0 R/XYZ null 596 0]>>endobj
+708 0 obj<</Subtype/Link/Rect[108.0 578.4 159.3 591.4]/Border[0 0 0]/Dest[1980 0 R/XYZ null 546 0]>>endobj
+709 0 obj<</Subtype/Link/Rect[108.0 565.2 147.1 578.2]/Border[0 0 0]/Dest[1980 0 R/XYZ null 495 0]>>endobj
+710 0 obj<</Subtype/Link/Rect[108.0 552.0 147.4 565.0]/Border[0 0 0]/Dest[1980 0 R/XYZ null 451 0]>>endobj
+711 0 obj<</Subtype/Link/Rect[72.0 538.8 147.8 551.8]/Border[0 0 0]/Dest[1983 0 R/XYZ null 799 0]>>endobj
+712 0 obj<</Subtype/Link/Rect[108.0 525.6 135.5 538.6]/Border[0 0 0]/Dest[1983 0 R/XYZ null 728 0]>>endobj
+713 0 obj<</Subtype/Link/Rect[108.0 512.4 156.9 525.4]/Border[0 0 0]/Dest[1983 0 R/XYZ null 683 0]>>endobj
+714 0 obj<</Subtype/Link/Rect[108.0 499.2 142.2 512.2]/Border[0 0 0]/Dest[1983 0 R/XYZ null 596 0]>>endobj
+715 0 obj<</Subtype/Link/Rect[108.0 486.0 159.3 499.0]/Border[0 0 0]/Dest[1983 0 R/XYZ null 546 0]>>endobj
+716 0 obj<</Subtype/Link/Rect[108.0 472.8 147.1 485.8]/Border[0 0 0]/Dest[1983 0 R/XYZ null 495 0]>>endobj
+717 0 obj<</Subtype/Link/Rect[108.0 459.6 147.4 472.6]/Border[0 0 0]/Dest[1983 0 R/XYZ null 451 0]>>endobj
+718 0 obj<</Subtype/Link/Rect[72.0 446.4 140.4 459.4]/Border[0 0 0]/Dest[1986 0 R/XYZ null 799 0]>>endobj
+719 0 obj<</Subtype/Link/Rect[108.0 433.2 135.5 446.2]/Border[0 0 0]/Dest[1986 0 R/XYZ null 728 0]>>endobj
+720 0 obj<</Subtype/Link/Rect[108.0 420.0 156.9 433.0]/Border[0 0 0]/Dest[1986 0 R/XYZ null 683 0]>>endobj
+721 0 obj<</Subtype/Link/Rect[108.0 406.8 142.2 419.8]/Border[0 0 0]/Dest[1986 0 R/XYZ null 596 0]>>endobj
+722 0 obj<</Subtype/Link/Rect[108.0 393.6 159.3 406.6]/Border[0 0 0]/Dest[1986 0 R/XYZ null 546 0]>>endobj
+723 0 obj<</Subtype/Link/Rect[108.0 380.4 147.1 393.4]/Border[0 0 0]/Dest[1986 0 R/XYZ null 495 0]>>endobj
+724 0 obj<</Subtype/Link/Rect[108.0 367.2 147.4 380.2]/Border[0 0 0]/Dest[1986 0 R/XYZ null 451 0]>>endobj
+725 0 obj<</Subtype/Link/Rect[72.0 354.0 144.7 367.0]/Border[0 0 0]/Dest[1989 0 R/XYZ null 799 0]>>endobj
+726 0 obj<</Subtype/Link/Rect[108.0 340.8 135.5 353.8]/Border[0 0 0]/Dest[1989 0 R/XYZ null 728 0]>>endobj
+727 0 obj<</Subtype/Link/Rect[108.0 327.6 156.9 340.6]/Border[0 0 0]/Dest[1989 0 R/XYZ null 683 0]>>endobj
+728 0 obj<</Subtype/Link/Rect[108.0 314.4 142.2 327.4]/Border[0 0 0]/Dest[1989 0 R/XYZ null 596 0]>>endobj
+729 0 obj<</Subtype/Link/Rect[108.0 301.2 159.3 314.2]/Border[0 0 0]/Dest[1989 0 R/XYZ null 546 0]>>endobj
+730 0 obj<</Subtype/Link/Rect[108.0 288.0 147.1 301.0]/Border[0 0 0]/Dest[1989 0 R/XYZ null 495 0]>>endobj
+731 0 obj<</Subtype/Link/Rect[108.0 274.8 147.4 287.8]/Border[0 0 0]/Dest[1989 0 R/XYZ null 451 0]>>endobj
+732 0 obj<</Subtype/Link/Rect[72.0 261.6 160.0 274.6]/Border[0 0 0]/Dest[1992 0 R/XYZ null 799 0]>>endobj
+733 0 obj<</Subtype/Link/Rect[108.0 248.4 135.5 261.4]/Border[0 0 0]/Dest[1992 0 R/XYZ null 728 0]>>endobj
+734 0 obj<</Subtype/Link/Rect[108.0 235.2 156.9 248.2]/Border[0 0 0]/Dest[1992 0 R/XYZ null 683 0]>>endobj
+735 0 obj<</Subtype/Link/Rect[108.0 222.0 142.2 235.0]/Border[0 0 0]/Dest[1992 0 R/XYZ null 596 0]>>endobj
+736 0 obj<</Subtype/Link/Rect[108.0 208.8 159.3 221.8]/Border[0 0 0]/Dest[1992 0 R/XYZ null 546 0]>>endobj
+737 0 obj<</Subtype/Link/Rect[108.0 195.6 147.1 208.6]/Border[0 0 0]/Dest[1992 0 R/XYZ null 495 0]>>endobj
+738 0 obj<</Subtype/Link/Rect[108.0 182.4 147.4 195.4]/Border[0 0 0]/Dest[1992 0 R/XYZ null 451 0]>>endobj
+739 0 obj<</Subtype/Link/Rect[72.0 169.2 164.3 182.2]/Border[0 0 0]/Dest[1995 0 R/XYZ null 799 0]>>endobj
+740 0 obj<</Subtype/Link/Rect[108.0 156.0 135.5 169.0]/Border[0 0 0]/Dest[1995 0 R/XYZ null 728 0]>>endobj
+741 0 obj<</Subtype/Link/Rect[108.0 142.8 156.9 155.8]/Border[0 0 0]/Dest[1995 0 R/XYZ null 683 0]>>endobj
+742 0 obj<</Subtype/Link/Rect[108.0 129.6 142.2 142.6]/Border[0 0 0]/Dest[1995 0 R/XYZ null 596 0]>>endobj
+743 0 obj<</Subtype/Link/Rect[108.0 116.4 159.3 129.4]/Border[0 0 0]/Dest[1995 0 R/XYZ null 546 0]>>endobj
+744 0 obj<</Subtype/Link/Rect[108.0 103.2 147.1 116.2]/Border[0 0 0]/Dest[1995 0 R/XYZ null 495 0]>>endobj
+745 0 obj<</Subtype/Link/Rect[108.0 90.0 147.4 103.0]/Border[0 0 0]/Dest[1995 0 R/XYZ null 451 0]>>endobj
+746 0 obj<</Subtype/Link/Rect[72.0 76.8 154.5 89.8]/Border[0 0 0]/Dest[1998 0 R/XYZ null 799 0]>>endobj
+747 0 obj<</Subtype/Link/Rect[108.0 63.6 135.5 76.6]/Border[0 0 0]/Dest[1998 0 R/XYZ null 728 0]>>endobj
+748 0 obj[701 0 R
+702 0 R
+703 0 R
+704 0 R
+705 0 R
+706 0 R
+707 0 R
+708 0 R
+709 0 R
+710 0 R
+711 0 R
+712 0 R
+713 0 R
+714 0 R
+715 0 R
+716 0 R
+717 0 R
+718 0 R
+719 0 R
+720 0 R
+721 0 R
+722 0 R
+723 0 R
+724 0 R
+725 0 R
+726 0 R
+727 0 R
+728 0 R
+729 0 R
+730 0 R
+731 0 R
+732 0 R
+733 0 R
+734 0 R
+735 0 R
+736 0 R
+737 0 R
+738 0 R
+739 0 R
+740 0 R
+741 0 R
+742 0 R
+743 0 R
+744 0 R
+745 0 R
+746 0 R
+747 0 R
+]endobj
+749 0 obj<</Subtype/Link/Rect[144.0 670.8 192.9 683.8]/Border[0 0 0]/Dest[1998 0 R/XYZ null 683 0]>>endobj
+750 0 obj<</Subtype/Link/Rect[144.0 657.6 178.2 670.6]/Border[0 0 0]/Dest[1998 0 R/XYZ null 596 0]>>endobj
+751 0 obj<</Subtype/Link/Rect[144.0 644.4 195.3 657.4]/Border[0 0 0]/Dest[1998 0 R/XYZ null 546 0]>>endobj
+752 0 obj<</Subtype/Link/Rect[144.0 631.2 183.1 644.2]/Border[0 0 0]/Dest[1998 0 R/XYZ null 495 0]>>endobj
+753 0 obj<</Subtype/Link/Rect[144.0 618.0 183.4 631.0]/Border[0 0 0]/Dest[1998 0 R/XYZ null 451 0]>>endobj
+754 0 obj<</Subtype/Link/Rect[108.0 604.8 175.2 617.8]/Border[0 0 0]/Dest[2001 0 R/XYZ null 799 0]>>endobj
+755 0 obj<</Subtype/Link/Rect[144.0 591.6 171.5 604.6]/Border[0 0 0]/Dest[2001 0 R/XYZ null 728 0]>>endobj
+756 0 obj<</Subtype/Link/Rect[144.0 578.4 192.9 591.4]/Border[0 0 0]/Dest[2001 0 R/XYZ null 683 0]>>endobj
+757 0 obj<</Subtype/Link/Rect[144.0 565.2 178.2 578.2]/Border[0 0 0]/Dest[2001 0 R/XYZ null 596 0]>>endobj
+758 0 obj<</Subtype/Link/Rect[144.0 552.0 195.3 565.0]/Border[0 0 0]/Dest[2001 0 R/XYZ null 546 0]>>endobj
+759 0 obj<</Subtype/Link/Rect[144.0 538.8 183.1 551.8]/Border[0 0 0]/Dest[2001 0 R/XYZ null 495 0]>>endobj
+760 0 obj<</Subtype/Link/Rect[144.0 525.6 183.4 538.6]/Border[0 0 0]/Dest[2001 0 R/XYZ null 451 0]>>endobj
+761 0 obj<</Subtype/Link/Rect[108.0 512.4 179.5 525.4]/Border[0 0 0]/Dest[2004 0 R/XYZ null 799 0]>>endobj
+762 0 obj<</Subtype/Link/Rect[144.0 499.2 171.5 512.2]/Border[0 0 0]/Dest[2004 0 R/XYZ null 728 0]>>endobj
+763 0 obj<</Subtype/Link/Rect[144.0 486.0 192.9 499.0]/Border[0 0 0]/Dest[2004 0 R/XYZ null 683 0]>>endobj
+764 0 obj<</Subtype/Link/Rect[144.0 472.8 178.2 485.8]/Border[0 0 0]/Dest[2004 0 R/XYZ null 596 0]>>endobj
+765 0 obj<</Subtype/Link/Rect[144.0 459.6 195.3 472.6]/Border[0 0 0]/Dest[2004 0 R/XYZ null 546 0]>>endobj
+766 0 obj<</Subtype/Link/Rect[144.0 446.4 183.1 459.4]/Border[0 0 0]/Dest[2004 0 R/XYZ null 495 0]>>endobj
+767 0 obj<</Subtype/Link/Rect[144.0 433.2 183.4 446.2]/Border[0 0 0]/Dest[2004 0 R/XYZ null 451 0]>>endobj
+768 0 obj<</Subtype/Link/Rect[108.0 420.0 185.6 433.0]/Border[0 0 0]/Dest[2007 0 R/XYZ null 799 0]>>endobj
+769 0 obj<</Subtype/Link/Rect[144.0 406.8 171.5 419.8]/Border[0 0 0]/Dest[2007 0 R/XYZ null 728 0]>>endobj
+770 0 obj<</Subtype/Link/Rect[144.0 393.6 192.9 406.6]/Border[0 0 0]/Dest[2007 0 R/XYZ null 683 0]>>endobj
+771 0 obj<</Subtype/Link/Rect[144.0 380.4 178.2 393.4]/Border[0 0 0]/Dest[2007 0 R/XYZ null 596 0]>>endobj
+772 0 obj<</Subtype/Link/Rect[144.0 367.2 195.3 380.2]/Border[0 0 0]/Dest[2007 0 R/XYZ null 546 0]>>endobj
+773 0 obj<</Subtype/Link/Rect[144.0 354.0 183.1 367.0]/Border[0 0 0]/Dest[2007 0 R/XYZ null 495 0]>>endobj
+774 0 obj<</Subtype/Link/Rect[144.0 340.8 183.4 353.8]/Border[0 0 0]/Dest[2007 0 R/XYZ null 451 0]>>endobj
+775 0 obj<</Subtype/Link/Rect[108.0 327.6 158.1 340.6]/Border[0 0 0]/Dest[2010 0 R/XYZ null 799 0]>>endobj
+776 0 obj<</Subtype/Link/Rect[144.0 314.4 171.5 327.4]/Border[0 0 0]/Dest[2010 0 R/XYZ null 728 0]>>endobj
+777 0 obj<</Subtype/Link/Rect[144.0 301.2 192.9 314.2]/Border[0 0 0]/Dest[2010 0 R/XYZ null 683 0]>>endobj
+778 0 obj<</Subtype/Link/Rect[144.0 288.0 178.2 301.0]/Border[0 0 0]/Dest[2010 0 R/XYZ null 596 0]>>endobj
+779 0 obj<</Subtype/Link/Rect[144.0 274.8 195.3 287.8]/Border[0 0 0]/Dest[2010 0 R/XYZ null 546 0]>>endobj
+780 0 obj<</Subtype/Link/Rect[144.0 261.6 183.1 274.6]/Border[0 0 0]/Dest[2010 0 R/XYZ null 495 0]>>endobj
+781 0 obj<</Subtype/Link/Rect[144.0 248.4 183.4 261.4]/Border[0 0 0]/Dest[2010 0 R/XYZ null 451 0]>>endobj
+782 0 obj<</Subtype/Link/Rect[108.0 235.2 189.3 248.2]/Border[0 0 0]/Dest[2013 0 R/XYZ null 799 0]>>endobj
+783 0 obj<</Subtype/Link/Rect[144.0 222.0 171.5 235.0]/Border[0 0 0]/Dest[2013 0 R/XYZ null 728 0]>>endobj
+784 0 obj<</Subtype/Link/Rect[144.0 208.8 192.9 221.8]/Border[0 0 0]/Dest[2013 0 R/XYZ null 683 0]>>endobj
+785 0 obj<</Subtype/Link/Rect[144.0 195.6 178.2 208.6]/Border[0 0 0]/Dest[2013 0 R/XYZ null 596 0]>>endobj
+786 0 obj<</Subtype/Link/Rect[144.0 182.4 195.3 195.4]/Border[0 0 0]/Dest[2013 0 R/XYZ null 546 0]>>endobj
+787 0 obj<</Subtype/Link/Rect[144.0 169.2 183.1 182.2]/Border[0 0 0]/Dest[2013 0 R/XYZ null 495 0]>>endobj
+788 0 obj<</Subtype/Link/Rect[144.0 156.0 183.4 169.0]/Border[0 0 0]/Dest[2013 0 R/XYZ null 451 0]>>endobj
+789 0 obj<</Subtype/Link/Rect[108.0 142.8 160.5 155.8]/Border[0 0 0]/Dest[2016 0 R/XYZ null 799 0]>>endobj
+790 0 obj<</Subtype/Link/Rect[144.0 129.6 171.5 142.6]/Border[0 0 0]/Dest[2016 0 R/XYZ null 728 0]>>endobj
+791 0 obj<</Subtype/Link/Rect[144.0 116.4 192.9 129.4]/Border[0 0 0]/Dest[2016 0 R/XYZ null 683 0]>>endobj
+792 0 obj<</Subtype/Link/Rect[144.0 103.2 178.2 116.2]/Border[0 0 0]/Dest[2016 0 R/XYZ null 596 0]>>endobj
+793 0 obj<</Subtype/Link/Rect[144.0 90.0 195.3 103.0]/Border[0 0 0]/Dest[2016 0 R/XYZ null 546 0]>>endobj
+794 0 obj<</Subtype/Link/Rect[144.0 76.8 183.1 89.8]/Border[0 0 0]/Dest[2016 0 R/XYZ null 495 0]>>endobj
+795 0 obj<</Subtype/Link/Rect[144.0 63.6 183.4 76.6]/Border[0 0 0]/Dest[2016 0 R/XYZ null 451 0]>>endobj
+796 0 obj[749 0 R
+750 0 R
+751 0 R
+752 0 R
+753 0 R
+754 0 R
+755 0 R
+756 0 R
+757 0 R
+758 0 R
+759 0 R
+760 0 R
+761 0 R
+762 0 R
+763 0 R
+764 0 R
+765 0 R
+766 0 R
+767 0 R
+768 0 R
+769 0 R
+770 0 R
+771 0 R
+772 0 R
+773 0 R
+774 0 R
+775 0 R
+776 0 R
+777 0 R
+778 0 R
+779 0 R
+780 0 R
+781 0 R
+782 0 R
+783 0 R
+784 0 R
+785 0 R
+786 0 R
+787 0 R
+788 0 R
+789 0 R
+790 0 R
+791 0 R
+792 0 R
+793 0 R
+794 0 R
+795 0 R
+]endobj
+797 0 obj<</Subtype/Link/Rect[72.0 670.8 114.2 683.8]/Border[0 0 0]/Dest[2019 0 R/XYZ null 799 0]>>endobj
+798 0 obj<</Subtype/Link/Rect[108.0 657.6 135.5 670.6]/Border[0 0 0]/Dest[2019 0 R/XYZ null 728 0]>>endobj
+799 0 obj<</Subtype/Link/Rect[108.0 644.4 156.9 657.4]/Border[0 0 0]/Dest[2019 0 R/XYZ null 683 0]>>endobj
+800 0 obj<</Subtype/Link/Rect[108.0 631.2 142.2 644.2]/Border[0 0 0]/Dest[2019 0 R/XYZ null 596 0]>>endobj
+801 0 obj<</Subtype/Link/Rect[108.0 618.0 159.3 631.0]/Border[0 0 0]/Dest[2019 0 R/XYZ null 546 0]>>endobj
+802 0 obj<</Subtype/Link/Rect[108.0 604.8 147.1 617.8]/Border[0 0 0]/Dest[2019 0 R/XYZ null 495 0]>>endobj
+803 0 obj<</Subtype/Link/Rect[108.0 591.6 147.4 604.6]/Border[0 0 0]/Dest[2019 0 R/XYZ null 451 0]>>endobj
+804 0 obj<</Subtype/Link/Rect[72.0 578.4 111.7 591.4]/Border[0 0 0]/Dest[2022 0 R/XYZ null 799 0]>>endobj
+805 0 obj<</Subtype/Link/Rect[108.0 565.2 135.5 578.2]/Border[0 0 0]/Dest[2022 0 R/XYZ null 728 0]>>endobj
+806 0 obj<</Subtype/Link/Rect[108.0 552.0 156.9 565.0]/Border[0 0 0]/Dest[2022 0 R/XYZ null 683 0]>>endobj
+807 0 obj<</Subtype/Link/Rect[108.0 538.8 142.2 551.8]/Border[0 0 0]/Dest[2022 0 R/XYZ null 596 0]>>endobj
+808 0 obj<</Subtype/Link/Rect[108.0 525.6 159.3 538.6]/Border[0 0 0]/Dest[2022 0 R/XYZ null 546 0]>>endobj
+809 0 obj<</Subtype/Link/Rect[108.0 512.4 147.1 525.4]/Border[0 0 0]/Dest[2022 0 R/XYZ null 495 0]>>endobj
+810 0 obj<</Subtype/Link/Rect[108.0 499.2 147.4 512.2]/Border[0 0 0]/Dest[2022 0 R/XYZ null 451 0]>>endobj
+811 0 obj<</Subtype/Link/Rect[72.0 486.0 116.0 499.0]/Border[0 0 0]/Dest[2025 0 R/XYZ null 799 0]>>endobj
+812 0 obj<</Subtype/Link/Rect[108.0 472.8 135.5 485.8]/Border[0 0 0]/Dest[2025 0 R/XYZ null 728 0]>>endobj
+813 0 obj<</Subtype/Link/Rect[108.0 459.6 156.9 472.6]/Border[0 0 0]/Dest[2025 0 R/XYZ null 683 0]>>endobj
+814 0 obj<</Subtype/Link/Rect[108.0 446.4 142.2 459.4]/Border[0 0 0]/Dest[2025 0 R/XYZ null 596 0]>>endobj
+815 0 obj<</Subtype/Link/Rect[108.0 433.2 159.3 446.2]/Border[0 0 0]/Dest[2025 0 R/XYZ null 546 0]>>endobj
+816 0 obj<</Subtype/Link/Rect[108.0 420.0 147.1 433.0]/Border[0 0 0]/Dest[2025 0 R/XYZ null 495 0]>>endobj
+817 0 obj<</Subtype/Link/Rect[108.0 406.8 147.4 419.8]/Border[0 0 0]/Dest[2025 0 R/XYZ null 451 0]>>endobj
+818 0 obj<</Subtype/Link/Rect[72.0 393.6 149.6 406.6]/Border[0 0 0]/Dest[2028 0 R/XYZ null 799 0]>>endobj
+819 0 obj<</Subtype/Link/Rect[108.0 380.4 135.5 393.4]/Border[0 0 0]/Dest[2028 0 R/XYZ null 728 0]>>endobj
+820 0 obj<</Subtype/Link/Rect[108.0 367.2 156.9 380.2]/Border[0 0 0]/Dest[2028 0 R/XYZ null 683 0]>>endobj
+821 0 obj<</Subtype/Link/Rect[108.0 354.0 142.2 367.0]/Border[0 0 0]/Dest[2028 0 R/XYZ null 596 0]>>endobj
+822 0 obj<</Subtype/Link/Rect[108.0 340.8 159.3 353.8]/Border[0 0 0]/Dest[2028 0 R/XYZ null 546 0]>>endobj
+823 0 obj<</Subtype/Link/Rect[108.0 327.6 147.1 340.6]/Border[0 0 0]/Dest[2028 0 R/XYZ null 495 0]>>endobj
+824 0 obj<</Subtype/Link/Rect[108.0 314.4 147.4 327.4]/Border[0 0 0]/Dest[2028 0 R/XYZ null 451 0]>>endobj
+825 0 obj<</Subtype/Link/Rect[72.0 301.2 118.4 314.2]/Border[0 0 0]/Dest[2031 0 R/XYZ null 799 0]>>endobj
+826 0 obj<</Subtype/Link/Rect[108.0 288.0 135.5 301.0]/Border[0 0 0]/Dest[2031 0 R/XYZ null 728 0]>>endobj
+827 0 obj<</Subtype/Link/Rect[108.0 274.8 156.9 287.8]/Border[0 0 0]/Dest[2031 0 R/XYZ null 683 0]>>endobj
+828 0 obj<</Subtype/Link/Rect[108.0 261.6 142.2 274.6]/Border[0 0 0]/Dest[2031 0 R/XYZ null 596 0]>>endobj
+829 0 obj<</Subtype/Link/Rect[108.0 248.4 159.3 261.4]/Border[0 0 0]/Dest[2031 0 R/XYZ null 546 0]>>endobj
+830 0 obj<</Subtype/Link/Rect[108.0 235.2 147.1 248.2]/Border[0 0 0]/Dest[2031 0 R/XYZ null 495 0]>>endobj
+831 0 obj<</Subtype/Link/Rect[108.0 222.0 147.4 235.0]/Border[0 0 0]/Dest[2031 0 R/XYZ null 451 0]>>endobj
+832 0 obj<</Subtype/Link/Rect[72.0 208.8 120.9 221.8]/Border[0 0 0]/Dest[2034 0 R/XYZ null 799 0]>>endobj
+833 0 obj<</Subtype/Link/Rect[108.0 195.6 135.5 208.6]/Border[0 0 0]/Dest[2034 0 R/XYZ null 728 0]>>endobj
+834 0 obj<</Subtype/Link/Rect[108.0 182.4 156.9 195.4]/Border[0 0 0]/Dest[2034 0 R/XYZ null 683 0]>>endobj
+835 0 obj<</Subtype/Link/Rect[108.0 169.2 142.2 182.2]/Border[0 0 0]/Dest[2034 0 R/XYZ null 596 0]>>endobj
+836 0 obj<</Subtype/Link/Rect[108.0 156.0 159.3 169.0]/Border[0 0 0]/Dest[2034 0 R/XYZ null 546 0]>>endobj
+837 0 obj<</Subtype/Link/Rect[108.0 142.8 147.1 155.8]/Border[0 0 0]/Dest[2034 0 R/XYZ null 495 0]>>endobj
+838 0 obj<</Subtype/Link/Rect[108.0 129.6 147.4 142.6]/Border[0 0 0]/Dest[2034 0 R/XYZ null 451 0]>>endobj
+839 0 obj<</Subtype/Link/Rect[72.0 116.4 136.2 129.4]/Border[0 0 0]/Dest[2037 0 R/XYZ null 799 0]>>endobj
+840 0 obj<</Subtype/Link/Rect[108.0 103.2 135.5 116.2]/Border[0 0 0]/Dest[2037 0 R/XYZ null 728 0]>>endobj
+841 0 obj<</Subtype/Link/Rect[108.0 90.0 156.9 103.0]/Border[0 0 0]/Dest[2037 0 R/XYZ null 683 0]>>endobj
+842 0 obj<</Subtype/Link/Rect[108.0 76.8 142.2 89.8]/Border[0 0 0]/Dest[2037 0 R/XYZ null 596 0]>>endobj
+843 0 obj<</Subtype/Link/Rect[108.0 63.6 159.3 76.6]/Border[0 0 0]/Dest[2037 0 R/XYZ null 546 0]>>endobj
+844 0 obj[797 0 R
+798 0 R
+799 0 R
+800 0 R
+801 0 R
+802 0 R
+803 0 R
+804 0 R
+805 0 R
+806 0 R
+807 0 R
+808 0 R
+809 0 R
+810 0 R
+811 0 R
+812 0 R
+813 0 R
+814 0 R
+815 0 R
+816 0 R
+817 0 R
+818 0 R
+819 0 R
+820 0 R
+821 0 R
+822 0 R
+823 0 R
+824 0 R
+825 0 R
+826 0 R
+827 0 R
+828 0 R
+829 0 R
+830 0 R
+831 0 R
+832 0 R
+833 0 R
+834 0 R
+835 0 R
+836 0 R
+837 0 R
+838 0 R
+839 0 R
+840 0 R
+841 0 R
+842 0 R
+843 0 R
+]endobj
+845 0 obj<</Subtype/Link/Rect[144.0 670.8 183.1 683.8]/Border[0 0 0]/Dest[2037 0 R/XYZ null 495 0]>>endobj
+846 0 obj<</Subtype/Link/Rect[144.0 657.6 183.4 670.6]/Border[0 0 0]/Dest[2037 0 R/XYZ null 451 0]>>endobj
+847 0 obj<</Subtype/Link/Rect[108.0 644.4 164.8 657.4]/Border[0 0 0]/Dest[2040 0 R/XYZ null 799 0]>>endobj
+848 0 obj<</Subtype/Link/Rect[144.0 631.2 171.5 644.2]/Border[0 0 0]/Dest[2040 0 R/XYZ null 728 0]>>endobj
+849 0 obj<</Subtype/Link/Rect[144.0 618.0 192.9 631.0]/Border[0 0 0]/Dest[2040 0 R/XYZ null 683 0]>>endobj
+850 0 obj<</Subtype/Link/Rect[144.0 604.8 178.2 617.8]/Border[0 0 0]/Dest[2040 0 R/XYZ null 596 0]>>endobj
+851 0 obj<</Subtype/Link/Rect[144.0 591.6 195.3 604.6]/Border[0 0 0]/Dest[2040 0 R/XYZ null 546 0]>>endobj
+852 0 obj<</Subtype/Link/Rect[144.0 578.4 183.1 591.4]/Border[0 0 0]/Dest[2040 0 R/XYZ null 495 0]>>endobj
+853 0 obj<</Subtype/Link/Rect[144.0 565.2 183.4 578.2]/Border[0 0 0]/Dest[2040 0 R/XYZ null 451 0]>>endobj
+854 0 obj<</Subtype/Link/Rect[108.0 552.0 153.2 565.0]/Border[0 0 0]/Dest[2043 0 R/XYZ null 799 0]>>endobj
+855 0 obj<</Subtype/Link/Rect[144.0 538.8 171.5 551.8]/Border[0 0 0]/Dest[2043 0 R/XYZ null 728 0]>>endobj
+856 0 obj<</Subtype/Link/Rect[144.0 525.6 192.9 538.6]/Border[0 0 0]/Dest[2043 0 R/XYZ null 683 0]>>endobj
+857 0 obj<</Subtype/Link/Rect[144.0 512.4 178.2 525.4]/Border[0 0 0]/Dest[2043 0 R/XYZ null 596 0]>>endobj
+858 0 obj<</Subtype/Link/Rect[144.0 499.2 195.3 512.2]/Border[0 0 0]/Dest[2043 0 R/XYZ null 546 0]>>endobj
+859 0 obj<</Subtype/Link/Rect[144.0 486.0 183.1 499.0]/Border[0 0 0]/Dest[2043 0 R/XYZ null 495 0]>>endobj
+860 0 obj<</Subtype/Link/Rect[144.0 472.8 183.4 485.8]/Border[0 0 0]/Dest[2043 0 R/XYZ null 451 0]>>endobj
+861 0 obj<</Subtype/Link/Rect[108.0 459.6 183.2 472.6]/Border[0 0 0]/Dest[2046 0 R/XYZ null 799 0]>>endobj
+862 0 obj<</Subtype/Link/Rect[144.0 446.4 171.5 459.4]/Border[0 0 0]/Dest[2046 0 R/XYZ null 728 0]>>endobj
+863 0 obj<</Subtype/Link/Rect[144.0 433.2 192.9 446.2]/Border[0 0 0]/Dest[2046 0 R/XYZ null 683 0]>>endobj
+864 0 obj<</Subtype/Link/Rect[144.0 420.0 178.2 433.0]/Border[0 0 0]/Dest[2046 0 R/XYZ null 596 0]>>endobj
+865 0 obj<</Subtype/Link/Rect[144.0 406.8 195.3 419.8]/Border[0 0 0]/Dest[2046 0 R/XYZ null 546 0]>>endobj
+866 0 obj<</Subtype/Link/Rect[144.0 393.6 183.1 406.6]/Border[0 0 0]/Dest[2046 0 R/XYZ null 495 0]>>endobj
+867 0 obj<</Subtype/Link/Rect[144.0 380.4 183.4 393.4]/Border[0 0 0]/Dest[2046 0 R/XYZ null 451 0]>>endobj
+868 0 obj<</Subtype/Link/Rect[108.0 367.2 217.4 380.2]/Border[0 0 0]/Dest[2049 0 R/XYZ null 799 0]>>endobj
+869 0 obj<</Subtype/Link/Rect[144.0 354.0 171.5 367.0]/Border[0 0 0]/Dest[2049 0 R/XYZ null 728 0]>>endobj
+870 0 obj<</Subtype/Link/Rect[144.0 340.8 192.9 353.8]/Border[0 0 0]/Dest[2049 0 R/XYZ null 683 0]>>endobj
+871 0 obj<</Subtype/Link/Rect[144.0 327.6 178.2 340.6]/Border[0 0 0]/Dest[2049 0 R/XYZ null 596 0]>>endobj
+872 0 obj<</Subtype/Link/Rect[144.0 314.4 195.3 327.4]/Border[0 0 0]/Dest[2049 0 R/XYZ null 546 0]>>endobj
+873 0 obj<</Subtype/Link/Rect[144.0 301.2 183.1 314.2]/Border[0 0 0]/Dest[2049 0 R/XYZ null 495 0]>>endobj
+874 0 obj<</Subtype/Link/Rect[144.0 288.0 183.4 301.0]/Border[0 0 0]/Dest[2049 0 R/XYZ null 451 0]>>endobj
+875 0 obj<</Subtype/Link/Rect[108.0 274.8 182.6 287.8]/Border[0 0 0]/Dest[2052 0 R/XYZ null 799 0]>>endobj
+876 0 obj<</Subtype/Link/Rect[144.0 261.6 171.5 274.6]/Border[0 0 0]/Dest[2052 0 R/XYZ null 728 0]>>endobj
+877 0 obj<</Subtype/Link/Rect[144.0 248.4 192.9 261.4]/Border[0 0 0]/Dest[2052 0 R/XYZ null 683 0]>>endobj
+878 0 obj<</Subtype/Link/Rect[144.0 235.2 178.2 248.2]/Border[0 0 0]/Dest[2052 0 R/XYZ null 596 0]>>endobj
+879 0 obj<</Subtype/Link/Rect[144.0 222.0 195.3 235.0]/Border[0 0 0]/Dest[2052 0 R/XYZ null 546 0]>>endobj
+880 0 obj<</Subtype/Link/Rect[144.0 208.8 183.1 221.8]/Border[0 0 0]/Dest[2052 0 R/XYZ null 495 0]>>endobj
+881 0 obj<</Subtype/Link/Rect[144.0 195.6 183.4 208.6]/Border[0 0 0]/Dest[2052 0 R/XYZ null 451 0]>>endobj
+882 0 obj<</Subtype/Link/Rect[108.0 182.4 174.0 195.4]/Border[0 0 0]/Dest[2055 0 R/XYZ null 799 0]>>endobj
+883 0 obj<</Subtype/Link/Rect[144.0 169.2 171.5 182.2]/Border[0 0 0]/Dest[2055 0 R/XYZ null 728 0]>>endobj
+884 0 obj<</Subtype/Link/Rect[144.0 156.0 192.9 169.0]/Border[0 0 0]/Dest[2055 0 R/XYZ null 683 0]>>endobj
+885 0 obj<</Subtype/Link/Rect[144.0 142.8 178.2 155.8]/Border[0 0 0]/Dest[2055 0 R/XYZ null 596 0]>>endobj
+886 0 obj<</Subtype/Link/Rect[144.0 129.6 195.3 142.6]/Border[0 0 0]/Dest[2055 0 R/XYZ null 546 0]>>endobj
+887 0 obj<</Subtype/Link/Rect[144.0 116.4 183.1 129.4]/Border[0 0 0]/Dest[2055 0 R/XYZ null 495 0]>>endobj
+888 0 obj<</Subtype/Link/Rect[144.0 103.2 183.4 116.2]/Border[0 0 0]/Dest[2055 0 R/XYZ null 451 0]>>endobj
+889 0 obj<</Subtype/Link/Rect[108.0 90.0 192.9 103.0]/Border[0 0 0]/Dest[2058 0 R/XYZ null 799 0]>>endobj
+890 0 obj<</Subtype/Link/Rect[144.0 76.8 171.5 89.8]/Border[0 0 0]/Dest[2058 0 R/XYZ null 728 0]>>endobj
+891 0 obj<</Subtype/Link/Rect[144.0 63.6 192.9 76.6]/Border[0 0 0]/Dest[2058 0 R/XYZ null 683 0]>>endobj
+892 0 obj[845 0 R
+846 0 R
+847 0 R
+848 0 R
+849 0 R
+850 0 R
+851 0 R
+852 0 R
+853 0 R
+854 0 R
+855 0 R
+856 0 R
+857 0 R
+858 0 R
+859 0 R
+860 0 R
+861 0 R
+862 0 R
+863 0 R
+864 0 R
+865 0 R
+866 0 R
+867 0 R
+868 0 R
+869 0 R
+870 0 R
+871 0 R
+872 0 R
+873 0 R
+874 0 R
+875 0 R
+876 0 R
+877 0 R
+878 0 R
+879 0 R
+880 0 R
+881 0 R
+882 0 R
+883 0 R
+884 0 R
+885 0 R
+886 0 R
+887 0 R
+888 0 R
+889 0 R
+890 0 R
+891 0 R
+]endobj
+893 0 obj<</Subtype/Link/Rect[108.0 670.8 142.2 683.8]/Border[0 0 0]/Dest[2058 0 R/XYZ null 596 0]>>endobj
+894 0 obj<</Subtype/Link/Rect[108.0 657.6 159.3 670.6]/Border[0 0 0]/Dest[2058 0 R/XYZ null 546 0]>>endobj
+895 0 obj<</Subtype/Link/Rect[108.0 644.4 147.1 657.4]/Border[0 0 0]/Dest[2058 0 R/XYZ null 495 0]>>endobj
+896 0 obj<</Subtype/Link/Rect[108.0 631.2 147.4 644.2]/Border[0 0 0]/Dest[2058 0 R/XYZ null 451 0]>>endobj
+897 0 obj<</Subtype/Link/Rect[72.0 618.0 150.2 631.0]/Border[0 0 0]/Dest[2061 0 R/XYZ null 799 0]>>endobj
+898 0 obj<</Subtype/Link/Rect[108.0 604.8 135.5 617.8]/Border[0 0 0]/Dest[2061 0 R/XYZ null 728 0]>>endobj
+899 0 obj<</Subtype/Link/Rect[108.0 591.6 156.9 604.6]/Border[0 0 0]/Dest[2061 0 R/XYZ null 683 0]>>endobj
+900 0 obj<</Subtype/Link/Rect[108.0 578.4 142.2 591.4]/Border[0 0 0]/Dest[2061 0 R/XYZ null 596 0]>>endobj
+901 0 obj<</Subtype/Link/Rect[108.0 565.2 159.3 578.2]/Border[0 0 0]/Dest[2061 0 R/XYZ null 546 0]>>endobj
+902 0 obj<</Subtype/Link/Rect[108.0 552.0 147.1 565.0]/Border[0 0 0]/Dest[2061 0 R/XYZ null 495 0]>>endobj
+903 0 obj<</Subtype/Link/Rect[108.0 538.8 147.4 551.8]/Border[0 0 0]/Dest[2061 0 R/XYZ null 451 0]>>endobj
+904 0 obj<</Subtype/Link/Rect[72.0 525.6 131.3 538.6]/Border[0 0 0]/Dest[2064 0 R/XYZ null 799 0]>>endobj
+905 0 obj<</Subtype/Link/Rect[108.0 512.4 135.5 525.4]/Border[0 0 0]/Dest[2064 0 R/XYZ null 728 0]>>endobj
+906 0 obj<</Subtype/Link/Rect[108.0 499.2 156.9 512.2]/Border[0 0 0]/Dest[2064 0 R/XYZ null 683 0]>>endobj
+907 0 obj<</Subtype/Link/Rect[108.0 486.0 142.2 499.0]/Border[0 0 0]/Dest[2064 0 R/XYZ null 596 0]>>endobj
+908 0 obj<</Subtype/Link/Rect[108.0 472.8 159.3 485.8]/Border[0 0 0]/Dest[2064 0 R/XYZ null 546 0]>>endobj
+909 0 obj<</Subtype/Link/Rect[108.0 459.6 147.1 472.6]/Border[0 0 0]/Dest[2064 0 R/XYZ null 495 0]>>endobj
+910 0 obj<</Subtype/Link/Rect[108.0 446.4 147.4 459.4]/Border[0 0 0]/Dest[2064 0 R/XYZ null 451 0]>>endobj
+911 0 obj<</Subtype/Link/Rect[72.0 433.2 136.8 446.2]/Border[0 0 0]/Dest[2067 0 R/XYZ null 799 0]>>endobj
+912 0 obj<</Subtype/Link/Rect[108.0 420.0 135.5 433.0]/Border[0 0 0]/Dest[2067 0 R/XYZ null 728 0]>>endobj
+913 0 obj<</Subtype/Link/Rect[108.0 406.8 156.9 419.8]/Border[0 0 0]/Dest[2067 0 R/XYZ null 683 0]>>endobj
+914 0 obj<</Subtype/Link/Rect[108.0 393.6 142.2 406.6]/Border[0 0 0]/Dest[2067 0 R/XYZ null 596 0]>>endobj
+915 0 obj<</Subtype/Link/Rect[108.0 380.4 159.3 393.4]/Border[0 0 0]/Dest[2067 0 R/XYZ null 546 0]>>endobj
+916 0 obj<</Subtype/Link/Rect[108.0 367.2 147.1 380.2]/Border[0 0 0]/Dest[2067 0 R/XYZ null 495 0]>>endobj
+917 0 obj<</Subtype/Link/Rect[108.0 354.0 147.4 367.0]/Border[0 0 0]/Dest[2067 0 R/XYZ null 451 0]>>endobj
+918 0 obj<</Subtype/Link/Rect[72.0 340.8 119.7 353.8]/Border[0 0 0]/Dest[2070 0 R/XYZ null 799 0]>>endobj
+919 0 obj<</Subtype/Link/Rect[108.0 327.6 135.5 340.6]/Border[0 0 0]/Dest[2070 0 R/XYZ null 728 0]>>endobj
+920 0 obj<</Subtype/Link/Rect[108.0 314.4 156.9 327.4]/Border[0 0 0]/Dest[2070 0 R/XYZ null 683 0]>>endobj
+921 0 obj<</Subtype/Link/Rect[108.0 301.2 142.2 314.2]/Border[0 0 0]/Dest[2070 0 R/XYZ null 596 0]>>endobj
+922 0 obj<</Subtype/Link/Rect[108.0 288.0 159.3 301.0]/Border[0 0 0]/Dest[2070 0 R/XYZ null 546 0]>>endobj
+923 0 obj<</Subtype/Link/Rect[108.0 274.8 147.1 287.8]/Border[0 0 0]/Dest[2070 0 R/XYZ null 495 0]>>endobj
+924 0 obj<</Subtype/Link/Rect[108.0 261.6 147.4 274.6]/Border[0 0 0]/Dest[2070 0 R/XYZ null 451 0]>>endobj
+925 0 obj<</Subtype/Link/Rect[72.0 248.4 148.4 261.4]/Border[0 0 0]/Dest[2073 0 R/XYZ null 799 0]>>endobj
+926 0 obj<</Subtype/Link/Rect[108.0 235.2 135.5 248.2]/Border[0 0 0]/Dest[2073 0 R/XYZ null 728 0]>>endobj
+927 0 obj<</Subtype/Link/Rect[108.0 222.0 156.9 235.0]/Border[0 0 0]/Dest[2073 0 R/XYZ null 683 0]>>endobj
+928 0 obj<</Subtype/Link/Rect[108.0 208.8 142.2 221.8]/Border[0 0 0]/Dest[2073 0 R/XYZ null 596 0]>>endobj
+929 0 obj<</Subtype/Link/Rect[108.0 195.6 159.3 208.6]/Border[0 0 0]/Dest[2073 0 R/XYZ null 546 0]>>endobj
+930 0 obj<</Subtype/Link/Rect[108.0 182.4 147.1 195.4]/Border[0 0 0]/Dest[2073 0 R/XYZ null 495 0]>>endobj
+931 0 obj<</Subtype/Link/Rect[108.0 169.2 147.4 182.2]/Border[0 0 0]/Dest[2073 0 R/XYZ null 451 0]>>endobj
+932 0 obj<</Subtype/Link/Rect[72.0 156.0 136.2 169.0]/Border[0 0 0]/Dest[2076 0 R/XYZ null 799 0]>>endobj
+933 0 obj<</Subtype/Link/Rect[108.0 142.8 135.5 155.8]/Border[0 0 0]/Dest[2076 0 R/XYZ null 728 0]>>endobj
+934 0 obj<</Subtype/Link/Rect[108.0 129.6 156.9 142.6]/Border[0 0 0]/Dest[2076 0 R/XYZ null 683 0]>>endobj
+935 0 obj<</Subtype/Link/Rect[108.0 116.4 142.2 129.4]/Border[0 0 0]/Dest[2076 0 R/XYZ null 596 0]>>endobj
+936 0 obj<</Subtype/Link/Rect[108.0 103.2 159.3 116.2]/Border[0 0 0]/Dest[2076 0 R/XYZ null 546 0]>>endobj
+937 0 obj<</Subtype/Link/Rect[108.0 90.0 147.1 103.0]/Border[0 0 0]/Dest[2076 0 R/XYZ null 495 0]>>endobj
+938 0 obj<</Subtype/Link/Rect[108.0 76.8 147.4 89.8]/Border[0 0 0]/Dest[2076 0 R/XYZ null 451 0]>>endobj
+939 0 obj<</Subtype/Link/Rect[72.0 63.6 144.7 76.6]/Border[0 0 0]/Dest[2079 0 R/XYZ null 799 0]>>endobj
+940 0 obj[893 0 R
+894 0 R
+895 0 R
+896 0 R
+897 0 R
+898 0 R
+899 0 R
+900 0 R
+901 0 R
+902 0 R
+903 0 R
+904 0 R
+905 0 R
+906 0 R
+907 0 R
+908 0 R
+909 0 R
+910 0 R
+911 0 R
+912 0 R
+913 0 R
+914 0 R
+915 0 R
+916 0 R
+917 0 R
+918 0 R
+919 0 R
+920 0 R
+921 0 R
+922 0 R
+923 0 R
+924 0 R
+925 0 R
+926 0 R
+927 0 R
+928 0 R
+929 0 R
+930 0 R
+931 0 R
+932 0 R
+933 0 R
+934 0 R
+935 0 R
+936 0 R
+937 0 R
+938 0 R
+939 0 R
+]endobj
+941 0 obj<</Subtype/Link/Rect[144.0 670.8 171.5 683.8]/Border[0 0 0]/Dest[2079 0 R/XYZ null 728 0]>>endobj
+942 0 obj<</Subtype/Link/Rect[144.0 657.6 192.9 670.6]/Border[0 0 0]/Dest[2079 0 R/XYZ null 683 0]>>endobj
+943 0 obj<</Subtype/Link/Rect[144.0 644.4 178.2 657.4]/Border[0 0 0]/Dest[2079 0 R/XYZ null 596 0]>>endobj
+944 0 obj<</Subtype/Link/Rect[144.0 631.2 195.3 644.2]/Border[0 0 0]/Dest[2079 0 R/XYZ null 546 0]>>endobj
+945 0 obj<</Subtype/Link/Rect[144.0 618.0 183.1 631.0]/Border[0 0 0]/Dest[2079 0 R/XYZ null 495 0]>>endobj
+946 0 obj<</Subtype/Link/Rect[144.0 604.8 183.4 617.8]/Border[0 0 0]/Dest[2079 0 R/XYZ null 451 0]>>endobj
+947 0 obj[941 0 R
+942 0 R
+943 0 R
+944 0 R
+945 0 R
+946 0 R
+]endobj
+948 0 obj<</Dests 949 0 R>>endobj
+949 0 obj<</Kids[950 0 R]>>endobj
+950 0 obj<</Limits[(1)(spm.shtml)]/Names[(1)951 0 R(1_1)952 0 R(1_2)953 0 R(2)954 0 R(2_1)955 0 R(2_2)956 0 R(2_3)957 0 R(2_4)958 0 R(2_5)959 0 R(2_6)960 0 R(2_7)961 0 R(3)962 0 R(3_1)963 0 R(3_1_1)964 0 R(3_2)965 0 R(3_2_1)966 0 R(3_2_2)967 0 R(3_2_3)968 0 R(3_2_4)969 0 R(3_2_5)970 0 R(3_3)971 0 R(3_3_1)972 0 R(3_3_2)973 0 R(3_3_3)974 0 R(3_3_4)975 0 R(3_3_5)976 0 R(3_3_6)977 0 R(3_3_7)978 0 R(3_4)979 0 R(3_4_1)980 0 R(3_4_10)981 0 R(3_4_2)982 0 R(3_4_3)983 0 R(3_4_4)984 0 R(3_4_5)985 0 R(3_4_6)986 0 R(3_4_7)987 0 R(3_4_8)988 0 R(3_4_9)989 0 R(3_5)990 0 R(3_5_1)991 0 R(3_5_2)992 0 R(3_5_3)993 0 R(3_5_4)994 0 R(3_6)995 0 R(3_6_1)996 0 R(3_6_2)997 0 R(3_6_3)998 0 R(3_6_4)999 0 R(3_6_5)1000 0 R(3_6_6)1001 0 R(3_6_7)1002 0 R(3_6_8)1003 0 R(4)1004 0 R(4_1)1005 0 R(4_1_1)1006 0 R(4_1_2)1007 0 R(4_1_3)1008 0 R(4_2)1009 0 R(4_2_1)1010 0 R(4_3)1011 0 R(4_4)1012 0 R(5)1013 0 R(5_1)1014 0 R(5_1_1)1015 0 R(5_1_2)1016 0 R(5_2)1017 0 R(5_2_1)1018 0 R(5_2_2)1019 0 R(5_2_3)1020 0 R(5_2_4)1021 0 R(5_3)1022 0 R(6)1023 0 R(6_1)1024 0 R(6_1_1)1025 0 R(6_1_2)1026 0 R(6_1_3)1027 0 R(6_1_4)1028 0 R(6_2)1029 0 R(6_2_1)1030 0 R(6_3)1031 0 R(6_4)1032 0 R(7)1033 0 R(7_1)1034 0 R(7_2)1035 0 R(7_3)1036 0 R(7_4)1037 0 R(7_5)1038 0 R(7_6)1039 0 R(8)1040 0 R(8_1)1041 0 R(9)1042 0 R(9_10_1)1043 0 R(9_10_2)1044 0 R(9_10_3)1045 0 R(9_10_4)1046 0 R(9_10_5)1047 0 R(9_11_1)1048 0 R(9_11_2)1049 0 R(9_11_3)1050 0 R(9_11_4)1051 0 R(9_11_5)1052 0 R(9_11_6)1053 0 R(9_12_1)1054 0 R(9_12_2)1055 0 R(9_12_3)1056 0 R(9_12_4)1057 0 R(9_12_5)1058 0 R(9_13_1)1059 0 R(9_13_2)1060 0 R(9_13_3)1061 0 R(9_13_4)1062 0 R(9_13_5)1063 0 R(9_13_6)1064 0 R(9_14_1)1065 0 R(9_14_2)1066 0 R(9_14_3)1067 0 R(9_14_4)1068 0 R(9_15_1)1069 0 R(9_15_2)1070 0 R(9_15_3)1071 0 R(9_15_4)1072 0 R(9_15_5)1073 0 R(9_16_1)1074 0 R(9_16_2)1075 0 R(9_16_3)1076 0 R(9_16_4)1077 0 R(9_16_5)1078 0 R(9_16_6)1079 0 R(9_17_1)1080 0 R(9_17_2)1081 0 R(9_17_3)1082 0 R(9_17_4)1083 0 R(9_17_5)1084 0 R(9_17_6)1085 0 R(9_18_1)1086 0 R(9_18_2)1087 0 R(9_18_3)1088 0 R(9_18_4)1089 0 R(9_18_5)1090 0 R(9_19_1)1091 0 R(9_19_2)1092 0 R(9_19_3)1093 0 R(9_19_4)1094 0 R(9_19_5)1095 0 R(9_19_6)1096 0 R(9_1_1)1097 0 R(9_1_2)1098 0 R(9_1_3)1099 0 R(9_1_4)1100 0 R(9_1_5)1101 0 R(9_1_6)1102 0 R(9_20_1)1103 0 R(9_20_2)1104 0 R(9_20_3)1105 0 R(9_20_4)1106 0 R(9_20_5)1107 0 R(9_20_6)1108 0 R(9_21_1)1109 0 R(9_21_2)1110 0 R(9_21_3)1111 0 R(9_21_4)1112 0 R(9_21_5)1113 0 R(9_21_6)1114 0 R(9_22_1)1115 0 R(9_22_2)1116 0 R(9_22_3)1117 0 R(9_22_4)1118 0 R(9_22_5)1119 0 R(9_23_1)1120 0 R(9_23_2)1121 0 R(9_23_3)1122 0 R(9_23_4)1123 0 R(9_23_5)1124 0 R(9_23_6)1125 0 R(9_24_1)1126 0 R(9_24_2)1127 0 R(9_24_3)1128 0 R(9_24_4)1129 0 R(9_24_5)1130 0 R(9_24_6)1131 0 R(9_25_1)1132 0 R(9_25_2)1133 0 R(9_25_3)1134 0 R(9_25_4)1135 0 R(9_25_5)1136 0 R(9_25_6)1137 0 R(9_26_1)1138 0 R(9_26_2)1139 0 R(9_26_3)1140 0 R(9_26_4)1141 0 R(9_26_5)1142 0 R(9_26_6)1143 0 R(9_27_1)1144 0 R(9_27_2)1145 0 R(9_27_3)1146 0 R(9_27_4)1147 0 R(9_27_5)1148 0 R(9_27_6)1149 0 R(9_28_1)1150 0 R(9_28_2)1151 0 R(9_28_3)1152 0 R(9_28_4)1153 0 R(9_28_5)1154 0 R(9_29_1)1155 0 R(9_29_2)1156 0 R(9_29_3)1157 0 R(9_29_4)1158 0 R(9_29_5)1159 0 R(9_2_1)1160 0 R(9_2_2)1161 0 R(9_2_3)1162 0 R(9_2_4)1163 0 R(9_2_5)1164 0 R(9_2_6)1165 0 R(9_30_1)1166 0 R(9_30_2)1167 0 R(9_30_3)1168 0 R(9_30_4)1169 0 R(9_30_5)1170 0 R(9_31_1)1171 0 R(9_31_2)1172 0 R(9_31_3)1173 0 R(9_31_4)1174 0 R(9_31_5)1175 0 R(9_31_6)1176 0 R(9_32_1)1177 0 R(9_32_2)1178 0 R(9_32_3)1179 0 R(9_32_4)1180 0 R(9_32_5)1181 0 R(9_32_6)1182 0 R(9_33_1)1183 0 R(9_33_2)1184 0 R(9_33_3)1185 0 R(9_33_4)1186 0 R(9_33_5)1187 0 R(9_33_6)1188 0 R(9_34_1)1189 0 R(9_34_2)1190 0 R(9_34_3)1191 0 R(9_34_4)1192 0 R(9_34_5)1193 0 R(9_34_6)1194 0 R(9_35_1)1195 0 R(9_35_2)1196 0 R(9_35_3)1197 0 R(9_35_4)1198 0 R(9_35_5)1199 0 R(9_35_6)1200 0 R(9_36_1)1201 0 R(9_36_2)1202 0 R(9_36_3)1203 0 R(9_36_4)1204 0 R(9_36_5)1205 0 R(9_36_6)1206 0 R(9_37_1)1207 0 R(9_37_2)1208 0 R(9_37_3)1209 0 R(9_37_4)1210 0 R(9_37_5)1211 0 R(9_37_6)1212 0 R(9_38_1)1213 0 R(9_38_2)1214 0 R(9_38_3)1215 0 R(9_38_4)1216 0 R(9_38_5)1217 0 R(9_38_6)1218 0 R(9_39_1)1219 0 R(9_39_2)1220 0 R(9_39_3)1221 0 R(9_39_4)1222 0 R(9_39_5)1223 0 R(9_39_6)1224 0 R(9_3_1)1225 0 R(9_3_2)1226 0 R(9_3_3)1227 0 R(9_3_4)1228 0 R(9_3_5)1229 0 R(9_3_6)1230 0 R(9_40_1)1231 0 R(9_40_2)1232 0 R(9_40_3)1233 0 R(9_40_4)1234 0 R(9_40_5)1235 0 R(9_40_6)1236 0 R(9_41_1)1237 0 R(9_41_2)1238 0 R(9_41_3)1239 0 R(9_41_4)1240 0 R(9_41_5)1241 0 R(9_41_6)1242 0 R(9_42_1)1243 0 R(9_42_2)1244 0 R(9_42_3)1245 0 R(9_42_4)1246 0 R(9_42_5)1247 0 R(9_42_6)1248 0 R(9_43_1)1249 0 R(9_43_2)1250 0 R(9_43_3)1251 0 R(9_43_4)1252 0 R(9_43_5)1253 0 R(9_43_6)1254 0 R(9_44_1)1255 0 R(9_44_2)1256 0 R(9_44_3)1257 0 R(9_44_4)1258 0 R(9_44_5)1259 0 R(9_44_6)1260 0 R(9_45_1)1261 0 R(9_45_2)1262 0 R(9_45_3)1263 0 R(9_45_4)1264 0 R(9_45_5)1265 0 R(9_45_6)1266 0 R(9_46_1)1267 0 R(9_46_2)1268 0 R(9_46_3)1269 0 R(9_46_4)1270 0 R(9_46_5)1271 0 R(9_46_6)1272 0 R(9_47_1)1273 0 R(9_47_2)1274 0 R(9_47_3)1275 0 R(9_47_4)1276 0 R(9_47_5)1277 0 R(9_47_6)1278 0 R(9_48_1)1279 0 R(9_48_2)1280 0 R(9_48_3)1281 0 R(9_48_4)1282 0 R(9_48_5)1283 0 R(9_48_6)1284 0 R(9_49_1)1285 0 R(9_49_2)1286 0 R(9_49_3)1287 0 R(9_49_4)1288 0 R(9_49_5)1289 0 R(9_49_6)1290 0 R(9_4_1)1291 0 R(9_4_2)1292 0 R(9_4_3)1293 0 R(9_4_4)1294 0 R(9_4_5)1295 0 R(9_4_6)1296 0 R(9_50_1)1297 0 R(9_50_2)1298 0 R(9_50_3)1299 0 R(9_50_4)1300 0 R(9_50_5)1301 0 R(9_50_6)1302 0 R(9_51_1)1303 0 R(9_51_2)1304 0 R(9_51_3)1305 0 R(9_51_4)1306 0 R(9_51_5)1307 0 R(9_51_6)1308 0 R(9_52_1)1309 0 R(9_52_2)1310 0 R(9_52_3)1311 0 R(9_52_4)1312 0 R(9_52_5)1313 0 R(9_52_6)1314 0 R(9_53_1)1315 0 R(9_53_2)1316 0 R(9_53_3)1317 0 R(9_53_4)1318 0 R(9_53_5)1319 0 R(9_53_6)1320 0 R(9_54_1)1321 0 R(9_54_2)1322 0 R(9_54_3)1323 0 R(9_54_4)1324 0 R(9_54_5)1325 0 R(9_54_6)1326 0 R(9_55_1)1327 0 R(9_55_2)1328 0 R(9_55_3)1329 0 R(9_55_4)1330 0 R(9_55_5)1331 0 R(9_55_6)1332 0 R(9_56_1)1333 0 R(9_56_2)1334 0 R(9_56_3)1335 0 R(9_56_4)1336 0 R(9_56_5)1337 0 R(9_56_6)1338 0 R(9_57_1)1339 0 R(9_57_2)1340 0 R(9_57_3)1341 0 R(9_57_4)1342 0 R(9_57_5)1343 0 R(9_57_6)1344 0 R(9_58_1)1345 0 R(9_58_2)1346 0 R(9_58_3)1347 0 R(9_58_4)1348 0 R(9_58_5)1349 0 R(9_58_6)1350 0 R(9_59_1)1351 0 R(9_59_2)1352 0 R(9_59_3)1353 0 R(9_59_4)1354 0 R(9_59_5)1355 0 R(9_59_6)1356 0 R(9_5_1)1357 0 R(9_5_2)1358 0 R(9_5_3)1359 0 R(9_5_4)1360 0 R(9_5_5)1361 0 R(9_60_1)1362 0 R(9_60_2)1363 0 R(9_60_3)1364 0 R(9_60_4)1365 0 R(9_60_5)1366 0 R(9_60_6)1367 0 R(9_61_1)1368 0 R(9_61_2)1369 0 R(9_61_3)1370 0 R(9_61_4)1371 0 R(9_61_5)1372 0 R(9_61_6)1373 0 R(9_62_1)1374 0 R(9_62_2)1375 0 R(9_62_3)1376 0 R(9_62_4)1377 0 R(9_62_5)1378 0 R(9_62_6)1379 0 R(9_63_1)1380 0 R(9_63_2)1381 0 R(9_63_3)1382 0 R(9_63_4)1383 0 R(9_63_5)1384 0 R(9_63_6)1385 0 R(9_64_1)1386 0 R(9_64_2)1387 0 R(9_64_3)1388 0 R(9_64_4)1389 0 R(9_64_5)1390 0 R(9_64_6)1391 0 R(9_65_1)1392 0 R(9_65_2)1393 0 R(9_65_3)1394 0 R(9_65_4)1395 0 R(9_65_5)1396 0 R(9_65_6)1397 0 R(9_66_1)1398 0 R(9_66_2)1399 0 R(9_66_3)1400 0 R(9_66_4)1401 0 R(9_66_5)1402 0 R(9_66_6)1403 0 R(9_67_1)1404 0 R(9_67_2)1405 0 R(9_67_3)1406 0 R(9_67_4)1407 0 R(9_67_5)1408 0 R(9_67_6)1409 0 R(9_68_1)1410 0 R(9_68_2)1411 0 R(9_68_3)1412 0 R(9_68_4)1413 0 R(9_68_5)1414 0 R(9_68_6)1415 0 R(9_69_1)1416 0 R(9_69_2)1417 0 R(9_69_3)1418 0 R(9_69_4)1419 0 R(9_69_5)1420 0 R(9_69_6)1421 0 R(9_6_1)1422 0 R(9_6_2)1423 0 R(9_6_3)1424 0 R(9_6_4)1425 0 R(9_6_5)1426 0 R(9_6_6)1427 0 R(9_70_1)1428 0 R(9_70_2)1429 0 R(9_70_3)1430 0 R(9_70_4)1431 0 R(9_70_5)1432 0 R(9_70_6)1433 0 R(9_71_1)1434 0 R(9_71_2)1435 0 R(9_71_3)1436 0 R(9_71_4)1437 0 R(9_71_5)1438 0 R(9_71_6)1439 0 R(9_72_1)1440 0 R(9_72_2)1441 0 R(9_72_3)1442 0 R(9_72_4)1443 0 R(9_72_5)1444 0 R(9_72_6)1445 0 R(9_73_1)1446 0 R(9_73_2)1447 0 R(9_73_3)1448 0 R(9_73_4)1449 0 R(9_73_5)1450 0 R(9_73_6)1451 0 R(9_74_1)1452 0 R(9_74_2)1453 0 R(9_74_3)1454 0 R(9_74_4)1455 0 R(9_74_5)1456 0 R(9_74_6)1457 0 R(9_75_1)1458 0 R(9_75_2)1459 0 R(9_75_3)1460 0 R(9_75_4)1461 0 R(9_75_5)1462 0 R(9_75_6)1463 0 R(9_76_1)1464 0 R(9_76_2)1465 0 R(9_76_3)1466 0 R(9_76_4)1467 0 R(9_76_5)1468 0 R(9_76_6)1469 0 R(9_77_1)1470 0 R(9_77_2)1471 0 R(9_77_3)1472 0 R(9_77_4)1473 0 R(9_77_5)1474 0 R(9_77_6)1475 0 R(9_78_1)1476 0 R(9_78_2)1477 0 R(9_78_3)1478 0 R(9_78_4)1479 0 R(9_78_5)1480 0 R(9_78_6)1481 0 R(9_79_1)1482 0 R(9_79_2)1483 0 R(9_79_3)1484 0 R(9_79_4)1485 0 R(9_79_5)1486 0 R(9_79_6)1487 0 R(9_7_1)1488 0 R(9_7_2)1489 0 R(9_7_3)1490 0 R(9_7_4)1491 0 R(9_7_5)1492 0 R(9_80_1)1493 0 R(9_80_2)1494 0 R(9_80_3)1495 0 R(9_80_4)1496 0 R(9_80_5)1497 0 R(9_80_6)1498 0 R(9_81_1)1499 0 R(9_81_2)1500 0 R(9_81_3)1501 0 R(9_81_4)1502 0 R(9_81_5)1503 0 R(9_81_6)1504 0 R(9_82_1)1505 0 R(9_82_2)1506 0 R(9_82_3)1507 0 R(9_82_4)1508 0 R(9_82_5)1509 0 R(9_82_6)1510 0 R(9_83_1)1511 0 R(9_83_2)1512 0 R(9_83_3)1513 0 R(9_83_4)1514 0 R(9_83_5)1515 0 R(9_83_6)1516 0 R(9_84_1)1517 0 R(9_84_2)1518 0 R(9_84_3)1519 0 R(9_84_4)1520 0 R(9_84_5)1521 0 R(9_84_6)1522 0 R(9_85_1)1523 0 R(9_85_2)1524 0 R(9_85_3)1525 0 R(9_85_4)1526 0 R(9_85_5)1527 0 R(9_85_6)1528 0 R(9_86_1)1529 0 R(9_86_2)1530 0 R(9_86_3)1531 0 R(9_86_4)1532 0 R(9_86_5)1533 0 R(9_86_6)1534 0 R(9_87_1)1535 0 R(9_87_2)1536 0 R(9_87_3)1537 0 R(9_87_4)1538 0 R(9_87_5)1539 0 R(9_87_6)1540 0 R(9_88_1)1541 0 R(9_88_2)1542 0 R(9_88_3)1543 0 R(9_88_4)1544 0 R(9_88_5)1545 0 R(9_88_6)1546 0 R(9_89_1)1547 0 R(9_89_2)1548 0 R(9_89_3)1549 0 R(9_89_4)1550 0 R(9_89_5)1551 0 R(9_89_6)1552 0 R(9_8_1)1553 0 R(9_8_2)1554 0 R(9_8_3)1555 0 R(9_8_4)1556 0 R(9_8_5)1557 0 R(9_90_1)1558 0 R(9_90_2)1559 0 R(9_90_3)1560 0 R(9_90_4)1561 0 R(9_90_5)1562 0 R(9_90_6)1563 0 R(9_91_1)1564 0 R(9_91_2)1565 0 R(9_91_3)1566 0 R(9_91_4)1567 0 R(9_91_5)1568 0 R(9_91_6)1569 0 R(9_92_1)1570 0 R(9_92_2)1571 0 R(9_92_3)1572 0 R(9_92_4)1573 0 R(9_92_5)1574 0 R(9_92_6)1575 0 R(9_93_1)1576 0 R(9_93_2)1577 0 R(9_93_3)1578 0 R(9_93_4)1579 0 R(9_93_5)1580 0 R(9_93_6)1581 0 R(9_94_1)1582 0 R(9_94_2)1583 0 R(9_94_3)1584 0 R(9_94_4)1585 0 R(9_94_5)1586 0 R(9_94_6)1587 0 R(9_95_1)1588 0 R(9_95_2)1589 0 R(9_95_3)1590 0 R(9_95_4)1591 0 R(9_95_5)1592 0 R(9_95_6)1593 0 R(9_96_1)1594 0 R(9_96_2)1595 0 R(9_96_3)1596 0 R(9_96_4)1597 0 R(9_96_5)1598 0 R(9_96_6)1599 0 R(9_9_1)1600 0 R(9_9_2)1601 0 R(9_9_3)1602 0 R(9_9_4)1603 0 R(9_9_5)1604 0 R(9_9_6)1605 0 R(cupsaddoption)1606 0 R(cupscanceljob)1607 0 R(cupsdofilerequest)1608 0 R(cupsdorequest)1609 0 R(cupsfreeoptions)1610 0 R(cupsgetclasses)1611 0 R(cupsgetdefault)1612 0 R(cupsgetoption)1613 0 R(cupsgetpassword)1614 0 R(cupsgetppd)1615 0 R(cupsgetprinters)1616 0 R(cupslangdefault)1617 0 R(cupslangencoding)1618 0 R(cupslangflush)1619 0 R(cupslangfree)1620 0 R(cupslangget)1621 0 R(cupslangstring)1622 0 R(cupslasterror)1623 0 R(cupsmarkoptions)1624 0 R(cupsparseoptions)1625 0 R(cupsprintfile)1626 0 R(cupsrasterclose)1627 0 R(cupsrasteropen)1628 0 R(cupsrasterreadheader)1629 0 R(cupsrasterreadpixels)1630 0 R(cupsrasterwriteheader)1631 0 R(cupsrasterwritepixels)1632 0 R(cupsserver)1633 0 R(cupstempfile)1634 0 R(cupsuser)1635 0 R(httpblocking)1636 0 R(httpcheck)1637 0 R(httpclearfields)1638 0 R(httpclose)1639 0 R(httpconnect)1640 0 R(httpdecode64)1641 0 R(httpdelete)1642 0 R(httpencode64)1643 0 R(httperror)1644 0 R(httpflush)1645 0 R(httpget)1646 0 R(httpgetdatestring)1647 0 R(httpgetdatetime)1648 0 R(httpgetfield)1649 0 R(httpgetlength)1650 0 R(httpgets)1651 0 R(httphead)1652 0 R(httpinitialize)1653 0 R(httpoptions)1654 0 R(httppost)1655 0 R(httpprintf)1656 0 R(httpput)1657 0 R(httpread)1658 0 R(httpreconnect)1659 0 R(httpseparate)1660 0 R(httpsetfield)1661 0 R(httptrace)1662 0 R(httpupdate)1663 0 R(httpwrite)1664 0 R(ippaddboolean)1665 0 R(ippaddbooleans)1666 0 R(ippadddate)1667 0 R(ippaddinteger)1668 0 R(ippaddintegers)1669 0 R(ippaddrange)1670 0 R(ippaddranges)1671 0 R(ippaddresolution)1672 0 R(ippaddresolutions)1673 0 R(ippaddseparator)1674 0 R(ippaddstring)1675 0 R(ippaddstrings)1676 0 R(ippdatetotime)1677 0 R(ippdelete)1678 0 R(ippfindattribute)1679 0 R(ipplength)1680 0 R(ippnew)1681 0 R(ippport)1682 0 R(ippread)1683 0 R(ipptimetodate)1684 0 R(ippwrite)1685 0 R(pddemitfd)1686 0 R(ppdclose)1687 0 R(ppdconflicts)1688 0 R(ppdemit)1689 0 R(ppdfindchoice)1690 0 R(ppdfindmarkedchoice)1691 0 R(ppdfindoption)1692 0 R(ppdismarked)1693 0 R(ppdmarkdefaults)1694 0 R(ppdmarkoption)1695 0 R(ppdopen)1696 0 R(ppdopenfd)1697 0 R(ppdopenfile)1698 0 R(ppdpagelength)1699 0 R(ppdpagesize)1700 0 R(ppdpagewidth)1701 0 R(spm.shtml)1702 0 R]>>endobj
+951 0 obj<</D[1710 0 R/XYZ null 818 null]>>endobj
+952 0 obj<</D[1710 0 R/XYZ null 435 null]>>endobj
+953 0 obj<</D[1710 0 R/XYZ null 209 null]>>endobj
+954 0 obj<</D[1716 0 R/XYZ null 818 null]>>endobj
+955 0 obj<</D[1716 0 R/XYZ null 428 null]>>endobj
+956 0 obj<</D[1719 0 R/XYZ null 799 null]>>endobj
+957 0 obj<</D[1719 0 R/XYZ null 534 null]>>endobj
+958 0 obj<</D[1719 0 R/XYZ null 441 null]>>endobj
+959 0 obj<</D[1719 0 R/XYZ null 360 null]>>endobj
+960 0 obj<</D[1722 0 R/XYZ null 799 null]>>endobj
+961 0 obj<</D[1722 0 R/XYZ null 693 null]>>endobj
+962 0 obj<</D[1728 0 R/XYZ null 818 null]>>endobj
+963 0 obj<</D[1728 0 R/XYZ null 428 null]>>endobj
+964 0 obj<</D[1728 0 R/XYZ null 356 null]>>endobj
+965 0 obj<</D[1728 0 R/XYZ null 316 null]>>endobj
+966 0 obj<</D[1728 0 R/XYZ null 244 null]>>endobj
+967 0 obj<</D[1728 0 R/XYZ null 194 null]>>endobj
+968 0 obj<</D[1731 0 R/XYZ null 782 null]>>endobj
+969 0 obj<</D[1731 0 R/XYZ null 731 null]>>endobj
+970 0 obj<</D[1731 0 R/XYZ null 681 null]>>endobj
+971 0 obj<</D[1731 0 R/XYZ null 641 null]>>endobj
+972 0 obj<</D[1731 0 R/XYZ null 569 null]>>endobj
+973 0 obj<</D[1731 0 R/XYZ null 519 null]>>endobj
+974 0 obj<</D[1731 0 R/XYZ null 468 null]>>endobj
+975 0 obj<</D[1731 0 R/XYZ null 418 null]>>endobj
+976 0 obj<</D[1731 0 R/XYZ null 368 null]>>endobj
+977 0 obj<</D[1731 0 R/XYZ null 317 null]>>endobj
+978 0 obj<</D[1731 0 R/XYZ null 267 null]>>endobj
+979 0 obj<</D[1731 0 R/XYZ null 227 null]>>endobj
+980 0 obj<</D[1734 0 R/XYZ null 782 null]>>endobj
+981 0 obj<</D[1734 0 R/XYZ null 328 null]>>endobj
+982 0 obj<</D[1734 0 R/XYZ null 731 null]>>endobj
+983 0 obj<</D[1734 0 R/XYZ null 681 null]>>endobj
+984 0 obj<</D[1734 0 R/XYZ null 630 null]>>endobj
+985 0 obj<</D[1734 0 R/XYZ null 580 null]>>endobj
+986 0 obj<</D[1734 0 R/XYZ null 530 null]>>endobj
+987 0 obj<</D[1734 0 R/XYZ null 479 null]>>endobj
+988 0 obj<</D[1734 0 R/XYZ null 429 null]>>endobj
+989 0 obj<</D[1734 0 R/XYZ null 378 null]>>endobj
+990 0 obj<</D[1734 0 R/XYZ null 288 null]>>endobj
+991 0 obj<</D[1734 0 R/XYZ null 216 null]>>endobj
+992 0 obj<</D[1737 0 R/XYZ null 782 null]>>endobj
+993 0 obj<</D[1737 0 R/XYZ null 731 null]>>endobj
+994 0 obj<</D[1737 0 R/XYZ null 681 null]>>endobj
+995 0 obj<</D[1737 0 R/XYZ null 641 null]>>endobj
+996 0 obj<</D[1737 0 R/XYZ null 569 null]>>endobj
+997 0 obj<</D[1737 0 R/XYZ null 519 null]>>endobj
+998 0 obj<</D[1737 0 R/XYZ null 468 null]>>endobj
+999 0 obj<</D[1737 0 R/XYZ null 418 null]>>endobj
+1000 0 obj<</D[1737 0 R/XYZ null 368 null]>>endobj
+1001 0 obj<</D[1737 0 R/XYZ null 317 null]>>endobj
+1002 0 obj<</D[1737 0 R/XYZ null 267 null]>>endobj
+1003 0 obj<</D[1737 0 R/XYZ null 216 null]>>endobj
+1004 0 obj<</D[1740 0 R/XYZ null 818 null]>>endobj
+1005 0 obj<</D[1740 0 R/XYZ null 428 null]>>endobj
+1006 0 obj<</D[1740 0 R/XYZ null 356 null]>>endobj
+1007 0 obj<</D[1740 0 R/XYZ null 296 null]>>endobj
+1008 0 obj<</D[1740 0 R/XYZ null 246 null]>>endobj
+1009 0 obj<</D[1740 0 R/XYZ null 206 null]>>endobj
+1010 0 obj<</D[1743 0 R/XYZ null 782 null]>>endobj
+1011 0 obj<</D[1743 0 R/XYZ null 742 null]>>endobj
+1012 0 obj<</D[1743 0 R/XYZ null 681 null]>>endobj
+1013 0 obj<</D[1746 0 R/XYZ null 818 null]>>endobj
+1014 0 obj<</D[1746 0 R/XYZ null 415 null]>>endobj
+1015 0 obj<</D[1746 0 R/XYZ null 343 null]>>endobj
+1016 0 obj<</D[1746 0 R/XYZ null 292 null]>>endobj
+1017 0 obj<</D[1746 0 R/XYZ null 253 null]>>endobj
+1018 0 obj<</D[1746 0 R/XYZ null 181 null]>>endobj
+1019 0 obj<</D[1749 0 R/XYZ null 782 null]>>endobj
+1020 0 obj<</D[1749 0 R/XYZ null 731 null]>>endobj
+1021 0 obj<</D[1749 0 R/XYZ null 681 null]>>endobj
+1022 0 obj<</D[1749 0 R/XYZ null 641 null]>>endobj
+1023 0 obj<</D[1752 0 R/XYZ null 818 null]>>endobj
+1024 0 obj<</D[1752 0 R/XYZ null 415 null]>>endobj
+1025 0 obj<</D[1752 0 R/XYZ null 343 null]>>endobj
+1026 0 obj<</D[1752 0 R/XYZ null 283 null]>>endobj
+1027 0 obj<</D[1752 0 R/XYZ null 232 null]>>endobj
+1028 0 obj<</D[1752 0 R/XYZ null 182 null]>>endobj
+1029 0 obj<</D[1755 0 R/XYZ null 799 null]>>endobj
+1030 0 obj<</D[1755 0 R/XYZ null 728 null]>>endobj
+1031 0 obj<</D[1755 0 R/XYZ null 688 null]>>endobj
+1032 0 obj<</D[1755 0 R/XYZ null 627 null]>>endobj
+1033 0 obj<</D[1758 0 R/XYZ null 818 null]>>endobj
+1034 0 obj<</D[1758 0 R/XYZ null 428 null]>>endobj
+1035 0 obj<</D[1758 0 R/XYZ null 367 null]>>endobj
+1036 0 obj<</D[1758 0 R/XYZ null 306 null]>>endobj
+1037 0 obj<</D[1758 0 R/XYZ null 244 null]>>endobj
+1038 0 obj<</D[1761 0 R/XYZ null 799 null]>>endobj
+1039 0 obj<</D[1761 0 R/XYZ null 738 null]>>endobj
+1040 0 obj<</D[1764 0 R/XYZ null 818 null]>>endobj
+1041 0 obj<</D[1764 0 R/XYZ null 326 null]>>endobj
+1042 0 obj<</D[1770 0 R/XYZ null 818 null]>>endobj
+1043 0 obj<</D[1809 0 R/XYZ null 728 null]>>endobj
+1044 0 obj<</D[1809 0 R/XYZ null 662 null]>>endobj
+1045 0 obj<</D[1809 0 R/XYZ null 561 null]>>endobj
+1046 0 obj<</D[1809 0 R/XYZ null 488 null]>>endobj
+1047 0 obj<</D[1809 0 R/XYZ null 375 null]>>endobj
+1048 0 obj<</D[1812 0 R/XYZ null 728 null]>>endobj
+1049 0 obj<</D[1812 0 R/XYZ null 662 null]>>endobj
+1050 0 obj<</D[1812 0 R/XYZ null 561 null]>>endobj
+1051 0 obj<</D[1812 0 R/XYZ null 501 null]>>endobj
+1052 0 obj<</D[1812 0 R/XYZ null 428 null]>>endobj
+1053 0 obj<</D[1812 0 R/XYZ null 178 null]>>endobj
+1054 0 obj<</D[1815 0 R/XYZ null 728 null]>>endobj
+1055 0 obj<</D[1815 0 R/XYZ null 662 null]>>endobj
+1056 0 obj<</D[1815 0 R/XYZ null 602 null]>>endobj
+1057 0 obj<</D[1815 0 R/XYZ null 489 null]>>endobj
+1058 0 obj<</D[1815 0 R/XYZ null 336 null]>>endobj
+1059 0 obj<</D[1818 0 R/XYZ null 728 null]>>endobj
+1060 0 obj<</D[1818 0 R/XYZ null 662 null]>>endobj
+1061 0 obj<</D[1818 0 R/XYZ null 561 null]>>endobj
+1062 0 obj<</D[1818 0 R/XYZ null 501 null]>>endobj
+1063 0 obj<</D[1818 0 R/XYZ null 428 null]>>endobj
+1064 0 obj<</D[1818 0 R/XYZ null 265 null]>>endobj
+1065 0 obj<</D[1821 0 R/XYZ null 728 null]>>endobj
+1066 0 obj<</D[1821 0 R/XYZ null 662 null]>>endobj
+1067 0 obj<</D[1821 0 R/XYZ null 602 null]>>endobj
+1068 0 obj<</D[1821 0 R/XYZ null 503 null]>>endobj
+1069 0 obj<</D[1824 0 R/XYZ null 728 null]>>endobj
+1070 0 obj<</D[1824 0 R/XYZ null 662 null]>>endobj
+1071 0 obj<</D[1824 0 R/XYZ null 561 null]>>endobj
+1072 0 obj<</D[1824 0 R/XYZ null 501 null]>>endobj
+1073 0 obj<</D[1824 0 R/XYZ null 392 null]>>endobj
+1074 0 obj<</D[1827 0 R/XYZ null 728 null]>>endobj
+1075 0 obj<</D[1827 0 R/XYZ null 662 null]>>endobj
+1076 0 obj<</D[1827 0 R/XYZ null 561 null]>>endobj
+1077 0 obj<</D[1827 0 R/XYZ null 501 null]>>endobj
+1078 0 obj<</D[1827 0 R/XYZ null 428 null]>>endobj
+1079 0 obj<</D[1827 0 R/XYZ null 265 null]>>endobj
+1080 0 obj<</D[1830 0 R/XYZ null 728 null]>>endobj
+1081 0 obj<</D[1830 0 R/XYZ null 651 null]>>endobj
+1082 0 obj<</D[1830 0 R/XYZ null 530 null]>>endobj
+1083 0 obj<</D[1830 0 R/XYZ null 470 null]>>endobj
+1084 0 obj<</D[1830 0 R/XYZ null 410 null]>>endobj
+1085 0 obj<</D[1830 0 R/XYZ null 225 null]>>endobj
+1086 0 obj<</D[1833 0 R/XYZ null 728 null]>>endobj
+1087 0 obj<</D[1833 0 R/XYZ null 662 null]>>endobj
+1088 0 obj<</D[1833 0 R/XYZ null 602 null]>>endobj
+1089 0 obj<</D[1833 0 R/XYZ null 528 null]>>endobj
+1090 0 obj<</D[1833 0 R/XYZ null 408 null]>>endobj
+1091 0 obj<</D[1836 0 R/XYZ null 728 null]>>endobj
+1092 0 obj<</D[1836 0 R/XYZ null 640 null]>>endobj
+1093 0 obj<</D[1836 0 R/XYZ null 499 null]>>endobj
+1094 0 obj<</D[1836 0 R/XYZ null 439 null]>>endobj
+1095 0 obj<</D[1836 0 R/XYZ null 366 null]>>endobj
+1096 0 obj<</D[1836 0 R/XYZ null 224 null]>>endobj
+1097 0 obj<</D[1773 0 R/XYZ null 728 null]>>endobj
+1098 0 obj<</D[1773 0 R/XYZ null 629 null]>>endobj
+1099 0 obj<</D[1773 0 R/XYZ null 468 null]>>endobj
+1100 0 obj<</D[1773 0 R/XYZ null 408 null]>>endobj
+1101 0 obj<</D[1773 0 R/XYZ null 348 null]>>endobj
+1102 0 obj<</D[1776 0 R/XYZ null 782 null]>>endobj
+1103 0 obj<</D[1839 0 R/XYZ null 728 null]>>endobj
+1104 0 obj<</D[1839 0 R/XYZ null 640 null]>>endobj
+1105 0 obj<</D[1839 0 R/XYZ null 499 null]>>endobj
+1106 0 obj<</D[1839 0 R/XYZ null 439 null]>>endobj
+1107 0 obj<</D[1839 0 R/XYZ null 366 null]>>endobj
+1108 0 obj<</D[1839 0 R/XYZ null 214 null]>>endobj
+1109 0 obj<</D[1842 0 R/XYZ null 728 null]>>endobj
+1110 0 obj<</D[1842 0 R/XYZ null 618 null]>>endobj
+1111 0 obj<</D[1842 0 R/XYZ null 437 null]>>endobj
+1112 0 obj<</D[1842 0 R/XYZ null 377 null]>>endobj
+1113 0 obj<</D[1842 0 R/XYZ null 304 null]>>endobj
+1114 0 obj<</D[1845 0 R/XYZ null 782 null]>>endobj
+1115 0 obj<</D[1848 0 R/XYZ null 728 null]>>endobj
+1116 0 obj<</D[1848 0 R/XYZ null 662 null]>>endobj
+1117 0 obj<</D[1848 0 R/XYZ null 561 null]>>endobj
+1118 0 obj<</D[1848 0 R/XYZ null 501 null]>>endobj
+1119 0 obj<</D[1848 0 R/XYZ null 381 null]>>endobj
+1120 0 obj<</D[1851 0 R/XYZ null 728 null]>>endobj
+1121 0 obj<</D[1851 0 R/XYZ null 651 null]>>endobj
+1122 0 obj<</D[1851 0 R/XYZ null 530 null]>>endobj
+1123 0 obj<</D[1851 0 R/XYZ null 470 null]>>endobj
+1124 0 obj<</D[1851 0 R/XYZ null 410 null]>>endobj
+1125 0 obj<</D[1851 0 R/XYZ null 290 null]>>endobj
+1126 0 obj<</D[1854 0 R/XYZ null 728 null]>>endobj
+1127 0 obj<</D[1854 0 R/XYZ null 651 null]>>endobj
+1128 0 obj<</D[1854 0 R/XYZ null 530 null]>>endobj
+1129 0 obj<</D[1854 0 R/XYZ null 470 null]>>endobj
+1130 0 obj<</D[1854 0 R/XYZ null 410 null]>>endobj
+1131 0 obj<</D[1857 0 R/XYZ null 782 null]>>endobj
+1132 0 obj<</D[1860 0 R/XYZ null 728 null]>>endobj
+1133 0 obj<</D[1860 0 R/XYZ null 640 null]>>endobj
+1134 0 obj<</D[1860 0 R/XYZ null 499 null]>>endobj
+1135 0 obj<</D[1860 0 R/XYZ null 439 null]>>endobj
+1136 0 obj<</D[1860 0 R/XYZ null 379 null]>>endobj
+1137 0 obj<</D[1863 0 R/XYZ null 782 null]>>endobj
+1138 0 obj<</D[1866 0 R/XYZ null 728 null]>>endobj
+1139 0 obj<</D[1866 0 R/XYZ null 651 null]>>endobj
+1140 0 obj<</D[1866 0 R/XYZ null 530 null]>>endobj
+1141 0 obj<</D[1866 0 R/XYZ null 470 null]>>endobj
+1142 0 obj<</D[1866 0 R/XYZ null 410 null]>>endobj
+1143 0 obj<</D[1866 0 R/XYZ null 193 null]>>endobj
+1144 0 obj<</D[1869 0 R/XYZ null 728 null]>>endobj
+1145 0 obj<</D[1869 0 R/XYZ null 640 null]>>endobj
+1146 0 obj<</D[1869 0 R/XYZ null 499 null]>>endobj
+1147 0 obj<</D[1869 0 R/XYZ null 439 null]>>endobj
+1148 0 obj<</D[1869 0 R/XYZ null 379 null]>>endobj
+1149 0 obj<</D[1872 0 R/XYZ null 782 null]>>endobj
+1150 0 obj<</D[1875 0 R/XYZ null 728 null]>>endobj
+1151 0 obj<</D[1875 0 R/XYZ null 662 null]>>endobj
+1152 0 obj<</D[1875 0 R/XYZ null 602 null]>>endobj
+1153 0 obj<</D[1875 0 R/XYZ null 449 null]>>endobj
+1154 0 obj<</D[1875 0 R/XYZ null 351 null]>>endobj
+1155 0 obj<</D[1878 0 R/XYZ null 728 null]>>endobj
+1156 0 obj<</D[1878 0 R/XYZ null 651 null]>>endobj
+1157 0 obj<</D[1878 0 R/XYZ null 530 null]>>endobj
+1158 0 obj<</D[1878 0 R/XYZ null 470 null]>>endobj
+1159 0 obj<</D[1878 0 R/XYZ null 397 null]>>endobj
+1160 0 obj<</D[1779 0 R/XYZ null 728 null]>>endobj
+1161 0 obj<</D[1779 0 R/XYZ null 651 null]>>endobj
+1162 0 obj<</D[1779 0 R/XYZ null 530 null]>>endobj
+1163 0 obj<</D[1779 0 R/XYZ null 470 null]>>endobj
+1164 0 obj<</D[1779 0 R/XYZ null 410 null]>>endobj
+1165 0 obj<</D[1779 0 R/XYZ null 333 null]>>endobj
+1166 0 obj<</D[1881 0 R/XYZ null 728 null]>>endobj
+1167 0 obj<</D[1881 0 R/XYZ null 662 null]>>endobj
+1168 0 obj<</D[1881 0 R/XYZ null 602 null]>>endobj
+1169 0 obj<</D[1881 0 R/XYZ null 528 null]>>endobj
+1170 0 obj<</D[1881 0 R/XYZ null 430 null]>>endobj
+1171 0 obj<</D[1884 0 R/XYZ null 728 null]>>endobj
+1172 0 obj<</D[1884 0 R/XYZ null 683 null]>>endobj
+1173 0 obj<</D[1884 0 R/XYZ null 596 null]>>endobj
+1174 0 obj<</D[1884 0 R/XYZ null 546 null]>>endobj
+1175 0 obj<</D[1884 0 R/XYZ null 495 null]>>endobj
+1176 0 obj<</D[1884 0 R/XYZ null 451 null]>>endobj
+1177 0 obj<</D[1887 0 R/XYZ null 728 null]>>endobj
+1178 0 obj<</D[1887 0 R/XYZ null 683 null]>>endobj
+1179 0 obj<</D[1887 0 R/XYZ null 596 null]>>endobj
+1180 0 obj<</D[1887 0 R/XYZ null 546 null]>>endobj
+1181 0 obj<</D[1887 0 R/XYZ null 495 null]>>endobj
+1182 0 obj<</D[1887 0 R/XYZ null 451 null]>>endobj
+1183 0 obj<</D[1890 0 R/XYZ null 728 null]>>endobj
+1184 0 obj<</D[1890 0 R/XYZ null 683 null]>>endobj
+1185 0 obj<</D[1890 0 R/XYZ null 596 null]>>endobj
+1186 0 obj<</D[1890 0 R/XYZ null 546 null]>>endobj
+1187 0 obj<</D[1890 0 R/XYZ null 495 null]>>endobj
+1188 0 obj<</D[1890 0 R/XYZ null 451 null]>>endobj
+1189 0 obj<</D[1893 0 R/XYZ null 728 null]>>endobj
+1190 0 obj<</D[1893 0 R/XYZ null 683 null]>>endobj
+1191 0 obj<</D[1893 0 R/XYZ null 596 null]>>endobj
+1192 0 obj<</D[1893 0 R/XYZ null 546 null]>>endobj
+1193 0 obj<</D[1893 0 R/XYZ null 495 null]>>endobj
+1194 0 obj<</D[1893 0 R/XYZ null 451 null]>>endobj
+1195 0 obj<</D[1896 0 R/XYZ null 728 null]>>endobj
+1196 0 obj<</D[1896 0 R/XYZ null 683 null]>>endobj
+1197 0 obj<</D[1896 0 R/XYZ null 596 null]>>endobj
+1198 0 obj<</D[1896 0 R/XYZ null 546 null]>>endobj
+1199 0 obj<</D[1896 0 R/XYZ null 495 null]>>endobj
+1200 0 obj<</D[1896 0 R/XYZ null 451 null]>>endobj
+1201 0 obj<</D[1899 0 R/XYZ null 728 null]>>endobj
+1202 0 obj<</D[1899 0 R/XYZ null 683 null]>>endobj
+1203 0 obj<</D[1899 0 R/XYZ null 596 null]>>endobj
+1204 0 obj<</D[1899 0 R/XYZ null 546 null]>>endobj
+1205 0 obj<</D[1899 0 R/XYZ null 495 null]>>endobj
+1206 0 obj<</D[1899 0 R/XYZ null 451 null]>>endobj
+1207 0 obj<</D[1902 0 R/XYZ null 728 null]>>endobj
+1208 0 obj<</D[1902 0 R/XYZ null 683 null]>>endobj
+1209 0 obj<</D[1902 0 R/XYZ null 596 null]>>endobj
+1210 0 obj<</D[1902 0 R/XYZ null 546 null]>>endobj
+1211 0 obj<</D[1902 0 R/XYZ null 495 null]>>endobj
+1212 0 obj<</D[1902 0 R/XYZ null 451 null]>>endobj
+1213 0 obj<</D[1905 0 R/XYZ null 728 null]>>endobj
+1214 0 obj<</D[1905 0 R/XYZ null 683 null]>>endobj
+1215 0 obj<</D[1905 0 R/XYZ null 596 null]>>endobj
+1216 0 obj<</D[1905 0 R/XYZ null 546 null]>>endobj
+1217 0 obj<</D[1905 0 R/XYZ null 495 null]>>endobj
+1218 0 obj<</D[1905 0 R/XYZ null 451 null]>>endobj
+1219 0 obj<</D[1908 0 R/XYZ null 728 null]>>endobj
+1220 0 obj<</D[1908 0 R/XYZ null 683 null]>>endobj
+1221 0 obj<</D[1908 0 R/XYZ null 596 null]>>endobj
+1222 0 obj<</D[1908 0 R/XYZ null 546 null]>>endobj
+1223 0 obj<</D[1908 0 R/XYZ null 495 null]>>endobj
+1224 0 obj<</D[1908 0 R/XYZ null 451 null]>>endobj
+1225 0 obj<</D[1782 0 R/XYZ null 728 null]>>endobj
+1226 0 obj<</D[1782 0 R/XYZ null 629 null]>>endobj
+1227 0 obj<</D[1782 0 R/XYZ null 468 null]>>endobj
+1228 0 obj<</D[1782 0 R/XYZ null 395 null]>>endobj
+1229 0 obj<</D[1782 0 R/XYZ null 309 null]>>endobj
+1230 0 obj<</D[1785 0 R/XYZ null 570 null]>>endobj
+1231 0 obj<</D[1911 0 R/XYZ null 728 null]>>endobj
+1232 0 obj<</D[1911 0 R/XYZ null 683 null]>>endobj
+1233 0 obj<</D[1911 0 R/XYZ null 596 null]>>endobj
+1234 0 obj<</D[1911 0 R/XYZ null 546 null]>>endobj
+1235 0 obj<</D[1911 0 R/XYZ null 495 null]>>endobj
+1236 0 obj<</D[1911 0 R/XYZ null 451 null]>>endobj
+1237 0 obj<</D[1914 0 R/XYZ null 728 null]>>endobj
+1238 0 obj<</D[1914 0 R/XYZ null 683 null]>>endobj
+1239 0 obj<</D[1914 0 R/XYZ null 596 null]>>endobj
+1240 0 obj<</D[1914 0 R/XYZ null 546 null]>>endobj
+1241 0 obj<</D[1914 0 R/XYZ null 495 null]>>endobj
+1242 0 obj<</D[1914 0 R/XYZ null 451 null]>>endobj
+1243 0 obj<</D[1917 0 R/XYZ null 728 null]>>endobj
+1244 0 obj<</D[1917 0 R/XYZ null 683 null]>>endobj
+1245 0 obj<</D[1917 0 R/XYZ null 596 null]>>endobj
+1246 0 obj<</D[1917 0 R/XYZ null 546 null]>>endobj
+1247 0 obj<</D[1917 0 R/XYZ null 495 null]>>endobj
+1248 0 obj<</D[1917 0 R/XYZ null 451 null]>>endobj
+1249 0 obj<</D[1920 0 R/XYZ null 728 null]>>endobj
+1250 0 obj<</D[1920 0 R/XYZ null 683 null]>>endobj
+1251 0 obj<</D[1920 0 R/XYZ null 596 null]>>endobj
+1252 0 obj<</D[1920 0 R/XYZ null 546 null]>>endobj
+1253 0 obj<</D[1920 0 R/XYZ null 495 null]>>endobj
+1254 0 obj<</D[1920 0 R/XYZ null 451 null]>>endobj
+1255 0 obj<</D[1923 0 R/XYZ null 728 null]>>endobj
+1256 0 obj<</D[1923 0 R/XYZ null 683 null]>>endobj
+1257 0 obj<</D[1923 0 R/XYZ null 596 null]>>endobj
+1258 0 obj<</D[1923 0 R/XYZ null 546 null]>>endobj
+1259 0 obj<</D[1923 0 R/XYZ null 495 null]>>endobj
+1260 0 obj<</D[1923 0 R/XYZ null 451 null]>>endobj
+1261 0 obj<</D[1926 0 R/XYZ null 728 null]>>endobj
+1262 0 obj<</D[1926 0 R/XYZ null 683 null]>>endobj
+1263 0 obj<</D[1926 0 R/XYZ null 596 null]>>endobj
+1264 0 obj<</D[1926 0 R/XYZ null 546 null]>>endobj
+1265 0 obj<</D[1926 0 R/XYZ null 495 null]>>endobj
+1266 0 obj<</D[1926 0 R/XYZ null 451 null]>>endobj
+1267 0 obj<</D[1929 0 R/XYZ null 728 null]>>endobj
+1268 0 obj<</D[1929 0 R/XYZ null 683 null]>>endobj
+1269 0 obj<</D[1929 0 R/XYZ null 596 null]>>endobj
+1270 0 obj<</D[1929 0 R/XYZ null 546 null]>>endobj
+1271 0 obj<</D[1929 0 R/XYZ null 495 null]>>endobj
+1272 0 obj<</D[1929 0 R/XYZ null 451 null]>>endobj
+1273 0 obj<</D[1932 0 R/XYZ null 728 null]>>endobj
+1274 0 obj<</D[1932 0 R/XYZ null 683 null]>>endobj
+1275 0 obj<</D[1932 0 R/XYZ null 596 null]>>endobj
+1276 0 obj<</D[1932 0 R/XYZ null 546 null]>>endobj
+1277 0 obj<</D[1932 0 R/XYZ null 495 null]>>endobj
+1278 0 obj<</D[1932 0 R/XYZ null 451 null]>>endobj
+1279 0 obj<</D[1935 0 R/XYZ null 728 null]>>endobj
+1280 0 obj<</D[1935 0 R/XYZ null 683 null]>>endobj
+1281 0 obj<</D[1935 0 R/XYZ null 596 null]>>endobj
+1282 0 obj<</D[1935 0 R/XYZ null 546 null]>>endobj
+1283 0 obj<</D[1935 0 R/XYZ null 495 null]>>endobj
+1284 0 obj<</D[1935 0 R/XYZ null 451 null]>>endobj
+1285 0 obj<</D[1938 0 R/XYZ null 728 null]>>endobj
+1286 0 obj<</D[1938 0 R/XYZ null 683 null]>>endobj
+1287 0 obj<</D[1938 0 R/XYZ null 596 null]>>endobj
+1288 0 obj<</D[1938 0 R/XYZ null 546 null]>>endobj
+1289 0 obj<</D[1938 0 R/XYZ null 495 null]>>endobj
+1290 0 obj<</D[1938 0 R/XYZ null 451 null]>>endobj
+1291 0 obj<</D[1788 0 R/XYZ null 728 null]>>endobj
+1292 0 obj<</D[1788 0 R/XYZ null 640 null]>>endobj
+1293 0 obj<</D[1788 0 R/XYZ null 499 null]>>endobj
+1294 0 obj<</D[1788 0 R/XYZ null 426 null]>>endobj
+1295 0 obj<</D[1788 0 R/XYZ null 353 null]>>endobj
+1296 0 obj<</D[1791 0 R/XYZ null 646 null]>>endobj
+1297 0 obj<</D[1941 0 R/XYZ null 728 null]>>endobj
+1298 0 obj<</D[1941 0 R/XYZ null 683 null]>>endobj
+1299 0 obj<</D[1941 0 R/XYZ null 596 null]>>endobj
+1300 0 obj<</D[1941 0 R/XYZ null 546 null]>>endobj
+1301 0 obj<</D[1941 0 R/XYZ null 495 null]>>endobj
+1302 0 obj<</D[1941 0 R/XYZ null 451 null]>>endobj
+1303 0 obj<</D[1944 0 R/XYZ null 728 null]>>endobj
+1304 0 obj<</D[1944 0 R/XYZ null 683 null]>>endobj
+1305 0 obj<</D[1944 0 R/XYZ null 596 null]>>endobj
+1306 0 obj<</D[1944 0 R/XYZ null 546 null]>>endobj
+1307 0 obj<</D[1944 0 R/XYZ null 495 null]>>endobj
+1308 0 obj<</D[1944 0 R/XYZ null 451 null]>>endobj
+1309 0 obj<</D[1947 0 R/XYZ null 728 null]>>endobj
+1310 0 obj<</D[1947 0 R/XYZ null 683 null]>>endobj
+1311 0 obj<</D[1947 0 R/XYZ null 596 null]>>endobj
+1312 0 obj<</D[1947 0 R/XYZ null 546 null]>>endobj
+1313 0 obj<</D[1947 0 R/XYZ null 495 null]>>endobj
+1314 0 obj<</D[1947 0 R/XYZ null 451 null]>>endobj
+1315 0 obj<</D[1950 0 R/XYZ null 728 null]>>endobj
+1316 0 obj<</D[1950 0 R/XYZ null 683 null]>>endobj
+1317 0 obj<</D[1950 0 R/XYZ null 596 null]>>endobj
+1318 0 obj<</D[1950 0 R/XYZ null 546 null]>>endobj
+1319 0 obj<</D[1950 0 R/XYZ null 495 null]>>endobj
+1320 0 obj<</D[1950 0 R/XYZ null 451 null]>>endobj
+1321 0 obj<</D[1953 0 R/XYZ null 728 null]>>endobj
+1322 0 obj<</D[1953 0 R/XYZ null 683 null]>>endobj
+1323 0 obj<</D[1953 0 R/XYZ null 596 null]>>endobj
+1324 0 obj<</D[1953 0 R/XYZ null 546 null]>>endobj
+1325 0 obj<</D[1953 0 R/XYZ null 495 null]>>endobj
+1326 0 obj<</D[1953 0 R/XYZ null 451 null]>>endobj
+1327 0 obj<</D[1956 0 R/XYZ null 728 null]>>endobj
+1328 0 obj<</D[1956 0 R/XYZ null 683 null]>>endobj
+1329 0 obj<</D[1956 0 R/XYZ null 596 null]>>endobj
+1330 0 obj<</D[1956 0 R/XYZ null 546 null]>>endobj
+1331 0 obj<</D[1956 0 R/XYZ null 495 null]>>endobj
+1332 0 obj<</D[1956 0 R/XYZ null 451 null]>>endobj
+1333 0 obj<</D[1959 0 R/XYZ null 728 null]>>endobj
+1334 0 obj<</D[1959 0 R/XYZ null 683 null]>>endobj
+1335 0 obj<</D[1959 0 R/XYZ null 596 null]>>endobj
+1336 0 obj<</D[1959 0 R/XYZ null 546 null]>>endobj
+1337 0 obj<</D[1959 0 R/XYZ null 495 null]>>endobj
+1338 0 obj<</D[1959 0 R/XYZ null 451 null]>>endobj
+1339 0 obj<</D[1962 0 R/XYZ null 728 null]>>endobj
+1340 0 obj<</D[1962 0 R/XYZ null 683 null]>>endobj
+1341 0 obj<</D[1962 0 R/XYZ null 596 null]>>endobj
+1342 0 obj<</D[1962 0 R/XYZ null 546 null]>>endobj
+1343 0 obj<</D[1962 0 R/XYZ null 495 null]>>endobj
+1344 0 obj<</D[1962 0 R/XYZ null 451 null]>>endobj
+1345 0 obj<</D[1965 0 R/XYZ null 728 null]>>endobj
+1346 0 obj<</D[1965 0 R/XYZ null 683 null]>>endobj
+1347 0 obj<</D[1965 0 R/XYZ null 596 null]>>endobj
+1348 0 obj<</D[1965 0 R/XYZ null 546 null]>>endobj
+1349 0 obj<</D[1965 0 R/XYZ null 495 null]>>endobj
+1350 0 obj<</D[1965 0 R/XYZ null 451 null]>>endobj
+1351 0 obj<</D[1968 0 R/XYZ null 728 null]>>endobj
+1352 0 obj<</D[1968 0 R/XYZ null 683 null]>>endobj
+1353 0 obj<</D[1968 0 R/XYZ null 596 null]>>endobj
+1354 0 obj<</D[1968 0 R/XYZ null 546 null]>>endobj
+1355 0 obj<</D[1968 0 R/XYZ null 495 null]>>endobj
+1356 0 obj<</D[1968 0 R/XYZ null 451 null]>>endobj
+1357 0 obj<</D[1794 0 R/XYZ null 728 null]>>endobj
+1358 0 obj<</D[1794 0 R/XYZ null 640 null]>>endobj
+1359 0 obj<</D[1794 0 R/XYZ null 519 null]>>endobj
+1360 0 obj<</D[1794 0 R/XYZ null 459 null]>>endobj
+1361 0 obj<</D[1794 0 R/XYZ null 329 null]>>endobj
+1362 0 obj<</D[1971 0 R/XYZ null 728 null]>>endobj
+1363 0 obj<</D[1971 0 R/XYZ null 683 null]>>endobj
+1364 0 obj<</D[1971 0 R/XYZ null 596 null]>>endobj
+1365 0 obj<</D[1971 0 R/XYZ null 546 null]>>endobj
+1366 0 obj<</D[1971 0 R/XYZ null 495 null]>>endobj
+1367 0 obj<</D[1971 0 R/XYZ null 451 null]>>endobj
+1368 0 obj<</D[1974 0 R/XYZ null 728 null]>>endobj
+1369 0 obj<</D[1974 0 R/XYZ null 683 null]>>endobj
+1370 0 obj<</D[1974 0 R/XYZ null 596 null]>>endobj
+1371 0 obj<</D[1974 0 R/XYZ null 546 null]>>endobj
+1372 0 obj<</D[1974 0 R/XYZ null 495 null]>>endobj
+1373 0 obj<</D[1974 0 R/XYZ null 451 null]>>endobj
+1374 0 obj<</D[1977 0 R/XYZ null 728 null]>>endobj
+1375 0 obj<</D[1977 0 R/XYZ null 683 null]>>endobj
+1376 0 obj<</D[1977 0 R/XYZ null 596 null]>>endobj
+1377 0 obj<</D[1977 0 R/XYZ null 546 null]>>endobj
+1378 0 obj<</D[1977 0 R/XYZ null 495 null]>>endobj
+1379 0 obj<</D[1977 0 R/XYZ null 451 null]>>endobj
+1380 0 obj<</D[1980 0 R/XYZ null 728 null]>>endobj
+1381 0 obj<</D[1980 0 R/XYZ null 683 null]>>endobj
+1382 0 obj<</D[1980 0 R/XYZ null 596 null]>>endobj
+1383 0 obj<</D[1980 0 R/XYZ null 546 null]>>endobj
+1384 0 obj<</D[1980 0 R/XYZ null 495 null]>>endobj
+1385 0 obj<</D[1980 0 R/XYZ null 451 null]>>endobj
+1386 0 obj<</D[1983 0 R/XYZ null 728 null]>>endobj
+1387 0 obj<</D[1983 0 R/XYZ null 683 null]>>endobj
+1388 0 obj<</D[1983 0 R/XYZ null 596 null]>>endobj
+1389 0 obj<</D[1983 0 R/XYZ null 546 null]>>endobj
+1390 0 obj<</D[1983 0 R/XYZ null 495 null]>>endobj
+1391 0 obj<</D[1983 0 R/XYZ null 451 null]>>endobj
+1392 0 obj<</D[1986 0 R/XYZ null 728 null]>>endobj
+1393 0 obj<</D[1986 0 R/XYZ null 683 null]>>endobj
+1394 0 obj<</D[1986 0 R/XYZ null 596 null]>>endobj
+1395 0 obj<</D[1986 0 R/XYZ null 546 null]>>endobj
+1396 0 obj<</D[1986 0 R/XYZ null 495 null]>>endobj
+1397 0 obj<</D[1986 0 R/XYZ null 451 null]>>endobj
+1398 0 obj<</D[1989 0 R/XYZ null 728 null]>>endobj
+1399 0 obj<</D[1989 0 R/XYZ null 683 null]>>endobj
+1400 0 obj<</D[1989 0 R/XYZ null 596 null]>>endobj
+1401 0 obj<</D[1989 0 R/XYZ null 546 null]>>endobj
+1402 0 obj<</D[1989 0 R/XYZ null 495 null]>>endobj
+1403 0 obj<</D[1989 0 R/XYZ null 451 null]>>endobj
+1404 0 obj<</D[1992 0 R/XYZ null 728 null]>>endobj
+1405 0 obj<</D[1992 0 R/XYZ null 683 null]>>endobj
+1406 0 obj<</D[1992 0 R/XYZ null 596 null]>>endobj
+1407 0 obj<</D[1992 0 R/XYZ null 546 null]>>endobj
+1408 0 obj<</D[1992 0 R/XYZ null 495 null]>>endobj
+1409 0 obj<</D[1992 0 R/XYZ null 451 null]>>endobj
+1410 0 obj<</D[1995 0 R/XYZ null 728 null]>>endobj
+1411 0 obj<</D[1995 0 R/XYZ null 683 null]>>endobj
+1412 0 obj<</D[1995 0 R/XYZ null 596 null]>>endobj
+1413 0 obj<</D[1995 0 R/XYZ null 546 null]>>endobj
+1414 0 obj<</D[1995 0 R/XYZ null 495 null]>>endobj
+1415 0 obj<</D[1995 0 R/XYZ null 451 null]>>endobj
+1416 0 obj<</D[1998 0 R/XYZ null 728 null]>>endobj
+1417 0 obj<</D[1998 0 R/XYZ null 683 null]>>endobj
+1418 0 obj<</D[1998 0 R/XYZ null 596 null]>>endobj
+1419 0 obj<</D[1998 0 R/XYZ null 546 null]>>endobj
+1420 0 obj<</D[1998 0 R/XYZ null 495 null]>>endobj
+1421 0 obj<</D[1998 0 R/XYZ null 451 null]>>endobj
+1422 0 obj<</D[1797 0 R/XYZ null 728 null]>>endobj
+1423 0 obj<</D[1797 0 R/XYZ null 662 null]>>endobj
+1424 0 obj<</D[1797 0 R/XYZ null 561 null]>>endobj
+1425 0 obj<</D[1797 0 R/XYZ null 501 null]>>endobj
+1426 0 obj<</D[1797 0 R/XYZ null 428 null]>>endobj
+1427 0 obj<</D[1797 0 R/XYZ null 178 null]>>endobj
+1428 0 obj<</D[2001 0 R/XYZ null 728 null]>>endobj
+1429 0 obj<</D[2001 0 R/XYZ null 683 null]>>endobj
+1430 0 obj<</D[2001 0 R/XYZ null 596 null]>>endobj
+1431 0 obj<</D[2001 0 R/XYZ null 546 null]>>endobj
+1432 0 obj<</D[2001 0 R/XYZ null 495 null]>>endobj
+1433 0 obj<</D[2001 0 R/XYZ null 451 null]>>endobj
+1434 0 obj<</D[2004 0 R/XYZ null 728 null]>>endobj
+1435 0 obj<</D[2004 0 R/XYZ null 683 null]>>endobj
+1436 0 obj<</D[2004 0 R/XYZ null 596 null]>>endobj
+1437 0 obj<</D[2004 0 R/XYZ null 546 null]>>endobj
+1438 0 obj<</D[2004 0 R/XYZ null 495 null]>>endobj
+1439 0 obj<</D[2004 0 R/XYZ null 451 null]>>endobj
+1440 0 obj<</D[2007 0 R/XYZ null 728 null]>>endobj
+1441 0 obj<</D[2007 0 R/XYZ null 683 null]>>endobj
+1442 0 obj<</D[2007 0 R/XYZ null 596 null]>>endobj
+1443 0 obj<</D[2007 0 R/XYZ null 546 null]>>endobj
+1444 0 obj<</D[2007 0 R/XYZ null 495 null]>>endobj
+1445 0 obj<</D[2007 0 R/XYZ null 451 null]>>endobj
+1446 0 obj<</D[2010 0 R/XYZ null 728 null]>>endobj
+1447 0 obj<</D[2010 0 R/XYZ null 683 null]>>endobj
+1448 0 obj<</D[2010 0 R/XYZ null 596 null]>>endobj
+1449 0 obj<</D[2010 0 R/XYZ null 546 null]>>endobj
+1450 0 obj<</D[2010 0 R/XYZ null 495 null]>>endobj
+1451 0 obj<</D[2010 0 R/XYZ null 451 null]>>endobj
+1452 0 obj<</D[2013 0 R/XYZ null 728 null]>>endobj
+1453 0 obj<</D[2013 0 R/XYZ null 683 null]>>endobj
+1454 0 obj<</D[2013 0 R/XYZ null 596 null]>>endobj
+1455 0 obj<</D[2013 0 R/XYZ null 546 null]>>endobj
+1456 0 obj<</D[2013 0 R/XYZ null 495 null]>>endobj
+1457 0 obj<</D[2013 0 R/XYZ null 451 null]>>endobj
+1458 0 obj<</D[2016 0 R/XYZ null 728 null]>>endobj
+1459 0 obj<</D[2016 0 R/XYZ null 683 null]>>endobj
+1460 0 obj<</D[2016 0 R/XYZ null 596 null]>>endobj
+1461 0 obj<</D[2016 0 R/XYZ null 546 null]>>endobj
+1462 0 obj<</D[2016 0 R/XYZ null 495 null]>>endobj
+1463 0 obj<</D[2016 0 R/XYZ null 451 null]>>endobj
+1464 0 obj<</D[2019 0 R/XYZ null 728 null]>>endobj
+1465 0 obj<</D[2019 0 R/XYZ null 683 null]>>endobj
+1466 0 obj<</D[2019 0 R/XYZ null 596 null]>>endobj
+1467 0 obj<</D[2019 0 R/XYZ null 546 null]>>endobj
+1468 0 obj<</D[2019 0 R/XYZ null 495 null]>>endobj
+1469 0 obj<</D[2019 0 R/XYZ null 451 null]>>endobj
+1470 0 obj<</D[2022 0 R/XYZ null 728 null]>>endobj
+1471 0 obj<</D[2022 0 R/XYZ null 683 null]>>endobj
+1472 0 obj<</D[2022 0 R/XYZ null 596 null]>>endobj
+1473 0 obj<</D[2022 0 R/XYZ null 546 null]>>endobj
+1474 0 obj<</D[2022 0 R/XYZ null 495 null]>>endobj
+1475 0 obj<</D[2022 0 R/XYZ null 451 null]>>endobj
+1476 0 obj<</D[2025 0 R/XYZ null 728 null]>>endobj
+1477 0 obj<</D[2025 0 R/XYZ null 683 null]>>endobj
+1478 0 obj<</D[2025 0 R/XYZ null 596 null]>>endobj
+1479 0 obj<</D[2025 0 R/XYZ null 546 null]>>endobj
+1480 0 obj<</D[2025 0 R/XYZ null 495 null]>>endobj
+1481 0 obj<</D[2025 0 R/XYZ null 451 null]>>endobj
+1482 0 obj<</D[2028 0 R/XYZ null 728 null]>>endobj
+1483 0 obj<</D[2028 0 R/XYZ null 683 null]>>endobj
+1484 0 obj<</D[2028 0 R/XYZ null 596 null]>>endobj
+1485 0 obj<</D[2028 0 R/XYZ null 546 null]>>endobj
+1486 0 obj<</D[2028 0 R/XYZ null 495 null]>>endobj
+1487 0 obj<</D[2028 0 R/XYZ null 451 null]>>endobj
+1488 0 obj<</D[1800 0 R/XYZ null 728 null]>>endobj
+1489 0 obj<</D[1800 0 R/XYZ null 662 null]>>endobj
+1490 0 obj<</D[1800 0 R/XYZ null 602 null]>>endobj
+1491 0 obj<</D[1800 0 R/XYZ null 528 null]>>endobj
+1492 0 obj<</D[1800 0 R/XYZ null 452 null]>>endobj
+1493 0 obj<</D[2031 0 R/XYZ null 728 null]>>endobj
+1494 0 obj<</D[2031 0 R/XYZ null 683 null]>>endobj
+1495 0 obj<</D[2031 0 R/XYZ null 596 null]>>endobj
+1496 0 obj<</D[2031 0 R/XYZ null 546 null]>>endobj
+1497 0 obj<</D[2031 0 R/XYZ null 495 null]>>endobj
+1498 0 obj<</D[2031 0 R/XYZ null 451 null]>>endobj
+1499 0 obj<</D[2034 0 R/XYZ null 728 null]>>endobj
+1500 0 obj<</D[2034 0 R/XYZ null 683 null]>>endobj
+1501 0 obj<</D[2034 0 R/XYZ null 596 null]>>endobj
+1502 0 obj<</D[2034 0 R/XYZ null 546 null]>>endobj
+1503 0 obj<</D[2034 0 R/XYZ null 495 null]>>endobj
+1504 0 obj<</D[2034 0 R/XYZ null 451 null]>>endobj
+1505 0 obj<</D[2037 0 R/XYZ null 728 null]>>endobj
+1506 0 obj<</D[2037 0 R/XYZ null 683 null]>>endobj
+1507 0 obj<</D[2037 0 R/XYZ null 596 null]>>endobj
+1508 0 obj<</D[2037 0 R/XYZ null 546 null]>>endobj
+1509 0 obj<</D[2037 0 R/XYZ null 495 null]>>endobj
+1510 0 obj<</D[2037 0 R/XYZ null 451 null]>>endobj
+1511 0 obj<</D[2040 0 R/XYZ null 728 null]>>endobj
+1512 0 obj<</D[2040 0 R/XYZ null 683 null]>>endobj
+1513 0 obj<</D[2040 0 R/XYZ null 596 null]>>endobj
+1514 0 obj<</D[2040 0 R/XYZ null 546 null]>>endobj
+1515 0 obj<</D[2040 0 R/XYZ null 495 null]>>endobj
+1516 0 obj<</D[2040 0 R/XYZ null 451 null]>>endobj
+1517 0 obj<</D[2043 0 R/XYZ null 728 null]>>endobj
+1518 0 obj<</D[2043 0 R/XYZ null 683 null]>>endobj
+1519 0 obj<</D[2043 0 R/XYZ null 596 null]>>endobj
+1520 0 obj<</D[2043 0 R/XYZ null 546 null]>>endobj
+1521 0 obj<</D[2043 0 R/XYZ null 495 null]>>endobj
+1522 0 obj<</D[2043 0 R/XYZ null 451 null]>>endobj
+1523 0 obj<</D[2046 0 R/XYZ null 728 null]>>endobj
+1524 0 obj<</D[2046 0 R/XYZ null 683 null]>>endobj
+1525 0 obj<</D[2046 0 R/XYZ null 596 null]>>endobj
+1526 0 obj<</D[2046 0 R/XYZ null 546 null]>>endobj
+1527 0 obj<</D[2046 0 R/XYZ null 495 null]>>endobj
+1528 0 obj<</D[2046 0 R/XYZ null 451 null]>>endobj
+1529 0 obj<</D[2049 0 R/XYZ null 728 null]>>endobj
+1530 0 obj<</D[2049 0 R/XYZ null 683 null]>>endobj
+1531 0 obj<</D[2049 0 R/XYZ null 596 null]>>endobj
+1532 0 obj<</D[2049 0 R/XYZ null 546 null]>>endobj
+1533 0 obj<</D[2049 0 R/XYZ null 495 null]>>endobj
+1534 0 obj<</D[2049 0 R/XYZ null 451 null]>>endobj
+1535 0 obj<</D[2052 0 R/XYZ null 728 null]>>endobj
+1536 0 obj<</D[2052 0 R/XYZ null 683 null]>>endobj
+1537 0 obj<</D[2052 0 R/XYZ null 596 null]>>endobj
+1538 0 obj<</D[2052 0 R/XYZ null 546 null]>>endobj
+1539 0 obj<</D[2052 0 R/XYZ null 495 null]>>endobj
+1540 0 obj<</D[2052 0 R/XYZ null 451 null]>>endobj
+1541 0 obj<</D[2055 0 R/XYZ null 728 null]>>endobj
+1542 0 obj<</D[2055 0 R/XYZ null 683 null]>>endobj
+1543 0 obj<</D[2055 0 R/XYZ null 596 null]>>endobj
+1544 0 obj<</D[2055 0 R/XYZ null 546 null]>>endobj
+1545 0 obj<</D[2055 0 R/XYZ null 495 null]>>endobj
+1546 0 obj<</D[2055 0 R/XYZ null 451 null]>>endobj
+1547 0 obj<</D[2058 0 R/XYZ null 728 null]>>endobj
+1548 0 obj<</D[2058 0 R/XYZ null 683 null]>>endobj
+1549 0 obj<</D[2058 0 R/XYZ null 596 null]>>endobj
+1550 0 obj<</D[2058 0 R/XYZ null 546 null]>>endobj
+1551 0 obj<</D[2058 0 R/XYZ null 495 null]>>endobj
+1552 0 obj<</D[2058 0 R/XYZ null 451 null]>>endobj
+1553 0 obj<</D[1803 0 R/XYZ null 728 null]>>endobj
+1554 0 obj<</D[1803 0 R/XYZ null 640 null]>>endobj
+1555 0 obj<</D[1803 0 R/XYZ null 499 null]>>endobj
+1556 0 obj<</D[1803 0 R/XYZ null 439 null]>>endobj
+1557 0 obj<</D[1803 0 R/XYZ null 258 null]>>endobj
+1558 0 obj<</D[2061 0 R/XYZ null 728 null]>>endobj
+1559 0 obj<</D[2061 0 R/XYZ null 683 null]>>endobj
+1560 0 obj<</D[2061 0 R/XYZ null 596 null]>>endobj
+1561 0 obj<</D[2061 0 R/XYZ null 546 null]>>endobj
+1562 0 obj<</D[2061 0 R/XYZ null 495 null]>>endobj
+1563 0 obj<</D[2061 0 R/XYZ null 451 null]>>endobj
+1564 0 obj<</D[2064 0 R/XYZ null 728 null]>>endobj
+1565 0 obj<</D[2064 0 R/XYZ null 683 null]>>endobj
+1566 0 obj<</D[2064 0 R/XYZ null 596 null]>>endobj
+1567 0 obj<</D[2064 0 R/XYZ null 546 null]>>endobj
+1568 0 obj<</D[2064 0 R/XYZ null 495 null]>>endobj
+1569 0 obj<</D[2064 0 R/XYZ null 451 null]>>endobj
+1570 0 obj<</D[2067 0 R/XYZ null 728 null]>>endobj
+1571 0 obj<</D[2067 0 R/XYZ null 683 null]>>endobj
+1572 0 obj<</D[2067 0 R/XYZ null 596 null]>>endobj
+1573 0 obj<</D[2067 0 R/XYZ null 546 null]>>endobj
+1574 0 obj<</D[2067 0 R/XYZ null 495 null]>>endobj
+1575 0 obj<</D[2067 0 R/XYZ null 451 null]>>endobj
+1576 0 obj<</D[2070 0 R/XYZ null 728 null]>>endobj
+1577 0 obj<</D[2070 0 R/XYZ null 683 null]>>endobj
+1578 0 obj<</D[2070 0 R/XYZ null 596 null]>>endobj
+1579 0 obj<</D[2070 0 R/XYZ null 546 null]>>endobj
+1580 0 obj<</D[2070 0 R/XYZ null 495 null]>>endobj
+1581 0 obj<</D[2070 0 R/XYZ null 451 null]>>endobj
+1582 0 obj<</D[2073 0 R/XYZ null 728 null]>>endobj
+1583 0 obj<</D[2073 0 R/XYZ null 683 null]>>endobj
+1584 0 obj<</D[2073 0 R/XYZ null 596 null]>>endobj
+1585 0 obj<</D[2073 0 R/XYZ null 546 null]>>endobj
+1586 0 obj<</D[2073 0 R/XYZ null 495 null]>>endobj
+1587 0 obj<</D[2073 0 R/XYZ null 451 null]>>endobj
+1588 0 obj<</D[2076 0 R/XYZ null 728 null]>>endobj
+1589 0 obj<</D[2076 0 R/XYZ null 683 null]>>endobj
+1590 0 obj<</D[2076 0 R/XYZ null 596 null]>>endobj
+1591 0 obj<</D[2076 0 R/XYZ null 546 null]>>endobj
+1592 0 obj<</D[2076 0 R/XYZ null 495 null]>>endobj
+1593 0 obj<</D[2076 0 R/XYZ null 451 null]>>endobj
+1594 0 obj<</D[2079 0 R/XYZ null 728 null]>>endobj
+1595 0 obj<</D[2079 0 R/XYZ null 683 null]>>endobj
+1596 0 obj<</D[2079 0 R/XYZ null 596 null]>>endobj
+1597 0 obj<</D[2079 0 R/XYZ null 546 null]>>endobj
+1598 0 obj<</D[2079 0 R/XYZ null 495 null]>>endobj
+1599 0 obj<</D[2079 0 R/XYZ null 451 null]>>endobj
+1600 0 obj<</D[1806 0 R/XYZ null 728 null]>>endobj
+1601 0 obj<</D[1806 0 R/XYZ null 662 null]>>endobj
+1602 0 obj<</D[1806 0 R/XYZ null 561 null]>>endobj
+1603 0 obj<</D[1806 0 R/XYZ null 501 null]>>endobj
+1604 0 obj<</D[1806 0 R/XYZ null 428 null]>>endobj
+1605 0 obj<</D[1806 0 R/XYZ null 308 null]>>endobj
+1606 0 obj<</D[1773 0 R/XYZ null 799 null]>>endobj
+1607 0 obj<</D[1779 0 R/XYZ null 799 null]>>endobj
+1608 0 obj<</D[1782 0 R/XYZ null 799 null]>>endobj
+1609 0 obj<</D[1788 0 R/XYZ null 799 null]>>endobj
+1610 0 obj<</D[1794 0 R/XYZ null 799 null]>>endobj
+1611 0 obj<</D[1797 0 R/XYZ null 799 null]>>endobj
+1612 0 obj<</D[1800 0 R/XYZ null 799 null]>>endobj
+1613 0 obj<</D[1803 0 R/XYZ null 799 null]>>endobj
+1614 0 obj<</D[1806 0 R/XYZ null 799 null]>>endobj
+1615 0 obj<</D[1809 0 R/XYZ null 799 null]>>endobj
+1616 0 obj<</D[1812 0 R/XYZ null 799 null]>>endobj
+1617 0 obj<</D[1815 0 R/XYZ null 799 null]>>endobj
+1618 0 obj<</D[1818 0 R/XYZ null 799 null]>>endobj
+1619 0 obj<</D[1821 0 R/XYZ null 799 null]>>endobj
+1620 0 obj<</D[1824 0 R/XYZ null 799 null]>>endobj
+1621 0 obj<</D[1827 0 R/XYZ null 799 null]>>endobj
+1622 0 obj<</D[1830 0 R/XYZ null 799 null]>>endobj
+1623 0 obj<</D[1833 0 R/XYZ null 799 null]>>endobj
+1624 0 obj<</D[1836 0 R/XYZ null 799 null]>>endobj
+1625 0 obj<</D[1839 0 R/XYZ null 799 null]>>endobj
+1626 0 obj<</D[1842 0 R/XYZ null 799 null]>>endobj
+1627 0 obj<</D[1848 0 R/XYZ null 799 null]>>endobj
+1628 0 obj<</D[1851 0 R/XYZ null 799 null]>>endobj
+1629 0 obj<</D[1854 0 R/XYZ null 799 null]>>endobj
+1630 0 obj<</D[1860 0 R/XYZ null 799 null]>>endobj
+1631 0 obj<</D[1866 0 R/XYZ null 799 null]>>endobj
+1632 0 obj<</D[1869 0 R/XYZ null 799 null]>>endobj
+1633 0 obj<</D[1875 0 R/XYZ null 799 null]>>endobj
+1634 0 obj<</D[1878 0 R/XYZ null 799 null]>>endobj
+1635 0 obj<</D[1881 0 R/XYZ null 799 null]>>endobj
+1636 0 obj<</D[1884 0 R/XYZ null 799 null]>>endobj
+1637 0 obj<</D[1887 0 R/XYZ null 799 null]>>endobj
+1638 0 obj<</D[1890 0 R/XYZ null 799 null]>>endobj
+1639 0 obj<</D[1893 0 R/XYZ null 799 null]>>endobj
+1640 0 obj<</D[1896 0 R/XYZ null 799 null]>>endobj
+1641 0 obj<</D[1899 0 R/XYZ null 799 null]>>endobj
+1642 0 obj<</D[1902 0 R/XYZ null 799 null]>>endobj
+1643 0 obj<</D[1905 0 R/XYZ null 799 null]>>endobj
+1644 0 obj<</D[1908 0 R/XYZ null 799 null]>>endobj
+1645 0 obj<</D[1911 0 R/XYZ null 799 null]>>endobj
+1646 0 obj<</D[1914 0 R/XYZ null 799 null]>>endobj
+1647 0 obj<</D[1920 0 R/XYZ null 799 null]>>endobj
+1648 0 obj<</D[1923 0 R/XYZ null 799 null]>>endobj
+1649 0 obj<</D[1926 0 R/XYZ null 799 null]>>endobj
+1650 0 obj<</D[1929 0 R/XYZ null 799 null]>>endobj
+1651 0 obj<</D[1917 0 R/XYZ null 799 null]>>endobj
+1652 0 obj<</D[1932 0 R/XYZ null 799 null]>>endobj
+1653 0 obj<</D[1935 0 R/XYZ null 799 null]>>endobj
+1654 0 obj<</D[1938 0 R/XYZ null 799 null]>>endobj
+1655 0 obj<</D[1941 0 R/XYZ null 799 null]>>endobj
+1656 0 obj<</D[1944 0 R/XYZ null 799 null]>>endobj
+1657 0 obj<</D[1947 0 R/XYZ null 799 null]>>endobj
+1658 0 obj<</D[1950 0 R/XYZ null 799 null]>>endobj
+1659 0 obj<</D[1953 0 R/XYZ null 799 null]>>endobj
+1660 0 obj<</D[1956 0 R/XYZ null 799 null]>>endobj
+1661 0 obj<</D[1959 0 R/XYZ null 799 null]>>endobj
+1662 0 obj<</D[1962 0 R/XYZ null 799 null]>>endobj
+1663 0 obj<</D[1965 0 R/XYZ null 799 null]>>endobj
+1664 0 obj<</D[1968 0 R/XYZ null 799 null]>>endobj
+1665 0 obj<</D[1971 0 R/XYZ null 799 null]>>endobj
+1666 0 obj<</D[1974 0 R/XYZ null 799 null]>>endobj
+1667 0 obj<</D[1977 0 R/XYZ null 799 null]>>endobj
+1668 0 obj<</D[1980 0 R/XYZ null 799 null]>>endobj
+1669 0 obj<</D[1983 0 R/XYZ null 799 null]>>endobj
+1670 0 obj<</D[1986 0 R/XYZ null 799 null]>>endobj
+1671 0 obj<</D[1989 0 R/XYZ null 799 null]>>endobj
+1672 0 obj<</D[1992 0 R/XYZ null 799 null]>>endobj
+1673 0 obj<</D[1995 0 R/XYZ null 799 null]>>endobj
+1674 0 obj<</D[1998 0 R/XYZ null 799 null]>>endobj
+1675 0 obj<</D[2001 0 R/XYZ null 799 null]>>endobj
+1676 0 obj<</D[2004 0 R/XYZ null 799 null]>>endobj
+1677 0 obj<</D[2007 0 R/XYZ null 799 null]>>endobj
+1678 0 obj<</D[2010 0 R/XYZ null 799 null]>>endobj
+1679 0 obj<</D[2013 0 R/XYZ null 799 null]>>endobj
+1680 0 obj<</D[2016 0 R/XYZ null 799 null]>>endobj
+1681 0 obj<</D[2019 0 R/XYZ null 799 null]>>endobj
+1682 0 obj<</D[2022 0 R/XYZ null 799 null]>>endobj
+1683 0 obj<</D[2025 0 R/XYZ null 799 null]>>endobj
+1684 0 obj<</D[2028 0 R/XYZ null 799 null]>>endobj
+1685 0 obj<</D[2031 0 R/XYZ null 799 null]>>endobj
+1686 0 obj<</D[2040 0 R/XYZ null 799 null]>>endobj
+1687 0 obj<</D[2034 0 R/XYZ null 799 null]>>endobj
+1688 0 obj<</D[2037 0 R/XYZ null 799 null]>>endobj
+1689 0 obj<</D[2043 0 R/XYZ null 799 null]>>endobj
+1690 0 obj<</D[2046 0 R/XYZ null 799 null]>>endobj
+1691 0 obj<</D[2049 0 R/XYZ null 799 null]>>endobj
+1692 0 obj<</D[2052 0 R/XYZ null 799 null]>>endobj
+1693 0 obj<</D[2055 0 R/XYZ null 799 null]>>endobj
+1694 0 obj<</D[2058 0 R/XYZ null 799 null]>>endobj
+1695 0 obj<</D[2061 0 R/XYZ null 799 null]>>endobj
+1696 0 obj<</D[2070 0 R/XYZ null 799 null]>>endobj
+1697 0 obj<</D[2064 0 R/XYZ null 799 null]>>endobj
+1698 0 obj<</D[2067 0 R/XYZ null 799 null]>>endobj
+1699 0 obj<</D[2073 0 R/XYZ null 799 null]>>endobj
+1700 0 obj<</D[2076 0 R/XYZ null 799 null]>>endobj
+1701 0 obj<</D[2079 0 R/XYZ null 799 null]>>endobj
+1702 0 obj<</D[1710 0 R/XYZ null 698 null]>>endobj
+1703 0 obj<</Type/Pages/MediaBox[0 0 595 792]/Count 144/Kids[1704 0 R
+1707 0 R
+2082 0 R
+2085 0 R
+2088 0 R
+2091 0 R
+2094 0 R
+2097 0 R
+2100 0 R
+2103 0 R
+2106 0 R
+2109 0 R
+2112 0 R
+2115 0 R
+2118 0 R
+2121 0 R
+2124 0 R
+2127 0 R
+2130 0 R
+2133 0 R
+1710 0 R
+1713 0 R
+1716 0 R
+1719 0 R
+1722 0 R
+1725 0 R
+1728 0 R
+1731 0 R
+1734 0 R
+1737 0 R
+1740 0 R
+1743 0 R
+1746 0 R
+1749 0 R
+1752 0 R
+1755 0 R
+1758 0 R
+1761 0 R
+1764 0 R
+1767 0 R
+1770 0 R
+1773 0 R
+1776 0 R
+1779 0 R
+1782 0 R
+1785 0 R
+1788 0 R
+1791 0 R
+1794 0 R
+1797 0 R
+1800 0 R
+1803 0 R
+1806 0 R
+1809 0 R
+1812 0 R
+1815 0 R
+1818 0 R
+1821 0 R
+1824 0 R
+1827 0 R
+1830 0 R
+1833 0 R
+1836 0 R
+1839 0 R
+1842 0 R
+1845 0 R
+1848 0 R
+1851 0 R
+1854 0 R
+1857 0 R
+1860 0 R
+1863 0 R
+1866 0 R
+1869 0 R
+1872 0 R
+1875 0 R
+1878 0 R
+1881 0 R
+1884 0 R
+1887 0 R
+1890 0 R
+1893 0 R
+1896 0 R
+1899 0 R
+1902 0 R
+1905 0 R
+1908 0 R
+1911 0 R
+1914 0 R
+1917 0 R
+1920 0 R
+1923 0 R
+1926 0 R
+1929 0 R
+1932 0 R
+1935 0 R
+1938 0 R
+1941 0 R
+1944 0 R
+1947 0 R
+1950 0 R
+1953 0 R
+1956 0 R
+1959 0 R
+1962 0 R
+1965 0 R
+1968 0 R
+1971 0 R
+1974 0 R
+1977 0 R
+1980 0 R
+1983 0 R
+1986 0 R
+1989 0 R
+1992 0 R
+1995 0 R
+1998 0 R
+2001 0 R
+2004 0 R
+2007 0 R
+2010 0 R
+2013 0 R
+2016 0 R
+2019 0 R
+2022 0 R
+2025 0 R
+2028 0 R
+2031 0 R
+2034 0 R
+2037 0 R
+2040 0 R
+2043 0 R
+2046 0 R
+2049 0 R
+2052 0 R
+2055 0 R
+2058 0 R
+2061 0 R
+2064 0 R
+2067 0 R
+2070 0 R
+2073 0 R
+2076 0 R
+2079 0 R
+]>>endobj
+1704 0 obj<</Type/Page/Parent 1703 0 R/Contents 1705 0 R/Resources<</ProcSet[/PDF/Text/ImageB/ImageC/ImageI]/Font<</F4 4 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+1705 0 obj<</Length 1706 0 R/Filter/FlateDecode>>stream
+xÚìÏsë8rÇIŠºÌ‰öŒî´üt§çyšÝ­ÚJñÙfvçÖ$ÁÊ!•S*Ç6•üÿ±~X"H€èn
+ ‚B¹ü29×$X,0X
+Ú Ñ ê£s5Ѭåà‹+›— 9Ä`™ãÅ`ž‹,öD.1?V{àÊàŠRç\uD98æY\‰c®}HÙ8媣`Jé++D91WTθš°ŒÒË TÔ  LÔ‹Š4ðZåªBÃ2t,OãQ ÔxŒsÆq‰±FãzX¢Æ#F,Ï%£FL×]w÷Ûí—×cy>|ðÛa0IwÅ÷_†ƒüxÂ[‡ÿîŠG’þÅ˃ŸßÝuoœ*=[ í,¸ö~ºªÛk[ça=xì.Bú±àæØ\ìPƒ–.Î$[q¹¸¡==·aMJ&³»XéÔœ.K™\¬@ž› É貘ÇUùÑv‡]¶cqeSÙàÕ›eN¤ܯjؤ½s’  ®|²¡ÅϼJé\Í Xd°˜ÎEùØÍÉÜZ9ƹfÂ"‚­¨\Õ FÈ1Å’È•Ï…EÛи'’ –Y¹0p7ñú¥uZj+Cg‘¼c,Òé« …«rÎX•½ÅÍÁ‘jÄ¥{.B J<—°ÒlKÃ7D=Ì:¸ÎåÛÁÉ %i=•œkˆàBf½ .Ú`Ø`¹Š¹2O”,Á W­Ç’ó ìÍПR4q‡ãÚa…„–¤8®
+¢6(®–KH·Kh%ºÖRÕÓbQ®•–ër›®Îa‡Àà ¯Çé¹ £^ >ÜÌÑ–œáµCrí´\Mçù
+.A5C#WªçÚŸòáªÕr[¬àª©fhäŠõ\盽§5Rì•K,×êZ[Éu6Ž;êÊ12BªWNZ,×:{—'%WG¤(³ÔgO@• ž«x j®Š5K5†ˆëWC6Ãq®ZÂp ÖbI…Ó5 ÊFÒâ¹Ä»Y¨¹®&EZ,ip-â\9%pžê¹*ÖšNè(;
+\n£á¬5;c+\ŒôÅq®æ"
+®K‰kvª‰@“¤¥pµ¥×qgéB™ÐdcMãº(½Ž«àôWòD@“ë¢ô:®Œ3¾êÙwCÅuQz WÍÛÃÈ0’ ¤h#!r]”^ÃUðûMÏ%s Æð2r•^Õñ6g
+ÌÃ’lì¨\g¥Ws5Ì]Œ
+㘴¶ÑR¹ÎJ¯æ½ØER2WÁ^f®“Ò«¹rfæŽÀ8f DQ)ëdÝ…~^¹e¢IàkSYŽ­\§Öç*®³Ý FÈ‘#š ”(ªT?º²k ±ÌukDŸ+?krN7Ä="à
+vt6מÍ•4™¿peM/\œ¥ùp‰'óg®úÃÉ<–k2ßÎ-‡Í‹ó·•Ü:\[æ»uîªßFwÒ²ßmµwpœOm›.Ò,²
+£®[‘"K~­lâŽÃÕ¿(—óM
+-áðì8—Ðpz®”ÃÕ¿¨7RNf™
+E2þW=/WÁá’.ªn»Ê·EÞÙ¹j—t‘趲¾Æ6ÓJ\‚Ã%_”ßö¿ž#Wé“«{œÍ%_T ö+Càª8\òE½H0?qÙL—Q\Ù8WÃá’/’gÆé>®Îq><Wï"©ÃÊbB®ƒ¾‰‡K¾HN#‚—qy#¶äÊ£îíZðS¹zýèN«i¸®oÐ4Qp¸ú=w–"p…S^Nù‡?ßÞ?oäZJ¬èžó2Ûâr{Ï\ù'×'׸V”+]&WöÉõÉõÉõÉõÉ…ãZr}rùçŠì¹Þf>ès¥”ºór=ŸÞa?bšK©;3×Cÿøšü²¡ÎæuÕ»êªHqPJÛ:k®|¾!çéw·§†u\y1Ô×áŒF~™WsÛìø9QeäÚÉÔ—Pì(É/4¬®KüuòÈȵ™‚ TSë¼;Àz齺 ®u+ M?\™j« ºã “Òdúu\©l)^¸š÷§»nÝî«ko»õʺ ®Dºµ®êºQ(¾unÚ‘À[v…².Ÿ«öÉUtv¬÷7ïÒqY·äsM]Õ2¿ú®òöNå“+ª›v^Ȭ©K㊯ۧoƒ8ñÆ•u—áòžÒߌ§©KãJ®£0qÙÄóÒŸá¦×VËIÚªº$®Õõq¼=™t®êöÓ5ä$mU]WÚ}tk;.CúáõÏÍÝ[ÙtĪ윑Ò×%qÏ]ÓÆ+W¢ûѧ“T<¶ÔçÊ/ƒô8„wÞ¸2]:ÎeO¹éÀd#©;x®ýåóŽŸ|ðÆuŽš¸1‘†‘¶.‰«¸ÈÐÑÒKo\—½Îûƒ:À:eR¼Ë¹¶.‰ nÙ¯qëë¢Z{þ⇬ã‹õu•\ÃÄÎEu¯G÷å«“Òkmñîm®¾X_—ÂU_ `ü`;®±}½n=é½ÄÕ0‹V[—ÂÕ\~<ºeK®±}ØByŽCš—ƺ.]_ˆ¾ç²Ú_ÚÌÒ|Õ¢¯Kàj/Žùô[\½,Ú¸ôdŠ¨Ká:;æ“[öÉÕyÑ–L¡ú’9]]
+×Ù1×'ï“ëý ì —J•l:ZÇuvÌõ©¾—1ïë9Sš\ùPÔuñ~ùäËãÿc—‹|¶—‡á¤¦ÒLrTu \ÕÉOnÙ?×­µ«^(u@Õ%póÉ-OÁõ.
+‰vÐŒÕ%póÉ-[raß-Z‹ÁX]—x÷b멸n‰×f®^]×Ù1ŸEr”Ë&ûø*™ìŠµ\cu)\o&˜4·õS/\²{ãb§®.…ëM2âúì-¹JÜú¡k¬.…ëØÓUt}Q7®D²­x”KW—Âçð±‰ËæÜM.ý9Õù±º®ê|²&±çÚO¿6ÝyÈJË5V—ÂUßœº7®ªÛŸûÞmz«Kájn³Q.›sˆçkãï·M­–k¬®!ž—Þ¶%ä]A ®qö¶ ©Ÿ,ŽÕ¥puÞ ë«™«õ¹Fê’¸r—Õ¹ìf¤jŸk¤.ƒ«´çZãÒmú¡ÿ VÑ×%qƒßxàjô
+3àj¸ëQ2\ŸÊ(—Õû7䵘_ÚQ.}]—îý¬n¹:}l \Úº$®úêÓǹ¬²Þè!Ón”ØÔµ*–ïí¹Ê×ëMóU|–ïY
+¶X¾ë“kz.óë–—ÉUüÓr•‹ä2/pÉe3aþäšžËjb¹h®õ"¹Ä?-WºH.ólõA¹’ere0ðµü>ŽO®¸ö0ðÅ}Ïn‘\ðA¹ª Z~¯Ù\Åô²×å÷ÐÍULMÞá¾70¸@ªrÃ\ e’ºÒò{9ç*Š+[\Àa
+%°ß{ZÀ‘›ì ù=Å¡­´e&=@~¯thG„â‚¥Âèo‘ßoXÀQã¸÷}ô•±
+-V裥ÈaÜ媗#ˆ(Â-ôDˆ *âƒ-ôéBäpÝåB} Â(»‚-ôÇ¥€VÏP„#C̾:\ÕB")£œ· Å }ºÙHd.„Ð'‹TæB,ÝÄ‹u+_†pd8
+h)‚¸xí\Åú,(ÖCéÑg­¡% ÇÜ ýŽhIÂ1ó
+œéöÔ1GNÀY™8樼A:©Ôï˜gB©œÇ ÜÏ™.H,8Ž¸Ãtºc wàÊêd†kÍÁ…Tú‰:,c=càÔ4Æ €?R§é°ŒÖÛNÓaÓ™_‚& :C¢Åqí±`»0ºk…äªé0‘qƒph­ ±÷UKó„ªY.´v†èuâ,øV­!zÕzìÓÝ ¹ð†èQ:j ›ÖÖãrfWŠØ ¬OKüñÍPË%¢¹-±±’.h­ Ñ“&æV>¬­'BõÊXZ?Þ¹±´°öõ^†˜È,e \<1÷3–½í#×½{&ܸ¥rU¤s©ÍJÉ\"š Ldö
+\EiW°/)áøf08Ô&[õ cœFwžÿf<[l2êR6W›qÀúßtŽ*OôÛø\‹+Š¿S;‹añcq€‰‹ÙaQt_zî¬ñ¹‘‘«ˆ¸ÿ¥ó/9k·6\"â—{”56¼OßYqYtØqœ=šÌñ…Ie˜>˜¹l:ìŒöªÿ짌ý¹K.ê4Li¯ƒ~/ÛÌæqµ¶\Mä¦ÜÝo·Û/¯oåe»}È,?mcÍå¢ÃœSþ#†KȵqÀe'‰³tŽ+¼Û9á
+®ÃÌK_8.±´îBrvm¦(ˆ=$W›…ÄupÇU/«»Ð\¬•O_ºäj‚áÚ´.¹‚ÑzÜöžKdKÑxw gÑ q!Øý^
+—XŒÒ¸°DôÖ!‰kvKÄgиš…X!•kæø—°MäšÕ)¹/T®9½3eû‰Ê5c`OJš#sÍ6Ähi/t®™†1±ŒÁ5Ï#îí2¸fbÔ,%=pÍ`sM®ôT9×ÄÚÁÈ
+·¶øhß"7\o¶è®Ë’CW+Âé,—\ŽºìþІÆÕŠ'küâª-.¹ø©ÕªGw-qËe‘^HÝž“‹Kæ–ʇìþ‹ë6øà"æÅÇ[ï¹ðÃõV^qÙññýw/·÷Æuê5ƒAÞm_}ÝÛ'ש۞¶wê~z|õy_ß\çŽ;žpØ>ÜËývûøúêý–“pÍPàÿ
+ä
+endobj
+1706 0 obj
+6288
+endobj
+1707 0 obj<</Type/Page/Parent 1703 0 R/Contents 1708 0 R/Resources<</ProcSet[/PDF/Text]>>>>endobj
+1708 0 obj<</Length 1709 0 R/Filter/FlateDecode>>stream
+xÚ+ä2T0
+ár á
+ä
+endobj
+1709 0 obj
+31
+endobj
+1710 0 obj<</Type/Page/Parent 1703 0 R/Contents 1711 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 4 0 R/F8 7 0 R/F9 8 0 R/Fc 10 0 R>>>>/Annots 13 0 R>>endobj
+1711 0 obj<</Length 1712 0 R/Filter/FlateDecode>>stream
+xÚUÉnÛ0½û+99@¥JòÞ[³Â@5v‚|¡%ÊfB‘*IÙp¿¾3”/I€Â€`R³¼7ófô§C„¿F ô†•«yçóÝ’æôã~8‚á¨ó¼›^°Œ_ΟѢqLAcô’Q˜Õ|-,X]¸-3*£W†•%7J¦j&éj#rþŽ‘P+ªÐ¦dNhøܚõ.K<>þ˜þ¦äq¯I–¡¹ÍvÖñÝ‹ëÇtv±¸„'LIAâ0ȈGý“(ô”Z·Ÿn6‚oO©‘Ý°±›Ÿ¢€ó¼¯œTÚ8¶”Īµ‘lÇ'C¾‹d0]qÃüKëئÖÌ‚Ûy'
+#l =bz<ÜÃx€Œ“Q¡„8š ®æ$aF=‹ðØÃ#‚]r® ç.1MË|õ8†Äáø(ì;öGh?‰ÂÞŒS³Úo±Ý2»ƒÙ¾ñ©Ñy9Ïú൞]pš*[jDZ°Ö1•3“
+kµ¬_…¤lz³á*×(7´¦¸ðª˜Ú¢.B •ºF"k;ùD~pÅÍ —|6/)Gm:nhlØêp¯C7¡¦d§¸;ȉ:i‰ÞÎï‚iš¢DYc¾dG†8`*¶"{OžõÒÓ€?5¯1)  ÏàáòiPb7Œ“BÝoéÍ'x¸»ŽãÑdqù f(o|ÿ[ËV®¤Î^ÐlöýŠÞR†¯U5ÃKÄ\µP13vˆI«ÏRÚº"£²¶Â­Ápl!ŠZeÔ &…Û½[–ç°,[m^†ˆiiôÖeB‘jëf™•;pâÖ_Oì"Mop¸,?‚öﱚ`E¸x0¯ÌU•kj•óf,ßGŒì±ë™¬›ÙÎjët)þbîM»^t÷?á~}€¾èfµ1\9¹ƒ¨.ÞZ ÂA´¸<ÃKE`
+DIm*n‘‡iŠ"aP"5Å™سRZG%kKjß®»~„S‹áotV—ìÞñg9îfaî,}Xäh¡ÍŠ)_L­½š -¥Þúñä^öK“%k³$cšÇ}¢E2Á)Œn Á› {Šx¼ÿú4Ázý†ßѪ?ŽÂ 2ÂÛ¸]¿:ÿ
+endobj
+1712 0 obj
+851
+endobj
+1713 0 obj<</Type/Page/Parent 1703 0 R/Contents 1714 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 4 0 R/F8 7 0 R/Fc 10 0 R>>>>>>endobj
+1714 0 obj<</Length 1715 0 R/Filter/FlateDecode>>stream
+xÚ•’±nà @w¾âÆt05˜ÚÎh;µÔ¡’[Sué‚Nic¬Üþ~%•²t@0Àݽ:ø" r Š2N=“V’Û^c 'à5åPÖ‚Ö ÷›7^Vp#?°@\
+62ﺗa„fx8§ÿø2V â¸@øÕÛ`Ýz{ ÆŸ’xqÅÞ:ÀÎÛïTÏÝ•§UúÓ¸}š AA·¸SP.¤‘-’cð««7ih‡h¿:,ž|Î՗ܶ¢q)¨ˆðî¹ée¼a|£q™Âò¶¼šgl<*·ªctdŒ—U< !¶4þÜÞLJ›½—ä‰ü…Ü‘3endstream
+endobj
+1715 0 obj
+248
+endobj
+1716 0 obj<</Type/Page/Parent 1703 0 R/Contents 1717 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 4 0 R/F6 6 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+1717 0 obj<</Length 1718 0 R/Filter/FlateDecode>>stream
+xÚUMÛ6½ûWÌqð*þŠw÷Ø ÚC².Ö›ô°ZÙìR¤JRrüïû†’?"-P0,™3ïÍ›7ÿGSšà3¥ûÍ—”W£ëÑ»O4›ÐºÄ÷2›Óò~AëâfJw´òÚFm·ô|‘+zjÙ·š÷·ë¿¶ éTÂ»ùì>›Ièz§å;UGöT{×ê‚)K®'WÒÎí)î˜>ºªr–^¾üþçÞÞù·uh4½´ Ý-&ÙC‡Ãç•wÃÕÔpx¶ÌR=Ÿœ§+¨;²ì܉ú˜¥þY–Ú©@µQÛ†‹Äôuö~™Ñ‹5úé³Î½ ®Œò–¾i[¸}H¿ùYåOÏã®<d‘Ä 5wJYG!*[(_°/UÎ:´X¼*å]S×Î÷4ÓéÑ/o›„ Î4Q; ¦ž[íš`¤Z¥BIc93@ÿÀþ ã”-Ž’=kÑq@ã<'€Ê…˜r+Ã6f}²£¾hFàëàž4mùD›^o"ä¬9¼ÞJ½+ä~ν®ãÕ!!·õªÞé<¼ÞŽÓó^ÇÝ ˜à*¦Ü©ïºÓã@9·mU€¿;1QT6ÆWvËbÄž$.µIbW*BÜßÜžaÙ1ÒäªAy¬ò!n«¼†’IDJv5{u!!ù©ÐeÉ^NtB(ÈJ¼EÉc*
+r Äë‚0…Èæ¹b8A°uÞ˜˜Ñz§"¤ª6èI§e‚6ºÒ/Z©’L̼ "÷¨"EWíQü“#Eo•ÄƒZÆD
+=Ø‚55õ¥•S
+„k–?1Õ¿-ˆ±XçãË
+†Z±dôV*K1VEþӞѓ+ ì@‚aÛz‹¡½m0GðÕ¥$áÒw? [æ"H³*c¼h…Äh±ŸFÆÊQ^œ*ç­¨Þ6pD
+endobj
+1718 0 obj
+847
+endobj
+1719 0 obj<</Type/Page/Parent 1703 0 R/Contents 1720 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 4 0 R/F6 6 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+1720 0 obj<</Length 1721 0 R/Filter/FlateDecode>>stream
+xÚWÁnÛ8½ç+昱§“=6M³M‘b½µŠ½äBK”Íš"U’ŠÖýú}CJr¬&Å¢Ac™äpæ½7o”'s:Ç¿9½[ðOQŸÜä'oïþ ùåVWWÙ‚òò4ßJÊe±5VÛÍþMþÛ.i>OÛf‹ì’·}ø¶\‘ò´^–Ô6Ö0$ké6ÊlèÞéŒ äƒ0¥p%Bkì ?..2·/ ¶°úŒ¬£ûå2ãÿh+_JÃIàêù»ì‚¯–õÚ‰±Ö{*íOi<ÙŠŽ%²(ÇÏ^º'üª…i+Q„ÖIçÏâŽNiØäÛ¦±.¤`œ›‘ÿú¢
+g½­Âäæ”)m‡ëéDLÜï}uÖïÀáäKY)#=‰M_%U¨²jG‘D6øDE„/.Òw»öGõØ&(‹rµÚIªe©Ä$A¯~Ê3rÒ[ÝòÖT¬·|cØfôÀAà%憜€ g]€DÀÒò’¶ÌÙž)± ãuÁN®îsô´mMédéù\ØÚÖ#…ÈP­4ãщ}FßL¬Á"¦{žÃÖvw¥d„Fæ=C8XÒûÉ­…5Á±nD‹P
+ »IZ£“<¨DF·j#}x)Tþ°z&Ñ
+Ä“PZ¬µBTµìÄ,ÄŽ³Õä^®6Kîx0ÑÙåyvÍ«ŸÑ¹¯ZçGÚ+4Cªúv]«À.tä Xé]SPŠ¶è£ÂŽãŸfô9Ú²’P©äj‚Z£~´°¶¶^³)á’ñÌ
+
+y*gôO Opl
+endobj
+1721 0 obj
+1365
+endobj
+1722 0 obj<</Type/Page/Parent 1703 0 R/Contents 1723 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 4 0 R/F6 6 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+1723 0 obj<</Length 1724 0 R/Filter/FlateDecode>>stream
+xÚ…UÁrÓ0½ç+–\3‰›¤!ioPJ` Ã¥U–cQY2’\7Ï®$»© ÃäXÞÝ·ûÞ[å÷hsü,`³„Ó5ðjt±\Ãb»߬7›l »|²µR{aáÒÊ{aÝ«Ý/Œ[ÁbãfËu¶:ŽËcH ï~l¯í¤ó`
+0ZÐWe¬€B*Oa®\’ƒ7À ŽE²˜*5WM.¾p¬ª•èÞS½8Í–!¡0>ˆV ïg[Æï˜Íá3sÂ~˜ÎáR¸;úª¸ ~–«úRºÔ7äñ´ñ°ZXæ±ãÚËŠ)0¯?€&Ì\…°B÷…qÂ\(7éc½\TÈ‚åJÓÂÁ4À™†ÖJ<Á' ¦Õ}zG!õŒ[J@ûRTøy#²²(Ì£€³Õ<;£¸¯Â·ÆÞI½ÿŸv‘+æ6m4#@ŽÃ»ƒóÇP9ÖxS1/ñXÀ•x–C+} ãm
+íóu„Ï`‡,FÀ
+¦uà ßÔÁ$ Ï%O¼zl\R­ëË â¤$*ãjc2Žö#¡nÐ
+¡Ba;°à]1ñ€;ä"ÍÔ±£$Êœ¨\ÕàN(Á}èoiÅÏÑÛRò²£ 9&Vo&"Ûg0Nùoˆ¼Å˜È|r´g7¯R¹ÎEag™r¤š¹—´¸Ñqë三Ä%–·"yì©!'Sˆ‘˸Q4‹Ä½!Úë#‡ž`3IŸ’ †$ÛÁߌV5ÊKºT"jƒÈ…ô%ëòàÈêÒ]'Óà(ñî¥)8Ä`£_z°B‘Ñ5äÕÛœ
+…ªxšÁ…ଷw`“`Ó¤½·âøÓþ& # П H›sL$ÎDÊc}{
+endobj
+1724 0 obj
+824
+endobj
+1725 0 obj<</Type/Page/Parent 1703 0 R/Contents 1726 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F8 7 0 R>>>>>>endobj
+1726 0 obj<</Length 1727 0 R/Filter/FlateDecode>>stream
+xÚ-‹Á‚0DïûsÔC•ES¼ªÈͤÊúƒ±WÔß—3s™7ybdc ›zŽ´šW+0CpnQää2)ëJ`°=¹uß ¯Nû«úƒ>±÷ÝËߧr#“<óm"K¶³¿í´í† (µ}ZzwBú©ð%\endstream
+endobj
+1727 0 obj
+127
+endobj
+1728 0 obj<</Type/Page/Parent 1703 0 R/Contents 1729 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 4 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+1729 0 obj<</Length 1730 0 R/Filter/FlateDecode>>stream
+xÚu‘±nÂ0E÷|Å qã$$d ´TH¨p7Ç1ÄUp¨mú÷uš”Vª'ëwÏ{öG@ùC‘ÇH2ˆS°dÁú@М̑å)X=‚5«·íåv3cïMAi†&qNâg² ?;iPK+Œª¤…»%œÏ­Ü©Nckº£á§“4í\H짯™ìgdP y¯Š¦Y ’1íEU†›Ï˜Žà<"ßÃ?J'…Súø;ÀØ¥Q^\':}øçÉè°Ì’[%°“檄´w--ÚK-±Ví@ý©=Kwó—W®Z^µÒo®ú}-¸®±j¹µ?ñ‹ña}D‘ìÞ¤‹ˆò弿?±à5øM
+‚Îendstream
+endobj
+1730 0 obj
+282
+endobj
+1731 0 obj<</Type/Page/Parent 1703 0 R/Contents 1732 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F8 7 0 R/F9 8 0 R>>>>>>endobj
+1732 0 obj<</Length 1733 0 R/Filter/FlateDecode>>stream
+xÚu‘ÁRƒ0E÷ùŠ·ÔH(RºT*Š3ŽñRx­8!±!èïK"Vq’UîɽwÞ;
+Áx(¬b{ëŽ\3r‘m€FÀö£'Ô5g…n¥iå²V`Î^I
+õ{[ã5:¹å²Cƒ‹}R%%Ö®’QÀêJ—x°7£Šfî”÷ý`!þÍôÛÉļàɨ2Ü G$›ßi[nø )‘/"‹CÊ‹ùŒ ÔB4Œ!¦‘ŸXl[^e <HŸ‹
+*µ7\ã¸'uмëP÷ðÀåÀ…µðìOoöcb_¢p5V –÷ÃÈù•À¥Äendstream
+endobj
+1733 0 obj
+277
+endobj
+1734 0 obj<</Type/Page/Parent 1703 0 R/Contents 1735 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F8 7 0 R/F9 8 0 R>>>>>>endobj
+1735 0 obj<</Length 1736 0 R/Filter/FlateDecode>>stream
+xÚ…‘_Oƒ0Åßù÷Q`‘?8G²dF„ú*½c(¬-êÇ—â’e84}jίçÜž{´¸ã!zp@ÙZÔZ¥1è~T‚ˆ8P~³e3p„´nPÝÒwËûÞu|£­%2]‹
+˜€m–AŽÇ•žQ ç†I´–õÛ ¹(ø¿&9²KHõP8£ÒZüµëºÞ
+”u‰gò´Ÿóóý¬Ò1ñ܉zÌ“”‚ Ó$E·×ŸL"d²«$k[”
+ž˜Xc lóÎ=÷ª½¹N “›û†Z/Ö7ϬNendstream
+endobj
+1736 0 obj
+277
+endobj
+1737 0 obj<</Type/Page/Parent 1703 0 R/Contents 1738 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F8 7 0 R/F9 8 0 R>>>>>>endobj
+1738 0 obj<</Length 1739 0 R/Filter/FlateDecode>>stream
+xÚ’ÑNƒ0…ïyŠs©°vL6/•Åd‹(ø
+endobj
+1739 0 obj
+290
+endobj
+1740 0 obj<</Type/Page/Parent 1703 0 R/Contents 1741 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 4 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+1741 0 obj<</Length 1742 0 R/Filter/FlateDecode>>stream
+xÚ}QOƒ0…ßùçQ@JØ£狉3cñá5£ÅFöïmÇ¢.KL“¦¹ùzî9çËcíaH#ðUï=ÞÝz…(DÑ€³,H¤1Šú†ÃÇ»£-Öb?’6·Å§¥c0æhÁ}¥Aä~0¨ºr°,j2•dЩ£Âlµ%±'w9¨Qùn³ åXê”CøqdNóõ@ú hþâ3p'›[ª&«|D®¤5éröuiÕòQ²ð;cƒ ”5žµšs%̹ åÒP?(]ê£KO'îÏÚMÙªJMÒ5tå?aK'¹ê{»ÌÒòºz’ãyiöë.û¯ò8m¡#sƒ§Â{ó¾-—€âendstream
+endobj
+1742 0 obj
+278
+endobj
+1743 0 obj<</Type/Page/Parent 1703 0 R/Contents 1744 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F8 7 0 R/F9 8 0 R>>>>>>endobj
+1744 0 obj<</Length 1745 0 R/Filter/FlateDecode>>stream
+xÚuËAE÷ýwÉb˜2Xz ƺѤe¦›Òˆ¿§3$6Rµª{îIê"ñ{4ì®=)êYÔ„<¼“´Mµr_é»ó#m5+oœ­ÊSɵJ.J?ÜÐÞ ;[hë±VlÔ6××@ÿ06ÞØ#Ær:Afr¯¹4¶AŒ”¤ /?Xt3‰ýÕl‰¥;ø‡b»#«¢Ð|ÅTٛʃ"
+ͨ•Ä¡HI85ãF­ƒøÏC)æâãuI&endstream
+endobj
+1745 0 obj
+188
+endobj
+1746 0 obj<</Type/Page/Parent 1703 0 R/Contents 1747 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 4 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+1747 0 obj<</Length 1748 0 R/Filter/FlateDecode>>stream
+xÚ…‘=OÃ0E÷üŠ;R‰„|Ñ´#º¡béb9ncÔØ©í4Ÿç:À€Êæœûλö)ÊÒ—¡ÊQÌÁ»è¾ŽnÖKä)ê=òb™T˜W%êæªDŒw#TlTN<yÆÎêJ•È2ŸŠC,.ò*É}²n¥oYï#´|°VX´z„Ói¦
+ðXGÏÑ'Ü2¨yendstream
+endobj
+1748 0 obj
+326
+endobj
+1749 0 obj<</Type/Page/Parent 1703 0 R/Contents 1750 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F8 7 0 R/F9 8 0 R>>>>>>endobj
+1750 0 obj<</Length 1751 0 R/Filter/FlateDecode>>stream
+xÚ…=oÂ0†wÿŠw ƒS;¤I!1PÉM\ua±À  |À9~~ãÒN•Zùn¹{Þ{ä “㓘&¾v-[höTÌ cèøI2FÐû`w=»Ò¸ÁRiÍ~=¶¥m°Lô‰ ðgÆ¿1UßmãþÄò¦wö‡ø2§3O¾ÍïTuw„ÁZq•o°¤úÃÒƒÏ ¥çe” "Ì|bYÎ ŽüMU¨úÃp3d¡¨?’i[K/¦»šÆŸà>ÉÓHø Œýh:šSˆÿ~½Òì•}’ö[<endstream
+endobj
+1751 0 obj
+207
+endobj
+1752 0 obj<</Type/Page/Parent 1703 0 R/Contents 1753 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 4 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+1753 0 obj<</Length 1754 0 R/Filter/FlateDecode>>stream
+xÚu‘MSÂ0E÷ýw©‹Ö~Aa)Œ¸¥Œëˆ¶IMR˜þ{_hѧ‹Î´'÷¼ûò$ˆéIP¤È¦àM°(ƒ‡ÕiŒrt–G)¦EŽ²º› Ä»‘NªŒ
+UÙûòƒðIâñpàÃ,-èEGÊ£´àGÖ:aP ËÜ ‹£>Ãiœ)L€a7„a¯ –Ûõ&ú‰×MÓ)É•4‚»ºÇYº#Z#¥Z0Uù)b„I6XY]“`Py9ìe}9Crës õ˜êáúV@ïɪ©¤Vp†)Û2#™£¡îIáë’2£™W¾â$ÅùÈG`G—åmï¨n¥VVVÂ0³?âÓéÀoíuæg£»Öþ βhzY±hZm˜é±’µ°ã*®Ú5;<r®;åïíæï›pFŠ1{ö;Ä,²ÿ»ñ¼È©vì‘dâ?<•Ákð ‚+°(endstream
+endobj
+1754 0 obj
+344
+endobj
+1755 0 obj<</Type/Page/Parent 1703 0 R/Contents 1756 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F8 7 0 R/F9 8 0 R>>>>>>endobj
+1756 0 obj<</Length 1757 0 R/Filter/FlateDecode>>stream
+xÚ}ÁNÃ0 †ïy
+áÒdk:ŽÛظ€Thg³fU qÀË@¼= Eˆ²Oþ¿ÿ“ü&”ã(˜™¼û VV\l/AÕ`cbêº0`»³u ©“7ž,¹?GéxnŸ'|>á²*‹ù„¿~µ#ǘ|¤_îG+*tæ6ôî9R–Á²Ç§Á}[ÿ0쓧Z74‘¬pÿâ¨›Ì P*›•6 U5õ®î—[ Ö»¦…6Ò²ƒ†cÏ‚ã#Ü"pÈ
+™›²Öe.*“O³J (ÿ~cÅø¨â\™endstream
+endobj
+1757 0 obj
+221
+endobj
+1758 0 obj<</Type/Page/Parent 1703 0 R/Contents 1759 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 4 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+1759 0 obj<</Length 1760 0 R/Filter/FlateDecode>>stream
+xÚu;Â0D{ŸbJ(lÇÄ¡‰"s
+æ#Áí±ù ÐV»ó´3»'Â@}1HŽ$ÅzOŠtÆ=p
+U"ér¤R@­>" ¶qÚº¦­vž`,PQÀ¢„˘Rm«úx4¶¨®¨«Æù¶®q(á¶ë÷ßi}6(LYYS`u{Ãe¾@?ŸÆO›˜ 6‘ q,ÄG/¦ìé?Q*ÿ+NóÿÚLÛÍEoÌ÷ÙëN2ÎãßïRøl4hL†ÁH‘9¹Iaüendstream
+endobj
+1760 0 obj
+207
+endobj
+1761 0 obj<</Type/Page/Parent 1703 0 R/Contents 1762 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F8 7 0 R/F9 8 0 R>>>>>>endobj
+1762 0 obj<</Length 1763 0 R/Filter/FlateDecode>>stream
+xÚmŽA‚0D÷=Å,uQèÓâRAv&ê-&F ¶5^_W&æÏj^æå?A,G(eÊed{Ãòv R0ÃB¤R™„¹®´nPÏSˆvŠamîL€KÊŠÄ:¢ó¿8o+% $ʯ¦év­G}Ö=úyˆoë´ŸoÞŽ£óG;½ì#)xZrUˆ4¤*U¢¬‚øûÐÁ°û
+endobj
+1763 0 obj
+157
+endobj
+1764 0 obj<</Type/Page/Parent 1703 0 R/Contents 1765 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 4 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+1765 0 obj<</Length 1766 0 R/Filter/FlateDecode>>stream
+xÚmŽA‚0E÷=Å_ê‚J¡¡²£‰; õ
+endobj
+1766 0 obj
+172
+endobj
+1767 0 obj<</Type/Page/Parent 1703 0 R/Contents 1768 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F8 7 0 R>>>>>>endobj
+1768 0 obj<</Length 1769 0 R/Filter/FlateDecode>>stream
+xÚ+ä2T0
+áÒw³P04TIS042S072PIÑp rt QÐUp VÎO+)O,JU(ÊO/JÌÍM-*VðMÌ+MÌÑ ÉâÒéÓ…j42
+endobj
+1769 0 obj
+126
+endobj
+1770 0 obj<</Type/Page/Parent 1703 0 R/Contents 1771 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 4 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+1771 0 obj<</Length 1772 0 R/Filter/FlateDecode>>stream
+xÚmŽA‚0E÷=Å_êlK\*‘ÄÆz
+endobj
+1772 0 obj
+167
+endobj
+1773 0 obj<</Type/Page/Parent 1703 0 R/Contents 1774 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 16 0 R>>endobj
+1774 0 obj<</Length 1775 0 R/Filter/FlateDecode>>stream
+xÚ••moÚ0Çßó)N4*Æy ê:©[Ë´Ûº6}‡Ty‰¡™òÀœd]÷éw¶C !ˆ, |Ÿ>ß Pü0˜¹ê¦ƒÁ`ºðy¬Ðâzq!ˆ†aµ)®¢èÛ¦Œól9\ŽFÁO£´râPb+åCÁ×ÂX)øµÑbÆMœ•Ê„#Œ’y‡ã0ÏŠÂ'.aœñTœ·ä°÷4Õ¿yRãêUéc®+NøF°ZùXÂx\OZŽ.¶>›™Ý]Éu•Š¬,ŒÂÆ”bîCà™ølEJÃ|F<ô€Ã×¢e¬×P–›`@ ÅÃyý¹ûöÕ¶ï #fùÄ‹ ¤€û1óæHeÔ–ããÌZŸ²]ã9ŠÁž34"‡
+ºFÓ h´àI€†|%¾›H˜žƒ{cju¦s™âÐ'ÜÁ¦ÇÿÎqí§ÕGáì:}™Õ‚üZ¥?„T„µ“KJÌäsRcs)ùË)ê™+õ¦Öê£Ô8¤s¶›ø6ÇZAä2oµèGÉlôÜ›R«;ósW`3×3!¾e%³º¼¶ù«Ú‹kjKgªxVE¾pr´f[u§šÖÛ#Ío 0Eœ¬^mµb#Âx‹¨·N†›?<Ý$G›æ›8 “*ðN§÷ußÚÚ !­‘é®E˜p æÁñÁxÚê{ª-îžF_œ4«ªòqUÇOÁSÇfþ$æìZz¹VwŒËvÁ|í«šd?5P=„q“`cŸ³¸Œyÿíµ·ÆnàèE˼5©ç–ÃVÓ_Žh¦Âë²Ugk8HS ­ g©ˆb~vg‰(±îð­yOÁÛý˧·[)Š<©Ôå{Fi´‰Oøž.æuZ3Ë×vt‘Ü]-˜ÀLJÛ{¸ÏWå³J­[™¯%OS! ø³Š'ÊÁDÍ›x–î#–¥†ljã H–¦Ø÷Á?x@%endstream
+endobj
+1775 0 obj
+714
+endobj
+1776 0 obj<</Type/Page/Parent 1703 0 R/Contents 1777 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F0 3 0 R/F4 4 0 R/F8 7 0 R/F9 8 0 R>>>>/Annots 20 0 R>>endobj
+1777 0 obj<</Length 1778 0 R/Filter/FlateDecode>>stream
+xÚ•’Ínƒ0„ï~Š9&R!^C€ÓrªJƒ{ËÅJ¨„Ôõõk¡M£V–V^Û³ßxíFàvb ¶d·’ÍÒ(„ÜÙ(!_@¾Nr­±,êj*ßÙƒdÜçVè¹°^¹Ñ<ð” VÕerWt$0{ÌR;§3íQ‹ÙžŽuj´~:6oÕ¡ÞL6Ó_‰D±LAÑý`òö̳Ş{‡¹f²µ¶ÒMçìª1!¾¯_"ihÍŒïñ?c™2õ¸i—(I/ò¼¥t{¿^¦î^²yµk>•ÑÈLµ7ª,µ©ñ¨'U¸ª‰^,øøW„±³Ü.‹ ïÍ3û{ùendstream
+endobj
+1778 0 obj
+265
+endobj
+1779 0 obj<</Type/Page/Parent 1703 0 R/Contents 1780 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 24 0 R>>endobj
+1780 0 obj<</Length 1781 0 R/Filter/FlateDecode>>stream
+xÚTMÚ0½ó+FÛ TÚ&D­*Ñ…­ºjU
+ìm/&È*q¨hÛß±@P‚=Ï3oÞÌøw‡Á/…ah~IÞù²ì|xˆF°Ü %Œ"/„庛T{}Ïe"²ÇbõÜ}îõ–/é;ä žoOšo…³ˆk#£ÎM*KcÂJ¼ÑÇI!u ÉŽ+x¿ºì7àðæƒîàõ>žÑ]ıÚV¹¥vˆ
+endobj
+1781 0 obj
+616
+endobj
+1782 0 obj<</Type/Page/Parent 1703 0 R/Contents 1783 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 34 0 R>>endobj
+1783 0 obj<</Length 1784 0 R/Filter/FlateDecode>>stream
+xÚ•Vmo›:þž_q¤}I«…á%¨Û¤ÝµÝ*åv¹)ýV)rÁ$\aÆ,Û¿ß± ¤IÉÚ*}ŸóœWûÇÈ-ðm˜zå£ÂÑÕ]
+‹ïá)ŽSŸËQ{9â–*Ͷ|÷9ÊÔ e1lS±Qôºx>Û¶Ö¥2~xšÏõV[=c(‹” Ê!M€ŒzR xg9Hé\ïøhýîúoêù:K*jΪ}†ryºõt­T%¶5UŘ3\D×ĆvaIHšU|gê[”¤”rŽÚ"ÂàEfºÆˆ¾üÆu–¥lý&$Vã&.`Š£Á†‚ [dÚË<_ÃŽ©„ÎtÓÊq:'•¸•ÚY}Ru¶sÉèasèµ½.Ž÷Ã.hqA+  Ú`¯ªF¦äÅÏ4F€ŒÜë–ÂBYÇÀý–âvJO_ ¬6±P$¨=i
+¸UÓL&¸GMYUÀõeh³^¿ä©˜‹½\*2”cgæRT’ªÚ<>0Jj<ÃD5IŒÒ˜Æýa¼ýEò2ë½ߥ,Êê˜ÂKcói°@,ÏÁÏŽñ׋Þ‘h·ä+q¼š»N]¶×æݩªM7¦ÕbÈ´ëì*r•¶îgp)Å5>Np¤Ó9`[´‹!ÎT—¨z :ß½®ÞúmÑaô¸hAm Ã8عº„¯T¨âŠiBêL@ë!\^Øè$¡×{rõ$Ë1°ò~V‹^ï5~¶{ÂÍÑȦÒ?Æʳ‰:üÚ—/œA±ÅÝîuè_ZÁI_W•’eÚhE-†}q<]L˜ºýKV“OÍ£(ñrÕÁ«4F¶èÕj±¼Ww÷óÛæY7k˜e{à©×“ŠËÏw!LàËÓâ‹Dl Ný/Öœä9åüKXM2ÅCžœø¶98)_^Û
+c»MØþý€Ñôëendstream
+endobj
+1784 0 obj
+990
+endobj
+1785 0 obj<</Type/Page/Parent 1703 0 R/Contents 1786 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F0 3 0 R/F4 4 0 R/F8 7 0 R/F9 8 0 R>>>>/Annots 44 0 R>>endobj
+1786 0 obj<</Length 1787 0 R/Filter/FlateDecode>>stream
+xÚ­Tëš@ÿî_1å“^wy)×Ü5ÔW/ñ<«øÍäBu}4
+–\ÿüκ,šž/­``†ßcÆùU£@ðKÁrÅoy¨} j­‚5>p;mÄ`UOدŒ¥\Ì/Œøhä—¯»
+BΓ݌³îZ"·ÔˆA€8Pq˜E
+endobj
+1787 0 obj
+726
+endobj
+1788 0 obj<</Type/Page/Parent 1703 0 R/Contents 1789 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 55 0 R>>endobj
+1789 0 obj<</Length 1790 0 R/Filter/FlateDecode>>stream
+xÚ•VÙNãH}ÏW\i^"Æv¼ÄÊLKiHÓ‘hÈ8æ )*ìJâ‘—t¹ÜÌü}ßZìPpùžª:ç®ù9°ÀÄ_ |ÆÄùàk4¸þ€åC´A‹çû†Q2Œë}u[†ôgM+þ<|¾¸ˆþQHG!G®i8ùT‘-UVm´-uLºß¯9\
+3¾µLcræðç%þ_@áèGÇÔÎ~l\‡xG˜ØP•5‹éóÅôŽñXQ±mÓ‚W
+á‚e Dà.êñ•Ø$0V`>ž€¯oi³tÏÓ²–y40 =Ý~„wàLíšžaãÝ®€mâ#£°:¶Y)´íŒqç-Cæhz®àà¶`!ü'‰Éã%±ïQ´ž(h,È/¡¢ìeF?Ogâæ§y*ôžŠo©øè°P\,— -N>bæ¹xÚ§™It'3W§i“ç¼×Ø  9…MÉ`ù¸Š>b‰‡xŸg)Ñgã|HÔq0Qn )¯Y¡Ó´ÉQsžÊQåÐjùO¥G97ªÐǧûûã†n€ïh‹ I³Ê€ÇB>ÕHIX)cxZL
+xΨ‹^þÃu–¥ÅöO,-0v'¨,‡ h™Ry‚g[80ÐÀ°›®qO*>š–tâR¹·•dtûIÁ¾½¯õcG <8,)id–ˆœh]GÐ+{VþJ¯½Íq,Áæ•.EXà–¬*›v6VzwxL†`,ë—<å=|!y e˜’¹0íIU½–,R#ªàiLdÍ“
+
+Jšt»cþ/É÷Ygÿ#-â¬N(ü)|bì¾ôÚ6U Ç6úQ-:-Ð:¼j
+œ©nírBL{/·Ñ©sÀ¤qô¢çr kªõ:#Ŷ›Á¥0×8ë> €½ÐC81šE‰ÿfPöŠoÇÞôdì©¡ØbTá7 &„†aœ¼¹¾„;ÊeB%tCêŒC£.¯Oîh-A§zår[4û,l­¶^uê×;ì÷‚{¼çV±é®ð¡7’»ßʹa”pŠYÐ×£’{'§1|(gÈl²Lo‘‹~1þR„1y ¯ÿS‚f5ú¢Œr”É2^§ ²EUë»y´^†‹‡h®gQ.¾>EóÕô¼Ä£Ãô£8JH·¦ïSb–$Òm)£ØH8géKͱ )v×¼#“Þsä´íÏyÛmSo[ájrœñÒs#ãJ*fwëÇå<œE‹Ç‡Ã«›ï³p5®TûšèþlÙx8^e<nÃÙ·Fpó´\ÁªÜðW‚mÉÊ-#yNY?HQ“LFEìù¶Ù9_è”vÛ×~ù{ðAÙäßendstream
+endobj
+1790 0 obj
+1003
+endobj
+1791 0 obj<</Type/Page/Parent 1703 0 R/Contents 1792 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F0 3 0 R/F4 4 0 R/F8 7 0 R/F9 8 0 R>>>>/Annots 63 0 R>>endobj
+1792 0 obj<</Length 1793 0 R/Filter/FlateDecode>>stream
+xÚ¥TÛn›@}ç+¦<áˆË.`.©ÒŠÆYr×ÆoHÅk슋³€òûÝ5—¤±ТYf朙=Ë€±ƒañ'J…¾ M¸àïØ˱Uü­ï/1,Kzø]•¤P¢}H RŠ2,6ó¹ ÿ0ö¤"–˜/˜/«)è:VM–Ðb S0­:•ÀšÃ~¡qÒpˆªc1³xœEùöÅê-5AR %Ì©
+c ‚ÁWî¥Ô™¶Z<Óáxô¶Û5+"‹‰’—Š¥ ³åòÙ÷¦ÏOËñÊógO‹·­¹·˜n¼éX>‚‚£ß×—,,+&JË¥mP_|ë _NÎð½ÛR¾uïA|›ÂÐÿÔ´YÍd, $T©èAìeWŸ¯Èpî5mŸe¦D£¤È+‘ Œ´;åPî 4lTU…;íKp̳‚Àðå«Ú9öey”¡«D<ÃÒ&.`³Vªb5êšð’"¿*DfÀ;'b¬³wm]•!ŸŒk˜!vÿãˆìÂ*alƒÁUDŒmÕî0uÃR‡×0Q‡i6˜-Å·ž°>’gsÒËM7õwý0ÓY7pkKù·MAh/']‡'×UÛ9±>ň«ì1Ï2u‡Ø—^’û……†lMÁÕÙÿ¦6nÓº0Ä=-rmÕíÀðq}]F;/¡áfÝØ ÆiA^ûzã´­ç#¨ó‰ÜJ£•7ñAÇÍr ë|W¾†”À’æ1 Ó”Ð~†Y&§ß3Ö-Plñ@Ýá[¦i
+endobj
+1793 0 obj
+592
+endobj
+1794 0 obj<</Type/Page/Parent 1703 0 R/Contents 1795 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 68 0 R>>endobj
+1795 0 obj<</Length 1796 0 R/Filter/FlateDecode>>stream
+xÚ”]o›0†ïó+Ž´›djÛ˜/eš”­M¯ºe ½‹T1p6>2ëúïgcHR )DBÄçåœÇ>çåψ
+endobj
+1796 0 obj
+622
+endobj
+1797 0 obj<</Type/Page/Parent 1703 0 R/Contents 1798 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 71 0 R>>endobj
+1798 0 obj<</Length 1799 0 R/Filter/FlateDecode>>stream
+xÚTÛnÚ@}ç+Fê IÊÆk°•6RîO•hpžšªÚ˜µÙʺ¶“VUÿ½3Þu
+Üö&;‡!js>züyðîËLendstream
+endobj
+1799 0 obj
+665
+endobj
+1800 0 obj<</Type/Page/Parent 1703 0 R/Contents 1801 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F0 3 0 R/F4 4 0 R/F8 7 0 R/F9 8 0 R>>>>/Annots 74 0 R>>endobj
+1801 0 obj<</Length 1802 0 R/Filter/FlateDecode>>stream
+xÚ}“QoÚ0…ßùG&Á4Ò8 ´i-mŸ&1Hßòâ%296³XÿýlLZ*É‘u¿œ{|}ò·GÚ‡ QÔ½»¬wû8I‘­meœ¦ÁYÙ/š~bfÎÖ´á&ïçƒAöÇ£‰G‡£0Hú¬é†ùjˆé©‘“ŽÚ ØR…/Ž±%“ZìeUæƒo}âØK-™i”оž€¶ÕØû˜a'+a˜‚‘0[†Ò+Û·6• ¦’"èTŸ3]¨jç ö,º†ÒšécÃŒîêòö¤BÁ©Ö²²ÒÐF*V¢ íÜâ r.´±Pa+·%çøÍ ÷LTe Èûn(ç/¶j¶GOšÖ {Ê–@×Î
+£Å…źóðÖ;ÞyÁŸ*Qð¦døî¦së–`ûãd¹…Ž'_çý›+þ¬ó\Ü|Å寅bÅf\K<d½0mÀÝBܲ|rÄQl#W#u÷è7«à3 Æ„Ó«ôPƒŒ¦Áè ¿»0£68Ïm%ïþƒ{ ¦Ý_‡°ð‰ÑmÔÞÃWÅÉI‘DcÄÓ‘ÏÈ|9{Ì0Äýób…•\›U %7ŠÖµUÄO*l<œèÐ}9L£°;âI꼘œFý«÷4endstream
+endobj
+1802 0 obj
+482
+endobj
+1803 0 obj<</Type/Page/Parent 1703 0 R/Contents 1804 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 79 0 R>>endobj
+1804 0 obj<</Length 1805 0 R/Filter/FlateDecode>>stream
+xÚ•[o›0Çßó)Žº—´J\lîê6)[—jR»v }«Tyà¤l:›öíç ÒB"”ØÎùùÜø=Á`‰Û“ß8›|Š&çË°ÑZìx¾<ˆ’i\¿”W¬º}©Ò"œ>žžF?µÒÑʹk!G*JºazׂÐllÌyYAüL9œIØÂ
+<´•9Íج#‡ƒ+Í+Èëì©PO—¯‹¥'£|ªàÌ<óxzÑ;“mkìßÔË«R+\ÀX*BW˜Ÿ_¼I 1ò…±|Éʘ§Ê‡ÜùM,d‰¨ïn÷WàBíZ"Â7A.Küä Vbi›5q|„[j•>Ç๒Á ‰¤!T`ʼ‹žÈe(ÖP‰ß:èuH'ð5R« 5šuzZ™b¬³ŒKJ£ÙVÀ”súï-^Oæh4¯RåuMgmG¡9®07M©ó½/X; tDïYUóÜ”kS²=S«ðRˆža­ŠVòáÝÖ¬„‚CÓÁúÑé·‡ëëCkSHÛui yQAÂÖiÎt´—:ý°÷ÑÂ;2mö®¹>Ÿò¿N¹˜Eל³<ÞÕ³¬í¤©jø:›æñ¶N„JWT35lDZiÖÉ”û9Ð1qiB+œhÎ]8úñA€÷òÌçò†ž?F$§ÛþjuËÅÀ íO¶žj?ZÅu&¢”Ò‹ŽK„PgEÉàtrs¢ÖOfãFÌÔc°Ø–E¯I°©{K[–zâ5ÔüÙê²ïèùÚ…Žm¬s(iIrXIƒîÙ‡?ì9¢8´ŒCßßû™©ÂèC)€%gìÖDä5ì…(Ü1×;~膡ƒqCù¯1ÄíPØbäøoaÖ‘w”—Ý`ôí/“KѸ™k—÷‹esøüp·‚U±®þR1ïx±á4Ë/á†æ5ÝJ«sL<˜ûD g›È%G¼4m=­GŒÇ÷ÉíTŠendstream
+endobj
+1805 0 obj
+740
+endobj
+1806 0 obj<</Type/Page/Parent 1703 0 R/Contents 1807 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 83 0 R>>endobj
+1807 0 obj<</Length 1808 0 R/Filter/FlateDecode>>stream
+xÚSMsÚ0½ó+vÒ tŠ°üí¦íLÚ„\Òs㢱¸µ-W%ý÷•,†´ãÛò>ï{»ûö×
+»=‹"„ξJø½ŽÝÌJJ5ó8)þýÍkk» îJÁzÆÖ–ê
+endobj
+1808 0 obj
+607
+endobj
+1809 0 obj<</Type/Page/Parent 1703 0 R/Contents 1810 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>>>>>>endobj
+1810 0 obj<</Length 1811 0 R/Filter/FlateDecode>>stream
+xÚTMsÓ0½çWì”KÂLTËqìt
+ …–^
+Z÷ÀL.ª-Ç[2²ÜÒÏÊ’S7ý€IÆ“hŸvßÛ·Þß
+~(,bûÍêɧtrøåhi‘8IH i>ͺ¦=çf½>ÝL7³YúÓÁ"›/YØu˶ÜE8òÁúJ¶²’ixk1¢Yí§Ã-¤áz3;~Rr±pYOô¶«¹4­C,R‹ AŒ”æa∠( ¢G”$˜Oy›iÑ¡¤œ¥“€Ø’Ýãò¢¢—˜h4¶EÀ„ 9\=iH†1 Fè¾·‘ç·´–Ô‹ð:{v}ž]Zr¬æ 
+0øÛ£È?¨Ö³ÿ¦Ú£Ÿ¥úÐì(ö>]rÓié[=H±Ç®ÍcÆ ¯¥™¾‡BTÐXÄrÛ«A³Ý¹Ò0ŒŒK8ýv}qñ¸ÆÄ£@ƤTn8T*c†ç6M®x x<L×õ «’Ýr¤3$/ÎÒÞ8<0I|îMx`¹å¦ÅR™jî×v: dh|ƒò™ŽTõ½CiV—¿Ž·ê==~`5>–ª5öþñðñÕA[ª®ÊÑÜZa/6ÓNVBþÚÌ3d…%qx†c+É¡k­_Â8žÕs-´FiÔ!$êm 3"ƒ›®(ÜØ2™Ã¨ª^Ó-×wZÃ%ž™8ËJŒQ£ö§àÕN¿lâÙV7Õ‹[èYÕåÞÙü‡öAÊCˆUå¶O“ï!{'‚÷0æúÔ¿¿^Íã±ùvën5,³0†e¹wðôòäK
+sø|½¾‚+U˜;kÖZ«­fuÍu _™ìXeSÌíÍyöëdÙ£ˆÆ„ºýò´Á¸U¾OþÜQ¯endstream
+endobj
+1811 0 obj
+659
+endobj
+1812 0 obj<</Type/Page/Parent 1703 0 R/Contents 1813 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 86 0 R>>endobj
+1813 0 obj<</Length 1814 0 R/Filter/FlateDecode>>stream
+xÚTÉnÛ0½û+è%K͈²6#m€ì§n¬œê¢ mJf¡Å%¥¦EÑïP"½HvR­™Ç73|ü1 àà—BèÂ(€E>¸‰c !Ä f‚0$ÄË“E½V¼šHQT\ªÙÉìô4þÞb½;ôâiì³b)o³ŒMÒ¥-®×)ŒP‡D©+&áììlm#§—½Z£QKw-Ó:çE¥Z„”jÄ8ÀŠa+ÇB4‚Ž) q=†ï¸ZH±®DYèÌ}<pˆƒÝØ<žÁ‹í#Q„•]2×!.HÓ`Í´ë¸r‹nÚêq¾ÖàS³»ËF^S¤‘7)›0T%è~°…~Y› “’ý&oÈvôèþ[vƒ>({Ûu/0#{âU- Ós»-=ä my¼âPÔù¥– ˜Ú_ì'›gœkg:ú$õª=“VÑ ¤¼Âr Ui%êÚßè! õÊfO|Ù6Ôª¬³%Ì9$’c´V¢H5AOÒÕr4/@T %de‘b/
+ääËãû¿ÿÅòuvÔEïD±Èê%‡º úAVWÆX;V—»5QÎ7»ý.Àد›¶¬„Nd— >Bo,o3ˆfû4Wà´Üö§ó”
+,ê\‚
+endobj
+1814 0 obj
+660
+endobj
+1815 0 obj<</Type/Page/Parent 1703 0 R/Contents 1816 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F0 3 0 R/F4 4 0 R/F8 7 0 R/F9 8 0 R>>>>/Annots 92 0 R>>endobj
+1816 0 obj<</Length 1817 0 R/Filter/FlateDecode>>stream
+xÚ•ÑnÚ0†ïyŠ#íª‘Æ % n“X U¥nëJ*í©r<96³¶¼ýì$¦4Z"9çøüŸ;ÿ:|óCí?É;ßãÎél (‚83‘ayCˆÓnR¬Õ æËK’á‚éEwÑëÅ«ÜA•Û?ó½Í½WxIª¨ã: ºàJC²ÂNlŽ !ßµj< š.zç{BaXÕº#º\Uñ ä´†ÈÖ‚rM$hzE ­*3*…¥e‘˜"Ä;(rIT"éZSÁÝšö„ºã º +VÀ-â ÙÊçAÜFM•s.ô«o2(')<nÊ2o9»7“ŸWMŸ¨<'\Ö?2£v•óÕš$4£¦àV2Áœ ˜H°6!“ÈËìÛ_óë?  EwÊ—ŒªÕ¢WN`
+ER¯ÎtÖ]`ÆšÀÎΙ$¤ÍK³›™ æÈI.ä°R"¡%Ø3Õ«¬Åèg ½`3H?²ïÓœ¯ÙÁ>þDyŠ”ÀK{ºÝ±Õ7·@ôzrløAÉK;o´¾çyi[ø¯ÐÒ]çìýùo=tÕŽ¦¹qt”° Ó¸ã{¾¹ìÙÇÝ•@àLvãÀ 냹½6ùr »g2ðÇo¹¦<)åK·¿­ŠãÈØã$QtDÓ¯5k®ZìsÙYûd¯î°Âôè1í2ƒÈ´í; ôÃ;mÞÊœÞxË¢ÑvtÁ~”áŠè£a0Ø.<‡Þ·Áq~a®åN?ì×;êv꣱5ä ՜˻É,†>\ÜßÎa.2ýl÷­K‰óœH?0/0³uû(B?
+|;1ÚW¡i3û!<|‘K~wþQ|ìKendstream
+endobj
+1817 0 obj
+653
+endobj
+1818 0 obj<</Type/Page/Parent 1703 0 R/Contents 1819 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 98 0 R>>endobj
+1819 0 obj<</Length 1820 0 R/Filter/FlateDecode>>stream
+xÚ•]o›0†ïó+Ž²›¶
+.¶ù0ê6©[›ÞlR×лJ"a"ñ¡íç÷Øؤ#M"°_ÎyÎ{lógBÁÅ?… ÝL¾Å“ëy4„x…3A’
+e†/ôùp÷t;ÁïÏ XT«öo‚'êc]eu²ÙȺŸIÙ%…ŠêP†Ÿ¨¹§7/ô0º–ðÐøñkòº¡ù¡endstream
+endobj
+1820 0 obj
+666
+endobj
+1821 0 obj<</Type/Page/Parent 1703 0 R/Contents 1822 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F0 3 0 R/F4 4 0 R/F8 7 0 R/F9 8 0 R>>>>/Annots 104 0 R>>endobj
+1822 0 obj<</Length 1823 0 R/Filter/FlateDecode>>stream
+xÚ•”]o›0†ïùGÚM" ÇÆ|E›&ukèÍ&µ…ÞåƇ0ÈŒiûógL«¤¤­@H>~}Îã÷Øüs`ý ¡yóÚù™9«d $‚l§gÂ(B!dÅ"ïÝoÖ”‰è»ýv±].³¿ƒÒ”n€‘o”w+ù0‹a=NzdHóÐV…™Ó!‚QüFf£Ø.¿¤§tÈpÉ»\VUµ-Bˆ­3°þ¨\ÀNrÞ„–õ:%û\õRÇÕž)س÷œ7FÖæLñÍBmžX}³»þR5¹è ß ÙÊÖDû£Vˆ:ŠœleÞ™”s¸]k›ÌÁëÞš1ŸÛ+3
+endobj
+1823 0 obj
+456
+endobj
+1824 0 obj<</Type/Page/Parent 1703 0 R/Contents 1825 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 110 0 R>>endobj
+1825 0 obj<</Length 1826 0 R/Filter/FlateDecode>>stream
+xÚ”ÛŽÚ0†ïyŠ‘zUclÇ9iW+Ñ.ìM+m—ìÒ*
+&¤ÊæÐöñ;Nl@BÙóg曆ü1 øaàq°]ˆóÑ×p4]À<7q=¸®Çq»«¿GE²¨¤\W“Iø«Š^h9”%|¯£DöQ
+rÖgùS¦kÃ+F‰žX>2<~4ðYý¶˜m5y8+gÛ}ÆY•´¹,šºW8À˜RÒXÜ뙌HiXÀˆ‡ðúYÖq•îš´,TdŽ(¡èÇþëí„jùX;Àšœ•„倘>±µš ¼=RwÆ
+ç(‡éLŸ^W¤Ã ·Lê¦jã¦ÅTM ôŠÜ@¦jpw#wêAäƒçÂÕ#;ñM ºoKMÚí-ÞÓÿ¸k¡†[¬w2N7©\4K.Î}þ/ÊwÙÅEû”qÖ®%<*©ÉL¶OzýŒppáNv”2ðØ¡½{u)%̲º<Ó“è¼wšŸƒo¦Y?ˆ}•ÀÁMõœƒÒ#®g¹‰Ú¬1Îô}5s]‘ùª× %©.©±t­/Йr¶‡˜q¹N‹ä*ó]\@ƒÁqÇ‚[†õ^ŽEÖÖÛ«ÜUkk l›ïO! é½/òú4lÛÛwžƒ`ⶆó^„eSMã<ßtáë²8U†GèüÛl‚ßÞ_—°,7Íß߯U™TQžËª†QÑF™Êk1î‚åqzé5 <•µ Ûöãçè?ªãž³endstream
+endobj
+1826 0 obj
+568
+endobj
+1827 0 obj<</Type/Page/Parent 1703 0 R/Contents 1828 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 116 0 R>>endobj
+1828 0 obj<</Length 1829 0 R/Filter/FlateDecode>>stream
+xÚ”]oÚ0†ïùGÝ TÃóºMêV¨*mZWRiHUœ)q˜íhûù;Nœ”B dÅöësŸÿQ°ðGÁñõ?)GŸ£ÑÕb4€(Å?ˆÑzœÔ[ù5æÙS«ñj2‰~·:·ÕM=‹¸Z÷$㌵»Ì̦M_Œ<håYÁ¥áµHxà ©¸Tlb—<.Ùjr}àÑqZ£7"«KÆ•lPªÔ
+E×AËÕ©´ˆÎ( Ð.ß2™ˆ|«òŠëy4²ˆ…!é‡Ç;pCT{h(ê{ÄÛÂA0Xˆmê™QÛ¶‹Î_ÔMl]ÃçišKèK6hƒ-Ú0ÐËP¥ ð»¨’¸`ä LKçëlÌF=ˆùh×7zdªÜ„¹»†Î®oB Û*çŠ PÄ ó\c1€T¢NðhË>˜Ã½4èÚ90?XÆѺAÇV¢‰¡Ü²$Os¶î¢ ÷»Á…\¯¬Yšsá×CWª†I¾/ïÂj<çY‘ËÍj²s\Ö¿¤ÊU­Øúøuçÿâr[m“w9OŠzÍàƒ¾óUw²ùd@û©“]ï‰!{+}€>« ^¤âÂtÚÉóÝ¡…`lÕ[;Õ£KÆà¦ÕAñRS8ê8–:ظí¤hËqO/2Ø-@ê¼z@nY×E_#ƒîÂPw‰ñGC]àGZÆ¡j<½‡& P=Âœ'Õ:çÙIúÄé!lì²Ù[éy‹¢Æâ<…`ûº·:Çñû,Eè8ÏDh*äãRâõ. ûÙQÄ<ÛÿR‰4ÚºZ„¦Œ°¸-ð¦¿ooLáËÓÖUªþÆø†<ˆ*qY2!á[Ìë¸Ð6§ÔöaØÍ“íZzÉ¥n÷†¼Y†£ÿx€å½endstream
+endobj
+1829 0 obj
+678
+endobj
+1830 0 obj<</Type/Page/Parent 1703 0 R/Contents 1831 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 122 0 R>>endobj
+1831 0 obj<</Length 1832 0 R/Filter/FlateDecode>>stream
+xÚ–ßoÚ0Çßù+NÝ ­Š‰ßê6‰­Ð—vêšôaÊÀL‰CDÝþûÙ± 
+HäÎwŸ¿wð6À`ˆ7—€éÀ2| ã™Ø…0Çu‘áj¸¬¶ÅcÄÖAɶžç××áåj)בm Kº¾Ñš*«¾6¬ãl"7Ò*by}Áåý"%ÜÈïJD¼í,‚ÃWÂÊö:£…d˜_ßAš¦â˜ðu•QVÊÃŒ¥6\ä ZWm¥ñ’NØÇÈ!Äã{Z,y²-“œIË4È…l?^Àò„·]$Êë8⚈
+endobj
+1832 0 obj
+725
+endobj
+1833 0 obj<</Type/Page/Parent 1703 0 R/Contents 1834 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F0 3 0 R/F4 4 0 R/F8 7 0 R/F9 8 0 R>>>>/Annots 125 0 R>>endobj
+1834 0 obj<</Length 1835 0 R/Filter/FlateDecode>>stream
+xÚuS]o›0}ϯ¸Ò^iql @ÕmR–†)[ײ„¾EŠ<⤞ŒaƬûù³kè6YøÞsï9ÜŸØ<üоy1ù˜MÉ5²“ñ„Q„BÈŽÓ¼©ê;ZëµR¥ÚO÷³YöÃ!‡œ_aXäcMÏÌy1\·N¸4¼ªµ¦º©ÚbŒ‹`_`øUòã~v3 ñ}—iËt£díüÒ1…NÆR“MÁÕ¼”—RS.¹<ƒ~b lÒ˜eC£,·¬Î¯lŠî—L#¥é4MA9©ˆ‰j(ó¼QŠlN ËÖÕYm˜®á™ Ñæh!>ò¬“ùðð¥/Àdû?¢ÅV÷ÉÝf•õÆë±þM‹JŒ¶÷ —¹hŽ ÞÙ¢,ìž>´¾4à>nz„PÏâpðÕ‘c°uiël‚6Ãnbí'{/Æ]@ä#¿½ØÙ=èáÕþ4/&7cEeÎÄçò{×þ‹tQl&½ã#Aðzâ–°Õ2½}éâPÖ‹„Tq©.X'a˜m‘Ä­ü+Eø±àÛí2É`«Çt»ò¤Ÿ©bªò¬ha6¨†¯T6Tجsâ…0<lÏšBÌðáÑU0µø6ùÅ.Áendstream
+endobj
+1835 0 obj
+480
+endobj
+1836 0 obj<</Type/Page/Parent 1703 0 R/Contents 1837 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 130 0 R>>endobj
+1837 0 obj<</Length 1838 0 R/Filter/FlateDecode>>stream
+xÚ••Ûn›@†ïý#õ&©â »œ•ª’ÛÄQ/¢Ò˜ÜEŠ¶fíÐrê=¼}gÙ%%'Ô–ff¾ùwücAÁÂ/ŸíÁ6_|ˆçë¨ñW<ß'ÄÉɶ­ê.¿®š´,êû“ûÓÓø›Ö:Z»t-â(í]Í÷B¯ZšEFu ´hÔ>¡ &CWUò°K3ñÐÀ[¼?éaôÁˆP´ùC©ßM®Ò­J`Þº?½8Ømkä•Ü·¹(šZ+\ T),™¯7Ý‹”††”ø_Šz+Ó.‡Z¹Š±°æO—ÛkpT»–GPæ`8fá­°™Sß&Ô¨™çªë“ºkžcð\Åà„LQ`;®.zÇ?
+ˆ¢KPu†¦„@^&tpÃÖlB­ž Ô\>ÕÝ4n‚W¿
+ 匛 .ô?¹”üÏkÜÈÌçîÔG¹]cãiæT%Ú‘±¤ÿÍé¸{6g§žtÀ? Ûa Ë|+šVÆÀ½CÔ©ô´{Ÿ{[»,Ý65ìʶHÈÑ“1r·:ï¡NŽã¤3_=nqïNŸàY]Â#/’LÔ(¯ª´Ø+ÒOQdÞƒ‚縆’þüÛ¤óþOžµ¸€ Q‡âãûºúÍó*;:ÃÞ¤Å6kïÔæÎÕ…<¾7yƒn0u>¿˜˜‡i¬ Eü¨¹x1JH=™š¬gÏf%Ì!`…õ?p/5†Äz(Îÿ.Ì}¦Ý8RË= ýþ]VI¢9{‡Læ
+qûdÔSGe:›e²ùþS’3èÊs
+endobj
+1838 0 obj
+717
+endobj
+1839 0 obj<</Type/Page/Parent 1703 0 R/Contents 1840 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 135 0 R>>endobj
+1840 0 obj<</Length 1841 0 R/Filter/FlateDecode>>stream
+xÚ•ÛnÚ@†ïyŠ•ªÁÆ볕¦mB”‹¨i W¥Š6°€[èÚNÚ·ïìzMÛ
+ÞßÍ ‹¨Q¶þP>ßORuCàKà‰ã)Šøâ"ÊÃmÄ!cI’bóÄO!Æ­ïëJ¤1dü™ i¾lv®þ²]î›™ÂdKŸdŠÎäl>kúÚ`­¸ZûœwÌÛÚôk¨*w„ÆJÍ%\€Ñt[™äçæÍ0óA뉷Û»ëù‡ósøf¶ÃÇ#¦õ”sGYÚê'ª[oÀ—ýþÅéë¨ì†Z¬¡Þjâë,Ž—˶ªÔÎX¾Iœ*ueóvG3t4ÏÛ‚JY HEŸÞì”ÎøÔ HP˜¶…ç÷4ä1 ×<?"¦«"°,ç`
+4å1·Lünd¡íïlâë Qê¡W×Ò=y?žÌ`_î¦0MWù ÃÉq'Òµ`qÌE·,)X$ýŽ¨éÂÈ3ÕD·m¹dùz3Þ›^˜‘ï½ÿÕÒxžendstream
+endobj
+1841 0 obj
+791
+endobj
+1842 0 obj<</Type/Page/Parent 1703 0 R/Contents 1843 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>>>>>>endobj
+1843 0 obj<</Length 1844 0 R/Filter/FlateDecode>>stream
+xÚ••mOÛ0Çß÷SœØ@Ôä©I*¦ilÀÄ´I„wHÈM6(q2ÇãÛïüRÚD-­ž¿Ï¿œïÎG.8øw!òÀ!-Gß’ÑùÍÜ’ -a‘’ÅqÚÖÍLä\Þä{<~<9Iž20ÊñÄ!R>4tɌթ5z®qƒ” g\‡Ä=ŽÓŠ7ÒpZ«i&ζVÀ»ßæ‚ ]pZ²ÃWÈ\{äÈ
+H4tC*}ÝÇíø¸×ÁÜZ=ÄíÇSSTýÌ—PW&ñ˜?ÊéÇþJȨ{Kè­ø~hÂ|Çd+¸í
+DÛƒ‰-À?£ÿ„ <äendstream
+endobj
+1844 0 obj
+710
+endobj
+1845 0 obj<</Type/Page/Parent 1703 0 R/Contents 1846 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 4 0 R/F8 7 0 R/F9 8 0 R>>>>/Annots 138 0 R>>endobj
+1846 0 obj<</Length 1847 0 R/Filter/FlateDecode>>stream
+xÚuQo …ßùçQ“•B¡->:g—,[â,{ó…U4YZÙ¨f°áËÔ@nrážóÜ/ÂÁÂáU¼Ý@î5É›¸„Þ…ŸJqZ@o'­µ˜÷£›ê²Ô„Q„±ðXÖ±AU
+ª0 T¤¦G=ÿÍû=òF‚óD)e ”îô9.Ì¡³ý“{ßL6Ó›¸Zřǥ¢å- KÀ*‘î­/cýFx6ãqé½óç—ny£Rü² uðý[ÓÃzÞhdX¼­Z´nwü6ÞbåÝÞ›a°~Ä‹9œLM3^TÈê‚E¡¬â“”ìʺ_Éå-dendstream
+endobj
+1847 0 obj
+238
+endobj
+1848 0 obj<</Type/Page/Parent 1703 0 R/Contents 1849 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 144 0 R>>endobj
+1849 0 obj<</Length 1850 0 R/Filter/FlateDecode>>stream
+xÚ•TÑnÚ0}ç+®´˜Š±;NÔiëÊö2Aª¾ UQ04SB˜¶~þìØB´ŠH°}|ï¹ç\ûO
+>ê‹Ám+ïÛˆc¹ÞbS)‹`@ˆALÑy¹%Õ  ˆDqBO*•Ù¶ÊÊY¹{a­Éþ5û4Ôh¦…@X„0xy ÌÏ€=Ì÷hÏÇšâ]‹K?f80âŠÐ5ÖÌXdyÅϬ *)’ªR#ºÂÇÞ̳FŸåyPšΨ±Œ½¶ão`uîl”¦î¾-CA¥KT[‘f«L,‹Efß¿$Å6ïì®Ù&ÍwKŸ ‘ Šž?»Žk`í»=A „Îì9*ìJ[Î…€q®Ê–]Ä9€CÌײÀ#ý±ƒÜp‚—k8HhöÑS¹nŦQûl¾¢hŸD‡Q+#v+—êjEZ¼^q˜‰dù]ÿ„¼ÈÄÃx_n>fˆ]£Òð}—iö"ru‘‹OÈ+¨çwÑpi¿Ë£Ì*q,L;lÿær«Ps>µsúÈ27êì'pÔ!5‹cIÚ,F“°¹@½
+endobj
+1850 0 obj
+574
+endobj
+1851 0 obj<</Type/Page/Parent 1703 0 R/Contents 1852 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 150 0 R>>endobj
+1852 0 obj<</Length 1853 0 R/Filter/FlateDecode>>stream
+xÚ•”koÚ0†¿ó+Ž´/0kç±Mb-Ý&u[RõK%C3åÂì úów'Â:®‰ýæø9ǯϟŠo¦£>«lð9\ÝúÀ\cœq\—8®‡«ÝV‘,¹ø¹åùÓði4
+k©¥¥›KId´áz–‚_Oìg)ª@ËÞ+Î2J¼s«$y ñzÜRÁé«
+™kŽÕßÓhÚa3M½üLlvÏK©60¦¶‰‘'†«hDJÃ|F\Œ€Ã7\®D²-“"W3óp@ Åò½þ_ÀòPm3—P°=ŒfPb€à°8£eÔ%¬›&CáA]mƒÕÐ)›:ûx­V¯¢WXá3‡8I9¬k¾B@YÀNrrÓòŒøFL->ƒYÁYž¡ë«êßÅS£5Ógè܆×÷‹e0[„ó`Ìg7zºI}˜Í…'ƒoá¼õÈ¿Òvd}kÚ•øìîÜe¾Þš€—;‘ËSe~§6l ´4¯6(} @–‚GÙ™D<ÜݵˑÄP>s¤ØG¢¸… ½~oyöüˆªïhíŽËpŒÄx±Nò‚ß‹¤ÄË~–ùK”mÓÞÎð.ÉWéòA]éµÈó§úð÷4¼š¶„ÖŠà#´ó¤ch›ïRëXp³Tw±Ú1xÌ1°¨x¦oRm˜–^làØ"Ì1Û›q’7»qvAÏAC6+2< ¬oIZ/YcÕk+ÃuÁŽ Ü߯øåâ"‰AU¥“:ýÙ7( ïÿ±Ü'/<•YLf•Å2¬W²^–øí,hv~Z˜nØáø²W,A³ªÓØõ]¯YðÊVýçÄ"ÅiIºW·^m3f`ßsê^uÌnC˜TG
+endobj
+1853 0 obj
+710
+endobj
+1854 0 obj<</Type/Page/Parent 1703 0 R/Contents 1855 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>>>>>>endobj
+1855 0 obj<</Length 1856 0 R/Filter/FlateDecode>>stream
+xÚ”ÛnÚ@†ïyŠ‘*UÐÀÆk|i%ÒBsѪœ«R!Ë,à
+ºk+©¢¼{gwí
+Ƨ,\Þá—ñy{Þé´¥ z¶A,ip/Â5Ó·øÕ¥Iµ·2ñ:eKyÇÔ Þù òtÁÕñ¢€øÔmØÁ©2Ëb±QŽ¤­~šwGØý¾&òu™°´Za¥RázÄF|W¿[-’êSâ¢<þÂDÄ㼈³TÞŒ‚–A ÌïË¿éW°<TÛÔ%˜uÃE|Ó &p³bj;V«M¨U¡¬
+Ï– ¶aêt`†™ò¯È‚ AgaE†nÂ%¬x–Ë´–ï Ÿ·Òjõ ZÅhy¦Î³®Dsy§’ñBÅ-”ØeT”è¬&Gaö¹ë`´7“+õÉ<ïÅ2}ä)Cš´j“º²ÅÝ#²DELˆ.^à¯Ñ1dç'g;°ÑEr|Žœ_žÆš¦­%™”‡Bä,ŠW1[¶Æy²Ñc˜äÛ³Cý.N£m¹dp#ñ®µS²ùT k-úë6NÙàÄ6ØÍ}õ‘ãIø2ëú¡)­D›×>óø‘mÅ/úæï¦!¤Áÿ°‰· ægj ·¼¯÷Œ®Èž»§£Íu
+endobj
+1856 0 obj
+664
+endobj
+1857 0 obj<</Type/Page/Parent 1703 0 R/Contents 1858 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 4 0 R/F8 7 0 R/F9 8 0 R>>>>/Annots 156 0 R>>endobj
+1858 0 obj<</Length 1859 0 R/Filter/FlateDecode>>stream
+xÚ“QOÂ0Çßû)îq$Ðõ¶®ˆN_Œ¸ÍøÂË…`6†D?¾­é¦ˆ[H›&×Þýÿ¿´×w‚ÀÌ@…«ŠÜäÄO¦€ò91Ò
+endobj
+1859 0 obj
+320
+endobj
+1860 0 obj<</Type/Page/Parent 1703 0 R/Contents 1861 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>>>>>>endobj
+1861 0 obj<</Length 1862 0 R/Filter/FlateDecode>>stream
+xÚ•TÛNÛ@}ÏWŒT©J€,^Ç·Z
+”‡VMóTWhIÖ±+_Òµ-¨ÿÞÙKJâ8@)Z휙9;{öüîQ°ðGÁ·aäÁ<ã«1PÂ#žïÂEÞ¬ª)«j.¦œ-&éϪ¨ á/àè„¡kG&ÜTlÉuÔ‚± ÚTWkŠ*]|!ã¸M-ìo"wo…Ú¾­á
+îš8æâ5–ž¼ 7³Tè½,]#j­––E“ß!É2†»?5¯äBs]°š­‡ûcÇÅ.of¬ÐxñhèñNy݈ÂHx­ùZ=­ß®3(=”ae—ß®äš QêÉw¾•–Þ¥/ì´zÙfÖÜúª}µ9E)M¨‘hµâó4Nñõnix?­Ë–¯²½Võ.-æY³àp*¹ë¢$ùhŒd CîI–ü¤ÃãžÝÌ|ÒÔ^&x\…׋6tÛȩ̂Í€ŽíŸíBH‹ÿ}’f¢Ö\«v‘4‰#x¯›G}åw\u·@Œ‰úr&ð¬585G"²ñ5O—Im"‡‡;}
+endobj
+1862 0 obj
+698
+endobj
+1863 0 obj<</Type/Page/Parent 1703 0 R/Contents 1864 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 4 0 R/F8 7 0 R/F9 8 0 R>>>>/Annots 162 0 R>>endobj
+1864 0 obj<</Length 1865 0 R/Filter/FlateDecode>>stream
+xÚ•“QOÂ0Çß÷)î(½­ëÊ#¢Ó#n3¾ð²@!˜aÑïDÄf[“kïþÿ_ÛÛ»‡ éA4¿³Ò»É¼A<T-hE>dóNj-ŒŠºêfoÞ]æI!©ä!¹ç
+@þò7<{_íÅÆendstream
+endobj
+1865 0 obj
+318
+endobj
+1866 0 obj<</Type/Page/Parent 1703 0 R/Contents 1867 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 168 0 R>>endobj
+1867 0 obj<</Length 1868 0 R/Filter/FlateDecode>>stream
+xÚ•”YoÚ@Çßù#UªHŽ×øBI+‘4ÇC«R êC©"Ë,àÊWwm%UÕïÞÙà ø€$X{ÿ3óÛÙ™ùÕ#`â—€gÁÈ…0é].zg7c ,Ö¸ãzžáÂbÕËœÏ^PöE½£ÁŠ²eyr²ø©,le1tLÃ÷<ØPµkÂXoZD¹+SmRºûøš˜† ŠxýÀäû‡ÞájP3„Ö´Ëãa+= cµZžœ7ÀG#Å6a›2¡iÁ•ÂB„bL à©ÓU"¡!¸å¡|ý‘òEye©Ø¹^ôLÃÄ?ÿÌnÁöQíÏÀÄcÌX&zfæ-bâ[¨SjËq÷Ôò®lçÇ´T>0E’Lú—d‹-•Bà£AE"˸0ÃÚcݾV©[`%¢™fu5Ê äY”
+H¤+YÜ(é3ð1ZÏů¦•êÖÔ¾Ô†mU^g´(Yª+£J½¨kW•,^†!å|€øD˘ÑYmµŠÍÒðz¤ù*Ž¾J—‰ã9 £uDWõûeÐMvý$yÜÙÂo¢4ŒË… w¦œÛº3+Þf³1ã(¥ç-½ÿÒäú#zýð¹¯Õ¢.­Æ „Û€U>óè‰Æü»OÆÖºa5þŽÜ‹o÷†ÉŽÑ:c°ì‹SÂ{0ÏåyáBCÂå6ÛBª«ÜùÓpM45àvà¦òdNs°ôò7VÇ”²Oôº
+ñ·³æ”Â$æY£ïˆn%|
+ba*œ
+d4&GS°ÿ‹Q•.цËþà`Y8bÈâ=™’ʇκ@ñ¨¥åŽœÝøºœˆ%’ìɱ:›Ü,`W÷Ó9̳uñà`Ÿ²lÂ$¡ŒÃç -ƒX¸
+»¡g™Gg­íÙØ+RçŒô±¿öþOdêendstream
+endobj
+1868 0 obj
+776
+endobj
+1869 0 obj<</Type/Page/Parent 1703 0 R/Contents 1870 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>>>>>>endobj
+1870 0 obj<</Length 1871 0 R/Filter/FlateDecode>>stream
+xÚ•”[oÚ0€ßùGš4A[Ü8ä*ºIíz{Ø$©ö0¦Ê &d"9ŽÚiÚŸí“”[ ,­åsû|n¿Z,õG¡çéÿ8k]E­óÛ¨ÑTI<ß'D“v\.‹!+$ßD*ù }áóbÜw:ÑO´pТëZÄÑK8J-+¡MÑ]™i’ó‰–«kj‘à@}ý(Ìý£„u:Û2„ƯŽñŒ 8Ywÿi:çy"gãN硽¾åR$eÆsY † ”j jY„ªû˜ŽZK+Ñ_¹P××¼ˆEº”é"×’›¨eKÕäõgxN ´]Ë#6P—*§¶¥Ž‚èA™†¡zjÛ½€¸kÚ¦¸NÅçj'´5…ʨá2Þ W4〇B
+Î2 xÖEQrÕ <bŠÚ ¨èS,¯Ò`Â${E|‹Ïs•Ï£ùŒö^>·êh쓾¼ÌžTSxú-yq4¤ã*ÇGCíÆz¯:¶˜Ë!—¥È«~­ûAO§‡½Ú„­™%ÏÉÞ1Øêd=õ;ŽßØ"5J3¤r¥@Š%ÓiªFq³Òl³?÷ƒÝ¼°l9ß»ŠÞ¥y</'.4Ý9:%³Õš¨ÕÒ\în‰yšó~Ã[-«êÓ;ë⌳‰ÑÇöêæ"«>œˆï íÛ„-þ­Ôß›8c=ùgð£VKnÍhº0nëW°úæ½pQAíòž§ÉLV’ÓS¬äÈŸm»‹°§/ \µµ×ƒ^éŽpñY]A×!þb¥ƒzÛx¶ƒ²ëáåm]øô0Áh1•ÏLMÊ@,Á²Œ‹¾°¼dsí¢«-»¾m&ÙuôUÏ q°6²꯭![ôIendstream
+endobj
+1871 0 obj
+650
+endobj
+1872 0 obj<</Type/Page/Parent 1703 0 R/Contents 1873 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 4 0 R/F8 7 0 R/F9 8 0 R>>>>/Annots 174 0 R>>endobj
+1873 0 obj<</Length 1874 0 R/Filter/FlateDecode>>stream
+xÚ•’Ao‚@…ïüŠ9b"ËÎ
+ ­-í¥©š^¼] ˆ]4íÏïl³•ˆm›Ì2ûÞ·Ãût8=‘€‘„EåÜ原ŒÈWôEÆÈäK7S
+&eSòç!w8ãtÐ,h–ôÑ Ã‹¡‚˜³È%dFó¢_¯ÁO@´.a@­ä²8ìš´höJO˺Qsw>¸jK"kQ†L^³äÖÒbY¯!é.Ø ÄËNm{0B†GAÃâ· ZÒ¿S¤ªX>Ñ«t/‹ „G–Ño„€ÿM2Û|«²iIºšî°?"'.úa(X`««1íægÉx×›½:ŸG—ÂOb›.¢˜¼Hä>$9x0}›eÕ«ýW¡Ìt½ÖEU)ÝÀs±=¥ÑôÌA/ü2ýAdö»†ö®¯Î“bÅ•endstream
+endobj
+1874 0 obj
+317
+endobj
+1875 0 obj<</Type/Page/Parent 1703 0 R/Contents 1876 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F0 3 0 R/F4 4 0 R/F8 7 0 R/F9 8 0 R/Fa 9 0 R>>>>/Annots 177 0 R>>endobj
+1876 0 obj<</Length 1877 0 R/Filter/FlateDecode>>stream
+xÚ”ÛŽÚ0†ïyŠÑö*9q궕¶-ìU+
+¡WH•7™€+Ǧ¶íÛ×ÎÍ&PU‰¬Ä3þç›ñØ¿;¸æñ Û7Î:Ÿ¢Îp1oQj,ãÉÄC”tããA­QžPn»Û^/úU¸……Û`ä:¡uÛ(²ÃÂê¬4ú^©!¸Ò·Öǘ<×™6åO‚&ÛÞ}+F2+ÔGÉUaÁóª0ã‚á‚r´
+” ÜH(M4‰˜TH¶lã[Â8SÆà A‰³¤Z#7szh&þ@LŒÙ
+endobj
+1877 0 obj
+591
+endobj
+1878 0 obj<</Type/Page/Parent 1703 0 R/Contents 1879 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/Fa 9 0 R>>>>>>endobj
+1879 0 obj<</Length 1880 0 R/Filter/FlateDecode>>stream
+xÚTËnÛ0¼û+èÅ.jY”õDŠi=pmåT÷ÀÈ”ÌBUŠN›~}—¢±#·±Bàggg—ü9!`ãŸ@àÀÒ‡¤˜|Œ'‹ÛH
+endobj
+1880 0 obj
+590
+endobj
+1881 0 obj<</Type/Page/Parent 1703 0 R/Contents 1882 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F0 3 0 R/F4 4 0 R/F8 7 0 R/F9 8 0 R>>>>/Annots 180 0 R>>endobj
+1882 0 obj<</Length 1883 0 R/Filter/FlateDecode>>stream
+xÚuS_o›0çSœ´—dZˆ µ”-M5©²@Þòâ“x›Ù¦Y¿}m k‚@Fpw¿?wÇ_2?´wZ8ßg¼œŽ Ù™HEnI6H«Rm•ÛÁv8Lþ4IA“4š 7°IEö´‰"˜µA·‚+ éHølsL#wz þ*X¶Þu|¿YS]I®šx
+endobj
+1883 0 obj
+480
+endobj
+1884 0 obj<</Type/Page/Parent 1703 0 R/Contents 1885 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F5 5 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+1885 0 obj<</Length 1886 0 R/Filter/FlateDecode>>stream
+xÚ…‘Ënà E÷þŠY& SC±1K§Iºª”ÚÎ.ä×­_¬öó ±ڨ!±˜9÷Üy 0Dö``n¨º`S7{˜Ay¶„1”@ù´z6fÜ´CõÚôõiuZ¯ËR†q„¨ZÔÒumR_ËT=u²7Ú«bÀØ©0qš°ï”ƒ0LjY[[ÞJ]©f4Íлή "ÙW/W~4µtL¹5òJëL@I(~€ uc/4CüOz¶&‹5CÑÎdq^àK®s\”Ç>×\šIõzìâÕŸ¿tv¢Ûë€ )!kõàóM—|»Uîmól_BwÇCÅp6ïÂ>ì †Z‰®“JÃè'Ñ:‹Ð)CF¢ß6OE©_OÌç­<Ÿ…,‘Gendstream
+endobj
+1886 0 obj
+293
+endobj
+1887 0 obj<</Type/Page/Parent 1703 0 R/Contents 1888 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F5 5 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+1888 0 obj<</Length 1889 0 R/Filter/FlateDecode>>stream
+xÚ…’=oƒ0†w~ÅÉ
+dËbQ‡¤å«Æ¨ýùõÕ5QÔ
+Äp÷Ükû1b
+‹ߢösïam'"ˆ ŒéV'U¼f‡ù<sw”’€#µïe©°kkœ»Z¢Ë¡VéÝT”âe8ã3qM!DckËkÕúÜ™sÛ`g“{$ vËÓ'}¾´tÈcä&m2­ »3ŽËŽ´â?éK4›¢E@þKfSòR/ºx:©©2ƒnú‹°QâÍ™u6_²îª[Á™RT}ëü.'¿x¥±[h&Û|XíwdíÑ|J»±nK-ëZéže3È
+#|œôûñ,ñ:"wÿ{%/Þ7Ä*Ž›endstream
+endobj
+1889 0 obj
+291
+endobj
+1890 0 obj<</Type/Page/Parent 1703 0 R/Contents 1891 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F5 5 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+1891 0 obj<</Length 1892 0 R/Filter/FlateDecode>>stream
+xÚ…‘?oà ÅwŠ“ÁÔPlÌè&q§J©M¶,(!®+ÿ+Øj?~A8VU©î~ï¼û0Dö``8µÁ“r˜¸ØNÂJ@œWoã8l%u^«ælŽ«ãz-Þ=K=Æ¢Ž=Y)×µ5J}-ÓÕÔªn4^ÆN…‰Ó„„ý¦„9FÌÚÚòV™“®‡±î;×Ù‰ B‘}ørÏ@SKÇ”[#¯´Î´‚ò˜P7öJ3ÄïÒ³5Y¬Šþs&‹ó_£ã¢<öÑjœtgæÀ®!ÞüùGg÷%Û¡¹ ¸T
+²Æô>ßtÉ7±‹å~жÈr!lûÊþ2~Jû°½î+-ÛVi/²›dã,B§ ‰î,Ÿ2ŠR¿¡Ï‹y ¾¸Ž“jendstream
+endobj
+1892 0 obj
+295
+endobj
+1893 0 obj<</Type/Page/Parent 1703 0 R/Contents 1894 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F5 5 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+1894 0 obj<</Length 1895 0 R/Filter/FlateDecode>>stream
+xÚ…’=oƒ0†w~ÅÉ
+dË‚R‡¦â«6¨ýù=×€Ú(Jb¸{îµý˜Á‡BÛ÷T{¹÷°“@ägìÄB1䯋·¾ïÖUkÔqq\.ówGqGù ¸¥¦(•íbsWKt9Ԫ雊€R;E™ñ™øKYˆJŒÅòF™“¾tý¥mlg›{$ ¸åù“>_!q‰An“hÙ ˜q»ìD‹@Þ¥Çh6G‹€ü—Ìæ䞤Žº¸ŒœÔTõƒnÌ(l’xuæ_íWQwÕµàL)H*Ó:¿«Ù¯½RéÚ¤É.Ö‡}Y{î? ÜØ^·¥.êZiÏE3•ðí¤/Øÿ˜ÙCtDnþx%/Þ7ø“ŽÐendstream
+endobj
+1895 0 obj
+291
+endobj
+1896 0 obj<</Type/Page/Parent 1703 0 R/Contents 1897 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F5 5 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+1897 0 obj<</Length 1898 0 R/Filter/FlateDecode>>stream
+xÚ…‘Ënƒ0E÷|Å,“®í KšGW•RpvÙ êÐT`¨1j?¿¶ ¨ú%/fνcßy `{p
+›ª6¸ÁÝ!ÂA\l'æÅ žW/ÆôÛN)Y™óê¼^‹WÏ1Ï…FÌq§¡¬¥ëÚc¾–ézl¥2ƒWE@ˆSê4!åß)‘” nmmy'‡J_{sí”ëìE€¶^®üXb鈥ÖÈ+­3-¡ø¦ÌiŽÒ?éÉš.Öáÿœéâ¼Às¬S\,|¬¹4£VÃØâÍŸ¿töeÛ7·RBÖ Ï7YòíRS?h—g!lOÇŠîbÞKû°£îj]¶­Ô<–j,g:eÈ)þeñŒ3”øíÄ›i)OÁ';„endstream
+endobj
+1898 0 obj
+292
+endobj
+1899 0 obj<</Type/Page/Parent 1703 0 R/Contents 1900 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F5 5 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+1900 0 obj<</Length 1901 0 R/Filter/FlateDecode>>stream
+xÚ…’Ërƒ †÷>ÅY& ©P”°´5éª3©’]6Œ!6oEœöñ E6ÓËè¸8ç;?ðák€!²†ÛĽe܉àfÇ3gÛIC ˆÓêÙ˜>SewR =®Žëµxñ õ`Gˆ:ð0ÈJ¹®­Qêk©®ÆFµfðS1`ì¦0q3!aß)aŽ³±¶œ©¡Ô—Þ\ºÖu¶"ˆPdw½|ò KÇ”Û ?i“ hÅ0¡nÙ™fˆÿIOÑd‰f(ú/™,É <{tQ{¯¹2£n‡IØ,ñêÌ_:ÛwÙôõµàB)Hë¡ó~7‹_w«Ü/”åéN@÷‡}Ew6oÒnl¯»J˦Qz€GÙŽ²v¡› ùôŸPW¢[GÑo¿‚½•§àNÛ>endstream
+endobj
+1901 0 obj
+293
+endobj
+1902 0 obj<</Type/Page/Parent 1703 0 R/Contents 1903 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F5 5 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+1903 0 obj<</Length 1904 0 R/Filter/FlateDecode>>stream
+xÚ…‘?oà ÅwŠ“Á(6ftë¤S¥Ô&[”7•ÿcµ¿ «¢VH w¿÷Þ}D°;8…ûŽmô £»­
+endobj
+1904 0 obj
+290
+endobj
+1905 0 obj<</Type/Page/Parent 1703 0 R/Contents 1906 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F5 5 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+1906 0 obj<</Length 1907 0 R/Filter/FlateDecode>>stream
+xÚ…’?oƒ0Åw>ÅÉ€‹]ƒñHÒ©R
+Ζ‡¦â_ ¨ýøõÕµQÔ
+‹áî÷žíw~÷(ö£pá*ïAyw[ T€:ÙN$‰@W¯ãاmÙuÄ«Ãz­ÞÈè‡áÒص5Î]-1ÕÔèvœ*JQEj|&~SQI‰°¶¶¼ÑCiÎýxîZì¤Ê H`O½ü²'à±¥C.­‘SZgFC~f·½Ð‚È?éÙš-Ö‚ÿ9³Åy/¹Îqqº\3=N¦æÀ.!^ÝùG'ý,š¾¾8×’zè\¾ñ’/NUº6Y²UàÃã~—CÞÆÂlgºÊM£Í
+endobj
+1907 0 obj
+295
+endobj
+1908 0 obj<</Type/Page/Parent 1703 0 R/Contents 1909 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F5 5 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+1909 0 obj<</Length 1910 0 R/Filter/FlateDecode>>stream
+xÚ…‘?oà ÅwŠ“Á(6ft§S¥Ôv¶,(%n*ÿ+`µ¿ «¢VH w¿÷Þ}D°;8…ûŽ]ôPGw[„C}r”s”Býºz³v,´ôauX¯ë÷@±@Å FÌS{#å»®ÆX¨åº™:Õ[T âU„zMLùoÊCDÄ­+o”9êóhÏCï;Ea„Ý“—«|–9:aÂ¥s¦ T7`ÊüØ Í‘ø“ž­ébÍþÏ™.Î | uŽ‹‰$„Z*;éÞÌ]B¼úóNñ%»±½¸R
+òÖ !ßlÉ7u+aЦ̷5Äð¸ßUP 'û)ÝÃvzh´ì:¥ <Ë~’­·ˆ½2æß\;ã ea7)ŸWò}GÎendstream
+endobj
+1910 0 obj
+290
+endobj
+1911 0 obj<</Type/Page/Parent 1703 0 R/Contents 1912 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F5 5 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+1912 0 obj<</Length 1913 0 R/Filter/FlateDecode>>stream
+xÚ…’?oƒ0Åw>ÅÉ
+†ÔN‹Zb×ÖsµLÕc+;£*BPE(j|ÊÿR‘”ÜÚÚòZêƒ:æÜwØÙT^„vËó¯x–X:b©5rJëLAI(oÀ”áØ Íƒô.=YÓÙšáÎtvžáK¨S\,\¨…4£êôØ%Ä«3ÿêl¾D;4×—RBÖèÞå›Ìù╦nкÈò
+|xÚmK(û£ùvc[Õ×J´­T^D7Š-|Túœþä'Xb+Þ|öJ^½o¿Žñendstream
+endobj
+1913 0 obj
+291
+endobj
+1914 0 obj<</Type/Page/Parent 1703 0 R/Contents 1915 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F5 5 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+1915 0 obj<</Length 1916 0 R/Filter/FlateDecode>>stream
+xÚ…‘Ënà E÷þŠY& S Ø˜¥Û<V•R›ì²±Râ¦ò«€Õ~~A8VE©XÌœ{î|F°;8…ÇŽmô$£‡
+endobj
+1916 0 obj
+288
+endobj
+1917 0 obj<</Type/Page/Parent 1703 0 R/Contents 1918 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F5 5 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+1918 0 obj<</Length 1919 0 R/Filter/FlateDecode>>stream
+xÚ…’Ko„ …÷þŠ»œYHAÑ¥í<VM¦êìfC¦ŒÆWÓþüBQÓš¦ÄŽß9À¹¼°ýlb·.Mp_wû‡òj;1ç(†òyõbLFŸWçõº|õóPaÄtÒ¢’®kkŒùZ¦ª¡‘­Ñ^!NE¨Ó„”ÿ¤DR‚¸µµå­ÔuëÍ­k]gWa{âù—€%–ŽXj¼Ò:SPŠ_`ÊܶÍQú'=ZÓÙš#üŸ3gxÊtŒ‹¥‘Ï4—fP­›B\Üù[g÷!š¾^\H Y­;Ÿo2çë&šú¶y¶/!„‡Ó±€¢»šwavT]¥DÓH¥áQ´ƒ¨Eè”!§_ùsìJl“ â²|v"OÁ'ÁŽ endstream
+endobj
+1919 0 obj
+291
+endobj
+1920 0 obj<</Type/Page/Parent 1703 0 R/Contents 1921 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F5 5 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+1921 0 obj<</Length 1922 0 R/Filter/FlateDecode>>stream
+xÚ…‘?oà ÅwŠ“ÁÔPlìÑ­“L•R›lYPJ\WþWÀj?~A8VE©î~ï¼û 0Dö``8uÁ¶`ül; c(þ¶z7fÜIS#+£š¾>®Žë5ÿð4õtGˆ:ú E-]×Ö(õµ\ÕS'{£½*Œ
+§ ûK9g1kkË…Ô'ÕŒ¦z×Ùð B‘}úr•; ©¥cšY#¯´Î”„êL¨{¡ÊîÒ³5Y¬Šþs&‹ó_Âã¢YìÃ-¥™T¯çÀ.!^ýùWgó-º±½¸’òV>ßtÉ7±«Íü ¢Ì·Bx>ì+¨†³ùöa{5ÔJtT^D?‰ÖY„N2Ý]?e¥~G Ï«y ~
+endobj
+1922 0 obj
+296
+endobj
+1923 0 obj<</Type/Page/Parent 1703 0 R/Contents 1924 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F5 5 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+1924 0 obj<</Length 1925 0 R/Filter/FlateDecode>>stream
+xÚ…’OSƒ0Åï|Š=¶"IiŽ(mOÎTHo½djŠ8ü3„Ñob(£ªÃa÷÷^6oy 0DöÁ°JÜ{j‚{Üm9`âl; c(ñ¼x1¦ß)“I£DÕ¨ãâ¸\ŠWÏRφq„¨cƒ,•ëÚ¥¾–êrlTk¯Šc§ÂÄiBÂ~RÂ#fmm9SÃIW½©ºÖu6"ˆPdŸ?ùèÚÒ1åÖÈ+­3­ ¸ꎽРñ?éÉšÌÖ Eÿ9“Ùy†/ÑNqQûhseFÝS`—¯îü­³ùM__\(i=t>ßõœ¯[,÷eyºÂÃa_@ÑÍ»´ƒíuWjÙ4Jð(ÛQÖÎ"tÊ‘¯üq¥•˜û…üò7ØÅ<Ÿb“Mendstream
+endobj
+1925 0 obj
+296
+endobj
+1926 0 obj<</Type/Page/Parent 1703 0 R/Contents 1927 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F5 5 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+1927 0 obj<</Length 1928 0 R/Filter/FlateDecode>>stream
+xÚ…‘?oƒ0Åw>ÅÉ€k;ÃH›©R
+dËb%¥â_QûñkË€Ú¨ieÉÃÝウ߽{°98…MçÆ{,¼‡4¡¸šNÈ9
+¡¸¬^µî÷R§•¬/§Õi½.ÞÈè1 QJÛ55Æ\-QåØÈVN
+ùendstream
+endobj
+1928 0 obj
+293
+endobj
+1929 0 obj<</Type/Page/Parent 1703 0 R/Contents 1930 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F5 5 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+1930 0 obj<</Length 1931 0 R/Filter/FlateDecode>>stream
+xÚ…’;oà …wÿŠ;&ƒ] Ø„ÑmK+¥6Ù² ”8©ü*`µ?¿P«¢VFîýÎÎå=€܇á>óëÐD"º[sÀ ÄÑu2Æ’ Äëìdm¿QöIµ•=ígûù\¼’2NQB=¹3²R¾ëj”†Z®«¡Q­5A•Æ^…‰×Ä„ý¦<„9N˜³uå¥2}îí¹k}g%"” wìéWl€.RÒ9Ð
+Ê0¡~Û Íþ'=Z“Éš%è?g29Oð%Ø1.ÊÓl¡ì [3v ñêÎ?:«OÙôõuÀ¥Rצ ù.¦|ýXyØhYäk1<î¶%”ÝÑ~Hw°­î*-›Fiϲdí-b¯ŒùΟQ_¢…qÜ| n(/Ñ—2‘†endstream
+endobj
+1931 0 obj
+294
+endobj
+1932 0 obj<</Type/Page/Parent 1703 0 R/Contents 1933 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F5 5 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+1933 0 obj<</Length 1934 0 R/Filter/FlateDecode>>stream
+xÚ…‘?oà ÅwŠ“ÁÔPlÌè6I»TJm²eA qSù_«ýøáXmµBb¸û½wðî#¸ƒ¸ÏàÐF"ºÛpÀ ÄÉu2ÆPâ¸x³vxVò¸_ì—Kñ  8MõÐÎÈZù®«Qj…®ÇVuÖU
+{&^ö›òæ1gëÊ+eú<ØsßùÎZD JÜ‹ç«|š;:¥Ü¥s& T7`BýØ Íÿ“ž¬ÉlÍPòŸ3™gø’éåiÈ´TvÔ™»„xõçõ—l‡æ:àJ)(Ó‡|ó9ßÌm”‡A«²Øˆáq·­ êOöSº‡mu_kÙ¶Jx‘Ý(o{eÌHrkë”Q”‡Õ°tÚÈkô dn×endstream
+endobj
+1934 0 obj
+290
+endobj
+1935 0 obj<</Type/Page/Parent 1703 0 R/Contents 1936 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F5 5 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+1936 0 obj<</Length 1937 0 R/Filter/FlateDecode>>stream
+xÚ…’;oà …wÿŠ;&ƒ©¡Ø„Ñm’ªC¥Ôv¶,(%®+¿
+X­úë Ŷš¨îýμ";0\'n›à¦®¶0ƒâd; c(âiñlLßV¦uõ!‹ÃrY¼x”z4Œ#Dº×¢”®kk”úZªÊ¡‘­Ñ^ÆN…‰Ó„„SÂ#fmmy-õQU½©ºÖu6E¡Èž{^²; +KÇ”[#¯´Î”„ü˜P·íD3Äÿ¤Gk2[3ýçLfçž’ã¢<öÉfÒ ªÕc`SˆwþÖÙ¼‹¦¯/Î¥„´ÖÏw5çëÞ•ûÖYº- „Ûý.‡¼;™7a¶S]©DÓH¥áA´ƒ¨Eè”!#_ù³Ä•(ÁÖ'úý3Øwy >Šî“endstream
+endobj
+1937 0 obj
+297
+endobj
+1938 0 obj<</Type/Page/Parent 1703 0 R/Contents 1939 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F5 5 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+1939 0 obj<</Length 1940 0 R/Filter/FlateDecode>>stream
+xÚ…‘KSà …÷ùwÙ.‚€$$Ëh[WŽ5¡»n˜Jc¼2úóI3Úñ1Ì°¸÷;繯ìNá:…C݈èj“á Ž®“rŽRO‹gk‡‡ÁžúÎìûåR¼Ž.N0bžÛY+ßu5ÆB­ÐõتΚ J€¯"ÔkbÊ¿S"9AÜÙºòJ™ƒ>}Nöµˆ0ÂîÑóUÞË°Ü¥s¦ T?À”ù±gš£üOz²¦³5Gø?g:;Ïð9Ö).–'!ÖRÙQwf
+ìâÅŸ¿tÖﲚˀ+¥ hLòÍæ|S·Ô< Z•ÅF@ ·»mU´oÒ=l«ûZ˶UÚÀ½ìFÙx‹Ø+cNñ/‹gœ¡,l‡ói)Ñg¦Íendstream
+endobj
+1940 0 obj
+290
+endobj
+1941 0 obj<</Type/Page/Parent 1703 0 R/Contents 1942 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F5 5 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+1942 0 obj<</Length 1943 0 R/Filter/FlateDecode>>stream
+xÚ…’½nƒ0…wžâŽÉ€‹Áx¤MÒ©²eA©CSñWÛ¨}üÚ5 E­°îýα}®ß= ù0„±]çÖ»/½»Ì ¼˜NÌŠ¡|Ù¼j=d½Ò§Íi»-ßDäG¢:ªª¶kj”ºZ*ë±VNÆV…‰Õø„ý¦,„9FÌØšòN¨³¼úÚw¶³/½
+endobj
+1943 0 obj
+291
+endobj
+1944 0 obj<</Type/Page/Parent 1703 0 R/Contents 1945 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F5 5 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+1945 0 obj<</Length 1946 0 R/Filter/FlateDecode>>stream
+xÚ…‘?oƒ0Åw>ÅÉ€‹]ƒñH›¤S%
+ΖÅJ ¥â_Qûñk˵QÔÊ’‡»ß{g¿û0Dö``î8wÁƒî0QÙNÂJ@¼nÞŒsÝô¦:mNÛ­x÷õXGˆ:ì8ÉZ¹®­Qêk™®çNõfòª0v*Lœ&$ì7å Ì1bÖÖ–wj:ëf4ÍлÎ^Šì›×«xšZ:¦Üy¥u& ”7`BÝØ Íÿ“^¬ÉjÍPôŸ3YWø’êå±OµPfÖý´v ñêÏ?:û/ÙíuÀ¥RµÓàóM×|»SîíŠì „Çc^B9TæSÚ‡åz¨µì:¥'x–ý,[g:eÈHt{ï”Q”úå0¾ìä%øÅSÝendstream
+endobj
+1946 0 obj
+291
+endobj
+1947 0 obj<</Type/Page/Parent 1703 0 R/Contents 1948 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F5 5 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+1948 0 obj<</Length 1949 0 R/Filter/FlateDecode>>stream
+xÚ…’OO„0Åï|Š9î¨m-Žèîz2AèÞöÒ¬]\Ã?K‰~|[ D‰Ñ@8ÌüÞ›ö ol·±{ÏMp'‚›C
+„ƒ¸ØNÌ9ŠA<o^ŒéóÑœ6§íV¼z†y&Œ0bŽ9²R®kkŒùZ¦«±Q­¼*BœŠP§ )ÿI9ˆ¤qkkË;5œõµ7×®u½0ÂöÀ˧x
+lquçoý‡lúzp©dõÐù|“%_·ÐÔÚÙA@÷Ǽ„²»˜wi–ë®Ò²i”àQ¶£¬Eè”!§_ù'Ø•˜•ø…¬þ»§à8:^endstream
+endobj
+1949 0 obj
+290
+endobj
+1950 0 obj<</Type/Page/Parent 1703 0 R/Contents 1951 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F5 5 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+1951 0 obj<</Length 1952 0 R/Filter/FlateDecode>>stream
+xÚ…‘?oà ÅwŠ“Á(6ft›¤S¥Ô&[”7•ÿlµ¿ «¢TH w¿÷Þ}F°;8…ÇMô$£‡
+ìâÕŸuÖߪéëë€K­!¯mòÍæ|S·Q­Š|#!†çݶ„²; _Ê=lkºÊ¨¦ÑÆ«jGU{‹Ø+cNñ­­3ÎPV“‘i#oÑz•èendstream
+endobj
+1952 0 obj
+289
+endobj
+1953 0 obj<</Type/Page/Parent 1703 0 R/Contents 1954 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F5 5 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+1954 0 obj<</Length 1955 0 R/Filter/FlateDecode>>stream
+xÚ…’?oƒ0Åw>ÅÉ
+ΖQ‡¦â_Qûñ뫵Q”
+‹áî÷žíwþð(ûQ¸p•÷ ½»]T€<ÙN$D|]½ÓgªìÚV•æ¸:®×òÝ‘Ü‘~HŽäa(*…][ãÜÕR]jÍàT!PŠ*ÊPã3ñ—Bˆ&4ÖÖ–7j(õ¹7ç®ÅÎVz$ öØË/{[:ä‰5rJëÌ@+ȯÀŒã¶3-‚ä&=Y³ÅZä?g¶8/ðìOBl¦Ì¨Ûa
+lñâο:Û¯¢éëË€s¥ ­‡Îå/ùâX·Ñ&Kw|x<ìsÈ»“ù,ìÁöº«tÑ4Jð\´cQ£…J_°Ÿüc†%Nây Wß‚Ë‹÷ ùo’/endstream
+endobj
+1955 0 obj
+294
+endobj
+1956 0 obj<</Type/Page/Parent 1703 0 R/Contents 1957 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F5 5 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+1957 0 obj<</Length 1958 0 R/Filter/FlateDecode>>stream
+xÚ…‘?oà ÅwŠ“Á6ft›¤S¥Ô&[”7•ÿcµ¿ «šVH w¿÷Þ½G°;8…U
+Ç&º—ÑÝV
+endobj
+1958 0 obj
+292
+endobj
+1959 0 obj<</Type/Page/Parent 1703 0 R/Contents 1960 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F5 5 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+1960 0 obj<</Length 1961 0 R/Filter/FlateDecode>>stream
+xÚ…’Írƒ …÷>Å]& ©PYÚ&vÕ™TÉ.'!ÖŽœöñ Å8m&ÓŽŒ‹{¿s€sy0DöÃpŸ¸uì‚Üå0q¶„1”€8­^KiòF¶§Ãê°^‹7R†q„¨÷ºª¥ëÚ¥¾–©zêdo´WÅ€±Saâ4!a¿)aŽ³¶¶¼‘ú¨šÑ4Cï:[D(²§^~ÅÐÔÒ1åÖÈ+­3%¡¼궽Рñ?éÙš,Ö Eÿ9“Åy/¹ÎqQû\ i&Õë9°KˆWwþÑÙ~VÝØ^\J Y«Ÿoºäë¦ÊýF›"Ë„ð¸ß•PgóQÙƒíÔP«ªë¤Òð\õSÕ:‹Ð)CF¾óO©+Q‚ý8n=;“—à ¶¥endstream
+endobj
+1961 0 obj
+293
+endobj
+1962 0 obj<</Type/Page/Parent 1703 0 R/Contents 1963 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F5 5 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+1963 0 obj<</Length 1964 0 R/Filter/FlateDecode>>stream
+xÚ…‘?oà ÅwŠ“Á(6ft›¤S¥Ô&[ä7•ÿlµ¿ «¢VH w¿÷Þ}D°;8…ûª6zÑÝN
+endobj
+1964 0 obj
+290
+endobj
+1965 0 obj<</Type/Page/Parent 1703 0 R/Contents 1966 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F5 5 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+1966 0 obj<</Length 1967 0 R/Filter/FlateDecode>>stream
+xÚ…’;oà …wÿŠ;&ƒ)PlìÑm’N•R?¶,(!n*¿
+XíÏ/Ûj£¨ˆáÞïàÀ{@
+endobj
+1967 0 obj
+292
+endobj
+1968 0 obj<</Type/Page/Parent 1703 0 R/Contents 1969 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F5 5 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+1969 0 obj<</Length 1970 0 R/Filter/FlateDecode>>stream
+xÚ…‘?oà ÅwŠ“Á(6öè6I§J©M”%‹•וÿ°Ú_¶ÕFQ+$†»ß{ï>ØœÂ} ç6xÁÝ.ÂA\l'æÅ ^WoÆ GUyZÖkñî)æ©0ˆ9ê ËJº®­1æk™ªÆVvF{U„8¡NRþ›rI âÖÖ–7RŸU=˜ºï\g+Œ°}òråOÀKG,µF^i)( Å ˜27v¦9Jÿ¤'kºXs„ÿs¦‹óÏ¡Nq±4ò¡æÒŒªÓS`sˆWþÑÙ~•íÐ\\H Y£{Ÿo²äÛ•¦~Ð&ÏvBx<ì (ú‹ù,íÃöª¯TÙ¶Rix.»±lœEè”!§øæÚg(ñ»Iø´’—àJ endstream
+endobj
+1970 0 obj
+291
+endobj
+1971 0 obj<</Type/Page/Parent 1703 0 R/Contents 1972 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F5 5 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+1972 0 obj<</Length 1973 0 R/Filter/FlateDecode>>stream
+xÚ…’Áoƒ ÆïþïØdÂPäh×v§%Ò[/¤Rã¢âP³ýùƒaÍÖ4[$Þû}ð=Þ ‘ý0<&nÛ`#‚‡=Ì@\l'a % ÊUÝ÷YYn´n”ìN«Óz-Þ<I=Æ¢Ž<²R®kk”úZfª©UÝ8xU ;&Nö›ræ1kkË[5œMݵî\g'‚EöØË/šZ:¦Üy¥u&`w`BݶWš!þ'=[“Åš¡è?g²8/ð5Ø9.Êcl®ÆÉtÃØ5Ä›;ÿèì>eÛ7·JAÖ Úç›.ùº±r¿Ñ6ÏöBx:
+(ôeüö`£+#ÛV™^d7ÉÆY„N2òšºb„ý@î¾;–×à J„‘Žendstream
+endobj
+1973 0 obj
+295
+endobj
+1974 0 obj<</Type/Page/Parent 1703 0 R/Contents 1975 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F5 5 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+1975 0 obj<</Length 1976 0 R/Filter/FlateDecode>>stream
+xÚ…‘?oà ÅwŠ“Á(6ftš¤S¥Ôv¶,(&–+ÿ+Øj?~A8V5­î~ï¼{`{p
+1œÛ`S{„Cq±˜sCQ®êaHËrÓ÷’9­NëuñæQæÑ0ˆ9ôhd¥\×ÖóµTWS«ºÑxU„8¡NRþ“rqkkË[eκƺï\gWaûîåÊž%–Ž˜°F^i)hù/0enì•æHüIÏÖt±æÿçLç¾&;ÇÅDä“ÍÔ8éÎÌ]C¼ùó·ÎîS¶Csp®¤é}¾É’ol÷*ü m–î áéxÈ!ï/ㇴ;è¾Ò²m•6ð"»I6Î"tÊS|÷Œ3”ø%bÞËkð
+endobj
+1976 0 obj
+294
+endobj
+1977 0 obj<</Type/Page/Parent 1703 0 R/Contents 1978 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F5 5 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+1978 0 obj<</Length 1979 0 R/Filter/FlateDecode>>stream
+xÚ…’½nƒ0FwžâŽÉ€k»Ç#-I§Jip¶,Vp5 öñk×€Ú(Jb¸÷ÜÏö1l±{Ïuð$ƒ‡
+endobj
+1979 0 obj
+291
+endobj
+1980 0 obj<</Type/Page/Parent 1703 0 R/Contents 1981 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F5 5 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+1981 0 obj<</Length 1982 0 R/Filter/FlateDecode>>stream
+xÚ…‘?oà ÅwŠ“Á(6ft›¤êP)- Š‰åÊÿ
+¶Ú_¶ÕFM+$†»ß{ïÞØœÂ} ç&xȃ»
+endobj
+1982 0 obj
+294
+endobj
+1983 0 obj<</Type/Page/Parent 1703 0 R/Contents 1984 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F5 5 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+1984 0 obj<</Length 1985 0 R/Filter/FlateDecode>>stream
+xÚ…’½nà …w?Å“Á(6at›¤êP)µ- Š‰åÊ[íãŠmµQÔÊÈýß9À¹¼°ýÜÇn›à!îö‡üb;1ç(†¼XU}ŸÅs;¨RisZÖëụ̈ͣa„sèÑÈR¹®­1æk‰.ÇFµƒñªq*B&¤ü7å "âÖÖ–·ÊœuÕU׺Î.0ÂöÜË/}¶±tÄ„5òJëLA+ÈnÀ”¹mgš#ñ'=YÓÅš#üŸ3]œxNvŠ‹‰È'›ªaÔ­™›C¼ºóÎîS6}}p¦$µé|¾›%_7Wá7Ú¦É>‡‡ ²î2|H{°ƒîJ-›ÆN^d;ÊÚY„Nrú¿ ®Äp4äöc°sy ¾
+endobj
+1985 0 obj
+296
+endobj
+1986 0 obj<</Type/Page/Parent 1703 0 R/Contents 1987 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F5 5 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+1987 0 obj<</Length 1988 0 R/Filter/FlateDecode>>stream
+xÚ…‘?oà ÅwŠ“Á6ft›¤S¥Ô&[Ë•ÿÛj?~A8V5­î~ï¼{`{p
+›ÎMð(ƒ‡½
+?h›¥{ !<9äÝeüPöaÓ•F56¼¨vRµ³2äßY<ã %~;b3/å5ørFñendstream
+endobj
+1988 0 obj
+292
+endobj
+1989 0 obj<</Type/Page/Parent 1703 0 R/Contents 1990 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F5 5 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+1990 0 obj<</Length 1991 0 R/Filter/FlateDecode>>stream
+xÚ…’½nà …w?Å“Á(6at›¤S¥Ôv¶,(&–+ÿlµ_(¶ÕFQ*#÷~ç
+endobj
+1991 0 obj
+294
+endobj
+1992 0 obj<</Type/Page/Parent 1703 0 R/Contents 1993 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F5 5 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+1993 0 obj<</Length 1994 0 R/Filter/FlateDecode>>stream
+xÚ…‘?oƒ0Åw>ÅÉ€k»ã‘6I§J)-‹DÅ¿Ú öã×–µQ•Ê’‡»ß{g¿û`{p
+1\Úੇâj;1ç(†¢ÜÔÖe¦LßLcÝwçÍy»-Þ=Ì<F1ŸŒ¬”ëÚc¾–êjjU7¯Š€§"ÔiBÊS"‚ nmmy§ÌE׃ì:û"ÀÛ—¯Wö,±tÄ„5òJëLA+Èÿ€)scš#q—ž­éjÍþÏ™®Î+¼d;ÇÅDä³ÍÔ8éÎÌ-!ÞüùGgÿ%Û¡¹ 8W
+ÒÆô>ßdÍ7¶›~Ð.K„ð|:æ÷×ñSÚ‡u_iÙ¶Jx•Ý$g:eÈ)¾·}ÆJüŠD4oæ-øƒ<”endstream
+endobj
+1994 0 obj
+294
+endobj
+1995 0 obj<</Type/Page/Parent 1703 0 R/Contents 1996 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F5 5 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+1996 0 obj<</Length 1997 0 R/Filter/FlateDecode>>stream
+xÚ…’Ënƒ0E÷|Å,“;ã%m’®*¥àì²AÁAT¼jƒÚϯ§&¨úˆÅ̹wì;¼zBûØÄøž[ï^zw{„ƒ¼ØNÌyƒ,Wõ0¤e™)Ó7ÓX÷9­Nëµ|q4s´…CúhŠJa×ÖsµTWS«ºÑ8U„ ŠPÔø”§"‚ÜÚÚòV™³®œŒô ´G_>Ù#°ÄÒÖÈ)­3­ ÿ¦ Ç^iˆ?éÙš.Ö<ÿs¦‹ó_Ããb"ráfjœtgæÀ®!ÞÜùKg÷^´Csp®¤é]¾É’/®V¸AÛ,ÝKðááxÈ!ï/ã[avÐ}¥‹¶UÚÀSÑMEƒ>*}N?ó1–6IâÖñëß`óì}
+endobj
+1997 0 obj
+296
+endobj
+1998 0 obj<</Type/Page/Parent 1703 0 R/Contents 1999 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F5 5 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+1999 0 obj<</Length 2000 0 R/Filter/FlateDecode>>stream
+xÚ…‘?oƒ0Åw>ÅÉ€k»ã‘6I§J)-‹DÅ¿Ú öã×–µQ•Ê’‡»ß{g¿û`{p
+1\Úੇâj;1ç(†¢ÜÔÖe®©åØëóæ¼Ýïžež #Œ˜cOFVÊum1_Ku5µªWE@ˆSê4!å¿)A·¶¶¼Sæ¢ëa¬ûÎuöE€¶_¯ìXbéˆ kä•Ö™‚VÿSæÆ.4Gâ.=[ÓÕš#üŸ3]Wx‰vŽ‹‰ÈG›©qÒ™[B¼ùóÎþK¶Csp®¤é}¾Éšol+ü ]–
+áùtÌ!ï¯ã§´;ê¾Ò²m•6ð*»I6Î"tÊS|gùŒ3”ø >/æ-øÁÔ“}endstream
+endobj
+2000 0 obj
+294
+endobj
+2001 0 obj<</Type/Page/Parent 1703 0 R/Contents 2002 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F5 5 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+2002 0 obj<</Length 2003 0 R/Filter/FlateDecode>>stream
+xÚ…’½nà …w?Å“ÁÔPlÌè6I§J©M¶,(&–+ÿlµ_(±ÕFQ*#÷~ç
+endobj
+2003 0 obj
+293
+endobj
+2004 0 obj<</Type/Page/Parent 1703 0 R/Contents 2005 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F5 5 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+2005 0 obj<</Length 2006 0 R/Filter/FlateDecode>>stream
+xÚ…‘Ënà E÷þŠY& SC±1K·Iºª”Úd— Š‰åʯ‚­öó ¶ڨ!±˜9÷Üy 0Dö``Áƒî0qµ„1”€(7õ0deYŒºî*sÞœ·[ñêIêÉ0ŽuäÉÈJ¹®­Qêk™®¦Vu£ñª0v*Lœ&$ì;å Ì1bÖÖ–wÊ\t=Œuß¹Î^Šì³×+šZ:¦Üy¥u& ?À„º± Íÿ“ž­ÉjÍPôŸ3YWx vŽ‹òØ›«qÒ™[B¼ùó—ÎþC¶Csp¡dé}¾éšob×Êý ]ž„ðx:Pô×ñ]Ú‡u_iÙ¶Jx–Ý$g:eÈHôëê)£(õûá|^ËKð Óœ‘ñendstream
+endobj
+2006 0 obj
+293
+endobj
+2007 0 obj<</Type/Page/Parent 1703 0 R/Contents 2008 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F5 5 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+2008 0 obj<</Length 2009 0 R/Filter/FlateDecode>>stream
+xÚ…’½nà …w?Å“Á(6atë¤S¥Ô&[”Ë•ÿ
+¶ÚÇ/Çj£¨•‘‡{¿s€sy`÷¸Oý:µÑƒŒîvyv”s”‚|]ÕëQË^Ö­>®Žëµ| $ dœ`Ä<y°ªÒ¾ëjŒ…Zfª©ÕÝhƒ*B¼ŠP¯‰)ÿMyˆ‚¸³uå\Û“©‡±î;ßÙÊ#쎽üŠ'`G'L8£ tÎŒ†òL™ßöBs$þ¤gkºXs„ÿs¦‹ó_‚ãb" ÁzœLgçÀ.!^ÝùGgû©Ú¡¹¸Ô²Æö!ßÍ’¯«åE¶“Ããa_BÙŸÇå¶7}eTÛjcáYu“j¼Eì•1§ßùŒ}a†’0‘›ÁÍå%úæ·‘Àendstream
+endobj
+2009 0 obj
+295
+endobj
+2010 0 obj<</Type/Page/Parent 1703 0 R/Contents 2011 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F5 5 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+2011 0 obj<</Length 2012 0 R/Filter/FlateDecode>>stream
+xÚ…‘?oƒ0Åw>ÅÉ
+ΖÅJDÅ¿Ú öã×–µQ”Ê’‡»ß{g¿û 0 {00 œÛàI{˜¸ØNÂX”€xßÔëFê´9m·âÃSÔSaŒ"ꨣ‘•r][£Ô×2]M­êFãU1`ìT˜8MHØ_ÊA˜ãˆY[[Ε9ëzë¾sP„ì“׫xšZ:¦Üy¥u& ”7`BÝØ…f¿KÏÖdµfúÏ™¬Î+¼„:ÇEyìC-Ô8éÎÌ-!^ýùWg÷-Û¡¹¸T
+²Æô>ßtÍ7±+å~P^d{!<%”ýeü’öaÝWZ¶­Ò^e7ÉÆY„N2‚n®&鲌𼓷ào3ŽÓendstream
+endobj
+2012 0 obj
+289
+endobj
+2013 0 obj<</Type/Page/Parent 1703 0 R/Contents 2014 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F5 5 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+2014 0 obj<</Length 2015 0 R/Filter/FlateDecode>>stream
+xÚ…’½nà …w?Å“Á(6ñè6q§J©ílYhB,Wþ+`µ_(¶ÕFU*ýß9À÷€
+endobj
+2015 0 obj
+296
+endobj
+2016 0 obj<</Type/Page/Parent 1703 0 R/Contents 2017 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F5 5 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+2017 0 obj<</Length 2018 0 R/Filter/FlateDecode>>stream
+xÚ…‘?oà ÅwŠ“Á6ft›¤K+¥6Ù² ”¸®ü¯ØVûñ ¶Ú(j…Äp÷{ïàÝG@
+endobj
+2018 0 obj
+291
+endobj
+2019 0 obj<</Type/Page/Parent 1703 0 R/Contents 2020 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F5 5 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+2020 0 obj<</Length 2021 0 R/Filter/FlateDecode>>stream
+xÚ…’½nà …w?Å“ÁÔlÂè6I§V©M¶,(%–+ÿl¥_(¶ÕDQ+#÷~ç
+endobj
+2021 0 obj
+290
+endobj
+2022 0 obj<</Type/Page/Parent 1703 0 R/Contents 2023 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F5 5 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+2023 0 obj<</Length 2024 0 R/Filter/FlateDecode>>stream
+xÚ…‘?oƒ0Åw>ÅÉ€k»ã‘6I§Jœ-‹•:ˆŠ5 öã×–µQ”Ê’‡»ß{g¿û `{p
+1œ›àI„ƒ¼ØNÌ9ŠA¾oª¾Ï:3ž6§íV~x†y&Œ0bŽ9ªÔ®kkŒùZjÊ©Ñí8xU„8¡NRþ—rqkkË;=œMÕU׺Î^aûàõÊ_€%–Ž˜°F^i) Å ˜27v¡9wéÙš®Öáÿœéê¼ÂK¤s\LD>Ò\“i‡9°%Ä«?ÿêì¿UÓ××ZCZÏ7YóíB…´ËÓƒ„žYEw¿”}XfºÒ¨¦Ñf€WÕNªv¡S†œâKgq²l†àhÞÈ[ðÉB{endstream
+endobj
+2024 0 obj
+288
+endobj
+2025 0 obj<</Type/Page/Parent 1703 0 R/Contents 2026 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F5 5 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+2026 0 obj<</Length 2027 0 R/Filter/FlateDecode>>stream
+xÚ…’½nà …w?Å“Á6ft›¤S¥Ô&[”Ë•ÿŠmµ_(¶ÕFQ+#÷~ç
+endobj
+2027 0 obj
+290
+endobj
+2028 0 obj<</Type/Page/Parent 1703 0 R/Contents 2029 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F5 5 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+2029 0 obj<</Length 2030 0 R/Filter/FlateDecode>>stream
+xÚ…‘1oƒ0…w~ÅÉ€k»ÃHKÒ©R
+ΖÅJD…PûókË€Ú¨MeÉÃÝ÷ÞÙïÞØœÂ} g<ˆànŸá .¶sŽb¯›ºïE­•èr9ªÓæ´ÝŠ7O2O†FÌ‘ÇAVÊum1_ËL5iÕŽƒWE@ˆSê4!å?)‘” nmm9WÃÙÔýXw­ëìD€¶Ï^¯â Xb鈥ÖÈ+­3£ ü¦Ì]hŽÒ›ôlMWkŽðÎtu^á%Ø9.–F>ØB“i‡9°%Ä«?ëì>¥î›ë€K¥ k†Î盬ùÆv­©”Ù^@ÇC ew?¤}ØÁt•‘Z+3À³l'Ù8‹Ð)CNñŸ«gq²ì‡`>ïå%øÖ‘Óendstream
+endobj
+2030 0 obj
+292
+endobj
+2031 0 obj<</Type/Page/Parent 1703 0 R/Contents 2032 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F5 5 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+2032 0 obj<</Length 2033 0 R/Filter/FlateDecode>>stream
+xÚ…’½nƒ0…wžâŽÉ€kƒñH›¤S¥ˆ²dA©ƒ¨ø« j¿v ¨EU+,†{¿slŸë7
+ÜDÖïÀŒäÙûûŽDendstream
+endobj
+2033 0 obj
+293
+endobj
+2034 0 obj<</Type/Page/Parent 1703 0 R/Contents 2035 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F5 5 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+2035 0 obj<</Length 2036 0 R/Filter/FlateDecode>>stream
+xÚ…‘Ënà E÷þŠY& S Ø˜¥›GW•Ò˜ì²A ±RùU°Õ~~AØVE©XÌœ{î|F°;8…çNuô"£§­
+endobj
+2036 0 obj
+288
+endobj
+2037 0 obj<</Type/Page/Parent 1703 0 R/Contents 2038 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F5 5 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+2038 0 obj<</Length 2039 0 R/Filter/FlateDecode>>stream
+xÚ…’»nƒ0†wžâŒÉ€k»Ç#Í¥S¥œ- "Qq« j¿vMPõ"Ã9ßùmæ5 €íCà>voÑ2¸; di;1ç(yY ÃeÛweS£9¯Îëµ|ñ ó`aÄx2y¥\×ÖóµDWS«ºÑø©qS„º™òˆ ˆÛX[Þ)Sèzë¾s½ 0Âv×Ë'}¶±tÄ„ ò“6™‚Vý
+endobj
+2039 0 obj
+294
+endobj
+2040 0 obj<</Type/Page/Parent 1703 0 R/Contents 2041 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F5 5 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+2041 0 obj<</Length 2042 0 R/Filter/FlateDecode>>stream
+xÚ…‘1oƒ0…w~ÅÉ
+endobj
+2042 0 obj
+289
+endobj
+2043 0 obj<</Type/Page/Parent 1703 0 R/Contents 2044 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F5 5 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+2044 0 obj<</Length 2045 0 R/Filter/FlateDecode>>stream
+xÚ…’½nƒ0…wžâŽÉ€‹ÁñHÒ©R
+dËb%¢â¯6¨}üÚ5 E­°îýα}®ß= ù0„±]—Æ{,¼‡Ì ¸™NÌŠ¡¸núþš6ÕpÞœ·ÛâÍ1Ô1~ j™“¥´]S£ÔÕUŽlíT`lU˜XOØoÊB˜cÄŒ­)量¨ªª®µ´ð˜/¿ìèÎÐåÆÈ)3%!¿j·i†øŸôdMk†‚ÿœÉâ¼Às¤S\”G.ÒL£jõØâêÎ?:é§húzp.%$µî\¾»%_;Pî6ÚgÉ¡
+endobj
+2045 0 obj
+291
+endobj
+2046 0 obj<</Type/Page/Parent 1703 0 R/Contents 2047 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F5 5 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+2047 0 obj<</Length 2048 0 R/Filter/FlateDecode>>stream
+xÚ…‘?oƒ0Åw>ÅÉ
+endobj
+2048 0 obj
+293
+endobj
+2049 0 obj<</Type/Page/Parent 1703 0 R/Contents 2050 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F5 5 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+2050 0 obj<</Length 2051 0 R/Filter/FlateDecode>>stream
+xÚ…’½nƒ0…wžâŽÉ€‹]ƒÃH“Ð)R
+dËbCiù« j¿v ¨‰ªTX ÷~çØ>×O³òÆyÊœ‡8Ì »èNÀ
+ +V}_ÄU[¸|Åöµ«rq^×ëìÍòÔò®ï!jø“â¥0]]£ÔÖ"YŽheU>`lT˜KØ5e bÄ´­.ï„ÊeÕUךÎ>s<äéÃ/¿äèFÓ> µ‘UjgR@úL¨Ùv¦
+ïÒ“5Y¬òþs&‹óÏñNqÑзñ&be«¦Àæoîü«³ÿâM_ßœ
+Q­:›ïfÉ× 7´í’(ÎÀ…íé˜BÚ]†O®v”])yÓ©àÀÛ‘×ÆÂ5J—‘Ÿü1¦¦¦ì<î<=›ç~š–endstream
+endobj
+2051 0 obj
+299
+endobj
+2052 0 obj<</Type/Page/Parent 1703 0 R/Contents 2053 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F5 5 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+2053 0 obj<</Length 2054 0 R/Filter/FlateDecode>>stream
+xÚ…‘Ënƒ0E÷|Å,“Ôv ÆKÚ„®ª¦@vÙXÁAT¼jƒÚϯ]j£>dÉ‹™sïØw^= È ŒÀmçÖ»+¼›”fP\L'b,ˆ (7ÃP¦uW> cÝw§Íi»-^Ié‡( –<jQIÛ55J]-QÕÔÊnÔNÆV…‰Õø„}§,„9˜±5åÔgU¶}á¡
+endobj
+2054 0 obj
+292
+endobj
+2055 0 obj<</Type/Page/Parent 1703 0 R/Contents 2056 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F5 5 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+2056 0 obj<</Length 2057 0 R/Filter/FlateDecode>>stream
+xÚ…’½nà …w?Å“ÁÔPlÂè6IÕ!Rj“- Š‰•Ö[íãŠmµQÔÊÈýß9À¹¼"ûa¸OÜ:ÕÁƒî¶0q¶„1”€(]W<›Ôoª8.ŽË¥xõõ\Gˆ:î`d©\×Ö(õµT—C­šÞxU ;&Nö›ræ1kkËkeNúÒõ—¶q"ÙCÏ¿ì èÊÒ1åÖÈ+­3­ ¿ê¶h†øŸôhMfk†¢ÿœÉì<ÃS¬c\”Ç>ÖLõƒnÌØâÕt6Ÿ²îªë€s¥ ­Lëó]Íùº¡r¿Ñ:K·Bx<ìsÈÛsÿ!íÁöº-µ¬k¥ ìd3ÈÊY„N2ò?Ɖ«Qœ ì'rã)Ø©¼_©<endstream
+endobj
+2057 0 obj
+295
+endobj
+2058 0 obj<</Type/Page/Parent 1703 0 R/Contents 2059 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F5 5 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+2059 0 obj<</Length 2060 0 R/Filter/FlateDecode>>stream
+xÚ…‘?oà ÅwŠ“Á(6ftë¤S¤4v¶,(ÁVZÿ+Øj?~AØVU©î~ï¼û`{p
+1œ›à©¶‡¢´˜sCqYõýe'õ{¦J9Öƒ9­NëuñæYæÙ0ˆ9öhd¥\×ÖóµTWc£ÚÁxU„8¡NRþ›rqkkË™2g}í‡k׺Φ0ÂöáËux–X:bÂy¥u¦ äÀ”¹±3Í‘¸KOÖt±æÿçL瞣âb"òÑÔ0êÖLÍ!ÞüùGgó%›¾¾ 8W
+ÒÚt>ßdÉ7¶‹~PvH·„ð|Üçwåð)íÃöº«´l¥ ìd;ÊÚY„NrŠï,ŸÅɼ!Bø´™×à^º“°endstream
+endobj
+2060 0 obj
+294
+endobj
+2061 0 obj<</Type/Page/Parent 1703 0 R/Contents 2062 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F5 5 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+2062 0 obj<</Length 2063 0 R/Filter/FlateDecode>>stream
+xÚ…’Os‚0Åï|Š=ê4I!GZµ'§âÍKF#cË¿˜öã71ÈTÇi†Ãîï½MÞòÀö!ð»w_O2xX äÑvbÎQ ò0kÛÃZ™×¶?5õn¶›Ïå»'™'Ã#æÈm§
+íº¶Æ˜¯¥¦*]÷WE@ˆSê4!å×”ƒˆ ˆ[[[^ènoNçÁ®³”FØ{úd/ÀKGLX#¯´ÎŒ†üL™{¡9Ò£5¬9Âÿ9ÓÉy‚/ÁŽq1ù`3ݦîÆÀ.!ÞÜùWgù­ª¶¼ 8×Ò²k|¾É”¯[«ðƒYº’Âóv“CÞû/e¶1MaTUiÓÁZÕƒ*Eè”!§çü I\a†"¿‘»?ƒÝË[ðNè’'endstream
+endobj
+2063 0 obj
+295
+endobj
+2064 0 obj<</Type/Page/Parent 1703 0 R/Contents 2065 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F5 5 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+2065 0 obj<</Length 2066 0 R/Filter/FlateDecode>>stream
+xÚ…‘?oà ÅwŠ“ÁÔPlÂè6q§ª©M¶,(&V*ÿ+Øj?~AØVE©î~ï¼û 0Dö``85Á“2˜8ÛNÂJ@”«¾/ßzÕfåqu\¯Å‡§¨§Â8BÔQ#+庶F©¯¥ºÕÆ«bÀØ©0qš°¿”ƒ0LjY[[Þ*sÒ—~¸t­ëìD¡È>y¹ò KÇ”[#¯´Î´‚âL¨;Ó ñ»ôdMk†¢ÿœÉâ¼Às¨S\”Ç>Ô\ £nÍØâÕŸuvß²éëë€ ¥ ­MçóÝ,ù&v¥ÜÚæi& „çþ€¢;_Ò>l¯»J˦QÚÀ«lGY;‹Ð)CF¢›k§ÉfÞ Æ|ÚÉ{ð1ÎŽ¤endstream
+endobj
+2066 0 obj
+290
+endobj
+2067 0 obj<</Type/Page/Parent 1703 0 R/Contents 2068 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F5 5 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+2068 0 obj<</Length 2069 0 R/Filter/FlateDecode>>stream
+xÚ…’Írƒ …÷>Å]& ©P”°´MìªÓTÉ.&!Žÿ
+:íãŠ:m&ÓŽŒ‹{¿s€sy0DöÃpŸ¸uj‚Üe0q±„1”€8¯úþüÒ«6«ju\×kñæ9ê¹0ŽuÜÁÈR¹®­Qêk©.ÇFµƒñª0v*Lœ&$ì7å Ì1bÖÖ–·ÊœtÕU׺ÎNŠì¡—_þtcé˜rkä•Ö™€VPÜ€ uÛÎ4CüOz²&‹5CÑÎdq^à9Ö).Êck®†Q·f
+lñêÎ?:»OÙôõuÀ…RÖ¦óùn–|ÝP¹ßh›§™€ûŠî2|H{°½îJ-›Fiϲeí,B§ ùÎßNÉÕ(Næ‰Üx
+v*¯Á pAendstream
+endobj
+2069 0 obj
+293
+endobj
+2070 0 obj<</Type/Page/Parent 1703 0 R/Contents 2071 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F5 5 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+2071 0 obj<</Length 2072 0 R/Filter/FlateDecode>>stream
+xÚ…‘?oà ÅwŠ“Á(6at›¤SÕÔ&[”+•ÿlµ¿ l«¢TH w¿÷Þ}F°;8…ÇŽuô$£‡­
+endobj
+2072 0 obj
+288
+endobj
+2073 0 obj<</Type/Page/Parent 1703 0 R/Contents 2074 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F5 5 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+2074 0 obj<</Length 2075 0 R/Filter/FlateDecode>>stream
+xÚ…’½nà …w?Å“Á(6at›¤K+¹6Ù² „¸©üWl«}üB±­&ŠZy¸÷;8—€
+endobj
+2075 0 obj
+295
+endobj
+2076 0 obj<</Type/Page/Parent 1703 0 R/Contents 2077 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F5 5 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+2077 0 obj<</Length 2078 0 R/Filter/FlateDecode>>stream
+xÚ…‘Ënà E÷þŠY& S ؘ¥Û$]Urc²ËÆJˆåʯ‚­Výú‚°­6êCH,fνw^ØœÂ&†sÜËàn/€pWÛ‰9G1È˪ï/YQª¼úP§Õi½–/žcž #Œ˜ãŽÆR®kkŒùZªË±Qí`¼*BœŠP§ )ÿN9ˆ‚¸µµå­2g]õCÕµ®³“FØ>z¹ÀKGLX#¯´Î´‚ü˜27v¦9Ò“5]¬9Âÿ9ÓÅyçX§¸˜ˆ|¬5Œº5S`sˆ7þÒÙ½M_ßœ+im:Ÿo²äÛ¥
+?h{H÷Bx8f9äÝux+ìÃ2Ý•ºh¥ <íXÔÎ"tÊSüËâYœÌÛ!t3må9øCxWendstream
+endobj
+2078 0 obj
+292
+endobj
+2079 0 obj<</Type/Page/Parent 1703 0 R/Contents 2080 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F5 5 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+2080 0 obj<</Length 2081 0 R/Filter/FlateDecode>>stream
+xÚ…’½nƒ0…wžâŽÉ€‹]ƒã‘6I§JeÉb‡RñWj¿v ¨¢VX ÷~çØ>×ï†À|î#»Îµ÷ ¼»=Ì@\L'b E òU×å‰,Ա̇×Óê´^‹7Rúa€¨½Ál×Ô(uµXc­š¡wª0¶*L¬Æ'ì7e!Ì1bÆÖ”·ª?ë²ʶ±ð˜S/¿ô èÆÐ!åÆÈ)3­ »j·i†øŸôdMk†‚ÿœÉâ¼Às®S\”‡.×T £nú)°9Ä«;ÿèì>eÝU×gJA\õ­Ëw³äk§ÊÝFÛ4Þ ðáñdµ—áCšƒ%º-´¬k¥{x–Í(+ká[¥ÏÈwþ˜P[£C¡›È­·`Æòâ}Ž‘endstream
+endobj
+2081 0 obj
+296
+endobj
+2082 0 obj<</Type/Page/Parent 1703 0 R/Contents 2083 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 4 0 R/F5 5 0 R/F8 7 0 R/F9 8 0 R>>>>/Annots 226 0 R>>endobj
+2083 0 obj<</Length 2084 0 R/Filter/FlateDecode>>stream
+xÚÍ›AwÛ¸€ïþ<¶‡( €àÑkÇ»éK7V
+L–b¡²]¦ÿâÔØf€?‘ß?eïïDÆü@'µ¨þq÷ûö±YµðCÊrQè.ýŸ‹Ù±µ¢'¦§W ©ÕR1P¬iZÞßqR‚^Y”ºGOøáÏÃÐg§‡ó…©ÔO´Ï?ÚýMûpœ-ê)5¥·¼Öv´Ë×3ÁÍ´Åò…1ÑÛ~uÜiÓ|Y_Þx­¯¢Ò:hØoy¾Š¹!‹n<¡Jý]kJ\ü÷h`n— eæÇ"Š‡k=U‹2da¯a¼{™áÛeÆ•êæ¸Û7Ý°éž^†yãwY-´¡0êæS67ZOŽç‹*dQÿ¹ ˆ‹@ß&BÖÆë
+¡ ϹQåèd—ßÚˆÆDkœƒ~}"v[Rz=‚ÖzÉ`ñ}ö;^¯ÃÍθ!sØy)å iÜ0*Æ4œ¢B<AåÄa¹Ò@ÔoQíê[×oû§?(˜þß9EIžP/Ös’ÔðÔÅ9ºàô_M¢Áêùç
+JÉðßϤd\
+t
+Ý*ƒXƒ-{JÖ§TäTÛ¡]Å3YŸ
+ÆÎ> pÓ½ÌòGk–€[Š€¥éú8ô«¾{4VVÿ…ÉAj³ÜD) wPØð¢T!0JýÒ6«ˆssƒ!¬ç`>Šú ö
+óÐXBOe­0ç°°åôTÖ5ƺ_Û!á¼ñ §Rûü€f÷FÞpðF°óB ìúG³Ùš‹áˆKr½ïs©—+D’
+5e;JP¤†S$¶<ÛQrÚ•Œ?ïdd³òdƒ-æq2U -UŒRaËÓ’–Qèo“;Ì5)P“O³ÅÄcž€L¾û€$@ÀÏ߇Mß(ãºP@H)[( oøclyÊ5·›¦[µÛm\ß.h~tB(@ÌñŽHÍ Xªx>Qraâ™”zaÃå%—Ë~[.ï#é„„MI –ëñIÔO¬ €w¸y¥)³*‹1JVÔòÌÊ< NÏ&FšÉ&š à첉”žXN¼€Ç lyz*Ìnú®Kn<
+ ì½Ý›KEy©8•Z¬\ø)a`Êåë“¥q¸k hV€€_Ú?Žíaˆkš9`ÅÆëiš9àݦݮ¤k9K]5=ŽëÏ,¨åt]Ôô4þñp8&tíÆãS{Ò¬
+XXzŸx Tõ8óñLc,ðah†#îñö9JjÉL¾€‡ E-oÉd…ñ¡íÖÉÓ„r4^lï4¡p‚7ªýgî€C ¯î¶š‹씚˜; lyjdyMJÉnôè;|–à“U,®ÊO?šàŠ/wñžH¯‹’
+ ÀObåקâ‚÷‘ìÚºËj¨’
+8ØmI'wus|ŽH™ea« ÿÅ–g–ÕLL°GšI°š àìì:¡'†å
+ŒA Æ–§'Fõ
+7û¶I]º8
+@ÔO(}àíÖÍ~»tØë&<ø@+@ÄÏßÛ}3>ƒpy«N©¹ÀÔEÁÂPZLU2QX æ`¨ß€>üoh»ƒVHLÇŽ€þ´
+yVà9 WxLIé7§º–¼ÐÎ[ž‚ói%2Þ`{M ¬
+endobj
+2084 0 obj
+2496
+endobj
+2085 0 obj<</Type/Page/Parent 1703 0 R/Contents 2086 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 4 0 R/F5 5 0 R/F8 7 0 R/F9 8 0 R>>>>/Annots 270 0 R>>endobj
+2086 0 obj<</Length 2087 0 R/Filter/FlateDecode>>stream
+xÚÕ›M{Û6Çïþ<¶+Ä S§Nú<ÎSÕV»—^¸2íåV¢¼””n÷Ó/3
+:ÆØB’E7qalÓ‰»]ûØÏ 7íÖLlç&ª@À6…j“üsTãAËå»JF}Ä©¡dÐI:;rj¶‘ËWÙ/ÂŒoÌ3£ +výürèwÃ>5«}{˜ÕzÁcUpÀvxLÀ‚æV•§Fìý¸;¾Ø5"Øb6_ç\­šÊ<4ºÚYÞÕª©m'ÍYA{p57s0¢™
+×)?û¶0›¹‰;1‰* gà8 Æ|Ñ(ëj-ÌÏÉÕÖ
+\­å×8Ú±Xe¦aIJŽFX2üø†~J×c U° eûœÚ<‚ÖêU°¬‡þ`H/5©ªÁAE+«ºÄmðc;þ‘Ù0|{psm"AD£
+H!ÍF 1ël>ÃGª¢m°Z4d>R5î77ÿêÖ9'y€Á N
+q®Ÿvc470ó¤úFÜã톧M¿F&ôBÌá󬫥À]‡Ù…d­ÀÕRâ–óÐå‚GЦ£‚¹Ò\í°Ö5|æé˜rR >bµ?° ãgêÓ›[E’*Ùe&¾ì"ßf™ K-ã:áfßnkÖö)„Š-åcH™
+Yº†Ñ Y*`ewèF;ZVŠ\€0?BöÅ~š“…žRØ@YÃNŽÆ4ˆî\cNWª”.?u㧾û¨’]d—Ó½¶«[6ep:A˯nÙ0ŒTÝúhfË_¹ãÉ„«ëSœ«À¦Ø·ï»±VyŽýÛ˜uí ÈJ8à8¨Ö­VÝöe7¶cÊC`ó\Bç\óÏ󆿜“ÌØb6R™‘ø¨R¨–ÓJ.hëÔJD¢rb½]¯wÇ—]ÍbÞ\@.¸HÅH3³É¢~™˜"Ç%±Ýšcàõ]?té…
+>Ö7õÜ‚—g&—6°ÚÃ(Áüá
+ƒêç~Ü 0äÉ…Æ'³²3R9!kÇr†{Ú窹÷¹¬§ì>cöq1§ÇàšÕ>As£}`‹Xµð¬ÄQ7h‹_Âœ 1TN «w©…ï›ãÀÉæ*ðiÿ0’Ì*21¿<I (h¡Õ¤ ñó+
+Zh;%eZA;”•º K«s´ƒ¨Ï T\¯ «‘§,°–c?˜ñJ¬[° M^Ç0*·°wcÿ‰ä¸P¡M™H/[Q3/¿ÑxM~ {¨þvä·Hï{BiÖ~ߪ¡ |^89î{‰Ê‰uiÂ)ç #“!Ü5 sÐ$+ u7»M*/4ÅÉÛEåûØÆß°‡ÐÖž-U˜[4‚[¹\AŽ
+ ¿× !P+ß·ûd` ZcšGÄ,*'ÚíqXOg69w¢ðë&“½÷‚)
+¢)›n©ÎËcO8›Ÿp®‚tÒóBòØ9•åÿ*²• £ÕÙyì‰góØÏU`îýËÉcgœÄ4ÞuÙ#ÞGLë3cƒ‡±ˆCåúb9çÔ‚Â'œÐ
+¼SÓ½Ø}w{pQ~S‘ß«rIz´¼jaÕtsu~š~BÚ4}„„4ýļœD}•™¯¯l2`Hí‚b6Qï@´FB•ëÒõUf‰é*Œ“Ì!A×Wç'êõÊ yy‰úœ§8^aY‘Fà)þ'G²B>"™‘rçÇÀwÜ"•sÆË›Ü+rL”îC¼"G¬ån<¤^&òmáO}Ú7W’ÕFÝz¦$1àpÀhèÆ©4‚º‘¡,õ狆nÞ¦<ìÚVsÂÂB‚¤N‡T‰ M
+ǃ)_G@,å^ {^Ö#ç±J»ôªÀÏ>@U ¥ V«eÒOÜQè…o%Vùú–ý¤è² ‘RQ Wvý´L8É7³º¢ŒXX½æ¡æ–bÎKo¸4¼B.ƒ .{ƒâù®žÉ†o g9e/|ƒìŠ-åîu‰KôWÎK/²”6ÏËu$ÁysÆÿHñ 1Ó#"
+–6¯Åv±`—u\ÑðX¾ƒåô߬4…k.Mô <ñ»û··«âº°qûa÷tø³»b9îžÇv»íÆ}ñ±Ží˜×ŒWÅu÷}O®üåêÿk½µendstream
+endobj
+2087 0 obj
+2148
+endobj
+2088 0 obj<</Type/Page/Parent 1703 0 R/Contents 2089 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 4 0 R/F5 5 0 R/F8 7 0 R/F9 8 0 R>>>>/Annots 316 0 R>>endobj
+2089 0 obj<</Length 2090 0 R/Filter/FlateDecode>>stream
+xÚÕZMoÛ8¼ûWðØ¢ðûã˜&u‹í&î­ÕQvœZ6vþ’|OåÀfŽ¯-àbªh¬¡gä‰Ïãñ¯`N2eÙr3û´˜]Ï“œ-ž˜0¡Ì:ÍíÏuǶOìvû²ï^öýÇůÙçÅŒ7<r¤‘^î¿D¦†3«LãÙ†YÛköøONØ=³ë¹fB¤wŒaãé>½á}Ûï»;û6ó&žÎ5}<Žlñªû}‹—­U££îøß ?ŸÕÈÓJò¯6Jäéfd€
+¯ç×-þT<œïÓ§ó+V0DºpJ‡ž« <¡à±¾±'<¡äyØïËýa×ÁÊ»FÐYù7w"¤kó7z¥x#é
+x£åìT¯âkž*‡àÂþçÌø|Wo/N™éÃ)=~ÇŒ,q+Ë–0²Ì/Ëý*îoÜÚžðý‘â²mÐÏÔw{á’Æqðµåᵿy|üû5­À?>&~§) ©”g5;™T-dÃBh ”cÉqTò½oŸ»D$µX˜ó&ÞTÉc$*j Ny³{>l†dÕ6. •UyÙ“•ämšQ!O‰ˆSžw1`“*Ik“V
+n&U|Ê
+…F’Λš8åÀ¯rÞ
+…Ò€ÏÐMœš¾ì Câ
+êeSMŸvE٨Ч=õ²©¦Oé±lPÈSæ½eÓÀv<!Âã†tÙ.Û±X‰çç@£ y4ã/ÝþvÝö}×Ó|üW¼2Ü}€b¸'ß7Õ]ÔMaÚC»lªió¢èšü´÷’|ÓT“g}Q4Ùi/nñš©&Θ¢e2fúÚü5iZŒ“ž6ØZ¾·`Büõ¹8©—šéraàaß—å’?é–Æj)†Ë]÷ÔÖD»%%Î}Òã—Ï„i\Ü«Ø"ÝÝßÌìŠÝ~ÿöÀ¶OûÛ]Ǿí¶Ï»v³év=û«}9´ë<òFÅS¯ô­«Õ
+—ôŸÙÿ‰ ÔUendstream
+endobj
+2090 0 obj
+1306
+endobj
+2091 0 obj<</Type/Page/Parent 1703 0 R/Contents 2092 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 4 0 R/F8 7 0 R/F9 8 0 R>>>>/Annots 364 0 R>>endobj
+2092 0 obj<</Length 2093 0 R/Filter/FlateDecode>>stream
+xÚÕšMSG†ïús´¬çûãH ø’TÈ7_X¥ôAVRœŸŸ™é^íÈUVsl€RÕËBKÏNïÛ£WûÏL ™¿•0¾ü<mf¿ÌgŸî’ÐRÌ_„r©SÂ+æÏæ‹?׽ؽˆÏ»í¡ßöçÏnç3ÙÉ\£<¨òððEÝIáë¢Øˆ”:‡b-KýþaXŠOwV(Už1èü·>ÿm~¯ûŲ/O¢C§ó«Ì¿ê˜|EÀkÝù ¯¤Ï/ÒBm²Î8ýá8lë‰4:C3b$ eêl!Ì,UC¨dW›æ¦ß? «×Ãj·-õlâ²’ŸKeå2ŸRù
+д+YÖ²ª†ÐZ°«ëayÜŒ³Íz6kHâ nUç ¨Ï(p+Îó†"Tœ¦ÎP ¡Š¼§ E' l–ªiVÑÀI Nó?Æ:èÇm!<Ž¥˜:²¾ìȶB”&(ö
+jrd›ø:òýb¿ÿ¾žGOŽŽM;˜Ëí`C¨nUíÄÔ6D0+ÎvLz ~UíTCèø[;¦ðœ³ªv ªÁsì͘â3l¦š1¨†/ï„™Û1ŧ-øLÝüƒjø´Ÿá»ù§øêxÀq¢ÁSº{ãö¬ãæ¬ÇRLǹ<nLŠÕ¥ª ƒ˜¦I Ljœ6÷7ã ñ†ÛË aòl”'Ò*¦†0‘ýîŸÂËoÇâiÜ€jø‚ÆM×qCá¹Ø¤M <—ø§M¡uMÚª!ÌkÅ<m¢øò‚Li¨†Ïîi“%œ4?äD%ùÕX©–­‘«í¡ö§(%°á4ŽXFÙdÞ šU”ï ×&
+ªÁ‹js6RŠÐc¬]TCè1Öæk¤Ÿ›i…ªás–·Rt&M1ˆÎÊ·ÆDcØ·Ÿ‚ãc)¦ûvwyÚhíñí[ùøÔ4m´SLôëb»¼é_Çõái>~å‰~Pfʉ@4ý ,ÿœˆ”m® ª!”ï ×&UlsmP¡ŠÌƒŠ.Ly´BÕÐL£ù2Åç!®® ¢Áó˜GÓ†<ÖC>+„DZSCö— YÙ€oßbirÛÚÙ•Å@{4äÛíÓîyµ]ŽŽœ$kÚ¢Œò"M7 µ9Û1¨1×®ÛP ¡æÚSpCí:k@5pCmγ† L˜j×Q“ÎCí„™6ßQCÀÅ)ŠV &¸ˆ14ß9CÀy ¢Ë,ðñÌW<ÆÐôÁ"0cÚ*pë00¸WÇáÛ>Yo`9‹—ÜÙnÿn}ÜÿuÊ–,«.ˆÔÇ6SºdÏ3l«ùgKŽ;éøC²ÄÝ*ÜD&Ow¶F¼³Õv!_cãm‘×wsq%>½»—Ã÷ÅЋûa·›M?ìÅo‹íq±.E¯”öâ*@FºúOâ³ÿ¸Fª%endstream
+endobj
+2093 0 obj
+1202
+endobj
+2094 0 obj<</Type/Page/Parent 1703 0 R/Contents 2095 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 4 0 R/F8 7 0 R/F9 8 0 R>>>>/Annots 412 0 R>>endobj
+2095 0 obj<</Length 2096 0 R/Filter/FlateDecode>>stream
+xÚÕšKsÛF„ïü{´†÷ý8ʶèK\QDúæ "C,&|(
+æ$S–=W¶«÷ëÀ$gÛg&Lh³N³í÷7Ûö÷CÇÎÏìãù4v§qx»ýcu¿]ñ†ÇÓƒ˜?Ça gV™Æ³#BL3’:°Íô?|G¿cï×:~áô”ñ±ÊƯÏxÿo{|9tÓó(ßÈøƒÆÿlüQ¾Â.ec#{àñ}“*êH²é:öÓ9è[áêå |<º; çi”QÿNS|‰ãiâ!bE<n&˜¤2^\F+x“ïéõeø¥=íÖ}×}{óíí4Ôú¸"d@#j¸¾&ظÀq#Bc²(a‚‹:²|Ú]Zwé(­ûM€^ÅÇøZJÞ„¬¡O¯ä]¿{=ÎÖ¡m<4«p6€—Å÷›Ê
+Á9~õ©žúý˸?Ÿ` ò"VùŒ½øŒÈ
+ñ.C×k|Z%£I¦ ái >S÷ãyøñbP>žGõãpÝâɤ—UüØ(.5ûñçn¼Ø±¢æWšWVB¸äW`ÈI •ž¸Wñ¸ÊŽ•ì8)ÄÇ58UC®áéôv›6uJRPO†õدý)Á)Ië†*¡s`5)r@!BçÁhÈFN•Ïjðš9 Ÿ5à5d#§ÊgøË´š ž‘·Î<g1&—tãæÊ)Jq£•ËWˆê¢JÜhåÁ£æ¸ÙŒýþ´›ÇiRÛ *Û 5\Nimƒ4`W”ó¦˜tÎPˆPȆô@ OŸ3šòTÁS!€[QΛ¡7ùúmÊPˆÐ[ÚiS£s•Q “Ä §*Ÿñ¥pðL¸µpšç@Þ,åãrátå4¥ÄQÚ Â TI NÃxß÷çžjã¤ee%¢]'h%”"ß8UãîñâÉI!BÉ©;rT:B|Ü/j|2HT:*|2(â¥S•Ï…R:@xžßZ:ÍsÀ“ƒàø<Š¨'Ëëž,-4ãBNªx²´®”N_ÚþÏ_Ó¦³+{CfÝUeŒ*…´FSwãž
+¨p…ø4'^8Õð¤E…(„'ý©F(*œ@!B¡©N5>ÎQá
+ñqA½pªð ïJá¢à ïo ›y
+„ÍbL>î Gº5ÂÍ¥ø5 JÔ§KáôÐöCG7kte,/u´ VЯ›j€Ú¡º "ÔžxÝTÃSÕM ž2ô릡¨n…¥¤55:îPÝ
+ÑqO½nªð]Ú¦°,³ƒ¹µkÊC ið8jHMúzÒø܆§Ò½XÔL^––é¡ßŸÆõþPîk²¤ŒX›Ên¾tLvYŠÛ@¿a2µÛbð-MËFÜXÒcj×€?ÁÇçË6\ç2œrºTðdn±áRÆÿP,5Ä?ËHp…Œ_î‘õùYݸøöʭϧǻõ–½c¿>lØæü<þÓö{èÏ»¾=»~`_ÚÓk{H¯c\bÃÞ9èFÿÎ'ñ·Õ¸0¸:endstream
+endobj
+2096 0 obj
+1179
+endobj
+2097 0 obj<</Type/Page/Parent 1703 0 R/Contents 2098 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 4 0 R/F8 7 0 R/F9 8 0 R>>>>/Annots 460 0 R>>endobj
+2098 0 obj<</Length 2099 0 R/Filter/FlateDecode>>stream
+xÚÍš9sG…süŠ ¥€«¹¦D;q™&¡R¢¦–,¸pÐ PÖÏ÷Ìt; —ÐÈØ:¨zZ°¡h¾Ç}šfJÈüS ã˯Çõì—ùìÃmZŠù“P.uJø`ÅüÛ»ùâ¯U/¶Oâf»Ù÷›ýîýüïÙ§ùLv2Ï(Tùpÿ«º“Â×E±J©2£ª•x(OpòóøpkóËSëócó3~ú±X¿¬úò<&v:ÿCó_v ~XG°kÝùÌždþ¢<m²Î$}/~:§ùTx ít^ÇQ׫ݶŒR)k>/“ÿ)^~‹óË$SÆ*x¾ ¨//£W²«‹÷øú²»_ìöýp³Úîú¯ï¾¾/sCÊ[„Նó á’Ïû›"uŸ.…¬3ÇçÝâ¹n»œ¶ý"ÀhòÇüVjÙ%T a¬oäõðüº>8‡õù¥àÁHÂùV–¿Ü ª.H°«ýîqX¾ì—ÛML\ÞD’Ïù£Í(T Ÿ `2|í˜â³¦úLõL žµ`3´æ€OáuÅÔŽÃy;vFV“RʼªF;vFKvüÇK¿9ºq`j#±*T¯3®¢Y¹[1…' ºUµâª>iÁ­Øš1gë—ZÆ“%EAx6)0«û~ÿ:l*œÑ|¾Q¸ˆ0°™7 ÂÁdøÆ Åç-úL‰P Ÿwà3|ã†âs
+ü¥¬&ˆÏéËÂæ0Âf2¯kÆQÏG5<ÊŽjŒ›ßï8šû~ñí·ü»£ä›;³MÄ"h ·%h@4‹ 8稡
+kì5 <…%6稡%6Ù5RN½Jb“Í7j>ñR¨F>yßÖt
+ìš ¸€ý54‡94“AxýP…ó s>h”Ç"Üšl= Æ Q‹ð“¨9½¯yssv–Ø mxMÍ6X,Ã9§ h°¯iª!4X‡³M
+Oc]ÓTƒ§¹Wh$Ÿ Íá
+.ªñ`@œ¶×Q_z,
+endobj
+2099 0 obj
+1220
+endobj
+2100 0 obj<</Type/Page/Parent 1703 0 R/Contents 2101 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 4 0 R/F8 7 0 R/F9 8 0 R>>>>/Annots 508 0 R>>endobj
+2101 0 obj<</Length 2102 0 R/Filter/FlateDecode>>stream
+xÚÕšËr7E÷ü
+,í…Çx?–²lf•*G¢vÞ0ôˆfÌWH*Éç@÷p0®2[˶\EÕõHMAó^°‰¿gJÈüO‰ …ñbµ›}XÌÞÏ“ÐR,ž…r©SÂ+_ß,–n{qx÷‡ý¥ß_ÎoÍ>-f²“¹FyPåáá·\¬“Â×E±J©R£ª­x,OðÃoœÖâýÜæ,O™ÿo|þÙüŒŸþ[îŽÛ¾<‰ÎhþÏŽÁ—ó»ÖÏìIæo ½ài“u&yì{ñÓ:ͯÂ=´ÓBxKÝmχRJ¥¬…Û”—8ß&™2VÆ“¾3¨/7£W²«·z9žýî8ßlû/o¾¼-E}Ì-Â4£†Ûá’Ï œ;"uÅØ.…¬3ËÓy¹®í®§v`4ù1¯¥–]BÕƺ’w§õËn°ëó­àÁHÂù^&ËÂjà‚¿zè//§}…3úëOçÁi²£T ¡ à3ûóê´9^6‡}]ÂÄ¥MI>kÀjjjø¬«á›8ᶕ:#«¿D[–¯ŠÑIQà/ÅIŸÎýipQëyué•6«©B5pÓ*šÅT‘»—RxÒ´vSUÃ'-»!mݾ vj$´Iq·Š/„Æn@5|!r·ŠÏ[ØΔ…ÑàyvCop‡:°ÁÂëXŠé7ÞveëlŠdÙ!€mÙºŠöír9~ØVß7ûõu{kØyV":„êYÕ’A4abÇÞ”)@mѲÊTC¨¾ãºÅ¥ðê+pÈP žÒàXœ3‡ 4)‚×ÔÌ5š”ÀiøfŘMÍP _ô¼‡¢
+ü¥Äˆ.hð:q†:8“BxK1Mœt;qŒ‹Õ¥bÌÎb ã˜T‰œûoýêû7Ns³ãòå%ÑÖUϪ™¢é‰l¾–yæ€F£e•ÌÕƒ›#¦™Câå×–¼f¨OKæ‰CòICâH75-‰cn¶‰Cñé4Ž§ª‘O'N³Í’/À|ºˆ/âxšÎœ¡dΤ\JñÌœ·©fŽöþú&N£CG{n×ÐÙöËÓ|Óo¿ž‡è –UK(¢%œ'O š–pì‡Â38讪á³8èf8žÆ)w P žÆ)7çÈ¡ιkä€jιùFÅ'åÕkª†Oâ ›oä|*âd»äˆOÅW¾Éª@àLÊàuÆoqnÜ¢7*Àt<¿Áu(Æ´Q‡ã6‡óõc›ØØÔ„V áaR^DÓåœ#‡´8*¯‘ª!´8*g9žÁ wP žÁù6çÈ¡5¹kä€j5ï©I'CsŒ TC'#ó£J_²ãI¥4i'÷ÚsJX⦭WçCJ7nP›sñòòG59¡q(^³æ°ß÷«ËõÉ.m¼!­Åñ„’ŸÇ}b>‰¢s®=ž4;ÖŸÜPdV5‡ìt0n5û£žŽÍI11?'
+endobj
+2102 0 obj
+1223
+endobj
+2103 0 obj<</Type/Page/Parent 1703 0 R/Contents 2104 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 4 0 R/F8 7 0 R/F9 8 0 R>>>>/Annots 556 0 R>>endobj
+2104 0 obj<</Length 2105 0 R/Filter/FlateDecode>>stream
+xÚÕšKsÓX…÷þw ‹ˆû~,3°š*HÌŽ'(ÁS~ddgàçsïí–u•*Ü^6@9uÓΑÚç“;ýßB ™ÿ*a|ù÷°]üµ\¼»MBK±|Ê¥N ¬X~{³\ý³éÅþQ¼ßïŽýîxx»üwq³\ÈNæåA•‡»"èN
+o\ÅV(¥Jª6â¾¼À«ïžÄ»[›ŸX^2èü\ŸŸ›_ñæçjû¼éËë˜Øéüƒæÿìüñ†ð®uç³÷$óh½ØÓ&ëìä¾ïÅoë4ß
+çÐÎ áq,u½9ìK)•²þNS¾Äù4É”me{ÒÚËÍè•ìjã}?Ÿ?ôûo½·_ß|}[Šú˜[„ÑlÕžï—|nàÜ©s(¦Žp)d½|9¬žj»ëÀ©Ý/2M~Ì×RË.¡jÆz%¯‡§—íÖçSÁÃ#iÎ'È2Y.¨Æ\WwýñeØUsFÿaýé<$MNƒªqèä̇þð0¬Ÿëý®^ÂÄ¥MIÖ@ÔTjüY Q×8”?#!_
+@4öŒ‚|¡‰3ÖâÌ
+áq,Å”8ö<qœ
+5¥bi\pœŠn6ý±aã4·4®^ÑÒÔÐäTÑ´„´Yœ‘C´µ#G䀚Ú¤ðÞˆ+t({!àÝQ¨Æ^ˆYœ¡C9ô¦BTãÐ;ˆ¾Ð¡ü9÷D: NóFåÎÈ—ò¾ј3ò…FÎX3+„DZSä¸óȱÚBJÉrjbŽÕRªPçf÷êCŽgÈÞ Q/ DÓJC^qaФˆyU€jrhRÂ[#®À¡ìEqUª±=wÜPþ‚‚ ©¸Õø b†/n(.ž¦E
+UãÏ%H¾À¡üYùR¨
+‡å•9 ‡
+‡ål™CÙ“8ãæÈù°\KœpsfáPÅ€¿Œ+Ì59T‘ùçÊ]0Íf¨Æ]°Ü·(^NÛ {^]º=0ÖæÌ
+áqÅz{ ž§Ž²0¾ô¸ ³åeã´<ð±?ŽÌÉà82§øMD[;­€hÚÂ8þ+”A­šPCÍ{¸F™“¡Y!
+á0Ùfƒ ÙYj%Ç}€0U³>ç£í¨¹/æ|œvü|¨íÓ¥›XWÕ⫽±SÔ$bm
+0)ý½^ãYü¼øi‚¶µendstream
+endobj
+2105 0 obj
+1208
+endobj
+2106 0 obj<</Type/Page/Parent 1703 0 R/Contents 2107 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 4 0 R/F8 7 0 R/F9 8 0 R>>>>/Annots 604 0 R>>endobj
+2107 0 obj<</Length 2108 0 R/Filter/FlateDecode>>stream
+xÚÕšMsÓH†ïþs„b¾?Ž–*¨ecsã¢ňòGÖV
+jýÎL·¤Ux|ìMª\y­¤Wn¿ÕîV‚ñø-˜“LYöe¿zµY½¼ Lr¶y`„F0ë4Û|}¶iÿÞuìøÀ^CwÎÏ7ßWo6+ÞðX#݈tsÿ6k8³Ê4ží™à¶‘¨vlà—¿8mÙË;Í„HéÒïÚø»ñï»áétÈ£d¼‹§;_ŽWÜKÙØä^ªF¡B÷Éa¼×&3·ÝùË©úã!ÕÓ!ž*.«þxhtò'Dê¬
+‚7¹gÞül÷».?ƒžŠ·kü™ºvÏOˆÙž .êèdÝuì·uŠ?…ó¤—…ð8–ºÙ©”ñÞÿÃiŠ/Òxš¼Š·©Íõ¤Ð^Œãs|†Ç·ÝpÛÝz8õ‡íçgŸŸ§ºÞiwQir˜…ÐE;8yõéÜns¯KG©×¯2h,æoªÂ¡qW7§íÓ~Ì~mãÉ á²jO+ˆ« #P…=­!®(§æPqš P…C% fè§æO8Hš P…?áiã¦æŽ+È—Ì„,
+s\C¾\¬ƒ¸) áq,E7â2nt6)¥P͸ÑA@JÀÙôûn¤“Wòr?hçr^eÞ€˜ûA;qE™75ƒVc\%Þ€*ZƒïŠ¨ò¦fÏH«ÌP…=#©Ó¦æO9È™LP…¿¾ž6mjþâûY;ÑTáOº¼©ùË|@Þ€(ì ùRçÍXx³(„DZQÞÈ˼QÁÃU !bÆ
+B
+qs×w»¯#k,'“U“Yu¹'T¼ã#s@Ì=¡¼¥Nœš='0²q@þœÄ7FT‰S³g<$V&¨Âž X”™Ss¨ dMf¨Âa|®4mæÔüÅ'DNÌUøS
+Ò†.sjþböó‘9
+{’_Gœ±
+gQŽC!¢¼QÞðqfîRóbfž€Ãq.ŽÀy߶÷‰8žØËY_n`Bžqbnp@N85ƒGä8 
+‡GädS³gq²ª°gq®M85‡fn'à€*⸩¹Sa T…;3mº¸©ù“0ÑÎT
+`‘ñ£›_6Æ:Dc+W50 ñ£àçâš&ÌKæ¶>p1ŽÞUsé?›mòicÖãÆlÛ€ã¤Ûû›» {Á^ú¸fëãÃð£=uìãé¸=µû}w:³íá©Ýå¦7*¾4^8™ö?ñœþµúÆ »Zendstream
+endobj
+2108 0 obj
+1223
+endobj
+2109 0 obj<</Type/Page/Parent 1703 0 R/Contents 2110 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 4 0 R/F8 7 0 R/F9 8 0 R>>>>/Annots 652 0 R>>endobj
+2110 0 obj<</Length 2111 0 R/Filter/FlateDecode>>stream
+xÚÕšKSÜV…÷ó+îÒ^ ß÷cIl“UªŒwÞ(X`\ó š¡âŸŸ{o_I-Weše¨©:zø¤Ö9LOÿ½QBæO%Œ/_ûÍoÛ͇›$´ÛG¡\ê”ðÁŠí·wÛþ¯Ý ŽâãñpçÓûíÍçíFv2×(ª<Üý.‚î¤ðÆuQìEJkb'îKý_~a|n¬Pª<cÐùg}þÙü„_NýÓPžD‡Nç¿2«cò¯uç3¼Ò²KM5úB¨MÖçz|zÝO§Òú|¢xP’x2u¶àIŸ¯ („§dW;æn8¿Ž‡
+gt¾¦Œ.!EèR!+Ðt¦©…Ð¥Ò‘æÓpzŸ_ÎÏÇC½„‰K£’|ÑäÇ̧T¾ÃA!¾X¯ßçŸýþeWoA9Ý‚$Oà=2ßg \à/÷à þ³úU8Kv]ŽO¥®w§c)¥Rþîÿá4eͧÉùêR¡Ü¿ ]Îç˜Ô÷óùåöx:}÷õ}mrÅÍgÞH´…5Õ¶j$@ma-¸çØ¡
+\‹mìPx*€iÕØ…ðTä:Ÿ4`7:U!>iÁlø†Ág«iN¡já³IáðŠ/ð—á…þBÇÎTbgU¨o¥˜ÆN¼;ÖÛêRÑeç±ÄŽõLªÆÎø|8?ÎÁ“Ø8Õ
+7]áTu­š: PW8Í=s(<ši•Ì…øòYg…§-xVÍPO;ð,ΩCV—™R"T¼†oê|&Eø§¨¦¨…Ϥ~Ã7u(¾ì£rJ/ú·eÎT2gU¦÷Œ']NTõ¨àJT±$Ž ,ª&Îëü:'wãù:§‹’h «iÕÈZÂ%ð,Æ¡CZ×<«„(D˜óÅr/gˆžCÂ3,‹qè„Ù@ä: ¡–¬#‡¤“nž©¦ln¶‘Cñéãíš <Út›©„ΪP;> ÊYÆÎ…ÓTcG“×é*ˆ%vtlSò;wCÿm~™ãyÝÊ3­"šÂÃȼ†Ô¾MÌ9‡èÚ̼†(DèxGgÚ¨»F(gÛ ›säP„Ú·÷äJä€B„º»ù†ŧÌ<5RM!>ÕÆÛ|C‡â“0܆dëÙ¶–m¶ý†ÐiuZèàBíø4&ç:êr訸 ÉSSKꨗÌy8ÃÃü‚ÇGf7´¾Ü*˜eÃ
+µã‘óÁ…ÓT'Á€<ÅÜâÉ®v’[V~ìÏÃœ6’Ýx-šË Õ²>×cò¨Ù/Pt>¢Ý¿ž’ûÄ{s€bs/¬GäŽû8œUhkÀªõûošùÎ
+endobj
+2111 0 obj
+1191
+endobj
+2112 0 obj<</Type/Page/Parent 1703 0 R/Contents 2113 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 4 0 R/F8 7 0 R/F9 8 0 R>>>>/Annots 700 0 R>>endobj
+2113 0 obj<</Length 2114 0 R/Filter/FlateDecode>>stream
+xÚÍšMoÛH †ïþslUçûã˜6Ížè&öÒ‹6VRþZÛÁöçï̲FjæÈ0ðF1¥ÇÃá+Ñüw¡„Ì¿J-ŒOÛŧåâã]ZŠå³P.uJø`ÅrõnÙÿ³ÄþY|ÞïÎÃî|z¿ü±ø²\ÈNæåE•—û?r°N
+o\ÅV$ÙyñPâÿò†ã‹øxg…RåŒù:¼ñùó †AüöMØm~¯êìü$xCÝlNûJ¥üWYþÒ1ø‰æ·xùÂóG¨u¾Üü†²
+U ]^+¯MÖ™äûù|xÎwëa³úöîÛûÓËÎñáXKä‹Ly 3lÊ—aKN(ÙÕ$|<õ/C ¤C§yR€.ù|É9Yµìª‰Ð¥TRõøòº·—õlÖ‘Ä‹&¿f<YÀ@5x±.ßýp~=î*šÑyk2Z@’Ï'¨5yßT _Pin‡ÓÓq}8¯÷»º€‰Kš’|Î×bSj©BÕð¹
+Oaƒ»¨Oa{›³áP„Râ7qÕp¤œW,‰Mn¾†Cð©.í"…jâS‘ùCEÌ4X
+Õ¬ç1£qQ‹³iíp°˜Û櫆ãiIRã1ÍœÀ¼+î¸kC±YÕ ØySÜjÞ#›ŽÍ„€Ž¿´–ØÏ
+endobj
+2114 0 obj
+1217
+endobj
+2115 0 obj<</Type/Page/Parent 1703 0 R/Contents 2116 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 4 0 R/F8 7 0 R/F9 8 0 R>>>>/Annots 748 0 R>>endobj
+2116 0 obj<</Length 2117 0 R/Filter/FlateDecode>>stream
+xÚÕšKoÛV…÷úw™,ÌÜ÷cé&qÑEÔVvÙ°1-¨Ð«’Œæç÷Þ;CñÒ@5ÚuâNhüQÃsÄÑü½PBæJ_¾¿o¿,’ÐR,_„r©SÂ+–Ïï–ýŸ›Aì_ÄÇýî<ìΧ÷Ë¿Ÿ— ÙÉ\£<¨òðø«º“Â×E±J›Î Úˆ§òo~㸬Pª<eÐùg}þÙüŒŸ†Ó÷ãúp^ïwå¹lêtþcóîþJ’`׺ó…]©rþªBö—ψ/ŸôÛÃf(µLäÂvŸLÍ|If ž’]m˜§aÿY§ùU8Ov^Ž¥î7§})¥Rþߟá4å–Â%Ÿ_ÕŒ'CçP!^¾Ø\
+Yg”õápÿüü[¾¨VÃñÛ»oïKUó5À†4³ªë-á¢É¹%RA­bj ë«øõÔ¯j·ëÀ©ÛoÂó ÝLv UÃ$8Öýqõº½Ñú|*xP’x΃aÉÒ£ <À°‡óëqWáŒþÉúÓ°šG BkÁhøFÅg$xMP ŸQà5|#‡âSü¥äˆOÅÛg¬3+ƒÇ#ã¸QDÜH•á"ª&n¤šÅÍiÌ›`Yuƒ¾Þ ¶¾Z˜6 ¦n°I]qÎ
+0´«’7 Âñ}×¼¡ð¼ÅwF%o@5xÞ[qΊÐ)pšš7 B§y§ EgÂÅjª†ÎD°¾iCñi þRœDƒ§ø 7cÈ›Y!<Ž¥˜&Ž¾ž8¶b•Ë¸¼}5%ŽU\
+ç±ß­†ËígåÇÉ\ï“bõ«š7 ¦~0)]qÎ
+0:´«’7 ÂèY§ ¸UMP \ÐàVœÓ†"t|¦¦ ¨†Ð%p¾yCñYw)T _îEË;o(¾ÜpzÌ ž1à/tÞŒu of…ð8–bš7æzÞ˜üçV—’± ¨¦¼1Z¾I›éîF±1«Êi‰v0;‡¸‘nîWÇãœã†
+&ã5l@4Í p0Î9l(@‰£q9k‰£q¶aCੈ3í6 &<ÙßÛP|Á4‹ ¾`¹/ P|^6Ë >¯¸/ P|6LË <o]ë@ØÌ
+áñÈzYÀ]ep$®ciscgËʸvY`
+›Ë-N’<.ëä‰vÐjZ
+endobj
+2117 0 obj
+1216
+endobj
+2118 0 obj<</Type/Page/Parent 1703 0 R/Contents 2119 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 4 0 R/F8 7 0 R/F9 8 0 R>>>>/Annots 796 0 R>>endobj
+2119 0 obj<</Length 2120 0 R/Filter/FlateDecode>>stream
+xÚÕšKoG €ïúsLÙÌûqTî¥R[¹å¢ÄkE…®´Fóó;3äj¹¢Ñ­¬  ×¢ôíRü$Š/”ùW‰ …ñâÛ~ñnµx{—„–bõ$”K>X±z|µZÝõâø$ÞCίW->®²“9G¹Qåæþ·œ¬“Â×E±JË.a´å~ºÇi#ÞÞY¡TyÈüD¼ñùó#.O›—ýøHÖw.?Õüçî¿þI¡®uç ºôÆÑ ž69Î÷ýðr:T8£31¸Ûeêl½¸¦3B%»Z2úó·ÓöyØõ¦|:XP¶ø\*W.ó)•_
+AC¯b«š6Ÿ‹ØªŠi "€.á["–®iÓY‡ï‰Šj "tIJVM0?_}1 DÐè/LMÓÆË Q^DÁÓ’±fÚp&Ü éæ­EâûË`´ M„ÇÇy8CË\;KU2:Á0<ú\LŽÑ gáÅ2ý®.†±‰Õ'šV]/ `.^mÁT:âXœ±mZ|çâÕ6@sq®¶iÑ9œgWÛ@Dèw×´ð N´«k "x'Úl]ÓÂÓþ2R<sl¶¶iá)bW%@@èΰ۶ó€mf‰ðø8giÕ°ÄY¸
+¥Æ%™…ÝHœ…gÝÜmËa8m¿¾LÖ ‰Ë+ZI}½T„™xU S=¨ÈýcM‹.²=
+endobj
+2120 0 obj
+1224
+endobj
+2121 0 obj<</Type/Page/Parent 1703 0 R/Contents 2122 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 4 0 R/F8 7 0 R/F9 8 0 R>>>>/Annots 844 0 R>>endobj
+2122 0 obj<</Length 2123 0 R/Filter/FlateDecode>>stream
+xÚÕšKsÛF €ïú{Lföý8ºvÜS;®­L/¹¨6íQF¯Pr“ŸßÝ(.3!G4ÎÈsù[.ñ‘0¾.”ùK ãËÿ§íâ·åâÃ]ZŠå‹P.uJø`ÅòùÝrõϦûq³ßúÝéø~ùeñq¹Ì9Ê‹*/¿çLÞ¸.Š­±ÓlÄcÉÿÆWñáÎ
+¥Ê'æ_Âç·ÇòëÃáÏþÛçwŸß—O²*ç‘åç«JÚŸ.DÐe!´î|^ˆ”:‡.DÈ £MŽ3Ô§ãêµ/‰tàÅIóÉÔÙ̧´ìF  ’]Ý>×ÃëÛvÜ6Öçµà
+lS£†MZ¨WŒmC
+JK1 [ÐPZhÙŒy@6³DxSñ”¿,ã">º•FD“mŒKP ²o–ëm¿Üß®Nýh˜`*ˆ½`]-TU34{!\Ë\3_®«ú¬ˆ@cðvˆ©f(º|Eɳf jè´d.
+OB§%#ݼRIlg³• §“>7‡FžNØÀf«
+/@ûºº
+N†f4
+̪fÀÎ{ßVsŸ ètl†
+endobj
+2123 0 obj
+1203
+endobj
+2124 0 obj<</Type/Page/Parent 1703 0 R/Contents 2125 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 4 0 R/F8 7 0 R/F9 8 0 R>>>>/Annots 892 0 R>>endobj
+2125 0 obj<</Length 2126 0 R/Filter/FlateDecode>>stream
+xÚÕšMsÛF †ïú{Laöûãè8V§‡LS[¹åÂZ´£V_•ä6?¿Ë(.=A· öŒ<¯)Az¹ ‹¿gJÈü«DÐÂxñ¸™}XÌÞÏ“ÐR,ž„r©QÂ+Ë7‹öu'vOâv·=uÛÓñíâÏÙÝb&™côª¸ÿ%k¤ðÆ5Ql„RªQÔZ<ôoðê‡gñ~nóû·ÌÄŸŸ›ßñî{»Ù¯»þ}Lltþ ùŸÍOÿQJÖµn|¶ždþ÷î´É:yè:ñÃ8ÕKáÚi <Ž¡nÖÇ]J¥¬ÿg)/p>K2eWù,é¼Ú Ð\ÎD¯dS²n¿\ÞmV§ùò뛯oû€Îqqx6ª.§ƒK>'o6š‡bL—BÖÙÏ—cû\R]>©~¿hòcNS-›„ª2Ë:Þž_6CÕ°¾á±Š´7Ÿ ŠÉ~Ù@UÞ‚„Juß^ÛâÍôéÌfùhƒÎCɵĠª º
+J Mš!fc(ž¤Q—IãT(*ö&@Œ¤qª\Áû}áÌ@k™QŒj"¤)Õ
+HSD•ÒB±bLŸ-É8ÔhÐ&…_‡˜²†rÔªÂP•»¡V1f eÐ[¨2…5 *ƒÞAaËÊžSð=¨°TeÏiÖ¤¡Ì™
+Tñj$1
+’æS{ø«{Å%rQVÊ™WDQe‚–ÌC¹“Ø ÄH7­R›à\C¸Ó {×1 Fw:açš1d(ƒÚ×
+àŒ9CùSØ/œUTØçÊÊÄÎ5pFN[àZbßš1gƒ*Ü]ë9j4¨"ï[Ê\0Õ
+endobj
+2126 0 obj
+1218
+endobj
+2127 0 obj<</Type/Page/Parent 1703 0 R/Contents 2128 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 4 0 R/F8 7 0 R/F9 8 0 R>>>>/Annots 940 0 R>>endobj
+2128 0 obj<</Length 2129 0 R/Filter/FlateDecode>>stream
+xÚÕšKsW…÷üŠ»´ß÷c©ØV6qY‘peã ±F›W
+o\ÅF(é;j-ËüðˆÃR¼¹³B©ò”¡ü®Ï¿›Ÿñ¡?½¶õyŒÎwÉrgÇàK©Ð€×ºó^›Î Bø˜ïõ…å]ürXíO«Ý¶Ô³)Ÿ)m<™:[ð”*ýQÁS²«óþûb³_÷õõ‹<ЮÂs©´ìF$™_&#K!ë òØ÷â?ë‡Âi²ÓBxKÝ®»RJ¥|ïÿà,åÐ|–¢É·…Îf
+PH—­ÄÅÚûýÓ‡ÅáÛÇÚãŸ_}~]j†ÀR©Ø蟪‰¥Ô9¤‚Ÿút\,k—ëÀ§Ë¯ãs}Jv t|êö°|Ù –o}> ÛtÖ€MÕE講b3-@#Á`jÌ€"€F½°™ž
+è0%f@<Y‡L N°–šU6iÁZ®¬ƒ!C áq,Å3dâ展!Ÿ%—Ę16)ð§œ2÷ýöîiH˜l\‘“Òt¹lÕ¬jÔ€ûÁ†^Å8jZ|Þ¢W•¨E
+áñaÎ2lÔå°Ñ
+GàõÒ Ô˜6Zá<§Í}öà_úíòôÇùcË©t£$ŒÂ!iät®%NÂ'MƒOEœ…פ5ªÈú’¦Å Y
+áqÇxàÂYªI“£'àÅ{AI“Nræqõãá™i´ƒ ãN
+endobj
+2129 0 obj
+1220
+endobj
+2130 0 obj<</Type/Page/Parent 1703 0 R/Contents 2131 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 4 0 R/F8 7 0 R/F9 8 0 R>>>>/Annots 947 0 R>>endobj
+2131 0 obj<</Length 2132 0 R/Filter/FlateDecode>>stream
+xÚÍ”Ínƒ0 Çï<…Û¡i> c×µ;Mê
+}
+endobj
+2132 0 obj
+385
+endobj
+2133 0 obj<</Type/Page/Parent 1703 0 R/Contents 2134 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F8 7 0 R>>>>>>endobj
+2134 0 obj<</Length 2135 0 R/Filter/FlateDecode>>stream
+xÚ+ä2T0
+áÒw³P04TIS042S072PIÑp rt QÐUp VÎO+)O,JU(ÊO/JÌÍM-*VðMÌ+MÌÑ ÉâÒéÓ…j¬(ËÌ̉º†pr
+endobj
+2135 0 obj
+109
+endobj
+2136 0 obj<</Count 10/First 2137 0 R/Last 2229 0 R>>endobj
+2137 0 obj<</Parent 2136 0 R/Title(Table of Contents)/Dest[2082 0 R/XYZ null 756 null]/Next 2138 0 R>>endobj
+2138 0 obj<</Parent 2136 0 R/Count -2/First 2139 0 R/Last 2140 0 R/Title(Preface)/Dest[1710 0 R/XYZ null 746 null]/Prev 2137 0 R/Next 2141 0 R>>endobj
+2139 0 obj<</Parent 2138 0 R/Title(System Overview)/Dest[1710 0 R/XYZ null 374 null]/Next 2140 0 R>>endobj
+2140 0 obj<</Parent 2138 0 R/Title(Document Overview)/Dest[1710 0 R/XYZ null 169 null]/Prev 2139 0 R>>endobj
+2141 0 obj<</Parent 2136 0 R/Count -7/First 2142 0 R/Last 2148 0 R/Title(1 - Printing System Overview)/Dest[1716 0 R/XYZ null 746 null]/Prev 2138 0 R/Next 2149 0 R>>endobj
+2142 0 obj<</Parent 2141 0 R/Title(The Printing Problem)/Dest[1716 0 R/XYZ null 387 null]/Next 2143 0 R>>endobj
+2143 0 obj<</Parent 2141 0 R/Title(The Technology)/Dest[1719 0 R/XYZ null 738 null]/Prev 2142 0 R/Next 2144 0 R>>endobj
+2144 0 obj<</Parent 2141 0 R/Title(Jobs)/Dest[1719 0 R/XYZ null 493 null]/Prev 2143 0 R/Next 2145 0 R>>endobj
+2145 0 obj<</Parent 2141 0 R/Title(Classes)/Dest[1719 0 R/XYZ null 400 null]/Prev 2144 0 R/Next 2146 0 R>>endobj
+2146 0 obj<</Parent 2141 0 R/Title(Filters)/Dest[1719 0 R/XYZ null 319 null]/Prev 2145 0 R/Next 2147 0 R>>endobj
+2147 0 obj<</Parent 2141 0 R/Title(Printer Drivers)/Dest[1722 0 R/XYZ null 738 null]/Prev 2146 0 R/Next 2148 0 R>>endobj
+2148 0 obj<</Parent 2141 0 R/Title(Networking)/Dest[1722 0 R/XYZ null 652 null]/Prev 2147 0 R>>endobj
+2149 0 obj<</Parent 2136 0 R/Count -6/First 2150 0 R/Last 2182 0 R/Title(2 - The CUPS API)/Dest[1728 0 R/XYZ null 746 null]/Prev 2141 0 R/Next 2191 0 R>>endobj
+2150 0 obj<</Parent 2149 0 R/Count -1/First 2151 0 R/Last 2151 0 R/Title(The CUPS Library)/Dest[1728 0 R/XYZ null 387 null]/Next 2152 0 R>>endobj
+2151 0 obj<</Parent 2150 0 R/Title(Detecting the CUPS Library in Autoconf)/Dest[1728 0 R/XYZ null 322 null]>>endobj
+2152 0 obj<</Parent 2149 0 R/Count -5/First 2153 0 R/Last 2157 0 R/Title(Basic Services)/Dest[1728 0 R/XYZ null 276 null]/Prev 2150 0 R/Next 2158 0 R>>endobj
+2153 0 obj<</Parent 2152 0 R/Title(Include Files)/Dest[1728 0 R/XYZ null 211 null]/Next 2154 0 R>>endobj
+2154 0 obj<</Parent 2152 0 R/Title(Getting the Available Printers and Classes)/Dest[1728 0 R/XYZ null 160 null]/Prev 2153 0 R/Next 2155 0 R>>endobj
+2155 0 obj<</Parent 2152 0 R/Title(Printing Files)/Dest[1731 0 R/XYZ null 731 null]/Prev 2154 0 R/Next 2156 0 R>>endobj
+2156 0 obj<</Parent 2152 0 R/Title(Setting Printer Options)/Dest[1731 0 R/XYZ null 698 null]/Prev 2155 0 R/Next 2157 0 R>>endobj
+2157 0 obj<</Parent 2152 0 R/Title(Cancelling Jobs)/Dest[1731 0 R/XYZ null 647 null]/Prev 2156 0 R>>endobj
+2158 0 obj<</Parent 2149 0 R/Count -7/First 2159 0 R/Last 2165 0 R/Title(HTTP Services)/Dest[1731 0 R/XYZ null 600 null]/Prev 2152 0 R/Next 2166 0 R>>endobj
+2159 0 obj<</Parent 2158 0 R/Title(Include Files)/Dest[1731 0 R/XYZ null 536 null]/Next 2160 0 R>>endobj
+2160 0 obj<</Parent 2158 0 R/Title(Connecting to a Server)/Dest[1731 0 R/XYZ null 485 null]/Prev 2159 0 R/Next 2161 0 R>>endobj
+2161 0 obj<</Parent 2158 0 R/Title(Setting Request Fields)/Dest[1731 0 R/XYZ null 435 null]/Prev 2160 0 R/Next 2162 0 R>>endobj
+2162 0 obj<</Parent 2158 0 R/Title(Issuing a Request)/Dest[1731 0 R/XYZ null 384 null]/Prev 2161 0 R/Next 2163 0 R>>endobj
+2163 0 obj<</Parent 2158 0 R/Title(Getting the Request Status)/Dest[1731 0 R/XYZ null 334 null]/Prev 2162 0 R/Next 2164 0 R>>endobj
+2164 0 obj<</Parent 2158 0 R/Title(Sending Request Data)/Dest[1731 0 R/XYZ null 284 null]/Prev 2163 0 R/Next 2165 0 R>>endobj
+2165 0 obj<</Parent 2158 0 R/Title(Reading Request Data)/Dest[1731 0 R/XYZ null 233 null]/Prev 2164 0 R>>endobj
+2166 0 obj<</Parent 2149 0 R/Count -10/First 2167 0 R/Last 2176 0 R/Title(IPP Services)/Dest[1731 0 R/XYZ null 186 null]/Prev 2158 0 R/Next 2177 0 R>>endobj
+2167 0 obj<</Parent 2166 0 R/Title(Include Files)/Dest[1734 0 R/XYZ null 731 null]/Next 2168 0 R>>endobj
+2168 0 obj<</Parent 2166 0 R/Title(Creating an IPP Request)/Dest[1734 0 R/XYZ null 698 null]/Prev 2167 0 R/Next 2169 0 R>>endobj
+2169 0 obj<</Parent 2166 0 R/Title(Adding Attributes)/Dest[1734 0 R/XYZ null 647 null]/Prev 2168 0 R/Next 2170 0 R>>endobj
+2170 0 obj<</Parent 2166 0 R/Title(Sending an IPP Request)/Dest[1734 0 R/XYZ null 597 null]/Prev 2169 0 R/Next 2171 0 R>>endobj
+2171 0 obj<</Parent 2166 0 R/Title(Reading an IPP Response)/Dest[1734 0 R/XYZ null 546 null]/Prev 2170 0 R/Next 2172 0 R>>endobj
+2172 0 obj<</Parent 2166 0 R/Title(Finding Attributes)/Dest[1734 0 R/XYZ null 496 null]/Prev 2171 0 R/Next 2173 0 R>>endobj
+2173 0 obj<</Parent 2166 0 R/Title(Looping Through Attributes)/Dest[1734 0 R/XYZ null 446 null]/Prev 2172 0 R/Next 2174 0 R>>endobj
+2174 0 obj<</Parent 2166 0 R/Title(IPP Standard Operations)/Dest[1734 0 R/XYZ null 395 null]/Prev 2173 0 R/Next 2175 0 R>>endobj
+2175 0 obj<</Parent 2166 0 R/Title(IPP Extension Operations)/Dest[1734 0 R/XYZ null 345 null]/Prev 2174 0 R/Next 2176 0 R>>endobj
+2176 0 obj<</Parent 2166 0 R/Title(CUPS Extension Operations)/Dest[1734 0 R/XYZ null 294 null]/Prev 2175 0 R>>endobj
+2177 0 obj<</Parent 2149 0 R/Count -4/First 2178 0 R/Last 2181 0 R/Title(Language Services)/Dest[1734 0 R/XYZ null 248 null]/Prev 2166 0 R/Next 2182 0 R>>endobj
+2178 0 obj<</Parent 2177 0 R/Title(Include Files)/Dest[1734 0 R/XYZ null 183 null]/Next 2179 0 R>>endobj
+2179 0 obj<</Parent 2177 0 R/Title(Getting the Default Language)/Dest[1737 0 R/XYZ null 731 null]/Prev 2178 0 R/Next 2180 0 R>>endobj
+2180 0 obj<</Parent 2177 0 R/Title(Getting the Language Encoding)/Dest[1737 0 R/XYZ null 698 null]/Prev 2179 0 R/Next 2181 0 R>>endobj
+2181 0 obj<</Parent 2177 0 R/Title(Getting a Language String)/Dest[1737 0 R/XYZ null 647 null]/Prev 2180 0 R>>endobj
+2182 0 obj<</Parent 2149 0 R/Count -8/First 2183 0 R/Last 2190 0 R/Title(PPD Services)/Dest[1737 0 R/XYZ null 600 null]/Prev 2177 0 R>>endobj
+2183 0 obj<</Parent 2182 0 R/Title(Include Files)/Dest[1737 0 R/XYZ null 536 null]/Next 2184 0 R>>endobj
+2184 0 obj<</Parent 2182 0 R/Title(Loading a PPD File)/Dest[1737 0 R/XYZ null 485 null]/Prev 2183 0 R/Next 2185 0 R>>endobj
+2185 0 obj<</Parent 2182 0 R/Title(Options and Groups)/Dest[1737 0 R/XYZ null 435 null]/Prev 2184 0 R/Next 2186 0 R>>endobj
+2186 0 obj<</Parent 2182 0 R/Title(Finding an Option)/Dest[1737 0 R/XYZ null 384 null]/Prev 2185 0 R/Next 2187 0 R>>endobj
+2187 0 obj<</Parent 2182 0 R/Title(Finding a Page Size)/Dest[1737 0 R/XYZ null 334 null]/Prev 2186 0 R/Next 2188 0 R>>endobj
+2188 0 obj<</Parent 2182 0 R/Title(Marking Options)/Dest[1737 0 R/XYZ null 284 null]/Prev 2187 0 R/Next 2189 0 R>>endobj
+2189 0 obj<</Parent 2182 0 R/Title(Checking for Conflicts)/Dest[1737 0 R/XYZ null 233 null]/Prev 2188 0 R/Next 2190 0 R>>endobj
+2190 0 obj<</Parent 2182 0 R/Title(Sending Options)/Dest[1737 0 R/XYZ null 183 null]/Prev 2189 0 R>>endobj
+2191 0 obj<</Parent 2136 0 R/Count -4/First 2192 0 R/Last 2199 0 R/Title(3 - Writing Filters)/Dest[1740 0 R/XYZ null 746 null]/Prev 2149 0 R/Next 2200 0 R>>endobj
+2192 0 obj<</Parent 2191 0 R/Count -3/First 2193 0 R/Last 2195 0 R/Title(Overview)/Dest[1740 0 R/XYZ null 387 null]/Next 2196 0 R>>endobj
+2193 0 obj<</Parent 2192 0 R/Title(Security Considerations)/Dest[1740 0 R/XYZ null 322 null]/Next 2194 0 R>>endobj
+2194 0 obj<</Parent 2192 0 R/Title(Temporary Files)/Dest[1740 0 R/XYZ null 262 null]/Prev 2193 0 R/Next 2195 0 R>>endobj
+2195 0 obj<</Parent 2192 0 R/Title(Page Accounting)/Dest[1740 0 R/XYZ null 212 null]/Prev 2194 0 R>>endobj
+2196 0 obj<</Parent 2191 0 R/Count -1/First 2197 0 R/Last 2197 0 R/Title(Command-Line Arguments)/Dest[1740 0 R/XYZ null 165 null]/Prev 2192 0 R/Next 2198 0 R>>endobj
+2197 0 obj<</Parent 2196 0 R/Title(Copy Generation)/Dest[1743 0 R/XYZ null 731 null]>>endobj
+2198 0 obj<</Parent 2191 0 R/Title(Environment Variables)/Dest[1743 0 R/XYZ null 701 null]/Prev 2196 0 R/Next 2199 0 R>>endobj
+2199 0 obj<</Parent 2191 0 R/Title(Writing a HTML Filter)/Dest[1743 0 R/XYZ null 640 null]/Prev 2198 0 R>>endobj
+2200 0 obj<</Parent 2136 0 R/Count -3/First 2201 0 R/Last 2209 0 R/Title(4 - Writing Printer Drivers)/Dest[1746 0 R/XYZ null 746 null]/Prev 2191 0 R/Next 2210 0 R>>endobj
+2201 0 obj<</Parent 2200 0 R/Count -2/First 2202 0 R/Last 2203 0 R/Title(Overview)/Dest[1746 0 R/XYZ null 374 null]/Next 2204 0 R>>endobj
+2202 0 obj<</Parent 2201 0 R/Title(Page Accounting)/Dest[1746 0 R/XYZ null 309 null]/Next 2203 0 R>>endobj
+2203 0 obj<</Parent 2201 0 R/Title(Color Management)/Dest[1746 0 R/XYZ null 259 null]/Prev 2202 0 R>>endobj
+2204 0 obj<</Parent 2200 0 R/Count -4/First 2205 0 R/Last 2208 0 R/Title(Raster Functions)/Dest[1746 0 R/XYZ null 212 null]/Prev 2201 0 R/Next 2209 0 R>>endobj
+2205 0 obj<</Parent 2204 0 R/Title(cupsRasterOpen\(\))/Dest[1746 0 R/XYZ null 147 null]/Next 2206 0 R>>endobj
+2206 0 obj<</Parent 2204 0 R/Title(cupsRasterReadHeader\(\))/Dest[1749 0 R/XYZ null 731 null]/Prev 2205 0 R/Next 2207 0 R>>endobj
+2207 0 obj<</Parent 2204 0 R/Title(cupsRasterReadPixels\(\))/Dest[1749 0 R/XYZ null 698 null]/Prev 2206 0 R/Next 2208 0 R>>endobj
+2208 0 obj<</Parent 2204 0 R/Title(cupsRasterClose\(\))/Dest[1749 0 R/XYZ null 647 null]/Prev 2207 0 R>>endobj
+2209 0 obj<</Parent 2200 0 R/Title(Writing a HP-PCL Driver)/Dest[1749 0 R/XYZ null 600 null]/Prev 2204 0 R>>endobj
+2210 0 obj<</Parent 2136 0 R/Count -4/First 2211 0 R/Last 2219 0 R/Title(5 - Writing Backends)/Dest[1752 0 R/XYZ null 746 null]/Prev 2200 0 R/Next 2220 0 R>>endobj
+2211 0 obj<</Parent 2210 0 R/Count -4/First 2212 0 R/Last 2215 0 R/Title(Overview)/Dest[1752 0 R/XYZ null 374 null]/Next 2216 0 R>>endobj
+2212 0 obj<</Parent 2211 0 R/Title(Security Considerations)/Dest[1752 0 R/XYZ null 309 null]/Next 2213 0 R>>endobj
+2213 0 obj<</Parent 2211 0 R/Title(Temporary Files)/Dest[1752 0 R/XYZ null 249 null]/Prev 2212 0 R/Next 2214 0 R>>endobj
+2214 0 obj<</Parent 2211 0 R/Title(Page Accounting)/Dest[1752 0 R/XYZ null 199 null]/Prev 2213 0 R/Next 2215 0 R>>endobj
+2215 0 obj<</Parent 2211 0 R/Title(Retries)/Dest[1752 0 R/XYZ null 148 null]/Prev 2214 0 R>>endobj
+2216 0 obj<</Parent 2210 0 R/Count -1/First 2217 0 R/Last 2217 0 R/Title(Command-Line Arguments)/Dest[1755 0 R/XYZ null 738 null]/Prev 2211 0 R/Next 2218 0 R>>endobj
+2217 0 obj<</Parent 2216 0 R/Title(Copy Generation)/Dest[1755 0 R/XYZ null 694 null]>>endobj
+2218 0 obj<</Parent 2210 0 R/Title(Environment Variables)/Dest[1755 0 R/XYZ null 647 null]/Prev 2216 0 R/Next 2219 0 R>>endobj
+2219 0 obj<</Parent 2210 0 R/Title(Writing a Serial Port Backend)/Dest[1755 0 R/XYZ null 586 null]/Prev 2218 0 R>>endobj
+2220 0 obj<</Parent 2136 0 R/Count -6/First 2221 0 R/Last 2226 0 R/Title(A - Constants)/Dest[1758 0 R/XYZ null 746 null]/Prev 2210 0 R/Next 2227 0 R>>endobj
+2221 0 obj<</Parent 2220 0 R/Title(CUPS Constants)/Dest[1758 0 R/XYZ null 387 null]/Next 2222 0 R>>endobj
+2222 0 obj<</Parent 2220 0 R/Title(HTTP Constants)/Dest[1758 0 R/XYZ null 326 null]/Prev 2221 0 R/Next 2223 0 R>>endobj
+2223 0 obj<</Parent 2220 0 R/Title(IPP Constants)/Dest[1758 0 R/XYZ null 265 null]/Prev 2222 0 R/Next 2224 0 R>>endobj
+2224 0 obj<</Parent 2220 0 R/Title(Language Constants)/Dest[1758 0 R/XYZ null 204 null]/Prev 2223 0 R/Next 2225 0 R>>endobj
+2225 0 obj<</Parent 2220 0 R/Title(PPD Constants)/Dest[1761 0 R/XYZ null 738 null]/Prev 2224 0 R/Next 2226 0 R>>endobj
+2226 0 obj<</Parent 2220 0 R/Title(Raster Constants)/Dest[1761 0 R/XYZ null 697 null]/Prev 2225 0 R>>endobj
+2227 0 obj<</Parent 2136 0 R/Count -1/First 2228 0 R/Last 2228 0 R/Title(B - Structures)/Dest[1764 0 R/XYZ null 746 null]/Prev 2220 0 R/Next 2229 0 R>>endobj
+2228 0 obj<</Parent 2227 0 R/Title()/Dest[1764 0 R/XYZ null 387 null]>>endobj
+2229 0 obj<</Parent 2136 0 R/Count -96/First 2230 0 R/Last 2882 0 R/Title(C - Functions)/Dest[1770 0 R/XYZ null 746 null]/Prev 2227 0 R>>endobj
+2230 0 obj<</Parent 2229 0 R/Count -6/First 2231 0 R/Last 2236 0 R/Title(cupsAddOption\(\))/Dest[1773 0 R/XYZ null 738 null]/Next 2237 0 R>>endobj
+2231 0 obj<</Parent 2230 0 R/Title(Usage)/Dest[1773 0 R/XYZ null 694 null]/Next 2232 0 R>>endobj
+2232 0 obj<</Parent 2230 0 R/Title(Arguments)/Dest[1773 0 R/XYZ null 596 null]/Prev 2231 0 R/Next 2233 0 R>>endobj
+2233 0 obj<</Parent 2230 0 R/Title(Returns)/Dest[1773 0 R/XYZ null 435 null]/Prev 2232 0 R/Next 2234 0 R>>endobj
+2234 0 obj<</Parent 2230 0 R/Title(Description)/Dest[1773 0 R/XYZ null 375 null]/Prev 2233 0 R/Next 2235 0 R>>endobj
+2235 0 obj<</Parent 2230 0 R/Title(Example)/Dest[1773 0 R/XYZ null 315 null]/Prev 2234 0 R/Next 2236 0 R>>endobj
+2236 0 obj<</Parent 2230 0 R/Title(See Also)/Dest[1776 0 R/XYZ null 731 null]/Prev 2235 0 R>>endobj
+2237 0 obj<</Parent 2229 0 R/Count -6/First 2238 0 R/Last 2243 0 R/Title(cupsCancelJob\(\))/Dest[1779 0 R/XYZ null 738 null]/Prev 2230 0 R/Next 2244 0 R>>endobj
+2238 0 obj<</Parent 2237 0 R/Title(Usage)/Dest[1779 0 R/XYZ null 694 null]/Next 2239 0 R>>endobj
+2239 0 obj<</Parent 2237 0 R/Title(Arguments)/Dest[1779 0 R/XYZ null 617 null]/Prev 2238 0 R/Next 2240 0 R>>endobj
+2240 0 obj<</Parent 2237 0 R/Title(Returns)/Dest[1779 0 R/XYZ null 497 null]/Prev 2239 0 R/Next 2241 0 R>>endobj
+2241 0 obj<</Parent 2237 0 R/Title(Description)/Dest[1779 0 R/XYZ null 437 null]/Prev 2240 0 R/Next 2242 0 R>>endobj
+2242 0 obj<</Parent 2237 0 R/Title(Example)/Dest[1779 0 R/XYZ null 377 null]/Prev 2241 0 R/Next 2243 0 R>>endobj
+2243 0 obj<</Parent 2237 0 R/Title(See Also)/Dest[1779 0 R/XYZ null 300 null]/Prev 2242 0 R>>endobj
+2244 0 obj<</Parent 2229 0 R/Count -6/First 2245 0 R/Last 2250 0 R/Title(cupsDoFileRequest\(\))/Dest[1782 0 R/XYZ null 738 null]/Prev 2237 0 R/Next 2251 0 R>>endobj
+2245 0 obj<</Parent 2244 0 R/Title(Usage)/Dest[1782 0 R/XYZ null 694 null]/Next 2246 0 R>>endobj
+2246 0 obj<</Parent 2244 0 R/Title(Arguments)/Dest[1782 0 R/XYZ null 596 null]/Prev 2245 0 R/Next 2247 0 R>>endobj
+2247 0 obj<</Parent 2244 0 R/Title(Returns)/Dest[1782 0 R/XYZ null 435 null]/Prev 2246 0 R/Next 2248 0 R>>endobj
+2248 0 obj<</Parent 2244 0 R/Title(Description)/Dest[1782 0 R/XYZ null 361 null]/Prev 2247 0 R/Next 2249 0 R>>endobj
+2249 0 obj<</Parent 2244 0 R/Title(Example)/Dest[1782 0 R/XYZ null 275 null]/Prev 2248 0 R/Next 2250 0 R>>endobj
+2250 0 obj<</Parent 2244 0 R/Title(See Also)/Dest[1785 0 R/XYZ null 537 null]/Prev 2249 0 R>>endobj
+2251 0 obj<</Parent 2229 0 R/Count -6/First 2252 0 R/Last 2257 0 R/Title(cupsDoRequest\(\))/Dest[1788 0 R/XYZ null 738 null]/Prev 2244 0 R/Next 2258 0 R>>endobj
+2252 0 obj<</Parent 2251 0 R/Title(Usage)/Dest[1788 0 R/XYZ null 694 null]/Next 2253 0 R>>endobj
+2253 0 obj<</Parent 2251 0 R/Title(Arguments)/Dest[1788 0 R/XYZ null 606 null]/Prev 2252 0 R/Next 2254 0 R>>endobj
+2254 0 obj<</Parent 2251 0 R/Title(Returns)/Dest[1788 0 R/XYZ null 466 null]/Prev 2253 0 R/Next 2255 0 R>>endobj
+2255 0 obj<</Parent 2251 0 R/Title(Description)/Dest[1788 0 R/XYZ null 392 null]/Prev 2254 0 R/Next 2256 0 R>>endobj
+2256 0 obj<</Parent 2251 0 R/Title(Example)/Dest[1788 0 R/XYZ null 319 null]/Prev 2255 0 R/Next 2257 0 R>>endobj
+2257 0 obj<</Parent 2251 0 R/Title(See Also)/Dest[1791 0 R/XYZ null 612 null]/Prev 2256 0 R>>endobj
+2258 0 obj<</Parent 2229 0 R/Count -5/First 2259 0 R/Last 2263 0 R/Title(cupsFreeOptions\(\))/Dest[1794 0 R/XYZ null 738 null]/Prev 2251 0 R/Next 2264 0 R>>endobj
+2259 0 obj<</Parent 2258 0 R/Title(Usage)/Dest[1794 0 R/XYZ null 694 null]/Next 2260 0 R>>endobj
+2260 0 obj<</Parent 2258 0 R/Title(Arguments)/Dest[1794 0 R/XYZ null 606 null]/Prev 2259 0 R/Next 2261 0 R>>endobj
+2261 0 obj<</Parent 2258 0 R/Title(Description)/Dest[1794 0 R/XYZ null 486 null]/Prev 2260 0 R/Next 2262 0 R>>endobj
+2262 0 obj<</Parent 2258 0 R/Title(Example)/Dest[1794 0 R/XYZ null 426 null]/Prev 2261 0 R/Next 2263 0 R>>endobj
+2263 0 obj<</Parent 2258 0 R/Title(See Also)/Dest[1794 0 R/XYZ null 295 null]/Prev 2262 0 R>>endobj
+2264 0 obj<</Parent 2229 0 R/Count -6/First 2265 0 R/Last 2270 0 R/Title(cupsGetClasses\(\))/Dest[1797 0 R/XYZ null 738 null]/Prev 2258 0 R/Next 2271 0 R>>endobj
+2265 0 obj<</Parent 2264 0 R/Title(Usage)/Dest[1797 0 R/XYZ null 694 null]/Next 2266 0 R>>endobj
+2266 0 obj<</Parent 2264 0 R/Title(Arguments)/Dest[1797 0 R/XYZ null 628 null]/Prev 2265 0 R/Next 2267 0 R>>endobj
+2267 0 obj<</Parent 2264 0 R/Title(Returns)/Dest[1797 0 R/XYZ null 528 null]/Prev 2266 0 R/Next 2268 0 R>>endobj
+2268 0 obj<</Parent 2264 0 R/Title(Description)/Dest[1797 0 R/XYZ null 468 null]/Prev 2267 0 R/Next 2269 0 R>>endobj
+2269 0 obj<</Parent 2264 0 R/Title(Example)/Dest[1797 0 R/XYZ null 394 null]/Prev 2268 0 R/Next 2270 0 R>>endobj
+2270 0 obj<</Parent 2264 0 R/Title(See Also)/Dest[1797 0 R/XYZ null 145 null]/Prev 2269 0 R>>endobj
+2271 0 obj<</Parent 2229 0 R/Count -5/First 2272 0 R/Last 2276 0 R/Title(cupsGetDefault\(\))/Dest[1800 0 R/XYZ null 738 null]/Prev 2264 0 R/Next 2277 0 R>>endobj
+2272 0 obj<</Parent 2271 0 R/Title(Usage)/Dest[1800 0 R/XYZ null 694 null]/Next 2273 0 R>>endobj
+2273 0 obj<</Parent 2271 0 R/Title(Returns)/Dest[1800 0 R/XYZ null 628 null]/Prev 2272 0 R/Next 2274 0 R>>endobj
+2274 0 obj<</Parent 2271 0 R/Title(Description)/Dest[1800 0 R/XYZ null 568 null]/Prev 2273 0 R/Next 2275 0 R>>endobj
+2275 0 obj<</Parent 2271 0 R/Title(Example)/Dest[1800 0 R/XYZ null 495 null]/Prev 2274 0 R/Next 2276 0 R>>endobj
+2276 0 obj<</Parent 2271 0 R/Title(See Also)/Dest[1800 0 R/XYZ null 418 null]/Prev 2275 0 R>>endobj
+2277 0 obj<</Parent 2229 0 R/Count -5/First 2278 0 R/Last 2282 0 R/Title(cupsGetOption\(\))/Dest[1803 0 R/XYZ null 738 null]/Prev 2271 0 R/Next 2283 0 R>>endobj
+2278 0 obj<</Parent 2277 0 R/Title(Usage)/Dest[1803 0 R/XYZ null 694 null]/Next 2279 0 R>>endobj
+2279 0 obj<</Parent 2277 0 R/Title(Arguments)/Dest[1803 0 R/XYZ null 606 null]/Prev 2278 0 R/Next 2280 0 R>>endobj
+2280 0 obj<</Parent 2277 0 R/Title(Returns)/Dest[1803 0 R/XYZ null 466 null]/Prev 2279 0 R/Next 2281 0 R>>endobj
+2281 0 obj<</Parent 2277 0 R/Title(Description)/Dest[1803 0 R/XYZ null 406 null]/Prev 2280 0 R/Next 2282 0 R>>endobj
+2282 0 obj<</Parent 2277 0 R/Title(See Also)/Dest[1803 0 R/XYZ null 224 null]/Prev 2281 0 R>>endobj
+2283 0 obj<</Parent 2229 0 R/Count -6/First 2284 0 R/Last 2289 0 R/Title(cupsGetPassword\(\))/Dest[1806 0 R/XYZ null 738 null]/Prev 2277 0 R/Next 2290 0 R>>endobj
+2284 0 obj<</Parent 2283 0 R/Title(Usage)/Dest[1806 0 R/XYZ null 694 null]/Next 2285 0 R>>endobj
+2285 0 obj<</Parent 2283 0 R/Title(Arguments)/Dest[1806 0 R/XYZ null 628 null]/Prev 2284 0 R/Next 2286 0 R>>endobj
+2286 0 obj<</Parent 2283 0 R/Title(Returns)/Dest[1806 0 R/XYZ null 528 null]/Prev 2285 0 R/Next 2287 0 R>>endobj
+2287 0 obj<</Parent 2283 0 R/Title(Description)/Dest[1806 0 R/XYZ null 468 null]/Prev 2286 0 R/Next 2288 0 R>>endobj
+2288 0 obj<</Parent 2283 0 R/Title(Example)/Dest[1806 0 R/XYZ null 394 null]/Prev 2287 0 R/Next 2289 0 R>>endobj
+2289 0 obj<</Parent 2283 0 R/Title(See Also)/Dest[1806 0 R/XYZ null 274 null]/Prev 2288 0 R>>endobj
+2290 0 obj<</Parent 2229 0 R/Count -5/First 2291 0 R/Last 2295 0 R/Title(cupsGetPPD\(\))/Dest[1809 0 R/XYZ null 738 null]/Prev 2283 0 R/Next 2296 0 R>>endobj
+2291 0 obj<</Parent 2290 0 R/Title(Usage)/Dest[1809 0 R/XYZ null 694 null]/Next 2292 0 R>>endobj
+2292 0 obj<</Parent 2290 0 R/Title(Arguments)/Dest[1809 0 R/XYZ null 628 null]/Prev 2291 0 R/Next 2293 0 R>>endobj
+2293 0 obj<</Parent 2290 0 R/Title(Returns)/Dest[1809 0 R/XYZ null 528 null]/Prev 2292 0 R/Next 2294 0 R>>endobj
+2294 0 obj<</Parent 2290 0 R/Title(Description)/Dest[1809 0 R/XYZ null 454 null]/Prev 2293 0 R/Next 2295 0 R>>endobj
+2295 0 obj<</Parent 2290 0 R/Title(Example)/Dest[1809 0 R/XYZ null 341 null]/Prev 2294 0 R>>endobj
+2296 0 obj<</Parent 2229 0 R/Count -6/First 2297 0 R/Last 2302 0 R/Title(cupsGetPrinters\(\))/Dest[1812 0 R/XYZ null 738 null]/Prev 2290 0 R/Next 2303 0 R>>endobj
+2297 0 obj<</Parent 2296 0 R/Title(Usage)/Dest[1812 0 R/XYZ null 694 null]/Next 2298 0 R>>endobj
+2298 0 obj<</Parent 2296 0 R/Title(Arguments)/Dest[1812 0 R/XYZ null 628 null]/Prev 2297 0 R/Next 2299 0 R>>endobj
+2299 0 obj<</Parent 2296 0 R/Title(Returns)/Dest[1812 0 R/XYZ null 528 null]/Prev 2298 0 R/Next 2300 0 R>>endobj
+2300 0 obj<</Parent 2296 0 R/Title(Description)/Dest[1812 0 R/XYZ null 468 null]/Prev 2299 0 R/Next 2301 0 R>>endobj
+2301 0 obj<</Parent 2296 0 R/Title(Example)/Dest[1812 0 R/XYZ null 394 null]/Prev 2300 0 R/Next 2302 0 R>>endobj
+2302 0 obj<</Parent 2296 0 R/Title(See Also)/Dest[1812 0 R/XYZ null 145 null]/Prev 2301 0 R>>endobj
+2303 0 obj<</Parent 2229 0 R/Count -5/First 2304 0 R/Last 2308 0 R/Title(cupsLangDefault\(\))/Dest[1815 0 R/XYZ null 738 null]/Prev 2296 0 R/Next 2309 0 R>>endobj
+2304 0 obj<</Parent 2303 0 R/Title(Usage)/Dest[1815 0 R/XYZ null 694 null]/Next 2305 0 R>>endobj
+2305 0 obj<</Parent 2303 0 R/Title(Returns)/Dest[1815 0 R/XYZ null 628 null]/Prev 2304 0 R/Next 2306 0 R>>endobj
+2306 0 obj<</Parent 2303 0 R/Title(Description)/Dest[1815 0 R/XYZ null 568 null]/Prev 2305 0 R/Next 2307 0 R>>endobj
+2307 0 obj<</Parent 2303 0 R/Title(Example)/Dest[1815 0 R/XYZ null 455 null]/Prev 2306 0 R/Next 2308 0 R>>endobj
+2308 0 obj<</Parent 2303 0 R/Title(See Also)/Dest[1815 0 R/XYZ null 303 null]/Prev 2307 0 R>>endobj
+2309 0 obj<</Parent 2229 0 R/Count -6/First 2310 0 R/Last 2315 0 R/Title(cupsLangEncoding\(\))/Dest[1818 0 R/XYZ null 738 null]/Prev 2303 0 R/Next 2316 0 R>>endobj
+2310 0 obj<</Parent 2309 0 R/Title(Usage)/Dest[1818 0 R/XYZ null 694 null]/Next 2311 0 R>>endobj
+2311 0 obj<</Parent 2309 0 R/Title(Arguments)/Dest[1818 0 R/XYZ null 628 null]/Prev 2310 0 R/Next 2312 0 R>>endobj
+2312 0 obj<</Parent 2309 0 R/Title(Returns)/Dest[1818 0 R/XYZ null 528 null]/Prev 2311 0 R/Next 2313 0 R>>endobj
+2313 0 obj<</Parent 2309 0 R/Title(Description)/Dest[1818 0 R/XYZ null 468 null]/Prev 2312 0 R/Next 2314 0 R>>endobj
+2314 0 obj<</Parent 2309 0 R/Title(Example)/Dest[1818 0 R/XYZ null 394 null]/Prev 2313 0 R/Next 2315 0 R>>endobj
+2315 0 obj<</Parent 2309 0 R/Title(See Also)/Dest[1818 0 R/XYZ null 231 null]/Prev 2314 0 R>>endobj
+2316 0 obj<</Parent 2229 0 R/Count -4/First 2317 0 R/Last 2320 0 R/Title(cupsLangFlush\(\))/Dest[1821 0 R/XYZ null 738 null]/Prev 2309 0 R/Next 2321 0 R>>endobj
+2317 0 obj<</Parent 2316 0 R/Title(Usage)/Dest[1821 0 R/XYZ null 694 null]/Next 2318 0 R>>endobj
+2318 0 obj<</Parent 2316 0 R/Title(Description)/Dest[1821 0 R/XYZ null 628 null]/Prev 2317 0 R/Next 2319 0 R>>endobj
+2319 0 obj<</Parent 2316 0 R/Title(Example)/Dest[1821 0 R/XYZ null 568 null]/Prev 2318 0 R/Next 2320 0 R>>endobj
+2320 0 obj<</Parent 2316 0 R/Title(See Also)/Dest[1821 0 R/XYZ null 470 null]/Prev 2319 0 R>>endobj
+2321 0 obj<</Parent 2229 0 R/Count -5/First 2322 0 R/Last 2326 0 R/Title(cupsLangFree\(\))/Dest[1824 0 R/XYZ null 738 null]/Prev 2316 0 R/Next 2327 0 R>>endobj
+2322 0 obj<</Parent 2321 0 R/Title(Usage)/Dest[1824 0 R/XYZ null 694 null]/Next 2323 0 R>>endobj
+2323 0 obj<</Parent 2321 0 R/Title(Arguments)/Dest[1824 0 R/XYZ null 628 null]/Prev 2322 0 R/Next 2324 0 R>>endobj
+2324 0 obj<</Parent 2321 0 R/Title(Description)/Dest[1824 0 R/XYZ null 528 null]/Prev 2323 0 R/Next 2325 0 R>>endobj
+2325 0 obj<</Parent 2321 0 R/Title(Example)/Dest[1824 0 R/XYZ null 468 null]/Prev 2324 0 R/Next 2326 0 R>>endobj
+2326 0 obj<</Parent 2321 0 R/Title(See Also)/Dest[1824 0 R/XYZ null 358 null]/Prev 2325 0 R>>endobj
+2327 0 obj<</Parent 2229 0 R/Count -6/First 2328 0 R/Last 2333 0 R/Title(cupsLangGet\(\))/Dest[1827 0 R/XYZ null 738 null]/Prev 2321 0 R/Next 2334 0 R>>endobj
+2328 0 obj<</Parent 2327 0 R/Title(Usage)/Dest[1827 0 R/XYZ null 694 null]/Next 2329 0 R>>endobj
+2329 0 obj<</Parent 2327 0 R/Title(Arguments)/Dest[1827 0 R/XYZ null 628 null]/Prev 2328 0 R/Next 2330 0 R>>endobj
+2330 0 obj<</Parent 2327 0 R/Title(Returns)/Dest[1827 0 R/XYZ null 528 null]/Prev 2329 0 R/Next 2331 0 R>>endobj
+2331 0 obj<</Parent 2327 0 R/Title(Description)/Dest[1827 0 R/XYZ null 468 null]/Prev 2330 0 R/Next 2332 0 R>>endobj
+2332 0 obj<</Parent 2327 0 R/Title(Example)/Dest[1827 0 R/XYZ null 394 null]/Prev 2331 0 R/Next 2333 0 R>>endobj
+2333 0 obj<</Parent 2327 0 R/Title(See Also)/Dest[1827 0 R/XYZ null 231 null]/Prev 2332 0 R>>endobj
+2334 0 obj<</Parent 2229 0 R/Count -6/First 2335 0 R/Last 2340 0 R/Title(cupsLangString\(\))/Dest[1830 0 R/XYZ null 738 null]/Prev 2327 0 R/Next 2341 0 R>>endobj
+2335 0 obj<</Parent 2334 0 R/Title(Usage)/Dest[1830 0 R/XYZ null 694 null]/Next 2336 0 R>>endobj
+2336 0 obj<</Parent 2334 0 R/Title(Arguments)/Dest[1830 0 R/XYZ null 617 null]/Prev 2335 0 R/Next 2337 0 R>>endobj
+2337 0 obj<</Parent 2334 0 R/Title(Returns)/Dest[1830 0 R/XYZ null 497 null]/Prev 2336 0 R/Next 2338 0 R>>endobj
+2338 0 obj<</Parent 2334 0 R/Title(Description)/Dest[1830 0 R/XYZ null 437 null]/Prev 2337 0 R/Next 2339 0 R>>endobj
+2339 0 obj<</Parent 2334 0 R/Title(Example)/Dest[1830 0 R/XYZ null 377 null]/Prev 2338 0 R/Next 2340 0 R>>endobj
+2340 0 obj<</Parent 2334 0 R/Title(See Also)/Dest[1830 0 R/XYZ null 192 null]/Prev 2339 0 R>>endobj
+2341 0 obj<</Parent 2229 0 R/Count -5/First 2342 0 R/Last 2346 0 R/Title(cupsLastError\(\))/Dest[1833 0 R/XYZ null 738 null]/Prev 2334 0 R/Next 2347 0 R>>endobj
+2342 0 obj<</Parent 2341 0 R/Title(Usage)/Dest[1833 0 R/XYZ null 694 null]/Next 2343 0 R>>endobj
+2343 0 obj<</Parent 2341 0 R/Title(Returns)/Dest[1833 0 R/XYZ null 628 null]/Prev 2342 0 R/Next 2344 0 R>>endobj
+2344 0 obj<</Parent 2341 0 R/Title(Description)/Dest[1833 0 R/XYZ null 568 null]/Prev 2343 0 R/Next 2345 0 R>>endobj
+2345 0 obj<</Parent 2341 0 R/Title(Example)/Dest[1833 0 R/XYZ null 495 null]/Prev 2344 0 R/Next 2346 0 R>>endobj
+2346 0 obj<</Parent 2341 0 R/Title(See Also)/Dest[1833 0 R/XYZ null 375 null]/Prev 2345 0 R>>endobj
+2347 0 obj<</Parent 2229 0 R/Count -6/First 2348 0 R/Last 2353 0 R/Title(cupsMarkOptions\(\))/Dest[1836 0 R/XYZ null 738 null]/Prev 2341 0 R/Next 2354 0 R>>endobj
+2348 0 obj<</Parent 2347 0 R/Title(Usage)/Dest[1836 0 R/XYZ null 694 null]/Next 2349 0 R>>endobj
+2349 0 obj<</Parent 2347 0 R/Title(Arguments)/Dest[1836 0 R/XYZ null 606 null]/Prev 2348 0 R/Next 2350 0 R>>endobj
+2350 0 obj<</Parent 2347 0 R/Title(Returns)/Dest[1836 0 R/XYZ null 466 null]/Prev 2349 0 R/Next 2351 0 R>>endobj
+2351 0 obj<</Parent 2347 0 R/Title(Description)/Dest[1836 0 R/XYZ null 406 null]/Prev 2350 0 R/Next 2352 0 R>>endobj
+2352 0 obj<</Parent 2347 0 R/Title(Example)/Dest[1836 0 R/XYZ null 332 null]/Prev 2351 0 R/Next 2353 0 R>>endobj
+2353 0 obj<</Parent 2347 0 R/Title(See Also)/Dest[1836 0 R/XYZ null 191 null]/Prev 2352 0 R>>endobj
+2354 0 obj<</Parent 2229 0 R/Count -6/First 2355 0 R/Last 2360 0 R/Title(cupsParseOptions\(\))/Dest[1839 0 R/XYZ null 738 null]/Prev 2347 0 R/Next 2361 0 R>>endobj
+2355 0 obj<</Parent 2354 0 R/Title(Usage)/Dest[1839 0 R/XYZ null 694 null]/Next 2356 0 R>>endobj
+2356 0 obj<</Parent 2354 0 R/Title(Arguments)/Dest[1839 0 R/XYZ null 606 null]/Prev 2355 0 R/Next 2357 0 R>>endobj
+2357 0 obj<</Parent 2354 0 R/Title(Returns)/Dest[1839 0 R/XYZ null 466 null]/Prev 2356 0 R/Next 2358 0 R>>endobj
+2358 0 obj<</Parent 2354 0 R/Title(Description)/Dest[1839 0 R/XYZ null 406 null]/Prev 2357 0 R/Next 2359 0 R>>endobj
+2359 0 obj<</Parent 2354 0 R/Title(Example)/Dest[1839 0 R/XYZ null 332 null]/Prev 2358 0 R/Next 2360 0 R>>endobj
+2360 0 obj<</Parent 2354 0 R/Title(See Also)/Dest[1839 0 R/XYZ null 180 null]/Prev 2359 0 R>>endobj
+2361 0 obj<</Parent 2229 0 R/Count -6/First 2362 0 R/Last 2367 0 R/Title(cupsPrintFile\(\))/Dest[1842 0 R/XYZ null 738 null]/Prev 2354 0 R/Next 2368 0 R>>endobj
+2362 0 obj<</Parent 2361 0 R/Title(Usage)/Dest[1842 0 R/XYZ null 694 null]/Next 2363 0 R>>endobj
+2363 0 obj<</Parent 2361 0 R/Title(Arguments)/Dest[1842 0 R/XYZ null 585 null]/Prev 2362 0 R/Next 2364 0 R>>endobj
+2364 0 obj<</Parent 2361 0 R/Title(Returns)/Dest[1842 0 R/XYZ null 404 null]/Prev 2363 0 R/Next 2365 0 R>>endobj
+2365 0 obj<</Parent 2361 0 R/Title(Description)/Dest[1842 0 R/XYZ null 344 null]/Prev 2364 0 R/Next 2366 0 R>>endobj
+2366 0 obj<</Parent 2361 0 R/Title(Example)/Dest[1842 0 R/XYZ null 270 null]/Prev 2365 0 R/Next 2367 0 R>>endobj
+2367 0 obj<</Parent 2361 0 R/Title(See Also)/Dest[1845 0 R/XYZ null 731 null]/Prev 2366 0 R>>endobj
+2368 0 obj<</Parent 2229 0 R/Count -5/First 2369 0 R/Last 2373 0 R/Title(cupsRasterClose\(\))/Dest[1848 0 R/XYZ null 738 null]/Prev 2361 0 R/Next 2374 0 R>>endobj
+2369 0 obj<</Parent 2368 0 R/Title(Usage)/Dest[1848 0 R/XYZ null 694 null]/Next 2370 0 R>>endobj
+2370 0 obj<</Parent 2368 0 R/Title(Arguments)/Dest[1848 0 R/XYZ null 628 null]/Prev 2369 0 R/Next 2371 0 R>>endobj
+2371 0 obj<</Parent 2368 0 R/Title(Description)/Dest[1848 0 R/XYZ null 528 null]/Prev 2370 0 R/Next 2372 0 R>>endobj
+2372 0 obj<</Parent 2368 0 R/Title(Example)/Dest[1848 0 R/XYZ null 468 null]/Prev 2371 0 R/Next 2373 0 R>>endobj
+2373 0 obj<</Parent 2368 0 R/Title(See Also)/Dest[1848 0 R/XYZ null 348 null]/Prev 2372 0 R>>endobj
+2374 0 obj<</Parent 2229 0 R/Count -6/First 2375 0 R/Last 2380 0 R/Title(cupsRasterOpen\(\))/Dest[1851 0 R/XYZ null 738 null]/Prev 2368 0 R/Next 2381 0 R>>endobj
+2375 0 obj<</Parent 2374 0 R/Title(Usage)/Dest[1851 0 R/XYZ null 694 null]/Next 2376 0 R>>endobj
+2376 0 obj<</Parent 2374 0 R/Title(Arguments)/Dest[1851 0 R/XYZ null 617 null]/Prev 2375 0 R/Next 2377 0 R>>endobj
+2377 0 obj<</Parent 2374 0 R/Title(Returns)/Dest[1851 0 R/XYZ null 497 null]/Prev 2376 0 R/Next 2378 0 R>>endobj
+2378 0 obj<</Parent 2374 0 R/Title(Description)/Dest[1851 0 R/XYZ null 437 null]/Prev 2377 0 R/Next 2379 0 R>>endobj
+2379 0 obj<</Parent 2374 0 R/Title(Example)/Dest[1851 0 R/XYZ null 377 null]/Prev 2378 0 R/Next 2380 0 R>>endobj
+2380 0 obj<</Parent 2374 0 R/Title(See Also)/Dest[1851 0 R/XYZ null 257 null]/Prev 2379 0 R>>endobj
+2381 0 obj<</Parent 2229 0 R/Count -6/First 2382 0 R/Last 2387 0 R/Title(cupsRasterReadHeader\(\))/Dest[1854 0 R/XYZ null 738 null]/Prev 2374 0 R/Next 2388 0 R>>endobj
+2382 0 obj<</Parent 2381 0 R/Title(Usage)/Dest[1854 0 R/XYZ null 694 null]/Next 2383 0 R>>endobj
+2383 0 obj<</Parent 2381 0 R/Title(Arguments)/Dest[1854 0 R/XYZ null 617 null]/Prev 2382 0 R/Next 2384 0 R>>endobj
+2384 0 obj<</Parent 2381 0 R/Title(Returns)/Dest[1854 0 R/XYZ null 497 null]/Prev 2383 0 R/Next 2385 0 R>>endobj
+2385 0 obj<</Parent 2381 0 R/Title(Description)/Dest[1854 0 R/XYZ null 437 null]/Prev 2384 0 R/Next 2386 0 R>>endobj
+2386 0 obj<</Parent 2381 0 R/Title(Example)/Dest[1854 0 R/XYZ null 377 null]/Prev 2385 0 R/Next 2387 0 R>>endobj
+2387 0 obj<</Parent 2381 0 R/Title(See Also)/Dest[1857 0 R/XYZ null 731 null]/Prev 2386 0 R>>endobj
+2388 0 obj<</Parent 2229 0 R/Count -6/First 2389 0 R/Last 2394 0 R/Title(cupsRasterReadPixels\(\))/Dest[1860 0 R/XYZ null 738 null]/Prev 2381 0 R/Next 2395 0 R>>endobj
+2389 0 obj<</Parent 2388 0 R/Title(Usage)/Dest[1860 0 R/XYZ null 694 null]/Next 2390 0 R>>endobj
+2390 0 obj<</Parent 2388 0 R/Title(Arguments)/Dest[1860 0 R/XYZ null 606 null]/Prev 2389 0 R/Next 2391 0 R>>endobj
+2391 0 obj<</Parent 2388 0 R/Title(Returns)/Dest[1860 0 R/XYZ null 466 null]/Prev 2390 0 R/Next 2392 0 R>>endobj
+2392 0 obj<</Parent 2388 0 R/Title(Description)/Dest[1860 0 R/XYZ null 406 null]/Prev 2391 0 R/Next 2393 0 R>>endobj
+2393 0 obj<</Parent 2388 0 R/Title(Example)/Dest[1860 0 R/XYZ null 346 null]/Prev 2392 0 R/Next 2394 0 R>>endobj
+2394 0 obj<</Parent 2388 0 R/Title(See Also)/Dest[1863 0 R/XYZ null 731 null]/Prev 2393 0 R>>endobj
+2395 0 obj<</Parent 2229 0 R/Count -6/First 2396 0 R/Last 2401 0 R/Title(cupsRasterWriteHeader\(\))/Dest[1866 0 R/XYZ null 738 null]/Prev 2388 0 R/Next 2402 0 R>>endobj
+2396 0 obj<</Parent 2395 0 R/Title(Usage)/Dest[1866 0 R/XYZ null 694 null]/Next 2397 0 R>>endobj
+2397 0 obj<</Parent 2395 0 R/Title(Arguments)/Dest[1866 0 R/XYZ null 617 null]/Prev 2396 0 R/Next 2398 0 R>>endobj
+2398 0 obj<</Parent 2395 0 R/Title(Returns)/Dest[1866 0 R/XYZ null 497 null]/Prev 2397 0 R/Next 2399 0 R>>endobj
+2399 0 obj<</Parent 2395 0 R/Title(Description)/Dest[1866 0 R/XYZ null 437 null]/Prev 2398 0 R/Next 2400 0 R>>endobj
+2400 0 obj<</Parent 2395 0 R/Title(Example)/Dest[1866 0 R/XYZ null 377 null]/Prev 2399 0 R/Next 2401 0 R>>endobj
+2401 0 obj<</Parent 2395 0 R/Title(See Also)/Dest[1866 0 R/XYZ null 159 null]/Prev 2400 0 R>>endobj
+2402 0 obj<</Parent 2229 0 R/Count -6/First 2403 0 R/Last 2408 0 R/Title(cupsRasterWritePixels\(\))/Dest[1869 0 R/XYZ null 738 null]/Prev 2395 0 R/Next 2409 0 R>>endobj
+2403 0 obj<</Parent 2402 0 R/Title(Usage)/Dest[1869 0 R/XYZ null 694 null]/Next 2404 0 R>>endobj
+2404 0 obj<</Parent 2402 0 R/Title(Arguments)/Dest[1869 0 R/XYZ null 606 null]/Prev 2403 0 R/Next 2405 0 R>>endobj
+2405 0 obj<</Parent 2402 0 R/Title(Returns)/Dest[1869 0 R/XYZ null 466 null]/Prev 2404 0 R/Next 2406 0 R>>endobj
+2406 0 obj<</Parent 2402 0 R/Title(Description)/Dest[1869 0 R/XYZ null 406 null]/Prev 2405 0 R/Next 2407 0 R>>endobj
+2407 0 obj<</Parent 2402 0 R/Title(Example)/Dest[1869 0 R/XYZ null 346 null]/Prev 2406 0 R/Next 2408 0 R>>endobj
+2408 0 obj<</Parent 2402 0 R/Title(See Also)/Dest[1872 0 R/XYZ null 731 null]/Prev 2407 0 R>>endobj
+2409 0 obj<</Parent 2229 0 R/Count -5/First 2410 0 R/Last 2414 0 R/Title(cupsServer\(\))/Dest[1875 0 R/XYZ null 738 null]/Prev 2402 0 R/Next 2415 0 R>>endobj
+2410 0 obj<</Parent 2409 0 R/Title(Usage)/Dest[1875 0 R/XYZ null 694 null]/Next 2411 0 R>>endobj
+2411 0 obj<</Parent 2409 0 R/Title(Returns)/Dest[1875 0 R/XYZ null 628 null]/Prev 2410 0 R/Next 2412 0 R>>endobj
+2412 0 obj<</Parent 2409 0 R/Title(Description)/Dest[1875 0 R/XYZ null 568 null]/Prev 2411 0 R/Next 2413 0 R>>endobj
+2413 0 obj<</Parent 2409 0 R/Title(Example)/Dest[1875 0 R/XYZ null 415 null]/Prev 2412 0 R/Next 2414 0 R>>endobj
+2414 0 obj<</Parent 2409 0 R/Title(See Also)/Dest[1875 0 R/XYZ null 317 null]/Prev 2413 0 R>>endobj
+2415 0 obj<</Parent 2229 0 R/Count -5/First 2416 0 R/Last 2420 0 R/Title(cupsTempFile\(\))/Dest[1878 0 R/XYZ null 738 null]/Prev 2409 0 R/Next 2421 0 R>>endobj
+2416 0 obj<</Parent 2415 0 R/Title(Usage)/Dest[1878 0 R/XYZ null 694 null]/Next 2417 0 R>>endobj
+2417 0 obj<</Parent 2415 0 R/Title(Arguments)/Dest[1878 0 R/XYZ null 617 null]/Prev 2416 0 R/Next 2418 0 R>>endobj
+2418 0 obj<</Parent 2415 0 R/Title(Returns)/Dest[1878 0 R/XYZ null 497 null]/Prev 2417 0 R/Next 2419 0 R>>endobj
+2419 0 obj<</Parent 2415 0 R/Title(Description)/Dest[1878 0 R/XYZ null 437 null]/Prev 2418 0 R/Next 2420 0 R>>endobj
+2420 0 obj<</Parent 2415 0 R/Title(Example)/Dest[1878 0 R/XYZ null 363 null]/Prev 2419 0 R>>endobj
+2421 0 obj<</Parent 2229 0 R/Count -5/First 2422 0 R/Last 2426 0 R/Title(cupsUser\(\))/Dest[1881 0 R/XYZ null 738 null]/Prev 2415 0 R/Next 2427 0 R>>endobj
+2422 0 obj<</Parent 2421 0 R/Title(Usage)/Dest[1881 0 R/XYZ null 694 null]/Next 2423 0 R>>endobj
+2423 0 obj<</Parent 2421 0 R/Title(Returns)/Dest[1881 0 R/XYZ null 628 null]/Prev 2422 0 R/Next 2424 0 R>>endobj
+2424 0 obj<</Parent 2421 0 R/Title(Description)/Dest[1881 0 R/XYZ null 568 null]/Prev 2423 0 R/Next 2425 0 R>>endobj
+2425 0 obj<</Parent 2421 0 R/Title(Example)/Dest[1881 0 R/XYZ null 495 null]/Prev 2424 0 R/Next 2426 0 R>>endobj
+2426 0 obj<</Parent 2421 0 R/Title(See Also)/Dest[1881 0 R/XYZ null 396 null]/Prev 2425 0 R>>endobj
+2427 0 obj<</Parent 2229 0 R/Count -6/First 2428 0 R/Last 2433 0 R/Title(httpBlocking\(\))/Dest[1884 0 R/XYZ null 738 null]/Prev 2421 0 R/Next 2434 0 R>>endobj
+2428 0 obj<</Parent 2427 0 R/Title(Usage)/Dest[1884 0 R/XYZ null 694 null]/Next 2429 0 R>>endobj
+2429 0 obj<</Parent 2427 0 R/Title(Arguments)/Dest[1884 0 R/XYZ null 650 null]/Prev 2428 0 R/Next 2430 0 R>>endobj
+2430 0 obj<</Parent 2427 0 R/Title(Returns)/Dest[1884 0 R/XYZ null 562 null]/Prev 2429 0 R/Next 2431 0 R>>endobj
+2431 0 obj<</Parent 2427 0 R/Title(Description)/Dest[1884 0 R/XYZ null 512 null]/Prev 2430 0 R/Next 2432 0 R>>endobj
+2432 0 obj<</Parent 2427 0 R/Title(Example)/Dest[1884 0 R/XYZ null 462 null]/Prev 2431 0 R/Next 2433 0 R>>endobj
+2433 0 obj<</Parent 2427 0 R/Title(See Also)/Dest[1884 0 R/XYZ null 417 null]/Prev 2432 0 R>>endobj
+2434 0 obj<</Parent 2229 0 R/Count -6/First 2435 0 R/Last 2440 0 R/Title(httpCheck\(\))/Dest[1887 0 R/XYZ null 738 null]/Prev 2427 0 R/Next 2441 0 R>>endobj
+2435 0 obj<</Parent 2434 0 R/Title(Usage)/Dest[1887 0 R/XYZ null 694 null]/Next 2436 0 R>>endobj
+2436 0 obj<</Parent 2434 0 R/Title(Arguments)/Dest[1887 0 R/XYZ null 650 null]/Prev 2435 0 R/Next 2437 0 R>>endobj
+2437 0 obj<</Parent 2434 0 R/Title(Returns)/Dest[1887 0 R/XYZ null 562 null]/Prev 2436 0 R/Next 2438 0 R>>endobj
+2438 0 obj<</Parent 2434 0 R/Title(Description)/Dest[1887 0 R/XYZ null 512 null]/Prev 2437 0 R/Next 2439 0 R>>endobj
+2439 0 obj<</Parent 2434 0 R/Title(Example)/Dest[1887 0 R/XYZ null 462 null]/Prev 2438 0 R/Next 2440 0 R>>endobj
+2440 0 obj<</Parent 2434 0 R/Title(See Also)/Dest[1887 0 R/XYZ null 417 null]/Prev 2439 0 R>>endobj
+2441 0 obj<</Parent 2229 0 R/Count -6/First 2442 0 R/Last 2447 0 R/Title(httpClearFields\(\))/Dest[1890 0 R/XYZ null 738 null]/Prev 2434 0 R/Next 2448 0 R>>endobj
+2442 0 obj<</Parent 2441 0 R/Title(Usage)/Dest[1890 0 R/XYZ null 694 null]/Next 2443 0 R>>endobj
+2443 0 obj<</Parent 2441 0 R/Title(Arguments)/Dest[1890 0 R/XYZ null 650 null]/Prev 2442 0 R/Next 2444 0 R>>endobj
+2444 0 obj<</Parent 2441 0 R/Title(Returns)/Dest[1890 0 R/XYZ null 562 null]/Prev 2443 0 R/Next 2445 0 R>>endobj
+2445 0 obj<</Parent 2441 0 R/Title(Description)/Dest[1890 0 R/XYZ null 512 null]/Prev 2444 0 R/Next 2446 0 R>>endobj
+2446 0 obj<</Parent 2441 0 R/Title(Example)/Dest[1890 0 R/XYZ null 462 null]/Prev 2445 0 R/Next 2447 0 R>>endobj
+2447 0 obj<</Parent 2441 0 R/Title(See Also)/Dest[1890 0 R/XYZ null 417 null]/Prev 2446 0 R>>endobj
+2448 0 obj<</Parent 2229 0 R/Count -6/First 2449 0 R/Last 2454 0 R/Title(httpClose\(\))/Dest[1893 0 R/XYZ null 738 null]/Prev 2441 0 R/Next 2455 0 R>>endobj
+2449 0 obj<</Parent 2448 0 R/Title(Usage)/Dest[1893 0 R/XYZ null 694 null]/Next 2450 0 R>>endobj
+2450 0 obj<</Parent 2448 0 R/Title(Arguments)/Dest[1893 0 R/XYZ null 650 null]/Prev 2449 0 R/Next 2451 0 R>>endobj
+2451 0 obj<</Parent 2448 0 R/Title(Returns)/Dest[1893 0 R/XYZ null 562 null]/Prev 2450 0 R/Next 2452 0 R>>endobj
+2452 0 obj<</Parent 2448 0 R/Title(Description)/Dest[1893 0 R/XYZ null 512 null]/Prev 2451 0 R/Next 2453 0 R>>endobj
+2453 0 obj<</Parent 2448 0 R/Title(Example)/Dest[1893 0 R/XYZ null 462 null]/Prev 2452 0 R/Next 2454 0 R>>endobj
+2454 0 obj<</Parent 2448 0 R/Title(See Also)/Dest[1893 0 R/XYZ null 417 null]/Prev 2453 0 R>>endobj
+2455 0 obj<</Parent 2229 0 R/Count -6/First 2456 0 R/Last 2461 0 R/Title(httpConnect\(\))/Dest[1896 0 R/XYZ null 738 null]/Prev 2448 0 R/Next 2462 0 R>>endobj
+2456 0 obj<</Parent 2455 0 R/Title(Usage)/Dest[1896 0 R/XYZ null 694 null]/Next 2457 0 R>>endobj
+2457 0 obj<</Parent 2455 0 R/Title(Arguments)/Dest[1896 0 R/XYZ null 650 null]/Prev 2456 0 R/Next 2458 0 R>>endobj
+2458 0 obj<</Parent 2455 0 R/Title(Returns)/Dest[1896 0 R/XYZ null 562 null]/Prev 2457 0 R/Next 2459 0 R>>endobj
+2459 0 obj<</Parent 2455 0 R/Title(Description)/Dest[1896 0 R/XYZ null 512 null]/Prev 2458 0 R/Next 2460 0 R>>endobj
+2460 0 obj<</Parent 2455 0 R/Title(Example)/Dest[1896 0 R/XYZ null 462 null]/Prev 2459 0 R/Next 2461 0 R>>endobj
+2461 0 obj<</Parent 2455 0 R/Title(See Also)/Dest[1896 0 R/XYZ null 417 null]/Prev 2460 0 R>>endobj
+2462 0 obj<</Parent 2229 0 R/Count -6/First 2463 0 R/Last 2468 0 R/Title(httpDecode64\(\))/Dest[1899 0 R/XYZ null 738 null]/Prev 2455 0 R/Next 2469 0 R>>endobj
+2463 0 obj<</Parent 2462 0 R/Title(Usage)/Dest[1899 0 R/XYZ null 694 null]/Next 2464 0 R>>endobj
+2464 0 obj<</Parent 2462 0 R/Title(Arguments)/Dest[1899 0 R/XYZ null 650 null]/Prev 2463 0 R/Next 2465 0 R>>endobj
+2465 0 obj<</Parent 2462 0 R/Title(Returns)/Dest[1899 0 R/XYZ null 562 null]/Prev 2464 0 R/Next 2466 0 R>>endobj
+2466 0 obj<</Parent 2462 0 R/Title(Description)/Dest[1899 0 R/XYZ null 512 null]/Prev 2465 0 R/Next 2467 0 R>>endobj
+2467 0 obj<</Parent 2462 0 R/Title(Example)/Dest[1899 0 R/XYZ null 462 null]/Prev 2466 0 R/Next 2468 0 R>>endobj
+2468 0 obj<</Parent 2462 0 R/Title(See Also)/Dest[1899 0 R/XYZ null 417 null]/Prev 2467 0 R>>endobj
+2469 0 obj<</Parent 2229 0 R/Count -6/First 2470 0 R/Last 2475 0 R/Title(httpDelete\(\))/Dest[1902 0 R/XYZ null 738 null]/Prev 2462 0 R/Next 2476 0 R>>endobj
+2470 0 obj<</Parent 2469 0 R/Title(Usage)/Dest[1902 0 R/XYZ null 694 null]/Next 2471 0 R>>endobj
+2471 0 obj<</Parent 2469 0 R/Title(Arguments)/Dest[1902 0 R/XYZ null 650 null]/Prev 2470 0 R/Next 2472 0 R>>endobj
+2472 0 obj<</Parent 2469 0 R/Title(Returns)/Dest[1902 0 R/XYZ null 562 null]/Prev 2471 0 R/Next 2473 0 R>>endobj
+2473 0 obj<</Parent 2469 0 R/Title(Description)/Dest[1902 0 R/XYZ null 512 null]/Prev 2472 0 R/Next 2474 0 R>>endobj
+2474 0 obj<</Parent 2469 0 R/Title(Example)/Dest[1902 0 R/XYZ null 462 null]/Prev 2473 0 R/Next 2475 0 R>>endobj
+2475 0 obj<</Parent 2469 0 R/Title(See Also)/Dest[1902 0 R/XYZ null 417 null]/Prev 2474 0 R>>endobj
+2476 0 obj<</Parent 2229 0 R/Count -6/First 2477 0 R/Last 2482 0 R/Title(httpEncode64\(\))/Dest[1905 0 R/XYZ null 738 null]/Prev 2469 0 R/Next 2483 0 R>>endobj
+2477 0 obj<</Parent 2476 0 R/Title(Usage)/Dest[1905 0 R/XYZ null 694 null]/Next 2478 0 R>>endobj
+2478 0 obj<</Parent 2476 0 R/Title(Arguments)/Dest[1905 0 R/XYZ null 650 null]/Prev 2477 0 R/Next 2479 0 R>>endobj
+2479 0 obj<</Parent 2476 0 R/Title(Returns)/Dest[1905 0 R/XYZ null 562 null]/Prev 2478 0 R/Next 2480 0 R>>endobj
+2480 0 obj<</Parent 2476 0 R/Title(Description)/Dest[1905 0 R/XYZ null 512 null]/Prev 2479 0 R/Next 2481 0 R>>endobj
+2481 0 obj<</Parent 2476 0 R/Title(Example)/Dest[1905 0 R/XYZ null 462 null]/Prev 2480 0 R/Next 2482 0 R>>endobj
+2482 0 obj<</Parent 2476 0 R/Title(See Also)/Dest[1905 0 R/XYZ null 417 null]/Prev 2481 0 R>>endobj
+2483 0 obj<</Parent 2229 0 R/Count -6/First 2484 0 R/Last 2489 0 R/Title(httpError\(\))/Dest[1908 0 R/XYZ null 738 null]/Prev 2476 0 R/Next 2490 0 R>>endobj
+2484 0 obj<</Parent 2483 0 R/Title(Usage)/Dest[1908 0 R/XYZ null 694 null]/Next 2485 0 R>>endobj
+2485 0 obj<</Parent 2483 0 R/Title(Arguments)/Dest[1908 0 R/XYZ null 650 null]/Prev 2484 0 R/Next 2486 0 R>>endobj
+2486 0 obj<</Parent 2483 0 R/Title(Returns)/Dest[1908 0 R/XYZ null 562 null]/Prev 2485 0 R/Next 2487 0 R>>endobj
+2487 0 obj<</Parent 2483 0 R/Title(Description)/Dest[1908 0 R/XYZ null 512 null]/Prev 2486 0 R/Next 2488 0 R>>endobj
+2488 0 obj<</Parent 2483 0 R/Title(Example)/Dest[1908 0 R/XYZ null 462 null]/Prev 2487 0 R/Next 2489 0 R>>endobj
+2489 0 obj<</Parent 2483 0 R/Title(See Also)/Dest[1908 0 R/XYZ null 417 null]/Prev 2488 0 R>>endobj
+2490 0 obj<</Parent 2229 0 R/Count -6/First 2491 0 R/Last 2496 0 R/Title(httpFlush\(\))/Dest[1911 0 R/XYZ null 738 null]/Prev 2483 0 R/Next 2497 0 R>>endobj
+2491 0 obj<</Parent 2490 0 R/Title(Usage)/Dest[1911 0 R/XYZ null 694 null]/Next 2492 0 R>>endobj
+2492 0 obj<</Parent 2490 0 R/Title(Arguments)/Dest[1911 0 R/XYZ null 650 null]/Prev 2491 0 R/Next 2493 0 R>>endobj
+2493 0 obj<</Parent 2490 0 R/Title(Returns)/Dest[1911 0 R/XYZ null 562 null]/Prev 2492 0 R/Next 2494 0 R>>endobj
+2494 0 obj<</Parent 2490 0 R/Title(Description)/Dest[1911 0 R/XYZ null 512 null]/Prev 2493 0 R/Next 2495 0 R>>endobj
+2495 0 obj<</Parent 2490 0 R/Title(Example)/Dest[1911 0 R/XYZ null 462 null]/Prev 2494 0 R/Next 2496 0 R>>endobj
+2496 0 obj<</Parent 2490 0 R/Title(See Also)/Dest[1911 0 R/XYZ null 417 null]/Prev 2495 0 R>>endobj
+2497 0 obj<</Parent 2229 0 R/Count -6/First 2498 0 R/Last 2503 0 R/Title(httpGet\(\))/Dest[1914 0 R/XYZ null 738 null]/Prev 2490 0 R/Next 2504 0 R>>endobj
+2498 0 obj<</Parent 2497 0 R/Title(Usage)/Dest[1914 0 R/XYZ null 694 null]/Next 2499 0 R>>endobj
+2499 0 obj<</Parent 2497 0 R/Title(Arguments)/Dest[1914 0 R/XYZ null 650 null]/Prev 2498 0 R/Next 2500 0 R>>endobj
+2500 0 obj<</Parent 2497 0 R/Title(Returns)/Dest[1914 0 R/XYZ null 562 null]/Prev 2499 0 R/Next 2501 0 R>>endobj
+2501 0 obj<</Parent 2497 0 R/Title(Description)/Dest[1914 0 R/XYZ null 512 null]/Prev 2500 0 R/Next 2502 0 R>>endobj
+2502 0 obj<</Parent 2497 0 R/Title(Example)/Dest[1914 0 R/XYZ null 462 null]/Prev 2501 0 R/Next 2503 0 R>>endobj
+2503 0 obj<</Parent 2497 0 R/Title(See Also)/Dest[1914 0 R/XYZ null 417 null]/Prev 2502 0 R>>endobj
+2504 0 obj<</Parent 2229 0 R/Count -6/First 2505 0 R/Last 2510 0 R/Title(httpGets\(\))/Dest[1917 0 R/XYZ null 738 null]/Prev 2497 0 R/Next 2511 0 R>>endobj
+2505 0 obj<</Parent 2504 0 R/Title(Usage)/Dest[1917 0 R/XYZ null 694 null]/Next 2506 0 R>>endobj
+2506 0 obj<</Parent 2504 0 R/Title(Arguments)/Dest[1917 0 R/XYZ null 650 null]/Prev 2505 0 R/Next 2507 0 R>>endobj
+2507 0 obj<</Parent 2504 0 R/Title(Returns)/Dest[1917 0 R/XYZ null 562 null]/Prev 2506 0 R/Next 2508 0 R>>endobj
+2508 0 obj<</Parent 2504 0 R/Title(Description)/Dest[1917 0 R/XYZ null 512 null]/Prev 2507 0 R/Next 2509 0 R>>endobj
+2509 0 obj<</Parent 2504 0 R/Title(Example)/Dest[1917 0 R/XYZ null 462 null]/Prev 2508 0 R/Next 2510 0 R>>endobj
+2510 0 obj<</Parent 2504 0 R/Title(See Also)/Dest[1917 0 R/XYZ null 417 null]/Prev 2509 0 R>>endobj
+2511 0 obj<</Parent 2229 0 R/Count -6/First 2512 0 R/Last 2517 0 R/Title(httpGetDateString\(\))/Dest[1920 0 R/XYZ null 738 null]/Prev 2504 0 R/Next 2518 0 R>>endobj
+2512 0 obj<</Parent 2511 0 R/Title(Usage)/Dest[1920 0 R/XYZ null 694 null]/Next 2513 0 R>>endobj
+2513 0 obj<</Parent 2511 0 R/Title(Arguments)/Dest[1920 0 R/XYZ null 650 null]/Prev 2512 0 R/Next 2514 0 R>>endobj
+2514 0 obj<</Parent 2511 0 R/Title(Returns)/Dest[1920 0 R/XYZ null 562 null]/Prev 2513 0 R/Next 2515 0 R>>endobj
+2515 0 obj<</Parent 2511 0 R/Title(Description)/Dest[1920 0 R/XYZ null 512 null]/Prev 2514 0 R/Next 2516 0 R>>endobj
+2516 0 obj<</Parent 2511 0 R/Title(Example)/Dest[1920 0 R/XYZ null 462 null]/Prev 2515 0 R/Next 2517 0 R>>endobj
+2517 0 obj<</Parent 2511 0 R/Title(See Also)/Dest[1920 0 R/XYZ null 417 null]/Prev 2516 0 R>>endobj
+2518 0 obj<</Parent 2229 0 R/Count -6/First 2519 0 R/Last 2524 0 R/Title(httpGetDateTime\(\))/Dest[1923 0 R/XYZ null 738 null]/Prev 2511 0 R/Next 2525 0 R>>endobj
+2519 0 obj<</Parent 2518 0 R/Title(Usage)/Dest[1923 0 R/XYZ null 694 null]/Next 2520 0 R>>endobj
+2520 0 obj<</Parent 2518 0 R/Title(Arguments)/Dest[1923 0 R/XYZ null 650 null]/Prev 2519 0 R/Next 2521 0 R>>endobj
+2521 0 obj<</Parent 2518 0 R/Title(Returns)/Dest[1923 0 R/XYZ null 562 null]/Prev 2520 0 R/Next 2522 0 R>>endobj
+2522 0 obj<</Parent 2518 0 R/Title(Description)/Dest[1923 0 R/XYZ null 512 null]/Prev 2521 0 R/Next 2523 0 R>>endobj
+2523 0 obj<</Parent 2518 0 R/Title(Example)/Dest[1923 0 R/XYZ null 462 null]/Prev 2522 0 R/Next 2524 0 R>>endobj
+2524 0 obj<</Parent 2518 0 R/Title(See Also)/Dest[1923 0 R/XYZ null 417 null]/Prev 2523 0 R>>endobj
+2525 0 obj<</Parent 2229 0 R/Count -6/First 2526 0 R/Last 2531 0 R/Title(httpGetField\(\))/Dest[1926 0 R/XYZ null 738 null]/Prev 2518 0 R/Next 2532 0 R>>endobj
+2526 0 obj<</Parent 2525 0 R/Title(Usage)/Dest[1926 0 R/XYZ null 694 null]/Next 2527 0 R>>endobj
+2527 0 obj<</Parent 2525 0 R/Title(Arguments)/Dest[1926 0 R/XYZ null 650 null]/Prev 2526 0 R/Next 2528 0 R>>endobj
+2528 0 obj<</Parent 2525 0 R/Title(Returns)/Dest[1926 0 R/XYZ null 562 null]/Prev 2527 0 R/Next 2529 0 R>>endobj
+2529 0 obj<</Parent 2525 0 R/Title(Description)/Dest[1926 0 R/XYZ null 512 null]/Prev 2528 0 R/Next 2530 0 R>>endobj
+2530 0 obj<</Parent 2525 0 R/Title(Example)/Dest[1926 0 R/XYZ null 462 null]/Prev 2529 0 R/Next 2531 0 R>>endobj
+2531 0 obj<</Parent 2525 0 R/Title(See Also)/Dest[1926 0 R/XYZ null 417 null]/Prev 2530 0 R>>endobj
+2532 0 obj<</Parent 2229 0 R/Count -6/First 2533 0 R/Last 2538 0 R/Title(httpGetLength\(\))/Dest[1929 0 R/XYZ null 738 null]/Prev 2525 0 R/Next 2539 0 R>>endobj
+2533 0 obj<</Parent 2532 0 R/Title(Usage)/Dest[1929 0 R/XYZ null 694 null]/Next 2534 0 R>>endobj
+2534 0 obj<</Parent 2532 0 R/Title(Arguments)/Dest[1929 0 R/XYZ null 650 null]/Prev 2533 0 R/Next 2535 0 R>>endobj
+2535 0 obj<</Parent 2532 0 R/Title(Returns)/Dest[1929 0 R/XYZ null 562 null]/Prev 2534 0 R/Next 2536 0 R>>endobj
+2536 0 obj<</Parent 2532 0 R/Title(Description)/Dest[1929 0 R/XYZ null 512 null]/Prev 2535 0 R/Next 2537 0 R>>endobj
+2537 0 obj<</Parent 2532 0 R/Title(Example)/Dest[1929 0 R/XYZ null 462 null]/Prev 2536 0 R/Next 2538 0 R>>endobj
+2538 0 obj<</Parent 2532 0 R/Title(See Also)/Dest[1929 0 R/XYZ null 417 null]/Prev 2537 0 R>>endobj
+2539 0 obj<</Parent 2229 0 R/Count -6/First 2540 0 R/Last 2545 0 R/Title(httpHead\(\))/Dest[1932 0 R/XYZ null 738 null]/Prev 2532 0 R/Next 2546 0 R>>endobj
+2540 0 obj<</Parent 2539 0 R/Title(Usage)/Dest[1932 0 R/XYZ null 694 null]/Next 2541 0 R>>endobj
+2541 0 obj<</Parent 2539 0 R/Title(Arguments)/Dest[1932 0 R/XYZ null 650 null]/Prev 2540 0 R/Next 2542 0 R>>endobj
+2542 0 obj<</Parent 2539 0 R/Title(Returns)/Dest[1932 0 R/XYZ null 562 null]/Prev 2541 0 R/Next 2543 0 R>>endobj
+2543 0 obj<</Parent 2539 0 R/Title(Description)/Dest[1932 0 R/XYZ null 512 null]/Prev 2542 0 R/Next 2544 0 R>>endobj
+2544 0 obj<</Parent 2539 0 R/Title(Example)/Dest[1932 0 R/XYZ null 462 null]/Prev 2543 0 R/Next 2545 0 R>>endobj
+2545 0 obj<</Parent 2539 0 R/Title(See Also)/Dest[1932 0 R/XYZ null 417 null]/Prev 2544 0 R>>endobj
+2546 0 obj<</Parent 2229 0 R/Count -6/First 2547 0 R/Last 2552 0 R/Title(httpInitialize\(\))/Dest[1935 0 R/XYZ null 738 null]/Prev 2539 0 R/Next 2553 0 R>>endobj
+2547 0 obj<</Parent 2546 0 R/Title(Usage)/Dest[1935 0 R/XYZ null 694 null]/Next 2548 0 R>>endobj
+2548 0 obj<</Parent 2546 0 R/Title(Arguments)/Dest[1935 0 R/XYZ null 650 null]/Prev 2547 0 R/Next 2549 0 R>>endobj
+2549 0 obj<</Parent 2546 0 R/Title(Returns)/Dest[1935 0 R/XYZ null 562 null]/Prev 2548 0 R/Next 2550 0 R>>endobj
+2550 0 obj<</Parent 2546 0 R/Title(Description)/Dest[1935 0 R/XYZ null 512 null]/Prev 2549 0 R/Next 2551 0 R>>endobj
+2551 0 obj<</Parent 2546 0 R/Title(Example)/Dest[1935 0 R/XYZ null 462 null]/Prev 2550 0 R/Next 2552 0 R>>endobj
+2552 0 obj<</Parent 2546 0 R/Title(See Also)/Dest[1935 0 R/XYZ null 417 null]/Prev 2551 0 R>>endobj
+2553 0 obj<</Parent 2229 0 R/Count -6/First 2554 0 R/Last 2559 0 R/Title(httpOptions\(\))/Dest[1938 0 R/XYZ null 738 null]/Prev 2546 0 R/Next 2560 0 R>>endobj
+2554 0 obj<</Parent 2553 0 R/Title(Usage)/Dest[1938 0 R/XYZ null 694 null]/Next 2555 0 R>>endobj
+2555 0 obj<</Parent 2553 0 R/Title(Arguments)/Dest[1938 0 R/XYZ null 650 null]/Prev 2554 0 R/Next 2556 0 R>>endobj
+2556 0 obj<</Parent 2553 0 R/Title(Returns)/Dest[1938 0 R/XYZ null 562 null]/Prev 2555 0 R/Next 2557 0 R>>endobj
+2557 0 obj<</Parent 2553 0 R/Title(Description)/Dest[1938 0 R/XYZ null 512 null]/Prev 2556 0 R/Next 2558 0 R>>endobj
+2558 0 obj<</Parent 2553 0 R/Title(Example)/Dest[1938 0 R/XYZ null 462 null]/Prev 2557 0 R/Next 2559 0 R>>endobj
+2559 0 obj<</Parent 2553 0 R/Title(See Also)/Dest[1938 0 R/XYZ null 417 null]/Prev 2558 0 R>>endobj
+2560 0 obj<</Parent 2229 0 R/Count -6/First 2561 0 R/Last 2566 0 R/Title(httpPost\(\))/Dest[1941 0 R/XYZ null 738 null]/Prev 2553 0 R/Next 2567 0 R>>endobj
+2561 0 obj<</Parent 2560 0 R/Title(Usage)/Dest[1941 0 R/XYZ null 694 null]/Next 2562 0 R>>endobj
+2562 0 obj<</Parent 2560 0 R/Title(Arguments)/Dest[1941 0 R/XYZ null 650 null]/Prev 2561 0 R/Next 2563 0 R>>endobj
+2563 0 obj<</Parent 2560 0 R/Title(Returns)/Dest[1941 0 R/XYZ null 562 null]/Prev 2562 0 R/Next 2564 0 R>>endobj
+2564 0 obj<</Parent 2560 0 R/Title(Description)/Dest[1941 0 R/XYZ null 512 null]/Prev 2563 0 R/Next 2565 0 R>>endobj
+2565 0 obj<</Parent 2560 0 R/Title(Example)/Dest[1941 0 R/XYZ null 462 null]/Prev 2564 0 R/Next 2566 0 R>>endobj
+2566 0 obj<</Parent 2560 0 R/Title(See Also)/Dest[1941 0 R/XYZ null 417 null]/Prev 2565 0 R>>endobj
+2567 0 obj<</Parent 2229 0 R/Count -6/First 2568 0 R/Last 2573 0 R/Title(httpPrintf\(\))/Dest[1944 0 R/XYZ null 738 null]/Prev 2560 0 R/Next 2574 0 R>>endobj
+2568 0 obj<</Parent 2567 0 R/Title(Usage)/Dest[1944 0 R/XYZ null 694 null]/Next 2569 0 R>>endobj
+2569 0 obj<</Parent 2567 0 R/Title(Arguments)/Dest[1944 0 R/XYZ null 650 null]/Prev 2568 0 R/Next 2570 0 R>>endobj
+2570 0 obj<</Parent 2567 0 R/Title(Returns)/Dest[1944 0 R/XYZ null 562 null]/Prev 2569 0 R/Next 2571 0 R>>endobj
+2571 0 obj<</Parent 2567 0 R/Title(Description)/Dest[1944 0 R/XYZ null 512 null]/Prev 2570 0 R/Next 2572 0 R>>endobj
+2572 0 obj<</Parent 2567 0 R/Title(Example)/Dest[1944 0 R/XYZ null 462 null]/Prev 2571 0 R/Next 2573 0 R>>endobj
+2573 0 obj<</Parent 2567 0 R/Title(See Also)/Dest[1944 0 R/XYZ null 417 null]/Prev 2572 0 R>>endobj
+2574 0 obj<</Parent 2229 0 R/Count -6/First 2575 0 R/Last 2580 0 R/Title(httpPut\(\))/Dest[1947 0 R/XYZ null 738 null]/Prev 2567 0 R/Next 2581 0 R>>endobj
+2575 0 obj<</Parent 2574 0 R/Title(Usage)/Dest[1947 0 R/XYZ null 694 null]/Next 2576 0 R>>endobj
+2576 0 obj<</Parent 2574 0 R/Title(Arguments)/Dest[1947 0 R/XYZ null 650 null]/Prev 2575 0 R/Next 2577 0 R>>endobj
+2577 0 obj<</Parent 2574 0 R/Title(Returns)/Dest[1947 0 R/XYZ null 562 null]/Prev 2576 0 R/Next 2578 0 R>>endobj
+2578 0 obj<</Parent 2574 0 R/Title(Description)/Dest[1947 0 R/XYZ null 512 null]/Prev 2577 0 R/Next 2579 0 R>>endobj
+2579 0 obj<</Parent 2574 0 R/Title(Example)/Dest[1947 0 R/XYZ null 462 null]/Prev 2578 0 R/Next 2580 0 R>>endobj
+2580 0 obj<</Parent 2574 0 R/Title(See Also)/Dest[1947 0 R/XYZ null 417 null]/Prev 2579 0 R>>endobj
+2581 0 obj<</Parent 2229 0 R/Count -6/First 2582 0 R/Last 2587 0 R/Title(httpRead\(\))/Dest[1950 0 R/XYZ null 738 null]/Prev 2574 0 R/Next 2588 0 R>>endobj
+2582 0 obj<</Parent 2581 0 R/Title(Usage)/Dest[1950 0 R/XYZ null 694 null]/Next 2583 0 R>>endobj
+2583 0 obj<</Parent 2581 0 R/Title(Arguments)/Dest[1950 0 R/XYZ null 650 null]/Prev 2582 0 R/Next 2584 0 R>>endobj
+2584 0 obj<</Parent 2581 0 R/Title(Returns)/Dest[1950 0 R/XYZ null 562 null]/Prev 2583 0 R/Next 2585 0 R>>endobj
+2585 0 obj<</Parent 2581 0 R/Title(Description)/Dest[1950 0 R/XYZ null 512 null]/Prev 2584 0 R/Next 2586 0 R>>endobj
+2586 0 obj<</Parent 2581 0 R/Title(Example)/Dest[1950 0 R/XYZ null 462 null]/Prev 2585 0 R/Next 2587 0 R>>endobj
+2587 0 obj<</Parent 2581 0 R/Title(See Also)/Dest[1950 0 R/XYZ null 417 null]/Prev 2586 0 R>>endobj
+2588 0 obj<</Parent 2229 0 R/Count -6/First 2589 0 R/Last 2594 0 R/Title(httpReconnect\(\))/Dest[1953 0 R/XYZ null 738 null]/Prev 2581 0 R/Next 2595 0 R>>endobj
+2589 0 obj<</Parent 2588 0 R/Title(Usage)/Dest[1953 0 R/XYZ null 694 null]/Next 2590 0 R>>endobj
+2590 0 obj<</Parent 2588 0 R/Title(Arguments)/Dest[1953 0 R/XYZ null 650 null]/Prev 2589 0 R/Next 2591 0 R>>endobj
+2591 0 obj<</Parent 2588 0 R/Title(Returns)/Dest[1953 0 R/XYZ null 562 null]/Prev 2590 0 R/Next 2592 0 R>>endobj
+2592 0 obj<</Parent 2588 0 R/Title(Description)/Dest[1953 0 R/XYZ null 512 null]/Prev 2591 0 R/Next 2593 0 R>>endobj
+2593 0 obj<</Parent 2588 0 R/Title(Example)/Dest[1953 0 R/XYZ null 462 null]/Prev 2592 0 R/Next 2594 0 R>>endobj
+2594 0 obj<</Parent 2588 0 R/Title(See Also)/Dest[1953 0 R/XYZ null 417 null]/Prev 2593 0 R>>endobj
+2595 0 obj<</Parent 2229 0 R/Count -6/First 2596 0 R/Last 2601 0 R/Title(httpSeparate\(\))/Dest[1956 0 R/XYZ null 738 null]/Prev 2588 0 R/Next 2602 0 R>>endobj
+2596 0 obj<</Parent 2595 0 R/Title(Usage)/Dest[1956 0 R/XYZ null 694 null]/Next 2597 0 R>>endobj
+2597 0 obj<</Parent 2595 0 R/Title(Arguments)/Dest[1956 0 R/XYZ null 650 null]/Prev 2596 0 R/Next 2598 0 R>>endobj
+2598 0 obj<</Parent 2595 0 R/Title(Returns)/Dest[1956 0 R/XYZ null 562 null]/Prev 2597 0 R/Next 2599 0 R>>endobj
+2599 0 obj<</Parent 2595 0 R/Title(Description)/Dest[1956 0 R/XYZ null 512 null]/Prev 2598 0 R/Next 2600 0 R>>endobj
+2600 0 obj<</Parent 2595 0 R/Title(Example)/Dest[1956 0 R/XYZ null 462 null]/Prev 2599 0 R/Next 2601 0 R>>endobj
+2601 0 obj<</Parent 2595 0 R/Title(See Also)/Dest[1956 0 R/XYZ null 417 null]/Prev 2600 0 R>>endobj
+2602 0 obj<</Parent 2229 0 R/Count -6/First 2603 0 R/Last 2608 0 R/Title(httpSetField\(\))/Dest[1959 0 R/XYZ null 738 null]/Prev 2595 0 R/Next 2609 0 R>>endobj
+2603 0 obj<</Parent 2602 0 R/Title(Usage)/Dest[1959 0 R/XYZ null 694 null]/Next 2604 0 R>>endobj
+2604 0 obj<</Parent 2602 0 R/Title(Arguments)/Dest[1959 0 R/XYZ null 650 null]/Prev 2603 0 R/Next 2605 0 R>>endobj
+2605 0 obj<</Parent 2602 0 R/Title(Returns)/Dest[1959 0 R/XYZ null 562 null]/Prev 2604 0 R/Next 2606 0 R>>endobj
+2606 0 obj<</Parent 2602 0 R/Title(Description)/Dest[1959 0 R/XYZ null 512 null]/Prev 2605 0 R/Next 2607 0 R>>endobj
+2607 0 obj<</Parent 2602 0 R/Title(Example)/Dest[1959 0 R/XYZ null 462 null]/Prev 2606 0 R/Next 2608 0 R>>endobj
+2608 0 obj<</Parent 2602 0 R/Title(See Also)/Dest[1959 0 R/XYZ null 417 null]/Prev 2607 0 R>>endobj
+2609 0 obj<</Parent 2229 0 R/Count -6/First 2610 0 R/Last 2615 0 R/Title(httpTrace\(\))/Dest[1962 0 R/XYZ null 738 null]/Prev 2602 0 R/Next 2616 0 R>>endobj
+2610 0 obj<</Parent 2609 0 R/Title(Usage)/Dest[1962 0 R/XYZ null 694 null]/Next 2611 0 R>>endobj
+2611 0 obj<</Parent 2609 0 R/Title(Arguments)/Dest[1962 0 R/XYZ null 650 null]/Prev 2610 0 R/Next 2612 0 R>>endobj
+2612 0 obj<</Parent 2609 0 R/Title(Returns)/Dest[1962 0 R/XYZ null 562 null]/Prev 2611 0 R/Next 2613 0 R>>endobj
+2613 0 obj<</Parent 2609 0 R/Title(Description)/Dest[1962 0 R/XYZ null 512 null]/Prev 2612 0 R/Next 2614 0 R>>endobj
+2614 0 obj<</Parent 2609 0 R/Title(Example)/Dest[1962 0 R/XYZ null 462 null]/Prev 2613 0 R/Next 2615 0 R>>endobj
+2615 0 obj<</Parent 2609 0 R/Title(See Also)/Dest[1962 0 R/XYZ null 417 null]/Prev 2614 0 R>>endobj
+2616 0 obj<</Parent 2229 0 R/Count -6/First 2617 0 R/Last 2622 0 R/Title(httpUpdate\(\))/Dest[1965 0 R/XYZ null 738 null]/Prev 2609 0 R/Next 2623 0 R>>endobj
+2617 0 obj<</Parent 2616 0 R/Title(Usage)/Dest[1965 0 R/XYZ null 694 null]/Next 2618 0 R>>endobj
+2618 0 obj<</Parent 2616 0 R/Title(Arguments)/Dest[1965 0 R/XYZ null 650 null]/Prev 2617 0 R/Next 2619 0 R>>endobj
+2619 0 obj<</Parent 2616 0 R/Title(Returns)/Dest[1965 0 R/XYZ null 562 null]/Prev 2618 0 R/Next 2620 0 R>>endobj
+2620 0 obj<</Parent 2616 0 R/Title(Description)/Dest[1965 0 R/XYZ null 512 null]/Prev 2619 0 R/Next 2621 0 R>>endobj
+2621 0 obj<</Parent 2616 0 R/Title(Example)/Dest[1965 0 R/XYZ null 462 null]/Prev 2620 0 R/Next 2622 0 R>>endobj
+2622 0 obj<</Parent 2616 0 R/Title(See Also)/Dest[1965 0 R/XYZ null 417 null]/Prev 2621 0 R>>endobj
+2623 0 obj<</Parent 2229 0 R/Count -6/First 2624 0 R/Last 2629 0 R/Title(httpWrite\(\))/Dest[1968 0 R/XYZ null 738 null]/Prev 2616 0 R/Next 2630 0 R>>endobj
+2624 0 obj<</Parent 2623 0 R/Title(Usage)/Dest[1968 0 R/XYZ null 694 null]/Next 2625 0 R>>endobj
+2625 0 obj<</Parent 2623 0 R/Title(Arguments)/Dest[1968 0 R/XYZ null 650 null]/Prev 2624 0 R/Next 2626 0 R>>endobj
+2626 0 obj<</Parent 2623 0 R/Title(Returns)/Dest[1968 0 R/XYZ null 562 null]/Prev 2625 0 R/Next 2627 0 R>>endobj
+2627 0 obj<</Parent 2623 0 R/Title(Description)/Dest[1968 0 R/XYZ null 512 null]/Prev 2626 0 R/Next 2628 0 R>>endobj
+2628 0 obj<</Parent 2623 0 R/Title(Example)/Dest[1968 0 R/XYZ null 462 null]/Prev 2627 0 R/Next 2629 0 R>>endobj
+2629 0 obj<</Parent 2623 0 R/Title(See Also)/Dest[1968 0 R/XYZ null 417 null]/Prev 2628 0 R>>endobj
+2630 0 obj<</Parent 2229 0 R/Count -6/First 2631 0 R/Last 2636 0 R/Title(ippAddBoolean\(\))/Dest[1971 0 R/XYZ null 738 null]/Prev 2623 0 R/Next 2637 0 R>>endobj
+2631 0 obj<</Parent 2630 0 R/Title(Usage)/Dest[1971 0 R/XYZ null 694 null]/Next 2632 0 R>>endobj
+2632 0 obj<</Parent 2630 0 R/Title(Arguments)/Dest[1971 0 R/XYZ null 650 null]/Prev 2631 0 R/Next 2633 0 R>>endobj
+2633 0 obj<</Parent 2630 0 R/Title(Returns)/Dest[1971 0 R/XYZ null 562 null]/Prev 2632 0 R/Next 2634 0 R>>endobj
+2634 0 obj<</Parent 2630 0 R/Title(Description)/Dest[1971 0 R/XYZ null 512 null]/Prev 2633 0 R/Next 2635 0 R>>endobj
+2635 0 obj<</Parent 2630 0 R/Title(Example)/Dest[1971 0 R/XYZ null 462 null]/Prev 2634 0 R/Next 2636 0 R>>endobj
+2636 0 obj<</Parent 2630 0 R/Title(See Also)/Dest[1971 0 R/XYZ null 417 null]/Prev 2635 0 R>>endobj
+2637 0 obj<</Parent 2229 0 R/Count -6/First 2638 0 R/Last 2643 0 R/Title(ippAddBooleans\(\))/Dest[1974 0 R/XYZ null 738 null]/Prev 2630 0 R/Next 2644 0 R>>endobj
+2638 0 obj<</Parent 2637 0 R/Title(Usage)/Dest[1974 0 R/XYZ null 694 null]/Next 2639 0 R>>endobj
+2639 0 obj<</Parent 2637 0 R/Title(Arguments)/Dest[1974 0 R/XYZ null 650 null]/Prev 2638 0 R/Next 2640 0 R>>endobj
+2640 0 obj<</Parent 2637 0 R/Title(Returns)/Dest[1974 0 R/XYZ null 562 null]/Prev 2639 0 R/Next 2641 0 R>>endobj
+2641 0 obj<</Parent 2637 0 R/Title(Description)/Dest[1974 0 R/XYZ null 512 null]/Prev 2640 0 R/Next 2642 0 R>>endobj
+2642 0 obj<</Parent 2637 0 R/Title(Example)/Dest[1974 0 R/XYZ null 462 null]/Prev 2641 0 R/Next 2643 0 R>>endobj
+2643 0 obj<</Parent 2637 0 R/Title(See Also)/Dest[1974 0 R/XYZ null 417 null]/Prev 2642 0 R>>endobj
+2644 0 obj<</Parent 2229 0 R/Count -6/First 2645 0 R/Last 2650 0 R/Title(ippAddDate\(\))/Dest[1977 0 R/XYZ null 738 null]/Prev 2637 0 R/Next 2651 0 R>>endobj
+2645 0 obj<</Parent 2644 0 R/Title(Usage)/Dest[1977 0 R/XYZ null 694 null]/Next 2646 0 R>>endobj
+2646 0 obj<</Parent 2644 0 R/Title(Arguments)/Dest[1977 0 R/XYZ null 650 null]/Prev 2645 0 R/Next 2647 0 R>>endobj
+2647 0 obj<</Parent 2644 0 R/Title(Returns)/Dest[1977 0 R/XYZ null 562 null]/Prev 2646 0 R/Next 2648 0 R>>endobj
+2648 0 obj<</Parent 2644 0 R/Title(Description)/Dest[1977 0 R/XYZ null 512 null]/Prev 2647 0 R/Next 2649 0 R>>endobj
+2649 0 obj<</Parent 2644 0 R/Title(Example)/Dest[1977 0 R/XYZ null 462 null]/Prev 2648 0 R/Next 2650 0 R>>endobj
+2650 0 obj<</Parent 2644 0 R/Title(See Also)/Dest[1977 0 R/XYZ null 417 null]/Prev 2649 0 R>>endobj
+2651 0 obj<</Parent 2229 0 R/Count -6/First 2652 0 R/Last 2657 0 R/Title(ippAddInteger\(\))/Dest[1980 0 R/XYZ null 738 null]/Prev 2644 0 R/Next 2658 0 R>>endobj
+2652 0 obj<</Parent 2651 0 R/Title(Usage)/Dest[1980 0 R/XYZ null 694 null]/Next 2653 0 R>>endobj
+2653 0 obj<</Parent 2651 0 R/Title(Arguments)/Dest[1980 0 R/XYZ null 650 null]/Prev 2652 0 R/Next 2654 0 R>>endobj
+2654 0 obj<</Parent 2651 0 R/Title(Returns)/Dest[1980 0 R/XYZ null 562 null]/Prev 2653 0 R/Next 2655 0 R>>endobj
+2655 0 obj<</Parent 2651 0 R/Title(Description)/Dest[1980 0 R/XYZ null 512 null]/Prev 2654 0 R/Next 2656 0 R>>endobj
+2656 0 obj<</Parent 2651 0 R/Title(Example)/Dest[1980 0 R/XYZ null 462 null]/Prev 2655 0 R/Next 2657 0 R>>endobj
+2657 0 obj<</Parent 2651 0 R/Title(See Also)/Dest[1980 0 R/XYZ null 417 null]/Prev 2656 0 R>>endobj
+2658 0 obj<</Parent 2229 0 R/Count -6/First 2659 0 R/Last 2664 0 R/Title(ippAddIntegers\(\))/Dest[1983 0 R/XYZ null 738 null]/Prev 2651 0 R/Next 2665 0 R>>endobj
+2659 0 obj<</Parent 2658 0 R/Title(Usage)/Dest[1983 0 R/XYZ null 694 null]/Next 2660 0 R>>endobj
+2660 0 obj<</Parent 2658 0 R/Title(Arguments)/Dest[1983 0 R/XYZ null 650 null]/Prev 2659 0 R/Next 2661 0 R>>endobj
+2661 0 obj<</Parent 2658 0 R/Title(Returns)/Dest[1983 0 R/XYZ null 562 null]/Prev 2660 0 R/Next 2662 0 R>>endobj
+2662 0 obj<</Parent 2658 0 R/Title(Description)/Dest[1983 0 R/XYZ null 512 null]/Prev 2661 0 R/Next 2663 0 R>>endobj
+2663 0 obj<</Parent 2658 0 R/Title(Example)/Dest[1983 0 R/XYZ null 462 null]/Prev 2662 0 R/Next 2664 0 R>>endobj
+2664 0 obj<</Parent 2658 0 R/Title(See Also)/Dest[1983 0 R/XYZ null 417 null]/Prev 2663 0 R>>endobj
+2665 0 obj<</Parent 2229 0 R/Count -6/First 2666 0 R/Last 2671 0 R/Title(ippAddRange\(\))/Dest[1986 0 R/XYZ null 738 null]/Prev 2658 0 R/Next 2672 0 R>>endobj
+2666 0 obj<</Parent 2665 0 R/Title(Usage)/Dest[1986 0 R/XYZ null 694 null]/Next 2667 0 R>>endobj
+2667 0 obj<</Parent 2665 0 R/Title(Arguments)/Dest[1986 0 R/XYZ null 650 null]/Prev 2666 0 R/Next 2668 0 R>>endobj
+2668 0 obj<</Parent 2665 0 R/Title(Returns)/Dest[1986 0 R/XYZ null 562 null]/Prev 2667 0 R/Next 2669 0 R>>endobj
+2669 0 obj<</Parent 2665 0 R/Title(Description)/Dest[1986 0 R/XYZ null 512 null]/Prev 2668 0 R/Next 2670 0 R>>endobj
+2670 0 obj<</Parent 2665 0 R/Title(Example)/Dest[1986 0 R/XYZ null 462 null]/Prev 2669 0 R/Next 2671 0 R>>endobj
+2671 0 obj<</Parent 2665 0 R/Title(See Also)/Dest[1986 0 R/XYZ null 417 null]/Prev 2670 0 R>>endobj
+2672 0 obj<</Parent 2229 0 R/Count -6/First 2673 0 R/Last 2678 0 R/Title(ippAddRanges\(\))/Dest[1989 0 R/XYZ null 738 null]/Prev 2665 0 R/Next 2679 0 R>>endobj
+2673 0 obj<</Parent 2672 0 R/Title(Usage)/Dest[1989 0 R/XYZ null 694 null]/Next 2674 0 R>>endobj
+2674 0 obj<</Parent 2672 0 R/Title(Arguments)/Dest[1989 0 R/XYZ null 650 null]/Prev 2673 0 R/Next 2675 0 R>>endobj
+2675 0 obj<</Parent 2672 0 R/Title(Returns)/Dest[1989 0 R/XYZ null 562 null]/Prev 2674 0 R/Next 2676 0 R>>endobj
+2676 0 obj<</Parent 2672 0 R/Title(Description)/Dest[1989 0 R/XYZ null 512 null]/Prev 2675 0 R/Next 2677 0 R>>endobj
+2677 0 obj<</Parent 2672 0 R/Title(Example)/Dest[1989 0 R/XYZ null 462 null]/Prev 2676 0 R/Next 2678 0 R>>endobj
+2678 0 obj<</Parent 2672 0 R/Title(See Also)/Dest[1989 0 R/XYZ null 417 null]/Prev 2677 0 R>>endobj
+2679 0 obj<</Parent 2229 0 R/Count -6/First 2680 0 R/Last 2685 0 R/Title(ippAddResolution\(\))/Dest[1992 0 R/XYZ null 738 null]/Prev 2672 0 R/Next 2686 0 R>>endobj
+2680 0 obj<</Parent 2679 0 R/Title(Usage)/Dest[1992 0 R/XYZ null 694 null]/Next 2681 0 R>>endobj
+2681 0 obj<</Parent 2679 0 R/Title(Arguments)/Dest[1992 0 R/XYZ null 650 null]/Prev 2680 0 R/Next 2682 0 R>>endobj
+2682 0 obj<</Parent 2679 0 R/Title(Returns)/Dest[1992 0 R/XYZ null 562 null]/Prev 2681 0 R/Next 2683 0 R>>endobj
+2683 0 obj<</Parent 2679 0 R/Title(Description)/Dest[1992 0 R/XYZ null 512 null]/Prev 2682 0 R/Next 2684 0 R>>endobj
+2684 0 obj<</Parent 2679 0 R/Title(Example)/Dest[1992 0 R/XYZ null 462 null]/Prev 2683 0 R/Next 2685 0 R>>endobj
+2685 0 obj<</Parent 2679 0 R/Title(See Also)/Dest[1992 0 R/XYZ null 417 null]/Prev 2684 0 R>>endobj
+2686 0 obj<</Parent 2229 0 R/Count -6/First 2687 0 R/Last 2692 0 R/Title(ippAddResolutions\(\))/Dest[1995 0 R/XYZ null 738 null]/Prev 2679 0 R/Next 2693 0 R>>endobj
+2687 0 obj<</Parent 2686 0 R/Title(Usage)/Dest[1995 0 R/XYZ null 694 null]/Next 2688 0 R>>endobj
+2688 0 obj<</Parent 2686 0 R/Title(Arguments)/Dest[1995 0 R/XYZ null 650 null]/Prev 2687 0 R/Next 2689 0 R>>endobj
+2689 0 obj<</Parent 2686 0 R/Title(Returns)/Dest[1995 0 R/XYZ null 562 null]/Prev 2688 0 R/Next 2690 0 R>>endobj
+2690 0 obj<</Parent 2686 0 R/Title(Description)/Dest[1995 0 R/XYZ null 512 null]/Prev 2689 0 R/Next 2691 0 R>>endobj
+2691 0 obj<</Parent 2686 0 R/Title(Example)/Dest[1995 0 R/XYZ null 462 null]/Prev 2690 0 R/Next 2692 0 R>>endobj
+2692 0 obj<</Parent 2686 0 R/Title(See Also)/Dest[1995 0 R/XYZ null 417 null]/Prev 2691 0 R>>endobj
+2693 0 obj<</Parent 2229 0 R/Count -6/First 2694 0 R/Last 2699 0 R/Title(ippAddSeparator\(\))/Dest[1998 0 R/XYZ null 738 null]/Prev 2686 0 R/Next 2700 0 R>>endobj
+2694 0 obj<</Parent 2693 0 R/Title(Usage)/Dest[1998 0 R/XYZ null 694 null]/Next 2695 0 R>>endobj
+2695 0 obj<</Parent 2693 0 R/Title(Arguments)/Dest[1998 0 R/XYZ null 650 null]/Prev 2694 0 R/Next 2696 0 R>>endobj
+2696 0 obj<</Parent 2693 0 R/Title(Returns)/Dest[1998 0 R/XYZ null 562 null]/Prev 2695 0 R/Next 2697 0 R>>endobj
+2697 0 obj<</Parent 2693 0 R/Title(Description)/Dest[1998 0 R/XYZ null 512 null]/Prev 2696 0 R/Next 2698 0 R>>endobj
+2698 0 obj<</Parent 2693 0 R/Title(Example)/Dest[1998 0 R/XYZ null 462 null]/Prev 2697 0 R/Next 2699 0 R>>endobj
+2699 0 obj<</Parent 2693 0 R/Title(See Also)/Dest[1998 0 R/XYZ null 417 null]/Prev 2698 0 R>>endobj
+2700 0 obj<</Parent 2229 0 R/Count -6/First 2701 0 R/Last 2706 0 R/Title(ippAddString\(\))/Dest[2001 0 R/XYZ null 738 null]/Prev 2693 0 R/Next 2707 0 R>>endobj
+2701 0 obj<</Parent 2700 0 R/Title(Usage)/Dest[2001 0 R/XYZ null 694 null]/Next 2702 0 R>>endobj
+2702 0 obj<</Parent 2700 0 R/Title(Arguments)/Dest[2001 0 R/XYZ null 650 null]/Prev 2701 0 R/Next 2703 0 R>>endobj
+2703 0 obj<</Parent 2700 0 R/Title(Returns)/Dest[2001 0 R/XYZ null 562 null]/Prev 2702 0 R/Next 2704 0 R>>endobj
+2704 0 obj<</Parent 2700 0 R/Title(Description)/Dest[2001 0 R/XYZ null 512 null]/Prev 2703 0 R/Next 2705 0 R>>endobj
+2705 0 obj<</Parent 2700 0 R/Title(Example)/Dest[2001 0 R/XYZ null 462 null]/Prev 2704 0 R/Next 2706 0 R>>endobj
+2706 0 obj<</Parent 2700 0 R/Title(See Also)/Dest[2001 0 R/XYZ null 417 null]/Prev 2705 0 R>>endobj
+2707 0 obj<</Parent 2229 0 R/Count -6/First 2708 0 R/Last 2713 0 R/Title(ippAddStrings\(\))/Dest[2004 0 R/XYZ null 738 null]/Prev 2700 0 R/Next 2714 0 R>>endobj
+2708 0 obj<</Parent 2707 0 R/Title(Usage)/Dest[2004 0 R/XYZ null 694 null]/Next 2709 0 R>>endobj
+2709 0 obj<</Parent 2707 0 R/Title(Arguments)/Dest[2004 0 R/XYZ null 650 null]/Prev 2708 0 R/Next 2710 0 R>>endobj
+2710 0 obj<</Parent 2707 0 R/Title(Returns)/Dest[2004 0 R/XYZ null 562 null]/Prev 2709 0 R/Next 2711 0 R>>endobj
+2711 0 obj<</Parent 2707 0 R/Title(Description)/Dest[2004 0 R/XYZ null 512 null]/Prev 2710 0 R/Next 2712 0 R>>endobj
+2712 0 obj<</Parent 2707 0 R/Title(Example)/Dest[2004 0 R/XYZ null 462 null]/Prev 2711 0 R/Next 2713 0 R>>endobj
+2713 0 obj<</Parent 2707 0 R/Title(See Also)/Dest[2004 0 R/XYZ null 417 null]/Prev 2712 0 R>>endobj
+2714 0 obj<</Parent 2229 0 R/Count -6/First 2715 0 R/Last 2720 0 R/Title(ippDateToTime\(\))/Dest[2007 0 R/XYZ null 738 null]/Prev 2707 0 R/Next 2721 0 R>>endobj
+2715 0 obj<</Parent 2714 0 R/Title(Usage)/Dest[2007 0 R/XYZ null 694 null]/Next 2716 0 R>>endobj
+2716 0 obj<</Parent 2714 0 R/Title(Arguments)/Dest[2007 0 R/XYZ null 650 null]/Prev 2715 0 R/Next 2717 0 R>>endobj
+2717 0 obj<</Parent 2714 0 R/Title(Returns)/Dest[2007 0 R/XYZ null 562 null]/Prev 2716 0 R/Next 2718 0 R>>endobj
+2718 0 obj<</Parent 2714 0 R/Title(Description)/Dest[2007 0 R/XYZ null 512 null]/Prev 2717 0 R/Next 2719 0 R>>endobj
+2719 0 obj<</Parent 2714 0 R/Title(Example)/Dest[2007 0 R/XYZ null 462 null]/Prev 2718 0 R/Next 2720 0 R>>endobj
+2720 0 obj<</Parent 2714 0 R/Title(See Also)/Dest[2007 0 R/XYZ null 417 null]/Prev 2719 0 R>>endobj
+2721 0 obj<</Parent 2229 0 R/Count -6/First 2722 0 R/Last 2727 0 R/Title(ippDelete\(\))/Dest[2010 0 R/XYZ null 738 null]/Prev 2714 0 R/Next 2728 0 R>>endobj
+2722 0 obj<</Parent 2721 0 R/Title(Usage)/Dest[2010 0 R/XYZ null 694 null]/Next 2723 0 R>>endobj
+2723 0 obj<</Parent 2721 0 R/Title(Arguments)/Dest[2010 0 R/XYZ null 650 null]/Prev 2722 0 R/Next 2724 0 R>>endobj
+2724 0 obj<</Parent 2721 0 R/Title(Returns)/Dest[2010 0 R/XYZ null 562 null]/Prev 2723 0 R/Next 2725 0 R>>endobj
+2725 0 obj<</Parent 2721 0 R/Title(Description)/Dest[2010 0 R/XYZ null 512 null]/Prev 2724 0 R/Next 2726 0 R>>endobj
+2726 0 obj<</Parent 2721 0 R/Title(Example)/Dest[2010 0 R/XYZ null 462 null]/Prev 2725 0 R/Next 2727 0 R>>endobj
+2727 0 obj<</Parent 2721 0 R/Title(See Also)/Dest[2010 0 R/XYZ null 417 null]/Prev 2726 0 R>>endobj
+2728 0 obj<</Parent 2229 0 R/Count -6/First 2729 0 R/Last 2734 0 R/Title(ippFindAttribute\(\))/Dest[2013 0 R/XYZ null 738 null]/Prev 2721 0 R/Next 2735 0 R>>endobj
+2729 0 obj<</Parent 2728 0 R/Title(Usage)/Dest[2013 0 R/XYZ null 694 null]/Next 2730 0 R>>endobj
+2730 0 obj<</Parent 2728 0 R/Title(Arguments)/Dest[2013 0 R/XYZ null 650 null]/Prev 2729 0 R/Next 2731 0 R>>endobj
+2731 0 obj<</Parent 2728 0 R/Title(Returns)/Dest[2013 0 R/XYZ null 562 null]/Prev 2730 0 R/Next 2732 0 R>>endobj
+2732 0 obj<</Parent 2728 0 R/Title(Description)/Dest[2013 0 R/XYZ null 512 null]/Prev 2731 0 R/Next 2733 0 R>>endobj
+2733 0 obj<</Parent 2728 0 R/Title(Example)/Dest[2013 0 R/XYZ null 462 null]/Prev 2732 0 R/Next 2734 0 R>>endobj
+2734 0 obj<</Parent 2728 0 R/Title(See Also)/Dest[2013 0 R/XYZ null 417 null]/Prev 2733 0 R>>endobj
+2735 0 obj<</Parent 2229 0 R/Count -6/First 2736 0 R/Last 2741 0 R/Title(ippLength\(\))/Dest[2016 0 R/XYZ null 738 null]/Prev 2728 0 R/Next 2742 0 R>>endobj
+2736 0 obj<</Parent 2735 0 R/Title(Usage)/Dest[2016 0 R/XYZ null 694 null]/Next 2737 0 R>>endobj
+2737 0 obj<</Parent 2735 0 R/Title(Arguments)/Dest[2016 0 R/XYZ null 650 null]/Prev 2736 0 R/Next 2738 0 R>>endobj
+2738 0 obj<</Parent 2735 0 R/Title(Returns)/Dest[2016 0 R/XYZ null 562 null]/Prev 2737 0 R/Next 2739 0 R>>endobj
+2739 0 obj<</Parent 2735 0 R/Title(Description)/Dest[2016 0 R/XYZ null 512 null]/Prev 2738 0 R/Next 2740 0 R>>endobj
+2740 0 obj<</Parent 2735 0 R/Title(Example)/Dest[2016 0 R/XYZ null 462 null]/Prev 2739 0 R/Next 2741 0 R>>endobj
+2741 0 obj<</Parent 2735 0 R/Title(See Also)/Dest[2016 0 R/XYZ null 417 null]/Prev 2740 0 R>>endobj
+2742 0 obj<</Parent 2229 0 R/Count -6/First 2743 0 R/Last 2748 0 R/Title(ippNew\(\))/Dest[2019 0 R/XYZ null 738 null]/Prev 2735 0 R/Next 2749 0 R>>endobj
+2743 0 obj<</Parent 2742 0 R/Title(Usage)/Dest[2019 0 R/XYZ null 694 null]/Next 2744 0 R>>endobj
+2744 0 obj<</Parent 2742 0 R/Title(Arguments)/Dest[2019 0 R/XYZ null 650 null]/Prev 2743 0 R/Next 2745 0 R>>endobj
+2745 0 obj<</Parent 2742 0 R/Title(Returns)/Dest[2019 0 R/XYZ null 562 null]/Prev 2744 0 R/Next 2746 0 R>>endobj
+2746 0 obj<</Parent 2742 0 R/Title(Description)/Dest[2019 0 R/XYZ null 512 null]/Prev 2745 0 R/Next 2747 0 R>>endobj
+2747 0 obj<</Parent 2742 0 R/Title(Example)/Dest[2019 0 R/XYZ null 462 null]/Prev 2746 0 R/Next 2748 0 R>>endobj
+2748 0 obj<</Parent 2742 0 R/Title(See Also)/Dest[2019 0 R/XYZ null 417 null]/Prev 2747 0 R>>endobj
+2749 0 obj<</Parent 2229 0 R/Count -6/First 2750 0 R/Last 2755 0 R/Title(ippPort\(\))/Dest[2022 0 R/XYZ null 738 null]/Prev 2742 0 R/Next 2756 0 R>>endobj
+2750 0 obj<</Parent 2749 0 R/Title(Usage)/Dest[2022 0 R/XYZ null 694 null]/Next 2751 0 R>>endobj
+2751 0 obj<</Parent 2749 0 R/Title(Arguments)/Dest[2022 0 R/XYZ null 650 null]/Prev 2750 0 R/Next 2752 0 R>>endobj
+2752 0 obj<</Parent 2749 0 R/Title(Returns)/Dest[2022 0 R/XYZ null 562 null]/Prev 2751 0 R/Next 2753 0 R>>endobj
+2753 0 obj<</Parent 2749 0 R/Title(Description)/Dest[2022 0 R/XYZ null 512 null]/Prev 2752 0 R/Next 2754 0 R>>endobj
+2754 0 obj<</Parent 2749 0 R/Title(Example)/Dest[2022 0 R/XYZ null 462 null]/Prev 2753 0 R/Next 2755 0 R>>endobj
+2755 0 obj<</Parent 2749 0 R/Title(See Also)/Dest[2022 0 R/XYZ null 417 null]/Prev 2754 0 R>>endobj
+2756 0 obj<</Parent 2229 0 R/Count -6/First 2757 0 R/Last 2762 0 R/Title(ippRead\(\))/Dest[2025 0 R/XYZ null 738 null]/Prev 2749 0 R/Next 2763 0 R>>endobj
+2757 0 obj<</Parent 2756 0 R/Title(Usage)/Dest[2025 0 R/XYZ null 694 null]/Next 2758 0 R>>endobj
+2758 0 obj<</Parent 2756 0 R/Title(Arguments)/Dest[2025 0 R/XYZ null 650 null]/Prev 2757 0 R/Next 2759 0 R>>endobj
+2759 0 obj<</Parent 2756 0 R/Title(Returns)/Dest[2025 0 R/XYZ null 562 null]/Prev 2758 0 R/Next 2760 0 R>>endobj
+2760 0 obj<</Parent 2756 0 R/Title(Description)/Dest[2025 0 R/XYZ null 512 null]/Prev 2759 0 R/Next 2761 0 R>>endobj
+2761 0 obj<</Parent 2756 0 R/Title(Example)/Dest[2025 0 R/XYZ null 462 null]/Prev 2760 0 R/Next 2762 0 R>>endobj
+2762 0 obj<</Parent 2756 0 R/Title(See Also)/Dest[2025 0 R/XYZ null 417 null]/Prev 2761 0 R>>endobj
+2763 0 obj<</Parent 2229 0 R/Count -6/First 2764 0 R/Last 2769 0 R/Title(ippTimeToDate\(\))/Dest[2028 0 R/XYZ null 738 null]/Prev 2756 0 R/Next 2770 0 R>>endobj
+2764 0 obj<</Parent 2763 0 R/Title(Usage)/Dest[2028 0 R/XYZ null 694 null]/Next 2765 0 R>>endobj
+2765 0 obj<</Parent 2763 0 R/Title(Arguments)/Dest[2028 0 R/XYZ null 650 null]/Prev 2764 0 R/Next 2766 0 R>>endobj
+2766 0 obj<</Parent 2763 0 R/Title(Returns)/Dest[2028 0 R/XYZ null 562 null]/Prev 2765 0 R/Next 2767 0 R>>endobj
+2767 0 obj<</Parent 2763 0 R/Title(Description)/Dest[2028 0 R/XYZ null 512 null]/Prev 2766 0 R/Next 2768 0 R>>endobj
+2768 0 obj<</Parent 2763 0 R/Title(Example)/Dest[2028 0 R/XYZ null 462 null]/Prev 2767 0 R/Next 2769 0 R>>endobj
+2769 0 obj<</Parent 2763 0 R/Title(See Also)/Dest[2028 0 R/XYZ null 417 null]/Prev 2768 0 R>>endobj
+2770 0 obj<</Parent 2229 0 R/Count -6/First 2771 0 R/Last 2776 0 R/Title(ippWrite\(\))/Dest[2031 0 R/XYZ null 738 null]/Prev 2763 0 R/Next 2777 0 R>>endobj
+2771 0 obj<</Parent 2770 0 R/Title(Usage)/Dest[2031 0 R/XYZ null 694 null]/Next 2772 0 R>>endobj
+2772 0 obj<</Parent 2770 0 R/Title(Arguments)/Dest[2031 0 R/XYZ null 650 null]/Prev 2771 0 R/Next 2773 0 R>>endobj
+2773 0 obj<</Parent 2770 0 R/Title(Returns)/Dest[2031 0 R/XYZ null 562 null]/Prev 2772 0 R/Next 2774 0 R>>endobj
+2774 0 obj<</Parent 2770 0 R/Title(Description)/Dest[2031 0 R/XYZ null 512 null]/Prev 2773 0 R/Next 2775 0 R>>endobj
+2775 0 obj<</Parent 2770 0 R/Title(Example)/Dest[2031 0 R/XYZ null 462 null]/Prev 2774 0 R/Next 2776 0 R>>endobj
+2776 0 obj<</Parent 2770 0 R/Title(See Also)/Dest[2031 0 R/XYZ null 417 null]/Prev 2775 0 R>>endobj
+2777 0 obj<</Parent 2229 0 R/Count -6/First 2778 0 R/Last 2783 0 R/Title(ppdClose\(\))/Dest[2034 0 R/XYZ null 738 null]/Prev 2770 0 R/Next 2784 0 R>>endobj
+2778 0 obj<</Parent 2777 0 R/Title(Usage)/Dest[2034 0 R/XYZ null 694 null]/Next 2779 0 R>>endobj
+2779 0 obj<</Parent 2777 0 R/Title(Arguments)/Dest[2034 0 R/XYZ null 650 null]/Prev 2778 0 R/Next 2780 0 R>>endobj
+2780 0 obj<</Parent 2777 0 R/Title(Returns)/Dest[2034 0 R/XYZ null 562 null]/Prev 2779 0 R/Next 2781 0 R>>endobj
+2781 0 obj<</Parent 2777 0 R/Title(Description)/Dest[2034 0 R/XYZ null 512 null]/Prev 2780 0 R/Next 2782 0 R>>endobj
+2782 0 obj<</Parent 2777 0 R/Title(Example)/Dest[2034 0 R/XYZ null 462 null]/Prev 2781 0 R/Next 2783 0 R>>endobj
+2783 0 obj<</Parent 2777 0 R/Title(See Also)/Dest[2034 0 R/XYZ null 417 null]/Prev 2782 0 R>>endobj
+2784 0 obj<</Parent 2229 0 R/Count -6/First 2785 0 R/Last 2790 0 R/Title(ppdConflicts\(\))/Dest[2037 0 R/XYZ null 738 null]/Prev 2777 0 R/Next 2791 0 R>>endobj
+2785 0 obj<</Parent 2784 0 R/Title(Usage)/Dest[2037 0 R/XYZ null 694 null]/Next 2786 0 R>>endobj
+2786 0 obj<</Parent 2784 0 R/Title(Arguments)/Dest[2037 0 R/XYZ null 650 null]/Prev 2785 0 R/Next 2787 0 R>>endobj
+2787 0 obj<</Parent 2784 0 R/Title(Returns)/Dest[2037 0 R/XYZ null 562 null]/Prev 2786 0 R/Next 2788 0 R>>endobj
+2788 0 obj<</Parent 2784 0 R/Title(Description)/Dest[2037 0 R/XYZ null 512 null]/Prev 2787 0 R/Next 2789 0 R>>endobj
+2789 0 obj<</Parent 2784 0 R/Title(Example)/Dest[2037 0 R/XYZ null 462 null]/Prev 2788 0 R/Next 2790 0 R>>endobj
+2790 0 obj<</Parent 2784 0 R/Title(See Also)/Dest[2037 0 R/XYZ null 417 null]/Prev 2789 0 R>>endobj
+2791 0 obj<</Parent 2229 0 R/Count -6/First 2792 0 R/Last 2797 0 R/Title(pddEmitFd\(\))/Dest[2040 0 R/XYZ null 738 null]/Prev 2784 0 R/Next 2798 0 R>>endobj
+2792 0 obj<</Parent 2791 0 R/Title(Usage)/Dest[2040 0 R/XYZ null 694 null]/Next 2793 0 R>>endobj
+2793 0 obj<</Parent 2791 0 R/Title(Arguments)/Dest[2040 0 R/XYZ null 650 null]/Prev 2792 0 R/Next 2794 0 R>>endobj
+2794 0 obj<</Parent 2791 0 R/Title(Returns)/Dest[2040 0 R/XYZ null 562 null]/Prev 2793 0 R/Next 2795 0 R>>endobj
+2795 0 obj<</Parent 2791 0 R/Title(Description)/Dest[2040 0 R/XYZ null 512 null]/Prev 2794 0 R/Next 2796 0 R>>endobj
+2796 0 obj<</Parent 2791 0 R/Title(Example)/Dest[2040 0 R/XYZ null 462 null]/Prev 2795 0 R/Next 2797 0 R>>endobj
+2797 0 obj<</Parent 2791 0 R/Title(See Also)/Dest[2040 0 R/XYZ null 417 null]/Prev 2796 0 R>>endobj
+2798 0 obj<</Parent 2229 0 R/Count -6/First 2799 0 R/Last 2804 0 R/Title(ppdEmit\(\))/Dest[2043 0 R/XYZ null 738 null]/Prev 2791 0 R/Next 2805 0 R>>endobj
+2799 0 obj<</Parent 2798 0 R/Title(Usage)/Dest[2043 0 R/XYZ null 694 null]/Next 2800 0 R>>endobj
+2800 0 obj<</Parent 2798 0 R/Title(Arguments)/Dest[2043 0 R/XYZ null 650 null]/Prev 2799 0 R/Next 2801 0 R>>endobj
+2801 0 obj<</Parent 2798 0 R/Title(Returns)/Dest[2043 0 R/XYZ null 562 null]/Prev 2800 0 R/Next 2802 0 R>>endobj
+2802 0 obj<</Parent 2798 0 R/Title(Description)/Dest[2043 0 R/XYZ null 512 null]/Prev 2801 0 R/Next 2803 0 R>>endobj
+2803 0 obj<</Parent 2798 0 R/Title(Example)/Dest[2043 0 R/XYZ null 462 null]/Prev 2802 0 R/Next 2804 0 R>>endobj
+2804 0 obj<</Parent 2798 0 R/Title(See Also)/Dest[2043 0 R/XYZ null 417 null]/Prev 2803 0 R>>endobj
+2805 0 obj<</Parent 2229 0 R/Count -6/First 2806 0 R/Last 2811 0 R/Title(ppdFindChoice\(\))/Dest[2046 0 R/XYZ null 738 null]/Prev 2798 0 R/Next 2812 0 R>>endobj
+2806 0 obj<</Parent 2805 0 R/Title(Usage)/Dest[2046 0 R/XYZ null 694 null]/Next 2807 0 R>>endobj
+2807 0 obj<</Parent 2805 0 R/Title(Arguments)/Dest[2046 0 R/XYZ null 650 null]/Prev 2806 0 R/Next 2808 0 R>>endobj
+2808 0 obj<</Parent 2805 0 R/Title(Returns)/Dest[2046 0 R/XYZ null 562 null]/Prev 2807 0 R/Next 2809 0 R>>endobj
+2809 0 obj<</Parent 2805 0 R/Title(Description)/Dest[2046 0 R/XYZ null 512 null]/Prev 2808 0 R/Next 2810 0 R>>endobj
+2810 0 obj<</Parent 2805 0 R/Title(Example)/Dest[2046 0 R/XYZ null 462 null]/Prev 2809 0 R/Next 2811 0 R>>endobj
+2811 0 obj<</Parent 2805 0 R/Title(See Also)/Dest[2046 0 R/XYZ null 417 null]/Prev 2810 0 R>>endobj
+2812 0 obj<</Parent 2229 0 R/Count -6/First 2813 0 R/Last 2818 0 R/Title(ppdFindMarkedChoice\(\))/Dest[2049 0 R/XYZ null 738 null]/Prev 2805 0 R/Next 2819 0 R>>endobj
+2813 0 obj<</Parent 2812 0 R/Title(Usage)/Dest[2049 0 R/XYZ null 694 null]/Next 2814 0 R>>endobj
+2814 0 obj<</Parent 2812 0 R/Title(Arguments)/Dest[2049 0 R/XYZ null 650 null]/Prev 2813 0 R/Next 2815 0 R>>endobj
+2815 0 obj<</Parent 2812 0 R/Title(Returns)/Dest[2049 0 R/XYZ null 562 null]/Prev 2814 0 R/Next 2816 0 R>>endobj
+2816 0 obj<</Parent 2812 0 R/Title(Description)/Dest[2049 0 R/XYZ null 512 null]/Prev 2815 0 R/Next 2817 0 R>>endobj
+2817 0 obj<</Parent 2812 0 R/Title(Example)/Dest[2049 0 R/XYZ null 462 null]/Prev 2816 0 R/Next 2818 0 R>>endobj
+2818 0 obj<</Parent 2812 0 R/Title(See Also)/Dest[2049 0 R/XYZ null 417 null]/Prev 2817 0 R>>endobj
+2819 0 obj<</Parent 2229 0 R/Count -6/First 2820 0 R/Last 2825 0 R/Title(ppdFindOption\(\))/Dest[2052 0 R/XYZ null 738 null]/Prev 2812 0 R/Next 2826 0 R>>endobj
+2820 0 obj<</Parent 2819 0 R/Title(Usage)/Dest[2052 0 R/XYZ null 694 null]/Next 2821 0 R>>endobj
+2821 0 obj<</Parent 2819 0 R/Title(Arguments)/Dest[2052 0 R/XYZ null 650 null]/Prev 2820 0 R/Next 2822 0 R>>endobj
+2822 0 obj<</Parent 2819 0 R/Title(Returns)/Dest[2052 0 R/XYZ null 562 null]/Prev 2821 0 R/Next 2823 0 R>>endobj
+2823 0 obj<</Parent 2819 0 R/Title(Description)/Dest[2052 0 R/XYZ null 512 null]/Prev 2822 0 R/Next 2824 0 R>>endobj
+2824 0 obj<</Parent 2819 0 R/Title(Example)/Dest[2052 0 R/XYZ null 462 null]/Prev 2823 0 R/Next 2825 0 R>>endobj
+2825 0 obj<</Parent 2819 0 R/Title(See Also)/Dest[2052 0 R/XYZ null 417 null]/Prev 2824 0 R>>endobj
+2826 0 obj<</Parent 2229 0 R/Count -6/First 2827 0 R/Last 2832 0 R/Title(ppdIsMarked\(\))/Dest[2055 0 R/XYZ null 738 null]/Prev 2819 0 R/Next 2833 0 R>>endobj
+2827 0 obj<</Parent 2826 0 R/Title(Usage)/Dest[2055 0 R/XYZ null 694 null]/Next 2828 0 R>>endobj
+2828 0 obj<</Parent 2826 0 R/Title(Arguments)/Dest[2055 0 R/XYZ null 650 null]/Prev 2827 0 R/Next 2829 0 R>>endobj
+2829 0 obj<</Parent 2826 0 R/Title(Returns)/Dest[2055 0 R/XYZ null 562 null]/Prev 2828 0 R/Next 2830 0 R>>endobj
+2830 0 obj<</Parent 2826 0 R/Title(Description)/Dest[2055 0 R/XYZ null 512 null]/Prev 2829 0 R/Next 2831 0 R>>endobj
+2831 0 obj<</Parent 2826 0 R/Title(Example)/Dest[2055 0 R/XYZ null 462 null]/Prev 2830 0 R/Next 2832 0 R>>endobj
+2832 0 obj<</Parent 2826 0 R/Title(See Also)/Dest[2055 0 R/XYZ null 417 null]/Prev 2831 0 R>>endobj
+2833 0 obj<</Parent 2229 0 R/Count -6/First 2834 0 R/Last 2839 0 R/Title(ppdMarkDefaults\(\))/Dest[2058 0 R/XYZ null 738 null]/Prev 2826 0 R/Next 2840 0 R>>endobj
+2834 0 obj<</Parent 2833 0 R/Title(Usage)/Dest[2058 0 R/XYZ null 694 null]/Next 2835 0 R>>endobj
+2835 0 obj<</Parent 2833 0 R/Title(Arguments)/Dest[2058 0 R/XYZ null 650 null]/Prev 2834 0 R/Next 2836 0 R>>endobj
+2836 0 obj<</Parent 2833 0 R/Title(Returns)/Dest[2058 0 R/XYZ null 562 null]/Prev 2835 0 R/Next 2837 0 R>>endobj
+2837 0 obj<</Parent 2833 0 R/Title(Description)/Dest[2058 0 R/XYZ null 512 null]/Prev 2836 0 R/Next 2838 0 R>>endobj
+2838 0 obj<</Parent 2833 0 R/Title(Example)/Dest[2058 0 R/XYZ null 462 null]/Prev 2837 0 R/Next 2839 0 R>>endobj
+2839 0 obj<</Parent 2833 0 R/Title(See Also)/Dest[2058 0 R/XYZ null 417 null]/Prev 2838 0 R>>endobj
+2840 0 obj<</Parent 2229 0 R/Count -6/First 2841 0 R/Last 2846 0 R/Title(ppdMarkOption\(\))/Dest[2061 0 R/XYZ null 738 null]/Prev 2833 0 R/Next 2847 0 R>>endobj
+2841 0 obj<</Parent 2840 0 R/Title(Usage)/Dest[2061 0 R/XYZ null 694 null]/Next 2842 0 R>>endobj
+2842 0 obj<</Parent 2840 0 R/Title(Arguments)/Dest[2061 0 R/XYZ null 650 null]/Prev 2841 0 R/Next 2843 0 R>>endobj
+2843 0 obj<</Parent 2840 0 R/Title(Returns)/Dest[2061 0 R/XYZ null 562 null]/Prev 2842 0 R/Next 2844 0 R>>endobj
+2844 0 obj<</Parent 2840 0 R/Title(Description)/Dest[2061 0 R/XYZ null 512 null]/Prev 2843 0 R/Next 2845 0 R>>endobj
+2845 0 obj<</Parent 2840 0 R/Title(Example)/Dest[2061 0 R/XYZ null 462 null]/Prev 2844 0 R/Next 2846 0 R>>endobj
+2846 0 obj<</Parent 2840 0 R/Title(See Also)/Dest[2061 0 R/XYZ null 417 null]/Prev 2845 0 R>>endobj
+2847 0 obj<</Parent 2229 0 R/Count -6/First 2848 0 R/Last 2853 0 R/Title(ppdOpenFd\(\))/Dest[2064 0 R/XYZ null 738 null]/Prev 2840 0 R/Next 2854 0 R>>endobj
+2848 0 obj<</Parent 2847 0 R/Title(Usage)/Dest[2064 0 R/XYZ null 694 null]/Next 2849 0 R>>endobj
+2849 0 obj<</Parent 2847 0 R/Title(Arguments)/Dest[2064 0 R/XYZ null 650 null]/Prev 2848 0 R/Next 2850 0 R>>endobj
+2850 0 obj<</Parent 2847 0 R/Title(Returns)/Dest[2064 0 R/XYZ null 562 null]/Prev 2849 0 R/Next 2851 0 R>>endobj
+2851 0 obj<</Parent 2847 0 R/Title(Description)/Dest[2064 0 R/XYZ null 512 null]/Prev 2850 0 R/Next 2852 0 R>>endobj
+2852 0 obj<</Parent 2847 0 R/Title(Example)/Dest[2064 0 R/XYZ null 462 null]/Prev 2851 0 R/Next 2853 0 R>>endobj
+2853 0 obj<</Parent 2847 0 R/Title(See Also)/Dest[2064 0 R/XYZ null 417 null]/Prev 2852 0 R>>endobj
+2854 0 obj<</Parent 2229 0 R/Count -6/First 2855 0 R/Last 2860 0 R/Title(ppdOpenFile\(\))/Dest[2067 0 R/XYZ null 738 null]/Prev 2847 0 R/Next 2861 0 R>>endobj
+2855 0 obj<</Parent 2854 0 R/Title(Usage)/Dest[2067 0 R/XYZ null 694 null]/Next 2856 0 R>>endobj
+2856 0 obj<</Parent 2854 0 R/Title(Arguments)/Dest[2067 0 R/XYZ null 650 null]/Prev 2855 0 R/Next 2857 0 R>>endobj
+2857 0 obj<</Parent 2854 0 R/Title(Returns)/Dest[2067 0 R/XYZ null 562 null]/Prev 2856 0 R/Next 2858 0 R>>endobj
+2858 0 obj<</Parent 2854 0 R/Title(Description)/Dest[2067 0 R/XYZ null 512 null]/Prev 2857 0 R/Next 2859 0 R>>endobj
+2859 0 obj<</Parent 2854 0 R/Title(Example)/Dest[2067 0 R/XYZ null 462 null]/Prev 2858 0 R/Next 2860 0 R>>endobj
+2860 0 obj<</Parent 2854 0 R/Title(See Also)/Dest[2067 0 R/XYZ null 417 null]/Prev 2859 0 R>>endobj
+2861 0 obj<</Parent 2229 0 R/Count -6/First 2862 0 R/Last 2867 0 R/Title(ppdOpen\(\))/Dest[2070 0 R/XYZ null 738 null]/Prev 2854 0 R/Next 2868 0 R>>endobj
+2862 0 obj<</Parent 2861 0 R/Title(Usage)/Dest[2070 0 R/XYZ null 694 null]/Next 2863 0 R>>endobj
+2863 0 obj<</Parent 2861 0 R/Title(Arguments)/Dest[2070 0 R/XYZ null 650 null]/Prev 2862 0 R/Next 2864 0 R>>endobj
+2864 0 obj<</Parent 2861 0 R/Title(Returns)/Dest[2070 0 R/XYZ null 562 null]/Prev 2863 0 R/Next 2865 0 R>>endobj
+2865 0 obj<</Parent 2861 0 R/Title(Description)/Dest[2070 0 R/XYZ null 512 null]/Prev 2864 0 R/Next 2866 0 R>>endobj
+2866 0 obj<</Parent 2861 0 R/Title(Example)/Dest[2070 0 R/XYZ null 462 null]/Prev 2865 0 R/Next 2867 0 R>>endobj
+2867 0 obj<</Parent 2861 0 R/Title(See Also)/Dest[2070 0 R/XYZ null 417 null]/Prev 2866 0 R>>endobj
+2868 0 obj<</Parent 2229 0 R/Count -6/First 2869 0 R/Last 2874 0 R/Title(ppdPageLength\(\))/Dest[2073 0 R/XYZ null 738 null]/Prev 2861 0 R/Next 2875 0 R>>endobj
+2869 0 obj<</Parent 2868 0 R/Title(Usage)/Dest[2073 0 R/XYZ null 694 null]/Next 2870 0 R>>endobj
+2870 0 obj<</Parent 2868 0 R/Title(Arguments)/Dest[2073 0 R/XYZ null 650 null]/Prev 2869 0 R/Next 2871 0 R>>endobj
+2871 0 obj<</Parent 2868 0 R/Title(Returns)/Dest[2073 0 R/XYZ null 562 null]/Prev 2870 0 R/Next 2872 0 R>>endobj
+2872 0 obj<</Parent 2868 0 R/Title(Description)/Dest[2073 0 R/XYZ null 512 null]/Prev 2871 0 R/Next 2873 0 R>>endobj
+2873 0 obj<</Parent 2868 0 R/Title(Example)/Dest[2073 0 R/XYZ null 462 null]/Prev 2872 0 R/Next 2874 0 R>>endobj
+2874 0 obj<</Parent 2868 0 R/Title(See Also)/Dest[2073 0 R/XYZ null 417 null]/Prev 2873 0 R>>endobj
+2875 0 obj<</Parent 2229 0 R/Count -6/First 2876 0 R/Last 2881 0 R/Title(ppdPageSize\(\))/Dest[2076 0 R/XYZ null 738 null]/Prev 2868 0 R/Next 2882 0 R>>endobj
+2876 0 obj<</Parent 2875 0 R/Title(Usage)/Dest[2076 0 R/XYZ null 694 null]/Next 2877 0 R>>endobj
+2877 0 obj<</Parent 2875 0 R/Title(Arguments)/Dest[2076 0 R/XYZ null 650 null]/Prev 2876 0 R/Next 2878 0 R>>endobj
+2878 0 obj<</Parent 2875 0 R/Title(Returns)/Dest[2076 0 R/XYZ null 562 null]/Prev 2877 0 R/Next 2879 0 R>>endobj
+2879 0 obj<</Parent 2875 0 R/Title(Description)/Dest[2076 0 R/XYZ null 512 null]/Prev 2878 0 R/Next 2880 0 R>>endobj
+2880 0 obj<</Parent 2875 0 R/Title(Example)/Dest[2076 0 R/XYZ null 462 null]/Prev 2879 0 R/Next 2881 0 R>>endobj
+2881 0 obj<</Parent 2875 0 R/Title(See Also)/Dest[2076 0 R/XYZ null 417 null]/Prev 2880 0 R>>endobj
+2882 0 obj<</Parent 2229 0 R/Count -6/First 2883 0 R/Last 2888 0 R/Title(ppdPageWidth\(\))/Dest[2079 0 R/XYZ null 738 null]/Prev 2875 0 R>>endobj
+2883 0 obj<</Parent 2882 0 R/Title(Usage)/Dest[2079 0 R/XYZ null 694 null]/Next 2884 0 R>>endobj
+2884 0 obj<</Parent 2882 0 R/Title(Arguments)/Dest[2079 0 R/XYZ null 650 null]/Prev 2883 0 R/Next 2885 0 R>>endobj
+2885 0 obj<</Parent 2882 0 R/Title(Returns)/Dest[2079 0 R/XYZ null 562 null]/Prev 2884 0 R/Next 2886 0 R>>endobj
+2886 0 obj<</Parent 2882 0 R/Title(Description)/Dest[2079 0 R/XYZ null 512 null]/Prev 2885 0 R/Next 2887 0 R>>endobj
+2887 0 obj<</Parent 2882 0 R/Title(Example)/Dest[2079 0 R/XYZ null 462 null]/Prev 2886 0 R/Next 2888 0 R>>endobj
+2888 0 obj<</Parent 2882 0 R/Title(See Also)/Dest[2079 0 R/XYZ null 417 null]/Prev 2887 0 R>>endobj
+2889 0 obj<</Type/Catalog/Pages 1703 0 R/Names 948 0 R/PageLayout/SinglePage/Outlines 2136 0 R/OpenAction[1710 0 R/XYZ null null null]/PageMode/UseOutlines/PageLabels<</Nums[0<</P(title)>>1<</P(eltit)>>2<</S/r>>20<</S/D>>]>>>>endobj
+xref
+0 2890
+0000000000 65535 f
+0000000015 00000 n
+0000000233 00000 n
+0000001554 00000 n
+0000001628 00000 n
+0000001706 00000 n
+0000001783 00000 n
+0000001862 00000 n
+0000001938 00000 n
+0000002019 00000 n
+0000002103 00000 n
+0000002162 00000 n
+0000002214 00000 n
+0000002299 00000 n
+0000002323 00000 n
+0000002368 00000 n
+0000002452 00000 n
+0000002476 00000 n
+0000002581 00000 n
+0000002687 00000 n
+0000002793 00000 n
+0000002831 00000 n
+0000002937 00000 n
+0000003042 00000 n
+0000003148 00000 n
+0000003186 00000 n
+0000003291 00000 n
+0000003329 00000 n
+0000003413 00000 n
+0000003456 00000 n
+0000003540 00000 n
+0000003577 00000 n
+0000003660 00000 n
+0000003766 00000 n
+0000003870 00000 n
+0000003929 00000 n
+0000004034 00000 n
+0000004140 00000 n
+0000004246 00000 n
+0000004351 00000 n
+0000004457 00000 n
+0000004563 00000 n
+0000004669 00000 n
+0000004774 00000 n
+0000004880 00000 n
+0000004960 00000 n
+0000005065 00000 n
+0000005103 00000 n
+0000005187 00000 n
+0000005230 00000 n
+0000005314 00000 n
+0000005351 00000 n
+0000005434 00000 n
+0000005540 00000 n
+0000005646 00000 n
+0000005749 00000 n
+0000005815 00000 n
+0000005921 00000 n
+0000006026 00000 n
+0000006132 00000 n
+0000006238 00000 n
+0000006344 00000 n
+0000006449 00000 n
+0000006555 00000 n
+0000006621 00000 n
+0000006726 00000 n
+0000006832 00000 n
+0000006938 00000 n
+0000007044 00000 n
+0000007089 00000 n
+0000007192 00000 n
+0000007296 00000 n
+0000007327 00000 n
+0000007432 00000 n
+0000007538 00000 n
+0000007569 00000 n
+0000007674 00000 n
+0000007780 00000 n
+0000007886 00000 n
+0000007992 00000 n
+0000008037 00000 n
+0000008142 00000 n
+0000008185 00000 n
+0000008270 00000 n
+0000008301 00000 n
+0000008404 00000 n
+0000008508 00000 n
+0000008539 00000 n
+0000008644 00000 n
+0000008750 00000 n
+0000008856 00000 n
+0000008962 00000 n
+0000009068 00000 n
+0000009120 00000 n
+0000009225 00000 n
+0000009331 00000 n
+0000009437 00000 n
+0000009543 00000 n
+0000009649 00000 n
+0000009701 00000 n
+0000009806 00000 n
+0000009913 00000 n
+0000010020 00000 n
+0000010127 00000 n
+0000010234 00000 n
+0000010291 00000 n
+0000010397 00000 n
+0000010504 00000 n
+0000010611 00000 n
+0000010718 00000 n
+0000010825 00000 n
+0000010883 00000 n
+0000010989 00000 n
+0000011096 00000 n
+0000011203 00000 n
+0000011310 00000 n
+0000011417 00000 n
+0000011475 00000 n
+0000011581 00000 n
+0000011688 00000 n
+0000011795 00000 n
+0000011902 00000 n
+0000012009 00000 n
+0000012067 00000 n
+0000012173 00000 n
+0000012280 00000 n
+0000012314 00000 n
+0000012420 00000 n
+0000012527 00000 n
+0000012634 00000 n
+0000012741 00000 n
+0000012791 00000 n
+0000012897 00000 n
+0000013004 00000 n
+0000013111 00000 n
+0000013218 00000 n
+0000013268 00000 n
+0000013374 00000 n
+0000013481 00000 n
+0000013515 00000 n
+0000013621 00000 n
+0000013728 00000 n
+0000013835 00000 n
+0000013942 00000 n
+0000014048 00000 n
+0000014106 00000 n
+0000014212 00000 n
+0000014319 00000 n
+0000014426 00000 n
+0000014533 00000 n
+0000014639 00000 n
+0000014697 00000 n
+0000014803 00000 n
+0000014910 00000 n
+0000015017 00000 n
+0000015124 00000 n
+0000015230 00000 n
+0000015288 00000 n
+0000015394 00000 n
+0000015501 00000 n
+0000015608 00000 n
+0000015715 00000 n
+0000015821 00000 n
+0000015879 00000 n
+0000015985 00000 n
+0000016092 00000 n
+0000016199 00000 n
+0000016306 00000 n
+0000016411 00000 n
+0000016469 00000 n
+0000016575 00000 n
+0000016682 00000 n
+0000016789 00000 n
+0000016896 00000 n
+0000017002 00000 n
+0000017060 00000 n
+0000017166 00000 n
+0000017273 00000 n
+0000017307 00000 n
+0000017413 00000 n
+0000017520 00000 n
+0000017554 00000 n
+0000017660 00000 n
+0000017767 00000 n
+0000017874 00000 n
+0000017980 00000 n
+0000018087 00000 n
+0000018194 00000 n
+0000018301 00000 n
+0000018408 00000 n
+0000018515 00000 n
+0000018622 00000 n
+0000018729 00000 n
+0000018835 00000 n
+0000018942 00000 n
+0000019049 00000 n
+0000019156 00000 n
+0000019263 00000 n
+0000019370 00000 n
+0000019477 00000 n
+0000019584 00000 n
+0000019691 00000 n
+0000019798 00000 n
+0000019905 00000 n
+0000020012 00000 n
+0000020119 00000 n
+0000020226 00000 n
+0000020333 00000 n
+0000020440 00000 n
+0000020547 00000 n
+0000020654 00000 n
+0000020761 00000 n
+0000020868 00000 n
+0000020975 00000 n
+0000021082 00000 n
+0000021189 00000 n
+0000021296 00000 n
+0000021403 00000 n
+0000021510 00000 n
+0000021617 00000 n
+0000021724 00000 n
+0000021831 00000 n
+0000021938 00000 n
+0000022045 00000 n
+0000022151 00000 n
+0000022256 00000 n
+0000022361 00000 n
+0000022739 00000 n
+0000022846 00000 n
+0000022953 00000 n
+0000023060 00000 n
+0000023167 00000 n
+0000023274 00000 n
+0000023381 00000 n
+0000023488 00000 n
+0000023595 00000 n
+0000023701 00000 n
+0000023807 00000 n
+0000023914 00000 n
+0000024021 00000 n
+0000024128 00000 n
+0000024234 00000 n
+0000024341 00000 n
+0000024447 00000 n
+0000024553 00000 n
+0000024659 00000 n
+0000024765 00000 n
+0000024872 00000 n
+0000024979 00000 n
+0000025085 00000 n
+0000025192 00000 n
+0000025299 00000 n
+0000025406 00000 n
+0000025513 00000 n
+0000025619 00000 n
+0000025725 00000 n
+0000025831 00000 n
+0000025938 00000 n
+0000026045 00000 n
+0000026152 00000 n
+0000026259 00000 n
+0000026365 00000 n
+0000026472 00000 n
+0000026578 00000 n
+0000026684 00000 n
+0000026790 00000 n
+0000026896 00000 n
+0000027002 00000 n
+0000027107 00000 n
+0000027211 00000 n
+0000027315 00000 n
+0000027677 00000 n
+0000027784 00000 n
+0000027890 00000 n
+0000027996 00000 n
+0000028103 00000 n
+0000028210 00000 n
+0000028317 00000 n
+0000028424 00000 n
+0000028531 00000 n
+0000028638 00000 n
+0000028745 00000 n
+0000028852 00000 n
+0000028959 00000 n
+0000029066 00000 n
+0000029173 00000 n
+0000029280 00000 n
+0000029387 00000 n
+0000029494 00000 n
+0000029601 00000 n
+0000029708 00000 n
+0000029815 00000 n
+0000029922 00000 n
+0000030029 00000 n
+0000030136 00000 n
+0000030243 00000 n
+0000030350 00000 n
+0000030457 00000 n
+0000030564 00000 n
+0000030671 00000 n
+0000030778 00000 n
+0000030885 00000 n
+0000030992 00000 n
+0000031099 00000 n
+0000031206 00000 n
+0000031313 00000 n
+0000031420 00000 n
+0000031527 00000 n
+0000031634 00000 n
+0000031741 00000 n
+0000031848 00000 n
+0000031955 00000 n
+0000032062 00000 n
+0000032169 00000 n
+0000032275 00000 n
+0000032380 00000 n
+0000032485 00000 n
+0000032863 00000 n
+0000032970 00000 n
+0000033077 00000 n
+0000033184 00000 n
+0000033291 00000 n
+0000033398 00000 n
+0000033504 00000 n
+0000033611 00000 n
+0000033718 00000 n
+0000033825 00000 n
+0000033932 00000 n
+0000034039 00000 n
+0000034145 00000 n
+0000034252 00000 n
+0000034359 00000 n
+0000034466 00000 n
+0000034573 00000 n
+0000034680 00000 n
+0000034787 00000 n
+0000034893 00000 n
+0000035000 00000 n
+0000035107 00000 n
+0000035214 00000 n
+0000035321 00000 n
+0000035428 00000 n
+0000035534 00000 n
+0000035641 00000 n
+0000035748 00000 n
+0000035855 00000 n
+0000035962 00000 n
+0000036069 00000 n
+0000036176 00000 n
+0000036282 00000 n
+0000036389 00000 n
+0000036496 00000 n
+0000036603 00000 n
+0000036710 00000 n
+0000036817 00000 n
+0000036923 00000 n
+0000037030 00000 n
+0000037137 00000 n
+0000037244 00000 n
+0000037351 00000 n
+0000037458 00000 n
+0000037565 00000 n
+0000037670 00000 n
+0000037775 00000 n
+0000037880 00000 n
+0000038274 00000 n
+0000038381 00000 n
+0000038488 00000 n
+0000038595 00000 n
+0000038702 00000 n
+0000038809 00000 n
+0000038916 00000 n
+0000039023 00000 n
+0000039130 00000 n
+0000039237 00000 n
+0000039344 00000 n
+0000039451 00000 n
+0000039558 00000 n
+0000039665 00000 n
+0000039772 00000 n
+0000039879 00000 n
+0000039986 00000 n
+0000040093 00000 n
+0000040200 00000 n
+0000040307 00000 n
+0000040414 00000 n
+0000040521 00000 n
+0000040628 00000 n
+0000040735 00000 n
+0000040842 00000 n
+0000040949 00000 n
+0000041056 00000 n
+0000041163 00000 n
+0000041270 00000 n
+0000041377 00000 n
+0000041484 00000 n
+0000041591 00000 n
+0000041698 00000 n
+0000041805 00000 n
+0000041912 00000 n
+0000042019 00000 n
+0000042126 00000 n
+0000042233 00000 n
+0000042340 00000 n
+0000042447 00000 n
+0000042554 00000 n
+0000042661 00000 n
+0000042768 00000 n
+0000042875 00000 n
+0000042982 00000 n
+0000043088 00000 n
+0000043193 00000 n
+0000043298 00000 n
+0000043692 00000 n
+0000043799 00000 n
+0000043906 00000 n
+0000044012 00000 n
+0000044119 00000 n
+0000044226 00000 n
+0000044333 00000 n
+0000044440 00000 n
+0000044547 00000 n
+0000044653 00000 n
+0000044760 00000 n
+0000044867 00000 n
+0000044974 00000 n
+0000045081 00000 n
+0000045188 00000 n
+0000045295 00000 n
+0000045401 00000 n
+0000045508 00000 n
+0000045615 00000 n
+0000045722 00000 n
+0000045829 00000 n
+0000045936 00000 n
+0000046043 00000 n
+0000046149 00000 n
+0000046256 00000 n
+0000046363 00000 n
+0000046470 00000 n
+0000046577 00000 n
+0000046684 00000 n
+0000046791 00000 n
+0000046897 00000 n
+0000047004 00000 n
+0000047111 00000 n
+0000047218 00000 n
+0000047325 00000 n
+0000047432 00000 n
+0000047539 00000 n
+0000047645 00000 n
+0000047752 00000 n
+0000047859 00000 n
+0000047966 00000 n
+0000048073 00000 n
+0000048180 00000 n
+0000048287 00000 n
+0000048393 00000 n
+0000048499 00000 n
+0000048604 00000 n
+0000048709 00000 n
+0000049103 00000 n
+0000049210 00000 n
+0000049317 00000 n
+0000049424 00000 n
+0000049531 00000 n
+0000049638 00000 n
+0000049745 00000 n
+0000049852 00000 n
+0000049959 00000 n
+0000050066 00000 n
+0000050173 00000 n
+0000050280 00000 n
+0000050387 00000 n
+0000050494 00000 n
+0000050601 00000 n
+0000050708 00000 n
+0000050815 00000 n
+0000050922 00000 n
+0000051029 00000 n
+0000051136 00000 n
+0000051243 00000 n
+0000051350 00000 n
+0000051457 00000 n
+0000051564 00000 n
+0000051671 00000 n
+0000051778 00000 n
+0000051885 00000 n
+0000051992 00000 n
+0000052099 00000 n
+0000052206 00000 n
+0000052313 00000 n
+0000052420 00000 n
+0000052527 00000 n
+0000052634 00000 n
+0000052741 00000 n
+0000052848 00000 n
+0000052955 00000 n
+0000053062 00000 n
+0000053169 00000 n
+0000053276 00000 n
+0000053383 00000 n
+0000053490 00000 n
+0000053597 00000 n
+0000053704 00000 n
+0000053811 00000 n
+0000053917 00000 n
+0000054022 00000 n
+0000054127 00000 n
+0000054521 00000 n
+0000054628 00000 n
+0000054735 00000 n
+0000054841 00000 n
+0000054948 00000 n
+0000055055 00000 n
+0000055162 00000 n
+0000055269 00000 n
+0000055376 00000 n
+0000055483 00000 n
+0000055589 00000 n
+0000055696 00000 n
+0000055803 00000 n
+0000055910 00000 n
+0000056017 00000 n
+0000056124 00000 n
+0000056231 00000 n
+0000056337 00000 n
+0000056444 00000 n
+0000056551 00000 n
+0000056658 00000 n
+0000056765 00000 n
+0000056872 00000 n
+0000056979 00000 n
+0000057085 00000 n
+0000057192 00000 n
+0000057299 00000 n
+0000057406 00000 n
+0000057513 00000 n
+0000057620 00000 n
+0000057727 00000 n
+0000057833 00000 n
+0000057940 00000 n
+0000058047 00000 n
+0000058154 00000 n
+0000058261 00000 n
+0000058368 00000 n
+0000058475 00000 n
+0000058581 00000 n
+0000058688 00000 n
+0000058795 00000 n
+0000058902 00000 n
+0000059009 00000 n
+0000059116 00000 n
+0000059223 00000 n
+0000059328 00000 n
+0000059433 00000 n
+0000059538 00000 n
+0000059932 00000 n
+0000060039 00000 n
+0000060146 00000 n
+0000060253 00000 n
+0000060360 00000 n
+0000060467 00000 n
+0000060574 00000 n
+0000060681 00000 n
+0000060788 00000 n
+0000060895 00000 n
+0000061002 00000 n
+0000061109 00000 n
+0000061216 00000 n
+0000061323 00000 n
+0000061430 00000 n
+0000061537 00000 n
+0000061644 00000 n
+0000061751 00000 n
+0000061858 00000 n
+0000061965 00000 n
+0000062072 00000 n
+0000062179 00000 n
+0000062286 00000 n
+0000062393 00000 n
+0000062500 00000 n
+0000062607 00000 n
+0000062714 00000 n
+0000062821 00000 n
+0000062928 00000 n
+0000063035 00000 n
+0000063142 00000 n
+0000063249 00000 n
+0000063356 00000 n
+0000063463 00000 n
+0000063570 00000 n
+0000063677 00000 n
+0000063784 00000 n
+0000063891 00000 n
+0000063998 00000 n
+0000064105 00000 n
+0000064212 00000 n
+0000064319 00000 n
+0000064426 00000 n
+0000064533 00000 n
+0000064640 00000 n
+0000064746 00000 n
+0000064851 00000 n
+0000064956 00000 n
+0000065350 00000 n
+0000065457 00000 n
+0000065564 00000 n
+0000065671 00000 n
+0000065778 00000 n
+0000065885 00000 n
+0000065992 00000 n
+0000066098 00000 n
+0000066205 00000 n
+0000066312 00000 n
+0000066419 00000 n
+0000066526 00000 n
+0000066633 00000 n
+0000066740 00000 n
+0000066846 00000 n
+0000066953 00000 n
+0000067060 00000 n
+0000067167 00000 n
+0000067274 00000 n
+0000067381 00000 n
+0000067488 00000 n
+0000067594 00000 n
+0000067701 00000 n
+0000067808 00000 n
+0000067915 00000 n
+0000068022 00000 n
+0000068129 00000 n
+0000068236 00000 n
+0000068342 00000 n
+0000068449 00000 n
+0000068556 00000 n
+0000068663 00000 n
+0000068770 00000 n
+0000068877 00000 n
+0000068984 00000 n
+0000069090 00000 n
+0000069197 00000 n
+0000069304 00000 n
+0000069411 00000 n
+0000069518 00000 n
+0000069625 00000 n
+0000069732 00000 n
+0000069838 00000 n
+0000069945 00000 n
+0000070052 00000 n
+0000070158 00000 n
+0000070263 00000 n
+0000070368 00000 n
+0000070762 00000 n
+0000070869 00000 n
+0000070976 00000 n
+0000071083 00000 n
+0000071190 00000 n
+0000071297 00000 n
+0000071404 00000 n
+0000071511 00000 n
+0000071618 00000 n
+0000071725 00000 n
+0000071832 00000 n
+0000071939 00000 n
+0000072046 00000 n
+0000072153 00000 n
+0000072260 00000 n
+0000072367 00000 n
+0000072474 00000 n
+0000072581 00000 n
+0000072688 00000 n
+0000072795 00000 n
+0000072902 00000 n
+0000073009 00000 n
+0000073116 00000 n
+0000073223 00000 n
+0000073330 00000 n
+0000073437 00000 n
+0000073544 00000 n
+0000073651 00000 n
+0000073758 00000 n
+0000073865 00000 n
+0000073972 00000 n
+0000074079 00000 n
+0000074186 00000 n
+0000074293 00000 n
+0000074400 00000 n
+0000074507 00000 n
+0000074614 00000 n
+0000074721 00000 n
+0000074828 00000 n
+0000074935 00000 n
+0000075042 00000 n
+0000075149 00000 n
+0000075256 00000 n
+0000075363 00000 n
+0000075470 00000 n
+0000075576 00000 n
+0000075681 00000 n
+0000075786 00000 n
+0000076180 00000 n
+0000076287 00000 n
+0000076394 00000 n
+0000076501 00000 n
+0000076607 00000 n
+0000076714 00000 n
+0000076821 00000 n
+0000076928 00000 n
+0000077035 00000 n
+0000077142 00000 n
+0000077249 00000 n
+0000077355 00000 n
+0000077462 00000 n
+0000077569 00000 n
+0000077676 00000 n
+0000077783 00000 n
+0000077890 00000 n
+0000077997 00000 n
+0000078103 00000 n
+0000078210 00000 n
+0000078317 00000 n
+0000078424 00000 n
+0000078531 00000 n
+0000078638 00000 n
+0000078745 00000 n
+0000078851 00000 n
+0000078958 00000 n
+0000079065 00000 n
+0000079172 00000 n
+0000079279 00000 n
+0000079386 00000 n
+0000079493 00000 n
+0000079599 00000 n
+0000079706 00000 n
+0000079813 00000 n
+0000079920 00000 n
+0000080027 00000 n
+0000080134 00000 n
+0000080241 00000 n
+0000080347 00000 n
+0000080454 00000 n
+0000080561 00000 n
+0000080668 00000 n
+0000080775 00000 n
+0000080882 00000 n
+0000080988 00000 n
+0000081092 00000 n
+0000081197 00000 n
+0000081591 00000 n
+0000081698 00000 n
+0000081805 00000 n
+0000081912 00000 n
+0000082019 00000 n
+0000082126 00000 n
+0000082233 00000 n
+0000082340 00000 n
+0000082447 00000 n
+0000082554 00000 n
+0000082661 00000 n
+0000082768 00000 n
+0000082875 00000 n
+0000082982 00000 n
+0000083089 00000 n
+0000083196 00000 n
+0000083303 00000 n
+0000083410 00000 n
+0000083517 00000 n
+0000083624 00000 n
+0000083731 00000 n
+0000083838 00000 n
+0000083945 00000 n
+0000084052 00000 n
+0000084159 00000 n
+0000084266 00000 n
+0000084373 00000 n
+0000084480 00000 n
+0000084587 00000 n
+0000084694 00000 n
+0000084801 00000 n
+0000084908 00000 n
+0000085015 00000 n
+0000085122 00000 n
+0000085229 00000 n
+0000085336 00000 n
+0000085443 00000 n
+0000085550 00000 n
+0000085657 00000 n
+0000085764 00000 n
+0000085871 00000 n
+0000085978 00000 n
+0000086085 00000 n
+0000086192 00000 n
+0000086299 00000 n
+0000086405 00000 n
+0000086510 00000 n
+0000086615 00000 n
+0000087009 00000 n
+0000087115 00000 n
+0000087222 00000 n
+0000087329 00000 n
+0000087436 00000 n
+0000087543 00000 n
+0000087650 00000 n
+0000087757 00000 n
+0000087863 00000 n
+0000087970 00000 n
+0000088077 00000 n
+0000088184 00000 n
+0000088291 00000 n
+0000088398 00000 n
+0000088505 00000 n
+0000088611 00000 n
+0000088718 00000 n
+0000088825 00000 n
+0000088932 00000 n
+0000089039 00000 n
+0000089146 00000 n
+0000089253 00000 n
+0000089359 00000 n
+0000089466 00000 n
+0000089573 00000 n
+0000089680 00000 n
+0000089787 00000 n
+0000089894 00000 n
+0000090001 00000 n
+0000090107 00000 n
+0000090214 00000 n
+0000090321 00000 n
+0000090428 00000 n
+0000090535 00000 n
+0000090642 00000 n
+0000090749 00000 n
+0000090855 00000 n
+0000090962 00000 n
+0000091069 00000 n
+0000091176 00000 n
+0000091283 00000 n
+0000091390 00000 n
+0000091497 00000 n
+0000091603 00000 n
+0000091710 00000 n
+0000091816 00000 n
+0000091921 00000 n
+0000092026 00000 n
+0000092420 00000 n
+0000092527 00000 n
+0000092634 00000 n
+0000092741 00000 n
+0000092848 00000 n
+0000092955 00000 n
+0000093062 00000 n
+0000093169 00000 n
+0000093276 00000 n
+0000093383 00000 n
+0000093490 00000 n
+0000093597 00000 n
+0000093704 00000 n
+0000093811 00000 n
+0000093918 00000 n
+0000094025 00000 n
+0000094132 00000 n
+0000094239 00000 n
+0000094346 00000 n
+0000094453 00000 n
+0000094560 00000 n
+0000094667 00000 n
+0000094774 00000 n
+0000094881 00000 n
+0000094988 00000 n
+0000095095 00000 n
+0000095202 00000 n
+0000095309 00000 n
+0000095416 00000 n
+0000095523 00000 n
+0000095630 00000 n
+0000095737 00000 n
+0000095844 00000 n
+0000095951 00000 n
+0000096058 00000 n
+0000096165 00000 n
+0000096272 00000 n
+0000096379 00000 n
+0000096486 00000 n
+0000096593 00000 n
+0000096700 00000 n
+0000096807 00000 n
+0000096914 00000 n
+0000097021 00000 n
+0000097128 00000 n
+0000097234 00000 n
+0000097339 00000 n
+0000097444 00000 n
+0000097838 00000 n
+0000097945 00000 n
+0000098052 00000 n
+0000098159 00000 n
+0000098266 00000 n
+0000098372 00000 n
+0000098479 00000 n
+0000098586 00000 n
+0000098693 00000 n
+0000098800 00000 n
+0000098907 00000 n
+0000099014 00000 n
+0000099120 00000 n
+0000099227 00000 n
+0000099334 00000 n
+0000099441 00000 n
+0000099548 00000 n
+0000099655 00000 n
+0000099762 00000 n
+0000099868 00000 n
+0000099975 00000 n
+0000100082 00000 n
+0000100189 00000 n
+0000100296 00000 n
+0000100403 00000 n
+0000100510 00000 n
+0000100616 00000 n
+0000100723 00000 n
+0000100830 00000 n
+0000100937 00000 n
+0000101044 00000 n
+0000101151 00000 n
+0000101258 00000 n
+0000101364 00000 n
+0000101471 00000 n
+0000101578 00000 n
+0000101685 00000 n
+0000101792 00000 n
+0000101899 00000 n
+0000102006 00000 n
+0000102112 00000 n
+0000102219 00000 n
+0000102326 00000 n
+0000102433 00000 n
+0000102540 00000 n
+0000102646 00000 n
+0000102751 00000 n
+0000102855 00000 n
+0000103249 00000 n
+0000103356 00000 n
+0000103463 00000 n
+0000103570 00000 n
+0000103677 00000 n
+0000103784 00000 n
+0000103891 00000 n
+0000103957 00000 n
+0000103991 00000 n
+0000104025 00000 n
+0000116415 00000 n
+0000116465 00000 n
+0000116515 00000 n
+0000116565 00000 n
+0000116615 00000 n
+0000116665 00000 n
+0000116715 00000 n
+0000116765 00000 n
+0000116815 00000 n
+0000116865 00000 n
+0000116915 00000 n
+0000116965 00000 n
+0000117015 00000 n
+0000117065 00000 n
+0000117115 00000 n
+0000117165 00000 n
+0000117215 00000 n
+0000117265 00000 n
+0000117315 00000 n
+0000117365 00000 n
+0000117415 00000 n
+0000117465 00000 n
+0000117515 00000 n
+0000117565 00000 n
+0000117615 00000 n
+0000117665 00000 n
+0000117715 00000 n
+0000117765 00000 n
+0000117815 00000 n
+0000117865 00000 n
+0000117915 00000 n
+0000117965 00000 n
+0000118015 00000 n
+0000118065 00000 n
+0000118115 00000 n
+0000118165 00000 n
+0000118215 00000 n
+0000118265 00000 n
+0000118315 00000 n
+0000118365 00000 n
+0000118415 00000 n
+0000118465 00000 n
+0000118515 00000 n
+0000118565 00000 n
+0000118615 00000 n
+0000118665 00000 n
+0000118715 00000 n
+0000118765 00000 n
+0000118815 00000 n
+0000118865 00000 n
+0000118916 00000 n
+0000118967 00000 n
+0000119018 00000 n
+0000119069 00000 n
+0000119120 00000 n
+0000119171 00000 n
+0000119222 00000 n
+0000119273 00000 n
+0000119324 00000 n
+0000119375 00000 n
+0000119426 00000 n
+0000119477 00000 n
+0000119528 00000 n
+0000119579 00000 n
+0000119630 00000 n
+0000119681 00000 n
+0000119732 00000 n
+0000119783 00000 n
+0000119834 00000 n
+0000119885 00000 n
+0000119936 00000 n
+0000119987 00000 n
+0000120038 00000 n
+0000120089 00000 n
+0000120140 00000 n
+0000120191 00000 n
+0000120242 00000 n
+0000120293 00000 n
+0000120344 00000 n
+0000120395 00000 n
+0000120446 00000 n
+0000120497 00000 n
+0000120548 00000 n
+0000120599 00000 n
+0000120650 00000 n
+0000120701 00000 n
+0000120752 00000 n
+0000120803 00000 n
+0000120854 00000 n
+0000120905 00000 n
+0000120956 00000 n
+0000121007 00000 n
+0000121058 00000 n
+0000121109 00000 n
+0000121160 00000 n
+0000121211 00000 n
+0000121262 00000 n
+0000121313 00000 n
+0000121364 00000 n
+0000121415 00000 n
+0000121466 00000 n
+0000121517 00000 n
+0000121568 00000 n
+0000121619 00000 n
+0000121670 00000 n
+0000121721 00000 n
+0000121772 00000 n
+0000121823 00000 n
+0000121874 00000 n
+0000121925 00000 n
+0000121976 00000 n
+0000122027 00000 n
+0000122078 00000 n
+0000122129 00000 n
+0000122180 00000 n
+0000122231 00000 n
+0000122282 00000 n
+0000122333 00000 n
+0000122384 00000 n
+0000122435 00000 n
+0000122486 00000 n
+0000122537 00000 n
+0000122588 00000 n
+0000122639 00000 n
+0000122690 00000 n
+0000122741 00000 n
+0000122792 00000 n
+0000122843 00000 n
+0000122894 00000 n
+0000122945 00000 n
+0000122996 00000 n
+0000123047 00000 n
+0000123098 00000 n
+0000123149 00000 n
+0000123200 00000 n
+0000123251 00000 n
+0000123302 00000 n
+0000123353 00000 n
+0000123404 00000 n
+0000123455 00000 n
+0000123506 00000 n
+0000123557 00000 n
+0000123608 00000 n
+0000123659 00000 n
+0000123710 00000 n
+0000123761 00000 n
+0000123812 00000 n
+0000123863 00000 n
+0000123914 00000 n
+0000123965 00000 n
+0000124016 00000 n
+0000124067 00000 n
+0000124118 00000 n
+0000124169 00000 n
+0000124220 00000 n
+0000124271 00000 n
+0000124322 00000 n
+0000124373 00000 n
+0000124424 00000 n
+0000124475 00000 n
+0000124526 00000 n
+0000124577 00000 n
+0000124628 00000 n
+0000124679 00000 n
+0000124730 00000 n
+0000124781 00000 n
+0000124832 00000 n
+0000124883 00000 n
+0000124934 00000 n
+0000124985 00000 n
+0000125036 00000 n
+0000125087 00000 n
+0000125138 00000 n
+0000125189 00000 n
+0000125240 00000 n
+0000125291 00000 n
+0000125342 00000 n
+0000125393 00000 n
+0000125444 00000 n
+0000125495 00000 n
+0000125546 00000 n
+0000125597 00000 n
+0000125648 00000 n
+0000125699 00000 n
+0000125750 00000 n
+0000125801 00000 n
+0000125852 00000 n
+0000125903 00000 n
+0000125954 00000 n
+0000126005 00000 n
+0000126056 00000 n
+0000126107 00000 n
+0000126158 00000 n
+0000126209 00000 n
+0000126260 00000 n
+0000126311 00000 n
+0000126362 00000 n
+0000126413 00000 n
+0000126464 00000 n
+0000126515 00000 n
+0000126566 00000 n
+0000126617 00000 n
+0000126668 00000 n
+0000126719 00000 n
+0000126770 00000 n
+0000126821 00000 n
+0000126872 00000 n
+0000126923 00000 n
+0000126974 00000 n
+0000127025 00000 n
+0000127076 00000 n
+0000127127 00000 n
+0000127178 00000 n
+0000127229 00000 n
+0000127280 00000 n
+0000127331 00000 n
+0000127382 00000 n
+0000127433 00000 n
+0000127484 00000 n
+0000127535 00000 n
+0000127586 00000 n
+0000127637 00000 n
+0000127688 00000 n
+0000127739 00000 n
+0000127790 00000 n
+0000127841 00000 n
+0000127892 00000 n
+0000127943 00000 n
+0000127994 00000 n
+0000128045 00000 n
+0000128096 00000 n
+0000128147 00000 n
+0000128198 00000 n
+0000128249 00000 n
+0000128300 00000 n
+0000128351 00000 n
+0000128402 00000 n
+0000128453 00000 n
+0000128504 00000 n
+0000128555 00000 n
+0000128606 00000 n
+0000128657 00000 n
+0000128708 00000 n
+0000128759 00000 n
+0000128810 00000 n
+0000128861 00000 n
+0000128912 00000 n
+0000128963 00000 n
+0000129014 00000 n
+0000129065 00000 n
+0000129116 00000 n
+0000129167 00000 n
+0000129218 00000 n
+0000129269 00000 n
+0000129320 00000 n
+0000129371 00000 n
+0000129422 00000 n
+0000129473 00000 n
+0000129524 00000 n
+0000129575 00000 n
+0000129626 00000 n
+0000129677 00000 n
+0000129728 00000 n
+0000129779 00000 n
+0000129830 00000 n
+0000129881 00000 n
+0000129932 00000 n
+0000129983 00000 n
+0000130034 00000 n
+0000130085 00000 n
+0000130136 00000 n
+0000130187 00000 n
+0000130238 00000 n
+0000130289 00000 n
+0000130340 00000 n
+0000130391 00000 n
+0000130442 00000 n
+0000130493 00000 n
+0000130544 00000 n
+0000130595 00000 n
+0000130646 00000 n
+0000130697 00000 n
+0000130748 00000 n
+0000130799 00000 n
+0000130850 00000 n
+0000130901 00000 n
+0000130952 00000 n
+0000131003 00000 n
+0000131054 00000 n
+0000131105 00000 n
+0000131156 00000 n
+0000131207 00000 n
+0000131258 00000 n
+0000131309 00000 n
+0000131360 00000 n
+0000131411 00000 n
+0000131462 00000 n
+0000131513 00000 n
+0000131564 00000 n
+0000131615 00000 n
+0000131666 00000 n
+0000131717 00000 n
+0000131768 00000 n
+0000131819 00000 n
+0000131870 00000 n
+0000131921 00000 n
+0000131972 00000 n
+0000132023 00000 n
+0000132074 00000 n
+0000132125 00000 n
+0000132176 00000 n
+0000132227 00000 n
+0000132278 00000 n
+0000132329 00000 n
+0000132380 00000 n
+0000132431 00000 n
+0000132482 00000 n
+0000132533 00000 n
+0000132584 00000 n
+0000132635 00000 n
+0000132686 00000 n
+0000132737 00000 n
+0000132788 00000 n
+0000132839 00000 n
+0000132890 00000 n
+0000132941 00000 n
+0000132992 00000 n
+0000133043 00000 n
+0000133094 00000 n
+0000133145 00000 n
+0000133196 00000 n
+0000133247 00000 n
+0000133298 00000 n
+0000133349 00000 n
+0000133400 00000 n
+0000133451 00000 n
+0000133502 00000 n
+0000133553 00000 n
+0000133604 00000 n
+0000133655 00000 n
+0000133706 00000 n
+0000133757 00000 n
+0000133808 00000 n
+0000133859 00000 n
+0000133910 00000 n
+0000133961 00000 n
+0000134012 00000 n
+0000134063 00000 n
+0000134114 00000 n
+0000134165 00000 n
+0000134216 00000 n
+0000134267 00000 n
+0000134318 00000 n
+0000134369 00000 n
+0000134420 00000 n
+0000134471 00000 n
+0000134522 00000 n
+0000134573 00000 n
+0000134624 00000 n
+0000134675 00000 n
+0000134726 00000 n
+0000134777 00000 n
+0000134828 00000 n
+0000134879 00000 n
+0000134930 00000 n
+0000134981 00000 n
+0000135032 00000 n
+0000135083 00000 n
+0000135134 00000 n
+0000135185 00000 n
+0000135236 00000 n
+0000135287 00000 n
+0000135338 00000 n
+0000135389 00000 n
+0000135440 00000 n
+0000135491 00000 n
+0000135542 00000 n
+0000135593 00000 n
+0000135644 00000 n
+0000135695 00000 n
+0000135746 00000 n
+0000135797 00000 n
+0000135848 00000 n
+0000135899 00000 n
+0000135950 00000 n
+0000136001 00000 n
+0000136052 00000 n
+0000136103 00000 n
+0000136154 00000 n
+0000136205 00000 n
+0000136256 00000 n
+0000136307 00000 n
+0000136358 00000 n
+0000136409 00000 n
+0000136460 00000 n
+0000136511 00000 n
+0000136562 00000 n
+0000136613 00000 n
+0000136664 00000 n
+0000136715 00000 n
+0000136766 00000 n
+0000136817 00000 n
+0000136868 00000 n
+0000136919 00000 n
+0000136970 00000 n
+0000137021 00000 n
+0000137072 00000 n
+0000137123 00000 n
+0000137174 00000 n
+0000137225 00000 n
+0000137276 00000 n
+0000137327 00000 n
+0000137378 00000 n
+0000137429 00000 n
+0000137480 00000 n
+0000137531 00000 n
+0000137582 00000 n
+0000137633 00000 n
+0000137684 00000 n
+0000137735 00000 n
+0000137786 00000 n
+0000137837 00000 n
+0000137888 00000 n
+0000137939 00000 n
+0000137990 00000 n
+0000138041 00000 n
+0000138092 00000 n
+0000138143 00000 n
+0000138194 00000 n
+0000138245 00000 n
+0000138296 00000 n
+0000138347 00000 n
+0000138398 00000 n
+0000138449 00000 n
+0000138500 00000 n
+0000138551 00000 n
+0000138602 00000 n
+0000138653 00000 n
+0000138704 00000 n
+0000138755 00000 n
+0000138806 00000 n
+0000138857 00000 n
+0000138908 00000 n
+0000138959 00000 n
+0000139010 00000 n
+0000139061 00000 n
+0000139112 00000 n
+0000139163 00000 n
+0000139214 00000 n
+0000139265 00000 n
+0000139316 00000 n
+0000139367 00000 n
+0000139418 00000 n
+0000139469 00000 n
+0000139520 00000 n
+0000139571 00000 n
+0000139622 00000 n
+0000139673 00000 n
+0000139724 00000 n
+0000139775 00000 n
+0000139826 00000 n
+0000139877 00000 n
+0000139928 00000 n
+0000139979 00000 n
+0000140030 00000 n
+0000140081 00000 n
+0000140132 00000 n
+0000140183 00000 n
+0000140234 00000 n
+0000140285 00000 n
+0000140336 00000 n
+0000140387 00000 n
+0000140438 00000 n
+0000140489 00000 n
+0000140540 00000 n
+0000140591 00000 n
+0000140642 00000 n
+0000140693 00000 n
+0000140744 00000 n
+0000140795 00000 n
+0000140846 00000 n
+0000140897 00000 n
+0000140948 00000 n
+0000140999 00000 n
+0000141050 00000 n
+0000141101 00000 n
+0000141152 00000 n
+0000141203 00000 n
+0000141254 00000 n
+0000141305 00000 n
+0000141356 00000 n
+0000141407 00000 n
+0000141458 00000 n
+0000141509 00000 n
+0000141560 00000 n
+0000141611 00000 n
+0000141662 00000 n
+0000141713 00000 n
+0000141764 00000 n
+0000141815 00000 n
+0000141866 00000 n
+0000141917 00000 n
+0000141968 00000 n
+0000142019 00000 n
+0000142070 00000 n
+0000142121 00000 n
+0000142172 00000 n
+0000142223 00000 n
+0000142274 00000 n
+0000142325 00000 n
+0000142376 00000 n
+0000142427 00000 n
+0000142478 00000 n
+0000142529 00000 n
+0000142580 00000 n
+0000142631 00000 n
+0000142682 00000 n
+0000142733 00000 n
+0000142784 00000 n
+0000142835 00000 n
+0000142886 00000 n
+0000142937 00000 n
+0000142988 00000 n
+0000143039 00000 n
+0000143090 00000 n
+0000143141 00000 n
+0000143192 00000 n
+0000143243 00000 n
+0000143294 00000 n
+0000143345 00000 n
+0000143396 00000 n
+0000143447 00000 n
+0000143498 00000 n
+0000143549 00000 n
+0000143600 00000 n
+0000143651 00000 n
+0000143702 00000 n
+0000143753 00000 n
+0000143804 00000 n
+0000143855 00000 n
+0000143906 00000 n
+0000143957 00000 n
+0000144008 00000 n
+0000144059 00000 n
+0000144110 00000 n
+0000144161 00000 n
+0000144212 00000 n
+0000144263 00000 n
+0000144314 00000 n
+0000144365 00000 n
+0000144416 00000 n
+0000144467 00000 n
+0000144518 00000 n
+0000144569 00000 n
+0000144620 00000 n
+0000144671 00000 n
+0000144722 00000 n
+0000144773 00000 n
+0000144824 00000 n
+0000144875 00000 n
+0000144926 00000 n
+0000144977 00000 n
+0000145028 00000 n
+0000145079 00000 n
+0000145130 00000 n
+0000145181 00000 n
+0000145232 00000 n
+0000145283 00000 n
+0000145334 00000 n
+0000145385 00000 n
+0000145436 00000 n
+0000145487 00000 n
+0000145538 00000 n
+0000145589 00000 n
+0000145640 00000 n
+0000145691 00000 n
+0000145742 00000 n
+0000145793 00000 n
+0000145844 00000 n
+0000145895 00000 n
+0000145946 00000 n
+0000145997 00000 n
+0000146048 00000 n
+0000146099 00000 n
+0000146150 00000 n
+0000146201 00000 n
+0000146252 00000 n
+0000146303 00000 n
+0000146354 00000 n
+0000146405 00000 n
+0000146456 00000 n
+0000146507 00000 n
+0000146558 00000 n
+0000146609 00000 n
+0000146660 00000 n
+0000146711 00000 n
+0000146762 00000 n
+0000146813 00000 n
+0000146864 00000 n
+0000146915 00000 n
+0000146966 00000 n
+0000147017 00000 n
+0000147068 00000 n
+0000147119 00000 n
+0000147170 00000 n
+0000147221 00000 n
+0000147272 00000 n
+0000147323 00000 n
+0000147374 00000 n
+0000147425 00000 n
+0000147476 00000 n
+0000147527 00000 n
+0000147578 00000 n
+0000147629 00000 n
+0000147680 00000 n
+0000147731 00000 n
+0000147782 00000 n
+0000147833 00000 n
+0000147884 00000 n
+0000147935 00000 n
+0000147986 00000 n
+0000148037 00000 n
+0000148088 00000 n
+0000148139 00000 n
+0000148190 00000 n
+0000148241 00000 n
+0000148292 00000 n
+0000148343 00000 n
+0000148394 00000 n
+0000148445 00000 n
+0000148496 00000 n
+0000148547 00000 n
+0000148598 00000 n
+0000148649 00000 n
+0000148700 00000 n
+0000148751 00000 n
+0000148802 00000 n
+0000148853 00000 n
+0000148904 00000 n
+0000148955 00000 n
+0000149006 00000 n
+0000149057 00000 n
+0000149108 00000 n
+0000149159 00000 n
+0000149210 00000 n
+0000149261 00000 n
+0000149312 00000 n
+0000149363 00000 n
+0000149414 00000 n
+0000149465 00000 n
+0000149516 00000 n
+0000149567 00000 n
+0000149618 00000 n
+0000149669 00000 n
+0000149720 00000 n
+0000149771 00000 n
+0000149822 00000 n
+0000149873 00000 n
+0000149924 00000 n
+0000149975 00000 n
+0000150026 00000 n
+0000150077 00000 n
+0000150128 00000 n
+0000150179 00000 n
+0000150230 00000 n
+0000150281 00000 n
+0000150332 00000 n
+0000150383 00000 n
+0000150434 00000 n
+0000150485 00000 n
+0000150536 00000 n
+0000150587 00000 n
+0000150638 00000 n
+0000150689 00000 n
+0000150740 00000 n
+0000150791 00000 n
+0000150842 00000 n
+0000150893 00000 n
+0000150944 00000 n
+0000150995 00000 n
+0000151046 00000 n
+0000151097 00000 n
+0000151148 00000 n
+0000151199 00000 n
+0000151250 00000 n
+0000151301 00000 n
+0000151352 00000 n
+0000151403 00000 n
+0000151454 00000 n
+0000151505 00000 n
+0000151556 00000 n
+0000151607 00000 n
+0000151658 00000 n
+0000151709 00000 n
+0000151760 00000 n
+0000151811 00000 n
+0000151862 00000 n
+0000151913 00000 n
+0000151964 00000 n
+0000152015 00000 n
+0000152066 00000 n
+0000152117 00000 n
+0000152168 00000 n
+0000152219 00000 n
+0000152270 00000 n
+0000152321 00000 n
+0000152372 00000 n
+0000152423 00000 n
+0000152474 00000 n
+0000152525 00000 n
+0000152576 00000 n
+0000152627 00000 n
+0000152678 00000 n
+0000152729 00000 n
+0000152780 00000 n
+0000152831 00000 n
+0000152882 00000 n
+0000152933 00000 n
+0000152984 00000 n
+0000153035 00000 n
+0000153086 00000 n
+0000153137 00000 n
+0000153188 00000 n
+0000153239 00000 n
+0000153290 00000 n
+0000153341 00000 n
+0000153392 00000 n
+0000153443 00000 n
+0000153494 00000 n
+0000153545 00000 n
+0000153596 00000 n
+0000153647 00000 n
+0000153698 00000 n
+0000153749 00000 n
+0000153800 00000 n
+0000153851 00000 n
+0000153902 00000 n
+0000153953 00000 n
+0000154004 00000 n
+0000154055 00000 n
+0000154106 00000 n
+0000154157 00000 n
+0000154208 00000 n
+0000154259 00000 n
+0000154310 00000 n
+0000154361 00000 n
+0000154412 00000 n
+0000154463 00000 n
+0000154514 00000 n
+0000154565 00000 n
+0000154616 00000 n
+0000154667 00000 n
+0000154718 00000 n
+0000156085 00000 n
+0000156240 00000 n
+0000162601 00000 n
+0000162624 00000 n
+0000162722 00000 n
+0000162826 00000 n
+0000162847 00000 n
+0000163005 00000 n
+0000163929 00000 n
+0000163951 00000 n
+0000164086 00000 n
+0000164407 00000 n
+0000164429 00000 n
+0000164572 00000 n
+0000165492 00000 n
+0000165514 00000 n
+0000165657 00000 n
+0000167095 00000 n
+0000167118 00000 n
+0000167261 00000 n
+0000168158 00000 n
+0000168180 00000 n
+0000168296 00000 n
+0000168496 00000 n
+0000168518 00000 n
+0000168652 00000 n
+0000169007 00000 n
+0000169029 00000 n
+0000169154 00000 n
+0000169504 00000 n
+0000169526 00000 n
+0000169651 00000 n
+0000170001 00000 n
+0000170023 00000 n
+0000170148 00000 n
+0000170511 00000 n
+0000170533 00000 n
+0000170667 00000 n
+0000171018 00000 n
+0000171040 00000 n
+0000171165 00000 n
+0000171426 00000 n
+0000171448 00000 n
+0000171582 00000 n
+0000171981 00000 n
+0000172003 00000 n
+0000172128 00000 n
+0000172408 00000 n
+0000172430 00000 n
+0000172564 00000 n
+0000172981 00000 n
+0000173003 00000 n
+0000173128 00000 n
+0000173422 00000 n
+0000173444 00000 n
+0000173578 00000 n
+0000173858 00000 n
+0000173880 00000 n
+0000174005 00000 n
+0000174235 00000 n
+0000174257 00000 n
+0000174391 00000 n
+0000174636 00000 n
+0000174658 00000 n
+0000174774 00000 n
+0000174973 00000 n
+0000174995 00000 n
+0000175129 00000 n
+0000175369 00000 n
+0000175391 00000 n
+0000175557 00000 n
+0000176344 00000 n
+0000176366 00000 n
+0000176523 00000 n
+0000176861 00000 n
+0000176883 00000 n
+0000177049 00000 n
+0000177738 00000 n
+0000177760 00000 n
+0000177926 00000 n
+0000178989 00000 n
+0000179011 00000 n
+0000179168 00000 n
+0000179967 00000 n
+0000179989 00000 n
+0000180155 00000 n
+0000181231 00000 n
+0000181254 00000 n
+0000181411 00000 n
+0000182076 00000 n
+0000182098 00000 n
+0000182264 00000 n
+0000182959 00000 n
+0000182981 00000 n
+0000183147 00000 n
+0000183885 00000 n
+0000183907 00000 n
+0000184064 00000 n
+0000184619 00000 n
+0000184641 00000 n
+0000184807 00000 n
+0000185620 00000 n
+0000185642 00000 n
+0000185808 00000 n
+0000186488 00000 n
+0000186510 00000 n
+0000186662 00000 n
+0000187394 00000 n
+0000187416 00000 n
+0000187582 00000 n
+0000188315 00000 n
+0000188337 00000 n
+0000188494 00000 n
+0000189220 00000 n
+0000189242 00000 n
+0000189408 00000 n
+0000190147 00000 n
+0000190169 00000 n
+0000190327 00000 n
+0000190856 00000 n
+0000190878 00000 n
+0000191045 00000 n
+0000191686 00000 n
+0000191708 00000 n
+0000191875 00000 n
+0000192626 00000 n
+0000192648 00000 n
+0000192815 00000 n
+0000193613 00000 n
+0000193635 00000 n
+0000193793 00000 n
+0000194346 00000 n
+0000194368 00000 n
+0000194535 00000 n
+0000195325 00000 n
+0000195347 00000 n
+0000195514 00000 n
+0000196378 00000 n
+0000196400 00000 n
+0000196552 00000 n
+0000197335 00000 n
+0000197357 00000 n
+0000197506 00000 n
+0000197817 00000 n
+0000197839 00000 n
+0000198006 00000 n
+0000198653 00000 n
+0000198675 00000 n
+0000198842 00000 n
+0000199625 00000 n
+0000199647 00000 n
+0000199799 00000 n
+0000200536 00000 n
+0000200558 00000 n
+0000200707 00000 n
+0000201100 00000 n
+0000201122 00000 n
+0000201274 00000 n
+0000202045 00000 n
+0000202067 00000 n
+0000202216 00000 n
+0000202607 00000 n
+0000202629 00000 n
+0000202796 00000 n
+0000203645 00000 n
+0000203667 00000 n
+0000203819 00000 n
+0000204542 00000 n
+0000204564 00000 n
+0000204713 00000 n
+0000205103 00000 n
+0000205125 00000 n
+0000205292 00000 n
+0000205956 00000 n
+0000205978 00000 n
+0000206139 00000 n
+0000206802 00000 n
+0000206824 00000 n
+0000206982 00000 n
+0000207535 00000 n
+0000207557 00000 n
+0000207691 00000 n
+0000208057 00000 n
+0000208079 00000 n
+0000208213 00000 n
+0000208577 00000 n
+0000208599 00000 n
+0000208733 00000 n
+0000209101 00000 n
+0000209123 00000 n
+0000209257 00000 n
+0000209621 00000 n
+0000209643 00000 n
+0000209777 00000 n
+0000210142 00000 n
+0000210164 00000 n
+0000210298 00000 n
+0000210664 00000 n
+0000210686 00000 n
+0000210820 00000 n
+0000211183 00000 n
+0000211205 00000 n
+0000211339 00000 n
+0000211707 00000 n
+0000211729 00000 n
+0000211863 00000 n
+0000212226 00000 n
+0000212248 00000 n
+0000212382 00000 n
+0000212746 00000 n
+0000212768 00000 n
+0000212902 00000 n
+0000213263 00000 n
+0000213285 00000 n
+0000213419 00000 n
+0000213783 00000 n
+0000213805 00000 n
+0000213939 00000 n
+0000214308 00000 n
+0000214330 00000 n
+0000214464 00000 n
+0000214833 00000 n
+0000214855 00000 n
+0000214989 00000 n
+0000215355 00000 n
+0000215377 00000 n
+0000215511 00000 n
+0000215878 00000 n
+0000215900 00000 n
+0000216034 00000 n
+0000216397 00000 n
+0000216419 00000 n
+0000216553 00000 n
+0000216923 00000 n
+0000216945 00000 n
+0000217079 00000 n
+0000217442 00000 n
+0000217464 00000 n
+0000217598 00000 n
+0000217962 00000 n
+0000217984 00000 n
+0000218118 00000 n
+0000218482 00000 n
+0000218504 00000 n
+0000218638 00000 n
+0000219001 00000 n
+0000219023 00000 n
+0000219157 00000 n
+0000219519 00000 n
+0000219541 00000 n
+0000219675 00000 n
+0000220042 00000 n
+0000220064 00000 n
+0000220198 00000 n
+0000220563 00000 n
+0000220585 00000 n
+0000220719 00000 n
+0000221085 00000 n
+0000221107 00000 n
+0000221241 00000 n
+0000221604 00000 n
+0000221626 00000 n
+0000221760 00000 n
+0000222125 00000 n
+0000222147 00000 n
+0000222281 00000 n
+0000222645 00000 n
+0000222667 00000 n
+0000222801 00000 n
+0000223169 00000 n
+0000223191 00000 n
+0000223325 00000 n
+0000223692 00000 n
+0000223714 00000 n
+0000223848 00000 n
+0000224212 00000 n
+0000224234 00000 n
+0000224368 00000 n
+0000224735 00000 n
+0000224757 00000 n
+0000224891 00000 n
+0000225260 00000 n
+0000225282 00000 n
+0000225416 00000 n
+0000225781 00000 n
+0000225803 00000 n
+0000225937 00000 n
+0000226304 00000 n
+0000226326 00000 n
+0000226460 00000 n
+0000226827 00000 n
+0000226849 00000 n
+0000226983 00000 n
+0000227352 00000 n
+0000227374 00000 n
+0000227508 00000 n
+0000227875 00000 n
+0000227897 00000 n
+0000228031 00000 n
+0000228397 00000 n
+0000228419 00000 n
+0000228553 00000 n
+0000228919 00000 n
+0000228941 00000 n
+0000229075 00000 n
+0000229443 00000 n
+0000229465 00000 n
+0000229599 00000 n
+0000229961 00000 n
+0000229983 00000 n
+0000230117 00000 n
+0000230486 00000 n
+0000230508 00000 n
+0000230642 00000 n
+0000231006 00000 n
+0000231028 00000 n
+0000231162 00000 n
+0000231525 00000 n
+0000231547 00000 n
+0000231681 00000 n
+0000232042 00000 n
+0000232064 00000 n
+0000232198 00000 n
+0000232561 00000 n
+0000232583 00000 n
+0000232717 00000 n
+0000233082 00000 n
+0000233104 00000 n
+0000233238 00000 n
+0000233604 00000 n
+0000233626 00000 n
+0000233760 00000 n
+0000234121 00000 n
+0000234143 00000 n
+0000234277 00000 n
+0000234644 00000 n
+0000234666 00000 n
+0000234800 00000 n
+0000235162 00000 n
+0000235184 00000 n
+0000235318 00000 n
+0000235682 00000 n
+0000235704 00000 n
+0000235838 00000 n
+0000236204 00000 n
+0000236226 00000 n
+0000236360 00000 n
+0000236732 00000 n
+0000236754 00000 n
+0000236888 00000 n
+0000237253 00000 n
+0000237275 00000 n
+0000237409 00000 n
+0000237777 00000 n
+0000237799 00000 n
+0000237933 00000 n
+0000238300 00000 n
+0000238322 00000 n
+0000238456 00000 n
+0000238824 00000 n
+0000238846 00000 n
+0000238980 00000 n
+0000239343 00000 n
+0000239365 00000 n
+0000239499 00000 n
+0000239865 00000 n
+0000239887 00000 n
+0000240021 00000 n
+0000240382 00000 n
+0000240404 00000 n
+0000240538 00000 n
+0000240906 00000 n
+0000240928 00000 n
+0000241062 00000 n
+0000241427 00000 n
+0000241449 00000 n
+0000241583 00000 n
+0000241952 00000 n
+0000241974 00000 n
+0000242132 00000 n
+0000244701 00000 n
+0000244724 00000 n
+0000244882 00000 n
+0000247103 00000 n
+0000247126 00000 n
+0000247284 00000 n
+0000248663 00000 n
+0000248686 00000 n
+0000248835 00000 n
+0000250110 00000 n
+0000250133 00000 n
+0000250282 00000 n
+0000251534 00000 n
+0000251557 00000 n
+0000251706 00000 n
+0000252999 00000 n
+0000253022 00000 n
+0000253171 00000 n
+0000254467 00000 n
+0000254490 00000 n
+0000254639 00000 n
+0000255920 00000 n
+0000255943 00000 n
+0000256092 00000 n
+0000257388 00000 n
+0000257411 00000 n
+0000257560 00000 n
+0000258824 00000 n
+0000258847 00000 n
+0000258996 00000 n
+0000260286 00000 n
+0000260309 00000 n
+0000260458 00000 n
+0000261747 00000 n
+0000261770 00000 n
+0000261919 00000 n
+0000263216 00000 n
+0000263239 00000 n
+0000263388 00000 n
+0000264664 00000 n
+0000264687 00000 n
+0000264836 00000 n
+0000266127 00000 n
+0000266150 00000 n
+0000266299 00000 n
+0000267592 00000 n
+0000267615 00000 n
+0000267764 00000 n
+0000268222 00000 n
+0000268244 00000 n
+0000268360 00000 n
+0000268542 00000 n
+0000268564 00000 n
+0000268623 00000 n
+0000268732 00000 n
+0000268883 00000 n
+0000268990 00000 n
+0000269099 00000 n
+0000269271 00000 n
+0000269383 00000 n
+0000269503 00000 n
+0000269613 00000 n
+0000269726 00000 n
+0000269839 00000 n
+0000269960 00000 n
+0000270062 00000 n
+0000270222 00000 n
+0000270368 00000 n
+0000270484 00000 n
+0000270642 00000 n
+0000270747 00000 n
+0000270895 00000 n
+0000271015 00000 n
+0000271144 00000 n
+0000271251 00000 n
+0000271408 00000 n
+0000271513 00000 n
+0000271641 00000 n
+0000271769 00000 n
+0000271892 00000 n
+0000272024 00000 n
+0000272150 00000 n
+0000272262 00000 n
+0000272419 00000 n
+0000272524 00000 n
+0000272653 00000 n
+0000272776 00000 n
+0000272904 00000 n
+0000273033 00000 n
+0000273157 00000 n
+0000273289 00000 n
+0000273418 00000 n
+0000273548 00000 n
+0000273665 00000 n
+0000273826 00000 n
+0000273931 00000 n
+0000274065 00000 n
+0000274200 00000 n
+0000274317 00000 n
+0000274459 00000 n
+0000274564 00000 n
+0000274688 00000 n
+0000274812 00000 n
+0000274935 00000 n
+0000275060 00000 n
+0000275181 00000 n
+0000275309 00000 n
+0000275416 00000 n
+0000275579 00000 n
+0000275717 00000 n
+0000275832 00000 n
+0000275953 00000 n
+0000276060 00000 n
+0000276226 00000 n
+0000276319 00000 n
+0000276446 00000 n
+0000276559 00000 n
+0000276730 00000 n
+0000276868 00000 n
+0000276975 00000 n
+0000277083 00000 n
+0000277243 00000 n
+0000277353 00000 n
+0000277483 00000 n
+0000277613 00000 n
+0000277724 00000 n
+0000277839 00000 n
+0000278003 00000 n
+0000278141 00000 n
+0000278256 00000 n
+0000278377 00000 n
+0000278498 00000 n
+0000278597 00000 n
+0000278763 00000 n
+0000278856 00000 n
+0000278983 00000 n
+0000279104 00000 n
+0000279261 00000 n
+0000279367 00000 n
+0000279487 00000 n
+0000279606 00000 n
+0000279730 00000 n
+0000279849 00000 n
+0000279957 00000 n
+0000280115 00000 n
+0000280193 00000 n
+0000280337 00000 n
+0000280484 00000 n
+0000280581 00000 n
+0000280696 00000 n
+0000280809 00000 n
+0000280926 00000 n
+0000281039 00000 n
+0000281139 00000 n
+0000281300 00000 n
+0000281397 00000 n
+0000281512 00000 n
+0000281625 00000 n
+0000281742 00000 n
+0000281855 00000 n
+0000281955 00000 n
+0000282120 00000 n
+0000282217 00000 n
+0000282332 00000 n
+0000282445 00000 n
+0000282562 00000 n
+0000282675 00000 n
+0000282775 00000 n
+0000282936 00000 n
+0000283033 00000 n
+0000283148 00000 n
+0000283261 00000 n
+0000283378 00000 n
+0000283491 00000 n
+0000283591 00000 n
+0000283754 00000 n
+0000283851 00000 n
+0000283966 00000 n
+0000284083 00000 n
+0000284196 00000 n
+0000284296 00000 n
+0000284458 00000 n
+0000284555 00000 n
+0000284670 00000 n
+0000284783 00000 n
+0000284900 00000 n
+0000285013 00000 n
+0000285113 00000 n
+0000285275 00000 n
+0000285372 00000 n
+0000285485 00000 n
+0000285602 00000 n
+0000285715 00000 n
+0000285815 00000 n
+0000285976 00000 n
+0000286073 00000 n
+0000286188 00000 n
+0000286301 00000 n
+0000286418 00000 n
+0000286518 00000 n
+0000286681 00000 n
+0000286778 00000 n
+0000286893 00000 n
+0000287006 00000 n
+0000287123 00000 n
+0000287236 00000 n
+0000287336 00000 n
+0000287494 00000 n
+0000287591 00000 n
+0000287706 00000 n
+0000287819 00000 n
+0000287936 00000 n
+0000288035 00000 n
+0000288198 00000 n
+0000288295 00000 n
+0000288410 00000 n
+0000288523 00000 n
+0000288640 00000 n
+0000288753 00000 n
+0000288853 00000 n
+0000289016 00000 n
+0000289113 00000 n
+0000289226 00000 n
+0000289343 00000 n
+0000289456 00000 n
+0000289556 00000 n
+0000289720 00000 n
+0000289817 00000 n
+0000289932 00000 n
+0000290045 00000 n
+0000290162 00000 n
+0000290275 00000 n
+0000290375 00000 n
+0000290536 00000 n
+0000290633 00000 n
+0000290750 00000 n
+0000290863 00000 n
+0000290963 00000 n
+0000291123 00000 n
+0000291220 00000 n
+0000291335 00000 n
+0000291452 00000 n
+0000291565 00000 n
+0000291665 00000 n
+0000291824 00000 n
+0000291921 00000 n
+0000292036 00000 n
+0000292149 00000 n
+0000292266 00000 n
+0000292379 00000 n
+0000292479 00000 n
+0000292641 00000 n
+0000292738 00000 n
+0000292853 00000 n
+0000292966 00000 n
+0000293083 00000 n
+0000293196 00000 n
+0000293296 00000 n
+0000293457 00000 n
+0000293554 00000 n
+0000293667 00000 n
+0000293784 00000 n
+0000293897 00000 n
+0000293997 00000 n
+0000294160 00000 n
+0000294257 00000 n
+0000294372 00000 n
+0000294485 00000 n
+0000294602 00000 n
+0000294715 00000 n
+0000294815 00000 n
+0000294979 00000 n
+0000295076 00000 n
+0000295191 00000 n
+0000295304 00000 n
+0000295421 00000 n
+0000295534 00000 n
+0000295634 00000 n
+0000295795 00000 n
+0000295892 00000 n
+0000296007 00000 n
+0000296120 00000 n
+0000296237 00000 n
+0000296350 00000 n
+0000296450 00000 n
+0000296613 00000 n
+0000296710 00000 n
+0000296825 00000 n
+0000296942 00000 n
+0000297055 00000 n
+0000297155 00000 n
+0000297317 00000 n
+0000297414 00000 n
+0000297529 00000 n
+0000297642 00000 n
+0000297759 00000 n
+0000297872 00000 n
+0000297972 00000 n
+0000298140 00000 n
+0000298237 00000 n
+0000298352 00000 n
+0000298465 00000 n
+0000298582 00000 n
+0000298695 00000 n
+0000298795 00000 n
+0000298963 00000 n
+0000299060 00000 n
+0000299175 00000 n
+0000299288 00000 n
+0000299405 00000 n
+0000299518 00000 n
+0000299618 00000 n
+0000299787 00000 n
+0000299884 00000 n
+0000299999 00000 n
+0000300112 00000 n
+0000300229 00000 n
+0000300342 00000 n
+0000300442 00000 n
+0000300611 00000 n
+0000300708 00000 n
+0000300823 00000 n
+0000300936 00000 n
+0000301053 00000 n
+0000301166 00000 n
+0000301266 00000 n
+0000301424 00000 n
+0000301521 00000 n
+0000301634 00000 n
+0000301751 00000 n
+0000301864 00000 n
+0000301964 00000 n
+0000302124 00000 n
+0000302221 00000 n
+0000302336 00000 n
+0000302449 00000 n
+0000302566 00000 n
+0000302665 00000 n
+0000302821 00000 n
+0000302918 00000 n
+0000303031 00000 n
+0000303148 00000 n
+0000303261 00000 n
+0000303361 00000 n
+0000303521 00000 n
+0000303618 00000 n
+0000303733 00000 n
+0000303846 00000 n
+0000303963 00000 n
+0000304076 00000 n
+0000304176 00000 n
+0000304333 00000 n
+0000304430 00000 n
+0000304545 00000 n
+0000304658 00000 n
+0000304775 00000 n
+0000304888 00000 n
+0000304988 00000 n
+0000305151 00000 n
+0000305248 00000 n
+0000305363 00000 n
+0000305476 00000 n
+0000305593 00000 n
+0000305706 00000 n
+0000305806 00000 n
+0000305963 00000 n
+0000306060 00000 n
+0000306175 00000 n
+0000306288 00000 n
+0000306405 00000 n
+0000306518 00000 n
+0000306618 00000 n
+0000306777 00000 n
+0000306874 00000 n
+0000306989 00000 n
+0000307102 00000 n
+0000307219 00000 n
+0000307332 00000 n
+0000307432 00000 n
+0000307592 00000 n
+0000307689 00000 n
+0000307804 00000 n
+0000307917 00000 n
+0000308034 00000 n
+0000308147 00000 n
+0000308247 00000 n
+0000308405 00000 n
+0000308502 00000 n
+0000308617 00000 n
+0000308730 00000 n
+0000308847 00000 n
+0000308960 00000 n
+0000309060 00000 n
+0000309220 00000 n
+0000309317 00000 n
+0000309432 00000 n
+0000309545 00000 n
+0000309662 00000 n
+0000309775 00000 n
+0000309875 00000 n
+0000310032 00000 n
+0000310129 00000 n
+0000310244 00000 n
+0000310357 00000 n
+0000310474 00000 n
+0000310587 00000 n
+0000310687 00000 n
+0000310844 00000 n
+0000310941 00000 n
+0000311056 00000 n
+0000311169 00000 n
+0000311286 00000 n
+0000311399 00000 n
+0000311499 00000 n
+0000311654 00000 n
+0000311751 00000 n
+0000311866 00000 n
+0000311979 00000 n
+0000312096 00000 n
+0000312209 00000 n
+0000312309 00000 n
+0000312465 00000 n
+0000312562 00000 n
+0000312677 00000 n
+0000312790 00000 n
+0000312907 00000 n
+0000313020 00000 n
+0000313120 00000 n
+0000313285 00000 n
+0000313382 00000 n
+0000313497 00000 n
+0000313610 00000 n
+0000313727 00000 n
+0000313840 00000 n
+0000313940 00000 n
+0000314103 00000 n
+0000314200 00000 n
+0000314315 00000 n
+0000314428 00000 n
+0000314545 00000 n
+0000314658 00000 n
+0000314758 00000 n
+0000314918 00000 n
+0000315015 00000 n
+0000315130 00000 n
+0000315243 00000 n
+0000315360 00000 n
+0000315473 00000 n
+0000315573 00000 n
+0000315734 00000 n
+0000315831 00000 n
+0000315946 00000 n
+0000316059 00000 n
+0000316176 00000 n
+0000316289 00000 n
+0000316389 00000 n
+0000316545 00000 n
+0000316642 00000 n
+0000316757 00000 n
+0000316870 00000 n
+0000316987 00000 n
+0000317100 00000 n
+0000317200 00000 n
+0000317362 00000 n
+0000317459 00000 n
+0000317574 00000 n
+0000317687 00000 n
+0000317804 00000 n
+0000317917 00000 n
+0000318017 00000 n
+0000318176 00000 n
+0000318273 00000 n
+0000318388 00000 n
+0000318501 00000 n
+0000318618 00000 n
+0000318731 00000 n
+0000318831 00000 n
+0000318987 00000 n
+0000319084 00000 n
+0000319199 00000 n
+0000319312 00000 n
+0000319429 00000 n
+0000319542 00000 n
+0000319642 00000 n
+0000319800 00000 n
+0000319897 00000 n
+0000320012 00000 n
+0000320125 00000 n
+0000320242 00000 n
+0000320355 00000 n
+0000320455 00000 n
+0000320610 00000 n
+0000320707 00000 n
+0000320822 00000 n
+0000320935 00000 n
+0000321052 00000 n
+0000321165 00000 n
+0000321265 00000 n
+0000321421 00000 n
+0000321518 00000 n
+0000321633 00000 n
+0000321746 00000 n
+0000321863 00000 n
+0000321976 00000 n
+0000322076 00000 n
+0000322237 00000 n
+0000322334 00000 n
+0000322449 00000 n
+0000322562 00000 n
+0000322679 00000 n
+0000322792 00000 n
+0000322892 00000 n
+0000323052 00000 n
+0000323149 00000 n
+0000323264 00000 n
+0000323377 00000 n
+0000323494 00000 n
+0000323607 00000 n
+0000323707 00000 n
+0000323867 00000 n
+0000323964 00000 n
+0000324079 00000 n
+0000324192 00000 n
+0000324309 00000 n
+0000324422 00000 n
+0000324522 00000 n
+0000324679 00000 n
+0000324776 00000 n
+0000324891 00000 n
+0000325004 00000 n
+0000325121 00000 n
+0000325234 00000 n
+0000325334 00000 n
+0000325492 00000 n
+0000325589 00000 n
+0000325704 00000 n
+0000325817 00000 n
+0000325934 00000 n
+0000326047 00000 n
+0000326147 00000 n
+0000326304 00000 n
+0000326401 00000 n
+0000326516 00000 n
+0000326629 00000 n
+0000326746 00000 n
+0000326859 00000 n
+0000326959 00000 n
+0000327120 00000 n
+0000327217 00000 n
+0000327332 00000 n
+0000327445 00000 n
+0000327562 00000 n
+0000327675 00000 n
+0000327775 00000 n
+0000327937 00000 n
+0000328034 00000 n
+0000328149 00000 n
+0000328262 00000 n
+0000328379 00000 n
+0000328492 00000 n
+0000328592 00000 n
+0000328750 00000 n
+0000328847 00000 n
+0000328962 00000 n
+0000329075 00000 n
+0000329192 00000 n
+0000329305 00000 n
+0000329405 00000 n
+0000329566 00000 n
+0000329663 00000 n
+0000329778 00000 n
+0000329891 00000 n
+0000330008 00000 n
+0000330121 00000 n
+0000330221 00000 n
+0000330383 00000 n
+0000330480 00000 n
+0000330595 00000 n
+0000330708 00000 n
+0000330825 00000 n
+0000330938 00000 n
+0000331038 00000 n
+0000331197 00000 n
+0000331294 00000 n
+0000331409 00000 n
+0000331522 00000 n
+0000331639 00000 n
+0000331752 00000 n
+0000331852 00000 n
+0000332012 00000 n
+0000332109 00000 n
+0000332224 00000 n
+0000332337 00000 n
+0000332454 00000 n
+0000332567 00000 n
+0000332667 00000 n
+0000332831 00000 n
+0000332928 00000 n
+0000333043 00000 n
+0000333156 00000 n
+0000333273 00000 n
+0000333386 00000 n
+0000333486 00000 n
+0000333651 00000 n
+0000333748 00000 n
+0000333863 00000 n
+0000333976 00000 n
+0000334093 00000 n
+0000334206 00000 n
+0000334306 00000 n
+0000334469 00000 n
+0000334566 00000 n
+0000334681 00000 n
+0000334794 00000 n
+0000334911 00000 n
+0000335024 00000 n
+0000335124 00000 n
+0000335284 00000 n
+0000335381 00000 n
+0000335496 00000 n
+0000335609 00000 n
+0000335726 00000 n
+0000335839 00000 n
+0000335939 00000 n
+0000336100 00000 n
+0000336197 00000 n
+0000336312 00000 n
+0000336425 00000 n
+0000336542 00000 n
+0000336655 00000 n
+0000336755 00000 n
+0000336916 00000 n
+0000337013 00000 n
+0000337128 00000 n
+0000337241 00000 n
+0000337358 00000 n
+0000337471 00000 n
+0000337571 00000 n
+0000337728 00000 n
+0000337825 00000 n
+0000337940 00000 n
+0000338053 00000 n
+0000338170 00000 n
+0000338283 00000 n
+0000338383 00000 n
+0000338547 00000 n
+0000338644 00000 n
+0000338759 00000 n
+0000338872 00000 n
+0000338989 00000 n
+0000339102 00000 n
+0000339202 00000 n
+0000339359 00000 n
+0000339456 00000 n
+0000339571 00000 n
+0000339684 00000 n
+0000339801 00000 n
+0000339914 00000 n
+0000340014 00000 n
+0000340168 00000 n
+0000340265 00000 n
+0000340380 00000 n
+0000340493 00000 n
+0000340610 00000 n
+0000340723 00000 n
+0000340823 00000 n
+0000340978 00000 n
+0000341075 00000 n
+0000341190 00000 n
+0000341303 00000 n
+0000341420 00000 n
+0000341533 00000 n
+0000341633 00000 n
+0000341788 00000 n
+0000341885 00000 n
+0000342000 00000 n
+0000342113 00000 n
+0000342230 00000 n
+0000342343 00000 n
+0000342443 00000 n
+0000342604 00000 n
+0000342701 00000 n
+0000342816 00000 n
+0000342929 00000 n
+0000343046 00000 n
+0000343159 00000 n
+0000343259 00000 n
+0000343415 00000 n
+0000343512 00000 n
+0000343627 00000 n
+0000343740 00000 n
+0000343857 00000 n
+0000343970 00000 n
+0000344070 00000 n
+0000344226 00000 n
+0000344323 00000 n
+0000344438 00000 n
+0000344551 00000 n
+0000344668 00000 n
+0000344781 00000 n
+0000344881 00000 n
+0000345041 00000 n
+0000345138 00000 n
+0000345253 00000 n
+0000345366 00000 n
+0000345483 00000 n
+0000345596 00000 n
+0000345696 00000 n
+0000345853 00000 n
+0000345950 00000 n
+0000346065 00000 n
+0000346178 00000 n
+0000346295 00000 n
+0000346408 00000 n
+0000346508 00000 n
+0000346663 00000 n
+0000346760 00000 n
+0000346875 00000 n
+0000346988 00000 n
+0000347105 00000 n
+0000347218 00000 n
+0000347318 00000 n
+0000347479 00000 n
+0000347576 00000 n
+0000347691 00000 n
+0000347804 00000 n
+0000347921 00000 n
+0000348034 00000 n
+0000348134 00000 n
+0000348301 00000 n
+0000348398 00000 n
+0000348513 00000 n
+0000348626 00000 n
+0000348743 00000 n
+0000348856 00000 n
+0000348956 00000 n
+0000349117 00000 n
+0000349214 00000 n
+0000349329 00000 n
+0000349442 00000 n
+0000349559 00000 n
+0000349672 00000 n
+0000349772 00000 n
+0000349931 00000 n
+0000350028 00000 n
+0000350143 00000 n
+0000350256 00000 n
+0000350373 00000 n
+0000350486 00000 n
+0000350586 00000 n
+0000350749 00000 n
+0000350846 00000 n
+0000350961 00000 n
+0000351074 00000 n
+0000351191 00000 n
+0000351304 00000 n
+0000351404 00000 n
+0000351565 00000 n
+0000351662 00000 n
+0000351777 00000 n
+0000351890 00000 n
+0000352007 00000 n
+0000352120 00000 n
+0000352220 00000 n
+0000352377 00000 n
+0000352474 00000 n
+0000352589 00000 n
+0000352702 00000 n
+0000352819 00000 n
+0000352932 00000 n
+0000353032 00000 n
+0000353191 00000 n
+0000353288 00000 n
+0000353403 00000 n
+0000353516 00000 n
+0000353633 00000 n
+0000353746 00000 n
+0000353846 00000 n
+0000354001 00000 n
+0000354098 00000 n
+0000354213 00000 n
+0000354326 00000 n
+0000354443 00000 n
+0000354556 00000 n
+0000354656 00000 n
+0000354817 00000 n
+0000354914 00000 n
+0000355029 00000 n
+0000355142 00000 n
+0000355259 00000 n
+0000355372 00000 n
+0000355472 00000 n
+0000355631 00000 n
+0000355728 00000 n
+0000355843 00000 n
+0000355956 00000 n
+0000356073 00000 n
+0000356186 00000 n
+0000356286 00000 n
+0000356432 00000 n
+0000356529 00000 n
+0000356644 00000 n
+0000356757 00000 n
+0000356874 00000 n
+0000356987 00000 n
+0000357087 00000 n
+trailer
+<</Size 2890/Root 2889 0 R/Info 1 0 R>>
+startxref
+357320
+%%EOF
diff --git a/doc/spm.shtml b/doc/spm.shtml
new file mode 100644
index 000000000..4e361fc9c
--- /dev/null
+++ b/doc/spm.shtml
@@ -0,0 +1,4202 @@
+<HTML>
+<HEAD>
+ <META NAME="COPYRIGHT" CONTENT="Copyright 1997-2000, All Rights Reserved">
+ <META NAME="DOCNUMBER" CONTENT="CUPS-SPM-1.1">
+ <META NAME="Author" CONTENT="Easy Software Products">
+ <TITLE>DRAFT - CUPS Software Programmers Manual</TITLE>
+</HEAD>
+<BODY>
+
+<H1 ALIGN="RIGHT">Preface</H1>
+
+This software programmers manual provides software programming
+information for the Common UNIX Printing System ("CUPS") Version 1.1.
+
+<EMBED SRC="system-overview.shtml">
+
+<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 - The CUPS API</LI>
+ <LI>3 - Writing Filters</LI>
+ <LI>4 - Writing Printer Drivers</LI>
+ <LI>5 - Writing Backends</LI>
+ <LI>A - Constants</LI>
+ <LI>B - Structures</LI>
+ <LI>C - Functions</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 - The CUPS API</H1>
+
+<P>This chapter describes the CUPS Application Programmers Interface ("API").
+
+<H2>The CUPS Library</H2>
+
+<H3>Detecting the CUPS Library in Autoconf</H3>
+
+
+<H2>Basic Services</H2>
+
+<H3>Include Files</H3>
+
+<H3>Getting the Available Printers and Classes</H3>
+
+<H3>Printing Files</H3>
+
+<H3>Setting Printer Options</H3>
+
+<H3>Cancelling Jobs</H3>
+
+
+<H2>HTTP Services</H2>
+
+<H3>Include Files</H3>
+
+<H3>Connecting to a Server</H3>
+
+<H3>Setting Request Fields</H3>
+
+<H3>Issuing a Request</H3>
+
+<H3>Getting the Request Status</H3>
+
+<H3>Sending Request Data</H3>
+
+<H3>Reading Request Data</H3>
+
+
+<H2>IPP Services</H2>
+
+<H3>Include Files</H3>
+
+<H3>Creating an IPP Request</H3>
+
+<H3>Adding Attributes</H3>
+
+<H3>Sending an IPP Request</H3>
+
+<H3>Reading an IPP Response</H3>
+
+<H3>Finding Attributes</H3>
+
+<H3>Looping Through Attributes</H3>
+
+<H3>IPP Standard Operations</H3>
+
+<H3>IPP Extension Operations</H3>
+
+<H3>CUPS Extension Operations</H3>
+
+
+<H2>Language Services</H2>
+
+<H3>Include Files</H3>
+
+<H3>Getting the Default Language</H3>
+
+<H3>Getting the Language Encoding</H3>
+
+<H3>Getting a Language String</H3>
+
+
+<H2>PPD Services</H2>
+
+<H3>Include Files</H3>
+
+<H3>Loading a PPD File</H3>
+
+<H3>Options and Groups</H3>
+
+<H3>Finding an Option</H3>
+
+<H3>Finding a Page Size</H3>
+
+<H3>Marking Options</H3>
+
+<H3>Checking for Conflicts</H3>
+
+<H3>Sending Options</H3>
+
+
+<H1 ALIGN="RIGHT">3 - Writing Filters</H1>
+
+<P>This chapter describes how to write a file filter for CUPS.
+
+<H2>Overview</H2>
+
+<H3>Security Considerations</H3>
+
+Users and Groups
+
+<H3>Temporary Files</H3>
+
+<H3>Page Accounting</H3>
+
+
+<H2>Command-Line Arguments</H2>
+
+<H3>Copy Generation</H3>
+
+
+<H2>Environment Variables</H2>
+
+
+<H2>Writing a HTML Filter</H2>
+
+
+<H1 ALIGN="RIGHT">4 - Writing Printer Drivers</H1>
+
+<P>This chapter discusses how to write a printer driver, which is a
+special filter program that converts CUPS raster data into the
+appropriate commands and data required for a printer.
+
+<H2>Overview</H2>
+
+<H3>Page Accounting</H3>
+
+<H3>Color Management</H3>
+
+
+<H2>Raster Functions</H2>
+
+<H3>cupsRasterOpen()</H3>
+
+<H3>cupsRasterReadHeader()</H3>
+
+<H3>cupsRasterReadPixels()</H3>
+
+<H3>cupsRasterClose()</H3>
+
+
+<H2>Writing a HP-PCL Driver</H2>
+
+
+<H1 ALIGN="RIGHT">5 - Writing Backends</H1>
+
+<P>This chapter describes how to write a backend for CUPS. Backends
+communicate directly with printers and allow printer drivers and
+filters to send data using any type of connection transparently.
+
+<H2>Overview</H2>
+
+<H3>Security Considerations</H3>
+
+Users and Groups
+
+<H3>Temporary Files</H3>
+
+<H3>Page Accounting</H3>
+
+<H3>Retries</H3>
+
+
+<H2>Command-Line Arguments</H2>
+
+<H3>Copy Generation</H3>
+
+
+<H2>Environment Variables</H2>
+
+
+<H2>Writing a Serial Port Backend</H2>
+
+
+<H1 ALIGN="RIGHT">A - Constants</H1>
+
+<P>This appendix lists all of the constants that are defined by the CUPS
+API.
+
+<H2>CUPS Constants</H2>
+
+<H2>HTTP Constants</H2>
+
+<H2>IPP Constants</H2>
+
+<H2>Language Constants</H2>
+
+<H2>PPD Constants</H2>
+
+<H2>Raster Constants</H2>
+
+
+<H1 ALIGN="RIGHT">B - Structures</H1>
+
+<P>This appendix describes all of the structures that are defined by the CUPS
+API.
+
+<H2></H2>
+
+
+<H1 ALIGN="RIGHT">C - Functions</H1>
+
+<P>This appendix provides a reference for all of the CUPS API functions.
+
+<!-- NEW PAGE --><H2><A NAME="cupsAddOption">cupsAddOption()</A></H2>
+
+<H3>Usage</H3>
+
+<PRE>
+int
+cupsAddOption(const char *name,
+ const char *value,
+ int num_options,
+ cups_option_t **options);
+</PRE>
+
+<H3>Arguments</H3>
+
+<CENTER><TABLE WIDTH="80%" BORDER>
+<TR>
+ <TH>Argument</TH>
+ <TH>Description</TH>
+</TR>
+<TR>
+ <TD>name</TD>
+ <TD>The name of the option.</TD>
+</TR>
+<TR>
+ <TD>value</TD>
+ <TD>The value of the option.</TD>
+</TR>
+<TR>
+ <TD>num_options</TD>
+ <TD>Number of options currently in the array.</TD>
+</TR>
+<TR>
+ <TD>options</TD>
+ <TD>Pointer to the options array.</TD>
+</TR>
+</TABLE></CENTER>
+
+<H3>Returns</H3>
+
+<P>The new number of options.
+
+<H3>Description</H3>
+
+<P><CODE>cupsAddOption()</CODE> adds an option to the specified array.
+
+<H3>Example</H3>
+
+<PRE>
+#include &lt;cups.h&gt;
+
+...
+
+/* Declare the options array */
+int num_options;
+<A HREF="#cups_option_t">cups_option_t</A> *options;
+
+/* Initialize the options array */
+num_options = 0;
+options = (cups_option_t *)0;
+
+/* Add options using cupsAddOption() */
+num_options = cupsAddOption("media", "letter", num_options, &amp;options);
+num_options = cupsAddOption("resolution", "300dpi", num_options, &amp;options);
+</PRE>
+
+<H3>See Also</H3>
+
+<A HREF="#cupsFreeOptions"><CODE>cupsFreeOptions()</CODE></A>,
+<A HREF="#cupsGetOption"><CODE>cupsGetOption()</CODE></A>,
+<A HREF="#cupsParseOptions"><CODE>cupsParseOptions()</CODE></A>
+
+<!-- NEW PAGE --><H2><A NAME="cupsCancelJob">cupsCancelJob()</A></H2>
+
+<H3>Usage</H3>
+
+<PRE>
+int
+cupsCancelJob(const char *dest,
+ int job);
+</PRE>
+
+<H3>Arguments</H3>
+
+<CENTER><TABLE WIDTH="80%" BORDER>
+<TR>
+ <TH>Argument</TH>
+ <TH>Description</TH>
+</TR>
+<TR>
+ <TD>dest</TD>
+ <TD>Printer or class name</TD>
+</TR>
+<TR>
+ <TD>job</TD>
+ <TD>Job ID</TD>
+</TR>
+</TABLE></CENTER>
+
+<H3>Returns</H3>
+
+<P>1 on success, 0 on failure. On failure the error can be found by calling
+<A HREF="#cupsLastError"><CODE>cupsLastError()</CODE></A>.
+
+<H3>Description</H3>
+
+<P><CODE>cupsCancelJob()</CODE> cancels the specifies job.
+
+<H3>Example</H3>
+
+<PRE>
+#include &lt;cups.h&gt;
+
+cupsCancelJob("LaserJet", 1);
+</PRE>
+
+<H3>See Also</H3>
+
+<P>
+<A HREF="#cupsLastError"><CODE>cupsLastError()</CODE></A>,
+<A HREF="#cupsPrintFile"><CODE>cupsPrintFile()</CODE></A>
+
+<!-- NEW PAGE --><H2><A NAME="cupsDoFileRequest">cupsDoFileRequest()</A></H2>
+
+<H3>Usage</H3>
+
+<PRE>
+ipp_t *
+cupsDoFileRequest(http_t *http,
+ ipp_t *request,
+ const char *resource,
+ const char *filename);
+</PRE>
+
+<H3>Arguments</H3>
+
+<CENTER><TABLE WIDTH="80%" BORDER>
+<TR>
+ <TH>Argument</TH>
+ <TH>Description</TH>
+</TR>
+<TR>
+ <TD>http</TD>
+ <TD>HTTP connection to server.</TD>
+</TR>
+<TR>
+ <TD>request</TD>
+ <TD>IPP request data.</TD>
+</TR>
+<TR>
+ <TD>resource</TD>
+ <TD>HTTP resource name for POST.</TD>
+</TR>
+<TR>
+ <TD>filename</TD>
+ <TD>File to send with POST request (<CODE>NULL</CODE> pointer if none.)</TD>
+</TR>
+</TABLE></CENTER>
+
+<H3>Returns</H3>
+
+<P>IPP response data or <CODE>NULL</CODE> if the request fails. On failure
+the error can be found by calling
+<A HREF="#cupsLastError"><CODE>cupsLastError()</CODE></A>.
+
+<H3>Description</H3>
+
+<P><CODE>cupsDoFileRequest()</CODE> does a HTTP POST request and provides the
+IPP request and optionally the contents of a file to the IPP server. It also
+handles resubmitting the request and performing password authentication as
+needed.
+
+<H3>Example</H3>
+
+<PRE>
+#include &lt;cups.h&gt;
+
+<A HREF="#http_t">http_t</A> *http;
+<A HREF="#cups_lang_t">cups_lang_t</A> *language;
+<A HREF="#ipp_t">ipp_t</A> *request;
+ipp_t *response;
+
+...
+
+/* Get the default language */
+language = <A HREF="#cupsLangDefault">cupsLangDefault()</A>;
+
+/* Create a new IPP request */
+request = <A HREF="#ippNew">ippNew()</A>;
+
+request-&gt;request.op.operation_id = IPP_PRINT_FILE;
+request-&gt;request.op.request_id = 1;
+
+/* Add required attributes */
+<A HREF="#ippAddString">ippAddString</A>(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
+ "attributes-charset", NULL, <A HREF="#cupsLangEncoding">cupsLangEncoding</A>(language));
+
+ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
+ "attributes-natural-language", NULL,
+ language != NULL ? language-&gt;language : "C");
+
+ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
+ NULL, "ipp://hostname/resource");
+
+ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
+ NULL, <A HREF="#cupsUser">cupsUser()</A>);
+
+/* Do the request... */
+response = cupsDoFileRequest(http, request, "/resource", "filename.txt");
+</PRE>
+
+<H3>See Also</H3>
+
+<P>
+<A HREF="#cupsLangDefault"><CODE>cupsLangDefault()</CODE></A>,
+<A HREF="#cupsLangEncoding"><CODE>cupsLangEncoding()</CODE></A>,
+<A HREF="#cupsUser"><CODE>cupsUser()</CODE></A>,
+<A HREF="#httpConnect"><CODE>httpConnect()</CODE></A>,
+<A HREF="#ippAddString"><CODE>ippAddString()</CODE></A>,
+<A HREF="#ippNew"><CODE>ippNew()</CODE></A>
+
+<!-- NEW PAGE --><H2><A NAME="cupsDoRequest">cupsDoRequest()</A></H2>
+
+<H3>Usage</H3>
+
+<PRE>
+ipp_t *
+cupsDoRequest(http_t *http,
+ ipp_t *request,
+ const char *resource);
+</PRE>
+
+<H3>Arguments</H3>
+
+<CENTER><TABLE WIDTH="80%" BORDER>
+<TR>
+ <TH>Argument</TH>
+ <TH>Description</TH>
+</TR>
+<TR>
+ <TD>http</TD>
+ <TD>HTTP connection to server.</TD>
+</TR>
+<TR>
+ <TD>request</TD>
+ <TD>IPP request data.</TD>
+</TR>
+<TR>
+ <TD>resource</TD>
+ <TD>HTTP resource name for POST.</TD>
+</TR>
+</TABLE></CENTER>
+
+<H3>Returns</H3>
+
+<P>IPP response data or <CODE>NULL</CODE> if the request fails. On failure
+the error can be found by calling
+<A HREF="#cupsLastError"><CODE>cupsLastError()</CODE></A>.
+
+<H3>Description</H3>
+
+<P><CODE>cupsDoRequest()</CODE> does a HTTP POST request and provides
+the IPP request to the IPP server. It also handles resubmitting the
+request and performing password authentication as needed.
+
+<H3>Example</H3>
+
+<PRE>
+#include &lt;cups.h&gt;
+
+<A HREF="#http_t">http_t</A> *http;
+<A HREF="#cups_lang_t">cups_lang_t</A> *language;
+<A HREF="#ipp_t">ipp_t</A> *request;
+ipp_t *response;
+
+...
+
+/* Get the default language */
+language = <A HREF="#cupsLangDefault">cupsLangDefault()</A>;
+
+/* Create a new IPP request */
+request = <A HREF="#ippNew">ippNew()</A>;
+
+request-&gt;request.op.operation_id = IPP_GET_PRINTER_ATTRIBUTES;
+request-&gt;request.op.request_id = 1;
+
+/* Add required attributes */
+<A HREF="#ippAddString">ippAddString</A>(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
+ "attributes-charset", NULL, <A HREF="#cupsLangEncoding">cupsLangEncoding</A>(language));
+
+ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
+ "attributes-natural-language", NULL,
+ language != NULL ? language-&gt;language : "C");
+
+ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
+ NULL, "ipp://hostname/resource");
+
+/* Do the request... */
+response = cupsDoRequest(http, request, "/resource");
+</PRE>
+
+<H3>See Also</H3>
+
+<P>
+<A HREF="#cupsLangDefault"><CODE>cupsLangDefault()</CODE></A>,
+<A HREF="#cupsLangEncoding"><CODE>cupsLangEncoding()</CODE></A>,
+<A HREF="#cupsUser"><CODE>cupsUser()</CODE></A>,
+<A HREF="#httpConnect"><CODE>httpConnect()</CODE></A>,
+<A HREF="#ippAddString"><CODE>ippAddString()</CODE></A>,
+<A HREF="#ippNew"><CODE>ippNew()</CODE></A>
+
+<!-- NEW PAGE --><H2><A NAME="cupsFreeOptions">cupsFreeOptions()</A></H2>
+
+<H3>Usage</H3>
+
+<PRE>
+void
+cupsFreeOptions(int num_options,
+ cups_option_t *options);
+
+<H3>Arguments</H3>
+
+<CENTER><TABLE WIDTH="80%" BORDER>
+<TR>
+ <TH>Argument</TH>
+ <TH>Description</TH>
+</TR>
+<TR>
+ <TD>num_options</TD>
+ <TD>Number of options in array.</TD>
+</TR>
+<TR>
+ <TD>options</TD>
+ <TD>Pointer to options array.</TD>
+</TR>
+</TABLE></CENTER>
+
+<H3>Description</H3>
+
+<P><CODE>cupsFreeOptions()</CODE> frees all memory associated with the
+option array specified.
+
+<H3>Example</H3>
+
+<PRE>
+#include &lt;cups/cups.h&gt;
+
+int num_options;
+cups_option_t *options;
+
+...
+
+cupsFreeOptions(num_options, options);
+</PRE>
+
+<H3>See Also</H3>
+
+<P>
+<A HREF="#cupsAddOption">cupsAddOption()</A>,
+<A HREF="#cupsGetOption">cupsGetOption()</A>,
+<A HREF="#cupsMarkOptions">cupsMarkOptions()</A>,
+<A HREF="#cupsParseOptions">cupsParseOptions()</A>
+
+<!-- NEW PAGE --><H2><A NAME="cupsGetClasses">cupsGetClasses()</A></H2>
+
+<H3>Usage</H3>
+
+<PRE>
+int
+cupsGetClasses(char ***classes);
+</PRE>
+
+<H3>Arguments</H3>
+
+<CENTER><TABLE WIDTH="80%" BORDER>
+<TR>
+ <TH>Argument</TH>
+ <TH>Description</TH>
+</TR>
+<TR>
+ <TD>classes</TD>
+ <TD>Pointer to character pointer array.</TD>
+</TR>
+</TABLE></CENTER>
+
+<H3>Returns</H3>
+
+<P>The number of printer classes available.
+
+<H3>Description</H3>
+
+<P><CODE>cupsGetClasses()</CODE> gets a list of the available printer classes.
+The returned array should be freed using the <CODE>free()</CODE> when it is
+no longer needed.
+
+<H3>Example</H3>
+
+<PRE>
+#include &lt;cups/cups.h&gt;
+
+int i;
+int num_classes;
+char **classes;
+
+...
+
+num_classes = cupsGetClasses(&classes);
+
+...
+
+if (num_classes > 0)
+{
+ for (i = 0; i < num_classes; i ++)
+ free(classes[i]);
+
+ free(classes);
+}
+</PRE>
+
+<H3>See Also</H3>
+
+<P>
+<A HREF="#cupsGetDefault">cupsGetDefault()</CODE>,
+<A HREF="#cupsGetPrinters">cupsGetPrinters()</CODE>
+
+<!-- NEW PAGE --><H2><A NAME="cupsGetDefault">cupsGetDefault()</A></H2>
+
+<H3>Usage</H3>
+
+<PRE>
+const char *
+cupsGetDefault(void);
+</PRE>
+
+<H3>Returns</H3>
+
+<P>A pointer to the default destination.
+
+<H3>Description</H3>
+
+<P><CODE>cupsGetDefault()</CODE> gets the default destination printer or class.
+The default destination is stored in a static string and will be overwritten
+(usually with the same value) after each call.
+
+<H3>Example</H3>
+
+<PRE>
+#include &lt;cups/cups.h&gt;
+
+printf("The default destination is %s\n", cupsGetDefault());
+</PRE>
+
+<H3>See Also</H3>
+
+<P>
+<A HREF="#cupsGetClasses">cupsGetClasses()</CODE>,
+<A HREF="#cupsGetPrinters">cupsGetPrinters()</CODE>
+
+<!-- NEW PAGE --><H2><A NAME="cupsGetOption">cupsGetOption()</A></H2>
+
+<H3>Usage</H3>
+
+<PRE>
+const char *
+cupsGetOption(const char *name,
+ int num_options,
+ cups_option_t *options);
+</PRE>
+
+<H3>Arguments</H3>
+
+<CENTER><TABLE WIDTH="80%" BORDER>
+<TR>
+ <TH>Argument</TH>
+ <TH>Description</TH>
+</TR>
+<TR>
+ <TD>name</TD>
+ <TD>The name of the option.</TD>
+</TR>
+<TR>
+ <TD>num_options</TD>
+ <TD>The number of options in the array.</TD>
+</TR>
+<TR>
+ <TD>options</TD>
+ <TD>The options array.</TD>
+</TR>
+</TABLE></CENTER>
+
+<H3>Returns</H3>
+
+<P>A pointer to the option values or <CODE>NULL</CODE> if the option is
+not defined.
+
+<H3>Description</H3>
+
+<P><CODE>cupsGetOption()</CODE> returns the first occurrence of the
+named option. If the option is not included in the options array then a
+<CODE>NULL</CODE> pointer is returned.
+
+<PRE>
+#include &lt;cups/cups.h&gt;
+
+int num_options;
+cups_option_t *options;
+const char *media;
+
+...
+
+media = cupsGetOption("media", num_options, options);
+</PRE>
+
+<H3>See Also</H3>
+
+<P>
+<A HREF="#cupsAddOption">cupsAddOption()</A>,
+<A HREF="#cupsFreeOptions">cupsFreeOptions()</A>,
+<A HREF="#cupsMarkOptions">cupsMarkOptions()</A>,
+<A HREF="#cupsParseOptions">cupsParseOptions()</A>
+
+<!-- NEW PAGE --><H2><A NAME="cupsGetPassword">cupsGetPassword()</A></H2>
+
+<H3>Usage</H3>
+
+<PRE>
+const char *
+cupsGetPassword(const char *prompt);
+</PRE>
+
+<H3>Arguments</H3>
+
+<CENTER><TABLE WIDTH="80%" BORDER>
+<TR>
+ <TH>Argument</TH>
+ <TH>Description</TH>
+</TR>
+<TR>
+ <TD>prompt</TD>
+ <TD>The prompt to display to the user.</TD>
+</TR>
+</TABLE></CENTER>
+
+<H3>Returns</H3>
+
+<P>A pointer to the password that was entered or <CODE>NULL</CODE> if no
+password was entered.
+
+<H3>Description</H3>
+
+<P><CODE>cupsGetPassword()</CODE> displays the prompt string and asks the user
+for a password. The password text is not echoed to the user.
+
+<H3>Example</H3>
+
+<PRE>
+#include &lt;cups/cups.h&gt;
+
+char *password;
+
+...
+
+password = cupsGetPassword("Please enter a password:");
+</PRE>
+
+<H3>See Also</H3>
+
+<P>
+<A HREF="#cupsServer">cupsServer()</A>,
+<A HREF="#cupsUser()">cupsUser()</A>
+
+<!-- NEW PAGE --><H2><A NAME="cupsGetPPD">cupsGetPPD()</A></H2>
+
+<H3>Usage</H3>
+
+<PRE>
+const char *
+cupsGetPPD(const char *printer);
+</PRE>
+
+<H3>Arguments</H3>
+
+<CENTER><TABLE WIDTH="80%" BORDER>
+<TR>
+ <TH>Argument</TH>
+ <TH>Description</TH>
+</TR>
+<TR>
+ <TD>printer</TD>
+ <TD>The name of the printer.</TD>
+</TR>
+</TABLE></CENTER>
+
+<H3>Returns</H3>
+
+<P>The name of a temporary file containing the PPD file or <CODE>NULL</CODE>
+if the printer cannot be located or does not have a PPD file.
+
+<H3>Description</H3>
+
+<P><CODE>cupsGetPPD()</CODE> gets a copy of the PPD file for the named printer.
+The printer name can be of the form "printer" or "printer@hostname".
+
+<P>You should remove (unlink) the PPD file after you are done using it. The
+filename is stored in a static buffer and will be overwritten with each call
+to <CODE>cupsGetPPD()</CODE>.
+
+<H3>Example</H3>
+
+<PRE>
+#include &lt;cups/cups.h&gt;
+
+char *ppd;
+
+...
+
+ppd = cupsGetPPD("printer@hostname");
+
+...
+
+unlink(ppd);
+</PRE>
+
+<!-- NEW PAGE --><H2><A NAME="cupsGetPrinters">cupsGetPrinters()</A></H2>
+
+<H3>Usage</H3>
+
+<PRE>
+int
+cupsGetPrinters(char ***printers);
+</PRE>
+
+<H3>Arguments</H3>
+
+<CENTER><TABLE WIDTH="80%" BORDER>
+<TR>
+ <TH>Argument</TH>
+ <TH>Description</TH>
+</TR>
+<TR>
+ <TD>printers</TD>
+ <TD>Pointer to character pointer array.</TD>
+</TR>
+</TABLE></CENTER>
+
+<H3>Returns</H3>
+
+<P>The number of printer printers available.
+
+<H3>Description</H3>
+
+<P><CODE>cupsGetPrinters()</CODE> gets a list of the available printers.
+The returned array should be freed using the <CODE>free()</CODE> when it is
+no longer needed.
+
+<H3>Example</H3>
+
+<PRE>
+#include &lt;cups/cups.h&gt;
+
+int i;
+int num_printers;
+char **printers;
+
+...
+
+num_printers = cupsGetPrinters(&printers);
+
+...
+
+if (num_printers > 0)
+{
+ for (i = 0; i < num_printers; i ++)
+ free(printers[i]);
+
+ free(printers);
+}
+</PRE>
+
+<H3>See Also</H3>
+
+<P>
+<A HREF="#cupsGetClasses">cupsGetClasses()</CODE>,
+<A HREF="#cupsGetDefault">cupsGetDefault()</CODE>
+
+<!-- NEW PAGE --><H2><A NAME="cupsLangDefault">cupsLangDefault()</A></H2>
+
+<H3>Usage</H3>
+
+<PRE>
+const char *
+cupsLangDefault(void);
+</PRE>
+
+<H3>Returns</H3>
+
+<P>A pointer to the default language structure.
+
+<H3>Description</H3>
+
+<P><CODE>cupsLangDefault()</CODE> returns a language structure for the default
+language. The default language is defined by the <CODE>LANG</CODE> environment
+variable. If the specified language cannot be located then the POSIX (English)
+locale is used.
+
+<P>Call <CODE>cupsLangFree()</CODE> to free any memory associated with the
+language structure when you are done.
+
+<H3>Example</H3>
+
+<PRE>
+#include &lt;cups/language.h&gt;
+
+cups_lang_t *language;
+...
+
+language = cupsLangDefault();
+
+...
+
+cupsLangFree(language);
+</PRE>
+
+<H3>See Also</H3>
+
+<P>
+<A HREF="#cupsLangEncoding">cupsLangEncoding()</A>,
+<A HREF="#cupsLangFlush">cupsLangFlush()</A>,
+<A HREF="#cupsLangFree">cupsLangFree()</A>,
+<A HREF="#cupsLangGet">cupsLangGet()</A>,
+<A HREF="#cupsLangString">cupsLangString()</A>
+
+<!-- NEW PAGE --><H2><A NAME="cupsLangEncoding">cupsLangEncoding()</A></H2>
+
+<H3>Usage</H3>
+
+<PRE>
+char *
+cupsLangEncoding(cups_lang_t *language);
+</PRE>
+
+<H3>Arguments</H3>
+
+<CENTER><TABLE WIDTH="80%" BORDER>
+<TR>
+ <TH>Argument</TH>
+ <TH>Description</TH>
+</TR>
+<TR>
+ <TD>language</TD>
+ <TD>The language structure.</TD>
+</TR>
+</TABLE></CENTER>
+
+<H3>Returns</H3>
+
+<P>A pointer to the encoding string.
+
+<H3>Description</H3>
+
+<P><CODE>cupsLangEncoding()</CODE> returns the language encoding used for the
+specified language, e.g. "iso-8859-1", "utf-8", etc.
+
+<H3>Example</H3>
+
+<PRE>
+#include &lt;cups/language.h&gt;
+
+cups_lang_t *language;
+char *encoding;
+...
+
+language = cupsLangDefault();
+encoding = cupsLangEncoding(language);
+...
+
+cupsLangFree(language);
+</PRE>
+
+<H3>See Also</H3>
+
+<P>
+<A HREF="#cupsLangDefault">cupsLangDefault()</A>,
+<A HREF="#cupsLangFlush">cupsLangFlush()</A>,
+<A HREF="#cupsLangFree">cupsLangFree()</A>,
+<A HREF="#cupsLangGet">cupsLangGet()</A>,
+<A HREF="#cupsLangString">cupsLangString()</A>
+
+<!-- NEW PAGE --><H2><A NAME="cupsLangFlush">cupsLangFlush()</A></H2>
+
+<H3>Usage</H3>
+
+<PRE>
+void
+cupsLangFlush(void);
+</PRE>
+
+<H3>Description</H3>
+
+<P><CODE>cupsLangFlush()</CODE> frees all language structures that have been
+allocated.
+
+<H3>Example</H3>
+
+<PRE>
+#include &lt;cups/language.h&gt;
+
+...
+
+cupsLangFlush();
+</PRE>
+
+<H3>See Also</H3>
+
+<P>
+<A HREF="#cupsLangDefault">cupsLangDefault()</A>,
+<A HREF="#cupsLangEncoding">cupsLangEncoding()</A>,
+<A HREF="#cupsLangFree">cupsLangFree()</A>,
+<A HREF="#cupsLangGet">cupsLangGet()</A>,
+<A HREF="#cupsLangString">cupsLangString()</A>
+
+<!-- NEW PAGE --><H2><A NAME="cupsLangFree">cupsLangFree()</A></H2>
+
+<H3>Usage</H3>
+
+<PRE>
+void
+cupsLangFree(cups_lang_t *language);
+</PRE>
+
+<H3>Arguments</H3>
+
+<CENTER><TABLE WIDTH="80%" BORDER>
+<TR>
+ <TH>Argument</TH>
+ <TH>Description</TH>
+</TR>
+<TR>
+ <TD>language</TD>
+ <TD>The language structure to free.</TD>
+</TR>
+</TABLE></CENTER>
+
+<H3>Description</H3>
+
+<P><CODE>cupsLangFree()</CODE> frees the specified language structure.
+
+<H3>Example</H3>
+
+<PRE>
+#include &lt;cups/language.h&gt;
+
+cups_lang_t *language;
+...
+
+cupsLangFree(language);
+</PRE>
+
+<H3>See Also</H3>
+
+<P>
+<A HREF="#cupsLangDefault">cupsLangDefault()</A>,
+<A HREF="#cupsLangEncoding">cupsLangEncoding()</A>,
+<A HREF="#cupsLangFlush">cupsLangFlush()</A>,
+<A HREF="#cupsLangGet">cupsLangGet()</A>,
+<A HREF="#cupsLangString">cupsLangString()</A>
+
+<!-- NEW PAGE --><H2><A NAME="cupsLangGet">cupsLangGet()</A></H2>
+
+<H3>Usage</H3>
+
+<PRE>
+cups_lang_t *
+cupsLangGet(const char *name);
+</PRE>
+
+<H3>Arguments</H3>
+
+<CENTER><TABLE WIDTH="80%" BORDER>
+<TR>
+ <TH>Argument</TH>
+ <TH>Description</TH>
+</TR>
+<TR>
+ <TD>name</TD>
+ <TD>The name of the locale.</TD>
+</TR>
+</TABLE></CENTER>
+
+<H3>Returns</H3>
+
+<P>A pointer to a language structure.
+
+<H3>Description</H3>
+
+<P><CODE>cupsLangGet()</CODE> returns a language structure for the specified
+locale. If the locale is not defined then the POSIX (English) locale is
+substituted.
+
+<H3>Example</H3>
+
+<PRE>
+#include &lt;cups/language.h&gt;
+
+cups_lang_t *language;
+
+...
+
+language = cupsLangGet("fr");
+
+...
+
+cupsLangFree(language);
+</PRE>
+
+<H3>See Also</H3>
+
+<P>
+<A HREF="#cupsLangDefault">cupsLangDefault()</A>,
+<A HREF="#cupsLangEncoding">cupsLangEncoding()</A>,
+<A HREF="#cupsLangFlush">cupsLangFlush()</A>,
+<A HREF="#cupsLangFree">cupsLangFree()</A>,
+<A HREF="#cupsLangString">cupsLangString()</A>
+
+<!-- NEW PAGE --><H2><A NAME="cupsLangString">cupsLangString()</A></H2>
+
+<H3>Usage</H3>
+
+<PRE>
+char *
+cupsLangString(cups_lang_t *language,
+ int message);
+</PRE>
+
+<H3>Arguments</H3>
+
+<CENTER><TABLE WIDTH="80%" BORDER>
+<TR>
+ <TH>Argument</TH>
+ <TH>Description</TH>
+</TR>
+<TR>
+ <TD>language</TD>
+ <TD>The language to query.</TD>
+</TR>
+<TR>
+ <TD>message</TD>
+ <TD>The message number.</TD>
+</TR>
+</TABLE></CENTER>
+
+<H3>Returns</H3>
+
+<P>A pointer to the message string or <CODE>NULL</CODE> if the message is
+not defined.
+
+<H3>Description</H3>
+
+<P><CODE>cupsLangString()</CODE> returns a pointer to the specified message
+string in the specified language.
+
+<H3>Example</H3>
+
+<PRE>
+#include &lt;cups/language.h&gt;
+
+cups_lang_t *language;
+char *s;
+...
+
+language = cupsLangGet("fr");
+
+s = cupsLangString(language, CUPS_MSG_YES);
+
+...
+
+cupsLangFree(language);
+</PRE>
+
+<H3>See Also</H3>
+
+<P>
+<A HREF="#cupsLangDefault">cupsLangDefault()</A>,
+<A HREF="#cupsLangEncoding">cupsLangEncoding()</A>,
+<A HREF="#cupsLangFlush">cupsLangFlush()</A>,
+<A HREF="#cupsLangFree">cupsLangFree()</A>,
+<A HREF="#cupsLangGet">cupsLangGet()</A>
+
+<!-- NEW PAGE --><H2><A NAME="cupsLastError">cupsLastError()</A></H2>
+
+<H3>Usage</H3>
+
+<PRE>
+ipp_status_t
+cupsLastError(void);
+</PRE>
+
+<H3>Returns</H3>
+
+<P>An enumeration containing the last IPP error.
+
+<H3>Description</H3>
+
+<P><CODE>cupsLastError()</CODE> returns the last IPP error that occurred.
+If no error occurred then it will return <CODE>IPP_OK</CODE> or
+<CODE>IPP_OK_CONFLICT</CODE>.
+
+<H3>Example</H3>
+
+<PRE>
+#include &lt;cups/cups.h&gt;
+
+ipp_status_t status;
+
+...
+
+status = cupsLastError();
+</PRE>
+
+<H3>See Also</H3>
+
+<P>
+<A HREF="#cupsCancelJob">cupsCancelJob()</A>,
+<A HREF="#cupsPrintFile">cupsPrintFile()</A>
+
+<!-- NEW PAGE --><H2><A NAME="cupsMarkOptions">cupsMarkOptions()</A></H2>
+
+<H3>Usage</H3>
+
+<PRE>
+int
+cupsMarkOptions(ppd_file_t *ppd,
+ int num_options,
+ cups_option_t *options);
+</PRE>
+
+<H3>Arguments</H3>
+
+<CENTER><TABLE WIDTH="80%" BORDER>
+<TR>
+ <TH>Argument</TH>
+ <TH>Description</TH>
+</TR>
+<TR>
+ <TD>ppd</TD>
+ <TD>The PPD file to mark.</TD>
+</TR>
+<TR>
+ <TD>num_options</TD>
+ <TD>The number of options in the options array.</TD>
+</TR>
+<TR>
+ <TD>options</TD>
+ <TD>A pointer to the options array.</TD>
+</TR>
+</TABLE></CENTER>
+
+<H3>Returns</H3>
+
+<P>The number of conflicts found.
+
+<H3>Description</H3>
+
+<P><CODE>cupsMarkOptions()</CODE> marks options in the PPD file. It also
+handles mapping of IPP option names and values to PPD option names.
+
+<H3>Example</H3>
+
+<PRE>
+#include &lt;cups/cups.h&gt;
+
+int num_options;
+cups_option_t *options;
+ppd_file_t *ppd;
+
+...
+
+cupsMarkOptions(ppd, num_options, options);
+</PRE>
+
+<H3>See Also</H3>
+
+<P>
+<A HREF="#cupsAddOption">cupsAddOption()</A>,
+<A HREF="#cupsFreeOptions">cupsFreeOptions()</A>,
+<A HREF="#cupsGetOption">cupsGetOption()</A>,
+<A HREF="#cupsParseOptions">cupsParseOptions()</A>
+
+<!-- NEW PAGE --><H2><A NAME="cupsParseOptions">cupsParseOptions()</A></H2>
+
+<H3>Usage</H3>
+
+<PRE>
+int
+cupsParseOptions(const char *arg,
+ int num_options,
+ cups_option_t **options);
+</PRE>
+
+<H3>Arguments</H3>
+
+<CENTER><TABLE WIDTH="80%" BORDER>
+<TR>
+ <TH>Argument</TH>
+ <TH>Description</TH>
+</TR>
+<TR>
+ <TD>arg</TD>
+ <TD>The string containing one or more options.</TD>
+</TR>
+<TR>
+ <TD>num_options</TD>
+ <TD>The number of options in the options array.</TD>
+</TR>
+<TR>
+ <TD>options</TD>
+ <TD>A pointer to the options array pointer.</TD>
+</TR>
+</TABLE></CENTER>
+
+<H3>Returns</H3>
+
+<P>The new number of options in the array.
+
+<H3>Description</H3>
+
+<P><CODE>cupsParseOptions()</CODE> parses the specifies string for one
+or more options of the form "name=value", "name", or "noname". It can
+be called multiple times to combine the options from several strings.
+
+<H3>Example</H3>
+
+<PRE>
+#include &lt;cups/cups.h&gt;
+
+int num_options;
+cups_option_t *options;
+
+...
+
+num_options = 0;
+options = (cups_option_t *)0;
+num_options = cupsParseOptions(argv[5], num_options, &amp;options);
+</PRE>
+
+<H3>See Also</H3>
+
+<P>
+<A HREF="#cupsAddOption">cupsAddOption()</A>,
+<A HREF="#cupsFreeOptions">cupsFreeOptions()</A>,
+<A HREF="#cupsGetOption">cupsGetOption()</A>,
+<A HREF="#cupsMarkOptions">cupsMarkOptions()</A>
+
+<!-- NEW PAGE --><H2><A NAME="cupsPrintFile">cupsPrintFile()</A></H2>
+
+<H3>Usage</H3>
+
+<PRE>
+int
+cupsPrintFile(const char *printer,
+ const char *filename,
+ const char *title,
+ int num_options,
+ cups_option_t *options);
+</PRE>
+
+<H3>Arguments</H3>
+
+<CENTER><TABLE WIDTH="80%" BORDER>
+<TR>
+ <TH>Argument</TH>
+ <TH>Description</TH>
+</TR>
+<TR>
+ <TD>printer</TD>
+ <TD>The printer or class to print to.</TD>
+</TR>
+<TR>
+ <TD>filename</TD>
+ <TD>The file to print.</TD>
+</TR>
+<TR>
+ <TD>title</TD>
+ <TD>The job title.</TD>
+</TR>
+<TR>
+ <TD>num_options</TD>
+ <TD>The number of options in the options array.</TD>
+</TR>
+<TR>
+ <TD>options</TD>
+ <TD>A pointer to the options array.</TD>
+</TR>
+</TABLE></CENTER>
+
+<H3>Returns</H3>
+
+<P>The new job ID number or 0 on error.
+
+<H3>Description</H3>
+
+<P><CODE>cupsPrintFile()</CODE> sends a file to the specified printer or
+class for printing. If the job cannot be printed the error code can be
+found by calling <CODE>cupsLastError()</CODE>.
+
+<H3>Example</H3>
+
+<PRE>
+#include &lt;cups/cups.h&gt;
+
+int num_options;
+cups_option_t *options;
+
+...
+
+cupsPrintFile("printer@hostname", "filename.ps", "Job Title", num_options,
+ options);
+</PRE>
+
+<H3>See Also</H3>
+
+<P>
+<A HREF="#cupsCancelJob">cupsCancelJob()</A>,
+<A HREF="#cupsLastError">cupsLastError()</A>
+
+<!-- NEW PAGE --><H2><A NAME="cupsRasterClose">cupsRasterClose()</A></H2>
+
+<H3>Usage</H3>
+
+<PRE>
+void
+cupsRasterClose(cups_raster_t *ras);
+</PRE>
+
+<H3>Arguments</H3>
+
+<CENTER><TABLE WIDTH="80%" BORDER>
+<TR>
+ <TH>Argument</TH>
+ <TH>Description</TH>
+</TR>
+<TR>
+ <TD>ras</TD>
+ <TD>The raster stream to close.</TD>
+</TR>
+</TABLE></CENTER>
+
+<H3>Description</H3>
+
+<P><CODE>cupsRasterClose()</CODE> closes the specified raster stream.
+
+<H3>Example</H3>
+
+<PRE>
+#include &lt;cups/raster.h&gt;
+
+cups_raster_t *ras;
+
+...
+
+cupsRasterClose(ras);
+</PRE>
+
+<H3>See Also</H3>
+
+<P>
+<A HREF="#cupsRasterOpen">cupsRasterOpen()</A>,
+<A HREF="#cupsRasterReadHeader">cupsRasterReadHeader()</A>,
+<A HREF="#cupsRasterReadPixels">cupsRasterReadPixels()</A>,
+<A HREF="#cupsRasterWriteHeader">cupsRasterWriteHeader()</A>,
+<A HREF="#cupsRasterWritePixels">cupsRasterWritePixels()</A>
+
+
+<!-- NEW PAGE --><H2><A NAME="cupsRasterOpen">cupsRasterOpen()</A></H2>
+
+<H3>Usage</H3>
+
+<PRE>
+cups_raster_t *
+cupsRasterOpen(int fd,
+ cups_mode_t mode);
+</PRE>
+
+<H3>Arguments</H3>
+
+<CENTER><TABLE WIDTH="80%" BORDER>
+<TR>
+ <TH>Argument</TH>
+ <TH>Description</TH>
+</TR>
+<TR>
+ <TD>fd</TD>
+ <TD>The file descriptor to use.</TD>
+</TR>
+<TR>
+ <TD>mode</TD>
+ <TD>The mode to use; <CODE>CUPS_RASTER_READ</CODE> or
+ <CODE>CUPS_RASTER_WRITE</CODE>.</TD>
+</TR>
+</TABLE></CENTER>
+
+<H3>Returns</H3>
+
+<P>A pointer to a raster stream or <CODE>NULL</CODE> if there was an error.
+
+<H3>Description</H3>
+
+<P><CODE>cupsRasterOpen()</CODE> opens a raster stream for reading or writing.
+
+<H3>Example</H3>
+
+<PRE>
+#include &lt;cups/raster.h&gt;
+
+cups_raster_t *ras;
+
+...
+
+ras = cupsRasterOpen(0, CUPS_RASTER_READ);
+</PRE>
+
+<H3>See Also</H3>
+
+<P>
+<A HREF="#cupsRasterClose">cupsRasterClose()</A>,
+<A HREF="#cupsRasterReadHeader">cupsRasterReadHeader()</A>,
+<A HREF="#cupsRasterReadPixels">cupsRasterReadPixels()</A>,
+<A HREF="#cupsRasterWriteHeader">cupsRasterWriteHeader()</A>,
+<A HREF="#cupsRasterWritePixels">cupsRasterWritePixels()</A>
+
+<!-- NEW PAGE --><H2><A NAME="cupsRasterReadHeader">cupsRasterReadHeader()</A></H2>
+
+<H3>Usage</H3>
+
+<PRE>
+unsigned
+cupsRasterReadHeader(cups_raster_t *ras,
+ cups_page_header_t *header);
+</PRE>
+
+<H3>Arguments</H3>
+
+<CENTER><TABLE WIDTH="80%" BORDER>
+<TR>
+ <TH>Argument</TH>
+ <TH>Description</TH>
+</TR>
+<TR>
+ <TD>ras</TD>
+ <TD>The raster stream to read from.</TD>
+</TR>
+<TR>
+ <TD>header</TD>
+ <TD>A pointer to a page header structure to read into.</TD>
+</TR>
+</TABLE></CENTER>
+
+<H3>Returns</H3>
+
+<P>1 on success, 0 on EOF or error.
+
+<H3>Description</H3>
+
+<P><CODE>cupsRasterReadHeader()</CODE> reads a page header from the specified
+raster stream.
+
+<H3>Example</H3>
+
+<PRE>
+#include &lt;cups/raster.h&gt;
+
+int line;
+cups_raster_t *ras;
+cups_raster_header_t header;
+unsigned char pixels[8192];
+...
+
+while (cupsRasterReadHeader(ras, &amp;header))
+{
+ ...
+
+ for (line = 0; line &lt; header.cupsHeight; line ++)
+ {
+ cupsRasterReadPixels(ras, pixels, header.cupsBytesPerLine);
+
+ ...
+ }
+}
+</PRE>
+
+<H3>See Also</H3>
+
+<P>
+<A HREF="#cupsRasterClose">cupsRasterClose()</A>,
+<A HREF="#cupsRasterOpen">cupsRasterOpen()</A>,
+<A HREF="#cupsRasterReadPixels">cupsRasterReadPixels()</A>,
+<A HREF="#cupsRasterWriteHeader">cupsRasterWriteHeader()</A>,
+<A HREF="#cupsRasterWritePixels">cupsRasterWritePixels()</A>
+
+<!-- NEW PAGE --><H2><A NAME="cupsRasterReadPixels">cupsRasterReadPixels()</A></H2>
+
+<H3>Usage</H3>
+
+<PRE>
+unsigned
+cupsRasterReadPixels(cups_raster_t *ras,
+ unsigned char *pixels,
+ unsigned length);
+</PRE>
+
+<H3>Arguments</H3>
+
+<CENTER><TABLE WIDTH="80%" BORDER>
+<TR>
+ <TH>Argument</TH>
+ <TH>Description</TH>
+</TR>
+<TR>
+ <TD>ras</TD>
+ <TD>The raster stream to read from.</TD>
+</TR>
+<TR>
+ <TD>pixels</TD>
+ <TD>The pointer to a pixel buffer.</TD>
+</TR>
+<TR>
+ <TD>length</TD>
+ <TD>The number of bytes of pixel data to read.</TD>
+</TR>
+</TABLE></CENTER>
+
+<H3>Returns</H3>
+
+<P>The number of bytes read or 0 on EOF or error.
+
+<H3>Description</H3>
+
+<P><CODE>cupsRasterReadPixels()</CODE> reads pixel data from the specified
+raster stream.
+
+<H3>Example</H3>
+
+<PRE>
+#include &lt;cups/raster.h&gt;
+
+int line;
+cups_raster_t *ras;
+cups_raster_header_t header;
+unsigned char pixels[8192];
+...
+
+while (cupsRasterReadHeader(ras, &amp;header))
+{
+ ...
+
+ for (line = 0; line &lt; header.cupsHeight; line ++)
+ {
+ cupsRasterReadPixels(ras, pixels, header.cupsBytesPerLine);
+
+ ...
+ }
+}
+</PRE>
+
+<H3>See Also</H3>
+
+<P>
+<A HREF="#cupsRasterClose">cupsRasterClose()</A>,
+<A HREF="#cupsRasterOpen">cupsRasterOpen()</A>,
+<A HREF="#cupsRasterReadHeader">cupsRasterReadHeader()</A>,
+<A HREF="#cupsRasterWriteHeader">cupsRasterWriteHeader()</A>,
+<A HREF="#cupsRasterWritePixels">cupsRasterWritePixels()</A>
+
+<!-- NEW PAGE --><H2><A NAME="cupsRasterWriteHeader">cupsRasterWriteHeader()</A></H2>
+
+<H3>Usage</H3>
+
+<PRE>
+unsigned
+cupsRasterWriteHeader(cups_raster_t *ras,
+ cups_page_header_t *header);
+</PRE>
+
+<H3>Arguments</H3>
+
+<CENTER><TABLE WIDTH="80%" BORDER>
+<TR>
+ <TH>Argument</TH>
+ <TH>Description</TH>
+</TR>
+<TR>
+ <TD>ras</TD>
+ <TD>The raster stream to write to.</TD>
+</TR>
+<TR>
+ <TD>header</TD>
+ <TD>A pointer to the page header to write.</TD>
+</TR>
+</TABLE></CENTER>
+
+<H3>Returns</H3>
+
+<P>1 on success, 0 on error.
+
+<H3>Description</H3>
+
+<P><CODE>cupsRasterWriteHeader()</CODE> writes the specified page header to
+a raster stream.
+
+<H3>Example</H3>
+
+<PRE>
+#include &lt;cups/raster.h&gt;
+
+int line;
+cups_raster_t *ras;
+cups_raster_header_t header;
+unsigned char pixels[8192];
+...
+
+cupsRasterWriteHeader(ras, &amp;header);
+
+for (line = 0; line &lt; header.cupsHeight; line ++)
+{
+ ...
+
+ cupsRasterWritePixels(ras, pixels, header.cupsBytesPerLine);
+}
+</PRE>
+
+<H3>See Also</H3>
+
+<P>
+<A HREF="#cupsRasterClose">cupsRasterClose()</A>,
+<A HREF="#cupsRasterOpen">cupsRasterOpen()</A>,
+<A HREF="#cupsRasterReadHeader">cupsRasterReadHeader()</A>,
+<A HREF="#cupsRasterReadPixels">cupsRasterReadPixels()</A>,
+<A HREF="#cupsRasterWritePixels">cupsRasterWritePixels()</A>
+
+<!-- NEW PAGE --><H2><A NAME="cupsRasterWritePixels">cupsRasterWritePixels()</A></H2>
+
+<H3>Usage</H3>
+
+<PRE>
+unsigned
+cupsRasterWritePixels(cups_raster_t *ras,
+ unsigned char *pixels,
+ unsigned length);
+</PRE>
+
+<H3>Arguments</H3>
+
+<CENTER><TABLE WIDTH="80%" BORDER>
+<TR>
+ <TH>Argument</TH>
+ <TH>Description</TH>
+</TR>
+<TR>
+ <TD>ras</TD>
+ <TD>The raster stream to write to.</TD>
+</TR>
+<TR>
+ <TD>pixels</TD>
+ <TD>The pixel data to write.</TD>
+</TR>
+<TR>
+ <TD>length</TD>
+ <TD>The number of bytes to write.</TD>
+</TR>
+</TABLE></CENTER>
+
+<H3>Returns</H3>
+
+<P>The number of bytes written.
+
+<H3>Description</H3>
+
+<P><CODE>cupsRasterWritePixels()</CODE> writes the specified pixel data to a
+raster stream.
+
+<H3>Example</H3>
+
+<PRE>
+#include &lt;cups/raster.h&gt;
+
+int line;
+cups_raster_t *ras;
+cups_raster_header_t header;
+unsigned char pixels[8192];
+...
+
+cupsRasterWriteHeader(ras, &amp;header);
+
+for (line = 0; line &lt; header.cupsHeight; line ++)
+{
+ ...
+
+ cupsRasterWritePixels(ras, pixels, header.cupsBytesPerLine);
+}
+</PRE>
+
+<H3>See Also</H3>
+
+<P>
+<A HREF="#cupsRasterClose">cupsRasterClose()</A>,
+<A HREF="#cupsRasterOpen">cupsRasterOpen()</A>,
+<A HREF="#cupsRasterReadHeader">cupsRasterReadHeader()</A>,
+<A HREF="#cupsRasterReadPixels">cupsRasterReadPixels()</A>,
+<A HREF="#cupsRasterWriteHeader">cupsRasterWriteHeader()</A>
+
+<!-- NEW PAGE --><H2><A NAME="cupsServer">cupsServer()</A></H2>
+
+<H3>Usage</H3>
+
+<PRE>
+const char *
+cupsServer(void);
+</PRE>
+
+<H3>Returns</H3>
+
+<P>A pointer to the default server name.
+
+<H3>Description</H3>
+
+<P><CODE>cupsServer()</CODE> returns a pointer to the default server name.
+The server name is stored in a static location and will be overwritten with
+every call to <CODE>cupsServer()</CODE>
+
+<P>The default server is determined from the following locations:
+
+<OL>
+
+ <LI>The <CODE>CUPS_SERVER</CODE> environment variable,
+
+ <LI>The <CODE>ServerName</CODE> directive in the
+ <VAR>cupsd.conf</VAR> file,
+
+ <LI>The default host, "localhost".
+
+</OL>
+
+<H3>Example</H3>
+
+<PRE>
+#include &lt;cups/cups.h&gt;
+
+const char *server;
+
+server = cupsServer();
+</PRE>
+
+<H3>See Also</H3>
+
+<P>
+<A HREF="#cupsGetPassword">cupsGetPassword()</A>,
+<A HREF="#cupsUser">cupsUser()</A>
+
+<!-- NEW PAGE --><H2><A NAME="cupsTempFile">cupsTempFile()</A></H2>
+
+<H3>Usage</H3>
+
+<PRE>
+char *
+cupsTempFile(char *filename,
+ int length);
+</PRE>
+
+<H3>Arguments</H3>
+
+<CENTER><TABLE WIDTH="80%" BORDER>
+<TR>
+ <TH>Argument</TH>
+ <TH>Description</TH>
+</TR>
+<TR>
+ <TD>filename</TD>
+ <TD>The character string to hold the temporary filename.</TD>
+</TR>
+<TR>
+ <TD>length</TD>
+ <TD>The size of the filename string in bytes.</TD>
+</TR>
+</TABLE></CENTER>
+
+<H3>Returns</H3>
+
+<P>A pointer to <CODE>filename</CODE>.
+
+<H3>Description</H3>
+
+<P><CODE>cupsTempFile()</CODE> generates a temporary filename for the
+<VAR>/var/tmp</VAR> directory or the directory specified by the
+<CODE>TMPDIR</CODE> environment variable.
+
+<H3>Example</H3>
+
+<PRE>
+#include &lt;cups/cups.h&gt;
+
+char filename[256];
+
+cupsTempFile(filename, sizeof(filename));
+</PRE>
+
+<!-- NEW PAGE --><H2><A NAME="cupsUser">cupsUser()</A></H2>
+
+<H3>Usage</H3>
+
+<PRE>
+const char *
+cupsUser(void);
+</PRE>
+
+<H3>Returns</H3>
+
+<P>A pointer to the current username or <CODE>NULL</CODE> if the user ID is
+undefined.
+
+<H3>Description</H3>
+
+<P><CODE>cupsUser()</CODE> returns the name associated with the current
+user ID as reported by the <CODE>getuid()</CODE> system call.
+
+<H3>Example</H3>
+
+<PRE>
+#include &lt;cups/cups.h&gt;
+
+const char *user;
+
+user = cupsUser();
+</PRE>
+
+<H3>See Also</H3>
+
+<P>
+<A HREF="#cupsGetPassword">cupsGetPassword()</A>,
+<A HREF="#cupsServer">cupsServer()</A>
+
+<!-- NEW PAGE --><H2><A NAME="httpBlocking">httpBlocking()</A></H2>
+
+<H3>Usage</H3>
+
+<PRE>
+</PRE>
+
+<H3>Arguments</H3>
+
+<CENTER><TABLE WIDTH="80%" BORDER>
+<TR>
+ <TH>Argument</TH>
+ <TH>Description</TH>
+</TR>
+<TR>
+ <TD></TD>
+ <TD></TD>
+</TR>
+</TABLE></CENTER>
+
+<H3>Returns</H3>
+
+<H3>Description</H3>
+
+<H3>Example</H3>
+
+<PRE>
+</PRE>
+
+<H3>See Also</H3>
+
+
+<!-- NEW PAGE --><H2><A NAME="httpCheck">httpCheck()</A></H2>
+
+<H3>Usage</H3>
+
+<PRE>
+</PRE>
+
+<H3>Arguments</H3>
+
+<CENTER><TABLE WIDTH="80%" BORDER>
+<TR>
+ <TH>Argument</TH>
+ <TH>Description</TH>
+</TR>
+<TR>
+ <TD></TD>
+ <TD></TD>
+</TR>
+</TABLE></CENTER>
+
+<H3>Returns</H3>
+
+<H3>Description</H3>
+
+<H3>Example</H3>
+
+<PRE>
+</PRE>
+
+<H3>See Also</H3>
+
+
+<!-- NEW PAGE --><H2><A NAME="httpClearFields">httpClearFields()</A></H2>
+
+<H3>Usage</H3>
+
+<PRE>
+</PRE>
+
+<H3>Arguments</H3>
+
+<CENTER><TABLE WIDTH="80%" BORDER>
+<TR>
+ <TH>Argument</TH>
+ <TH>Description</TH>
+</TR>
+<TR>
+ <TD></TD>
+ <TD></TD>
+</TR>
+</TABLE></CENTER>
+
+<H3>Returns</H3>
+
+<H3>Description</H3>
+
+<H3>Example</H3>
+
+<PRE>
+</PRE>
+
+<H3>See Also</H3>
+
+
+<!-- NEW PAGE --><H2><A NAME="httpClose">httpClose()</A></H2>
+
+<H3>Usage</H3>
+
+<PRE>
+</PRE>
+
+<H3>Arguments</H3>
+
+<CENTER><TABLE WIDTH="80%" BORDER>
+<TR>
+ <TH>Argument</TH>
+ <TH>Description</TH>
+</TR>
+<TR>
+ <TD></TD>
+ <TD></TD>
+</TR>
+</TABLE></CENTER>
+
+<H3>Returns</H3>
+
+<H3>Description</H3>
+
+<H3>Example</H3>
+
+<PRE>
+</PRE>
+
+<H3>See Also</H3>
+
+
+<!-- NEW PAGE --><H2><A NAME="httpConnect">httpConnect()</A></H2>
+
+<H3>Usage</H3>
+
+<PRE>
+</PRE>
+
+<H3>Arguments</H3>
+
+<CENTER><TABLE WIDTH="80%" BORDER>
+<TR>
+ <TH>Argument</TH>
+ <TH>Description</TH>
+</TR>
+<TR>
+ <TD></TD>
+ <TD></TD>
+</TR>
+</TABLE></CENTER>
+
+<H3>Returns</H3>
+
+<H3>Description</H3>
+
+<H3>Example</H3>
+
+<PRE>
+</PRE>
+
+<H3>See Also</H3>
+
+
+<!-- NEW PAGE --><H2><A NAME="httpDecode64">httpDecode64()</A></H2>
+
+<H3>Usage</H3>
+
+<PRE>
+</PRE>
+
+<H3>Arguments</H3>
+
+<CENTER><TABLE WIDTH="80%" BORDER>
+<TR>
+ <TH>Argument</TH>
+ <TH>Description</TH>
+</TR>
+<TR>
+ <TD></TD>
+ <TD></TD>
+</TR>
+</TABLE></CENTER>
+
+<H3>Returns</H3>
+
+<H3>Description</H3>
+
+<H3>Example</H3>
+
+<PRE>
+</PRE>
+
+<H3>See Also</H3>
+
+
+<!-- NEW PAGE --><H2><A NAME="httpDelete">httpDelete()</A></H2>
+
+<H3>Usage</H3>
+
+<PRE>
+</PRE>
+
+<H3>Arguments</H3>
+
+<CENTER><TABLE WIDTH="80%" BORDER>
+<TR>
+ <TH>Argument</TH>
+ <TH>Description</TH>
+</TR>
+<TR>
+ <TD></TD>
+ <TD></TD>
+</TR>
+</TABLE></CENTER>
+
+<H3>Returns</H3>
+
+<H3>Description</H3>
+
+<H3>Example</H3>
+
+<PRE>
+</PRE>
+
+<H3>See Also</H3>
+
+
+<!-- NEW PAGE --><H2><A NAME="httpEncode64">httpEncode64()</A></H2>
+
+<H3>Usage</H3>
+
+<PRE>
+</PRE>
+
+<H3>Arguments</H3>
+
+<CENTER><TABLE WIDTH="80%" BORDER>
+<TR>
+ <TH>Argument</TH>
+ <TH>Description</TH>
+</TR>
+<TR>
+ <TD></TD>
+ <TD></TD>
+</TR>
+</TABLE></CENTER>
+
+<H3>Returns</H3>
+
+<H3>Description</H3>
+
+<H3>Example</H3>
+
+<PRE>
+</PRE>
+
+<H3>See Also</H3>
+
+
+<!-- NEW PAGE --><H2><A NAME="httpError">httpError()</A></H2>
+
+<H3>Usage</H3>
+
+<PRE>
+</PRE>
+
+<H3>Arguments</H3>
+
+<CENTER><TABLE WIDTH="80%" BORDER>
+<TR>
+ <TH>Argument</TH>
+ <TH>Description</TH>
+</TR>
+<TR>
+ <TD></TD>
+ <TD></TD>
+</TR>
+</TABLE></CENTER>
+
+<H3>Returns</H3>
+
+<H3>Description</H3>
+
+<H3>Example</H3>
+
+<PRE>
+</PRE>
+
+<H3>See Also</H3>
+
+
+<!-- NEW PAGE --><H2><A NAME="httpFlush">httpFlush()</A></H2>
+
+<H3>Usage</H3>
+
+<PRE>
+</PRE>
+
+<H3>Arguments</H3>
+
+<CENTER><TABLE WIDTH="80%" BORDER>
+<TR>
+ <TH>Argument</TH>
+ <TH>Description</TH>
+</TR>
+<TR>
+ <TD></TD>
+ <TD></TD>
+</TR>
+</TABLE></CENTER>
+
+<H3>Returns</H3>
+
+<H3>Description</H3>
+
+<H3>Example</H3>
+
+<PRE>
+</PRE>
+
+<H3>See Also</H3>
+
+
+<!-- NEW PAGE --><H2><A NAME="httpGet">httpGet()</A></H2>
+
+<H3>Usage</H3>
+
+<PRE>
+</PRE>
+
+<H3>Arguments</H3>
+
+<CENTER><TABLE WIDTH="80%" BORDER>
+<TR>
+ <TH>Argument</TH>
+ <TH>Description</TH>
+</TR>
+<TR>
+ <TD></TD>
+ <TD></TD>
+</TR>
+</TABLE></CENTER>
+
+<H3>Returns</H3>
+
+<H3>Description</H3>
+
+<H3>Example</H3>
+
+<PRE>
+</PRE>
+
+<H3>See Also</H3>
+
+
+<!-- NEW PAGE --><H2><A NAME="httpGets">httpGets()</A></H2>
+
+<H3>Usage</H3>
+
+<PRE>
+</PRE>
+
+<H3>Arguments</H3>
+
+<CENTER><TABLE WIDTH="80%" BORDER>
+<TR>
+ <TH>Argument</TH>
+ <TH>Description</TH>
+</TR>
+<TR>
+ <TD></TD>
+ <TD></TD>
+</TR>
+</TABLE></CENTER>
+
+<H3>Returns</H3>
+
+<H3>Description</H3>
+
+<H3>Example</H3>
+
+<PRE>
+</PRE>
+
+<H3>See Also</H3>
+
+
+<!-- NEW PAGE --><H2><A NAME="httpGetDateString">httpGetDateString()</A></H2>
+
+<H3>Usage</H3>
+
+<PRE>
+</PRE>
+
+<H3>Arguments</H3>
+
+<CENTER><TABLE WIDTH="80%" BORDER>
+<TR>
+ <TH>Argument</TH>
+ <TH>Description</TH>
+</TR>
+<TR>
+ <TD></TD>
+ <TD></TD>
+</TR>
+</TABLE></CENTER>
+
+<H3>Returns</H3>
+
+<H3>Description</H3>
+
+<H3>Example</H3>
+
+<PRE>
+</PRE>
+
+<H3>See Also</H3>
+
+
+<!-- NEW PAGE --><H2><A NAME="httpGetDateTime">httpGetDateTime()</A></H2>
+
+<H3>Usage</H3>
+
+<PRE>
+</PRE>
+
+<H3>Arguments</H3>
+
+<CENTER><TABLE WIDTH="80%" BORDER>
+<TR>
+ <TH>Argument</TH>
+ <TH>Description</TH>
+</TR>
+<TR>
+ <TD></TD>
+ <TD></TD>
+</TR>
+</TABLE></CENTER>
+
+<H3>Returns</H3>
+
+<H3>Description</H3>
+
+<H3>Example</H3>
+
+<PRE>
+</PRE>
+
+<H3>See Also</H3>
+
+
+<!-- NEW PAGE --><H2><A NAME="httpGetField">httpGetField()</A></H2>
+
+<H3>Usage</H3>
+
+<PRE>
+</PRE>
+
+<H3>Arguments</H3>
+
+<CENTER><TABLE WIDTH="80%" BORDER>
+<TR>
+ <TH>Argument</TH>
+ <TH>Description</TH>
+</TR>
+<TR>
+ <TD></TD>
+ <TD></TD>
+</TR>
+</TABLE></CENTER>
+
+<H3>Returns</H3>
+
+<H3>Description</H3>
+
+<H3>Example</H3>
+
+<PRE>
+</PRE>
+
+<H3>See Also</H3>
+
+
+<!-- NEW PAGE --><H2><A NAME="httpGetLength">httpGetLength()</A></H2>
+
+<H3>Usage</H3>
+
+<PRE>
+</PRE>
+
+<H3>Arguments</H3>
+
+<CENTER><TABLE WIDTH="80%" BORDER>
+<TR>
+ <TH>Argument</TH>
+ <TH>Description</TH>
+</TR>
+<TR>
+ <TD></TD>
+ <TD></TD>
+</TR>
+</TABLE></CENTER>
+
+<H3>Returns</H3>
+
+<H3>Description</H3>
+
+<H3>Example</H3>
+
+<PRE>
+</PRE>
+
+<H3>See Also</H3>
+
+
+<!-- NEW PAGE --><H2><A NAME="httpHead">httpHead()</A></H2>
+
+<H3>Usage</H3>
+
+<PRE>
+</PRE>
+
+<H3>Arguments</H3>
+
+<CENTER><TABLE WIDTH="80%" BORDER>
+<TR>
+ <TH>Argument</TH>
+ <TH>Description</TH>
+</TR>
+<TR>
+ <TD></TD>
+ <TD></TD>
+</TR>
+</TABLE></CENTER>
+
+<H3>Returns</H3>
+
+<H3>Description</H3>
+
+<H3>Example</H3>
+
+<PRE>
+</PRE>
+
+<H3>See Also</H3>
+
+
+<!-- NEW PAGE --><H2><A NAME="httpInitialize">httpInitialize()</A></H2>
+
+<H3>Usage</H3>
+
+<PRE>
+</PRE>
+
+<H3>Arguments</H3>
+
+<CENTER><TABLE WIDTH="80%" BORDER>
+<TR>
+ <TH>Argument</TH>
+ <TH>Description</TH>
+</TR>
+<TR>
+ <TD></TD>
+ <TD></TD>
+</TR>
+</TABLE></CENTER>
+
+<H3>Returns</H3>
+
+<H3>Description</H3>
+
+<H3>Example</H3>
+
+<PRE>
+</PRE>
+
+<H3>See Also</H3>
+
+
+<!-- NEW PAGE --><H2><A NAME="httpOptions">httpOptions()</A></H2>
+
+<H3>Usage</H3>
+
+<PRE>
+</PRE>
+
+<H3>Arguments</H3>
+
+<CENTER><TABLE WIDTH="80%" BORDER>
+<TR>
+ <TH>Argument</TH>
+ <TH>Description</TH>
+</TR>
+<TR>
+ <TD></TD>
+ <TD></TD>
+</TR>
+</TABLE></CENTER>
+
+<H3>Returns</H3>
+
+<H3>Description</H3>
+
+<H3>Example</H3>
+
+<PRE>
+</PRE>
+
+<H3>See Also</H3>
+
+
+<!-- NEW PAGE --><H2><A NAME="httpPost">httpPost()</A></H2>
+
+<H3>Usage</H3>
+
+<PRE>
+</PRE>
+
+<H3>Arguments</H3>
+
+<CENTER><TABLE WIDTH="80%" BORDER>
+<TR>
+ <TH>Argument</TH>
+ <TH>Description</TH>
+</TR>
+<TR>
+ <TD></TD>
+ <TD></TD>
+</TR>
+</TABLE></CENTER>
+
+<H3>Returns</H3>
+
+<H3>Description</H3>
+
+<H3>Example</H3>
+
+<PRE>
+</PRE>
+
+<H3>See Also</H3>
+
+
+<!-- NEW PAGE --><H2><A NAME="httpPrintf">httpPrintf()</A></H2>
+
+<H3>Usage</H3>
+
+<PRE>
+</PRE>
+
+<H3>Arguments</H3>
+
+<CENTER><TABLE WIDTH="80%" BORDER>
+<TR>
+ <TH>Argument</TH>
+ <TH>Description</TH>
+</TR>
+<TR>
+ <TD></TD>
+ <TD></TD>
+</TR>
+</TABLE></CENTER>
+
+<H3>Returns</H3>
+
+<H3>Description</H3>
+
+<H3>Example</H3>
+
+<PRE>
+</PRE>
+
+<H3>See Also</H3>
+
+
+<!-- NEW PAGE --><H2><A NAME="httpPut">httpPut()</A></H2>
+
+<H3>Usage</H3>
+
+<PRE>
+</PRE>
+
+<H3>Arguments</H3>
+
+<CENTER><TABLE WIDTH="80%" BORDER>
+<TR>
+ <TH>Argument</TH>
+ <TH>Description</TH>
+</TR>
+<TR>
+ <TD></TD>
+ <TD></TD>
+</TR>
+</TABLE></CENTER>
+
+<H3>Returns</H3>
+
+<H3>Description</H3>
+
+<H3>Example</H3>
+
+<PRE>
+</PRE>
+
+<H3>See Also</H3>
+
+
+<!-- NEW PAGE --><H2><A NAME="httpRead">httpRead()</A></H2>
+
+<H3>Usage</H3>
+
+<PRE>
+</PRE>
+
+<H3>Arguments</H3>
+
+<CENTER><TABLE WIDTH="80%" BORDER>
+<TR>
+ <TH>Argument</TH>
+ <TH>Description</TH>
+</TR>
+<TR>
+ <TD></TD>
+ <TD></TD>
+</TR>
+</TABLE></CENTER>
+
+<H3>Returns</H3>
+
+<H3>Description</H3>
+
+<H3>Example</H3>
+
+<PRE>
+</PRE>
+
+<H3>See Also</H3>
+
+
+<!-- NEW PAGE --><H2><A NAME="httpReconnect">httpReconnect()</A></H2>
+
+<H3>Usage</H3>
+
+<PRE>
+</PRE>
+
+<H3>Arguments</H3>
+
+<CENTER><TABLE WIDTH="80%" BORDER>
+<TR>
+ <TH>Argument</TH>
+ <TH>Description</TH>
+</TR>
+<TR>
+ <TD></TD>
+ <TD></TD>
+</TR>
+</TABLE></CENTER>
+
+<H3>Returns</H3>
+
+<H3>Description</H3>
+
+<H3>Example</H3>
+
+<PRE>
+</PRE>
+
+<H3>See Also</H3>
+
+
+<!-- NEW PAGE --><H2><A NAME="httpSeparate">httpSeparate()</A></H2>
+
+<H3>Usage</H3>
+
+<PRE>
+</PRE>
+
+<H3>Arguments</H3>
+
+<CENTER><TABLE WIDTH="80%" BORDER>
+<TR>
+ <TH>Argument</TH>
+ <TH>Description</TH>
+</TR>
+<TR>
+ <TD></TD>
+ <TD></TD>
+</TR>
+</TABLE></CENTER>
+
+<H3>Returns</H3>
+
+<H3>Description</H3>
+
+<H3>Example</H3>
+
+<PRE>
+</PRE>
+
+<H3>See Also</H3>
+
+
+<!-- NEW PAGE --><H2><A NAME="httpSetField">httpSetField()</A></H2>
+
+<H3>Usage</H3>
+
+<PRE>
+</PRE>
+
+<H3>Arguments</H3>
+
+<CENTER><TABLE WIDTH="80%" BORDER>
+<TR>
+ <TH>Argument</TH>
+ <TH>Description</TH>
+</TR>
+<TR>
+ <TD></TD>
+ <TD></TD>
+</TR>
+</TABLE></CENTER>
+
+<H3>Returns</H3>
+
+<H3>Description</H3>
+
+<H3>Example</H3>
+
+<PRE>
+</PRE>
+
+<H3>See Also</H3>
+
+
+<!-- NEW PAGE --><H2><A NAME="httpTrace">httpTrace()</A></H2>
+
+<H3>Usage</H3>
+
+<PRE>
+</PRE>
+
+<H3>Arguments</H3>
+
+<CENTER><TABLE WIDTH="80%" BORDER>
+<TR>
+ <TH>Argument</TH>
+ <TH>Description</TH>
+</TR>
+<TR>
+ <TD></TD>
+ <TD></TD>
+</TR>
+</TABLE></CENTER>
+
+<H3>Returns</H3>
+
+<H3>Description</H3>
+
+<H3>Example</H3>
+
+<PRE>
+</PRE>
+
+<H3>See Also</H3>
+
+
+<!-- NEW PAGE --><H2><A NAME="httpUpdate">httpUpdate()</A></H2>
+
+<H3>Usage</H3>
+
+<PRE>
+</PRE>
+
+<H3>Arguments</H3>
+
+<CENTER><TABLE WIDTH="80%" BORDER>
+<TR>
+ <TH>Argument</TH>
+ <TH>Description</TH>
+</TR>
+<TR>
+ <TD></TD>
+ <TD></TD>
+</TR>
+</TABLE></CENTER>
+
+<H3>Returns</H3>
+
+<H3>Description</H3>
+
+<H3>Example</H3>
+
+<PRE>
+</PRE>
+
+<H3>See Also</H3>
+
+
+<!-- NEW PAGE --><H2><A NAME="httpWrite">httpWrite()</A></H2>
+
+<H3>Usage</H3>
+
+<PRE>
+</PRE>
+
+<H3>Arguments</H3>
+
+<CENTER><TABLE WIDTH="80%" BORDER>
+<TR>
+ <TH>Argument</TH>
+ <TH>Description</TH>
+</TR>
+<TR>
+ <TD></TD>
+ <TD></TD>
+</TR>
+</TABLE></CENTER>
+
+<H3>Returns</H3>
+
+<H3>Description</H3>
+
+<H3>Example</H3>
+
+<PRE>
+</PRE>
+
+<H3>See Also</H3>
+
+
+<!-- NEW PAGE --><H2><A NAME="ippAddBoolean">ippAddBoolean()</A></H2>
+
+<H3>Usage</H3>
+
+<PRE>
+</PRE>
+
+<H3>Arguments</H3>
+
+<CENTER><TABLE WIDTH="80%" BORDER>
+<TR>
+ <TH>Argument</TH>
+ <TH>Description</TH>
+</TR>
+<TR>
+ <TD></TD>
+ <TD></TD>
+</TR>
+</TABLE></CENTER>
+
+<H3>Returns</H3>
+
+<H3>Description</H3>
+
+<H3>Example</H3>
+
+<PRE>
+</PRE>
+
+<H3>See Also</H3>
+
+
+<!-- NEW PAGE --><H2><A NAME="ippAddBooleans">ippAddBooleans()</A></H2>
+
+<H3>Usage</H3>
+
+<PRE>
+</PRE>
+
+<H3>Arguments</H3>
+
+<CENTER><TABLE WIDTH="80%" BORDER>
+<TR>
+ <TH>Argument</TH>
+ <TH>Description</TH>
+</TR>
+<TR>
+ <TD></TD>
+ <TD></TD>
+</TR>
+</TABLE></CENTER>
+
+<H3>Returns</H3>
+
+<H3>Description</H3>
+
+<H3>Example</H3>
+
+<PRE>
+</PRE>
+
+<H3>See Also</H3>
+
+
+<!-- NEW PAGE --><H2><A NAME="ippAddDate">ippAddDate()</A></H2>
+
+<H3>Usage</H3>
+
+<PRE>
+</PRE>
+
+<H3>Arguments</H3>
+
+<CENTER><TABLE WIDTH="80%" BORDER>
+<TR>
+ <TH>Argument</TH>
+ <TH>Description</TH>
+</TR>
+<TR>
+ <TD></TD>
+ <TD></TD>
+</TR>
+</TABLE></CENTER>
+
+<H3>Returns</H3>
+
+<H3>Description</H3>
+
+<H3>Example</H3>
+
+<PRE>
+</PRE>
+
+<H3>See Also</H3>
+
+
+<!-- NEW PAGE --><H2><A NAME="ippAddInteger">ippAddInteger()</A></H2>
+
+<H3>Usage</H3>
+
+<PRE>
+</PRE>
+
+<H3>Arguments</H3>
+
+<CENTER><TABLE WIDTH="80%" BORDER>
+<TR>
+ <TH>Argument</TH>
+ <TH>Description</TH>
+</TR>
+<TR>
+ <TD></TD>
+ <TD></TD>
+</TR>
+</TABLE></CENTER>
+
+<H3>Returns</H3>
+
+<H3>Description</H3>
+
+<H3>Example</H3>
+
+<PRE>
+</PRE>
+
+<H3>See Also</H3>
+
+
+<!-- NEW PAGE --><H2><A NAME="ippAddIntegers">ippAddIntegers()</A></H2>
+
+<H3>Usage</H3>
+
+<PRE>
+</PRE>
+
+<H3>Arguments</H3>
+
+<CENTER><TABLE WIDTH="80%" BORDER>
+<TR>
+ <TH>Argument</TH>
+ <TH>Description</TH>
+</TR>
+<TR>
+ <TD></TD>
+ <TD></TD>
+</TR>
+</TABLE></CENTER>
+
+<H3>Returns</H3>
+
+<H3>Description</H3>
+
+<H3>Example</H3>
+
+<PRE>
+</PRE>
+
+<H3>See Also</H3>
+
+
+<!-- NEW PAGE --><H2><A NAME="ippAddRange">ippAddRange()</A></H2>
+
+<H3>Usage</H3>
+
+<PRE>
+</PRE>
+
+<H3>Arguments</H3>
+
+<CENTER><TABLE WIDTH="80%" BORDER>
+<TR>
+ <TH>Argument</TH>
+ <TH>Description</TH>
+</TR>
+<TR>
+ <TD></TD>
+ <TD></TD>
+</TR>
+</TABLE></CENTER>
+
+<H3>Returns</H3>
+
+<H3>Description</H3>
+
+<H3>Example</H3>
+
+<PRE>
+</PRE>
+
+<H3>See Also</H3>
+
+
+<!-- NEW PAGE --><H2><A NAME="ippAddRanges">ippAddRanges()</A></H2>
+
+<H3>Usage</H3>
+
+<PRE>
+</PRE>
+
+<H3>Arguments</H3>
+
+<CENTER><TABLE WIDTH="80%" BORDER>
+<TR>
+ <TH>Argument</TH>
+ <TH>Description</TH>
+</TR>
+<TR>
+ <TD></TD>
+ <TD></TD>
+</TR>
+</TABLE></CENTER>
+
+<H3>Returns</H3>
+
+<H3>Description</H3>
+
+<H3>Example</H3>
+
+<PRE>
+</PRE>
+
+<H3>See Also</H3>
+
+
+<!-- NEW PAGE --><H2><A NAME="ippAddResolution">ippAddResolution()</A></H2>
+
+<H3>Usage</H3>
+
+<PRE>
+</PRE>
+
+<H3>Arguments</H3>
+
+<CENTER><TABLE WIDTH="80%" BORDER>
+<TR>
+ <TH>Argument</TH>
+ <TH>Description</TH>
+</TR>
+<TR>
+ <TD></TD>
+ <TD></TD>
+</TR>
+</TABLE></CENTER>
+
+<H3>Returns</H3>
+
+<H3>Description</H3>
+
+<H3>Example</H3>
+
+<PRE>
+</PRE>
+
+<H3>See Also</H3>
+
+
+<!-- NEW PAGE --><H2><A NAME="ippAddResolutions">ippAddResolutions()</A></H2>
+
+<H3>Usage</H3>
+
+<PRE>
+</PRE>
+
+<H3>Arguments</H3>
+
+<CENTER><TABLE WIDTH="80%" BORDER>
+<TR>
+ <TH>Argument</TH>
+ <TH>Description</TH>
+</TR>
+<TR>
+ <TD></TD>
+ <TD></TD>
+</TR>
+</TABLE></CENTER>
+
+<H3>Returns</H3>
+
+<H3>Description</H3>
+
+<H3>Example</H3>
+
+<PRE>
+</PRE>
+
+<H3>See Also</H3>
+
+
+<!-- NEW PAGE --><H2><A NAME="ippAddSeparator">ippAddSeparator()</A></H2>
+
+<H3>Usage</H3>
+
+<PRE>
+</PRE>
+
+<H3>Arguments</H3>
+
+<CENTER><TABLE WIDTH="80%" BORDER>
+<TR>
+ <TH>Argument</TH>
+ <TH>Description</TH>
+</TR>
+<TR>
+ <TD></TD>
+ <TD></TD>
+</TR>
+</TABLE></CENTER>
+
+<H3>Returns</H3>
+
+<H3>Description</H3>
+
+<H3>Example</H3>
+
+<PRE>
+</PRE>
+
+<H3>See Also</H3>
+
+
+<!-- NEW PAGE --><H2><A NAME="ippAddString">ippAddString()</A></H2>
+
+<H3>Usage</H3>
+
+<PRE>
+</PRE>
+
+<H3>Arguments</H3>
+
+<CENTER><TABLE WIDTH="80%" BORDER>
+<TR>
+ <TH>Argument</TH>
+ <TH>Description</TH>
+</TR>
+<TR>
+ <TD></TD>
+ <TD></TD>
+</TR>
+</TABLE></CENTER>
+
+<H3>Returns</H3>
+
+<H3>Description</H3>
+
+<H3>Example</H3>
+
+<PRE>
+</PRE>
+
+<H3>See Also</H3>
+
+
+<!-- NEW PAGE --><H2><A NAME="ippAddStrings">ippAddStrings()</A></H2>
+
+<H3>Usage</H3>
+
+<PRE>
+</PRE>
+
+<H3>Arguments</H3>
+
+<CENTER><TABLE WIDTH="80%" BORDER>
+<TR>
+ <TH>Argument</TH>
+ <TH>Description</TH>
+</TR>
+<TR>
+ <TD></TD>
+ <TD></TD>
+</TR>
+</TABLE></CENTER>
+
+<H3>Returns</H3>
+
+<H3>Description</H3>
+
+<H3>Example</H3>
+
+<PRE>
+</PRE>
+
+<H3>See Also</H3>
+
+
+<!-- NEW PAGE --><H2><A NAME="ippDateToTime">ippDateToTime()</A></H2>
+
+<H3>Usage</H3>
+
+<PRE>
+</PRE>
+
+<H3>Arguments</H3>
+
+<CENTER><TABLE WIDTH="80%" BORDER>
+<TR>
+ <TH>Argument</TH>
+ <TH>Description</TH>
+</TR>
+<TR>
+ <TD></TD>
+ <TD></TD>
+</TR>
+</TABLE></CENTER>
+
+<H3>Returns</H3>
+
+<H3>Description</H3>
+
+<H3>Example</H3>
+
+<PRE>
+</PRE>
+
+<H3>See Also</H3>
+
+
+<!-- NEW PAGE --><H2><A NAME="ippDelete">ippDelete()</A></H2>
+
+<H3>Usage</H3>
+
+<PRE>
+</PRE>
+
+<H3>Arguments</H3>
+
+<CENTER><TABLE WIDTH="80%" BORDER>
+<TR>
+ <TH>Argument</TH>
+ <TH>Description</TH>
+</TR>
+<TR>
+ <TD></TD>
+ <TD></TD>
+</TR>
+</TABLE></CENTER>
+
+<H3>Returns</H3>
+
+<H3>Description</H3>
+
+<H3>Example</H3>
+
+<PRE>
+</PRE>
+
+<H3>See Also</H3>
+
+
+<!-- NEW PAGE --><H2><A NAME="ippFindAttribute">ippFindAttribute()</A></H2>
+
+<H3>Usage</H3>
+
+<PRE>
+</PRE>
+
+<H3>Arguments</H3>
+
+<CENTER><TABLE WIDTH="80%" BORDER>
+<TR>
+ <TH>Argument</TH>
+ <TH>Description</TH>
+</TR>
+<TR>
+ <TD></TD>
+ <TD></TD>
+</TR>
+</TABLE></CENTER>
+
+<H3>Returns</H3>
+
+<H3>Description</H3>
+
+<H3>Example</H3>
+
+<PRE>
+</PRE>
+
+<H3>See Also</H3>
+
+
+<!-- NEW PAGE --><H2><A NAME="ippLength">ippLength()</A></H2>
+
+<H3>Usage</H3>
+
+<PRE>
+</PRE>
+
+<H3>Arguments</H3>
+
+<CENTER><TABLE WIDTH="80%" BORDER>
+<TR>
+ <TH>Argument</TH>
+ <TH>Description</TH>
+</TR>
+<TR>
+ <TD></TD>
+ <TD></TD>
+</TR>
+</TABLE></CENTER>
+
+<H3>Returns</H3>
+
+<H3>Description</H3>
+
+<H3>Example</H3>
+
+<PRE>
+</PRE>
+
+<H3>See Also</H3>
+
+
+<!-- NEW PAGE --><H2><A NAME="ippNew">ippNew()</A></H2>
+
+<H3>Usage</H3>
+
+<PRE>
+</PRE>
+
+<H3>Arguments</H3>
+
+<CENTER><TABLE WIDTH="80%" BORDER>
+<TR>
+ <TH>Argument</TH>
+ <TH>Description</TH>
+</TR>
+<TR>
+ <TD></TD>
+ <TD></TD>
+</TR>
+</TABLE></CENTER>
+
+<H3>Returns</H3>
+
+<H3>Description</H3>
+
+<H3>Example</H3>
+
+<PRE>
+</PRE>
+
+<H3>See Also</H3>
+
+
+<!-- NEW PAGE --><H2><A NAME="ippPort">ippPort()</A></H2>
+
+<H3>Usage</H3>
+
+<PRE>
+</PRE>
+
+<H3>Arguments</H3>
+
+<CENTER><TABLE WIDTH="80%" BORDER>
+<TR>
+ <TH>Argument</TH>
+ <TH>Description</TH>
+</TR>
+<TR>
+ <TD></TD>
+ <TD></TD>
+</TR>
+</TABLE></CENTER>
+
+<H3>Returns</H3>
+
+<H3>Description</H3>
+
+<H3>Example</H3>
+
+<PRE>
+</PRE>
+
+<H3>See Also</H3>
+
+
+<!-- NEW PAGE --><H2><A NAME="ippRead">ippRead()</A></H2>
+
+<H3>Usage</H3>
+
+<PRE>
+</PRE>
+
+<H3>Arguments</H3>
+
+<CENTER><TABLE WIDTH="80%" BORDER>
+<TR>
+ <TH>Argument</TH>
+ <TH>Description</TH>
+</TR>
+<TR>
+ <TD></TD>
+ <TD></TD>
+</TR>
+</TABLE></CENTER>
+
+<H3>Returns</H3>
+
+<H3>Description</H3>
+
+<H3>Example</H3>
+
+<PRE>
+</PRE>
+
+<H3>See Also</H3>
+
+
+<!-- NEW PAGE --><H2><A NAME="ippTimeToDate">ippTimeToDate()</A></H2>
+
+<H3>Usage</H3>
+
+<PRE>
+</PRE>
+
+<H3>Arguments</H3>
+
+<CENTER><TABLE WIDTH="80%" BORDER>
+<TR>
+ <TH>Argument</TH>
+ <TH>Description</TH>
+</TR>
+<TR>
+ <TD></TD>
+ <TD></TD>
+</TR>
+</TABLE></CENTER>
+
+<H3>Returns</H3>
+
+<H3>Description</H3>
+
+<H3>Example</H3>
+
+<PRE>
+</PRE>
+
+<H3>See Also</H3>
+
+
+<!-- NEW PAGE --><H2><A NAME="ippWrite">ippWrite()</A></H2>
+
+<H3>Usage</H3>
+
+<PRE>
+</PRE>
+
+<H3>Arguments</H3>
+
+<CENTER><TABLE WIDTH="80%" BORDER>
+<TR>
+ <TH>Argument</TH>
+ <TH>Description</TH>
+</TR>
+<TR>
+ <TD></TD>
+ <TD></TD>
+</TR>
+</TABLE></CENTER>
+
+<H3>Returns</H3>
+
+<H3>Description</H3>
+
+<H3>Example</H3>
+
+<PRE>
+</PRE>
+
+<H3>See Also</H3>
+
+
+<!-- NEW PAGE --><H2><A NAME="ppdClose">ppdClose()</A></H2>
+
+<H3>Usage</H3>
+
+<PRE>
+</PRE>
+
+<H3>Arguments</H3>
+
+<CENTER><TABLE WIDTH="80%" BORDER>
+<TR>
+ <TH>Argument</TH>
+ <TH>Description</TH>
+</TR>
+<TR>
+ <TD></TD>
+ <TD></TD>
+</TR>
+</TABLE></CENTER>
+
+<H3>Returns</H3>
+
+<H3>Description</H3>
+
+<H3>Example</H3>
+
+<PRE>
+</PRE>
+
+<H3>See Also</H3>
+
+
+<!-- NEW PAGE --><H2><A NAME="ppdConflicts">ppdConflicts()</A></H2>
+
+<H3>Usage</H3>
+
+<PRE>
+</PRE>
+
+<H3>Arguments</H3>
+
+<CENTER><TABLE WIDTH="80%" BORDER>
+<TR>
+ <TH>Argument</TH>
+ <TH>Description</TH>
+</TR>
+<TR>
+ <TD></TD>
+ <TD></TD>
+</TR>
+</TABLE></CENTER>
+
+<H3>Returns</H3>
+
+<H3>Description</H3>
+
+<H3>Example</H3>
+
+<PRE>
+</PRE>
+
+<H3>See Also</H3>
+
+
+<!-- NEW PAGE --><H2><A NAME="pddEmitFd">pddEmitFd()</A></H2>
+
+<H3>Usage</H3>
+
+<PRE>
+</PRE>
+
+<H3>Arguments</H3>
+
+<CENTER><TABLE WIDTH="80%" BORDER>
+<TR>
+ <TH>Argument</TH>
+ <TH>Description</TH>
+</TR>
+<TR>
+ <TD></TD>
+ <TD></TD>
+</TR>
+</TABLE></CENTER>
+
+<H3>Returns</H3>
+
+<H3>Description</H3>
+
+<H3>Example</H3>
+
+<PRE>
+</PRE>
+
+<H3>See Also</H3>
+
+
+<!-- NEW PAGE --><H2><A NAME="ppdEmit">ppdEmit()</A></H2>
+
+<H3>Usage</H3>
+
+<PRE>
+</PRE>
+
+<H3>Arguments</H3>
+
+<CENTER><TABLE WIDTH="80%" BORDER>
+<TR>
+ <TH>Argument</TH>
+ <TH>Description</TH>
+</TR>
+<TR>
+ <TD></TD>
+ <TD></TD>
+</TR>
+</TABLE></CENTER>
+
+<H3>Returns</H3>
+
+<H3>Description</H3>
+
+<H3>Example</H3>
+
+<PRE>
+</PRE>
+
+<H3>See Also</H3>
+
+
+<!-- NEW PAGE --><H2><A NAME="ppdFindChoice">ppdFindChoice()</A></H2>
+
+<H3>Usage</H3>
+
+<PRE>
+</PRE>
+
+<H3>Arguments</H3>
+
+<CENTER><TABLE WIDTH="80%" BORDER>
+<TR>
+ <TH>Argument</TH>
+ <TH>Description</TH>
+</TR>
+<TR>
+ <TD></TD>
+ <TD></TD>
+</TR>
+</TABLE></CENTER>
+
+<H3>Returns</H3>
+
+<H3>Description</H3>
+
+<H3>Example</H3>
+
+<PRE>
+</PRE>
+
+<H3>See Also</H3>
+
+
+<!-- NEW PAGE --><H2><A NAME="ppdFindMarkedChoice">ppdFindMarkedChoice()</A></H2>
+
+<H3>Usage</H3>
+
+<PRE>
+</PRE>
+
+<H3>Arguments</H3>
+
+<CENTER><TABLE WIDTH="80%" BORDER>
+<TR>
+ <TH>Argument</TH>
+ <TH>Description</TH>
+</TR>
+<TR>
+ <TD></TD>
+ <TD></TD>
+</TR>
+</TABLE></CENTER>
+
+<H3>Returns</H3>
+
+<H3>Description</H3>
+
+<H3>Example</H3>
+
+<PRE>
+</PRE>
+
+<H3>See Also</H3>
+
+
+<!-- NEW PAGE --><H2><A NAME="ppdFindOption">ppdFindOption()</A></H2>
+
+<H3>Usage</H3>
+
+<PRE>
+</PRE>
+
+<H3>Arguments</H3>
+
+<CENTER><TABLE WIDTH="80%" BORDER>
+<TR>
+ <TH>Argument</TH>
+ <TH>Description</TH>
+</TR>
+<TR>
+ <TD></TD>
+ <TD></TD>
+</TR>
+</TABLE></CENTER>
+
+<H3>Returns</H3>
+
+<H3>Description</H3>
+
+<H3>Example</H3>
+
+<PRE>
+</PRE>
+
+<H3>See Also</H3>
+
+
+<!-- NEW PAGE --><H2><A NAME="ppdIsMarked">ppdIsMarked()</A></H2>
+
+<H3>Usage</H3>
+
+<PRE>
+</PRE>
+
+<H3>Arguments</H3>
+
+<CENTER><TABLE WIDTH="80%" BORDER>
+<TR>
+ <TH>Argument</TH>
+ <TH>Description</TH>
+</TR>
+<TR>
+ <TD></TD>
+ <TD></TD>
+</TR>
+</TABLE></CENTER>
+
+<H3>Returns</H3>
+
+<H3>Description</H3>
+
+<H3>Example</H3>
+
+<PRE>
+</PRE>
+
+<H3>See Also</H3>
+
+
+<!-- NEW PAGE --><H2><A NAME="ppdMarkDefaults">ppdMarkDefaults()</A></H2>
+
+<H3>Usage</H3>
+
+<PRE>
+</PRE>
+
+<H3>Arguments</H3>
+
+<CENTER><TABLE WIDTH="80%" BORDER>
+<TR>
+ <TH>Argument</TH>
+ <TH>Description</TH>
+</TR>
+<TR>
+ <TD></TD>
+ <TD></TD>
+</TR>
+</TABLE></CENTER>
+
+<H3>Returns</H3>
+
+<H3>Description</H3>
+
+<H3>Example</H3>
+
+<PRE>
+</PRE>
+
+<H3>See Also</H3>
+
+
+<!-- NEW PAGE --><H2><A NAME="ppdMarkOption">ppdMarkOption()</A></H2>
+
+<H3>Usage</H3>
+
+<PRE>
+</PRE>
+
+<H3>Arguments</H3>
+
+<CENTER><TABLE WIDTH="80%" BORDER>
+<TR>
+ <TH>Argument</TH>
+ <TH>Description</TH>
+</TR>
+<TR>
+ <TD></TD>
+ <TD></TD>
+</TR>
+</TABLE></CENTER>
+
+<H3>Returns</H3>
+
+<H3>Description</H3>
+
+<H3>Example</H3>
+
+<PRE>
+</PRE>
+
+<H3>See Also</H3>
+
+
+<!-- NEW PAGE --><H2><A NAME="ppdOpenFd">ppdOpenFd()</A></H2>
+
+<H3>Usage</H3>
+
+<PRE>
+</PRE>
+
+<H3>Arguments</H3>
+
+<CENTER><TABLE WIDTH="80%" BORDER>
+<TR>
+ <TH>Argument</TH>
+ <TH>Description</TH>
+</TR>
+<TR>
+ <TD></TD>
+ <TD></TD>
+</TR>
+</TABLE></CENTER>
+
+<H3>Returns</H3>
+
+<H3>Description</H3>
+
+<H3>Example</H3>
+
+<PRE>
+</PRE>
+
+<H3>See Also</H3>
+
+
+<!-- NEW PAGE --><H2><A NAME="ppdOpenFile">ppdOpenFile()</A></H2>
+
+<H3>Usage</H3>
+
+<PRE>
+</PRE>
+
+<H3>Arguments</H3>
+
+<CENTER><TABLE WIDTH="80%" BORDER>
+<TR>
+ <TH>Argument</TH>
+ <TH>Description</TH>
+</TR>
+<TR>
+ <TD></TD>
+ <TD></TD>
+</TR>
+</TABLE></CENTER>
+
+<H3>Returns</H3>
+
+<H3>Description</H3>
+
+<H3>Example</H3>
+
+<PRE>
+</PRE>
+
+<H3>See Also</H3>
+
+
+<!-- NEW PAGE --><H2><A NAME="ppdOpen">ppdOpen()</A></H2>
+
+<H3>Usage</H3>
+
+<PRE>
+</PRE>
+
+<H3>Arguments</H3>
+
+<CENTER><TABLE WIDTH="80%" BORDER>
+<TR>
+ <TH>Argument</TH>
+ <TH>Description</TH>
+</TR>
+<TR>
+ <TD></TD>
+ <TD></TD>
+</TR>
+</TABLE></CENTER>
+
+<H3>Returns</H3>
+
+<H3>Description</H3>
+
+<H3>Example</H3>
+
+<PRE>
+</PRE>
+
+<H3>See Also</H3>
+
+
+<!-- NEW PAGE --><H2><A NAME="ppdPageLength">ppdPageLength()</A></H2>
+
+<H3>Usage</H3>
+
+<PRE>
+</PRE>
+
+<H3>Arguments</H3>
+
+<CENTER><TABLE WIDTH="80%" BORDER>
+<TR>
+ <TH>Argument</TH>
+ <TH>Description</TH>
+</TR>
+<TR>
+ <TD></TD>
+ <TD></TD>
+</TR>
+</TABLE></CENTER>
+
+<H3>Returns</H3>
+
+<H3>Description</H3>
+
+<H3>Example</H3>
+
+<PRE>
+</PRE>
+
+<H3>See Also</H3>
+
+
+<!-- NEW PAGE --><H2><A NAME="ppdPageSize">ppdPageSize()</A></H2>
+
+<H3>Usage</H3>
+
+<PRE>
+</PRE>
+
+<H3>Arguments</H3>
+
+<CENTER><TABLE WIDTH="80%" BORDER>
+<TR>
+ <TH>Argument</TH>
+ <TH>Description</TH>
+</TR>
+<TR>
+ <TD></TD>
+ <TD></TD>
+</TR>
+</TABLE></CENTER>
+
+<H3>Returns</H3>
+
+<H3>Description</H3>
+
+<H3>Example</H3>
+
+<PRE>
+</PRE>
+
+<H3>See Also</H3>
+
+
+<!-- NEW PAGE --><H2><A NAME="ppdPageWidth">ppdPageWidth()</A></H2>
+
+<H3>Usage</H3>
+
+<PRE>
+</PRE>
+
+<H3>Arguments</H3>
+
+<CENTER><TABLE WIDTH="80%" BORDER>
+<TR>
+ <TH>Argument</TH>
+ <TH>Description</TH>
+</TR>
+<TR>
+ <TD></TD>
+ <TD></TD>
+</TR>
+</TABLE></CENTER>
+
+<H3>Returns</H3>
+
+<H3>Description</H3>
+
+<H3>Example</H3>
+
+<PRE>
+</PRE>
+
+<H3>See Also</H3>
+
+
+</BODY>
+</HTML>
diff --git a/doc/ssr.html b/doc/ssr.html
new file mode 100644
index 000000000..7158d97ff
--- /dev/null
+++ b/doc/ssr.html
@@ -0,0 +1,202 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
+<HTML>
+<HEAD>
+<TITLE>CUPS Software Security Report</TITLE>
+<META NAME="AUTHOR" CONTENT="Easy Software Products">
+<META NAME="COPYRIGHT" CONTENT="Copyright 1997-2000, All Rights Reserved">
+<META NAME="DOCNUMBER" CONTENT="CUPS-SSR-1.1">
+<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
+<STYLE>
+BODY { font-family: serif; font-size: 11.0pt }
+H1 { font-family: sans-serif; font-size: 20.0pt }
+H2 { font-family: sans-serif; font-size: 17.0pt }
+H3 { font-family: sans-serif; font-size: 14.0pt }
+H4 { font-family: sans-serif; font-size: 11.0pt }
+H5 { font-family: sans-serif; font-size: 9.0pt }
+H6 { font-family: sans-serif; font-size: 8.0pt }
+SUB { font-size: 8.0pt }
+SUP { font-size: 8.0pt }
+PRE { font-size: 9.0pt }
+A:link { text-decoration: underline }
+A:visited { text-decoration: underline }
+A:active { text-decoration: underline }
+</STYLE>
+</HEAD>
+<BODY>
+<CENTER><A HREF="#CONTENTS"><IMG SRC="images/cups-large.gif" BORDER="0"><BR>
+<H1>CUPS Software Security Report</H1></A><BR>
+CUPS-SSR-1.1<BR>
+Easy Software Products<BR>
+Copyright 1997-2000, 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>
+<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.1.
+<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 <A HREF="http://www.easysw.com">
+Easy Software Products</A> 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 5.50) and an image file RIP that
+is 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>
+<P>The following CUPS documentation is referenced by this document: </P>
+<UL>
+<LI>CUPS-CMP-1.1: CUPS Configuration Management Plan </LI>
+<LI>CUPS-IDD-1.1: CUPS System Interface Design Description </LI>
+<LI>CUPS-IPP-1.1: CUPS Implmentation of IPP </LI>
+<LI>CUPS-SAM-1.1.x: CUPS Software Administrators Manual </LI>
+<LI>CUPS-SDD-1.1: CUPS Software Design Description </LI>
+<LI>CUPS-SPM-1.1: CUPS Software Programming Manual </LI>
+<LI>CUPS-SSR-1.1: CUPS Software Security Report </LI>
+<LI>CUPS-STP-1.1: CUPS Software Test Plan </LI>
+<LI>CUPS-SUM-1.1.x: CUPS Software Users Manual </LI>
+<LI>CUPS-SVD-1.1.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>Adobe PostScript Printer Description File Format Specification,
+ Version 4.3. </LI>
+<LI>Adobe PostScript Language Reference, Third Edition. </LI>
+<LI>IPP: Job and Printer Set Operations </LI>
+<LI>IPP/1.1: Encoding and Transport </LI>
+<LI>IPP/1.1: Implementers Guide </LI>
+<LI>IPP/1.1: Model and Semantics </LI>
+<LI>RFC 1179, Line Printer Daemon Protocol </LI>
+<LI>RFC 2567, Design Goals for an Internet Printing Protocol </LI>
+<LI>RFC 2568, Rationale for the Structure of the Model and Protocol
+ for the Internet Printing Protocol </LI>
+<LI>RFC 2569, Mapping between LPD and IPP Protocols </LI>
+<LI>RFC 2616, Hypertext Transfer Protocol -- HTTP/1.1 </LI>
+<LI>RFC 2617, HTTP Authentication: Basic and Digest Access
+ Authentication </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>There are two known security vulnerabilities with local access: </P>
+<OL>
+<LI>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. </LI>
+<P>This problem can be alleviated by making the request directory
+readable only by the user specified in the CUPS configuration file. </P>
+<LI>Device URIs are passed to backend filters in argv[0] and in an
+environment variable. Since device URIs can contain usernames and
+passwords it may be possible for a local user to gain access to a
+remote resource. </LI>
+<P>We recommend that any password-protected accounts used for remote
+printing have limited access priviledges so that the possible damages
+can be minimized. </P>
+<P>The device URI is &quot;sanitized&quot; (the username and password are
+ removed) when sent to an IPP client so that a remote user cannot
+exploit this vulnerability. </P>
+</OL>
+<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. </LI>
+<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>Repeatedly opening and closing connections to the server as fast
+ as possible. </LI>
+<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>Flooding the network with broadcast packets on port 631. </LI>
+<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>Sending partial IPP requests; specifically, sending part of an
+ attribute value and then stopping transmission. </LI>
+<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>Sending large/long print jobs to printers, preventing other users
+ from printing. </LI>
+<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>
+</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>
+</BODY>
+</HTML>
diff --git a/doc/ssr.pdf b/doc/ssr.pdf
new file mode 100644
index 000000000..90d8c2562
--- /dev/null
+++ b/doc/ssr.pdf
Binary files differ
diff --git a/doc/ssr.shtml b/doc/ssr.shtml
new file mode 100644
index 000000000..ac1a42663
--- /dev/null
+++ b/doc/ssr.shtml
@@ -0,0 +1,165 @@
+<HTML>
+<HEAD>
+ <META NAME="COPYRIGHT" CONTENT="Copyright 1997-2000, All Rights Reserved">
+ <META NAME="DOCNUMBER" CONTENT="CUPS-SSR-1.1">
+ <META NAME="Author" CONTENT="Easy Software Products">
+ <TITLE>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.1.</P>
+
+<EMBED SRC="system-overview.shtml">
+
+<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>
+
+<EMBED SRC="references.shtml">
+
+<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>There is one known security vulnerabilities with local access:
+
+<OL>
+
+ <LI>Device URIs are passed to backend filters in argv[0] and in
+ an environment variable. Since device URIs can contain
+ usernames and passwords it may be possible for a local user to
+ gain access to a remote resource.
+
+ <P>We recommend that any password-protected accounts used for
+ remote printing have limited access priviledges so that the
+ possible damages can be minimized.
+
+ <P>The device URI is "sanitized" (the username and password are
+ removed) when sent to an IPP client so that a remote user
+ cannot exploit this vulnerability.
+
+</OL>
+
+<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 supports Basic, Digest, and local certificate
+authentication:
+
+<OL>
+
+ <LI>Basic authentication 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.
+
+ <LI>Digest authentication uses an MD5 checksum of the username,
+ password, and domain ("CUPS"), so the original username and
+ password is not sent over the network. However, the current
+ implementation does not authenticate the entire message and
+ uses the client's IP address for the nonce value, making it
+ possible to launch "man in the middle" and replay attacks from
+ the same client. The next minor release of CUPS will support
+ Digest authentication of the entire message body, effectively
+ stopping these methods of attack.
+
+ <LI>Local certificate authentication passes 128-bit
+ "certificates" that identify an authenticated user.
+ Certificates are created on-the-fly from random data and stored
+ in files under <CODE>/etc/cups/certs</CODE>. They have
+ restricted read permissions: root + system for the root
+ certificate, and lp + system for CGI certificates. Because
+ certificates are only available on the local system, the CUPS
+ server does not accept local authentication unless the client
+ is connected to the localhost address (127.0.0.1.)
+
+</OL>
+
+<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. Also, we highly recommend using Digest
+authentication when possible; unfortunately, most web browsers do not
+support Digest authentication at this time.
+
+<EMBED SRC="glossary.shtml">
+
+</BODY>
+</HTML>
diff --git a/doc/stp.html b/doc/stp.html
new file mode 100644
index 000000000..9596d626c
--- /dev/null
+++ b/doc/stp.html
@@ -0,0 +1,172 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
+<HTML>
+<HEAD>
+<TITLE>DRAFT - CUPS Software Test Plan</TITLE>
+<META NAME="AUTHOR" CONTENT="Easy Software Products">
+<META NAME="COPYRIGHT" CONTENT="Copyright 1997-2000, All Rights Reserved">
+<META NAME="DOCNUMBER" CONTENT="CUPS-STP-1.1">
+<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
+<STYLE>
+BODY { font-family: serif; font-size: 11.0pt }
+H1 { font-family: sans-serif; font-size: 20.0pt }
+H2 { font-family: sans-serif; font-size: 17.0pt }
+H3 { font-family: sans-serif; font-size: 14.0pt }
+H4 { font-family: sans-serif; font-size: 11.0pt }
+H5 { font-family: sans-serif; font-size: 9.0pt }
+H6 { font-family: sans-serif; font-size: 8.0pt }
+SUB { font-size: 8.0pt }
+SUP { font-size: 8.0pt }
+PRE { font-size: 9.0pt }
+A:link { text-decoration: underline }
+A:visited { text-decoration: underline }
+A:active { text-decoration: underline }
+</STYLE>
+</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.1<BR>
+Easy Software Products<BR>
+Copyright 1997-2000, 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.1.
+<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 <A HREF="http://www.easysw.com">
+Easy Software Products</A> 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 5.50) and an image file RIP that
+is 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>
+<P>The following CUPS documentation is referenced by this document: </P>
+<UL>
+<LI>CUPS-CMP-1.1: CUPS Configuration Management Plan </LI>
+<LI>CUPS-IDD-1.1: CUPS System Interface Design Description </LI>
+<LI>CUPS-IPP-1.1: CUPS Implmentation of IPP </LI>
+<LI>CUPS-SAM-1.1.x: CUPS Software Administrators Manual </LI>
+<LI>CUPS-SDD-1.1: CUPS Software Design Description </LI>
+<LI>CUPS-SPM-1.1: CUPS Software Programming Manual </LI>
+<LI>CUPS-SSR-1.1: CUPS Software Security Report </LI>
+<LI>CUPS-STP-1.1: CUPS Software Test Plan </LI>
+<LI>CUPS-SUM-1.1.x: CUPS Software Users Manual </LI>
+<LI>CUPS-SVD-1.1.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>Adobe PostScript Printer Description File Format Specification,
+ Version 4.3. </LI>
+<LI>Adobe PostScript Language Reference, Third Edition. </LI>
+<LI>IPP: Job and Printer Set Operations </LI>
+<LI>IPP/1.1: Encoding and Transport </LI>
+<LI>IPP/1.1: Implementers Guide </LI>
+<LI>IPP/1.1: Model and Semantics </LI>
+<LI>RFC 1179, Line Printer Daemon Protocol </LI>
+<LI>RFC 2567, Design Goals for an Internet Printing Protocol </LI>
+<LI>RFC 2568, Rationale for the Structure of the Model and Protocol
+ for the Internet Printing Protocol </LI>
+<LI>RFC 2569, Mapping between LPD and IPP Protocols </LI>
+<LI>RFC 2616, Hypertext Transfer Protocol -- HTTP/1.1 </LI>
+<LI>RFC 2617, HTTP Authentication: Basic and Digest Access
+ Authentication </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..7c0c26297
--- /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..339ccb267
--- /dev/null
+++ b/doc/stp.shtml
@@ -0,0 +1,47 @@
+<HTML>
+<HEAD>
+ <META NAME="Description" CONTENT="Common UNIX Printing System Software Test Plan">
+ <META NAME="COPYRIGHT" CONTENT="Copyright 1997-2000, All Rights Reserved">
+ <META NAME="DOCNUMBER" CONTENT="CUPS-STP-1.1">
+ <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.1.
+
+<EMBED SRC="system-overview.shtml">
+
+<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>
+
+<EMBED SRC="references.shtml">
+
+<H1>Local Tests</H1>
+
+
+
+
+<H1>Remote Tests</H1>
+
+
+
+
+<EMBED SRC="glossary.shtml">
+
+</BODY>
+</HTML>
diff --git a/doc/sum.html b/doc/sum.html
new file mode 100644
index 000000000..f98faf5be
--- /dev/null
+++ b/doc/sum.html
@@ -0,0 +1,543 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
+<HTML>
+<HEAD>
+<TITLE>CUPS Software Users Manual</TITLE>
+<META NAME="AUTHOR" CONTENT="Easy Software Products">
+<META NAME="COPYRIGHT" CONTENT="Copyright 1997-2000, All Rights Reserved">
+<META NAME="DOCNUMBER" CONTENT="CUPS-SUM-1.1">
+<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
+<STYLE>
+BODY { font-family: serif; font-size: 11.0pt }
+H1 { font-family: sans-serif; font-size: 20.0pt }
+H2 { font-family: sans-serif; font-size: 17.0pt }
+H3 { font-family: sans-serif; font-size: 14.0pt }
+H4 { font-family: sans-serif; font-size: 11.0pt }
+H5 { font-family: sans-serif; font-size: 9.0pt }
+H6 { font-family: sans-serif; font-size: 8.0pt }
+SUB { font-size: 8.0pt }
+SUP { font-size: 8.0pt }
+PRE { font-size: 9.0pt }
+A:link { text-decoration: underline }
+A:visited { text-decoration: underline }
+A:active { text-decoration: underline }
+</STYLE>
+</HEAD>
+<BODY>
+<CENTER><A HREF="#CONTENTS"><IMG SRC="images/cups-large.gif" BORDER="0"><BR>
+<H1>CUPS Software Users Manual</H1></A><BR>
+CUPS-SUM-1.1<BR>
+Easy Software Products<BR>
+Copyright 1997-2000, 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">N-Up Printing</A></LI>
+<LI><A HREF="#4_1_6">Setting the Brightness</A></LI>
+<LI><A HREF="#4_1_7">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.1.
+<H2><A NAME="1_1">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 <A HREF="http://www.easysw.com">
+Easy Software Products</A> 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 5.50) and an image file RIP that
+is 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-edge&quot; and &quot;-o
+sides=two-sided-long-edge&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-edge filename ENTER
+% lp -o sides=two-sided-long-edge 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">N-Up Printing</A></H3>
+<P>The &quot;-o number-up=value&quot; option selects N-Up printing. N-Up
+printing places multiple document pages on a single printed page. CUPS
+supports 1-Up, 2-Up, and 4-Up formats: </P>
+<UL>
+<PRE>
+% lp -o number-up=1 filename ENTER
+% lp -o number-up=2 filename ENTER
+% lp -o number-up=4 filename ENTER
+</PRE>
+</UL>
+<P>The default format is 1-Up. </P>
+<H3><A NAME="4_1_6">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_7">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. The default gamma is 2200 which matches the
+sRGB specification. </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..d86de3ce4
--- /dev/null
+++ b/doc/sum.pdf
@@ -0,0 +1,512 @@
+%PDF-1.2
+%âãÏÓ
+1 0 obj<</Producer(htmldoc 1.8.5 Copyright 1997-2000 Easy Software Products, All Rights Reserved.)/CreationDate(D:20000418184128Z)/Title(CUPS Software Users Manual)/Author(Easy Software Products)>>endobj
+2 0 obj<</Type/Encoding/Differences[ 32/space/exclam/quotedbl/numbersign/dollar/percent/ampersand/quotesingle/parenleft/parenright/asterisk/plus/comma/minus/period/slash/zero/one/two/three/four/five/six/seven/eight/nine/colon/semicolon/less/equal/greater/question/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/grave/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 128/Euro 130/quotesinglbase/florin/quotedblbase/ellipsis/dagger/daggerdbl/circumflex/perthousand/Scaron/guilsinglleft/OE 145/quoteleft/quoteright/quotedblleft/quotedblright/bullet/endash/emdash/tilde/trademark/scaron/guilsinglright/oe 159/Ydieresis/space/exclamdown/cent/sterling/currency/yen/brokenbar/section/dieresis/copyright/ordfeminine/guillemotleft/logicalnot/hyphen/registered/macron/degree/plusminus/twosuperior/threesuperior/acute/mu/paragraph/periodcentered/cedilla/onesuperior/ordmasculine/guillemotright/onequarter/onehalf/threequarters/questiondown/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]>>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://www.easysw.com)>>endobj
+11 0 obj<</Subtype/Link/Rect[157.2 320.8 262.3 333.8]/Border[0 0 0]/A 10 0 R>>endobj
+12 0 obj[11 0 R
+]endobj
+13 0 obj<</S/URI/URI(http://localhost:631)>>endobj
+14 0 obj<</Subtype/Link/Rect[353.9 547.2 439.5 560.2]/Border[0 0 0]/A 13 0 R>>endobj
+15 0 obj[14 0 R
+]endobj
+16 0 obj<</Subtype/Link/Rect[72.0 670.8 107.4 683.8]/Border[0 0 0]/Dest[88 0 R/XYZ null 818 0]>>endobj
+17 0 obj<</Subtype/Link/Rect[108.0 657.6 186.5 670.6]/Border[0 0 0]/Dest[88 0 R/XYZ null 448 0]>>endobj
+18 0 obj<</Subtype/Link/Rect[108.0 644.4 199.9 657.4]/Border[0 0 0]/Dest[88 0 R/XYZ null 223 0]>>endobj
+19 0 obj<</Subtype/Link/Rect[72.0 618.0 211.8 631.0]/Border[0 0 0]/Dest[94 0 R/XYZ null 818 0]>>endobj
+20 0 obj<</Subtype/Link/Rect[108.0 604.8 234.8 617.8]/Border[0 0 0]/Dest[94 0 R/XYZ null 428 0]>>endobj
+21 0 obj<</Subtype/Link/Rect[108.0 591.6 190.5 604.6]/Border[0 0 0]/Dest[94 0 R/XYZ null 234 0]>>endobj
+22 0 obj<</Subtype/Link/Rect[108.0 578.4 209.5 591.4]/Border[0 0 0]/Dest[97 0 R/XYZ null 628 0]>>endobj
+23 0 obj<</Subtype/Link/Rect[108.0 565.2 217.4 578.2]/Border[0 0 0]/Dest[97 0 R/XYZ null 460 0]>>endobj
+24 0 obj<</Subtype/Link/Rect[108.0 552.0 344.0 565.0]/Border[0 0 0]/Dest[97 0 R/XYZ null 297 0]>>endobj
+25 0 obj<</Subtype/Link/Rect[108.0 538.8 292.5 551.8]/Border[0 0 0]/Dest[100 0 R/XYZ null 675 0]>>endobj
+26 0 obj<</Subtype/Link/Rect[108.0 525.6 202.4 538.6]/Border[0 0 0]/Dest[100 0 R/XYZ null 581 0]>>endobj
+27 0 obj<</Subtype/Link/Rect[72.0 499.2 210.3 512.2]/Border[0 0 0]/Dest[106 0 R/XYZ null 818 0]>>endobj
+28 0 obj<</Subtype/Link/Rect[108.0 486.0 180.4 499.0]/Border[0 0 0]/Dest[106 0 R/XYZ null 415 0]>>endobj
+29 0 obj<</Subtype/Link/Rect[144.0 472.8 336.5 485.8]/Border[0 0 0]/Dest[106 0 R/XYZ null 333 0]>>endobj
+30 0 obj<</Subtype/Link/Rect[144.0 459.6 244.2 472.6]/Border[0 0 0]/Dest[109 0 R/XYZ null 659 0]>>endobj
+31 0 obj<</Subtype/Link/Rect[144.0 446.4 302.3 459.4]/Border[0 0 0]/Dest[109 0 R/XYZ null 564 0]>>endobj
+32 0 obj<</Subtype/Link/Rect[144.0 433.2 263.8 446.2]/Border[0 0 0]/Dest[109 0 R/XYZ null 432 0]>>endobj
+33 0 obj<</Subtype/Link/Rect[144.0 420.0 209.8 433.0]/Border[0 0 0]/Dest[109 0 R/XYZ null 191 0]>>endobj
+34 0 obj<</Subtype/Link/Rect[144.0 406.8 241.2 419.8]/Border[0 0 0]/Dest[112 0 R/XYZ null 706 0]>>endobj
+35 0 obj<</Subtype/Link/Rect[144.0 393.6 278.7 406.6]/Border[0 0 0]/Dest[112 0 R/XYZ null 585 0]>>endobj
+36 0 obj<</Subtype/Link/Rect[108.0 380.4 165.8 393.4]/Border[0 0 0]/Dest[112 0 R/XYZ null 465 0]>>endobj
+37 0 obj<</Subtype/Link/Rect[144.0 367.2 331.6 380.2]/Border[0 0 0]/Dest[112 0 R/XYZ null 383 0]>>endobj
+38 0 obj<</Subtype/Link/Rect[144.0 354.0 309.0 367.0]/Border[0 0 0]/Dest[112 0 R/XYZ null 288 0]>>endobj
+39 0 obj<</Subtype/Link/Rect[144.0 340.8 284.6 353.8]/Border[0 0 0]/Dest[112 0 R/XYZ null 193 0]>>endobj
+40 0 obj<</Subtype/Link/Rect[144.0 327.6 254.9 340.6]/Border[0 0 0]/Dest[115 0 R/XYZ null 782 0]>>endobj
+41 0 obj<</Subtype/Link/Rect[144.0 314.4 208.5 327.4]/Border[0 0 0]/Dest[115 0 R/XYZ null 615 0]>>endobj
+42 0 obj<</Subtype/Link/Rect[108.0 301.2 173.1 314.2]/Border[0 0 0]/Dest[115 0 R/XYZ null 521 0]>>endobj
+43 0 obj<</Subtype/Link/Rect[144.0 288.0 223.4 301.0]/Border[0 0 0]/Dest[115 0 R/XYZ null 440 0]>>endobj
+44 0 obj<</Subtype/Link/Rect[144.0 274.8 308.4 287.8]/Border[0 0 0]/Dest[115 0 R/XYZ null 228 0]>>endobj
+45 0 obj<</Subtype/Link/Rect[144.0 261.6 342.0 274.6]/Border[0 0 0]/Dest[118 0 R/XYZ null 584 0]>>endobj
+46 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
+]endobj
+47 0 obj<</Dests 48 0 R>>endobj
+48 0 obj<</Kids[49 0 R]>>endobj
+49 0 obj<</Limits[(1)(sum.shtml)]/Names[(1)50 0 R(1_1)51 0 R(1_2)52 0 R(2)53 0 R(2_1)54 0 R(2_2)55 0 R(2_3)56 0 R(2_4)57 0 R(2_5)58 0 R(2_6)59 0 R(2_7)60 0 R(3)61 0 R(3_1)62 0 R(3_1_1)63 0 R(3_1_2)64 0 R(3_1_3)65 0 R(3_1_4)66 0 R(3_1_5)67 0 R(3_1_6)68 0 R(3_1_7)69 0 R(3_2)70 0 R(3_2_1)71 0 R(3_2_2)72 0 R(3_2_3)73 0 R(3_2_4)74 0 R(3_2_5)75 0 R(3_3)76 0 R(3_3_1)77 0 R(3_3_2)78 0 R(3_3_3)79 0 R(sum.shtml)80 0 R]>>endobj
+50 0 obj<</D[88 0 R/XYZ null 818 null]>>endobj
+51 0 obj<</D[88 0 R/XYZ null 448 null]>>endobj
+52 0 obj<</D[88 0 R/XYZ null 223 null]>>endobj
+53 0 obj<</D[94 0 R/XYZ null 818 null]>>endobj
+54 0 obj<</D[94 0 R/XYZ null 428 null]>>endobj
+55 0 obj<</D[94 0 R/XYZ null 234 null]>>endobj
+56 0 obj<</D[97 0 R/XYZ null 628 null]>>endobj
+57 0 obj<</D[97 0 R/XYZ null 460 null]>>endobj
+58 0 obj<</D[97 0 R/XYZ null 297 null]>>endobj
+59 0 obj<</D[100 0 R/XYZ null 675 null]>>endobj
+60 0 obj<</D[100 0 R/XYZ null 581 null]>>endobj
+61 0 obj<</D[106 0 R/XYZ null 818 null]>>endobj
+62 0 obj<</D[106 0 R/XYZ null 415 null]>>endobj
+63 0 obj<</D[106 0 R/XYZ null 333 null]>>endobj
+64 0 obj<</D[109 0 R/XYZ null 659 null]>>endobj
+65 0 obj<</D[109 0 R/XYZ null 564 null]>>endobj
+66 0 obj<</D[109 0 R/XYZ null 432 null]>>endobj
+67 0 obj<</D[109 0 R/XYZ null 191 null]>>endobj
+68 0 obj<</D[112 0 R/XYZ null 706 null]>>endobj
+69 0 obj<</D[112 0 R/XYZ null 585 null]>>endobj
+70 0 obj<</D[112 0 R/XYZ null 465 null]>>endobj
+71 0 obj<</D[112 0 R/XYZ null 383 null]>>endobj
+72 0 obj<</D[112 0 R/XYZ null 288 null]>>endobj
+73 0 obj<</D[112 0 R/XYZ null 193 null]>>endobj
+74 0 obj<</D[115 0 R/XYZ null 782 null]>>endobj
+75 0 obj<</D[115 0 R/XYZ null 615 null]>>endobj
+76 0 obj<</D[115 0 R/XYZ null 521 null]>>endobj
+77 0 obj<</D[115 0 R/XYZ null 440 null]>>endobj
+78 0 obj<</D[115 0 R/XYZ null 228 null]>>endobj
+79 0 obj<</D[118 0 R/XYZ null 584 null]>>endobj
+80 0 obj<</D[88 0 R/XYZ null 698 null]>>endobj
+81 0 obj<</Type/Pages/MediaBox[0 0 595 792]/Count 16/Kids[82 0 R
+85 0 R
+124 0 R
+127 0 R
+88 0 R
+91 0 R
+94 0 R
+97 0 R
+100 0 R
+103 0 R
+106 0 R
+109 0 R
+112 0 R
+115 0 R
+118 0 R
+121 0 R
+]>>endobj
+82 0 obj<</Type/Page/Parent 81 0 R/Contents 83 0 R/Resources<</ProcSet[/PDF/Text/ImageB/ImageC/ImageI]/Font<</F4 4 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+83 0 obj<</Length 84 0 R/Filter/FlateDecode>>stream
+xÚìÏsë8rÇIŠºÌ‰öŒî´üt§çyšÝ­ÚJñÙfvçÖ$ÁÊ!•S*Ç6•üÿ±~X"H€èn
+ ‚B¹ü29×$X,0X
+Ú Ñ ê£s5Ѭåà‹+›— 9Ä`™ãÅ`ž‹,öD.1?V{àÊàŠRç\uD98æY\‰c®}HÙ8媣`Jé++D91WTθš°ŒÒË TÔ  LÔ‹Š4ðZåªBÃ2t,OãQ ÔxŒsÆq‰±FãzX¢Æ#F,Ï%£FL×]w÷Ûí—×cy>|ðÛa0IwÅ÷_†ƒüxÂ[‡ÿîŠG’þÅ˃ŸßÝuoœ*=[ í,¸ö~ºªÛk[ça=xì.Bú±àæØ\ìPƒ–.Î$[q¹¸¡==·aMJ&³»XéÔœ.K™\¬@ž› É貘ÇUùÑv‡]¶cqeSÙàÕ›eN¤ܯjؤ½s’  ®|²¡ÅϼJé\Í Xd°˜ÎEùØÍÉÜZ9ƹfÂ"‚­¨\Õ FÈ1Å’È•Ï…EÛи'’ –Y¹0p7ñú¥uZj+Cg‘¼c,Òé« …«rÎX•½ÅÍÁ‘jÄ¥{.B J<—°ÒlKÃ7D=Ì:¸ÎåÛÁÉ %i=•œkˆàBf½ .Ú`Ø`¹Š¹2O”,Á W­Ç’ó ìÍПR4q‡ãÚa…„–¤8®
+¢6(®–KH·Kh%ºÖRÕÓbQ®•–ër›®Îa‡Àà ¯Çé¹ £^ >ÜÌÑ–œáµCrí´\Mçù
+.A5C#WªçÚŸòáªÕr[¬àª©fhäŠõ\盽§5Rì•K,×êZ[Éu6Ž;êÊ12BªWNZ,×:{—'%WG¤(³ÔgO@• ž«x j®Š5K5†ˆëWC6Ãq®ZÂp ÖbI…Ó5 ÊFÒâ¹Ä»Y¨¹®&EZ,ip-â\9%pžê¹*ÖšNè(;
+\n£á¬5;c+\ŒôÅq®æ"
+®K‰kvª‰@“¤¥pµ¥×qgéB™ÐdcMãº(½Ž«àôWòD@“ë¢ô:®Œ3¾êÙwCÅuQz WÍÛÃÈ0’ ¤h#!r]”^ÃUðûMÏ%s Æð2r•^Õñ6g
+ÌÃ’lì¨\g¥Ws5Ì]Œ
+㘴¶ÑR¹ÎJ¯æ½ØER2WÁ^f®“Ò«¹rfæŽÀ8f DQ)ëdÝ…~^¹e¢IàkSYŽ­\§Öç*®³Ý FÈ‘#š ”(ªT?º²k ±ÌukDŸ+?krN7Ä="à
+vt6מÍ•4™¿peM/\œ¥ùp‰'óg®úÃÉ<–k2ßÎ-‡Í‹ó·•Ü:\[æ»uîªßFwÒ²ßmµwpœOm›.Ò,²
+£®[‘"K~­lâŽÃÕ¿(—óM
+-áðì8—Ðpz®”ÃÕ¿¨7RNf™
+E2þW=/WÁá’.ªn»Ê·EÞÙ¹j—t‘趲¾Æ6ÓJ\‚Ã%_”ßö¿ž#Wé“«{œÍ%_T ö+Càª8\òE½H0?qÙL—Q\Ù8WÃá’/’gÆé>®Îq><Wï"©ÃÊbB®ƒ¾‰‡K¾HN#‚—qy#¶äÊ£îíZðS¹zýèN«i¸®oÐ4Qp¸ú=w–"p…S^Nù‡?ßÞ?oäZJ¬èžó2Ûâr{Ï\ù'×'׸V”+]&WöÉõÉõÉõÉõÉ…ãZr}rùçŠì¹Þf>ès¥”ºór=ŸÞa?bšK©;3×Cÿøšü²¡ÎæuÕ»êªHqPJÛ:k®|¾!çéw·§†u\y1Ô×áŒF~™WsÛìø9QeäÚÉÔ—Pì(É/4¬®KüuòÈȵ™‚ TSë¼;Àz齺 ®u+ M?\™j« ºã “Òdúu\©l)^¸š÷§»nÝî«ko»õʺ ®Dºµ®êºQ(¾unÚ‘À[v…².Ÿ«öÉUtv¬÷7ïÒqY·äsM]Õ2¿ú®òöNå“+ª›v^Ȭ©K㊯ۧoƒ8ñÆ•u—áòžÒߌ§©KãJ®£0qÙÄóÒŸá¦×VËIÚªº$®Õõq¼=™t®êöÓ5ä$mU]WÚ}tk;.CúáõÏÍÝ[ÙtĪ윑Ò×%qÏ]ÓÆ+W¢ûѧ“T<¶ÔçÊ/ƒô8„wÞ¸2]:ÎeO¹éÀd#©;x®ýåóŽŸ|ðÆuŽš¸1‘†‘¶.‰«¸ÈÐÑÒKo\—½Îûƒ:À:eR¼Ë¹¶.‰ nÙ¯qëë¢Z{þ⇬ã‹õu•\ÃÄÎEu¯G÷å«“Òkmñîm®¾X_—ÂU_ `ü`;®±}½n=é½ÄÕ0‹V[—ÂÕ\~<ºeK®±}ØByŽCš—ƺ.]_ˆ¾ç²Ú_ÚÌÒ|Õ¢¯Kàj/Žùô[\½,Ú¸ôdŠ¨Ká:;æ“[öÉÕyÑ–L¡ú’9]]
+×Ù1×'ï“ëý ì —J•l:ZÇuvÌõ©¾—1ïë9Sš\ùPÔuñ~ùäËãÿc—‹|¶—‡á¤¦ÒLrTu \ÕÉOnÙ?×­µ«^(u@Õ%póÉ-OÁõ.
+‰vÐŒÕ%póÉ-[raß-Z‹ÁX]—x÷b멸n‰×f®^]×Ù1ŸEr”Ë&ûø*™ìŠµ\cu)\o&˜4·õS/\²{ãb§®.…ëM2âúì-¹JÜú¡k¬.…ëØÓUt}Q7®D²­x”KW—Âçð±‰ËæÜM.ý9Õù±º®ê|²&±çÚO¿6ÝyÈJË5V—ÂUßœº7®ªÛŸûÞmz«Kájn³Q.›sˆçkãï·M­–k¬®!ž—Þ¶%ä]A ®qö¶ ©Ÿ,ŽÕ¥puÞ ë«™«õ¹Fê’¸r—Õ¹ìf¤jŸk¤.ƒ«´çZãÒmú¡ÿ VÑ×%qƒßxàjô
+3àj¸ëQ2\ŸÊ(—Õû7䵘_ÚQ.}]—îý¬n¹:}l \Úº$®úêÓǹ¬²Þè!Ón”ØÔµ*–ïí¹Ê×ëMóU|–ïY
+¶X¾ë“kz.óë–—ÉUüÓr•‹ä2/pÉe3aþäšžËjb¹h®õ"¹Ä?-WºH.ólõA¹’ere0ðµü>ŽO®¸ö0ðÅ}Ïn‘\ðA¹ª Z~¯Ù\Åô²×å÷ÐÍULMÞá¾70¸@ªrÃ\ e’ºÒò{9ç*Š+[\Àa
+%°ß{ZÀ‘›ì ù=Å¡­´e&=@~¯thG„â‚¥Âèo‘ßoXÀQã¸÷}ô•±
+-V裥ÈaÜ媗#ˆ(Â-ôDˆ *âƒ-ôéBäpÝåB} Â(»‚-ôÇ¥€VÏP„#C̾:\ÕB")£œ· Å }ºÙHd.„Ð'‹TæB,ÝÄ‹u+_†pd8
+h)‚¸xí\Åú,(ÖCéÑg­¡% ÇÜ ýŽhIÂ1ó
+œéöÔ1GNÀY™8樼A:©Ôï˜gB©œÇ ÜÏ™.H,8Ž¸Ãtºc wàÊêd†kÍÁ…Tú‰:,c=càÔ4Æ €?R§é°ŒÖÛNÓaÓ™_‚& :C¢Åqí±`»0ºk…äªé0‘qƒph­ ±÷UKó„ªY.´v†èuâ,øV­!zÕzìÓÝ ¹ð†èQ:j ›ÖÖãrfWŠØ ¬OKüñÍPË%¢¹-±±’.h­ Ñ“&æV>¬­'BõÊXZ?Þ¹±´°öõ^†˜È,e \<1÷3–½í#×½{&ܸ¥rU¤s©ÍJÉ\"š Ldö
+\EiW°/)áøf08Ô&[õ cœFwžÿf<[l2êR6W›qÀúßtŽ*OôÛø\‹+Š¿S;‹añcq€‰‹ÙaQt_zî¬ñ¹‘‘«ˆ¸ÿ¥ó/9k·6\"â—{”56¼OßYqYtØqœ=šÌñ…Ie˜>˜¹l:ìŒöªÿ짌ý¹K.ê4Li¯ƒ~/ÛÌæqµ¶\Mä¦ÜÝo·Û/¯oåe»}È,?mcÍå¢ÃœSþ#†KȵqÀe'‰³tŽ+¼Û9á
+®ÃÌK_8.±´îBrvm¦(ˆ=$W›…ÄupÇU/«»Ð\¬•O_ºäj‚áÚ´.¹‚ÑzÜöžKdKÑxw gÑ q!Øý^
+—XŒÒ¸°DôÖ!‰kvKÄgиš…X!•kæø—°MäšÕ)¹/T®9½3eû‰Ê5c`OJš#sÍ6Ähi/t®™†1±ŒÁ5Ï#îí2¸fbÔ,%=pÍ`sM®ôT9×ÄÚÁÈ
+·¶øhß"7\o¶è®Ë’CW+Âé,—\ŽºìþІÆÕŠ'küâª-.¹ø©ÕªGw-qËe‘^HÝž“‹Kæ–ʇìþ‹ë6øà"æÅÇ[ï¹ðÃõV^qÙññýw/·÷Æuê5ƒAÞm_}ÝÛ'ש۞¶wê~z|õy_ß\çŽ;žpØ>ÜËývûøúêý–“pÍPàÿ
+ä
+š‚jûº¨ Úñ©­Úë ×·I÷Kuõš
+endobj
+84 0 obj
+6275
+endobj
+85 0 obj<</Type/Page/Parent 81 0 R/Contents 86 0 R/Resources<</ProcSet[/PDF/Text]>>>>endobj
+86 0 obj<</Length 87 0 R/Filter/FlateDecode>>stream
+xÚ+ä2T0
+ár á
+ä
+endobj
+87 0 obj
+31
+endobj
+88 0 obj<</Type/Page/Parent 81 0 R/Contents 89 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 4 0 R/F8 7 0 R/F9 8 0 R/Fc 9 0 R>>>>/Annots 12 0 R>>endobj
+89 0 obj<</Length 90 0 R/Filter/FlateDecode>>stream
+xÚUËnÛ0¼û+=9@¥Jò»·æ }¨±Sôà -Q¶JTIʆûõ’2ì¼Ð"€‰ÃÝÙÙÙõï^Lþbš$4SVõ.—½·3J"Z4Œ‡á„Æ“!-ó~ªxÁ2~±üÄâØ" É$L,j¹-5iY˜=SœZÍ•¦ŠÕ-”s©rÍ5m垌´§d¶œ®dUÉš¾ÎRªÊÚ”õ†mxE«þ»«‡tñnuA?«.ãг˜Q<±,"
+’(t,»kßv\íJ¾ÊÖâÆ·üGÞFÉ] ÆĨ‘Ê°µàøÖa;pE…Tîî*I6\1w¨]
+endobj
+90 0 obj
+847
+endobj
+91 0 obj<</Type/Page/Parent 81 0 R/Contents 92 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 4 0 R/F8 7 0 R/Fc 9 0 R>>>>>>endobj
+92 0 obj<</Length 93 0 R/Filter/FlateDecode>>stream
+xÚu±nÂ0EwÅÃ7vL0+ˆn¨A1tébÅǨ¿_ÂX½·¼«sôn„¡ˆÃPViÍ…,y{7` ª—”£’‚J¨6ûâÕ uŠ€¬DŽ&h×jߢö½ Öãã'ôW7<Ñ—«@ÎʨûG$¢hy´æÜ»ÂÑ&k¸Øõúq~Úý³!ÇŒ–’Š¤\në͵ ¿Ú[l묵»ëïÔÊY5§Sä3^$˜§Lˆ9MßÇ»ö¶ÓƦt¥È†ü€×H¾endstream
+endobj
+93 0 obj
+200
+endobj
+94 0 obj<</Type/Page/Parent 81 0 R/Contents 95 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F0 3 0 R/F4 4 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+95 0 obj<</Length 96 0 R/Filter/FlateDecode>>stream
+xÚTMoÛ0 ½çWp‡x±“%ÍŽ-ÚÛ†nqwêE±éZ­,¹’œÀÿ~¤d÷#]‡"@I|äããc'),è“Â&ƒåŠfržO¾^m![@^Ñ÷*Ia½YA^N3˜Ã“ú|pm¥ö|ØõÎcó%¿'Ü
+Ò”qóœ/³M’16¯¥ƒ¢­G ®6G½é€~€7àº}#ý ;´ý „.¡º@-W{³wWʪB‹tîѺ$ÖÝBºáº ˜¯ÉWÜ…œá•Tè 2ö‰ôk¶„ÊÖIèñâæzGÉÍA–Ù_‡fcðn§º SÕ¾Î5½ýøŸ£}@…ý¿ö-¤Õ,LÓÞ%›¡{5Àý3•+Ñ)Ï)ˆwºŒúz€Ñ!ÈE¾·Sê™tæ{Õ¢¬¢øâ€ñÁh$|wß9± åú¿ì·L•,2Ï‚PŸAµ— ÂåÏüò÷‰Bì «±ÿËc?ž(̧Ó%Þ³L@bõ/|Á”˜*dtô`±ðŠ<%u¡º’¾6Îï
++[%q‡1œ4g›
+¥ÈŸ'³8~œHeMC™$Ê+ÚVÉBxi4U§³c†9WRã Žµ,j<ð
+¢Ú
+K2Ò“”WÒ{k,­¸yYÙ°\[£?û ‰Ç†hRuP’Ô¢©>CG†3ær¼Y3rˆ?Ù´q"ƒÏž5\}KÖú“[-’-!(vÉçË|òkò±¯£¾endstream
+endobj
+96 0 obj
+637
+endobj
+97 0 obj<</Type/Page/Parent 81 0 R/Contents 98 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
+98 0 obj<</Length 99 0 R/Filter/FlateDecode>>stream
+xÚVÛnÛ8}÷W ²(*­d)–³@Ú Ý}ضÞÚý
+xú²xúöûâSˆc²Ð<O”Ì%‡›@߀ÒN( Vó\¬·àJ ²S l™tàXÎA%¬µm„tÜØ÷ÀdƦ®•qÖŸç1CØu¼&,qÒÂ-øŠ5•ëÃ2WÌÚ°³pªÖ„€°eÞ»½‡ÆòhJGmöQ—ým¥鸅\Õ5&ñgoØsŒÓžQ¢²‡¸—¬æW¹Å\ZH³«Ì/`2Ì.‚º‡8@¥Q8%Ï9wNÈ5Ì:ǯ˜=¼øO˜"ÙÛiTVž®Â²S'Õkƒ¡Ã–l³Â" *þ
+à˜ HÎ ÂßjË7T8 cxçp¤ 'j¼q[réuHžD`^2¹n«ÝßGÑhfœÈ›Šµtx'†Á=:ÌùDSo©ûÍå²ÀªJmý͈T£O¸i,Ñß
+Cõ¸¬=Ï™æô€_*ŒñÝÑSÍ Á>¤ƒÂg½î™ìŠÞÉå×â<-FQáø¡˜>¾ýi2qe^PCšEX²ö©‚y;œ^Ä”dÓ1Î6LTlYñf6ÌìPRšË‚(¢v)«È'›,˜)ªoÁmnÄÕ!$œG¼‡‘ß…Ù!â#³’ìÐ?–LÞij;XGõmxÔv­ûÐy³N†ð[Fh$àQiœ´g[ï£råÛ&ØOŠ´d›ÃÞé;je¨³˜Döqr+½£Žg^—5*A6uûŒ^™F§büí¢ý™‘Ú2æ«.•©±Ûv]â“>M©ÜiâUÅ꣛B¨%kî^~èp¼¼EÇÿö°0 [›Ppá§)¹îfÀ?–<ÿ¯1ý”ŸãFÐ ÏFÕþü±•Bðü¬þ^´[œä¨ûÈb7¨‰çüY-÷
+/?Û,káú¢ô¼D°ßj^c¯K2ˆñžRŠCšŽ³,›œ±vñ]6=g• VI6ž¦ã+K”O‘tI R;ŠüÊܯ;ØŠª"zÚVxÒNµ-E^¶oUßÈýf壱܉ ßßoôÍÑkòº4û}pO:Ó.8IÂ;˜ÜÇ8©H<ßgs˜«•ÛÒåß-ÁøÌdÃ*r âÉ=ZÙ8"ã”Î’l&]Y-p"ÿ;úé)+„endstream
+endobj
+99 0 obj
+956
+endobj
+100 0 obj<</Type/Page/Parent 81 0 R/Contents 101 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
+101 0 obj<</Length 102 0 R/Filter/FlateDecode>>stream
+xÚ•T]o›0}ϯ¸‹4)“bŠ ÒÇuí´jݲ†jÏ@œB ¾Ì6òïwmHº¤ËC…„Äõñ¹ç\þŒ8øôpˆ˜EP4£ÏéèâƇ¤‰’Ø ]OZUI#|úùV¸«äã>ȸ÷)}¢û!pn 1° òBËðSAÐ%]4¥€'Ì5drí>v=…N 3»ã1kÇ€­©PêËžþµ<8êP·ÚdkáúGz}o¡>0î{‰…,û ŒSâR®Q@qGgpÁÇçqr5; fq„Á ì]C[
+A½Ìy@.jH±Ø›_5¬¬Ù|úœõËn‰ßº¤AYTGÞ
+endobj
+102 0 obj
+645
+endobj
+103 0 obj<</Type/Page/Parent 81 0 R/Contents 104 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F8 7 0 R>>>>>>endobj
+104 0 obj<</Length 105 0 R/Filter/FlateDecode>>stream
+xÚ-‹±‚0E÷÷wÔ¥¨Wn&Jœ+¾
+*%¶%þ¾V̽ÓÉ9/H¿XÈøº£­¢ù!‡PBnf+¬³ê:ÙUE‰²7á­£òì<ŽÚú9UwJF7ùË2²,_bL®­½!4ŒÂµ6°Ct<Œë»?ó%F{E'ú
+endobj
+105 0 obj
+133
+endobj
+106 0 obj<</Type/Page/Parent 81 0 R/Contents 107 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
+107 0 obj<</Length 108 0 R/Filter/FlateDecode>>stream
+xÚUÑnÚ0}ç+®:M¢Ic'$dÒº•M“ÚµÙ/n0Å“c{¶)Я¯‡([Õ‰'tï9çžs¯áOAâ>
+ iuÓûTõ¨æ€Ñ0N!/2¨fý"˜X"fDÏàF3a©†ke™æ´úí` äaQÀE).bì¡Õ‚¨DyÈŒšZ³[jÀ.(˜-£êe`tEbh
+äž0Nn9…Õ‚ŠÐÇĬ˜]´A<éÄû\íOÓ§°ß“@„Ò0Wú°½–Mã &…PѲ$yÐW*¨&ü¸{ׇó8 ¾)Ì%çråÞ:#JñÍÂ9Ør¡È9̧;êYÇš¦qîY'”ÓºEy÷WtÆLØ@åÞ.LäR×ôŸƒDþ¸Þ<œtÓ¡6,¦-iyí–÷Lj0-õ‡m¢¥§v·á–ù=pOÌ—ÔúzC‚4Æß«ñOôèÂ<†\-¹e7K­¤¡¯Ãõ |¥‰0ʨ7ÿƒßÕlçðÛv2~¾Ýç8MÈÓ„E…4{Šº¯n>üî1 àvi¡‘Æ‚Y*%µm«/Njk©»éi¿&.6÷æ »lÎj"ìô´[YÝ ‹G®ûiÚ)΋ײ8|üš@·Øi×®ÂD½ð–Ü}`”¯qQ6Íôt_pw£jw„ÿEÌU¶ZÙ¡V:Ì߬už½ú6¹†ó¬•Á…óçå¾T²Æeñf©Ï×W(9jëJ`,î)—Š:ÙÒ¹Ëb„‡{ªZ£døfÕ‹Ëã/.w%Gqž;Í4ݓĉsŸ<KŽž~ÖÛ‹IQ}íÿ %qéæt­…ÿ>®z?z¶€×Õendstream
+endobj
+108 0 obj
+636
+endobj
+109 0 obj<</Type/Page/Parent 81 0 R/Contents 110 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
+110 0 obj<</Length 111 0 R/Filter/FlateDecode>>stream
+xÚVÛnã6}÷W Í’Ö²]_ÈCw›š]w­¼å…–h›…D²$×ýúÎ’ï^;AÃáÜÏœòŸN
+]üI¡?¤ß¼ê|Î:Ï!M![@oœô`8$cÈŠû—Þp²¿Q¡Û(Üg†I«™á2ßÑ A ûR¨x!¸æ  XU›œ“6\â´/ÄzÖš›Ó þ4£OgØæM>ÿTës>ýñ{}>Õ¥ÓÚheù©k/u¿;mf–ü Ó,î î^ y#?
+û®Æ½a2 ÷ÙŠË]ÍJPÚ %-ØZke/
+W§6ô<®õƒçÐÉÌzoíŒ&‡ÿ‚.YŽ©èA¤i竼®ðjl:®ä½fî˜ÙPƒôøò<íö~Š!"èùOº{7EÅÜÕ ¼«&= ظ"E°~ᤠ÷ ÌÔ­é¹ôliž˜Ä•n¥Ã jÇ£^—”ÇtÖð¨{å5ò˜uþêüíqïendstream
+endobj
+111 0 obj
+952
+endobj
+112 0 obj<</Type/Page/Parent 81 0 R/Contents 113 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F0 3 0 R/F4 4 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+113 0 obj<</Length 114 0 R/Filter/FlateDecode>>stream
+xÚU]oÓ0}ϯ¸š„Éâ4ô©/­Ú ÆXS$=×i ŽlgÿÛéšVÐ&›ò”äžsï¹ç^ûw€ ¶‚Qƒ!2˜eÁõ2† d¹û2¢²ÍÛ7À+%ˆº| *¬«i9ãTà’Ââ6[Ü¿Ë~1„(ŽÆÿ¤Ý
+Ë Š»fœB6Xý²fúyrƒËÃ\*E‰ëÍ«œÙzr éåM½¾Æ44Šûšrð2C^ãÈ?–Àñ
+ùâž+4G€Ý§$q¸‚‘솑‚jŸLßßÌ@W”°œìºÕ<Ú{•îõgôÉÀWßÒó«æJÉ%çrç\i Ѐ«Šÿ±ém½^žwÌѹþé~3uë7ó+Ll5ÜÙ/Ÿ).Öãæ‚Tìt*@SÓô@ˆIKl—˜%îÇ‹’žƒs6¸ï‡æ²üÏÌžý•ó>ʹçì+ÚQŽ{j>û"ÉsÉë²cê¼ËM\·^?|ûèN‹÷¤½]¾½ï«GVñ{K{ì|}·‚•ÌÍ+
+kí&ð 5æ¢áÄƆ#{\¼üÒqMÀMÜû" ¾W,u6endstream
+endobj
+114 0 obj
+607
+endobj
+115 0 obj<</Type/Page/Parent 81 0 R/Contents 116 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
+116 0 obj<</Length 117 0 R/Filter/FlateDecode>>stream
+xÚ­VKoÛ8¾ûW
+pP[•l×qvÑC6Èb{h7Û¨·\hyl1¡D•¤êxýÎ’Jc»À “óüæ›~ï%Ó'ñ”¿YÑû#í½ÿó
+’ ¤Kº™Î’hé¢ÎÉr.G¸+„Ϭdi/ÒGR˜@’…áhMXá‹6…Pjã5*Ö(‚ƒ`ÑÓþ.fJÒYÐË ndéÐDj‹ÇÚº—Vj‹þðÍPs ä9‡PYl¨pé>þªÆ7–ñÊC#WùOŽ®¶‡¢\ì.æÚ9]4w +'ui 9ÇpÅ)lÑÏø-¨
+ZÍXJ…¥(n¿¤·_Û`ãhöBi/¼_ÐÚFÿ :û‰V3çêí˜0ô¸4\H©NázÚ\÷½…C•>1cUX:Ö×6Ôè[ib„ýPdyøÃ2ÉûËÝÒ‘6GãE›;ÇãhÊAÜ¢ðî˜[ÄäW‰›R YÞS±-=T5‘T@ŽbD %áÏÄ ¡1E×Òå;–u1G3€G='Bxè׶n{£CÞÓ‡‹@E6³#¸VVàÆŸÞ¼{O¸Yk³ý”s³A/Ó…GRɃˆtBÉLþ‹‹“Þe~.7Žjør\nY1i´? Ïß¡©Ž–c©•Òk?M ‚¨*‚oc†ßIoÝÛ×YpŸ
+Íóœä +42¶¢’É
+endobj
+117 0 obj
+1008
+endobj
+118 0 obj<</Type/Page/Parent 81 0 R/Contents 119 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
+119 0 obj<</Length 120 0 R/Filter/FlateDecode>>stream
+xÚ–ßOÛ0ÇßûWœ¦D²8?ÛI<@ÅÒŒ–‡I¼˜ÔIM“¸sºî¯ßÙN›h‰&¤JØ_ßÝçξËïÿD.x!Äùàb:øòÍB`šàN8ôí!LgÇeçTò¿¬„jÎ žÓ"e°õç,ƒ’1Xñj3ž$L²¢‚XdB–_O¦Ïh/hìϵ}°ÜÑæ­ä)/h¦D‘k{èWç5;³ü@-ŽüÝE³v98¶ƒQo¸à ‚†CbpÛÉ`òžxèÚ£FŒxþA±Fƒ†¶³#¸*Q¡á.Æ{Ïf*X‹¸«å2cjM»Ôk¿X–‰•%¤ÊåGlAË}Ù´¸/›¿ekˆH¤B½’Œ]¢&út³Ór]d5k×A‚^ûBiq_(-ÞŒBs« C—ëv[Ž–Hã[ë­ú
+ôM<S!4‚cMBÅ2öÂK4ÚL1ì'*Lœ“–«£üÙÞž±‚æ .L/ï7ÓuÔ9Ùû`› K»ÞI‘Ù›íãÆF÷Ð1P™Ö¹²].Y̾á:;©K¤È±•À:;6œ¿UU¤¥³:F#ôUâž2/,Z̬՜W …¼¨NÿÉPx¡x‹• ´Ž_ øÙÓÝZ”ÀþT’å,[cÝ*V”Ì6(ÃÍŒ Gv
+endobj
+120 0 obj
+779
+endobj
+121 0 obj<</Type/Page/Parent 81 0 R/Contents 122 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F8 7 0 R>>>>>>endobj
+122 0 obj<</Length 123 0 R/Filter/FlateDecode>>stream
+xÚ-‹±‚0E÷~Åa
+ò³#ßYƒ*Èí`]ÂjÁe¤V-ç“dwö',endstream
+endobj
+123 0 obj
+143
+endobj
+124 0 obj<</Type/Page/Parent 81 0 R/Contents 125 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 4 0 R/F5 5 0 R/F8 7 0 R/F9 8 0 R>>>>/Annots 46 0 R>>endobj
+125 0 obj<</Length 126 0 R/Filter/FlateDecode>>stream
+xÚÍ›ßsÛ6ÇßýWð±9+
+³ž(ê.gó;s…”`2Q¹ì†aþ-ì„`Þ8Í`þÛ×ûC,]1C+¡$id€Éjè#U?÷6¬êÉ8Á|ĸs›¨BsêPäU߶uwsùŸmgwf…øëå9'8‘b]b5aLpB¯Ž’ÁPFÝ„[ :"‘&8Šúõê(Ù9ª‚óœ
+ÏñP ˆ·HzŽV=­3X9înħ¤¤
+j¹Å`bjvÍfæ…@]p&ðœA¹$›0wÜÃO.Îì]s³¬¦
+p΀pfшÀ˜»åpŒÂÈÀ µ? t4/¾ÍÑüœ•ÆÄ$2¸.Ûb
+TžÏm±¹?Ôn
+r§9ÆõÇÜ!Àq#‚½¶MgŠ7³ ØæòT§‚åL°¥Ä# {Vl±`KµZz^Ê
+[,B…rÙ¢|8Òl¾
+hÎ@-©Ç˜;¨„ÂÈ€`¯‡íÝý¾k­ uv²¢¸š vV²â[,ØYuTñ1Ò0œæ ªÅÅÇ3>ÅF{[·m?4*FœùîªXw©”yɽ_ßâ¥ö¼½MÌÅWÔð§4©Î1Õ­›ß#§!Ì·M*8Ÿ
+@Ô?}n¢Ïa|¬¼SE'V}¶Åämvêy;ÊÓœ—ÈÛÁr1½4g@°íçè±#`­^Nxd.È´ÌN1á)ZÈ"b]Ý×C½Ù7C¤‚äÃÔbœWª‘1?ÄËÝ ÌÞ³!L ú¡ÛÜÛ³¼ÒÜàßD¼s”¥{«ÛbTâù9ʃ³È
+»ŒbzUd€¨QmÕ·ÞbÌ©Nd¼„YXB‰ü¸ÊÑPœæ òå%”ƒ¡0l¡öåðÈ€x ÔG,%íz°È€XWýîÐR^¬^ì]º™YW•bµ¶ü¬«*=ª–ivVš3H×r#LÃKd!Œ [·ùÃa„šÔí]=Ümiž²—yмÝ(ff¬ —7íƒ l±+…;2Söeî¹Æ3¥bÂ๣± áé·ƒ‘ÙšWå9>K²k?}Í«ò‚}Õ sÆhËEýÅÓ'?gö²ö¬¢2÷>(œ
+`‹)*ÿM½›Y¼7ž*U!Ëè÷¿w†½œÐ…(2Ðã4Úƒ›ï6f㜺?KÐRZŠT›'þ~óóáqîÏàæ…·v8äÌc#ïц]Cû×!z‚çÝñe‹rCûôÝÚ¬,Ÿ¾¿pQzbG“! 1š²™7Œ°o YhàXu«½7”yù4ldàX^¦ßúuÙY*ÿú©¤Ó¡¢—O—ëÐñP'œç Š%YŸ¹cžžÂÈ€`×õþ0اG1ñ(ÆÄÂ;d’1?A•Ö1=rìv«)Ò2¦Gî »\I8`‘{±8¦Gæ /²‰q¦ÇM.Ëë±øëwÒþ†¿H1þ¥SIo\JaŸuUô¬åêã‡ë亿ÝÿVMòñNÞÕÝ¡Þá_ dÉe;¦-…à¿û §endstream
+endobj
+126 0 obj
+2386
+endobj
+127 0 obj<</Type/Page/Parent 81 0 R/Contents 128 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F8 7 0 R>>>>>>endobj
+128 0 obj<</Length 129 0 R/Filter/FlateDecode>>stream
+xÚ+ä2T0
+áÒw³P04TIS04³Ô3U072PIÑp VÎO+)O,JU-N-*VðMÌ+MÌÑ ÉâÒ…¨Õ…*ÎÌ º†pr
+endobj
+129 0 obj
+94
+endobj
+130 0 obj<</Count 4/First 131 0 R/Last 143 0 R>>endobj
+131 0 obj<</Parent 130 0 R/Title(Table of Contents)/Dest[124 0 R/XYZ null 756 null]/Next 132 0 R>>endobj
+132 0 obj<</Parent 130 0 R/Count -2/First 133 0 R/Last 134 0 R/Title(Preface)/Dest[88 0 R/XYZ null 746 null]/Prev 131 0 R/Next 135 0 R>>endobj
+133 0 obj<</Parent 132 0 R/Title(System Overview)/Dest[88 0 R/XYZ null 387 null]/Next 134 0 R>>endobj
+134 0 obj<</Parent 132 0 R/Title(Document Overview)/Dest[88 0 R/XYZ null 182 null]/Prev 133 0 R>>endobj
+135 0 obj<</Parent 130 0 R/Count -7/First 136 0 R/Last 142 0 R/Title(2 - Using the Printing System)/Dest[94 0 R/XYZ null 746 null]/Prev 132 0 R/Next 143 0 R>>endobj
+136 0 obj<</Parent 135 0 R/Title(Submitting Files for Printing)/Dest[94 0 R/XYZ null 387 null]/Next 137 0 R>>endobj
+137 0 obj<</Parent 135 0 R/Title(Choosing a Printer)/Dest[94 0 R/XYZ null 193 null]/Prev 136 0 R/Next 138 0 R>>endobj
+138 0 obj<</Parent 135 0 R/Title(Setting Printer Options)/Dest[97 0 R/XYZ null 587 null]/Prev 137 0 R/Next 139 0 R>>endobj
+139 0 obj<</Parent 135 0 R/Title(Printing Multiple Copies)/Dest[97 0 R/XYZ null 419 null]/Prev 138 0 R/Next 140 0 R>>endobj
+140 0 obj<</Parent 135 0 R/Title(Checking the Printer Status from the Command-Line)/Dest[97 0 R/XYZ null 256 null]/Prev 139 0 R/Next 141 0 R>>endobj
+141 0 obj<</Parent 135 0 R/Title(Checking the Printer Status from the Web)/Dest[100 0 R/XYZ null 634 null]/Prev 140 0 R/Next 142 0 R>>endobj
+142 0 obj<</Parent 135 0 R/Title(Canceling a Print Job)/Dest[100 0 R/XYZ null 540 null]/Prev 141 0 R>>endobj
+143 0 obj<</Parent 130 0 R/Count -3/First 144 0 R/Last 158 0 R/Title(3 - Standard Printer Options)/Dest[106 0 R/XYZ null 746 null]/Prev 135 0 R>>endobj
+144 0 obj<</Parent 143 0 R/Count -7/First 145 0 R/Last 151 0 R/Title(General Options)/Dest[106 0 R/XYZ null 374 null]/Next 152 0 R>>endobj
+145 0 obj<</Parent 144 0 R/Title(Selecting the Media Size, Type, and Source)/Dest[106 0 R/XYZ null 299 null]/Next 146 0 R>>endobj
+146 0 obj<</Parent 144 0 R/Title(Setting the Orientation)/Dest[109 0 R/XYZ null 625 null]/Prev 145 0 R/Next 147 0 R>>endobj
+147 0 obj<</Parent 144 0 R/Title(Printing On Both Sides of the Paper)/Dest[109 0 R/XYZ null 531 null]/Prev 146 0 R/Next 148 0 R>>endobj
+148 0 obj<</Parent 144 0 R/Title(Selecting a Range of Pages)/Dest[109 0 R/XYZ null 399 null]/Prev 147 0 R/Next 149 0 R>>endobj
+149 0 obj<</Parent 144 0 R/Title(N-Up Printing)/Dest[109 0 R/XYZ null 157 null]/Prev 148 0 R/Next 150 0 R>>endobj
+150 0 obj<</Parent 144 0 R/Title(Setting the Brightness)/Dest[112 0 R/XYZ null 672 null]/Prev 149 0 R/Next 151 0 R>>endobj
+151 0 obj<</Parent 144 0 R/Title(Setting the Gamma Correction)/Dest[112 0 R/XYZ null 551 null]/Prev 150 0 R>>endobj
+152 0 obj<</Parent 143 0 R/Count -5/First 153 0 R/Last 157 0 R/Title(Text Options)/Dest[112 0 R/XYZ null 424 null]/Prev 144 0 R/Next 158 0 R>>endobj
+153 0 obj<</Parent 152 0 R/Title(Setting the Number of Characters Per Inch)/Dest[112 0 R/XYZ null 349 null]/Next 154 0 R>>endobj
+154 0 obj<</Parent 152 0 R/Title(Setting the Number of Lines Per Inch)/Dest[112 0 R/XYZ null 255 null]/Prev 153 0 R/Next 155 0 R>>endobj
+155 0 obj<</Parent 152 0 R/Title(Setting the Number of Columns)/Dest[112 0 R/XYZ null 160 null]/Prev 154 0 R/Next 156 0 R>>endobj
+156 0 obj<</Parent 152 0 R/Title(Setting the Page Margins)/Dest[115 0 R/XYZ null 731 null]/Prev 155 0 R/Next 157 0 R>>endobj
+157 0 obj<</Parent 152 0 R/Title(Pretty Printing)/Dest[115 0 R/XYZ null 581 null]/Prev 156 0 R>>endobj
+158 0 obj<</Parent 143 0 R/Count -3/First 159 0 R/Last 161 0 R/Title(Image Options)/Dest[115 0 R/XYZ null 480 null]/Prev 152 0 R>>endobj
+159 0 obj<</Parent 158 0 R/Title(Scaling the Image)/Dest[115 0 R/XYZ null 406 null]/Next 160 0 R>>endobj
+160 0 obj<</Parent 158 0 R/Title(Adjusting the Hue \(Tint\) of an Image)/Dest[115 0 R/XYZ null 195 null]/Prev 159 0 R/Next 161 0 R>>endobj
+161 0 obj<</Parent 158 0 R/Title(Adjusting the Saturation \(Color\) of an Image)/Dest[118 0 R/XYZ null 550 null]/Prev 160 0 R>>endobj
+162 0 obj<</Type/Catalog/Pages 81 0 R/Names 47 0 R/PageLayout/SinglePage/Outlines 130 0 R/OpenAction[88 0 R/XYZ null null null]/PageMode/UseOutlines/PageLabels<</Nums[0<</P(title)>>1<</P(eltit)>>2<</S/r>>4<</S/D>>]>>>>endobj
+xref
+0 163
+0000000000 65535 f
+0000000015 00000 n
+0000000219 00000 n
+0000001785 00000 n
+0000001859 00000 n
+0000001937 00000 n
+0000002014 00000 n
+0000002093 00000 n
+0000002169 00000 n
+0000002250 00000 n
+0000002308 00000 n
+0000002360 00000 n
+0000002445 00000 n
+0000002469 00000 n
+0000002520 00000 n
+0000002605 00000 n
+0000002629 00000 n
+0000002732 00000 n
+0000002836 00000 n
+0000002940 00000 n
+0000003043 00000 n
+0000003147 00000 n
+0000003251 00000 n
+0000003355 00000 n
+0000003459 00000 n
+0000003563 00000 n
+0000003668 00000 n
+0000003773 00000 n
+0000003877 00000 n
+0000003982 00000 n
+0000004087 00000 n
+0000004192 00000 n
+0000004297 00000 n
+0000004402 00000 n
+0000004507 00000 n
+0000004612 00000 n
+0000004717 00000 n
+0000004822 00000 n
+0000004927 00000 n
+0000005032 00000 n
+0000005137 00000 n
+0000005242 00000 n
+0000005347 00000 n
+0000005452 00000 n
+0000005557 00000 n
+0000005662 00000 n
+0000005767 00000 n
+0000005994 00000 n
+0000006026 00000 n
+0000006058 00000 n
+0000006479 00000 n
+0000006526 00000 n
+0000006573 00000 n
+0000006620 00000 n
+0000006667 00000 n
+0000006714 00000 n
+0000006761 00000 n
+0000006808 00000 n
+0000006855 00000 n
+0000006902 00000 n
+0000006950 00000 n
+0000006998 00000 n
+0000007046 00000 n
+0000007094 00000 n
+0000007142 00000 n
+0000007190 00000 n
+0000007238 00000 n
+0000007286 00000 n
+0000007334 00000 n
+0000007382 00000 n
+0000007430 00000 n
+0000007478 00000 n
+0000007526 00000 n
+0000007574 00000 n
+0000007622 00000 n
+0000007670 00000 n
+0000007718 00000 n
+0000007766 00000 n
+0000007814 00000 n
+0000007862 00000 n
+0000007910 00000 n
+0000007957 00000 n
+0000008147 00000 n
+0000008296 00000 n
+0000014640 00000 n
+0000014661 00000 n
+0000014753 00000 n
+0000014853 00000 n
+0000014872 00000 n
+0000015023 00000 n
+0000015939 00000 n
+0000015959 00000 n
+0000016087 00000 n
+0000016356 00000 n
+0000016376 00000 n
+0000016513 00000 n
+0000017219 00000 n
+0000017239 00000 n
+0000017385 00000 n
+0000018410 00000 n
+0000018430 00000 n
+0000018592 00000 n
+0000019308 00000 n
+0000019329 00000 n
+0000019441 00000 n
+0000019645 00000 n
+0000019666 00000 n
+0000019814 00000 n
+0000020521 00000 n
+0000020542 00000 n
+0000020699 00000 n
+0000021722 00000 n
+0000021743 00000 n
+0000021882 00000 n
+0000022560 00000 n
+0000022581 00000 n
+0000022729 00000 n
+0000023808 00000 n
+0000023830 00000 n
+0000023987 00000 n
+0000024837 00000 n
+0000024858 00000 n
+0000024970 00000 n
+0000025184 00000 n
+0000025205 00000 n
+0000025358 00000 n
+0000027815 00000 n
+0000027837 00000 n
+0000027949 00000 n
+0000028114 00000 n
+0000028134 00000 n
+0000028189 00000 n
+0000028294 00000 n
+0000028437 00000 n
+0000028539 00000 n
+0000028643 00000 n
+0000028808 00000 n
+0000028924 00000 n
+0000029042 00000 n
+0000029165 00000 n
+0000029289 00000 n
+0000029438 00000 n
+0000029579 00000 n
+0000029688 00000 n
+0000029840 00000 n
+0000029979 00000 n
+0000030109 00000 n
+0000030233 00000 n
+0000030369 00000 n
+0000030496 00000 n
+0000030610 00000 n
+0000030733 00000 n
+0000030849 00000 n
+0000030998 00000 n
+0000031127 00000 n
+0000031264 00000 n
+0000031394 00000 n
+0000031519 00000 n
+0000031622 00000 n
+0000031759 00000 n
+0000031864 00000 n
+0000032003 00000 n
+0000032137 00000 n
+trailer
+<</Size 163/Root 162 0 R/Info 1 0 R>>
+startxref
+32362
+%%EOF
diff --git a/doc/sum.shtml b/doc/sum.shtml
new file mode 100644
index 000000000..c0164a021
--- /dev/null
+++ b/doc/sum.shtml
@@ -0,0 +1,465 @@
+<HTML>
+<HEAD>
+ <META NAME="Description" CONTENT="Common UNIX Printing System Software Users Manual">
+ <META NAME="COPYRIGHT" CONTENT="Copyright 1997-2000, All Rights Reserved">
+ <META NAME="DOCNUMBER" CONTENT="CUPS-SUM-1.1">
+ <META NAME="Author" CONTENT="Easy Software Products">
+ <TITLE>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.1.
+
+<EMBED SRC="system-overview.shtml">
+
+<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>
+
+<EMBED SRC="printing-overview.shtml">
+
+<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> and <CODE>lpr</CODE> commands allow you to pass
+printer options using the "-o" option:
+
+<UL><PRE>
+% lp -o landscape -o scaling=75 -o media=A4 filename.jpg
+% lpr -o landscape -o scaling=75 -o media=A4 filename.jpg
+</PRE></UL>
+
+<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 "-o Collate=True" option:
+
+<UL><PRE>
+% lp -n num-copies -o Collate=True filename ENTER
+% lpr -#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> and <CODE>lprm</CODE> commands cancel a print job:
+
+<UL><PRE>
+% cancel job-id ENTER
+% lprm 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> and <CODE>lpr</CODE> commands.
+
+<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
+% lpr -o media=Letter,Transparency filename ENTER
+% lpr -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
+% lpr -o landscape filename ENTER
+</PRE></UL>
+
+<H3>Printing On Both Sides of the Paper</H3>
+
+<P>The "-o sides=two-sided-short-edge" and "-o
+sides=two-sided-long-edge" 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-edge filename ENTER
+% lp -o sides=two-sided-long-edge filename ENTER
+% lpr -o sides=two-sided-long-edge 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
+% lpr -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
+% lpr -o page-set=even filename ENTER
+</PRE></UL>
+
+<H3>N-Up Printing</H3>
+
+<P>The "-o number-up=value" option selects N-Up printing. N-Up
+printing places multiple document pages on a single printed
+page. CUPS supports 1-Up, 2-Up, and 4-Up formats:
+
+<UL><PRE>
+% lp -o number-up=1 filename ENTER
+% lp -o number-up=2 filename ENTER
+% lp -o number-up=4 filename ENTER
+% lpr -o number-up=4 filename ENTER
+</PRE></UL>
+
+<P>The default format is 1-Up.
+
+<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
+% lpr -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
+% lpr -o gamma=1700 filename ENTER
+</PRE></UL>
+
+<P>Values greater than 1000 will lighten the print, while values less
+than 1000 will darken it. The default gamma is 2200 which matches the
+sRGB specification.
+
+<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
+% lpr -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
+% lpr -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
+% lpr -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
+% lpr -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
+% lpr -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
+% lpr -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
+% lpr -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
+% lpr -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.shtml b/doc/svd.shtml
new file mode 100644
index 000000000..f5bdbf9a7
--- /dev/null
+++ b/doc/svd.shtml
@@ -0,0 +1,156 @@
+<HTML>
+<HEAD>
+ <META NAME="COPYRIGHT" CONTENT="Copyright 1997-2000, All Rights Reserved">
+ <META NAME="DOCNUMBER" CONTENT="CUPS-SVD-1.1">
+ <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.1.
+
+<EMBED SRC="system-overview.shtml">
+
+<H2>Document Overview</H2>
+
+<P>This software version description document is organized into the following
+sections:</P>
+
+<UL>
+ <LI><A HREF="#1">1 - Scope</A></LI>
+ <LI><A HREF="#2">2 - References</A></LI>
+ <LI><A HREF="#3">3 - Additions</A></LI>
+ <LI><A HREF="#4">4 - Changes</A></LI>
+ <LI><A HREF="#5">A - Glossary</A></LI>
+</UL>
+
+<EMBED SRC="references.shtml">
+
+<H1>Additions</H1>
+
+CUPS 1.1 includes many new features from the 1.0.x releases.
+
+<H2>Filters</H2>
+
+<H3><CODE>imagetoraster</CODE>, <CODE>imagetops</CODE></H3>
+
+The image file filters have been upgraded to support conversion of Microsoft
+Bitmap ("BMP") files.
+
+<H3>pstoraster</H3>
+
+<P>The <CODE>pstoraster</CODE> filter has been integrated with GNU GhostScript 5.50. The
+new RIP supports most Level 3 PostScript language features and much better
+color management for the sample printer drivers.
+
+<H2>User-Defined Printers and Options</H2>
+
+<P>The new <CODE>lpoptions</CODE> command allows users to configure default
+document options and create additional "instances" of existing printers,
+each with unique options.
+
+<P>The <CODE>lp</CODE>, <CODE>lpr</CODE>, and <CODE>lpstat</CODE> commands
+have been upgraded to use this option and printer instance information
+automatically.
+
+
+
+<H1>Changes</H1>
+
+CUPS 1.1 includes many changes from the 1.0.x releases.
+
+<H2>Directory Structure</H2>
+
+The directory structure in CUPS 1.1 has been modified to conform to the
+Filesystem Hierarchy Standard, 2.0. The following table describes the
+new file locations.
+
+<CENTER><TABLE WIDTH="80%" BORDER>
+<CAPTION>Table N: Directory structure changes from CUPS 1.0.x to 1.1.x.</CAPTION>
+<TR>
+ <TH>Description</TH>
+ <TH>CUPS 1.0.x</TH>
+ <TH>CUPS 1.1.x</TH>
+</TR>
+<TR>
+ <TD>Backends</TD>
+ <TD>/var/cups/backend</TD>
+ <TD>/usr/lib/cups/backend</TD>
+</TR>
+<TR>
+ <TD>CGI programs</TD>
+ <TD>/var/cups/cgi-bin</TD>
+ <TD>/usr/lib/cups/cgi-bin</TD>
+</TR>
+<TR>
+ <TD>Configuration files</TD>
+ <TD>/var/cups/conf</TD>
+ <TD>/etc/cups</TD>
+</TR>
+<TR>
+ <TD>Documentation</TD>
+ <TD>/usr/share/cups/doc</TD>
+ <TD>/usr/share/doc/cups</TD>
+</TR>
+<TR>
+ <TD>Filter programs</TD>
+ <TD>/var/cups/filter</TD>
+ <TD>/usr/lib/cups/filter</TD>
+</TR>
+<TR>
+ <TD>Interface scripts</TD>
+ <TD>/var/cups/interfaces</TD>
+ <TD>/etc/cups/interfaces</TD>
+</TR>
+<TR>
+ <TD>Locale data</TD>
+ <TD>/usr/lib/locale</TD>
+ <TD>/usr/share/locale</TD>
+</TR>
+<TR>
+ <TD>Log files</TD>
+ <TD>/var/cups/logs</TD>
+ <TD>/var/log/cups</TD>
+</TR>
+<TR>
+ <TD>PPD files</TD>
+ <TD>/var/cups/ppd</TD>
+ <TD>/etc/cups/ppd</TD>
+</TR>
+<TR>
+ <TD>Request files</TD>
+ <TD>/var/cups/requests</TD>
+ <TD>/var/spool/cups</TD>
+</TR>
+</TABLE></CENTER>
+
+<H2>IPP Implementation</H2>
+
+<P>CUPS 1.1 is based on version 1.1 of the Internet Printing Protocol,
+and implements the <CODE>set-job-attributes</CODE> extension operation.
+
+<P>Two new CUPS-specific extension operations are provided to determine
+which devices and printer drivers are available on the system.
+
+<P>The <CODE>CUPS-get-printers</CODE> and <CODE>CUPS-get-classes</CODE>
+operations have been upgraded to support limited filtering based upon
+the <CODE>printer-type</CODE>, <CODE>printer-location</CODE>,
+<CODE>printer-info</CODE>, and <CODE>printer-make-and-model</CODE>
+attributes.
+
+<P>The <CODE>CUPS-add-printer</CODE> operation now supports the
+<CODE>ppd-name</CODE> attribute to specify a locally-available PPD
+file rather than sending the PPD file from the client with the request.
+
+<P>Further information on the CUPS implementation of IPP can be found
+in CUPS-IPP-1.1.
+
+<EMBED SRC="glossary.shtml">
+
+</BODY>
+</HTML>
diff --git a/doc/system-overview.shtml b/doc/system-overview.shtml
new file mode 100644
index 000000000..08449c55a
--- /dev/null
+++ b/doc/system-overview.shtml
@@ -0,0 +1,20 @@
+<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
+<A HREF="http://www.easysw.com">Easy Software Products</A> 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 5.50) and an image file RIP that
+is used to support non-PostScript printers.
diff --git a/filter/Makefile b/filter/Makefile
new file mode 100644
index 000000000..97127226d
--- /dev/null
+++ b/filter/Makefile
@@ -0,0 +1,191 @@
+#
+# "$Id$"
+#
+# Filter makefile for the Common UNIX Printing System (CUPS).
+#
+# Copyright 1997-2000 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 \
+ rastertoepson 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
+FORMOBJS = form-attr.o form-main.o form-ps.o form-text.o form-tree.o
+OBJS = $(HPGLOBJS) $(IMAGEOBJS) $(FORMOBJS) \
+ imagetops.o imagetoraster.o common.o pstops.o raster.o \
+ rastertoepson.o rastertohp.o texttops.o textcommon.o
+
+
+#
+# Make all targets...
+#
+
+all: $(TARGETS)
+
+
+#
+# Clean all object files...
+#
+
+clean:
+ $(RM) $(OBJS) $(TARGETS) $(LIBCUPSIMAGE)
+
+
+#
+# Install all targets...
+#
+
+install:
+ -$(MKDIR) $(SERVERBIN)/filter
+ $(INSTALL_BIN) $(TARGETS) $(SERVERBIN)/filter
+ -$(MKDIR) $(INCLUDEDIR)/cups
+ $(INSTALL_DATA) raster.h $(INCLUDEDIR)/cups
+ -$(MKDIR) $(LIBDIR)
+ $(INSTALL_DATA) $(LIBCUPSIMAGE) $(LIBDIR)
+ -if test $(LIBCUPSIMAGE) != "libcupsimage.a"; then \
+ $(RM) `basename $(LIBCUPSIMAGE) .2`; \
+ $(LN) $(LIBCUPSIMAGE) `basename $(LIBCUPSIMAGE) .2`; \
+ fi
+
+
+#
+# formtops
+#
+
+formtops: $(FORMOBJS) common.o ../Makedefs ../cups/$(LIBCUPS)
+ echo Linking $@...
+ $(CC) $(LDFLAGS) -o $@ $(FORMOBJS) common.o $(LIBS) -lm
+$(FORMOBJS): form.h
+
+
+#
+# hpgltops
+#
+
+hpgltops: $(HPGLOBJS) common.o ../Makedefs ../cups/$(LIBCUPS)
+ echo Linking $@...
+ $(CC) $(LDFLAGS) -o $@ $(HPGLOBJS) common.o $(LIBS) -lm
+$(HPGLOBJS): hpgltops.h
+
+
+#
+# libcupsimage.so.2, libcupsimage.sl.2
+#
+
+libcupsimage.so.2 libcupsimage.sl.2: $(IMAGEOBJS) raster.o ../Makedefs
+ echo Linking $@...
+ $(DSO) $@ $(IMAGEOBJS) raster.o $(DSOLIBS) -lm
+ $(RM) `basename $@ .2`
+ $(LN) $@ `basename $@ .2`
+
+
+#
+# libcupsimage.a
+#
+
+libcupsimage.a: $(IMAGEOBJS) raster.o ../Makedefs
+ echo Archiving $@...
+ $(RM) $@
+ $(AR) $(ARFLAGS) $@ $(IMAGEOBJS) raster.o
+ $(RANLIB) $@
+
+$(IMAGEOBJS): image.h
+raster.o: raster.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 raster.h
+
+
+#
+# pstops
+#
+
+pstops: pstops.o common.o ../Makedefs ../cups/$(LIBCUPS)
+ echo Linking $@...
+ $(CC) $(LDFLAGS) -o $@ pstops.o common.o $(LIBS)
+pstops.o: common.h
+
+
+#
+# rastertoepson
+#
+
+rastertoepson: rastertoepson.o ../Makedefs ../cups/$(LIBCUPS)
+ echo Linking $@...
+ $(CC) $(LDFLAGS) -o $@ rastertoepson.o -L. -lcupsimage $(LIBS)
+rastertoepson.o: raster.h
+
+
+#
+# rastertohp
+#
+
+rastertohp: rastertohp.o ../Makedefs ../cups/$(LIBCUPS)
+ echo Linking $@...
+ $(CC) $(LDFLAGS) -o $@ rastertohp.o -L. -lcupsimage $(LIBS)
+rastertohp.o: 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..b41cc4ec3
--- /dev/null
+++ b/filter/common.c
@@ -0,0 +1,255 @@
+/*
+ * "$Id$"
+ *
+ * Common filter routines for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-2000 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;
+
+ fprintf(stderr, "DEBUG: Page = %.0fx%.0f; %.0f,%.0f to %.0f,%.0f\n",
+ PageWidth, PageLength, PageLeft, PageBottom, PageRight, PageTop);
+ }
+
+ 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 &&
+ strncasecmp(val, "two-", 4) == 0)
+ Duplex = 1;
+ else if ((val = cupsGetOption("Duplex", num_options, options)) != NULL &&
+ strncasecmp(val, "Duplex", 6) == 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..c92a12118
--- /dev/null
+++ b/filter/common.h
@@ -0,0 +1,67 @@
+/*
+ * "$Id$"
+ *
+ * Common filter definitions for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-2000 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/form-main.c b/filter/form-main.c
new file mode 100644
index 000000000..941cafa0c
--- /dev/null
+++ b/filter/form-main.c
@@ -0,0 +1,60 @@
+/*
+ * "$Id$"
+ *
+ * CUPS form main entry for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-2000 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() - Load the specified form file and output PostScript.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "form.h"
+
+
+/*
+ * Globals...
+ */
+
+int NumOptions; /* Number of command-line options */
+cups_option_t *Options; /* Command-line options */
+ppd_file_t *PPD; /* PPD file */
+
+
+/*
+ * 'main()' - Load the specified form file and output PostScript.
+ */
+
+int /* O - Exit status */
+main(int argc, /* I - Number of command-line arguments */
+ char *argv[]) /* I - Command-line arguments */
+{
+
+ return (0);
+}
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/filter/form-ps.c b/filter/form-ps.c
new file mode 100644
index 000000000..4d1349f3b
--- /dev/null
+++ b/filter/form-ps.c
@@ -0,0 +1,47 @@
+/*
+ * "$Id$"
+ *
+ * CUPS form PostScript routines for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-2000 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 "form.h"
+
+
+/*
+ * 'formWrite()' - Write PostScript output for the given form document.
+ */
+
+void
+formWrite(tree_t *t) /* I - Document tree to write */
+{
+}
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/filter/form-tree.c b/filter/form-tree.c
new file mode 100644
index 000000000..9e44558d2
--- /dev/null
+++ b/filter/form-tree.c
@@ -0,0 +1,622 @@
+/*
+ * "$Id$"
+ *
+ * CUPS form document tree routines for the Common UNIX Printing
+ * System (CUPS).
+ *
+ * Copyright 1997-2000 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 "form.h"
+
+
+/*
+ * Local functions...
+ */
+
+static int compare_attr(attr_t *a0, attr_t *a1);
+static int compare_elements(char **e0, char **e1);
+static int parse_attr(tree_t *t, FILE *fp);
+static int parse_element(tree_t *t, FILE *fp);
+
+
+/*
+ * Local globals...
+ */
+
+static char *elements[] =
+ {
+ "",
+ "!--",
+ "ARC",
+ "BOX",
+ "BR",
+ "B",
+ "CUPSFORM",
+ "DEFVAR",
+ "FONT",
+ "H1",
+ "H2",
+ "H3",
+ "H4",
+ "H5",
+ "H6",
+ "HEAD",
+ "IMG",
+ "I",
+ "LINE",
+ "PAGE",
+ "PIE",
+ "POLY",
+ "PRE",
+ "P",
+ "RECT",
+ "TEXT",
+ "TT",
+ "VAR"
+ };
+
+
+/*
+ * 'formDelete()' - Delete a node and its children.
+ */
+
+void
+formDelete(tree_t *t) /* I - Tree node */
+{
+}
+
+
+/*
+ * 'formGetAttr()' - Get a node attribute value.
+ */
+
+char * /* O - Value or NULL */
+formGetAttr(tree_t *t, /* I - Tree node */
+ const char *name) /* I - Name of attribute */
+{
+}
+
+
+/*
+ * 'formNew()' - Create a new form node.
+ */
+
+tree_t * /* O - New tree node */
+formNew(tree_t *p) /* I - Parent node */
+{
+ tree_t *t; /* New tree node */
+
+
+ /*
+ * Allocate the new node...
+ */
+
+ if ((t = (tree_t *)calloc(sizeof(tree_t), 1)) == NULL)
+ return (NULL);
+
+ /*
+ * Set/copy attributes...
+ */
+
+ if (p == NULL)
+ {
+ t->bg[0] = 1.0;
+ t->bg[1] = 1.0;
+ t->bg[2] = 1.0;
+ t->halign = HALIGN_LEFT;
+ t->valign = VALIGN_MIDDLE;
+ t->typeface = "Courier";
+ t->size = 12.0;
+ }
+ else
+ {
+ memcpy(t, p, sizeof(tree_t));
+
+ t->prev = NULL;
+ t->next = NULL;
+ t->child = NULL;
+ t->last_child = NULL;
+ t->parent = NULL;
+ t->num_attrs = 0;
+ t->attrs = NULL;
+ t->data = NULL;
+ }
+
+ /*
+ * Return the new node...
+ */
+
+ return (t);
+}
+
+
+/*
+ * 'formRead()' - Read a form tree from a file.
+ */
+
+tree_t * /* O - New form tree */
+formRead(FILE *fp, /* I - File to read from */
+ tree_t *p) /* I - Parent node */
+{
+ int ch, /* Character from file */
+ closech, /* Closing character */
+ have_whitespace; /* Leading whitespace? */
+ static char s[10240]; /* String from file */
+ uchar *ptr, /* Pointer in string */
+ glyph[16], /* Glyph name (&#nnn;) */
+ *glyphptr; /* Pointer in glyph string */
+ tree_t *tree, /* "top" of this tree */
+ *t, /* New tree node */
+ *prev, /* Previous tree node */
+ *temp; /* Temporary looping var */
+ uchar *face, /* Typeface for FONT tag */
+ *color, /* Color for FONT tag */
+ *size; /* Size for FONT tag */
+
+
+ /*
+ * Start off with no previous tree node...
+ */
+
+ prev = NULL;
+ tree = NULL;
+
+ /*
+ * Parse data until we hit end-of-file...
+ */
+
+ while ((ch = getc(fp)) != EOF)
+ {
+ /*
+ * Ignore leading whitespace...
+ */
+
+ have_whitespace = 0;
+ closech = '/';
+
+ if (p == NULL || !p->preformatted)
+ {
+ while (isspace(ch))
+ {
+ have_whitespace = 1;
+ ch = getc(fp);
+ }
+
+ if (ch == EOF)
+ break;
+ }
+
+ /*
+ * Allocate a new tree node - use calloc() to get zeroed data...
+ */
+
+ t = formNew(p);
+
+ /*
+ * See what the character was...
+ */
+
+ if (ch == '<')
+ {
+ /*
+ * Markup char; grab the next char to see if this is a /...
+ */
+
+ ch = getc(fp);
+ if (ch == ' ')
+ {
+ /*
+ * Illegal lone "<"! Ignore it...
+ */
+
+ free(t);
+ continue;
+ }
+
+ if (ch != '/')
+ ungetc(ch, fp);
+
+ if (parse_element(t, fp) < 0)
+ {
+ free(t);
+ break;
+ }
+
+ if ((closech = getc(fp)) == '/')
+ getc(fp);
+
+ /*
+ * If this is the matching close mark, or if we are starting the same
+ * element, or if we've completed a list, we're done!
+ */
+
+ if (ch == '/')
+ {
+ /*
+ * Close element; find matching element...
+ */
+
+ for (temp = p; temp != NULL; temp = temp->p)
+ if (temp->element == t->element)
+ break;
+
+ free(t);
+
+ if (temp != NULL)
+ break;
+ else
+ continue;
+ }
+ }
+ else if (t->preformatted)
+ {
+ /*
+ * Read a pre-formatted string into the current tree node...
+ */
+
+ ptr = s;
+ while (ch != '<' && ch != EOF && ptr < (s + sizeof(s) - 1))
+ {
+ if (ch == '&')
+ {
+ for (glyphptr = glyph;
+ (ch = getc(fp)) != EOF && (glyphptr - glyph) < 15;
+ glyphptr ++)
+ if (!isalnum(ch))
+ break;
+ else
+ *glyphptr = ch;
+
+ *glyphptr = '\0';
+ if (atoi(glyph) > 0)
+ ch = atoi(glyph);
+ else if (strcmp(glyph, "lt") == 0)
+ ch = '<';
+ else if (strcmp(glyph, "gt") == 0)
+ ch = '>';
+ else if (strcmp(glyph, "quot") == 0)
+ ch = '\'';
+ else if (strcmp(glyph, "nbsp") == 0)
+ ch = ' ';
+ else
+ ch = '&';
+ }
+
+ if (ch != 0)
+ *ptr++ = ch;
+
+ if (ch == '\n')
+ break;
+
+ ch = getc(fp);
+ }
+
+ *ptr = '\0';
+
+ if (ch == '<')
+ ungetc(ch, fp);
+
+ t->element = ELEMENT_FRAGMENT;
+ t->data = strdup(s);
+ }
+ else
+ {
+ /*
+ * Read the next string fragment...
+ */
+
+ ptr = s;
+ if (have_whitespace)
+ *ptr++ = ' ';
+
+ while (!isspace(ch) && ch != '<' && ch != EOF && ptr < (s + sizeof(s) - 1))
+ {
+ if (ch == '&')
+ {
+ for (glyphptr = glyph;
+ (ch = getc(fp)) != EOF && (glyphptr - glyph) < 15;
+ glyphptr ++)
+ if (!isalnum(ch))
+ break;
+ else
+ *glyphptr = ch;
+
+ *glyphptr = '\0';
+ if (atoi(glyph) > 0)
+ ch = atoi(glyph);
+ else if (strcmp(glyph, "lt") == 0)
+ ch = '<';
+ else if (strcmp(glyph, "gt") == 0)
+ ch = '>';
+ else if (strcmp(glyph, "quot") == 0)
+ ch = '\'';
+ else if (strcmp(glyph, "nbsp") == 0)
+ ch = ' ';
+ else
+ ch = '&';
+ }
+
+ if (ch != 0)
+ *ptr++ = ch;
+
+ ch = getc(fp);
+ }
+
+ if (isspace(ch))
+ *ptr++ = ' ';
+
+ *ptr = '\0';
+
+ if (ch == '<')
+ ungetc(ch, fp);
+
+ t->element = ELEMENT_FRAGMENT;
+ t->data = strdup(s);
+ }
+
+ /*
+ * If the p tree pointer is not NULL and this is the first
+ * entry we've read, set the child pointer...
+ */
+
+ if (p != NULL && prev == NULL)
+ p->child = t;
+
+ if (p != NULL)
+ p->last_child = t;
+
+ /*
+ * Do the prev/next links...
+ */
+
+ t->parent = p;
+ t->prev = prev;
+ if (prev != NULL)
+ prev->next = t;
+ else
+ tree = t;
+
+ prev = t;
+
+ /*
+ * Do child stuff as needed...
+ */
+
+ if (closech == '>')
+ t->child = formRead(t, fp);
+ }
+
+ return (tree);
+}
+
+
+/*
+ * 'formSetAttr()' - Set a node attribute.
+ */
+
+void
+formSetAttr(tree_t *t, /* I - Tree node */
+ const char *name, /* I - Attribute name */
+ const char *value) /* I - Attribute value */
+{
+}
+
+
+/*
+ * 'compare_attr()' - Compare two attributes.
+ */
+
+static int /* O - -1 if a0 < a1, etc. */
+compare_attr(attr_t *a0, /* I - First attribute */
+ attr_t *a1) /* I - Second attribute */
+{
+ return (strcasecmp(a0->name, a1->name));
+}
+
+
+/*
+ * 'compare_elements()' - Compare two elements.
+ */
+
+static int /* O - -1 if e0 < e1, etc. */
+compare_elements(char **e0, /* I - First element */
+ char **e1) /* I - Second element */
+{
+ return (strcasecmp(*e0, *e1));
+}
+
+
+/*
+ * 'parse_attr()' - Parse an element attribute string.
+ */
+
+static int /* O - -1 on error, 0 on success */
+parse_attr(tree_t *t, /* I - Current tree node */
+ FILE *fp) /* I - Input file */
+{
+ char name[1024], /* Name of attr */
+ value[10240], /* Value of attr */
+ *ptr; /* Temporary pointer */
+ int ch; /* Character from file */
+
+
+ ptr = name;
+ while ((ch = getc(fp)) != EOF)
+ if (isalnum(ch))
+ {
+ if (ptr < (name + sizeof(name) - 1))
+ *ptr++ = ch;
+ }
+ else
+ break;
+
+ *ptr = '\0';
+
+ while (isspace(ch) || ch == '\r')
+ ch = getc(fp);
+
+ switch (ch)
+ {
+ default :
+ ungetc(ch, fp);
+ return (formSetAttr(t, name, NULL));
+ case EOF :
+ return (-1);
+ case '=' :
+ ptr = value;
+ ch = getc(fp);
+
+ while (isspace(ch) || ch == '\r')
+ ch = getc(fp);
+
+ if (ch == EOF)
+ return (-1);
+
+ if (ch == '\'')
+ {
+ while ((ch = getc(fp)) != EOF)
+ if (ch == '\'')
+ break;
+ else if (ptr < (value + sizeof(value) - 1))
+ *ptr++ = ch;
+
+ *ptr = '\0';
+ }
+ else if (ch == '\"')
+ {
+ while ((ch = getc(fp)) != EOF)
+ if (ch == '\"')
+ break;
+ else if (ptr < (value + sizeof(value) - 1))
+ *ptr++ = ch;
+
+ *ptr = '\0';
+ }
+ else
+ {
+ *ptr++ = ch;
+ while ((ch = getc(fp)) != EOF)
+ if (isspace(ch) || ch == '>' || ch == '/' || ch == '\r')
+ break;
+ else if (ptr < (value + sizeof(value) - 1))
+ *ptr++ = ch;
+
+ *ptr = '\0';
+ if (ch == '>' || ch == '/')
+ ungetc(ch, fp);
+ }
+
+ return (formSetAttr(t, name, value));
+ }
+}
+
+
+/*
+ * 'parse_element()' - Parse an element.
+ */
+
+static int /* O - -1 on error or ELEMENT_nnnn */
+parse_element(tree_t *t, /* I - Current tree node */
+ FILE *fp) /* I - Input file */
+{
+ int ch; /* Character from file */
+ char element[255], /* Element string... */
+ *eptr, /* Current character... */
+ comment[10240], /* Comment string */
+ *cptr, /* Current char... */
+ **temp; /* Element variable entry */
+
+
+ eptr = element;
+
+ while ((ch = getc(fp)) != EOF && eptr < (element + sizeof(element) - 1))
+ if (ch == '>' || ch == '/' || isspace(ch))
+ break;
+ else
+ *eptr++ = ch;
+
+ *eptr = '\0';
+
+ if (ch == EOF)
+ return (ELEMENT_ERROR);
+
+ eptr = element;
+ temp = bsearch(&mptr, elements, sizeof(elements) / sizeof(elements[0]),
+ sizeof(elements[0]),
+ (int (*)(const void *, const void *))compare_elements);
+
+ if (temp == NULL)
+ {
+ /*
+ * Unrecognized element stuff...
+ */
+
+ t->element = ELEMENT_COMMENT;
+ strcpy(comment, element);
+ cptr = comment + strlen(comment);
+ }
+ else
+ {
+ t->element = (element_t)((char **)temp - elements);
+ cptr = comment;
+ }
+
+ if (t->element == ELEMENT_COMMENT)
+ {
+ while (ch != EOF && ch != '>' && cptr < (comment + sizeof(comment) - 1))
+ {
+ *cptr++ = ch;
+ ch = getc(fp);
+ }
+
+ *cptr = '\0';
+ t->data = strdup(comment);
+ }
+ else
+ {
+ while (ch != EOF && ch != '>' && ch != '/')
+ {
+ if (!isspace(ch))
+ {
+ ungetc(ch, fp);
+ parse_variable(t, fp);
+ }
+
+ ch = getc(fp);
+ }
+
+ if (ch != EOF)
+ ungetc(ch, fp);
+ }
+
+ return (t->element);
+}
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/filter/form.h b/filter/form.h
new file mode 100644
index 000000000..2644b2889
--- /dev/null
+++ b/filter/form.h
@@ -0,0 +1,175 @@
+/*
+ * "$Id$"
+ *
+ * CUPS form header file for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-2000 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"
+
+
+/*
+ * Form elements...
+ */
+
+typedef enum
+{
+ ELEMENT_FILE = -1, /* Pseudo element, not in file, but above */
+ ELEMENT_FRAGMENT, /* Text fragment */
+ ELEMENT_COMMENT, /* <!-- .... --> */
+ ELEMENT_ARC,
+ ELEMENT_BOX,
+ ELEMENT_BR,
+ ELEMENT_B,
+ ELEMENT_CUPSFORM,
+ ELEMENT_DEFVAR,
+ ELEMENT_FONT,
+ ELEMENT_H1,
+ ELEMENT_H2,
+ ELEMENT_H3,
+ ELEMENT_H4,
+ ELEMENT_H5,
+ ELEMENT_H6,
+ ELEMENT_HEAD,
+ ELEMENT_IMG,
+ ELEMENT_I,
+ ELEMENT_LINE,
+ ELEMENT_PAGE,
+ ELEMENT_PIE,
+ ELEMENT_POLY,
+ ELEMENT_PRE,
+ ELEMENT_P,
+ ELEMENT_RECT,
+ ELEMENT_TEXT,
+ ELEMENT_TT,
+ ELEMENT_VAR
+} element_t;
+
+
+/*
+ * Font styles...
+ */
+
+typedef enum
+{
+ STYLE_NORMAL,
+ STYLE_BOLD,
+ STYLE_ITALIC,
+ STYLE_BOLD_ITALIC
+} style_t;
+
+
+/*
+ * Text alignments...
+ */
+
+typedef enum
+{
+ HALIGN_LEFT,
+ HALIGN_CENTER,
+ HALIGN_RIGHT
+} halign_t;
+
+typedef enum
+{
+ VALIGN_BOTTOM,
+ VALIGN_CENTER,
+ VALIGN_TOP
+} valign_t;
+
+
+/*
+ * Text directions...
+ */
+
+typedef enun
+{
+ DIR_LEFT_TO_RIGHT,
+ DIR_RIGHT_TO_LEFT
+} dir_t;
+
+
+/*
+ * Attribute structure...
+ */
+
+typedef struct
+{
+ char *name, /* Name of attribute */
+ *value; /* Value of attribute */
+} attr_t;
+
+
+/*
+ * Form document tree structure...
+ */
+
+typedef struct tree_str
+{
+ struct tree_str *prev, /* Previous tree node */
+ *next, /* Next tree node */
+ *parent, /* Parent tree node */
+ *child, /* First child node */
+ *last_child; /* Last child node */
+ element_t element; /* Element type */
+ float x, y, w, h; /* Position and size in points */
+ float bg[3], fg[3]; /* Colors of element */
+ float thickness; /* Thickness of lines */
+ int preformatted; /* Preformatted text? */
+ float size; /* Height of text in points */
+ char *typeface; /* Typeface of text */
+ style_t style; /* Style of text */
+ halign_t halign; /* Horizontal alignment */
+ valign_t valign; /* Vertical alignment */
+ dir_t dir; /* Direction of text */
+ int num_attrs; /* Number of attributes */
+ attr_t *attrs; /* Attributes */
+ void *data; /* Text fragment data */
+} tree_t;
+
+
+/*
+ * Globals...
+ */
+
+extern int NumOptions; /* Number of command-line options */
+extern cups_option_t *Options; /* Command-line options */
+extern ppd_file_t *PPD; /* PPD file */
+
+
+/*
+ * Prototypes...
+ */
+
+extern void formDelete(tree_t *t);
+extern char *formGetAttr(tree_t *t, const char *name);
+extern tree_t *formNew(tree_t *p);
+extern tree_t *formRead(FILE *fp, tree_t *p);
+extern void formSetAttr(tree_t *t, const char *name, const char *value);
+extern void formWrite(tree_t *p);
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/filter/hpgl-attr.c b/filter/hpgl-attr.c
new file mode 100644
index 000000000..0056f3af1
--- /dev/null
+++ b/filter/hpgl-attr.c
@@ -0,0 +1,442 @@
+/*
+ * "$Id$"
+ *
+ * HP-GL/2 attribute processing for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1993-2000 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)
+ {
+ MiterLimit = 3.0f;
+ LineCap = 0;
+ LineJoin = 0;
+ }
+ else for (i = 0; i < (num_params - 1); i += 2)
+ switch ((int)params[i].value.number)
+ {
+ case 1 :
+ LineCap = 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 :
+ LineJoin = 0;
+ break;
+ case 5 :
+ LineJoin = 2;
+ break;
+ default :
+ LineJoin = 1;
+ break;
+ }
+ break;
+ case 3 :
+ MiterLimit = 1.0 + 0.5 * (params[i + 1].value.number - 1.0);
+ break;
+ }
+
+ if (PageDirty)
+ {
+ printf("%.1f setmiterlimit\n", MiterLimit);
+ printf("%d setlinecap\n", LineCap);
+ printf("%d setlinejoin\n", LineJoin);
+ }
+}
+
+
+/*
+ * '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 && params[0].value.number <= 1024)
+ PenCount = (int)params[0].value.number;
+ else
+ fprintf(stderr, "WARNING: HP-GL/2 \'NP\' command with invalid number of parameters (%d)!\n",
+ num_params);
+
+ for (i = 0; i <= PenCount; i ++)
+ Pens[i].width = PenWidth;
+
+ PC_pen_color(0, NULL);
+}
+
+
+/*
+ * '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)
+ {
+ Pens[i].rgb[0] = standard_colors[i][0];
+ Pens[i].rgb[1] = standard_colors[i][1];
+ Pens[i].rgb[2] = standard_colors[i][2];
+ }
+ else
+ {
+ Pens[i].rgb[0] = 0.0f;
+ Pens[i].rgb[1] = 0.0f;
+ Pens[i].rgb[2] = 0.0f;
+ }
+
+ if (PageDirty)
+ printf("%.3f %.3f %.3f %.2f SP\n", Pens[PenNumber].rgb[0],
+ Pens[PenNumber].rgb[1], Pens[PenNumber].rgb[2],
+ Pens[PenNumber].width * PenScaling);
+ }
+ else if (num_params == 1 || num_params == 4)
+ {
+ i = (int)params[0].value.number;
+
+ if (num_params == 1)
+ {
+ Pens[i].rgb[0] = standard_colors[i & 7][0];
+ Pens[i].rgb[1] = standard_colors[i & 7][1];
+ Pens[i].rgb[2] = standard_colors[i & 7][2];
+ }
+ else
+ {
+ Pens[i].rgb[0] = params[1].value.number;
+ Pens[i].rgb[1] = params[2].value.number;
+ Pens[i].rgb[2] = params[3].value.number;
+ }
+
+ if (PageDirty && i == PenNumber)
+ printf("%.3f %.3f %.3f %.2f SP\n", Pens[PenNumber].rgb[0],
+ Pens[PenNumber].rgb[1], Pens[PenNumber].rgb[2],
+ Pens[PenNumber].width * PenScaling);
+ }
+ 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)
+ {
+ pen = (int)params[1].value.number;
+
+ Pens[pen].width = w;
+
+ if (PageDirty && pen == PenNumber)
+ printf("%.3f %.3f %.3f %.2f SP\n", Pens[PenNumber].rgb[0],
+ Pens[PenNumber].rgb[1], Pens[PenNumber].rgb[2],
+ Pens[PenNumber].width * PenScaling);
+ }
+ else if (num_params < 2)
+ {
+ /*
+ * Set width for all pens...
+ */
+
+ for (pen = 0; pen <= PenCount; pen ++)
+ Pens[pen].width = w;
+
+ if (PageDirty)
+ printf("%.3f %.3f %.3f %.2f SP\n", Pens[PenNumber].rgb[0],
+ Pens[PenNumber].rgb[1], Pens[PenNumber].rgb[2],
+ Pens[PenNumber].width * PenScaling);
+ }
+ 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);
+
+ if (PageDirty)
+ printf("%.3f %.3f %.3f %.2f SP\n", Pens[PenNumber].rgb[0],
+ Pens[PenNumber].rgb[1], Pens[PenNumber].rgb[2],
+ Pens[PenNumber].width * PenScaling);
+}
+
+
+/*
+ * '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..aa29ba909
--- /dev/null
+++ b/filter/hpgl-char.c
@@ -0,0 +1,498 @@
+/*
+ * "$Id$"
+ *
+ * HP-GL/2 character processing for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1993-2000 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 */
+
+
+ /*
+ * Set default font attributes...
+ */
+
+ AlternateFont.typeface = 48;
+ AlternateFont.posture = 0;
+ AlternateFont.weight = 0;
+ AlternateFont.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 :
+ AlternateFont.height = params[i + 1].value.number;
+ break;
+ case 5 :
+ AlternateFont.posture = (int)params[i + 1].value.number;
+ break;
+ case 6 :
+ AlternateFont.weight = (int)params[i + 1].value.number;
+ break;
+ case 7 :
+ AlternateFont.typeface = (int)params[i + 1].value.number;
+ break;
+ }
+
+ /*
+ * Define the font...
+ */
+
+ if (PageDirty)
+ printf("/SA {\n"
+ " /%s%s%s%s findfont\n"
+ " [ %f %f %f %f 0.0 0.0 ] makefont\n"
+ " setfont\n"
+ "} bind def\n",
+ AlternateFont.typeface == 48 ? "Courier" : "Helvetica",
+ (AlternateFont.weight != 0 || AlternateFont.posture != 0) ? "-" : "",
+ AlternateFont.weight != 0 ? "Bold" : "",
+ AlternateFont.posture != 0 ? "Oblique" : "",
+ AlternateFont.x * AlternateFont.height,
+ -AlternateFont.y * AlternateFont.height,
+ AlternateFont.y * AlternateFont.height,
+ AlternateFont.x * AlternateFont.height);
+
+ CharHeight[1] = AlternateFont.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 */
+{
+ if (CharFont)
+ {
+ if (num_params == 2)
+ {
+ AlternateFont.x = params[0].value.number;
+ AlternateFont.y = params[1].value.number;
+ }
+
+ if (PageDirty)
+ {
+ printf("/SA {\n"
+ " /%s%s%s%s findfont\n"
+ " [ %f %f %f %f 0.0 0.0 ] makefont\n"
+ " setfont\n"
+ "} bind def\n",
+ AlternateFont.typeface == 48 ? "Courier" : "Helvetica",
+ (AlternateFont.weight != 0 || AlternateFont.posture != 0) ? "-" : "",
+ AlternateFont.weight != 0 ? "Bold" : "",
+ AlternateFont.posture != 0 ? "Oblique" : "",
+ AlternateFont.x * AlternateFont.height,
+ -AlternateFont.y * AlternateFont.height,
+ AlternateFont.y * AlternateFont.height,
+ AlternateFont.x * AlternateFont.height);
+ }
+ }
+ else
+ {
+ if (num_params == 2)
+ {
+ StandardFont.x = params[0].value.number;
+ StandardFont.y = params[1].value.number;
+ }
+
+ if (PageDirty)
+ {
+ printf("/SS {\n"
+ " /%s%s%s%s findfont\n"
+ " [ %f %f %f %f 0.0 0.0 ] makefont\n"
+ " setfont\n"
+ "} bind def\n",
+ StandardFont.typeface == 48 ? "Courier" : "Helvetica",
+ (StandardFont.weight != 0 || StandardFont.posture != 0) ? "-" : "",
+ StandardFont.weight != 0 ? "Bold" : "",
+ StandardFont.posture != 0 ? "Oblique" : "",
+ StandardFont.x * StandardFont.height,
+ -StandardFont.y * StandardFont.height,
+ StandardFont.y * StandardFont.height,
+ StandardFont.x * StandardFont.height);
+ }
+ }
+}
+
+
+/*
+ * '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 \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("%.3f %.3f %.3f %.2f SP ST\n", Pens[CharPen].rgb[0],
+ Pens[CharPen].rgb[CharPen], Pens[CharPen].rgb[2],
+ Pens[CharPen].width * PenScaling);
+ Outputf("%.3f %.3f %.3f %.2f SP\n", Pens[PenNumber].rgb[0],
+ Pens[PenNumber].rgb[PenNumber], Pens[PenNumber].rgb[2],
+ Pens[PenNumber].width * PenScaling);
+ }
+
+ 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;
+
+ if (PageDirty)
+ puts("SA");
+
+ 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 */
+
+
+ /*
+ * Set default font attributes...
+ */
+
+ StandardFont.typeface = 48;
+ StandardFont.posture = 0;
+ StandardFont.weight = 0;
+ StandardFont.height = 11.5;
+ StandardFont.x = 1.0;
+ StandardFont.y = 0.0;
+
+ /*
+ * Loop through parameter value pairs...
+ */
+
+ for (i = 0; i < (num_params - 1); i += 2)
+ switch ((int)params[i].value.number)
+ {
+ case 4 :
+ StandardFont.height = params[i + 1].value.number;
+ break;
+ case 5 :
+ StandardFont.posture = (int)params[i + 1].value.number;
+ break;
+ case 6 :
+ StandardFont.weight = (int)params[i + 1].value.number;
+ break;
+ case 7 :
+ StandardFont.typeface = (int)params[i + 1].value.number;
+ break;
+ }
+
+ /*
+ * Define the font...
+ */
+
+ if (PageDirty)
+ printf("/SS {\n"
+ " /%s%s%s%s findfont\n"
+ " [ %f %f %f %f 0.0 0.0 ] makefont\n"
+ " setfont\n"
+ "} bind def\n",
+ StandardFont.typeface == 48 ? "Courier" : "Helvetica",
+ (StandardFont.weight != 0 || StandardFont.posture != 0) ? "-" : "",
+ StandardFont.weight != 0 ? "Bold" : "",
+ StandardFont.posture != 0 ? "Oblique" : "",
+ StandardFont.x * StandardFont.height,
+ -StandardFont.y * StandardFont.height,
+ StandardFont.y * StandardFont.height,
+ StandardFont.x * StandardFont.height);
+
+ CharHeight[0] = StandardFont.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;
+
+ if (PageDirty)
+ puts("SS");
+
+ 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..57f0e4473
--- /dev/null
+++ b/filter/hpgl-config.c
@@ -0,0 +1,638 @@
+/*
+ * "$Id$"
+ *
+ * HP-GL/2 configuration routines for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1993-2000 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 page_width, /* Actual page width */
+ page_height; /* Actual page height */
+ float scaling; /* Scaling factor */
+ float left, right, /* Scaling window */
+ bottom, top;
+ float width, height; /* Scaling width and height */
+ float iw1[2], iw2[2]; /* Clipping window */
+
+
+ /*
+ * Get the page and input window sizes...
+ */
+
+ if (FitPlot)
+ {
+ page_width = PageRight - PageLeft;
+ page_height = PageTop - PageBottom;
+ }
+ else
+ {
+ page_width = (P2[0] - P1[0]) * 72.0f / 1016.0f;
+ page_height = (P2[1] - P1[1]) * 72.0f / 1016.0f;
+ }
+
+ fprintf(stderr, "DEBUG: page_width = %.0f, page_height = %.0f\n",
+ page_width, page_height);
+
+ if (page_width == 0 || page_height == 0)
+ return;
+
+ /*
+ * Set the scaling window...
+ */
+
+ switch (ScalingType)
+ {
+ default : /* No user scaling */
+ left = P1[0];
+ bottom = P1[1];
+ right = P2[0];
+ top = P2[1];
+ break;
+
+ case 0 : /* Anisotropic (non-uniform) scaling */
+ left = Scaling1[0];
+ bottom = Scaling1[1];
+ right = Scaling2[0];
+ top = Scaling2[1];
+ break;
+
+ case 1 : /* Isotropic (uniform) scaling */
+ left = Scaling1[0];
+ bottom = Scaling1[1];
+ right = Scaling2[0];
+ top = Scaling2[1];
+
+ width = right - left;
+ height = top - bottom;
+
+ if (width == 0 || height == 0)
+ return;
+
+ if ((width * page_height) != (height * page_width))
+ {
+ scaling = height * page_width / page_height;
+ if (width < scaling)
+ {
+ width = scaling;
+ left = 0.5f * (left + right - width);
+ right = left + width;
+ }
+ else
+ {
+ height = width * page_height / page_width;
+ bottom = 0.5f * (bottom + top - height);
+ top = bottom + height;
+ }
+ }
+ break;
+
+ case 2 :
+ left = Scaling1[0];
+ bottom = Scaling1[1];
+ right = left + page_width * Scaling2[0];
+ top = bottom + page_height * Scaling2[1];
+ break;
+ }
+
+ width = right - left;
+ height = top - bottom;
+
+ if (width == 0 || height == 0)
+ return;
+
+ /*
+ * Scale the plot as needed...
+ */
+
+ if (Rotation == 0 || Rotation == 180)
+ scaling = page_width / width;
+ else
+ scaling = page_width / height;
+
+ if (FitPlot)
+ {
+ if (Rotation == 0 || Rotation == 180)
+ scaling *= page_width / PlotSize[1];
+ else
+ scaling *= page_width / PlotSize[0];
+ }
+
+ /*
+ * Offset for the current P1 location...
+ */
+
+ if (FitPlot)
+ {
+ left = 0;
+ bottom = 0;
+ }
+ else
+ {
+ left = P1[0] * 72.0f / 1016.0f;
+ bottom = P1[1] * 72.0f / 1016.0f;
+ }
+
+ /*
+ * Generate a new transformation matrix...
+ */
+
+ switch (Rotation)
+ {
+ case 0 :
+ Transform[0][0] = scaling;
+ Transform[0][1] = 0.0;
+ Transform[0][2] = -left;
+ Transform[1][0] = 0.0;
+ Transform[1][1] = scaling;
+ Transform[1][2] = -bottom;
+ break;
+
+ case 90 :
+ Transform[0][0] = 0.0;
+ Transform[0][1] = -scaling;
+ Transform[0][2] = PageLength - left;
+ Transform[1][0] = scaling;
+ Transform[1][1] = 0.0;
+ Transform[1][2] = -bottom;
+ break;
+
+ case 180 :
+ Transform[0][0] = -scaling;
+ Transform[0][1] = 0.0;
+ Transform[0][2] = PageLength - left;
+ Transform[1][0] = 0.0;
+ Transform[1][1] = -scaling;
+ Transform[1][2] = PageWidth - bottom;
+ break;
+
+ case 270 :
+ Transform[0][0] = 0.0;
+ Transform[0][1] = scaling;
+ Transform[0][2] = -left;
+ Transform[1][0] = -scaling;
+ Transform[1][1] = 0.0;
+ Transform[1][2] = PageWidth - bottom;
+ break;
+ }
+
+ fprintf(stderr, "DEBUG: Transform = [ %.3f %.3f\n"
+ "DEBUG: %.3f %.3f\n"
+ "DEBUG: %.3f %.3f ]\n",
+ Transform[0][0], Transform[1][0], Transform[0][1],
+ Transform[1][1], Transform[0][2], Transform[1][2]);
+
+ if (FitPlot)
+ {
+ if (Rotation == 0 || Rotation == 180)
+ PenScaling *= page_width / PlotSize[1];
+ else
+ PenScaling *= page_width / PlotSize[0];
+ }
+ else
+ PenScaling = 1.0;
+
+ if (PenScaling < 0.0)
+ PenScaling = -PenScaling;
+
+ if (PageDirty)
+ {
+ printf("%.2f setlinewidth\n", Pens[PenNumber].width * PenScaling);
+
+ if (IW1[0] != IW2[0] && IW1[1] != IW2[1])
+ {
+ iw1[0] = IW1[0] * 72.0f / 1016.0f;
+ iw1[1] = IW1[1] * 72.0f / 1016.0f;
+ iw2[0] = IW2[0] * 72.0f / 1016.0f;
+ iw2[1] = IW2[1] * 72.0f / 1016.0f;
+
+ printf("initclip MP %.3f %.3f MO %.3f %.3f LI %.3f %.3f LI %.3f %.3f LI CP clip\n",
+ iw1[0], iw1[1], iw1[0], iw2[1], iw2[0], iw2[1], iw2[0], iw1[1]);
+ }
+ }
+}
+
+
+/*
+ * '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);
+
+ PenWidth = 1;
+
+ 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] = 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;
+ 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];
+
+ if (ScalingType < 0)
+ {
+ Scaling1[0] = P1[0];
+ Scaling1[0] = P1[1];
+ Scaling2[0] = P2[0];
+ Scaling2[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];
+
+ if (ScalingType < 0)
+ {
+ Scaling1[0] = P1[0];
+ Scaling1[0] = P1[1];
+ Scaling2[0] = P2[0];
+ Scaling2[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] = PageLeft / 72.0f * 1016.0f;
+ IW1[1] = PageBottom / 72.0f * 1016.0f;
+ IW2[0] = PageRight / 72.0f * 1016.0f;
+ IW2[1] = PageTop / 72.0f * 1016.0f;
+ }
+ else if (num_params == 4)
+ {
+
+ if (ScalingType < 0)
+ {
+ IW1[0] = params[0].value.number;
+ IW1[1] = params[1].value.number;
+ IW2[0] = params[2].value.number;
+ IW2[1] = params[3].value.number;
+ }
+ else
+ {
+ IW1[0] = (Transform[0][0] * params[0].value.number +
+ Transform[0][1] * params[1].value.number +
+ Transform[0][2]) / 72.0f * 1016.0f;
+ IW1[1] = (Transform[1][0] * params[0].value.number +
+ Transform[1][1] * params[1].value.number +
+ Transform[1][2]) / 72.0f * 1016.0f;
+ IW2[0] = (Transform[0][0] * params[2].value.number +
+ Transform[0][1] * params[3].value.number +
+ Transform[0][2]) / 72.0f * 1016.0f;
+ IW2[1] = (Transform[1][0] * params[2].value.number +
+ Transform[1][1] * params[3].value.number +
+ Transform[1][2]) / 72.0f * 1016.0f;
+ }
+
+ fprintf(stderr, "DEBUG: IW%.0f,%.0f,%.0f,%.0f = [ %.0f %.0f %.0f %.0f ]\n",
+ params[0].value.number, params[1].value.number,
+ params[2].value.number, params[3].value.number,
+ IW1[0], IW1[1], IW2[0], IW2[1]);
+ }
+
+
+ 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 : /* PS ; */
+ if (Rotation == 0 || Rotation == 180)
+ {
+ PlotSize[0] = PageWidth;
+ PlotSize[1] = PageLength;
+ }
+ else
+ {
+ PlotSize[0] = PageLength;
+ PlotSize[1] = PageWidth;
+ }
+ break;
+ case 1 : /* PS length ; */
+ 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 : /* PS length, width ; */
+ /*
+ * Unfortunately, it appears that NO application correctly
+ * sends a two-argument PS command as documented in the
+ * HP-GL/2 Reference Manual from HP. Instead, applications
+ * send the width before the length, which causes all sorts
+ * of problems.
+ *
+ * Rather than fight it, we now look for them as width,length
+ * instead of length,width.
+ *
+ * Don't like it? Send mail to the folks that make Ideas, Pro/E,
+ * AutoCAD, etc.
+ */
+
+ if (Rotation == 0 || Rotation == 180)
+ {
+ PlotSize[0] = 72.0f * params[0].value.number / 1016.0f;
+ PlotSize[1] = 72.0f * params[1].value.number / 1016.0f;
+ }
+ else
+ {
+ PlotSize[0] = 72.0f * params[1].value.number / 1016.0f;
+ PlotSize[1] = 72.0f * params[0].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;
+ Scaling1[0] = P1[0];
+ Scaling1[0] = P1[1];
+ Scaling2[0] = P2[0];
+ Scaling2[1] = P2[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 = 1;
+ }
+
+ update_transform();
+}
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/filter/hpgl-input.c b/filter/hpgl-input.c
new file mode 100644
index 000000000..720122ee1
--- /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-2000 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, "%262143[^\"]\"", 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..cc3009e05
--- /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-2000 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 */
+ 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 */
+
+
+ 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);
+
+ PPD = SetCommonOptions(num_options, options, 1);
+
+ PlotSize[0] = PageWidth;
+ PlotSize[1] = PageLength;
+
+ 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);
+
+ 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..c6646a6e5
--- /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-2000 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..97ba13b3e
--- /dev/null
+++ b/filter/hpgl-prolog.c
@@ -0,0 +1,375 @@
+/*
+ * "$Id$"
+ *
+ * HP-GL/2 prolog routines for for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1993-2000 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 */
+{
+ 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.1 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("%%Trailer");
+ 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 */
+ float iw1[2], iw2[2]; /* Clipping window */
+ int i; /* Looping var */
+ ppd_size_t *size; /* Page size */
+ ppd_option_t *option; /* Page size option */
+ ppd_choice_t *choice; /* Page size choice */
+ float width, length; /* Page dimensions */
+ int landscape; /* Rotate for landscape orientation? */
+
+
+ /*
+ * Write the page header as needed...
+ */
+
+ if (!PageDirty)
+ {
+ PageDirty = 1;
+ PageCount ++;
+
+ if (PPD != NULL && !FitPlot)
+ {
+ /*
+ * Set the page size for this page...
+ */
+
+ if (PageRotation == 0 || PageRotation == 180)
+ {
+ width = PlotSize[0];
+ length = PlotSize[1];
+ }
+ else
+ {
+ width = PlotSize[1];
+ length = PlotSize[0];
+ }
+
+ landscape = 0;
+
+ /*
+ * Lookup the closest PageSize and set it...
+ */
+
+ for (i = PPD->num_sizes, size = PPD->sizes; i > 0; i --, size ++)
+ if ((fabs(length - size->length) < 36.0 && size->width >= width) ||
+ (fabs(length - size->width) < 36.0 && size->length >= width))
+ break;
+
+ if (i == 0 && PPD->variable_sizes)
+ {
+ for (i = PPD->num_sizes, size = PPD->sizes; i > 0; i --, size ++)
+ if (strcasecmp(size->name, "custom") == 0)
+ break;
+ }
+
+ if (i > 0)
+ {
+ /*
+ * Found a matching size...
+ */
+
+ option = ppdFindOption(PPD, "PageSize");
+ choice = ppdFindChoice(option, size->name);
+
+ puts("%%BeginSetup");
+ printf("%%%%BeginFeature: PageSize %s\n", size->name);
+
+ if (strcasecmp(size->name, "custom") == 0)
+ {
+ PageLeft = PPD->custom_margins[0];
+ PageRight = width - PPD->custom_margins[2];
+ PageWidth = width;
+ PageBottom = PPD->custom_margins[1];
+ PageTop = length - PPD->custom_margins[3];
+ PageLength = length;
+
+ printf("%.0f %.0f 0 0 0\n", width, length);
+
+ if (choice->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...
+ */
+
+ puts("pop pop pop");
+ puts("<</PageSize[5 -2 roll]/ImagingBBox null>>setpagedevice\n");
+ }
+ else
+ {
+ /*
+ * Use the vendor-supplied command...
+ */
+
+ printf("%s\n", choice->code);
+ }
+ }
+ else
+ {
+ if (choice->code)
+ printf("%s\n", choice->code);
+
+ if (fabs(length - size->width) < 36.0)
+ {
+ /*
+ * Do landscape orientation...
+ */
+
+ PageLeft = size->bottom;
+ PageRight = size->top;
+ PageWidth = size->length;
+ PageBottom = size->left;
+ PageTop = size->right;
+ PageLength = size->width;
+
+ landscape = 1;
+ }
+ else
+ {
+ /*
+ * Do portrait orientation...
+ */
+
+ PageLeft = size->left;
+ PageRight = size->right;
+ PageWidth = size->width;
+ PageBottom = size->bottom;
+ PageTop = size->top;
+ PageLength = size->length;
+ }
+ }
+
+ puts("%%EndFeature");
+ puts("%%EndSetup");
+ }
+ }
+ else
+ landscape = 0;
+
+ printf("%%%%Page: %d %d\n", PageCount, PageCount);
+
+ printf("/SA {\n"
+ " /%s%s%s%s findfont\n"
+ " [ %f %f %f %f 0.0 0.0 ] makefont\n"
+ " setfont\n"
+ "} bind def\n",
+ AlternateFont.typeface == 48 ? "Courier" : "Helvetica",
+ (AlternateFont.weight != 0 || AlternateFont.posture != 0) ? "-" : "",
+ AlternateFont.weight != 0 ? "Bold" : "",
+ AlternateFont.posture != 0 ? "Oblique" : "",
+ AlternateFont.x * AlternateFont.height,
+ -AlternateFont.y * AlternateFont.height,
+ AlternateFont.y * AlternateFont.height,
+ AlternateFont.x * AlternateFont.height);
+
+ printf("/SS {\n"
+ " /%s%s%s%s findfont\n"
+ " [ %f %f %f %f 0.0 0.0 ] makefont\n"
+ " setfont\n"
+ "} bind def\n",
+ StandardFont.typeface == 48 ? "Courier" : "Helvetica",
+ (StandardFont.weight != 0 || StandardFont.posture != 0) ? "-" : "",
+ StandardFont.weight != 0 ? "Bold" : "",
+ StandardFont.posture != 0 ? "Oblique" : "",
+ StandardFont.x * StandardFont.height,
+ -StandardFont.y * StandardFont.height,
+ StandardFont.y * StandardFont.height,
+ StandardFont.x * StandardFont.height);
+
+ if (CharFont)
+ puts("SA");
+ else
+ puts("SS");
+
+ printf("%.1f setmiterlimit\n", MiterLimit);
+ printf("%d setlinecap\n", LineCap);
+ printf("%d setlinejoin\n", LineJoin);
+
+ printf("%.3f %.3f %.3f %.2f SP\n", Pens[1].rgb[0], Pens[1].rgb[1],
+ Pens[1].rgb[2], Pens[1].width * PenScaling);
+
+ puts("gsave");
+
+ if (Duplex && (PageCount & 1) == 0)
+ switch ((PageRotation / 90 + landscape) & 3)
+ {
+ case 0 :
+ printf("%.1f %.1f translate\n", PageWidth - PageRight, PageBottom);
+ break;
+ case 1 :
+ printf("%.0f 0 translate 90 rotate\n", PageLength);
+ printf("%.1f %.1f translate\n", PageLength - PageTop,
+ PageWidth - PageRight);
+ break;
+ case 2 :
+ printf("%.0f %.0f translate 180 rotate\n", PageWidth, PageLength);
+ printf("%.1f %.1f translate\n", PageLeft, PageLength - PageTop);
+ break;
+ case 3 :
+ printf("0 %.0f translate -90 rotate\n", PageWidth);
+ printf("%.1f %.1f translate\n", PageBottom, PageLeft);
+ break;
+ }
+ else
+ switch ((PageRotation / 90 + landscape) & 3)
+ {
+ case 0 :
+ printf("%.1f %.1f translate\n", PageLeft, PageBottom);
+ break;
+ case 1 :
+ printf("%.0f 0 translate 90 rotate\n", PageLength);
+ printf("%.1f %.1f translate\n", PageBottom, PageWidth - PageRight);
+ break;
+ case 2 :
+ printf("%.0f %.0f translate 180 rotate\n", PageWidth, PageLength);
+ printf("%.1f %.1f translate\n", PageWidth - PageRight,
+ PageLength - PageTop);
+ break;
+ case 3 :
+ printf("0 %.0f translate -90 rotate\n", PageWidth);
+ printf("%.1f %.1f translate\n", PageLength - PageTop, PageLeft);
+ break;
+ }
+
+ if (IW1[0] != IW2[0] && IW1[1] != IW2[1])
+ {
+ iw1[0] = IW1[0] * 72.0f / 1016.0f;
+ iw1[1] = IW1[1] * 72.0f / 1016.0f;
+ iw2[0] = IW2[0] * 72.0f / 1016.0f;
+ iw2[1] = IW2[1] * 72.0f / 1016.0f;
+
+ printf("initclip MP %.3f %.3f MO %.3f %.3f LI %.3f %.3f LI %.3f %.3f LI CP clip\n",
+ iw1[0], iw1[1], iw1[0], iw2[1], iw2[0], iw2[1], iw2[0], iw1[1]);
+ }
+ }
+
+ /*
+ * 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..5b7162920
--- /dev/null
+++ b/filter/hpgl-vector.c
@@ -0,0 +1,707 @@
+/*
+ * "$Id$"
+ *
+ * HP-GL/2 vector routines for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1993-2000 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 ++;
+ PenNumber = (int)decode_number(&s, base_bits, 1.0);
+ if (PageDirty)
+ printf("%.3f %.3f %.3f %.2f SP\n", Pens[PenNumber].rgb[0],
+ Pens[PenNumber].rgb[PenNumber], Pens[PenNumber].rgb[2],
+ Pens[PenNumber].width * PenScaling);
+ 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..d1b65d2da
--- /dev/null
+++ b/filter/hpgltops.h
@@ -0,0 +1,232 @@
+/*
+ * "$Id$"
+ *
+ * HP-GL/2 to PostScript filter for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1993-2000 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
+
+
+/*
+ * Font information...
+ */
+
+typedef struct
+{
+ int typeface, /* Typeface number */
+ posture, /* Posture number */
+ weight; /* Weight number */
+ float height; /* Height/size of font */
+ float x, y; /* X and Y direction/scaling */
+} font_t;
+
+
+/*
+ * Pen information...
+ */
+
+typedef struct
+{
+ float rgb[3]; /* Pen color */
+ float width; /* Pen width */
+} pen_t;
+
+
+/*
+ * 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 ppd_file_t *PPD VALUE(NULL); /* PPD file */
+
+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 font_t StandardFont, /* Standard font */
+ AlternateFont; /* Alternate font */
+VAR float PenPosition[2] VALUE2(0.0f, 0.0f),
+ /* Current pen position */
+ PenScaling VALUE(1.0f), /* Pen width scaling factor */
+ PenWidth VALUE(1.0f); /* Default pen width */
+VAR pen_t Pens[1024]; /* State of each pen */
+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(0), /* 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_ */
+;
+
+VAR int LineCap VALUE(0); /* Line capping */
+VAR int LineJoin VALUE(0); /* Line joining */
+VAR float MiterLimit VALUE(3.0f); /* Miter limit at joints */
+
+
+/*
+ * 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);
+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..1a42458be
--- /dev/null
+++ b/filter/image-colorspace.c
@@ -0,0 +1,882 @@
+/*
+ * "$Id$"
+ *
+ * Colorspace conversions for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1993-2000 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(const 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(const 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(const 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(const 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(const 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(const 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(const 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(const 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 */
+ km; /* Maximum K value */
+ 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));
+
+ if ((km = max(c, max(m, y))) > k)
+ k = k * k / km;
+
+ c -= k;
+ m -= k;
+ y -= k;
+
+ 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++ = ImageDensity[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 ((km = max(c, max(m, y))) > k)
+ k = k * k / km;
+
+ c -= k;
+ m -= k;
+ y -= k;
+
+ *out++ = c;
+ *out++ = m;
+ *out++ = y;
+ *out++ = k;
+
+ count --;
+ }
+}
+
+
+/*
+ * 'ImageRGBToWhite()' - Convert RGB colors to luminance.
+ */
+
+void
+ImageRGBToWhite(const 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(const 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, /* IO - Input/output pixels */
+ int count, /* I - Number of pixels/bytes to adjust */
+ const 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, /* IO - 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..d1d5c6e97
--- /dev/null
+++ b/filter/image-gif.c
@@ -0,0 +1,644 @@
+/*
+ * "$Id$"
+ *
+ * GIF image routines for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1993-2000 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) */
+ const 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 /* O - -1 on error, 0 on success */
+gif_read_cmap(FILE *fp, /* I - File to read from */
+ int ncolors, /* I - Number of colors in file */
+ gif_cmap_t cmap, /* O - Colormap information */
+ int *gray) /* IO - Is the image grayscale? */
+{
+ int i; /* Looping var */
+
+
+ /*
+ * 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..b352642b8
--- /dev/null
+++ b/filter/image-jpeg.c
@@ -0,0 +1,190 @@
+/*
+ * "$Id$"
+ *
+ * JPEG image routines for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1993-2000 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) */
+ const 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..9df3044de
--- /dev/null
+++ b/filter/image-photocd.c
@@ -0,0 +1,323 @@
+/*
+ * "$Id$"
+ *
+ * PhotoCD routines for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1993-2000 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) */
+ const 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);
+ else
+ rgb = NULL;
+
+ 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.
+ */
+
+ cb = cr = 0.0f;
+
+ 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..76be54c1d
--- /dev/null
+++ b/filter/image-png.c
@@ -0,0 +1,205 @@
+/*
+ * "$Id$"
+ *
+ * PNG image routines for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1993-2000 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) */
+ const 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..dbc956c61
--- /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-2000 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) */
+ const 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..8aa11321a
--- /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-2000 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) */
+ const 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], alpha = rows[3], 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..ad8aa9e61
--- /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-2000 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..c6b6d9c22
--- /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-2000 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..ada20b5bb
--- /dev/null
+++ b/filter/image-sun.c
@@ -0,0 +1,377 @@
+/*
+ * "$Id$"
+ *
+ * Sun Raster image file routines for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1993-2000 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) */
+ const 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;
+ run_value = 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..674a55a38
--- /dev/null
+++ b/filter/image-tiff.c
@@ -0,0 +1,1621 @@
+/*
+ * "$Id$"
+ *
+ * TIFF file routines for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1993-2000 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>
+# include <unistd.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) */
+ const 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...
+ */
+
+ lseek(fileno(fp), 0, SEEK_SET); /* Work around "feature" in some stdio's */
+
+ 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 = 128;
+ img->yppi = 128;
+ }
+ }
+
+ /*
+ * 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] = 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, 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..fe15632f1
--- /dev/null
+++ b/filter/image-zoom.c
@@ -0,0 +1,310 @@
+/*
+ * "$Id$"
+ *
+ * Image zoom routines for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1993-2000 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..15dd9a270
--- /dev/null
+++ b/filter/image.c
@@ -0,0 +1,780 @@
+/*
+ * "$Id$"
+ *
+ * Base image support for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1993-2000 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>
+#include <cups/cups.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 */
+ const 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 */
+
+
+ /*
+ * Wipe the tile cache file (if any)...
+ */
+
+ if (img->cachefile != NULL)
+ {
+ fprintf(stderr, "DEBUG: Closing and removing swap file \"%s\"...\n",
+ img->cachename);
+
+ fclose(img->cachefile);
+ unlink(img->cachename);
+ }
+
+ /*
+ * Free the image cache...
+ */
+
+ fputs("DEBUG: Freeing memory...\n", stderr);
+
+ for (current = img->first, next = NULL; current != NULL; current = next)
+ {
+ fprintf(stderr, "DEBUG: Freeing cache (%p, next = %p)...\n",
+ current, next);
+
+ next = current->next;
+ free(current);
+ }
+
+ /*
+ * Free the rest of memory...
+ */
+
+ if (img->tiles != NULL)
+ {
+ fprintf(stderr, "DEBUG: Freeing tiles (%p)...\n", img->tiles[0]);
+
+ free(img->tiles[0]);
+
+ fprintf(stderr, "DEBUG: Freeing tile pointers (%p)...\n", img->tiles);
+
+ 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 or the RIP_CACHE environment variable.
+ */
+
+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%254s", &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;
+
+ fprintf(stderr, "DEBUG: max_ics=%d...\n", img->max_ics);
+}
+
+
+/*
+ * '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 */
+ float m; /* Current matrix value */
+ int *im; /* Pointer into ImageMatrix */
+
+ ImageHaveProfile = 1;
+
+ for (i = 0, im = ImageMatrix[0][0]; i < 3; i ++)
+ for (j = 0; j < 3; j ++)
+ for (k = 0, m = matrix[i][j]; k < 256; k ++)
+ *im++ = (int)(k * m + 0.5);
+
+ for (k = 0, im = ImageDensity; k < 256; k ++)
+ *im++ = 255.0 * d * pow((float)k / 255.0, g) + 0.5;
+}
+
+
+/*
+ * 'ImageGetCol()' - Get a column of pixels from an image.
+ */
+
+int /* O - -1 on error, 0 on success */
+ImageGetCol(image_t *img, /* I - Image */
+ int x, /* I - Column */
+ int y, /* I - Start row */
+ int height, /* I - Column height */
+ ib_t *pixels) /* O - Pixel data */
+{
+ int bpp, /* Bytes per pixel */
+ twidth, /* Tile width */
+ count; /* Number of pixels to get */
+ const ib_t *ib; /* Pointer into tile */
+
+
+ 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 /* O - -1 on error, 0 on success */
+ImageGetRow(image_t *img, /* I - Image */
+ int x, /* I - Start column */
+ int y, /* I - Row */
+ int width, /* I - Width of row */
+ ib_t *pixels) /* O - Pixel data */
+{
+ int bpp, /* Bytes per pixel */
+ count; /* Number of pixels to get */
+ const ib_t *ib; /* Pointer to pixels */
+
+
+ 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 /* O - -1 on error, 0 on success */
+ImagePutCol(image_t *img, /* I - Image */
+ int x, /* I - Column */
+ int y, /* I - Start row */
+ int height, /* I - Column height */
+ const ib_t *pixels) /* I - Pixels to put */
+{
+ int bpp, /* Bytes per pixel */
+ twidth, /* Width of tile */
+ count; /* Number of pixels to put */
+ int tilex, /* Column within tile */
+ tiley; /* Row within tile */
+ ib_t *ib; /* Pointer to pixels in tile */
+
+
+ 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 /* O - -1 on error, 0 on success */
+ImagePutRow(image_t *img, /* I - Image */
+ int x, /* I - Start column */
+ int y, /* I - Row */
+ int width, /* I - Row width */
+ const ib_t *pixels) /* I - Pixel data */
+{
+ int bpp, /* Bytes per pixel */
+ count; /* Number of pixels to put */
+ int tilex, /* Column within tile */
+ tiley; /* Row within tile */
+ ib_t *ib; /* Pointer to pixels in tile */
+
+
+ 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 * /* O - Pointer to tile or NULL */
+get_tile(image_t *img, /* I - Image */
+ int x, /* I - Column in image */
+ int y) /* I - Row in image */
+{
+ int bpp, /* Bytes per pixel */
+ tilex, /* Column within tile */
+ tiley, /* Row within tile */
+ xtiles, /* Number of tiles horizontally */
+ ytiles; /* Number of tiles vertically */
+ ic_t *ic; /* Cache pointer */
+ itile_t *tile; /* Tile pointer */
+
+
+ if (x >= img->xsize || y >= img->ysize)
+ {
+ fprintf(stderr, "ERROR: Internal image RIP error - %d,%d is outside of %dx%d\n",
+ x, y, img->xsize, img->ysize);
+ return (NULL);
+ }
+
+ if (img->tiles == NULL)
+ {
+ xtiles = (img->xsize + TILE_SIZE - 1) / TILE_SIZE;
+ ytiles = (img->ysize + TILE_SIZE - 1) / TILE_SIZE;
+
+ fprintf(stderr, "DEBUG: Creating tile array (%dx%d)\n", xtiles, ytiles);
+
+ 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)
+ {
+ ic = calloc(sizeof(ic_t) + bpp * TILE_SIZE * TILE_SIZE, 1);
+ ic->pixels = ((ib_t *)ic) + sizeof(ic_t);
+
+ img->num_ics ++;
+
+ fprintf(stderr, "DEBUG: Allocated cache tile %d (%p)...\n",
+ img->num_ics, ic);
+ }
+ else
+ {
+ fprintf(stderr, "DEBUG: Flushing old cache tile (%p)...\n",
+ img->first);
+
+ flush_tile(img);
+ ic = img->first;
+ }
+
+ ic->tile = tile;
+ tile->ic = ic;
+
+ if (tile->pos >= 0)
+ {
+ fprintf(stderr, "DEBUG: Loading cache tile from file position %ld...\n",
+ tile->pos);
+
+ 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
+ {
+ fputs("DEBUG: Clearing cache tile...\n", stderr);
+
+ memset(ic->pixels, 0, bpp * TILE_SIZE * TILE_SIZE);
+ }
+ }
+
+ if (ic == img->first)
+ {
+ if (ic->next != NULL)
+ ic->next->prev = NULL;
+
+ img->first = ic->next;
+ ic->next = NULL;
+ ic->prev = NULL;
+ }
+ else if (img->first == NULL)
+ img->first = ic;
+
+ if (ic != img->last)
+ {
+ /*
+ * Remove the cache entry from the list...
+ */
+
+ if (ic->prev != NULL)
+ ic->prev->next = ic->next;
+ if (ic->next != NULL)
+ ic->next->prev = ic->prev;
+
+ /*
+ * And add it to the end...
+ */
+
+ 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) /* I - Image */
+{
+ int bpp; /* Bytes per pixel */
+ itile_t *tile; /* Pointer to tile */
+
+
+
+ bpp = ImageGetDepth(img);
+ tile = img->first->tile;
+
+ if (!tile->dirty)
+ {
+ tile->ic = NULL;
+ return;
+ }
+
+ if (img->cachefile == NULL)
+ {
+ cupsTempFile(img->cachename, sizeof(img->cachename));
+
+ fprintf(stderr, "DEBUG: Creating swap file \"%s\"...\n", img->cachename);
+
+ if ((img->cachefile = fopen(img->cachename, "wb+")) == NULL)
+ {
+ perror("ERROR: Unable to create image swap file");
+ tile->ic = NULL;
+ tile->dirty = 0;
+ return;
+ }
+ }
+
+ if (tile->pos >= 0)
+ {
+ if (ftell(img->cachefile) != tile->pos)
+ if (fseek(img->cachefile, tile->pos, SEEK_SET))
+ {
+ perror("ERROR: Unable to seek in swap file");
+ tile->ic = NULL;
+ tile->dirty = 0;
+ return;
+ }
+ }
+ else
+ {
+ if (fseek(img->cachefile, 0, SEEK_END))
+ {
+ perror("ERROR: Unable to append to swap file");
+ tile->ic = NULL;
+ tile->dirty = 0;
+ return;
+ }
+
+ tile->pos = ftell(img->cachefile);
+ }
+
+
+ if (fwrite(tile->ic->pixels, bpp, TILE_SIZE * TILE_SIZE, img->cachefile) < 1)
+ perror("ERROR: Unable to write tile to swap file");
+ else
+ fprintf(stderr, "DEBUG: Wrote tile at position %ld...\n", tile->pos);
+
+ 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..74525c521
--- /dev/null
+++ b/filter/image.h
@@ -0,0 +1,225 @@
+/*
+ * "$Id$"
+ *
+ * Image library definitions for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1993-2000 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, const 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, const ib_t *pixels);
+extern int ImagePutRow(image_t *img, int x, int y, int width, const ib_t *pixels);
+
+/*
+ * File formats...
+ */
+
+extern int ImageReadGIF(image_t *img, FILE *fp, int primary, int secondary,
+ int saturation, int hue, const ib_t *lut);
+extern int ImageReadJPEG(image_t *img, FILE *fp, int primary, int secondary,
+ int saturation, int hue, const ib_t *lut);
+extern int ImageReadPNG(image_t *img, FILE *fp, int primary, int secondary,
+ int saturation, int hue, const ib_t *lut);
+extern int ImageReadPNM(image_t *img, FILE *fp, int primary, int secondary,
+ int saturation, int hue, const ib_t *lut);
+extern int ImageReadPhotoCD(image_t *img, FILE *fp, int primary,
+ int secondary, int saturation, int hue,
+ const ib_t *lut);
+extern int ImageReadSGI(image_t *img, FILE *fp, int primary, int secondary,
+ int saturation, int hue, const ib_t *lut);
+extern int ImageReadSunRaster(image_t *img, FILE *fp, int primary,
+ int secondary, int saturation, int hue,
+ const ib_t *lut);
+extern int ImageReadTIFF(image_t *img, FILE *fp, int primary, int secondary,
+ int saturation, int hue, const ib_t *lut);
+
+/*
+ * Colorspace conversions...
+ */
+
+extern void ImageWhiteToWhite(const ib_t *in, ib_t *out, int count);
+extern void ImageWhiteToRGB(const ib_t *in, ib_t *out, int count);
+extern void ImageWhiteToBlack(const ib_t *in, ib_t *out, int count);
+extern void ImageWhiteToCMY(const ib_t *in, ib_t *out, int count);
+extern void ImageWhiteToCMYK(const ib_t *in, ib_t *out, int count);
+
+extern void ImageRGBToWhite(const ib_t *in, ib_t *out, int count);
+extern void ImageRGBToRGB(const ib_t *in, ib_t *out, int count);
+extern void ImageRGBToBlack(const ib_t *in, ib_t *out, int count);
+extern void ImageRGBToCMY(const ib_t *in, ib_t *out, int count);
+extern void ImageRGBToCMYK(const 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, const 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..0ac004f4a
--- /dev/null
+++ b/filter/imagetops.c
@@ -0,0 +1,564 @@
+/*
+ * "$Id$"
+ *
+ * Image file to PostScript filter for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1993-2000 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,
+ xsize2,
+ ysize2;
+ 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);
+
+ 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 = strcasecmp(val, "separate-documents-collated-copies") != 0;
+ }
+
+ if ((val = cupsGetOption("Collate", num_options, options)) != NULL &&
+ strcasecmp(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...
+ */
+
+ if (zoom == 0.0 && ppi == 0)
+ ppi = img->xppi;
+
+ xprint = (PageRight - PageLeft) / 72.0;
+ yprint = (PageTop - PageBottom) / 72.0;
+
+ 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;
+
+ /*
+ * Rotate the image if it will fit landscape but not portrait...
+ */
+
+ if ((xinches > xprint || yinches > yprint) &&
+ xinches <= yprint && yinches <= xprint)
+ {
+ /*
+ * Rotate the image as needed...
+ */
+
+ Orientation = (Orientation + 1) & 3;
+ xsize = yprint;
+ yprint = xprint;
+ xprint = xsize;
+
+ xsize = PageLeft;
+ PageLeft = PageBottom;
+ PageBottom = PageWidth - PageRight;
+ PageRight = PageTop;
+ PageTop = PageLength - xsize;
+
+ xsize = PageWidth;
+ PageWidth = PageLength;
+ PageLength = xsize;
+ }
+ }
+ 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;
+ }
+
+ xsize2 = yprint * zoom;
+ ysize2 = xsize2 * img->ysize / img->xsize;
+
+ if (ysize2 > (xprint * zoom))
+ {
+ ysize2 = xprint * zoom;
+ xsize2 = ysize2 * img->xsize / img->ysize;
+ }
+
+ /*
+ * Choose the rotation with the largest area, but prefer
+ * portrait if they are equal...
+ */
+
+ if ((xsize * ysize) < (xsize2 * xsize2))
+ {
+ /*
+ * Do landscape orientation...
+ */
+
+ Orientation = 1;
+ xinches = xsize2;
+ yinches = ysize2;
+ xprint = (PageTop - PageBottom) / 72.0;
+ yprint = (PageRight - PageLeft) / 72.0;
+
+ xsize = PageLeft;
+ PageLeft = PageBottom;
+ PageBottom = PageWidth - PageRight;
+ PageRight = PageTop;
+ PageTop = PageLength - xsize;
+
+ xsize = PageWidth;
+ PageWidth = PageLength;
+ PageLength = xsize;
+ }
+ else
+ {
+ /*
+ * Do portrait orientation...
+ */
+
+ Orientation = 0;
+ 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? */
+{
+ 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)
+ {
+ memset(data + length, 0, 4 - length);
+ b = (((((data[0] << 8) | data[1]) << 8) | data[2]) << 8) | data[3];
+
+ 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..67160b6e2
--- /dev/null
+++ b/filter/imagetoraster.c
@@ -0,0 +1,3975 @@
+/*
+ * "$Id$"
+ *
+ * Image file to raster filter for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1993-2000 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 "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,
+ xsize2,
+ ysize2;
+ 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 */
+ ppd_profile_t userprofile; /* User-specified 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);
+
+ 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 = strcasecmp(val, "separate-documents-collated-copies") != 0;
+ }
+
+ if ((val = cupsGetOption("Collate", num_options, options)) != NULL &&
+ strcasecmp(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, "CutMedia")) != NULL)
+ exec_choice(&header, choice);
+
+ if ((choice = ppdFindMarkedChoice(ppd, "ESPFinishing")) != 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...
+ */
+
+ 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 ((val = cupsGetOption("profile", num_options, options)) != NULL)
+ {
+ profile = &userprofile;
+ sscanf(val, "%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f",
+ &(userprofile.density), &(userprofile.gamma),
+ userprofile.matrix[0] + 0, userprofile.matrix[0] + 1,
+ userprofile.matrix[0] + 2,
+ userprofile.matrix[1] + 0, userprofile.matrix[1] + 1,
+ userprofile.matrix[1] + 2,
+ userprofile.matrix[2] + 0, userprofile.matrix[2] + 1,
+ userprofile.matrix[2] + 2);
+
+ userprofile.density *= 0.001f;
+ userprofile.gamma *= 0.001f;
+ userprofile.matrix[0][0] *= 0.001f;
+ userprofile.matrix[0][1] *= 0.001f;
+ userprofile.matrix[0][2] *= 0.001f;
+ userprofile.matrix[1][0] *= 0.001f;
+ userprofile.matrix[1][1] *= 0.001f;
+ userprofile.matrix[1][2] *= 0.001f;
+ userprofile.matrix[2][0] *= 0.001f;
+ userprofile.matrix[2][1] *= 0.001f;
+ userprofile.matrix[2][2] *= 0.001f;
+ }
+ else if (ppd != NULL)
+ {
+ fprintf(stderr, "DEBUG: Searching for profile \"%s/%s\"...\n",
+ resolution, media_type);
+
+ for (i = 0, profile = ppd->profiles; i < ppd->num_profiles; i ++, profile ++)
+ {
+ fprintf(stderr, "DEBUG: \"%s/%s\" = ", profile->resolution,
+ profile->media_type);
+
+ if ((strcmp(profile->resolution, resolution) == 0 ||
+ profile->resolution[0] == '-') &&
+ (strcmp(profile->media_type, media_type) == 0 ||
+ profile->media_type[0] == '-'))
+ {
+ fputs("MATCH!\n", stderr);
+ break;
+ }
+ else
+ fputs("no.\n", stderr);
+ }
+
+ /*
+ * If we found a color profile, use it!
+ */
+
+ if (i >= ppd->num_profiles)
+ profile = NULL;
+ }
+ else
+ profile = NULL;
+
+ if (profile)
+ 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 (zoom == 0.0 && ppi == 0)
+ ppi = img->xppi;
+
+ if (ppi > 0)
+ {
+ /*
+ * Scale the image as neccesary to match the desired pixels-per-inch.
+ */
+
+ if (Orientation & 1)
+ {
+ xprint = (PageTop - PageBottom) / 72.0;
+ yprint = (PageRight - PageLeft) / 72.0;
+ }
+ else
+ {
+ xprint = (PageRight - PageLeft) / 72.0;
+ yprint = (PageTop - PageBottom) / 72.0;
+ }
+
+ xinches = (float)img->xsize / (float)ppi;
+ yinches = (float)img->ysize / (float)ppi;
+
+ /*
+ * Rotate the image if it will fit landscape but not portrait...
+ */
+
+ if ((xinches > xprint || yinches > yprint) &&
+ xinches <= yprint && yinches <= xprint)
+ {
+ /*
+ * Rotate the image as needed...
+ */
+
+ Orientation = (Orientation + 1) & 3;
+ xsize = yprint;
+ yprint = xprint;
+ xprint = xsize;
+ }
+ }
+ else
+ {
+ /*
+ * Scale percentage of page size...
+ */
+
+ xprint = (PageRight - PageLeft) / 72.0;
+ yprint = (PageTop - PageBottom) / 72.0;
+
+ xsize = xprint * zoom;
+ ysize = xsize * img->ysize / img->xsize;
+
+ if (ysize > (yprint * zoom))
+ {
+ ysize = yprint * zoom;
+ xsize = ysize * img->xsize / img->ysize;
+ }
+
+ xsize2 = yprint * zoom;
+ ysize2 = xsize2 * img->ysize / img->xsize;
+
+ if (ysize2 > (xprint * zoom))
+ {
+ ysize2 = xprint * zoom;
+ xsize2 = ysize2 * img->xsize / img->ysize;
+ }
+
+ /*
+ * Choose the rotation with the largest area, but prefer
+ * portrait if they are equal...
+ */
+
+ if ((xsize * ysize) < (xsize2 * xsize2))
+ {
+ /*
+ * Do landscape orientation...
+ */
+
+ Orientation = 1;
+ xinches = xsize2;
+ yinches = ysize2;
+ xprint = (PageTop - PageBottom) / 72.0;
+ yprint = (PageRight - PageLeft) / 72.0;
+ }
+ else
+ {
+ /*
+ * Do portrait orientation...
+ */
+
+ Orientation = 0;
+ xinches = xsize;
+ yinches = ysize;
+ }
+ }
+
+ xpages = ceil(xinches / xprint);
+ ypages = ceil(yinches / yprint);
+
+ /*
+ * Compute the bitmap size...
+ */
+
+ xprint = xinches / xpages;
+ yprint = yinches / ypages;
+
+ if ((choice = ppdFindMarkedChoice(ppd, "PageSize")) != NULL &&
+ strcasecmp(choice->choice, "Custom") == 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;
+ }
+
+ header.Margins[0] = PageLeft;
+ header.Margins[1] = PageBottom;
+
+ switch (Orientation)
+ {
+ case 0 :
+ header.ImagingBoundingBox[0] = PageLeft;
+ header.ImagingBoundingBox[1] = PageBottom;
+ header.ImagingBoundingBox[2] = PageLeft + xprint * 72;
+ header.ImagingBoundingBox[3] = PageBottom + yprint * 72;
+ break;
+ case 1 :
+ header.ImagingBoundingBox[0] = PageRight - yprint * 72;
+ header.ImagingBoundingBox[1] = PageBottom;
+ header.ImagingBoundingBox[2] = PageRight;
+ header.ImagingBoundingBox[3] = PageBottom + xprint * 72;
+ break;
+ case 2 :
+ header.ImagingBoundingBox[0] = PageRight - xprint * 72;
+ header.ImagingBoundingBox[1] = PageTop - yprint * 72;
+ header.ImagingBoundingBox[2] = PageRight;
+ header.ImagingBoundingBox[3] = PageTop;
+ break;
+ case 3 :
+ header.ImagingBoundingBox[0] = PageLeft;
+ header.ImagingBoundingBox[1] = PageTop - xprint * 72;
+ header.ImagingBoundingBox[2] = PageLeft + yprint * 72 + 0.5f;
+ header.ImagingBoundingBox[3] = PageTop;
+ break;
+ }
+
+ switch (header.cupsColorOrder)
+ {
+ default :
+ 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(2 * 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, "CutMedia") == 0)
+ header->CutMedia = (cups_cut_t)atoi(value);
+ else if (strcmp(name, "HWResolution") == 0)
+ sscanf(value, "%d%d", header->HWResolution + 0, header->HWResolution + 1);
+ else if (strcmp(name, "cupsMediaPosition") == 0 || /* Compatibility */
+ strcmp(name, "MediaPosition") == 0)
+ header->MediaPosition = atoi(value);
+ else if (strcmp(name, "MediaType") == 0)
+ strncpy(header->MediaType, value, sizeof(header->MediaType) - 1);
+ else if (strcmp(name, "OutputType") == 0)
+ strncpy(header->OutputType, value, sizeof(header->OutputType) - 1);
+ }
+}
+
+
+/*
+ * '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 / 3;
+
+ 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 / 3;
+
+ 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..36e0389f8
--- /dev/null
+++ b/filter/pstops.c
@@ -0,0 +1,870 @@
+/*
+ * "$Id$"
+ *
+ * PostScript filter for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1993-2000 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 */
+ int page_count; /* Page count for NUp */
+ int subpage; /* Sub-page number */
+ int copy; /* Current copy */
+
+
+ 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]);
+
+ options = NULL;
+ num_options = cupsParseOptions(argv[5], 0, &options);
+
+ ppd = SetCommonOptions(num_options, options, 1);
+
+ 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 = strcasecmp(val, "separate-documents-collated-copies") != 0;
+ }
+
+ if ((val = cupsGetOption("Collate", num_options, options)) != NULL &&
+ strcasecmp(val, "True") == 0)
+ Collate = 1;
+
+ if ((val = cupsGetOption("OutputOrder", num_options, options)) != NULL &&
+ strcasecmp(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;
+ }
+ else
+ temp = 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);
+ }
+
+ /*
+ * 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 - 1)) == 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 (strcmp(line, "%%Trailer") == 0 && level == 0)
+ break;
+ 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 - 1);
+
+ if (NumPages & (NUp - 1))
+ {
+ start_nup(NUp - 1);
+ end_nup(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 - 1)) == 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);
+ }
+
+ if (NumPages & (NUp - 1))
+ {
+ start_nup(NUp - 1);
+ end_nup(NUp - 1);
+ }
+
+ Copies --;
+ }
+ }
+ else
+ {
+ page_count = (NumPages + NUp - 1) / NUp;
+ copy = 0;
+
+ do
+ {
+ for (page = page_count - 1; page >= 0; page --)
+ {
+ if (ppd == NULL || ppd->num_filters == 0)
+ fprintf(stderr, "PAGE: %d %d\n", page + 1,
+ slowcollate ? 1 : Copies);
+
+ if (slowcollate)
+ printf("%%%%Page: %d %d\n", page + 1,
+ page_count - page + copy * page_count);
+ else
+ printf("%%%%Page: %d %d\n", page + 1, page_count - page);
+
+ ppdEmit(ppd, stdout, PPD_ORDER_PAGE);
+
+ for (subpage = 0, number = page * NUp;
+ subpage < NUp && number < NumPages;
+ subpage ++, number ++)
+ {
+ start_nup(number);
+ fseek(temp, Pages[number], SEEK_SET);
+ copy_bytes(temp, Pages[number + 1] - Pages[number]);
+ end_nup(number);
+ }
+
+ if (number & (NUp - 1))
+ {
+ start_nup(NUp - 1);
+ end_nup(NUp - 1);
+ }
+ }
+
+ copy ++;
+ }
+ while (copy < Copies && slowcollate);
+ }
+ }
+
+ /*
+ * Copy the trailer, if any...
+ */
+
+ while ((nbytes = fread(line, 1, sizeof(line), fp)) > 0)
+ fwrite(line, 1, nbytes, stdout);
+ }
+ 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 (strcasecmp(PageSet, "even") == 0 && (page & 1))
+ return (0);
+ if (strcasecmp(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 (nleft > sizeof(buffer))
+ nbytes = sizeof(buffer);
+ else
+ nbytes = nleft;
+
+ if ((nbytes = fread(buffer, 1, nbytes, 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("ESPsave restore");
+
+ 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;
+ ch = EOF;
+
+ 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 */
+ float pw, pl; /* Printable width and length of full page */
+
+
+ if (Flip || Orientation || NUp > 1)
+ puts("/ESPsave save def");
+
+ if (Flip)
+ printf("%.0f 0 translate -1 1 scale\n", PageWidth);
+
+ pw = PageRight - PageLeft;
+ pl = PageTop - PageBottom;
+
+ 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 = pl;
+ l = w * PageLength / PageWidth;
+
+ if (l > (pw * 0.5))
+ {
+ l = pw * 0.5;
+ w = l * PageWidth / PageLength;
+ }
+
+ tx = pw * 0.5 - l;
+ ty = (pl - w) * 0.5;
+ }
+ else
+ {
+ l = pw;
+ w = l * PageWidth / PageLength;
+
+ if (w > (pl * 0.5))
+ {
+ w = pl * 0.5;
+ l = w * PageLength / PageWidth;
+ }
+
+ tx = pl * 0.5 - w;
+ ty = (pw - l) * 0.5;
+ }
+
+ if (Duplex && (number & 2))
+ printf("%.0f %.0f translate\n", PageWidth - PageRight, PageBottom);
+ else
+ printf("%.0f %.0f translate\n", PageLeft, PageBottom);
+
+ if (Orientation & 1)
+ {
+ printf("0 %.0f translate -90 rotate\n", pl);
+ printf("%.0f %.0f translate %.3f %.3f scale\n",
+ ty, tx + l * x, w / pw, l / pl);
+ }
+ else
+ {
+ printf("%.0f 0 translate 90 rotate\n", pw);
+ printf("%.0f %.0f translate %.3f %.3f scale\n",
+ tx + w * x, ty, w / pw, l / pl);
+ }
+
+ 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 = pw * 0.5;
+ l = w * PageLength / PageWidth;
+
+ if (l > (pl * 0.5))
+ {
+ l = pl * 0.5;
+ w = l * PageWidth / PageLength;
+ }
+
+ if (Duplex && (number & 4))
+ printf("%.0f %.0f translate\n", PageWidth - PageRight, PageBottom);
+ else
+ printf("%.0f %.0f translate\n", PageLeft, PageBottom);
+
+ printf("%.0f %.0f translate %.3f %.3f scale\n", x * w, y * l,
+ 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;
+ }
+}
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/filter/raster.c b/filter/raster.c
new file mode 100644
index 000000000..be4f46623
--- /dev/null
+++ b/filter/raster.c
@@ -0,0 +1,252 @@
+/*
+ * "$Id$"
+ *
+ * Raster file routines for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-2000 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/filter/raster.h b/filter/raster.h
new file mode 100644
index 000000000..2c4052d56
--- /dev/null
+++ b/filter/raster.h
@@ -0,0 +1,233 @@
+/*
+ * "$Id$"
+ *
+ * Raster file definitions for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-2000 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/filter/rastertoepson.c b/filter/rastertoepson.c
new file mode 100644
index 000000000..3c2dd68b4
--- /dev/null
+++ b/filter/rastertoepson.c
@@ -0,0 +1,596 @@
+/*
+ * "$Id$"
+ *
+ * EPSON ESC/P and ESC/P2 filter for the Common UNIX Printing System
+ * (CUPS).
+ *
+ * Copyright 1993-2000 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:
+ *
+ * Setup() - Prepare the printer for printing.
+ * StartPage() - Start a page of graphics.
+ * EndPage() - Finish a page of graphics.
+ * Shutdown() - Shutdown the printer.
+ * CompressData() - Compress a line of graphics.
+ * OutputLine() - Output a line of graphics.
+ * main() - Main entry and processing of driver.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include <cups/cups.h>
+#include <cups/ppd.h>
+#include <cups/string.h>
+#include "raster.h"
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+
+/*
+ * Macros...
+ */
+
+#define pwrite(s,n) fwrite((s), 1, (n), stdout)
+
+
+/*
+ * Globals...
+ */
+
+unsigned char *Planes[6], /* 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, ppd_file_t *ppd);
+void EndPage(cups_page_header_t *header);
+void Shutdown(void);
+
+void CompressData(unsigned char *line, int length, int plane, int type,
+ int xstep, int ystep);
+void OutputLine(cups_page_header_t *header);
+
+
+/*
+ * 'Setup()' - Prepare the printer for printing.
+ */
+
+void
+Setup(void)
+{
+ /*
+ * Send a reset sequence.
+ */
+
+ printf("\033@");
+}
+
+
+/*
+ * 'StartPage()' - Start a page of graphics.
+ */
+
+void
+StartPage(cups_page_header_t *header, /* I - Page header */
+ ppd_file_t *ppd) /* I - PPD file */
+{
+ int n, t; /* Numbers */
+ int plane; /* Looping var */
+
+
+ /*
+ * Send a reset sequence.
+ */
+
+ printf("\033@");
+
+ /*
+ * Set graphics mode...
+ */
+
+ pwrite("\033(G\001\000\001", 6); /* Graphics mode */
+
+ /*
+ * Set the media size...
+ */
+
+ pwrite("\033(U\001\000", 5); /* Resolution/units */
+ putchar(3600 / header->HWResolution[1]);
+
+ n = header->PageSize[1] * header->HWResolution[1] / 72.0;
+
+ pwrite("\033(C\002\000", 5); /* Page length */
+ putchar(n);
+ putchar(n >> 8);
+
+ t = (ppd->sizes[1].length - ppd->sizes[1].top) *
+ header->HWResolution[1] / 72.0;
+
+ pwrite("\033(c\004\000", 5); /* Top & bottom margins */
+ putchar(t);
+ putchar(t >> 8);
+ putchar(n);
+ putchar(n >> 8);
+
+ /*
+ * Set other stuff...
+ */
+
+ if (header->cupsColorSpace == CUPS_CSPACE_CMY)
+ NumPlanes = 3;
+ else if (header->cupsColorSpace == CUPS_CSPACE_KCMY)
+ NumPlanes = 4;
+ else if (header->cupsColorSpace == CUPS_CSPACE_KCMYcm)
+ NumPlanes = 6;
+ else
+ NumPlanes = 1;
+
+ if (header->HWResolution[1] == 720)
+ {
+ pwrite("\033(i\001\000\001", 6); /* Microweave */
+ pwrite("\033(e\002\000\000\001", 7);/* Small dots */
+ }
+
+ pwrite("\033(V\002\000\000\000", 7); /* Set absolute position 0 */
+
+ 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...
+ */
+
+ putchar(12); /* Form feed */
+
+ /*
+ * Free memory...
+ */
+
+ free(Planes[0]);
+
+ if (header->cupsCompression)
+ free(CompBuffer);
+}
+
+
+/*
+ * 'Shutdown()' - Shutdown the printer.
+ */
+
+void
+Shutdown(void)
+{
+ /*
+ * Send a reset sequence.
+ */
+
+ printf("\033@");
+}
+
+
+/*
+ * '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 */
+ int xstep, /* I - X resolution */
+ int ystep) /* I - Y resolution */
+{
+ 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 */
+ temp; /* Current byte */
+ int count; /* Count of bytes for output */
+ static int ctable[6] = { 0, 2, 1, 4, 2, 1 };
+ /* KCMYcm color values */
+
+
+ /*
+ * Setup pointers...
+ */
+
+ line_ptr = line;
+ line_end = line + length;
+
+ /*
+ * Do depletion for 720 DPI printing...
+ */
+
+ if (ystep == 5)
+ {
+ for (comp_ptr = line; comp_ptr < line_end;)
+ {
+ /*
+ * Grab the current byte...
+ */
+
+ temp = *comp_ptr;
+
+ /*
+ * Check adjacent bits...
+ */
+
+ if ((temp & 0xc0) == 0xc0)
+ temp &= 0xbf;
+ if ((temp & 0x60) == 0x60)
+ temp &= 0xdf;
+ if ((temp & 0x30) == 0x30)
+ temp &= 0xef;
+ if ((temp & 0x18) == 0x18)
+ temp &= 0xf7;
+ if ((temp & 0x0c) == 0x0c)
+ temp &= 0xfb;
+ if ((temp & 0x06) == 0x06)
+ temp &= 0xfd;
+ if ((temp & 0x03) == 0x03)
+ temp &= 0xfe;
+
+ *comp_ptr++ = temp;
+
+ /*
+ * Check the last bit in the current byte and the first bit in the
+ * next byte...
+ */
+
+ if ((temp & 0x01) && comp_ptr < line_end && *comp_ptr & 0x80)
+ *comp_ptr &= 0x7f;
+ }
+ }
+
+ switch (type)
+ {
+ case 0 :
+ /*
+ * Do no compression...
+ */
+ break;
+
+ case 1 :
+ /*
+ * Do TIFF pack-bits encoding...
+ */
+
+ 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 color if necessary...
+ */
+
+ if (NumPlanes > 1)
+ {
+ if (plane > 3)
+ printf("\033(r%c%c%c%c", 2, 0, 1, ctable[plane]);
+ /* Set extended color */
+ else if (NumPlanes == 3)
+ printf("\033r%c", ctable[plane + 1]);
+ /* Set color */
+ else
+ printf("\033r%c", ctable[plane]); /* Set color */
+ }
+
+ /*
+ * Send a raster plane...
+ */
+
+ putchar(0x0d); /* Move print head to left margin */
+
+ length *= 8;
+ printf("\033."); /* Raster graphics */
+ putchar(type);
+ putchar(ystep);
+ putchar(xstep);
+ putchar(1);
+ putchar(length);
+ putchar(length >> 8);
+
+ pwrite(line_ptr, line_end - line_ptr);
+}
+
+
+/*
+ * 'OutputLine()' - Output a line of graphics.
+ */
+
+void
+OutputLine(cups_page_header_t *header) /* I - Page header */
+{
+ int plane; /* Current plane */
+ int bytes; /* Bytes per plane */
+ int xstep, ystep; /* X & Y resolutions */
+
+ /*
+ * Write bitmap data as needed...
+ */
+
+ xstep = 3600 / header->HWResolution[0];
+ ystep = 3600 / header->HWResolution[1];
+ bytes = header->cupsBytesPerLine / NumPlanes;
+
+ for (plane = 0; plane < NumPlanes; plane ++)
+ {
+ /*
+ * Skip blank data...
+ */
+
+ if (!Planes[plane][0] &&
+ memcmp(Planes[plane], Planes[plane] + 1, bytes - 1) == 0)
+ continue;
+
+ /*
+ * Output whitespace as needed...
+ */
+
+ if (Feed > 0)
+ {
+ pwrite("\033(v\002\000", 5); /* Relative vertical position */
+ putchar(Feed);
+ putchar(Feed >> 8);
+
+ Feed = 0;
+ }
+
+ CompressData(Planes[plane], bytes, plane, header->cupsCompression, xstep,
+ ystep);
+ }
+
+ Feed ++;
+}
+
+
+/*
+ * '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 */
+ ppd_file_t *ppd; /* PPD 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 return.
+ */
+
+ fputs("ERROR: rastertoepson job-id user title copies options [file]\n", stderr);
+ 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...
+ */
+
+ ppd = ppdOpenFile(getenv("PPD"));
+
+ 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, ppd);
+
+ /*
+ * 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;
+
+ /*
+ * Write it to the printer...
+ */
+
+ OutputLine(&header);
+ }
+
+ /*
+ * Eject the page...
+ */
+
+ EndPage(&header);
+ }
+
+ /*
+ * Shutdown the printer...
+ */
+
+ Shutdown();
+
+ ppdClose(ppd);
+
+ /*
+ * 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);
+
+ return (page == 0);
+}
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/filter/rastertohp.c b/filter/rastertohp.c
new file mode 100644
index 000000000..842607a99
--- /dev/null
+++ b/filter/rastertohp.c
@@ -0,0 +1,503 @@
+/*
+ * "$Id$"
+ *
+ * Hewlett-Packard Page Control Language filter for the Common UNIX
+ * Printing System (CUPS).
+ *
+ * Copyright 1993-2000 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:
+ *
+ * Setup() - Prepare the printer for printing.
+ * StartPage() - Start a page of graphics.
+ * EndPage() - Finish a page of graphics.
+ * Shutdown() - Shutdown the printer.
+ * CompressData() - Compress a line of graphics.
+ * OutputLine() - Output a line of graphics.
+ * main() - Main entry and processing of driver.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include <cups/cups.h>
+#include <cups/string.h>
+#include "raster.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)
+ {
+ default :
+ /*
+ * 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 return.
+ */
+
+ fputs("ERROR: rastertopcl job-id user title copies options [file]\n", stderr);
+ 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);
+
+ return (page == 0);
+}
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/filter/textcommon.c b/filter/textcommon.c
new file mode 100644
index 000000000..a0b18dd7b
--- /dev/null
+++ b/filter/textcommon.c
@@ -0,0 +1,746 @@
+/*
+ * "$Id$"
+ *
+ * Common text filter routines for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-2000 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...
+ */
+
+ lastch = 0;
+ 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][column].attr |= ATTR_BOLD;
+ }
+ else if ((ch == '/' || ch == '*') && lastch == '/' &&
+ column < ColumnWidth)
+ {
+ /*
+ * Highlight first comment character...
+ */
+
+ Page[line][column - 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..354ce7ae5
--- /dev/null
+++ b/filter/textcommon.h
@@ -0,0 +1,93 @@
+/*
+ * "$Id$"
+ *
+ * Common text filter definitions for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-2000 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_NORMAL 0x00
+#define ATTR_BOLD 0x01
+#define ATTR_ITALIC 0x02
+#define ATTR_BOLDITALIC 0x03
+#define ATTR_FONT 0x03
+
+#define ATTR_UNDERLINE 0x04
+#define ATTR_RAISED 0x08
+#define ATTR_LOWERED 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..bfdc8f434
--- /dev/null
+++ b/filter/texttops.c
@@ -0,0 +1,1205 @@
+/*
+ * "$Id$"
+ *
+ * Text to PostScript filter for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1993-2000 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 */
+int NumFonts; /* Number of fonts to use */
+char *Fonts[256][4]; /* Fonts to use */
+unsigned short Chars[65536]; /* 0xffcc (ff = font, cc = char) */
+unsigned short Codes[65536]; /* Unicode glyph mapping to fonts */
+int Widths[256]; /* Widths of each font */
+int Directions[256];/* Text directions for each font */
+
+
+/*
+ * Local functions...
+ */
+
+static void write_line(int row, lchar_t *line);
+static void write_string(int col, int row, int len, lchar_t *s);
+static void write_text(char *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 i, j, k; /* Looping vars */
+ char *charset; /* Character set string */
+ char filename[1024]; /* Glyph filenames */
+ FILE *fp; /* Glyph files */
+ char line[1024], /* Line from file */
+ *lineptr, /* Pointer into line */
+ *valptr; /* Pointer to value in line */
+ int ch, unicode; /* Character values */
+ int start, end; /* Start and end values for range */
+ char glyph[64]; /* Glyph name */
+ time_t curtime; /* Current time */
+ struct tm *curtm; /* Current date */
+ char curdate[255]; /* Current date (text format) */
+ int num_fonts; /* Number of unique fonts */
+ char *fonts[1024]; /* Unique fonts */
+ static char *names[] = /* Font names */
+ {
+ "cupsNormal",
+ "cupsBold",
+ "cupsItalic"
+ };
+
+
+ /*
+ * Allocate memory for the page...
+ */
+
+ 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 (i = 1; i < SizeLines; i ++)
+ Page[i] = Page[0] + i * SizeColumns;
+
+ if (PageColumns > 1)
+ {
+ ColumnGutter = CharsPerInch / 2;
+ ColumnWidth = (SizeColumns - ColumnGutter * (PageColumns - 1)) /
+ PageColumns;
+ }
+ else
+ ColumnWidth = SizeColumns;
+
+ /*
+ * Output the DSC header...
+ */
+
+ 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);
+ puts("%%DocumentSuppliedResources: procset texttops 1.1 0");
+ puts("%%Pages: (atend)");
+
+ /*
+ * Initialize globals...
+ */
+
+ NumFonts = 0;
+ memset(Fonts, 0, sizeof(Fonts));
+ memset(Glyphs, 0, sizeof(Glyphs));
+ memset(Chars, 0, sizeof(Chars));
+ memset(Codes, 0, sizeof(Codes));
+
+ /*
+ * Load the PostScript glyph names and the corresponding character
+ * set definition...
+ */
+
+ if ((fp = fopen(CUPS_DATADIR "/data/psglyphs", "r")) != NULL)
+ {
+ while (fscanf(fp, "%x%63s", &unicode, glyph) == 2)
+ Glyphs[unicode] = strdup(glyph);
+
+ fclose(fp);
+ }
+ else
+ {
+ perror("ERROR: Unable to open " CUPS_DATADIR "/data/psglyphs");
+ exit(1);
+ }
+
+ /*
+ * Get the output character set...
+ */
+
+ charset = getenv("CHARSET");
+ if (charset != NULL && strcmp(charset, "us-ascii") != 0)
+ {
+ snprintf(filename, sizeof(filename), CUPS_DATADIR "/charsets/%s", charset);
+
+ if ((fp = fopen(filename, "r")) == NULL)
+ {
+ /*
+ * Can't open charset file!
+ */
+
+ fprintf(stderr, "ERROR: Unable to open %s: %s\n", filename,
+ strerror(errno));
+ exit(1);
+ }
+
+ /*
+ * Opened charset file; now see if this is really a charset file...
+ */
+
+ if (fgets(line, sizeof(line), fp) == NULL)
+ {
+ /*
+ * Bad/empty charset file!
+ */
+
+ fclose(fp);
+ fprintf(stderr, "ERROR: Bad/empty charset file %s\n", filename);
+ exit(1);
+ }
+
+ if (strncmp(line, "charset", 7) != 0)
+ {
+ /*
+ * Bad format/not a charset file!
+ */
+
+ fclose(fp);
+ fprintf(stderr, "ERROR: Bad charset file %s\n", filename);
+ exit(1);
+ }
+
+ /*
+ * See if this is an 8-bit or UTF-8 character set file...
+ */
+
+ line[strlen(line) - 1] = '\0'; /* Drop \n */
+ for (lineptr = line + 7; isspace(*lineptr); lineptr ++); /* Skip whitespace */
+
+ if (strcmp(lineptr, "8bit") == 0)
+ {
+ /*
+ * 8-bit text...
+ */
+
+ UTF8 = 0;
+ NumFonts = 0;
+
+ /*
+ * Read the font description(s)...
+ */
+
+ while (fgets(line, sizeof(line), fp) != NULL)
+ {
+ /*
+ * Skip comment and blank lines...
+ */
+
+ if (line[0] == '#' || line[0] == '\n')
+ continue;
+
+ /*
+ * Read the font descriptions that should look like:
+ *
+ * first last direction width normal [bold italic bold-italic]
+ */
+
+ lineptr = line;
+
+ start = strtol(lineptr, &lineptr, 16);
+ end = strtol(lineptr, &lineptr, 16);
+
+ while (isspace(*lineptr))
+ lineptr ++;
+
+ if (!*lineptr)
+ break; /* Must be a font mapping */
+
+ valptr = lineptr;
+
+ while (!isspace(*lineptr) && *lineptr)
+ lineptr ++;
+
+ if (!*lineptr)
+ {
+ /*
+ * Can't have a font without all required values...
+ */
+
+ fprintf(stderr, "ERROR: bad font description line: %s\n", valptr);
+ fclose(fp);
+ exit(1);
+ }
+
+ *lineptr++ = '\0';
+
+ if (strcmp(valptr, "ltor") == 0)
+ Directions[NumFonts] = 1;
+ else if (strcmp(valptr, "rtol") == 0)
+ Directions[NumFonts] = -1;
+ else
+ {
+ fprintf(stderr, "ERROR: Bad text direction %s\n", valptr);
+ fclose(fp);
+ exit(1);
+ }
+
+ /*
+ * Got the direction, now get the width...
+ */
+
+ while (isspace(*lineptr))
+ lineptr ++;
+
+ valptr = lineptr;
+
+ while (!isspace(*lineptr) && *lineptr)
+ lineptr ++;
+
+ if (!*lineptr)
+ {
+ /*
+ * Can't have a font without all required values...
+ */
+
+ fprintf(stderr, "ERROR: bad font description line: %s\n", valptr);
+ fclose(fp);
+ exit(1);
+ }
+
+ *lineptr++ = '\0';
+
+ if (strcmp(valptr, "single") == 0)
+ Widths[NumFonts] = 1;
+ else if (strcmp(valptr, "double") == 0)
+ Widths[NumFonts] = 2;
+ else
+ {
+ fprintf(stderr, "ERROR: Bad text width %s\n", valptr);
+ fclose(fp);
+ exit(1);
+ }
+
+ /*
+ * Get the fonts...
+ */
+
+ for (i = 0; *lineptr && i < 4; i ++)
+ {
+ while (isspace(*lineptr))
+ lineptr ++;
+
+ valptr = lineptr;
+
+ while (!isspace(*lineptr) && *lineptr)
+ lineptr ++;
+
+ if (*lineptr)
+ *lineptr++ = '\0';
+
+ if (lineptr > valptr)
+ Fonts[NumFonts][i] = strdup(valptr);
+ }
+
+ /*
+ * Fill in remaining fonts as needed...
+ */
+
+ for (j = i; j < 4; j ++)
+ Fonts[NumFonts][j] = strdup(Fonts[NumFonts][0]);
+
+ /*
+ * Define the character mappings...
+ */
+
+ for (i = start, j = NumFonts * 256; i <= end; i ++, j ++)
+ Chars[i] = j;
+
+ NumFonts ++;
+ }
+
+ /*
+ * Read encoding lines...
+ */
+
+ do
+ {
+ /*
+ * Skip comment and blank lines...
+ */
+
+ if (line[0] == '#' || line[0] == '\n')
+ continue;
+
+ /*
+ * Grab the character and unicode glyph number.
+ */
+
+ if (sscanf(line, "%x%x", &ch, &unicode) == 2 && ch < 256)
+ Codes[Chars[ch]] = unicode;
+ }
+ while (fgets(line, sizeof(line), fp) != NULL);
+
+ fclose(fp);
+ }
+ else if (strcmp(lineptr, "utf8") == 0)
+ {
+ /*
+ * UTF-8 (Unicode) text...
+ */
+
+ UTF8 = 1;
+
+ /*
+ * Read the font descriptions...
+ */
+
+ NumFonts = 0;
+
+ while (fgets(line, sizeof(line), fp) != NULL)
+ {
+ /*
+ * Skip comment and blank lines...
+ */
+
+ if (line[0] == '#' || line[0] == '\n')
+ continue;
+
+ /*
+ * Read the font descriptions that should look like:
+ *
+ * start end direction width normal [bold italic bold-italic]
+ */
+
+ lineptr = line;
+
+ start = strtol(lineptr, &lineptr, 16);
+ end = strtol(lineptr, &lineptr, 16);
+
+ while (isspace(*lineptr))
+ lineptr ++;
+
+ valptr = lineptr;
+
+ while (!isspace(*lineptr) && *lineptr)
+ lineptr ++;
+
+ if (!*lineptr)
+ {
+ /*
+ * Can't have a font without all required values...
+ */
+
+ fprintf(stderr, "ERROR: bad font description line: %s\n", valptr);
+ fclose(fp);
+ exit(1);
+ }
+
+ *lineptr++ = '\0';
+
+ if (strcmp(valptr, "ltor") == 0)
+ Directions[NumFonts] = 1;
+ else if (strcmp(valptr, "rtol") == 0)
+ Directions[NumFonts] = -1;
+ else
+ {
+ fprintf(stderr, "ERROR: Bad text direction %s\n", valptr);
+ fclose(fp);
+ exit(1);
+ }
+
+ /*
+ * Got the direction, now get the width...
+ */
+
+ while (isspace(*lineptr))
+ lineptr ++;
+
+ valptr = lineptr;
+
+ while (!isspace(*lineptr) && *lineptr)
+ lineptr ++;
+
+ if (!*lineptr)
+ {
+ /*
+ * Can't have a font without all required values...
+ */
+
+ fprintf(stderr, "ERROR: bad font description line: %s\n", valptr);
+ fclose(fp);
+ exit(1);
+ }
+
+ *lineptr++ = '\0';
+
+ if (strcmp(valptr, "single") == 0)
+ Widths[NumFonts] = 1;
+ else if (strcmp(valptr, "double") == 0)
+ Widths[NumFonts] = 2;
+ else
+ {
+ fprintf(stderr, "ERROR: Bad text width %s\n", valptr);
+ fclose(fp);
+ exit(1);
+ }
+
+ /*
+ * Get the fonts...
+ */
+
+ for (i = 0; *lineptr && i < 4; i ++)
+ {
+ while (isspace(*lineptr))
+ lineptr ++;
+
+ valptr = lineptr;
+
+ while (!isspace(*lineptr) && *lineptr)
+ lineptr ++;
+
+ if (*lineptr)
+ *lineptr++ = '\0';
+
+ if (lineptr > valptr)
+ Fonts[NumFonts][i] = strdup(valptr);
+ }
+
+ /*
+ * Fill in remaining fonts as needed...
+ */
+
+ for (j = i; j < 4; j ++)
+ Fonts[NumFonts][j] = strdup(Fonts[NumFonts][0]);
+
+ /*
+ * Define the character mappings...
+ */
+
+ for (i = start, j = NumFonts * 256; i <= end; i ++, j ++)
+ {
+ Chars[i] = j;
+ Codes[j] = i;
+ }
+
+ /*
+ * Move to the next font, stopping if needed...
+ */
+
+ NumFonts ++;
+ if (NumFonts >= 256)
+ break;
+ }
+
+ fclose(fp);
+ }
+ else
+ {
+ fprintf(stderr, "ERROR: Bad charset type %s\n", lineptr);
+ fclose(fp);
+ exit(1);
+ }
+ }
+ else
+ {
+ /*
+ * Standard ASCII output just uses Courier, Courier-Bold, and
+ * possibly Courier-Oblique.
+ */
+
+ NumFonts = 1;
+
+ Fonts[0][ATTR_NORMAL] = strdup("Courier");
+ Fonts[0][ATTR_BOLD] = strdup("Courier-Bold");
+ Fonts[0][ATTR_ITALIC] = strdup("Courier-Oblique");
+ Fonts[0][ATTR_BOLDITALIC] = strdup("Courier-BoldOblique");
+
+ Widths[0] = 1;
+ Directions[0] = 1;
+
+ /*
+ * Define US-ASCII characters...
+ */
+
+ for (i = 32; i < 127; i ++)
+ {
+ Chars[i] = i;
+ Codes[i] = i;
+ }
+ }
+
+ /*
+ * Generate a list of unique fonts to use...
+ */
+
+ for (i = 0, num_fonts = 0; i < NumFonts; i ++)
+ for (j = 1 + PrettyPrint; j >= 0; j --)
+ {
+ for (k = 0; k < num_fonts; k ++)
+ if (strcmp(Fonts[i][j], fonts[k]) == 0)
+ break;
+
+ if (k >= num_fonts)
+ {
+ /*
+ * Add new font...
+ */
+
+ fonts[num_fonts] = Fonts[i][j];
+ num_fonts ++;
+ }
+ }
+
+ /*
+ * List the fonts that will be used...
+ */
+
+ for (i = 0; i < num_fonts; i ++)
+ if (i == 0)
+ printf("%%DocumentNeededResources: font %s\n", fonts[i]);
+ else
+ printf("%%+ font %s\n", fonts[i]);
+
+ puts("%%EndComments");
+
+ puts("%%BeginProlog");
+
+ /*
+ * Write the encoding array(s)...
+ */
+
+ puts("% character encoding(s)");
+
+ for (i = 0; i < NumFonts; i ++)
+ {
+ printf("/cupsEncoding%02x [\n", i);
+
+ for (ch = 0; ch < 256; ch ++)
+ {
+ if (Glyphs[Codes[i * 256 + ch]])
+ printf("/%s", Glyphs[Codes[i * 256 + ch]]);
+ else
+ printf("/.notdef");
+
+ if ((ch & 7) == 7)
+ putchar('\n');
+ }
+
+ puts("] def");
+ }
+
+ /*
+ * Create the fonts...
+ */
+
+ if (NumFonts == 1)
+ {
+ /*
+ * Just reencode the named fonts...
+ */
+
+ puts("% Reencode fonts");
+
+ for (i = 1 + PrettyPrint; i >= 0; i --)
+ {
+ printf("/%s findfont\n", Fonts[0][i]);
+ puts("dup length 1 add dict begin\n"
+ " { 1 index /FID ne { def } { pop pop } ifelse } forall\n"
+ " /Encoding cupsEncoding00 def\n"
+ " currentdict\n"
+ "end");
+ printf("/%s exch definefont pop\n", names[i]);
+ }
+ }
+ else
+ {
+ /*
+ * Construct composite fonts... Start by reencoding the base fonts...
+ */
+
+ puts("% Reencode base fonts");
+
+ for (i = 1 + PrettyPrint; i >= 0; i --)
+ for (j = 0; j < NumFonts; j ++)
+ {
+ printf("/%s findfont\n", Fonts[j][i]);
+ printf("dup length 1 add dict begin\n"
+ " { 1 index /FID ne { def } { pop pop } ifelse } forall\n"
+ " /Encoding cupsEncoding%02x def\n"
+ " currentdict\n"
+ "end\n", j);
+ printf("/%s%02x exch definefont /%s%02x exch def\n", names[i], j,
+ names[i], j);
+ }
+
+ /*
+ * Then merge them into composite fonts...
+ */
+
+ puts("% Create composite fonts...");
+
+ for (i = 1 + PrettyPrint; i >= 0; i --)
+ {
+ puts("8 dict begin");
+ puts("/FontType 0 def/FontMatrix[1.0 0 0 1.0 0 0]def/FMapType 2 def/Encoding[");
+ for (j = 0; j < NumFonts; j ++)
+ if (j == (NumFonts - 1))
+ printf("%d", j);
+ else if ((j & 15) == 15)
+ printf("%d\n", j);
+ else
+ printf("%d ", j);
+ puts("]def/FDepVector[");
+ for (j = 0; j < NumFonts; j ++)
+ if (j == (NumFonts - 1))
+ printf("%s%02x", names[i], j);
+ else if ((j & 3) == 3)
+ printf("%s%02x\n", names[i], j);
+ else
+ printf("%s%02x ", names[i], j);
+ puts("]def currentdict end");
+ printf("/%s exch definefont pop\n", names[i]);
+ }
+ }
+
+ /*
+ * Output the texttops procset...
+ */
+
+ puts("%%BeginResource: procset texttops 1.1 0");
+
+ puts("% Define fonts");
+
+ printf("/FN /cupsNormal findfont [%.1f 0 0 %.1f 0 0] makefont def\n",
+ 120.0 / CharsPerInch, 68.0 / LinesPerInch);
+ printf("/FB /cupsBold findfont [%.1f 0 0 %.1f 0 0] makefont def\n",
+ 120.0 / CharsPerInch, 68.0 / LinesPerInch);
+ if (PrettyPrint)
+ printf("/FI /cupsItalic 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");
+ printf("/U { gsave 0.5 setlinewidth 0 %.2f rmoveto "
+ "0 rlineto stroke grestore } bind def\n", -6.8 / LinesPerInch);
+
+ 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("/n {");
+ puts("\t20 string cvs % convert page number to string");
+ puts("\tdup length % get length");
+ puts("\tdup 2 mul string /P exch def % P = string twice as long");
+ puts("\t0 1 2 index 1 sub { % loop through each character in the page number");
+ puts("\t\tdup 3 index exch get % get character N from the page number");
+ puts("\t\texch 2 mul dup % compute offset in P");
+ puts("\t\tP exch 0 put % font 0");
+ puts("\t\t1 add P exch 2 index put % character");
+ puts("\t\tpop % discard character");
+ puts("\t} for % do for loop");
+ puts("\tpop pop % discard string and length");
+ puts("\tP % put string on stack");
+ puts("} bind def");
+
+ printf("/T");
+ write_text(title);
+ puts("def");
+
+ printf("/D");
+ write_text(curdate);
+ 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("\tD dup 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 n 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("\tn 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 i; /* Looping var */
+ int col; /* Current column */
+ int attr; /* Current attribute */
+ int font, /* Font to use */
+ lastfont, /* Last font */
+ mono; /* Monospaced? */
+ 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;
+
+ if (NumFonts == 1)
+ {
+ /*
+ * All characters in a single font - assume monospaced...
+ */
+
+ 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);
+ }
+ else
+ {
+ /*
+ * Multiple fonts; break up based on the font...
+ */
+
+ attr = line->attr;
+ start = line;
+ lastfont = Chars[line->ch] / 256;
+ mono = strncmp(Fonts[lastfont][0], "Courier", 7) == 0;
+ col ++;
+ line ++;
+
+ if (mono)
+ {
+ while (col < SizeColumns && line->ch != 0 && attr == line->attr)
+ {
+ font = Chars[line->ch] / 256;
+ if (strncmp(Fonts[font][0], "Courier", 7) != 0 ||
+ font != lastfont)
+ break;
+
+ col ++;
+ line ++;
+ }
+ }
+
+ if (Directions[lastfont] > 0)
+ write_string(col - (line - start), row, line - start, start);
+ else
+ {
+ /*
+ * Do right-to-left text...
+ */
+
+ while (col < SizeColumns && line->ch != 0 && attr == line->attr)
+ {
+ if (Directions[Chars[line->ch] / 256] > 0 &&
+ !ispunct(line->ch) && !isspace(line->ch))
+ break;
+
+ col ++;
+ line ++;
+ }
+
+ for (i = 1; start < line; i ++, start ++)
+ if (!isspace(start->ch))
+ write_string(col - i, row, 1, 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 ch; /* Current character */
+ 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);
+
+ if (NumFonts > 1)
+ {
+ /*
+ * Write a hex string...
+ */
+
+ putchar('<');
+
+ while (len > 0)
+ {
+ printf("%04x", Chars[s->ch]);
+
+ len --;
+ s ++;
+ }
+
+ putchar('>');
+ }
+ else
+ {
+ /*
+ * Write a quoted string...
+ */
+
+ putchar('(');
+
+ while (len > 0)
+ {
+ ch = Chars[s->ch];
+
+ if (ch < 32 || ch > 126)
+ {
+ /*
+ * Quote 8-bit and control characters...
+ */
+
+ printf("\\%03o", ch);
+ }
+ else
+ {
+ /*
+ * Quote the parenthesis and backslash as needed...
+ */
+
+ if (ch == '(' || ch == ')' || ch == '\\')
+ putchar('\\');
+
+ putchar(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");
+}
+
+
+/*
+ * 'write_text()' - Write a text string, quoting/encoding as needed.
+ */
+
+static void
+write_text(char *s) /* I - String to write */
+{
+ int ch; /* Actual character value (UTF8) */
+ unsigned char *utf8; /* UTF8 text */
+
+
+ if (NumFonts > 1)
+ {
+ /*
+ * 8/8 encoding...
+ */
+
+ putchar('<');
+
+ utf8 = (unsigned char *)s;
+
+ while (*utf8)
+ {
+ if (*utf8 < 0xc0 || !UTF8)
+ ch = *utf8 ++;
+ else if ((*utf8 & 0xe0) == 0xc0)
+ {
+ /*
+ * Two byte character...
+ */
+
+ ch = ((utf8[0] & 0x1f) << 6) | (utf8[1] & 0x3f);
+ utf8 += 2;
+ }
+ else
+ {
+ /*
+ * Three byte character...
+ */
+
+ ch = ((((utf8[0] & 0x1f) << 6) | (utf8[1] & 0x3f)) << 6) |
+ (utf8[2] & 0x3f);
+ utf8 += 3;
+ }
+
+ printf("%04x", Chars[ch]);
+ }
+
+ putchar('>');
+ }
+ else
+ {
+ /*
+ * Standard 8-bit encoding...
+ */
+
+ putchar('(');
+
+ while (*s)
+ {
+ if (*s < 32 || *s > 126)
+ printf("\\%03o", *s);
+ else
+ {
+ if (*s == '(' || *s == ')' || *s == '\\')
+ putchar('\\');
+
+ putchar(*s);
+ }
+
+ s ++;
+ }
+
+ putchar(')');
+ }
+}
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/fonts/AvantGarde-Book b/fonts/AvantGarde-Book
new file mode 100644
index 000000000..4d3a8b2ba
--- /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..c25c6ee4c
--- /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..67046dbe6
--- /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..e0f6559e9
--- /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..4e26c1cf5
--- /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..6520e718d
--- /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..7cbac2c45
--- /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..3ef45dcbe
--- /dev/null
+++ b/fonts/Bookman-LightItalic
Binary files differ
diff --git a/fonts/Charter-Bold b/fonts/Charter-Bold
new file mode 100644
index 000000000..0d82077ec
--- /dev/null
+++ b/fonts/Charter-Bold
Binary files differ
diff --git a/fonts/Charter-BoldItalic b/fonts/Charter-BoldItalic
new file mode 100644
index 000000000..c7a5f8798
--- /dev/null
+++ b/fonts/Charter-BoldItalic
Binary files differ
diff --git a/fonts/Charter-Italic b/fonts/Charter-Italic
new file mode 100644
index 000000000..6abe1cdfd
--- /dev/null
+++ b/fonts/Charter-Italic
Binary files differ
diff --git a/fonts/Charter-Roman b/fonts/Charter-Roman
new file mode 100644
index 000000000..b25133d51
--- /dev/null
+++ b/fonts/Charter-Roman
Binary files differ
diff --git a/fonts/Courier b/fonts/Courier
new file mode 100644
index 000000000..0cadce7d1
--- /dev/null
+++ b/fonts/Courier
Binary files differ
diff --git a/fonts/Courier-Bold b/fonts/Courier-Bold
new file mode 100644
index 000000000..f1da6121b
--- /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..8b7c24ff3
--- /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..107a51337
--- /dev/null
+++ b/fonts/Courier-Oblique
Binary files differ
diff --git a/fonts/Helvetica b/fonts/Helvetica
new file mode 100644
index 000000000..ff605552c
--- /dev/null
+++ b/fonts/Helvetica
Binary files differ
diff --git a/fonts/Helvetica-Bold b/fonts/Helvetica-Bold
new file mode 100644
index 000000000..aec380a33
--- /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..479904083
--- /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..f2387225d
--- /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..7ee6a2c81
--- /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..d2e96f3b7
--- /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..4ff13e5f6
--- /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..876cda876
--- /dev/null
+++ b/fonts/Helvetica-Oblique
Binary files differ
diff --git a/fonts/Makefile b/fonts/Makefile
new file mode 100644
index 000000000..d818c873c
--- /dev/null
+++ b/fonts/Makefile
@@ -0,0 +1,73 @@
+#
+# "$Id$"
+#
+# Fonts makefile for the Common UNIX Printing System (CUPS).
+#
+# Copyright 1993-2000 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 Charter-Bold \
+ Charter-BoldItalic Charter-Italic Charter-Roman 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
+ $(INSTALL_DATA) $(FONTS) $(DATADIR)/fonts
+
+
+#
+# End of "$Id$".
+#
diff --git a/fonts/NewCenturySchlbk-Bold b/fonts/NewCenturySchlbk-Bold
new file mode 100644
index 000000000..321a282f4
--- /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..31e589003
--- /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..2fbd61647
--- /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..6cbded3c7
--- /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..7f5df43f3
--- /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..1c812b839
--- /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..8d0f820de
--- /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..4101b7335
--- /dev/null
+++ b/fonts/Palatino-Roman
Binary files differ
diff --git a/fonts/Symbol b/fonts/Symbol
new file mode 100644
index 000000000..d0505e46c
--- /dev/null
+++ b/fonts/Symbol
Binary files differ
diff --git a/fonts/Times-Bold b/fonts/Times-Bold
new file mode 100644
index 000000000..47f8fd57d
--- /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..2d19d942e
--- /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..aa9ff5f8a
--- /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..cbae7ed15
--- /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..28443517d
--- /dev/null
+++ b/fonts/ZapfChancery-MediumItalic
Binary files differ
diff --git a/fonts/ZapfDingbats b/fonts/ZapfDingbats
new file mode 100644
index 000000000..4a3c386d2
--- /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..aaef09b7a
--- /dev/null
+++ b/locale/C/cups_C
@@ -0,0 +1,132 @@
+iso-8859-1
+OK
+Cancel
+Help
+Quit
+Close
+Yes
+No
+On
+Off
+Save
+Discard
+Default
+Options
+More Info
+Black
+Color
+Cyan
+Magenta
+Yellow
+Copyright 1993-2000 by Easy Software Products, All Rights Reserved.
+General
+Printer
+Image
+HP-GL/2
+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
+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
+Text
+Pretty Print
+Margins
+Left
+Right
+Bottom
+Top
+Filename(s)
+Print
+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..cb96f0fb3
--- /dev/null
+++ b/locale/Makefile
@@ -0,0 +1,78 @@
+#
+# "$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 ; \
+ $(INSTALL_DATA) $$dir/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..f6f699a3a
--- /dev/null
+++ b/locale/de/cups_de
@@ -0,0 +1,133 @@
+iso-8859-1
+Okay
+Abbrechen
+Hilfe
+Beenden
+Schließen
+Ja
+Nein
+An
+Aus
+Speichern
+Verwerfen
+Default
+Optionen
+Mehr Info
+Schwarz
+Farbe
+Cyan
+Magenta
+Gelb
+Copyright 1993-2000 durch Easy Software Products, alle Rechte vorbehalten.
+Allgemein
+Drucker
+Bild
+HP-GL/2
+Speziell
+Dokument
+Andere
+Druckbereich:
+Gesamtes Dokument
+Seitenbereich:
+Umgedrehte Reihenfolge:
+Seitenformat:
+ normal
+ 2 auf 1
+ 4 auf 1
+Bild-Skalierung:
+Natürliche Bildgröße
+Zoom in Prozent
+Zoom in PPI
+Gespiegelte Ausgabe:
+Farbsättigung:
+Farbton:
+Auf Seite anpassen:
+Schattiert:
+Strichstärke:
+Gamma-Korrektur:
+Helligkeit:
+Hinzufügen
+Löschen
+Ändern
+Drucker-URI
+Drucker-Name
+Drucker-Standort
+Drucker-Info
+Drucker-Modell
+Device-URI
+Formatiere Seite
+Drucke Seite
+Initialisiere Drucker
+Drucker-Zustand
+Bereit
+Nicht bereit
+Druckaufträge
+Klasse
+Lokal
+Remote
+Duplex
+Hefter
+Schnellkopien
+Sortieren/Gruppieren
+Locher
+Deckblatt
+Bindung
+Sortieren
+Klein (bis 14x35cm)
+Medium (14x35cm bis 33x48cm)
+Groß (33x48cm und größer)
+Benutzerspezifische Größe
+Leerlauf
+In Arbeit
+Gestoppt
+Alles
+Ungerade
+Gerade
+Dunkler Heller
+Medien-Größe
+Medium
+Medien-Quelle
+Ausrichtung:
+Hochformat
+Querformat
+Job-Status
+Job-Name
+Benutzername
+Priorität
+Kopien
+Dateigröße
+In Warteposition
+Ausgabe-Modus
+Auflösung
+Text
+Spezieller Druck
+Seitenränder
+Links
+Recht
+Unterseite
+Oberseite
+Dateiname(s)
+Druker
+400 Der Server versteht die Anfrage Ihres Browsers nicht.
+Der Server konnte nicht Ihre Berechtigung überprüfen, diese Ressource zu benutzen.
+Sie müssen bezahlen, um auf diesen Server zuzugreifen.
+Sie sind nicht berechtigt, auf diese Ressource des Servers zuzugreifen.
+Die gewünschte Ressource wurde auf diesem Server nicht gefunden.
+Die gewünschte Methode ist mit dieser Ressource nicht erlaubt.
+Eine passende Art der Ressource wurde auf diesem Server nicht gefunden.
+Sie können diesen Server nicht als Proxy-Server verwenden.
+Der Auftrag brauchte zu lang zur Beendigung und wurde abgebrochen.
+Die gewünschte Ressource besitzt mehr als einen Wert.
+Die gewünschte Ressource existiert nicht mehr und wurde nicht ersetzt.
+Die gewünschte Methode benötigt eine gültige Länge des Inhalts.
+Die Voraussetzungen für den Auftrag sind nicht erfüllt.
+Der Auftrag ist zu groß, um auf diesem Server verarbeitet zu werden.
+Die URI des Auftrags ist zu groß, um auf diesem Server verarbeitet zu werden.
+Das Format des Auftrags wird von diesem Server nicht verstanden.
+500 Der Server hat einen nicht behebbaren Fehler entdeckt und kann Ihren Auftrag nicht verarbeiten.
+Die gewünschte Methode ist auf diesen Server nicht implementiert.
+Der Proxy-Server empfing eine unzulässige Antwort von einem höheren Server.
+Die gewünschte Ressource ist aktuell auf diesem Server nicht verfügbarr.
+Der Proxy-Server braucht zu lang, um auf diesen Server zu reagieren.
+Dieser Server unterstützt nicht die HTTP-Version, die Ihr Browser benötigt.
+
diff --git a/locale/en/cups_en b/locale/en/cups_en
new file mode 100644
index 000000000..35d291a17
--- /dev/null
+++ b/locale/en/cups_en
@@ -0,0 +1,132 @@
+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-2000 by Easy Software Products, All Rights Reserved.
+General
+Printer
+Image
+HP-GL/2
+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
+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
+Text
+Pretty Print
+Margins
+Left
+Right
+Bottom
+Top
+Filename(s)
+Print
+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..c9c3420cc
--- /dev/null
+++ b/locale/es/cups_es
@@ -0,0 +1,132 @@
+iso-8859-1
+OK
+Cancel
+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-2000 por Easy Software Products, todos endereza reservado.
+General
+Impresora
+Imagen
+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
+Par
+Más Oscuro Más Brillante
+Talla De Media
+Tipo De Media
+Fuente De los Media
+Orientación:
+Retrato
+Paisaje
+Estatus del trabajo
+Nombre del trabajo
+Nombre del utilizador
+Prioridad
+Copias
+Tamaño
+Pendiente
+Modo de impresión
+Resolución
+Texto
+Especial impresión
+Márgenes
+Izquierda
+La derecha
+Fondo
+Tapa
+Nombre(s)
+Impresión
+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..542749347
--- /dev/null
+++ b/locale/fr/cups_fr
@@ -0,0 +1,132 @@
+iso-8859-1
+OK
+Annulation
+Aide
+Quitté
+Fermer
+Oui
+Non
+Oui
+Non
+Sauver
+Quitté
+Défaut
+Options
+Plus d'information
+Noir
+Couleur
+Cyan
+Magenta
+Jaune
+Copyright 1993-2000 par Easy Software Products, tous droits réservés.
+Général
+Imprimante
+Image
+HP-GL/2
+Options supplémentaires
+Document
+Autre
+Pages d'impression:
+Entier document
+Chaîne de page
+Commande d'Inversion:
+Pages par feuilles:
+ 1
+ 2
+ 4
+Graduation d'image:
+Emploi taille normale d'image
+Zoom par pourcent
+Zoom par PPI
+Image de miroir:
+Saturation de couleur:
+Teinture de couleur:
+Correspondre au page:
+Ombrageant:
+Largeur de crayon lecteur:
+Correction de Gamma:
+Éclat:
+Ajoutez
+Éffacer
+Modifiez
+URI de l'imprimante
+Nom de l'imprimante
+Emplacement de l'imprimante
+Information de l'imprimante
+Font et modèlent de l'imprimante
+Dispositif de l'URI
+Formatage du page
+Imprimant la page
+Initialisation de l'imprimante
+État de l'Imprimante
+Recevant les travaux
+Ne recevant pas Les Travaux
+Tirages
+Classe
+Local
+Distant
+Duplexage
+Agrafant
+Copie Rapides
+Copies Assemblées
+Poinçon de trou
+Bâche
+Liant
+Triant
+Petit (jusqu'à 9.5x1pouce)
+Moyen (9.5x1pouce à 13x19pouce)
+Grand (13x19pouce et plus grand)
+Taille faite sur commande
+Arrêter
+Traitant
+Arrêté
+Tout
+Impair
+Même
+Plus foncé Plus Lumineux
+Dimension du medias
+Sorte de medias
+Source du medias
+Orientation:
+Verticale
+Horizontal
+État du travail
+Nom du travail
+Nom de l'utilisateur
+Priorité
+Copies
+Grandeur du fichier
+Imminent
+Method de sortie
+Resolution
+Texte
+Empreinte Spéciale
+Marge
+Gauche
+Droite
+Bas
+Haut
+Nom du ficher(s)
+Imprimer
+400 Votre browser a envoyé une demande que ce serveur ne pouvait pas comprendre.
+Ce serveur ne pouvait pas vérifier que vous êtes autoriséz à 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 ce serveur.
+L'Uri de demande est trop grand pour ce serveur.
+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 actuellement 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..29e7725b0
--- /dev/null
+++ b/locale/it/cups_it
@@ -0,0 +1,132 @@
+iso-8859-1
+Procedi
+Annulla
+Aiuto
+Esci
+Chiudi
+Sì
+No
+Attivo
+Inattivo
+Salva
+Scarta
+Standard
+Opzioni
+Più Informazioni
+Nero
+Colore
+Ciano
+Fucsia
+Giallo
+Copyright 1993-2000 di Easy Software Products, tutti i diritti riservati.
+Generale
+Stampante
+Immagini
+HP-GL/2
+Extra
+Documento
+Altro
+Stampa delle pagine:
+Intero Documento
+Stampa intervallo:
+Ordine inverso:
+Formato della pagina:
+ 1-Up
+ 2-Up
+ 4-Up
+Scaling dell'immagine:
+Usa formato naturale dell'immagine
+Zoom in percentuale
+Zoom in PPI
+Immagine riflessa:
+Saturazione del colore:
+Tonalità del colore:
+Adatta alla pagina:
+Ombreggiatura:
+Larghezza della penna:
+Correzione Gamma:
+Luminosità:
+Aggiungi
+Cancella
+Modifica
+URI della stampante
+Nome della stampante
+Collocazione della stampante
+Informazioni sulla stampante
+Produttore e modello della stampante
+URI Del Dispositivo
+Preparazione della pagina
+Stampa della Pagina
+Inizializzazione Stampante
+Condizione della stampante
+Sto accettando le richieste di stampa
+Non sto accettando le richieste di stampa
+Richieste di stampa
+Codice categoria
+Locale
+Remoto
+Fronte-retro
+Sto cucendo con punti metallici
+Copie veloci
+Copie fascicolate
+Perforazione delle pagine (per fascicolatura)
+Inserendo copertina
+Applicando fascicolatura
+Ordinando
+Piccolo (fino a 9.5x1în)
+Medio (9.5x1în - 13x19in)
+Grande (13x19in e più grande)
+Formato personalizzato
+Idle
+Elaborando
+Arrestato
+Tutto
+Dispari
+Pari
+Più Scuro Più Luminoso
+Formato del supporto
+Tipo del supporto
+Sorgente del supporto
+Orientamento:
+Verticale
+Orizzontale
+Condizione di lavoro
+Nome di lavoro
+Nome dell' utente
+Priorita'
+Copie
+Dimensioni del file
+In attesa
+Modo stampa
+Risoluzione
+Testo
+Speciale stampa
+Margini
+Sinistra
+Destra
+Inferiore
+Superiore
+Nome(s)
+Stampa
+400 Il vostro browser ha trasmesso una richiesta che questo server non ha capito
+Questo server non ha potuto verificare che siete autorizzati ad accedere alla risorsa.
+Dovete pagare accedere a questo server.
+Non avete il permesso di accedere alla risorsa richiesta su questo server.
+La risorsa chiesta non è stata trovata su questo server.
+Il metodo richiesto non è consentito con la risorsa desiderata.
+Una rappresentazione adatta per la risorsa non è stata trovata su questo server.
+Non avete il permesso utilizzare questo server come proxy.
+La richiesta ha impiegato troppo tempo per essere completata ed è stata abbandonata.
+La risorsa chiesta ha più di un valore.
+La risorsa chiesta non e' piu' disponibile e non è stata ancora rimpiazzata.
+Il metodo chiesto richiede un campo "Content-Length" valido.
+I prerequisiti per la richiesta non sono soddisfatti.
+La richiesta è troppo grande per essere elaborata da questo server.
+L'URI della richiesta è troppo grande per essere elaborato da questo server.
+Il formato della richiesta non è capito da questo server.
+500 Il server ha rilevato un errore non recuperabile e non può elaborare la vostra richiesta.
+Il metodo chiesto non è implementato da questo server.
+Il proxy server ha ricevuto una risposta non valida da un server di livello superiore.
+La risorsa chiesta è attualmente non disponibile su questo server.
+Il proxy server ha impiegato troppo tempo per rispondere a questo server.
+Questo server non supporta la versione 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..0747ed3f2
--- /dev/null
+++ b/man/Makefile
@@ -0,0 +1,118 @@
+#
+# "$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...
+#
+
+CAT1 = backend.1 filter.1 lp.1 lpq.1 lprm.1 lpr.1 lpstat.1
+CAT5 = classes.conf.5 cupsd.conf.5 mime.convs.5 mime.types.5 \
+ printers.conf.5
+CAT8 = accept.8 cupsd.8 enable.8 lpadmin.8 lpc.8
+
+MAN1 = $(CAT1:.1=.man)
+MAN5 = $(CAT5:.5=.man)
+MAN8 = $(CAT8:.8=.man)
+
+
+#
+# Make everything...
+#
+
+all: $(CAT1) $(CAT5) $(CAT8)
+
+
+#
+# Clean all config and object files...
+#
+
+clean:
+
+
+#
+# Install files...
+#
+
+install: install-man $(INSTALL_CAT)
+
+install-man:
+ -$(MKDIR) $(MANDIR)/man1
+ for file in $(MAN1); do \
+ $(INSTALL_MAN) $$file $(MANDIR)/man1/`basename $$file man`1; \
+ done
+ $(RM) $(MANDIR)/man1/cancel.1
+ $(LN) lp.1 $(MANDIR)/man1/cancel.1
+ -$(MKDIR) $(MANDIR)/man5
+ for file in $(MAN5); do \
+ $(INSTALL_MAN) $$file $(MANDIR)/man5/`basename $$file man`5; \
+ done
+ -$(MKDIR) $(MANDIR)/man8
+ for file in $(MAN8); do \
+ $(INSTALL_MAN) $$file $(MANDIR)/man8/`basename $$file man`8; \
+ done
+ $(RM) $(MANDIR)/man8/reject.8
+ $(LN) accept.8 $(MANDIR)/man8/reject.8
+ $(RM) $(MANDIR)/man8/disable.8
+ $(LN) enable.8 $(MANDIR)/man8/disable.8
+
+install-cat:
+ -$(MKDIR) $(MANDIR)/cat1
+ $(INSTALL_MAN) $(CAT1) $(MANDIR)/cat1
+ $(RM) $(MANDIR)/cat1/cancel.1
+ $(LN) lp.1 $(MANDIR)/cat1/cancel.1
+ -$(MKDIR) $(MANDIR)/cat5
+ $(INSTALL_MAN) $(CAT5) $(MANDIR)/cat5
+ -$(MKDIR) $(MANDIR)/cat8
+ $(INSTALL_MAN) $(CAT8) $(MANDIR)/cat8
+ $(RM) $(MANDIR)/cat8/reject.8
+ $(LN) accept.8 $(MANDIR)/cat8/reject.8
+ $(RM) $(MANDIR)/cat8/disable.8
+ $(LN) enable.8 $(MANDIR)/cat8/disable.8
+
+install-bsdcat:
+ -$(MKDIR) $(MANDIR)/cat1
+ for file in $(CAT1); do \
+ $(INSTALL_MAN) $$file $(MANDIR)/cat1/`basename $$file 1`0; \
+ done
+ $(RM) $(MANDIR)/cat1/cancel.0
+ $(LN) lp.0 $(MANDIR)/cat1/cancel.0
+ -$(MKDIR) $(MANDIR)/cat5
+ for file in $(CAT5); do \
+ $(INSTALL_MAN) $$file $(MANDIR)/cat5/`basename $$file 5`0; \
+ done
+ -$(MKDIR) $(MANDIR)/cat8
+ for file in $(CAT8); do \
+ $(INSTALL_MAN) $$file $(MANDIR)/cat8/`basename $$file 8`0; \
+ done
+ $(RM) $(MANDIR)/cat8/reject.0
+ $(LN) accept.0 $(MANDIR)/cat8/reject.0
+ $(RM) $(MANDIR)/cat8/disable.0
+ $(LN) enable.0 $(MANDIR)/cat8/disable.0
+
+
+#
+# End of "$Id$".
+#
diff --git a/man/accept.8 b/man/accept.8
new file mode 100644
index 000000000..4a7831efc
--- /dev/null
+++ b/man/accept.8
@@ -0,0 +1,66 @@
+
+
+
+accept(8) Easy Software Products accept(8)
+
+
+NNAAMMEE
+ accept/reject - accept/reject jobs sent to a destination
+
+SSYYNNOOPPSSIISS
+ aacccceepptt destination(s)
+ rreejjeecctt [ -h _s_e_r_v_e_r ] [ -r [ _r_e_a_s_o_n ] ] destination(s)
+
+DDEESSCCRRIIPPTTIIOONN
+ _a_c_c_e_p_t instructs the printing system to accept print jobs
+ to the specified destinations.
+
+ _r_e_j_e_c_t instructs the printing system to reject print jobs
+ to the specified destinations. The _-_r option sets the rea-
+ son for rejecting print jobs. If not specified the reason
+ defaults to "Reason Unknown".
+
+CCOOMMPPAATTIIBBIILLIITTYY
+ The CUPS versions of _a_c_c_e_p_t and _r_e_j_e_c_t 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.
+
+SSEEEE AALLSSOO
+ cancel(1), disable(8), enable(8), lp(1), lpadmin(8),
+ lpstat(1), CUPS Software Administrators Manual
+
+CCOOPPYYRRIIGGHHTT
+ Copyright 1993-2000 by Easy Software Products, All Rights
+ Reserved.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+22 September 1999 Common UNIX Printing System 1
+
+
diff --git a/man/accept.man b/man/accept.man
new file mode 100644
index 000000000..0e4b6e47d
--- /dev/null
+++ b/man/accept.man
@@ -0,0 +1,57 @@
+.\"
+.\" "$Id: accept.man 911 2000-02-23 03:17:06Z mike $"
+.\"
+.\" accept/reject man page for the Common UNIX Printing System (CUPS).
+.\"
+.\" Copyright 1997-2000 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" "22 September 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
+\fIaccept\fR instructs the printing system to accept print jobs to the
+specified destinations.
+.LP
+\fIreject\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 \fIaccept\fR and \fIreject\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 Administrators Manual
+.SH COPYRIGHT
+Copyright 1993-2000 by Easy Software Products, All Rights Reserved.
+.\"
+.\" End of "$Id: accept.man 911 2000-02-23 03:17:06Z mike $".
+.\"
diff --git a/man/backend.1 b/man/backend.1
new file mode 100644
index 000000000..161cbf865
--- /dev/null
+++ b/man/backend.1
@@ -0,0 +1,132 @@
+
+
+
+backend(1) Easy Software Products backend(1)
+
+
+NNAAMMEE
+ backend - backend transmission interfaces
+
+SSYYNNOOPPSSIISS
+ bbaacckkeenndd job user title num-copies options _[ _f_i_l_e_n_a_m_e _]
+
+DDEESSCCRRIIPPTTIIOONN
+ The CUPS backend interface provides a standard method for
+ sending document files to different physical interfaces.
+
+ 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.
+
+ The command name (argv[0]) is set to the device URI of the
+ destination printer.
+
+EENNVVIIRROONNMMEENNTT VVAARRIIAABBLLEESS
+ The following environment variables are defined by the
+ CUPS server when executing the backend:
+
+ CHARSET
+ The default text character set (typically us-ascii or
+ iso-8859-1).
+
+ CONTENT_TYPE
+ The MIME type associated with the file (e.g. applica-
+ tion/postscript).
+
+ DEVICE_URI
+ The device-uri associated with the printer; this is
+ provided for shell scripts which may not be able to
+ get the passed argv[0] string.
+
+ LANG
+ The default language locale (typically C or en).
+
+ PATH
+ The standard execution path for external programs
+ that may be run by the backend.
+
+ PPD
+ The full pathname of the PostScript Printer Descrip-
+ tion (PPD) file for this printer.
+
+ PRINTER
+ The name of the printer.
+
+ RIP_CACHE
+ The recommended amount of memory to use for Raster
+ Image Processors (RIPs).
+
+ SERVER_ROOT
+ The root directory of the server.
+
+
+
+22 September 1999 Common UNIX Printing System 1
+
+
+
+
+
+backend(1) Easy Software Products backend(1)
+
+
+ SOFTWARE
+ The name and version number of the server (typically
+ CUPS/1.0).
+
+ TZ
+ The timezone of the server.
+
+ USER
+ The user executing the backend (typically lp).
+
+SSEEEE AALLSSOO
+ cupsd(8), filter(1) CUPS Software Administrators Manual,
+ CUPS Interface Design Description
+
+CCOOPPYYRRIIGGHHTT
+ Copyright 1993-2000 by Easy Software Products, All Rights
+ Reserved.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+22 September 1999 Common UNIX Printing System 2
+
+
diff --git a/man/backend.man b/man/backend.man
new file mode 100644
index 000000000..dcee09288
--- /dev/null
+++ b/man/backend.man
@@ -0,0 +1,102 @@
+.\"
+.\" "$Id: backend.man 911 2000-02-23 03:17:06Z mike $"
+.\"
+.\" backend man page for the Common UNIX Printing System (CUPS).
+.\"
+.\" Copyright 1997-2000 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" "22 September 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
+CONTENT_TYPE
+.br
+The MIME type associated with the file (e.g. application/postscript).
+.TP 5
+DEVICE_URI
+.br
+The device-uri associated with the printer; this is provided for shell
+scripts which may not be able to get the passed argv[0] string.
+.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
+PRINTER
+.br
+The name of the 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.
+.TP 5
+USER
+.br
+The user executing the backend (typically lp).
+.SH SEE ALSO
+cupsd(8), filter(1)
+CUPS Software Administrators Manual,
+CUPS Interface Design Description
+.SH COPYRIGHT
+Copyright 1993-2000 by Easy Software Products, All Rights Reserved.
+.\"
+.\" End of "$Id: backend.man 911 2000-02-23 03:17:06Z mike $".
+.\"
diff --git a/man/classes.conf.5 b/man/classes.conf.5
new file mode 100644
index 000000000..53b8945e4
--- /dev/null
+++ b/man/classes.conf.5
@@ -0,0 +1,66 @@
+
+
+
+classes.conf(5) Easy Software Products classes.conf(5)
+
+
+NNAAMMEE
+ classes.conf - class configuration file for cups
+
+DDEESSCCRRIIPPTTIIOONN
+ The _c_l_a_s_s_e_s_._c_o_n_f file defines the local printer classes
+ that are available. It is normally generated by the
+ _c_u_p_s_d_(_8_) program when printer classes are added or
+ deleted.
+
+SSEEEE AALLSSOO
+ cupsd(8), cupsd.conf(5), mime.convs(5), mime.types(5),
+ printers.conf(5), CUPS Software Administrators Manual,
+ CUPS Interface Design Description
+
+CCOOPPYYRRIIGGHHTT
+ Copyright 1993-2000 by Easy Software Products, All Rights
+ Reserved.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+22 September 1999 Common UNIX Printing System 1
+
+
diff --git a/man/classes.conf.man b/man/classes.conf.man
new file mode 100644
index 000000000..74d8a183a
--- /dev/null
+++ b/man/classes.conf.man
@@ -0,0 +1,39 @@
+.\"
+.\" "$Id: classes.conf.man 911 2000-02-23 03:17:06Z mike $"
+.\"
+.\" classes.conf man page for the Common UNIX Printing System (CUPS).
+.\"
+.\" Copyright 1997-2000 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" "22 September 1999" "Easy Software Products"
+.SH NAME
+classes.conf \- class configuration file for cups
+.SH DESCRIPTION
+The \fIclasses.conf\fR file defines the local printer classes that are
+available. It is normally generated by the \fIcupsd(8)\fR program when
+printer classes are added or deleted.
+.SH SEE ALSO
+cupsd(8), cupsd.conf(5), mime.convs(5), mime.types(5), printers.conf(5),
+CUPS Software Administrators Manual,
+CUPS Interface Design Description
+.SH COPYRIGHT
+Copyright 1993-2000 by Easy Software Products, All Rights Reserved.
+.\"
+.\" End of "$Id: classes.conf.man 911 2000-02-23 03:17:06Z mike $".
+.\"
diff --git a/man/cupsd.8 b/man/cupsd.8
new file mode 100644
index 000000000..8c2c15eeb
--- /dev/null
+++ b/man/cupsd.8
@@ -0,0 +1,66 @@
+
+
+
+cupsd(8) Easy Software Products cupsd(8)
+
+
+NNAAMMEE
+ cupsd - common unix printing system daemon
+
+SSYYNNOOPPSSIISS
+ ccuuppssdd _[ _-_c _c_o_n_f_i_g_-_f_i_l_e _]
+
+DDEESSCCRRIIPPTTIIOONN
+ _c_u_p_s_d is the scheduler for the Common UNIX Printing Sys-
+ tem. It implements a printing system based upon the Inter-
+ net Printing Protocol, version 1.0. If no options are
+ specified on the command-line then the default configura-
+ tion file (usually _/_v_a_r_/_c_u_p_s_/_c_o_n_f_/_c_u_p_s_d_._c_o_n_f) will be
+ used.
+
+CCOOMMPPAATTIIBBIILLIITTYY
+ _c_u_p_s_d implements all of the required IPP/1.0 attributes
+ and operations. It also implements optional operation set
+ 1 and several CUPS-specific administation operations.
+
+SSEEEE AALLSSOO
+ backend(1), classes.conf(5), cupsd.conf(5), filter(1),
+ mime.convs(5), mime.types(5), printers.conf(5), CUPS Soft-
+ ware Administrators Manual, CUPS Interface Design Descrip-
+ tion
+
+CCOOPPYYRRIIGGHHTT
+ Copyright 1993-2000 by Easy Software Products, All Rights
+ Reserved.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+22 September 1999 Common UNIX Printing System 1
+
+
diff --git a/man/cupsd.conf.5 b/man/cupsd.conf.5
new file mode 100644
index 000000000..da09c6d2e
--- /dev/null
+++ b/man/cupsd.conf.5
@@ -0,0 +1,66 @@
+
+
+
+cupsd.conf(5) Easy Software Products cupsd.conf(5)
+
+
+NNAAMMEE
+ cupsd.conf - server configuration file for cups
+
+DDEESSCCRRIIPPTTIIOONN
+ The _c_u_p_s_d_._c_o_n_f file configures the CUPS scheduler,
+ _c_u_p_s_d_(_8_).
+
+SSEEEE AALLSSOO
+ classes.conf(5), cupsd(8), mime.convs(5), mime.types(5),
+ printers.conf(5), CUPS Software Administrators Manual,
+ CUPS Interface Design Description
+
+CCOOPPYYRRIIGGHHTT
+ Copyright 1993-2000 by Easy Software Products, All Rights
+ Reserved.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+22 September 1999 Common UNIX Printing System 1
+
+
diff --git a/man/cupsd.conf.man b/man/cupsd.conf.man
new file mode 100644
index 000000000..5c1ecf59c
--- /dev/null
+++ b/man/cupsd.conf.man
@@ -0,0 +1,37 @@
+.\"
+.\" "$Id: cupsd.conf.man 911 2000-02-23 03:17:06Z mike $"
+.\"
+.\" cupsd.conf man page for the Common UNIX Printing System (CUPS).
+.\"
+.\" Copyright 1997-2000 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" "22 September 1999" "Easy Software Products"
+.SH NAME
+cupsd.conf \- server configuration file for cups
+.SH DESCRIPTION
+The \fIcupsd.conf\fR file configures the CUPS scheduler, \fIcupsd(8)\fR.
+.SH SEE ALSO
+classes.conf(5), cupsd(8), mime.convs(5), mime.types(5), printers.conf(5),
+CUPS Software Administrators Manual,
+CUPS Interface Design Description
+.SH COPYRIGHT
+Copyright 1993-2000 by Easy Software Products, All Rights Reserved.
+.\"
+.\" End of "$Id: cupsd.conf.man 911 2000-02-23 03:17:06Z mike $".
+.\"
diff --git a/man/cupsd.man b/man/cupsd.man
new file mode 100644
index 000000000..4b5616b3d
--- /dev/null
+++ b/man/cupsd.man
@@ -0,0 +1,48 @@
+.\"
+.\" "$Id: cupsd.man 911 2000-02-23 03:17:06Z mike $"
+.\"
+.\" cupsd man page for the Common UNIX Printing System (CUPS).
+.\"
+.\" Copyright 1997-2000 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" "22 September 1999" "Easy Software Products"
+.SH NAME
+cupsd \- common unix printing system daemon
+.SH SYNOPSIS
+.B cupsd
+.I [ \-c config-file ]
+.SH DESCRIPTION
+\fIcupsd\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/cupsd.conf\fR) will be used.
+.SH COMPATIBILITY
+\fIcupsd\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
+backend(1), classes.conf(5), cupsd.conf(5), filter(1), mime.convs(5),
+mime.types(5), printers.conf(5),
+CUPS Software Administrators Manual,
+CUPS Interface Design Description
+.SH COPYRIGHT
+Copyright 1993-2000 by Easy Software Products, All Rights Reserved.
+.\"
+.\" End of "$Id: cupsd.man 911 2000-02-23 03:17:06Z mike $".
+.\"
diff --git a/man/enable.8 b/man/enable.8
new file mode 100644
index 000000000..dbf4a587d
--- /dev/null
+++ b/man/enable.8
@@ -0,0 +1,66 @@
+
+
+
+enable(8) Easy Software Products enable(8)
+
+
+NNAAMMEE
+ disable, enable - stop/start printers and classes
+
+SSYYNNOOPPSSIISS
+ ddiissaabbllee [ -c ] [ -h _s_e_r_v_e_r ] [ -r [ _r_e_a_s_o_n ] ] destina-
+ tion(s)
+ eennaabbllee destination(s)
+
+DDEESSCCRRIIPPTTIIOONN
+ _e_n_a_b_l_e starts the named printers or classes.
+
+ _d_i_s_a_b_l_e stops the named printers or classes. The follow-
+ ing options may be used:
+
+ -c
+ Cancels all jobs on the named destination.
+
+ -r [ _r_e_a_s_o_n ]
+ Sets the message associated with the stopped state.
+ If no reason is specified then the message is set to
+ "Reason Unknown".
+
+CCOOMMPPAATTIIBBIILLIITTYY
+ The CUPS versions of _d_i_s_a_b_l_e and _e_n_a_b_l_e 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.
+
+SSEEEE AALLSSOO
+ accept(8), cancel(1), lp(1), lpadmin(8), lpstat(1),
+ reject(8), CUPS Software Administrators Manual
+
+CCOOPPYYRRIIGGHHTT
+ Copyright 1993-2000 by Easy Software Products, All Rights
+ Reserved.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+22 September 1999 Common UNIX Printing System 1
+
+
diff --git a/man/enable.man b/man/enable.man
new file mode 100644
index 000000000..fd9e3b881
--- /dev/null
+++ b/man/enable.man
@@ -0,0 +1,64 @@
+.\"
+.\" "$Id: enable.man 911 2000-02-23 03:17:06Z mike $"
+.\"
+.\" enable/disable man page for the Common UNIX Printing System (CUPS).
+.\"
+.\" Copyright 1997-2000 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" "22 September 1999" "Easy Software Products"
+.SH NAME
+disable, enable \- stop/start printers and classes
+.SH SYNOPSIS
+.B disable
+[ \-c ] [ -h
+.I server
+] [ \-r [
+.I reason
+] ] destination(s)
+.br
+.B enable
+destination(s)
+.SH DESCRIPTION
+\fIenable\fR starts the named printers or classes.
+.LP
+\fIdisable\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 \fIdisable\fR and \fIenable\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 Administrators Manual
+.SH COPYRIGHT
+Copyright 1993-2000 by Easy Software Products, All Rights Reserved.
+
+.\"
+.\" End of "$Id: enable.man 911 2000-02-23 03:17:06Z mike $".
+.\"
diff --git a/man/filter.1 b/man/filter.1
new file mode 100644
index 000000000..d6d3cb856
--- /dev/null
+++ b/man/filter.1
@@ -0,0 +1,132 @@
+
+
+
+filter(1) Easy Software Products filter(1)
+
+
+NNAAMMEE
+ filter - file conversion filter interfaces
+
+SSYYNNOOPPSSIISS
+ ffiilltteerr job user title num-copies options _[ _f_i_l_e_n_a_m_e _]
+
+DDEESSCCRRIIPPTTIIOONN
+ 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.
+
+ 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.
+
+ The command name (argv[0]) is set to the name of the des-
+ tination printer.
+
+EENNVVIIRROONNMMEENNTT VVAARRIIAABBLLEESS
+ The following environment variables are defined by the
+ CUPS server when executing each filter:
+
+ CHARSET
+ The default text character set (typically us-ascii or
+ iso-8859-1).
+
+ CONTENT_TYPE
+ The MIME type associated with the file (e.g. applica-
+ tion/postscript).
+
+ DEVICE_URI
+ The device-uri associated with the printer.
+
+ LANG
+ The default language locale (typically C or en).
+
+ PATH
+ The standard execution path for external programs
+ that may be run by the filter.
+
+ PPD
+ The full pathname of the PostScript Printer Descrip-
+ tion (PPD) file for this printer.
+
+ PRINTER
+ The name of the printer; this is provided for shell
+ scripts which may not be able to get the passed
+ argv[0] string.
+
+ RIP_CACHE
+ The recommended amount of memory to use for Raster
+ Image Processors (RIPs).
+
+
+
+22 September 1999 Common UNIX Printing System 1
+
+
+
+
+
+filter(1) Easy Software Products filter(1)
+
+
+ SERVER_ROOT
+ The root directory of the server.
+
+ SOFTWARE
+ The name and version number of the server (typically
+ CUPS/1.0).
+
+ TZ
+ The timezone of the server.
+
+ USER
+ The user executing the filter (typically lp).
+
+CCOOMMPPAATTIIBBIILLIITTYY
+ 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 inter-
+ face script will be provided via the llppaaddmmiinn((88)) command
+ using the _-_i option.
+
+SSEEEE AALLSSOO
+ backend(1), cupsd(8), CUPS Software Administrators Manual,
+ CUPS Interface Design Description
+
+CCOOPPYYRRIIGGHHTT
+ Copyright 1993-2000 by Easy Software Products, All Rights
+ Reserved.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+22 September 1999 Common UNIX Printing System 2
+
+
diff --git a/man/filter.man b/man/filter.man
new file mode 100644
index 000000000..b32c8c688
--- /dev/null
+++ b/man/filter.man
@@ -0,0 +1,108 @@
+.\"
+.\" "$Id: filter.man 911 2000-02-23 03:17:06Z mike $"
+.\"
+.\" filter man page for the Common UNIX Printing System (CUPS).
+.\"
+.\" Copyright 1997-2000 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" "22 September 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
+CONTENT_TYPE
+.br
+The MIME type associated with the file (e.g. application/postscript).
+.TP 5
+DEVICE_URI
+.br
+The device-uri associated with the printer.
+.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
+PRINTER
+.br
+The name of the printer; this is provided for shell scripts which may not be
+able to get the passed argv[0] string.
+.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.
+.TP 5
+USER
+.br
+The user executing the filter (typically lp).
+.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 Administrators Manual,
+CUPS Interface Design Description
+.SH COPYRIGHT
+Copyright 1993-2000 by Easy Software Products, All Rights Reserved.
+.\"
+.\" End of "$Id: filter.man 911 2000-02-23 03:17:06Z mike $".
+.\"
diff --git a/man/lp.1 b/man/lp.1
new file mode 100644
index 000000000..e01fca04f
--- /dev/null
+++ b/man/lp.1
@@ -0,0 +1,132 @@
+
+
+
+lp(1) Easy Software Products lp(1)
+
+
+NNAAMMEE
+ lp - print files
+ cancel - cancel jobs
+
+SSYYNNOOPPSSIISS
+ llpp [ -c ] [ -d _d_e_s_t_i_n_a_t_i_o_n ] [ -h _s_e_r_v_e_r ] [ -m ] [ -n
+ _n_u_m_-_c_o_p_i_e_s [ -o _o_p_t_i_o_n ] [ -p/q _p_r_i_o_r_i_t_y ] [ -s ] [ -t
+ _t_i_t_l_e ] [ _f_i_l_e_(_s_) ]
+ ccaanncceell [ -a ] [ -h _s_e_r_v_e_r ] [ _i_d ] [ _d_e_s_t_i_n_a_t_i_o_n ] [ _d_e_s_-
+ _t_i_n_a_t_i_o_n_-_i_d ]
+
+DDEESSCCRRIIPPTTIIOONN
+ llpp submits files for printing.
+
+ ccaanncceell cancels existing print jobs. The _-_a option will
+ remove all jobs from the specified destination.
+
+OOPPTTIIOONNSS
+ The following options are recognized by llpp:
+
+ -d _d_e_s_t_i_n_a_t_i_o_n
+ Prints files to the named printer.
+
+ -h _h_o_s_t_n_a_m_e
+ Specifies the print server hostname. The default is
+ "localhost" or the value of the CUPS_SERVER environ-
+ ment variable.
+
+ -m
+ Send email when the job is completed (ignored in CUPS
+ 1.0.)
+
+ -n _c_o_p_i_e_s
+ Sets the number of copies to print from 1 to 100.
+
+ -o _o_p_t_i_o_n
+ Sets a job option.
+
+ -p/q _p_r_i_o_r_i_t_y
+ Sets the job priority from 1 (lowest) to 100 (high-
+ est). The default priority is 50.
+
+ -s
+ Do not report the resulting job IDs (silent mode.)
+
+ -t _n_a_m_e
+ Sets the job name.
+
+CCOOMMPPAATTIIBBIILLIITTYY
+ 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 nnoott case-sensitive.
+
+ The "m" option is not functional in CUPS 1.0.
+
+
+
+9 September 1999 Common UNIX Printing System 1
+
+
+
+
+
+lp(1) Easy Software Products lp(1)
+
+
+SSEEEE AALLSSOO
+ lpstat(1), CUPS Software Users Manual
+
+CCOOPPYYRRIIGGHHTT
+ Copyright 1993-2000 by Easy Software Products, All Rights
+ Reserved.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+9 September 1999 Common UNIX Printing System 2
+
+
diff --git a/man/lp.man b/man/lp.man
new file mode 100644
index 000000000..cc5a5f8ef
--- /dev/null
+++ b/man/lp.man
@@ -0,0 +1,111 @@
+.\"
+.\" "$Id: lp.man 911 2000-02-23 03:17:06Z mike $"
+.\"
+.\" lp/cancel man page for the Common UNIX Printing System (CUPS).
+.\"
+.\" Copyright 1997-2000 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" "9 September 1999" "Easy Software Products"
+.SH NAME
+lp \- print files
+.br
+cancel \- cancel jobs
+.SH SYNOPSIS
+.B lp
+[ \-c ] [ \-d
+.I destination
+] [ -h
+.I server
+] [ \-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 OPTIONS
+The following options are recognized by \fBlp\fR:
+.TP 5
+\-d \fIdestination\fR
+.br
+Prints files to the named printer.
+.TP 5
+\-h \fIhostname\fR
+.br
+Specifies the print server hostname. The default is "localhost" or the value
+of the CUPS_SERVER environment variable.
+.TP 5
+\-m
+.br
+Send email when the job is completed (ignored in CUPS 1.0.)
+.TP 5
+\-n \fIcopies\fR
+.br
+Sets the number of copies to print from 1 to 100.
+.TP 5
+\-o \fIoption\fR
+.br
+Sets a job option.
+.TP 5
+\-p/q \fIpriority\fR
+.br
+Sets the job priority from 1 (lowest) to 100 (highest). The default priority
+is 50.
+.TP 5
+\-s
+.br
+Do not report the resulting job IDs (silent mode.)
+.TP 5
+\-t \fIname\fR
+.br
+Sets the job name.
+.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 "m" option is not functional in CUPS 1.0.
+.SH SEE ALSO
+lpstat(1),
+CUPS Software Users Manual
+.SH COPYRIGHT
+Copyright 1993-2000 by Easy Software Products, All Rights Reserved.
+.\"
+.\" End of "$Id: lp.man 911 2000-02-23 03:17:06Z mike $".
+.\"
diff --git a/man/lpadmin.8 b/man/lpadmin.8
new file mode 100644
index 000000000..6504b1c48
--- /dev/null
+++ b/man/lpadmin.8
@@ -0,0 +1,132 @@
+
+
+
+lpadmin(8) Easy Software Products lpadmin(8)
+
+
+NNAAMMEE
+ lpadmin - configure cups printers and classes
+
+SSYYNNOOPPSSIISS
+ llppaaddmmiinn [ -h _s_e_r_v_e_r ] -d _d_e_s_t_i_n_a_t_i_o_n
+ llppaaddmmiinn [ -h _s_e_r_v_e_r ] -p _p_r_i_n_t_e_r _o_p_t_i_o_n_(_s_)
+ llppaaddmmiinn [ -h _s_e_r_v_e_r ] -x _d_e_s_t_i_n_a_t_i_o_n
+
+DDEESSCCRRIIPPTTIIOONN
+ _l_p_a_d_m_i_n configures printer and class queues provided by
+ CUPS. It can also be used to set the system default
+ printer or class.
+
+ The first form of the command sets the default printer or
+ class to _d_e_s_t_i_n_a_t_i_o_n. Subsequent print jobs submitted via
+ the _l_p_(_1_) or _l_p_r_(_1_) commands will use this destination
+ unless the user specifies otherwise.
+
+ The second form of the command configures the named
+ printer. The additional options are described below.
+
+ The third form of the command deletes the printer or class
+ _d_e_s_t_i_n_a_t_i_o_n. Any jobs that are pending for the destina-
+ tion will be removed and any job that is currently printed
+ will be aborted.
+
+CCOONNFFIIGGUURRAATTIIOONN OOPPTTIIOONNSS
+ The following options are recognized when configuring a
+ printer queue:
+
+ -c _c_l_a_s_s
+ Adds the named _p_r_i_n_t_e_r to _c_l_a_s_s. If _c_l_a_s_s does not
+ exist it is created automatically.
+
+ -i _i_n_t_e_r_f_a_c_e
+ Sets a System V style interface script for the
+ printer. This option cannot be specified with the _-_P
+ option (PPD file) and is intended for providing sup-
+ port for legacy printer drivers.
+
+ -m _m_o_d_e_l
+ Sets a standard System V interface script or PPD file
+ from the model directory.
+
+ -r _c_l_a_s_s
+ Removes the named _p_r_i_n_t_e_r from _c_l_a_s_s. If the result-
+ ing class becomes empty it is removed.
+
+ -v _d_e_v_i_c_e_-_u_r_i
+ Sets the _d_e_v_i_c_e_-_u_r_i attribute of the printer queue.
+ If _d_e_v_i_c_e_-_u_r_i is a filename it is automatically con-
+ verted to the form ffiillee:://ffiillee//nnaammee.
+
+
+
+
+
+22 September 1999 Common UNIX Printing System 1
+
+
+
+
+
+lpadmin(8) Easy Software Products lpadmin(8)
+
+
+ -D _i_n_f_o
+ Provides a textual description of the printer.
+
+ -E
+ Enables the printer and accepts jobs; this is the
+ same as running the _a_c_c_e_p_t_(_8_) and _e_n_a_b_l_e_(_8_) programs
+ on the printer.
+
+ -L _l_o_c_a_t_i_o_n
+ Provides a textual location of the printer.
+
+ -P _p_p_d_-_f_i_l_e
+ Specifies a PostScript Printer Description file to
+ use with the printer. If specified, this option over-
+ rides the _-_i option (interface script).
+
+CCOOMMPPAATTIIBBIILLIITTYY
+ 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 nnoott case-sensitive.
+ Finally, the CUPS version of _l_p_a_d_m_i_n may ask the user for
+ an access password depending on the printing system con-
+ figuration. This differs from the System V version which
+ requires the root user to execute this command.
+
+LLIIMMIITTAATTIIOONNSS
+ The CUPS version of _l_p_a_d_m_i_n does not support all of the
+ System V or Solaris printing system configuration options.
+
+SSEEEE AALLSSOO
+ accept(8), cancel(1), disable(8), enable(8), lp(1),
+ lpstat(1), reject(8), CUPS Software Administrators Manual
+
+CCOOPPYYRRIIGGHHTT
+ Copyright 1993-2000 by Easy Software Products, All Rights
+ Reserved.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+22 September 1999 Common UNIX Printing System 2
+
+
diff --git a/man/lpadmin.man b/man/lpadmin.man
new file mode 100644
index 000000000..1b3e1f81c
--- /dev/null
+++ b/man/lpadmin.man
@@ -0,0 +1,124 @@
+.\"
+.\" "$Id: lpadmin.man 911 2000-02-23 03:17:06Z mike $"
+.\"
+.\" lpadmin man page for the Common UNIX Printing System (CUPS).
+.\"
+.\" Copyright 1997-2000 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 September 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
+\fIlpadmin\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 and accepts jobs; 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 \fIlpadmin\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 \fIlpadmin\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 Administrators Manual
+.SH COPYRIGHT
+Copyright 1993-2000 by Easy Software Products, All Rights Reserved.
+.\"
+.\" End of "$Id: lpadmin.man 911 2000-02-23 03:17:06Z mike $".
+.\"
diff --git a/man/lpc.8 b/man/lpc.8
new file mode 100644
index 000000000..0f77d52d8
--- /dev/null
+++ b/man/lpc.8
@@ -0,0 +1,66 @@
+
+
+
+lpc(8) Easy Software Products lpc(8)
+
+
+NNAAMMEE
+ lpc - line printer control program
+
+SSYYNNOOPPSSIISS
+ llppcc [ _c_o_m_m_a_n_d [ _p_a_r_a_m_e_t_e_r_(_s_) ] ]
+
+DDEESSCCRRIIPPTTIIOONN
+ _l_p_c provides limited control over printer and class queues
+ provided by CUPS. It can also be used to query the state
+ of queues.
+
+ If no command is specified on the command-line, lpc will
+ display a prompt and accept commands from the standard
+ input.
+
+CCOOMMMMAANNDDSS
+ The _l_p_c program accepts a subset of commands accepted by
+ the Berkeley _l_p_c program of the same name:
+
+ _e_x_i_t
+ Exits the command interpreter.
+
+ help _[_c_o_m_m_a_n_d_]
+ Displays a short help message.
+
+ quit
+ Exits the command interpreter.
+
+ status _[_q_u_e_u_e_]
+ Displays the status of one or more printer or class
+ queues.
+
+ ? _[_c_o_m_m_a_n_d_]
+ Display a short help message.
+
+LLIIMMIITTAATTIIOONNSS
+ Since _l_p_c is geared towards the Berkeley printing system,
+ it is impossible to use _l_p_c to configure printer or class
+ queues provided by CUPS. To configure printer or class
+ queues you must use the _l_p_a_d_m_i_n_(_8_) command or another
+ CUPS-compatible client with that functionality.
+
+CCOOMMPPAATTIIBBIILLIITTYY
+ The CUPS version of _l_p_c does not implement all of the
+ standard Berkeley commands.
+
+SSEEEE AALLSSOO
+ accept(8), cancel(1), disable(8), enable(8), lp(1),
+ lpr(1), lprm(1), lpstat(1), reject(8), CUPS Software
+ Administrators Manual
+
+CCOOPPYYRRIIGGHHTT
+ Copyright 1993-2000 by Easy Software Products, All Rights
+ Reserved.
+
+
+
+22 September 1999 Common UNIX Printing System 1
+
+
diff --git a/man/lpc.man b/man/lpc.man
new file mode 100644
index 000000000..6e357b792
--- /dev/null
+++ b/man/lpc.man
@@ -0,0 +1,79 @@
+.\"
+.\" "$Id: lpc.man 911 2000-02-23 03:17:06Z mike $"
+.\"
+.\" lpc man page for the Common UNIX Printing System (CUPS).
+.\"
+.\" Copyright 1997-2000 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" "22 September 1999" "Easy Software Products"
+.SH NAME
+lpc \- line printer control program
+.SH SYNOPSIS
+.B lpc
+[
+.I command
+[
+.I parameter(s)
+] ]
+.SH DESCRIPTION
+\fIlpc\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 \fIlpc\fR program accepts a subset of commands accepted by the Berkeley
+\fIlpc\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 \fIlpc\fR is geared towards the Berkeley printing system, it is impossible
+to use \fIlpc\fR to configure printer or class queues provided by CUPS. To
+configure printer or class queues you must use the \fIlpadmin(8)\fR command
+or another CUPS-compatible client with that functionality.
+.SH COMPATIBILITY
+The CUPS version of \fIlpc\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 Administrators Manual
+.SH COPYRIGHT
+Copyright 1993-2000 by Easy Software Products, All Rights Reserved.
+.\"
+.\" End of "$Id: lpc.man 911 2000-02-23 03:17:06Z mike $".
+.\"
diff --git a/man/lpq.1 b/man/lpq.1
new file mode 100644
index 000000000..b153b687e
--- /dev/null
+++ b/man/lpq.1
@@ -0,0 +1,66 @@
+
+
+
+lpq(1) Easy Software Products lpq(1)
+
+
+NNAAMMEE
+ lpq - show printer queue status
+
+SSYYNNOOPPSSIISS
+ llppqq [ -P _d_e_s_t ] [ -l ] [ _+_i_n_t_e_r_v_a_l ]
+
+DDEESSCCRRIIPPTTIIOONN
+ _l_p_q shows the current print queue status on the named
+ printer. Jobs queued on the default destination will be
+ shown if no printer or class is specified on the command-
+ line.
+
+ The _i_n_t_e_r_v_a_l option allows you to continuously report the
+ jobs in the queue until the queue is empty; the list of
+ jobs is show one every _i_n_t_e_r_v_a_l seconds.
+
+ The _-_l option requests a more verbose reporting format.
+
+SSEEEE AALLSSOO
+ cancel(1), lp(1), lpr(1), lprm(1), lpstat(1)
+ CUPS Software Users Manual
+
+CCOOPPYYRRIIGGHHTT
+ Copyright 1993-2000 by Easy Software Products, All Rights
+ Reserved.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+7 December 1999 Common UNIX Printing System 1
+
+
diff --git a/man/lpq.man b/man/lpq.man
new file mode 100644
index 000000000..2879a0fcb
--- /dev/null
+++ b/man/lpq.man
@@ -0,0 +1,52 @@
+.\"
+.\" "$Id: lpq.man 911 2000-02-23 03:17:06Z mike $"
+.\"
+.\" lpq man page for the Common UNIX Printing System (CUPS).
+.\"
+.\" Copyright 1997-2000 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 lpq 1 "Common UNIX Printing System" "7 December 1999" "Easy Software Products"
+.SH NAME
+lpq \- show printer queue status
+.SH SYNOPSIS
+.B lpq
+[ \-P
+.I dest
+] [ \-l ] [
+.I +interval
+]
+.SH DESCRIPTION
+\fIlpq\fR shows the current print queue status on the named printer.
+Jobs queued on the default destination will be shown if no printer or
+class is specified on the command-line.
+.LP
+The \fIinterval\fR option allows you to continuously report the jobs
+in the queue until the queue is empty; the list of jobs is show one
+every \fIinterval\fR seconds.
+.LP
+The \fI-l\fR option requests a more verbose reporting format.
+.SH SEE ALSO
+cancel(1), lp(1), lpr(1), lprm(1), lpstat(1)
+.br
+CUPS Software Users Manual
+.SH COPYRIGHT
+Copyright 1993-2000 by Easy Software Products, All Rights Reserved.
+.\"
+.\" End of "$Id: lpq.man 911 2000-02-23 03:17:06Z mike $".
+.\"
diff --git a/man/lpr.1 b/man/lpr.1
new file mode 100644
index 000000000..7597c5e7b
--- /dev/null
+++ b/man/lpr.1
@@ -0,0 +1,132 @@
+
+
+
+lpr(1) Easy Software Products lpr(1)
+
+
+NNAAMMEE
+ lpr - print files
+
+SSYYNNOOPPSSIISS
+ llpprr [ -P _d_e_s_t_i_n_a_t_i_o_n ] [ -# _n_u_m_-_c_o_p_i_e_s [ -l ] [ -o _o_p_t_i_o_n
+ ] [ -p] [ -r ] [ -C/J/T _t_i_t_l_e ] [ _f_i_l_e_(_s_) ]
+
+DDEESSCCRRIIPPTTIIOONN
+ llpprr 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 llpprr reads the print file
+ from the standard input.
+
+OOPPTTIIOONNSS
+ The following options are recognized by llpprr:
+
+ -P _d_e_s_t_i_n_a_t_i_o_n
+ Prints files to the named printer.
+
+ -# _c_o_p_i_e_s
+ Sets the number of copies to print from 1 to 100.
+
+ -C _n_a_m_e
+ Sets the job name.
+
+ -J _n_a_m_e
+ Sets the job name.
+
+ -T _n_a_m_e
+ Sets the job name.
+
+ -l
+ Specifies that the print file is already formatted
+ for the destination and should be sent without fil-
+ tering. This option is equivalent to "-oraw".
+
+ -o _o_p_t_i_o_n
+ Sets a job option.
+
+ -p
+ 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.
+
+ -r
+ Specifies that the named print files should be
+ deleted after printing them.
+
+CCOOMMPPAATTIIBBIILLIITTYY
+ The "c", "d", "f", "g", "i", "m", "n", "t", "v", and "w"
+ options are not supported by CUPS and will produce a
+
+
+
+9 September 1999 Common UNIX Printing System 1
+
+
+
+
+
+lpr(1) Easy Software Products lpr(1)
+
+
+ warning message if used.
+
+SSEEEE AALLSSOO
+ cancel(1), lp(1), lpstat(1), CUPS Software Users Manual
+
+CCOOPPYYRRIIGGHHTT
+ Copyright 1993-2000 by Easy Software Products, All Rights
+ Reserved.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+9 September 1999 Common UNIX Printing System 2
+
+
diff --git a/man/lpr.man b/man/lpr.man
new file mode 100644
index 000000000..ec91d456e
--- /dev/null
+++ b/man/lpr.man
@@ -0,0 +1,96 @@
+.\"
+.\" "$Id: lpr.man 911 2000-02-23 03:17:06Z mike $"
+.\"
+.\" lpr man page for the Common UNIX Printing System (CUPS).
+.\"
+.\" Copyright 1997-2000 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" "9 September 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 a job option.
+.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 Users Manual
+.SH COPYRIGHT
+Copyright 1993-2000 by Easy Software Products, All Rights Reserved.
+.\"
+.\" End of "$Id: lpr.man 911 2000-02-23 03:17:06Z mike $".
+.\"
diff --git a/man/lprm.1 b/man/lprm.1
new file mode 100644
index 000000000..475461b58
--- /dev/null
+++ b/man/lprm.1
@@ -0,0 +1,66 @@
+
+
+
+lprm(1) Easy Software Products lprm(1)
+
+
+NNAAMMEE
+ lprm - cancel print jobs
+
+SSYYNNOOPPSSIISS
+ llpprrmm [ - ] [ -P _d_e_s_t_i_n_a_t_i_o_n ] [ _j_o_b _I_D_(_s_) ]
+
+DDEESSCCRRIIPPTTIIOONN
+ llpprrmm cancels print jobs that have been queued for print-
+ ing. The _-_P option specifies the destination printer or
+ class.
+
+ If no arguments are supplied, the current job on the
+ default destination is cancelled. You can specify one or
+ more job ID numbers to cancel those jobs, or use the _-
+ option to cancel all jobs.
+
+CCOOMMPPAATTIIBBIILLIITTYY
+ The CUPS version of _l_p_r_m is compatible with the standard
+ Berkeley _l_p_r_m command.
+
+SSEEEE AALLSSOO
+ cancel(1), lp(1), lpstat(1), lpr(1), CUPS Software Users
+ Manual
+
+CCOOPPYYRRIIGGHHTT
+ Copyright 1993-2000 by Easy Software Products, All Rights
+ Reserved.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+22 September 1999 Common UNIX Printing System 1
+
+
diff --git a/man/lprm.man b/man/lprm.man
new file mode 100644
index 000000000..5c3a58aa8
--- /dev/null
+++ b/man/lprm.man
@@ -0,0 +1,51 @@
+.\"
+.\" "$Id: lprm.man 911 2000-02-23 03:17:06Z mike $"
+.\"
+.\" lprm man page for the Common UNIX Printing System (CUPS).
+.\"
+.\" Copyright 1997-2000 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" "22 September 1999" "Easy Software Products"
+.SH NAME
+lprm \- cancel print jobs
+.SH SYNOPSIS
+.B lprm
+[ - ] [ -P
+.I destination
+] [
+.I job ID(s)
+]
+.SH DESCRIPTION
+\fBlprm\fR cancels print jobs that have been queued for printing. The \fI-P\fR
+option specifies the destination printer or class.
+.LP
+If no arguments are supplied, the current job on the default destination is
+cancelled. You can specify one or more job ID numbers to cancel those jobs,
+or use the \fI\-\fR option to cancel all jobs.
+.SH COMPATIBILITY
+The CUPS version of \fIlprm\fR is compatible with the standard Berkeley
+\fIlprm\fR command.
+.SH SEE ALSO
+cancel(1), lp(1), lpstat(1), lpr(1),
+CUPS Software Users Manual
+.SH COPYRIGHT
+Copyright 1993-2000 by Easy Software Products, All Rights Reserved.
+.\"
+.\" End of "$Id: lprm.man 911 2000-02-23 03:17:06Z mike $".
+.\"
diff --git a/man/lpstat.1 b/man/lpstat.1
new file mode 100644
index 000000000..ac5c8f42c
--- /dev/null
+++ b/man/lpstat.1
@@ -0,0 +1,132 @@
+
+
+
+lpstat(1) Easy Software Products lpstat(1)
+
+
+NNAAMMEE
+ lpstat - print cups status information
+
+SSYYNNOOPPSSIISS
+ llppssttaatt [ -a [ _d_e_s_t_i_n_a_t_i_o_n_(_s_) ] ] [ -c [ _c_l_a_s_s_(_e_s_) ] ] [ -d
+ ] [ -h _s_e_r_v_e_r ] [ -o [ _d_e_s_t_i_n_a_t_i_o_n_(_s_) ] ] [ -p [
+ _p_r_i_n_t_e_r_(_s_) ] ] [ -r ] [ -s ] [ -t ] [ -u [ _u_s_e_r_(_s_) ] ] [
+ -v [ _p_r_i_n_t_e_r_(_s_) ] ]
+
+DDEESSCCRRIIPPTTIIOONN
+ llppssttaatt displays status information about the current
+ classes, jobs, and printers. When run with no arguments,
+ llppssttaatt will list jobs queued by the user. Other options
+ include:
+
+ -a [_p_r_i_n_t_e_r_(_s_)]
+ Shows the accepting state of printer queues. If no
+ printers are specified then all printers are listed.
+
+ -c [_c_l_a_s_s_(_e_s_)]
+ Shows the printer classes and the printers that
+ belong to them. If no classes are specified then all
+ classes are listed.
+
+ -d
+ Shows the current default destination.
+
+ -h _s_e_r_v_e_r
+ Specifies the CUPS server to communicate with.
+
+ -o [_d_e_s_t_i_n_a_t_i_o_n_(_s_)]
+ Shows the jobs queue on the specified destinations.
+ If no destinations are specified all jobs are shown.
+
+ -p [_p_r_i_n_t_e_r_(_s_)]
+ Shows the printers and whether or not they are
+ enabled for printing. If no printers are specified
+ then all printers are listed.
+
+ -r
+ Shows whether or not the CUPS server is running.
+
+ -s
+ 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.
+
+ -t
+ Shows all status information. This is equivalent to
+ using the "-r", "-d", "-c", "-d", "-v", "-a", "-p",
+ and "-o" options.
+
+
+
+
+22 September 1999 Common UNIX Printing System 1
+
+
+
+
+
+lpstat(1) Easy Software Products lpstat(1)
+
+
+ -u [_u_s_e_r_(_s_)]
+ Shows a list of print jobs queued by the specified
+ users. If no users are specified, lists the jobs
+ queued by the current user.
+
+ -v [_p_r_i_n_t_e_r_(_s_)]
+ Shows the printers and what device they are attached
+ to. If no printers are specified then all printers
+ are listed.
+
+CCOOMMPPAATTIIBBIILLIITTYY
+ 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 nnoott case-sensitive.
+
+ The "-h" option is not a standard System V option.
+
+SSEEEE AALLSSOO
+ cancel(1), lp(1), CUPS Software Users Manual
+
+CCOOPPYYRRIIGGHHTT
+ Copyright 1993-2000 by Easy Software Products, All Rights
+ Reserved.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+22 September 1999 Common UNIX Printing System 2
+
+
diff --git a/man/lpstat.man b/man/lpstat.man
new file mode 100644
index 000000000..a6af5296a
--- /dev/null
+++ b/man/lpstat.man
@@ -0,0 +1,115 @@
+.\"
+.\" "$Id: lpstat.man 911 2000-02-23 03:17:06Z mike $"
+.\"
+.\" lpstat man page for the Common UNIX Printing System (CUPS).
+.\"
+.\" Copyright 1997-2000 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" "22 September 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 Users Manual
+.SH COPYRIGHT
+Copyright 1993-2000 by Easy Software Products, All Rights Reserved.
+.\"
+.\" End of "$Id: lpstat.man 911 2000-02-23 03:17:06Z mike $".
+.\"
diff --git a/man/mime.convs.5 b/man/mime.convs.5
new file mode 100644
index 000000000..5e70155ff
--- /dev/null
+++ b/man/mime.convs.5
@@ -0,0 +1,66 @@
+
+
+
+mime.convs(5) Easy Software Products mime.convs(5)
+
+
+NNAAMMEE
+ mime.convs - mime type conversion file for cups
+
+DDEESSCCRRIIPPTTIIOONN
+ The _m_i_m_e_._c_o_n_v_s file defines the filters that are available
+ for converting files from one format to another. The stan-
+ dard filters support text, PDF, PostScript, HP-GL/2, and
+ many types of image files.
+
+ Additional filters can be added to the _m_i_m_e_._c_o_n_v_s file or
+ to other files in the configuration directory
+ (//vvaarr//ccuuppss//ccoonnff) with the extension ".convs".
+
+SSEEEE AALLSSOO
+ classes.conf(5), cupsd(8), cupsd.conf(5), mime.types(5),
+ printers.conf(5), CUPS Software Administrators Manual,
+ CUPS Interface Design Description
+
+CCOOPPYYRRIIGGHHTT
+ Copyright 1993-2000 by Easy Software Products, All Rights
+ Reserved.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+22 September 1999 Common UNIX Printing System 1
+
+
diff --git a/man/mime.convs.man b/man/mime.convs.man
new file mode 100644
index 000000000..e764f9e6f
--- /dev/null
+++ b/man/mime.convs.man
@@ -0,0 +1,43 @@
+.\"
+.\" "$Id: mime.convs.man 911 2000-02-23 03:17:06Z mike $"
+.\"
+.\" mime.convs man page for the Common UNIX Printing System (CUPS).
+.\"
+.\" Copyright 1997-2000 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" "22 September 1999" "Easy Software Products"
+.SH NAME
+mime.convs \- mime type conversion file for cups
+.SH DESCRIPTION
+The \fImime.convs\fR file defines the filters that are available for
+converting files from one format to another. The standard filters
+support text, PDF, PostScript, HP-GL/2, and many types of image files.
+.LP
+Additional filters can be added to the \fImime.convs\fR file or to
+other files in the configuration directory (\fB/var/cups/conf\fR) with
+the extension ".convs".
+.SH SEE ALSO
+classes.conf(5), cupsd(8), cupsd.conf(5), mime.types(5), printers.conf(5),
+CUPS Software Administrators Manual,
+CUPS Interface Design Description
+.SH COPYRIGHT
+Copyright 1993-2000 by Easy Software Products, All Rights Reserved.
+.\"
+.\" End of "$Id: mime.convs.man 911 2000-02-23 03:17:06Z mike $".
+.\"
diff --git a/man/mime.types.5 b/man/mime.types.5
new file mode 100644
index 000000000..8fdc6b966
--- /dev/null
+++ b/man/mime.types.5
@@ -0,0 +1,66 @@
+
+
+
+mime.types(5) Easy Software Products mime.types(5)
+
+
+NNAAMMEE
+ mime.types - mime type description file for cups
+
+DDEESSCCRRIIPPTTIIOONN
+ The _m_i_m_e_._t_y_p_e_s file defines the recognized file types.
+
+ Additional file types can be added to _m_i_m_e_._t_y_p_e_s or in
+ additional files in the configuration directory
+ //vvaarr//ccuuppss//ccoonnff with the extension ".types".
+
+SSEEEE AALLSSOO
+ classes.conf(5), cupsd(8), cupsd.conf(5), mime.convs(5),
+ printers.conf(5), CUPS Software Administrators Manual,
+ CUPS Interface Design Description
+
+CCOOPPYYRRIIGGHHTT
+ Copyright 1993-2000 by Easy Software Products, All Rights
+ Reserved.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+22 September 1999 Common UNIX Printing System 1
+
+
diff --git a/man/mime.types.man b/man/mime.types.man
new file mode 100644
index 000000000..049302455
--- /dev/null
+++ b/man/mime.types.man
@@ -0,0 +1,40 @@
+.\"
+.\" "$Id: mime.types.man 911 2000-02-23 03:17:06Z mike $"
+.\"
+.\" mime.types man page for the Common UNIX Printing System (CUPS).
+.\"
+.\" Copyright 1997-2000 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" "22 September 1999" "Easy Software Products"
+.SH NAME
+mime.types \- mime type description file for cups
+.SH DESCRIPTION
+The \fImime.types\fR file defines the recognized file types.
+.LP
+Additional file types can be added to \fImime.types\fR or in additional files
+in the configuration directory \fB/var/cups/conf\fR with the extension ".types".
+.SH SEE ALSO
+classes.conf(5), cupsd(8), cupsd.conf(5), mime.convs(5), printers.conf(5),
+CUPS Software Administrators Manual,
+CUPS Interface Design Description
+.SH COPYRIGHT
+Copyright 1993-2000 by Easy Software Products, All Rights Reserved.
+.\"
+.\" End of "$Id: mime.types.man 911 2000-02-23 03:17:06Z mike $".
+.\"
diff --git a/man/printers.conf.5 b/man/printers.conf.5
new file mode 100644
index 000000000..46456d1ea
--- /dev/null
+++ b/man/printers.conf.5
@@ -0,0 +1,66 @@
+
+
+
+printers.conf(5) Easy Software Products printers.conf(5)
+
+
+NNAAMMEE
+ printers.conf - printer configuration file for cups
+
+DDEESSCCRRIIPPTTIIOONN
+ The _p_r_i_n_t_e_r_s_._c_o_n_f file defines the local printers that are
+ available. It is normally generated by the _c_u_p_s_d_(_8_) pro-
+ gram when printers are added, deleted, or modified.
+
+SSEEEE AALLSSOO
+ classes.conf(5), cupsd(8), cupsd.conf(5), mime.convs(5),
+ mime.types(5), CUPS Software Administrators Manual, CUPS
+ Interface Design Description
+
+CCOOPPYYRRIIGGHHTT
+ Copyright 1993-2000 by Easy Software Products, All Rights
+ Reserved.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+22 September 1999 Common UNIX Printing System 1
+
+
diff --git a/man/printers.conf.man b/man/printers.conf.man
new file mode 100644
index 000000000..eae17b704
--- /dev/null
+++ b/man/printers.conf.man
@@ -0,0 +1,39 @@
+.\"
+.\" "$Id: printers.conf.man 911 2000-02-23 03:17:06Z mike $"
+.\"
+.\" printers.conf man page for the Common UNIX Printing System (CUPS).
+.\"
+.\" Copyright 1997-2000 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" "22 September 1999" "Easy Software Products"
+.SH NAME
+printers.conf \- printer configuration file for cups
+.SH DESCRIPTION
+The \fIprinters.conf\fR file defines the local printers that are
+available. It is normally generated by the \fIcupsd(8)\fR program when
+printers are added, deleted, or modified.
+.SH SEE ALSO
+classes.conf(5), cupsd(8), cupsd.conf(5), mime.convs(5), mime.types(5),
+CUPS Software Administrators Manual,
+CUPS Interface Design Description
+.SH COPYRIGHT
+Copyright 1993-2000 by Easy Software Products, All Rights Reserved.
+.\"
+.\" End of "$Id: printers.conf.man 911 2000-02-23 03:17:06Z mike $".
+.\"
diff --git a/ppd/Makefile b/ppd/Makefile
new file mode 100644
index 000000000..511b476f6
--- /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 stcolor.ppd stphoto.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..c21a28887
--- /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-2000 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
+*%
+*FormatVersion: "4.3"
+*FileVersion: "1.1"
+*LanguageVersion: English
+*LanguageEncoding: ISOLatin1
+*PCFileName: "DESKJET.PPD"
+*Manufacturer: "ESP"
+*Product: "(CUPS v1.1)"
+*cupsVersion: 1.1
+*cupsManualCopies: True
+*cupsFilter: "application/vnd.cups-raster 0 rastertohp"
+*ModelName: "HP DeskJet Series"
+*ShortNickName: "HP DeskJet Series"
+*NickName: "HP DeskJet Series CUPS v1.1"
+*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..ca9e3a631
--- /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-2000 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
+*%
+*FormatVersion: "4.3"
+*FileVersion: "1.1"
+*LanguageVersion: English
+*LanguageEncoding: ISOLatin1
+*PCFileName: "LASERJET.PPD"
+*Manufacturer: "ESP"
+*Product: "(CUPS v1.1)"
+*cupsVersion: 1.1
+*cupsManualCopies: False
+*cupsFilter: "application/vnd.cups-raster 0 rastertohp"
+*ModelName: "HP LaserJet Series"
+*ShortNickName: "HP LaserJet Series"
+*NickName: "HP LaserJet Series CUPS v1.1"
+*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/ppd/stcolor.ppd b/ppd/stcolor.ppd
new file mode 100644
index 000000000..7ecbef95d
--- /dev/null
+++ b/ppd/stcolor.ppd
@@ -0,0 +1,131 @@
+*PPD-Adobe: "4.3"
+*%
+*% "$Id$"
+*%
+*% Sample EPSON Stylus Color driver PPD file for the Common UNIX Printing
+*% System (CUPS).
+*%
+*% Copyright 1997-2000 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
+*%
+*FormatVersion: "4.3"
+*FileVersion: "1.1"
+*LanguageVersion: English
+*LanguageEncoding: ISOLatin1
+*PCFileName: "STCOLOR.PPD"
+*Manufacturer: "ESP"
+*Product: "(CUPS v1.1)"
+*cupsVersion: 1.1
+*cupsManualCopies: True
+*cupsFilter: "application/vnd.cups-raster 0 rastertoepson"
+*cupsColorProfile 180dpi/-: "1.0 1.0 1.0 0.0 -0.2 -0.4 1.0 0.0 -0.2 0.0 1.0"
+*cupsColorProfile 360dpi/-: "1.0 1.5 1.0 0.0 -0.2 -0.4 1.0 0.0 -0.2 0.0 1.0"
+*cupsColorProfile 720dpi/-: "1.0 2.5 1.0 0.0 -0.2 -0.4 1.0 0.0 -0.2 0.0 1.0"
+*ModelName: "EPSON Stylus Color Series"
+*ShortNickName: "EPSON Stylus Color Series"
+*NickName: "EPSON Stylus Color Series CUPS v1.1"
+*PSVersion: "(2017.000) 0"
+*LanguageLevel: "2"
+*ColorDevice: True
+*DefaultColorSpace: RGB
+*FileSystem: False
+*Throughput: "1"
+*LandscapeOrientation: Plus90
+*VariablePaperSize: False
+*TTRasterizer: Type42
+
+*OpenUI *PageSize/Media Size: PickOne
+*OrderDependency: 10 AnySetup *PageSize
+*DefaultPageSize: Letter
+*PageSize Letter: "<</PageSize[612 792]/ImagingBBox null>>setpagedevice"
+*PageSize Legal: "<</PageSize[612 1008]/ImagingBBox null>>setpagedevice"
+*PageSize A4: "<</PageSize[595 842]/ImagingBBox null>>setpagedevice"
+*CloseUI: *PageSize
+
+*OpenUI *PageRegion: PickOne
+*OrderDependency: 10 AnySetup *PageRegion
+*DefaultPageRegion: Letter
+*PageRegion Letter: "<</PageSize[612 792]/ImagingBBox null>>setpagedevice"
+*PageRegion Legal: "<</PageSize[612 1008]/ImagingBBox null>>setpagedevice"
+*PageRegion A4: "<</PageSize[595 842]/ImagingBBox null>>setpagedevice"
+*CloseUI: *PageRegion
+
+*DefaultImageableArea: Letter
+*ImageableArea Letter: "8.60 39.60 603.40 766.49"
+*ImageableArea Legal: "8.60 39.60 603.40 982.49"
+*ImageableArea A4: "8.60 39.60 586.40 816.49"
+
+*DefaultPaperDimension: Letter
+*PaperDimension Letter: "612 792"
+*PaperDimension Legal: "612 1008"
+*PaperDimension A4: "595 842"
+
+*OpenUI *Resolution/Output Resolution: PickOne
+*OrderDependency: 20 AnySetup *Resolution
+*DefaultResolution: 360dpi
+*Resolution 180dpi/180 DPI: "<</HWResolution[180 180]>>setpagedevice"
+*Resolution 360dpi/360 DPI: "<</HWResolution[360 360]>>setpagedevice{0.6666 exp}bind settransfer"
+*Resolution 720dpi/720 DPI: "<</HWResolution[720 720]>>setpagedevice{0.4 exp}bind settransfer"
+*CloseUI: *Resolution
+
+*OpenUI *ColorModel/Output Mode: PickOne
+*OrderDependency: 10 AnySetup *ColorModel
+*DefaultColorModel: CMYK
+*ColorModel CMYK/Color: "<</cupsColorOrder 1/cupsColorSpace 8/cupsCompression 1>>setpagedevice"
+*ColorModel Gray/Grayscale: "<</cupsColorOrder 0/cupsColorSpace 3/cupsCompression 1>>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/stphoto.ppd b/ppd/stphoto.ppd
new file mode 100644
index 000000000..08bf671ef
--- /dev/null
+++ b/ppd/stphoto.ppd
@@ -0,0 +1,131 @@
+*PPD-Adobe: "4.3"
+*%
+*% "$Id$"
+*%
+*% Sample EPSON Stylus Photo driver PPD file for the Common UNIX Printing
+*% System (CUPS).
+*%
+*% Copyright 1997-2000 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
+*%
+*FormatVersion: "4.3"
+*FileVersion: "1.1"
+*LanguageVersion: English
+*LanguageEncoding: ISOLatin1
+*PCFileName: "STPHOTO.PPD"
+*Manufacturer: "ESP"
+*Product: "(CUPS v1.1)"
+*cupsVersion: 1.1
+*cupsManualCopies: True
+*cupsFilter: "application/vnd.cups-raster 0 rastertoepson"
+*cupsColorProfile 180dpi/-: "1.0 1.0 1.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 1.0"
+*cupsColorProfile 360dpi/-: "1.0 1.5 1.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 1.0"
+*cupsColorProfile 720dpi/-: "1.0 2.5 1.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 1.0"
+*ModelName: "EPSON Stylus Photo Series"
+*ShortNickName: "EPSON Stylus Photo Series"
+*NickName: "EPSON Stylus Photo Series CUPS v1.1"
+*PSVersion: "(2017.000) 0"
+*LanguageLevel: "2"
+*ColorDevice: True
+*DefaultColorSpace: RGB
+*FileSystem: False
+*Throughput: "1"
+*LandscapeOrientation: Plus90
+*VariablePaperSize: False
+*TTRasterizer: Type42
+
+*OpenUI *PageSize/Media Size: PickOne
+*OrderDependency: 10 AnySetup *PageSize
+*DefaultPageSize: Letter
+*PageSize Letter: "<</PageSize[612 792]/ImagingBBox null>>setpagedevice"
+*PageSize Legal: "<</PageSize[612 1008]/ImagingBBox null>>setpagedevice"
+*PageSize A4: "<</PageSize[595 842]/ImagingBBox null>>setpagedevice"
+*CloseUI: *PageSize
+
+*OpenUI *PageRegion: PickOne
+*OrderDependency: 10 AnySetup *PageRegion
+*DefaultPageRegion: Letter
+*PageRegion Letter: "<</PageSize[612 792]/ImagingBBox null>>setpagedevice"
+*PageRegion Legal: "<</PageSize[612 1008]/ImagingBBox null>>setpagedevice"
+*PageRegion A4: "<</PageSize[595 842]/ImagingBBox null>>setpagedevice"
+*CloseUI: *PageRegion
+
+*DefaultImageableArea: Letter
+*ImageableArea Letter: "8.60 39.60 603.40 766.49"
+*ImageableArea Legal: "8.60 39.60 603.40 982.49"
+*ImageableArea A4: "8.60 39.60 586.40 816.49"
+
+*DefaultPaperDimension: Letter
+*PaperDimension Letter: "612 792"
+*PaperDimension Legal: "612 1008"
+*PaperDimension A4: "595 842"
+
+*OpenUI *Resolution/Output Resolution: PickOne
+*OrderDependency: 20 AnySetup *Resolution
+*DefaultResolution: 360dpi
+*Resolution 180dpi/180 DPI: "<</HWResolution[180 180]>>setpagedevice"
+*Resolution 360dpi/360 DPI: "<</HWResolution[360 360]>>setpagedevice{0.6666 exp}bind settransfer"
+*Resolution 720dpi/720 DPI: "<</HWResolution[720 720]>>setpagedevice{0.4 exp}bind settransfer"
+*CloseUI: *Resolution
+
+*OpenUI *ColorModel/Output Mode: PickOne
+*OrderDependency: 10 AnySetup *ColorModel
+*DefaultColorModel: CMYK
+*ColorModel CMYK/Color: "<</cupsColorOrder 1/cupsColorSpace 9/cupsCompression 1>>setpagedevice"
+*ColorModel Gray/Grayscale: "<</cupsColorOrder 0/cupsColorSpace 3/cupsCompression 1>>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/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..2210434b2
--- /dev/null
+++ b/pstoraster/Makefile
@@ -0,0 +1,435 @@
+#
+# "$Id$"
+#
+# GNU Ghostscript makefile for the Common UNIX Printing System (CUPS).
+#
+# Copyright 1993-2000 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 = gconfig.o \
+ gdevabuf.o \
+ gdevbbox.o \
+ gdevcups.o \
+ gdevdbit.o \
+ gdevddrw.o \
+ gdevdflt.o \
+ gdevdgbr.o \
+ gdevhit.o \
+ gdevm16.o \
+ gdevm1.o \
+ gdevm24.o \
+ gdevm2.o \
+ gdevm32.o \
+ gdevm4.o \
+ gdevm8.o \
+ gdevmem.o \
+ gdevmpla.o \
+ gdevnfwd.o \
+ gdevpipe.o \
+ gdevprn.o \
+ gdevpsde.o \
+ gdevpsdf.o \
+ gdevpsdi.o \
+ gdevpsdp.o \
+ gdevpsds.o \
+ gdevpstr.o \
+ gdevps.o \
+ gdevvec.o \
+ gp_getnv.o \
+ gp_nofb.o \
+ gp_nsync.o \
+ gp_unifn.o \
+ gp_unifs.o \
+ gp_unix.o \
+ gsalloc.o \
+ gsalpha.o \
+ gsargs.o \
+ gsbitops.o \
+ gsbittab.o \
+ gscdefs.o \
+ gscdevn.o \
+ gschar0.o \
+ gschar.o \
+ gscie.o \
+ gsclipsr.o \
+ gscolor1.o \
+ gscolor2.o \
+ gscolor3.o \
+ gscolor.o \
+ gscoord.o \
+ gscparam.o \
+ gscpixel.o \
+ gscrdp.o \
+ gscrd.o \
+ gscscie.o \
+ gscsepr.o \
+ gscspace.o \
+ gsdevice.o \
+ gsdevmem.o \
+ gsdparam.o \
+ gsdps1.o \
+ gsdsrc.o \
+ gsfcmap.o \
+ gsfont0.o \
+ gsfont.o \
+ gsfunc0.o \
+ gsfunc3.o \
+ gsfunc.o \
+ gshsb.o \
+ gsht1.o \
+ gshtscr.o \
+ gsht.o \
+ gsimage.o \
+ gsimpath.o \
+ gsinit.o \
+ gsiodev.o \
+ gsline.o \
+ gsmalloc.o \
+ gsmatrix.o \
+ gsmemory.o \
+ gsmisc.o \
+ gsnorop.o \
+ gspaint.o \
+ gsparams.o \
+ gsparam.o \
+ gspath1.o \
+ gspath.o \
+ gspcolor.o \
+ gsshade.o \
+ gsstate.o \
+ gstext.o \
+ gstrap.o \
+ gstype1.o \
+ gstype2.o \
+ gstype42.o \
+ gsutil.o \
+ gxacpath.o \
+ gxbcache.o \
+ gxccache.o \
+ gxccman.o \
+ gxcht.o \
+ gxclbits.o \
+ gxclimag.o \
+ gxclip2.o \
+ gxclipm.o \
+ gxclip.o \
+ gxclist.o \
+ gxclmem.o \
+ gxclpage.o \
+ gxclpath.o \
+ gxclrast.o \
+ gxclread.o \
+ gxclrect.o \
+ gxclutil.o \
+ gxclzlib.o \
+ gxcmap.o \
+ gxcpath.o \
+ gxctable.o \
+ gxdcconv.o \
+ gxdcolor.o \
+ gxdither.o \
+ gxfill.o \
+ gxhint1.o \
+ gxhint2.o \
+ gxhint3.o \
+ gxht.o \
+ gxi12bit.o \
+ gxicolor.o \
+ gxidata.o \
+ gxifast.o \
+ gxiinit.o \
+ gximage3.o \
+ gximage4.o \
+ gximono.o \
+ gxiscale.o \
+ gxmclip.o \
+ gxp1fill.o \
+ gxpaint.o \
+ gxpath2.o \
+ gxpath.o \
+ gxpcmap.o \
+ gxpcopy.o \
+ gxpdash.o \
+ gxpflat.o \
+ gxsample.o \
+ gxshade1.o \
+ gxshade4.o \
+ gxshade6.o \
+ gxshade.o \
+ gxstroke.o \
+ gxtype1.o \
+ ialloc.o \
+ ibnum.o \
+ iccinit0.o \
+ iconfig.o \
+ icontext.o \
+ idebug.o \
+ idict.o \
+ idparam.o \
+ idstack.o \
+ igcref.o \
+ igcstr.o \
+ igc.o \
+ iinit.o \
+ ilocate.o \
+ imainarg.o \
+ imain.o \
+ iname.o \
+ interp.o \
+ iparam.o \
+ ireclaim.o \
+ isave.o \
+ iscanbin.o \
+ iscannum.o \
+ iscan.o \
+ istack.o \
+ iutil2.o \
+ iutil.o \
+ sbcp.o \
+ sbhc.o \
+ sbwbs.o \
+ scantab.o \
+ scfdtab.o \
+ scfd.o \
+ scfetab.o \
+ scfe.o \
+ scfparam.o \
+ sdcparam.o \
+ sdctc.o \
+ sdctd.o \
+ sdcte.o \
+ sddparam.o \
+ sdeparam.o \
+ seexec.o \
+ sfilter1.o \
+ sfilter2.o \
+ sfxstdio.o \
+ shcgen.o \
+ shc.o \
+ siscale.o \
+ sjpegc.o \
+ sjpegd.o \
+ sjpegerr.o \
+ sjpege.o \
+ slzwce.o \
+ slzwc.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 \
+ zcfont.o \
+ zchar1.o \
+ zchar2.o \
+ zchar32.o \
+ zchar42.o \
+ zcharout.o \
+ zchar.o \
+ zcid.o \
+ zcie.o \
+ zcolor1.o \
+ zcolor2.o \
+ zcolor.o \
+ zcontrol.o \
+ zcrd.o \
+ zcsdevn.o \
+ zcsindex.o \
+ zcspixel.o \
+ zcssepr.o \
+ zdevcal.o \
+ zdevice2.o \
+ zdevice.o \
+ zdict.o \
+ zdps1.o \
+ zfbcp.o \
+ zfcmap.o \
+ zfdctd.o \
+ zfdcte.o \
+ zfdecode.o \
+ zfileio.o \
+ zfile.o \
+ zfilter2.o \
+ zfilterx.o \
+ zfilter.o \
+ zfname.o \
+ zfont0.o \
+ zfont1.o \
+ zfont2.o \
+ zfont32.o \
+ zfont42.o \
+ zfont.o \
+ zfproc.o \
+ zfreuse.o \
+ zfunc0.o \
+ zfunc3.o \
+ zfunc.o \
+ zfzlib.o \
+ zgeneric.o \
+ zgstate.o \
+ zhsb.o \
+ zht1.o \
+ zht2.o \
+ zht.o \
+ zimage2.o \
+ zimage3.o \
+ zimage.o \
+ ziodev2.o \
+ ziodev.o \
+ zmath.o \
+ zmatrix.o \
+ zmedia2.o \
+ zmisc1.o \
+ zmisc2.o \
+ zmisc3.o \
+ zmisc.o \
+ zpacked.o \
+ zpaint.o \
+ zpath1.o \
+ zpath.o \
+ zpcolor.o \
+ zrelbit.o \
+ zshade.o \
+ zstack.o \
+ zstring.o \
+ zsysvm.o \
+ ztoken.o \
+ ztrap.o \
+ ztype.o \
+ zupath.o \
+ zusparam.o \
+ zvmem2.o \
+ zvmem.o
+
+OBJS = $(LIBOBJS) genarch.o pstoraster.o
+
+#
+# Data files...
+#
+
+DFILES = Fontmap \
+ gs_btokn.ps gs_ccfnt.ps gs_cff.ps gs_cidfn.ps \
+ gs_cmap.ps gs_cmdl.ps gs_dbt_e.ps gs_diskf.ps \
+ gs_dpnxt.ps gs_dps1.ps gs_dps2.ps gs_dps.ps gs_epsf.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_ll3.ps gs_mex_e.ps gs_mro_e.ps gs_pdfwr.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_typ32.ps gs_typ42.ps gs_type1.ps gs_wan_e.ps \
+ gs_wl1_e.ps gs_wl2_e.ps gs_wl5_e.ps pdf2dsc.ps \
+ pdf_base.ps pdf_draw.ps pdf_font.ps pdf_main.ps \
+ pdf_ops.ps pdf_sec.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) $(SERVERBIN)/filter
+ $(INSTALL_PROGRAM) pstoraster $(SERVERBIN)/filter
+ $(RM) $(SERVERBIN)/filter/pdftops
+ $(LN) pstoraster $(SERVERBIN)/filter/pdftops
+ -$(MKDIR) $(DATADIR)/pstoraster
+ $(INSTALL_DATA) $(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: ../filter/raster.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 \
+ ../filter/$(LIBCUPSIMAGE) $(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..7adae17de
--- /dev/null
+++ b/pstoraster/bfont.h
@@ -0,0 +1,76 @@
+/* Copyright (C) 1992, 1995, 1996, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Interpreter internal routines and data needed for building fonts */
+/* Requires gxfont.h */
+
+#ifndef bfont_INCLUDED
+# define bfont_INCLUDED
+
+#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;
+
+/* Options for collecting parameters from a font dictionary. */
+/* The comment indicates where the option is tested. */
+typedef enum {
+ bf_options_none = 0,
+ bf_Encoding_optional = 1, /* build_gs_font */
+ bf_FontBBox_required = 2, /* build_gs_simple_font */
+ bf_UniqueID_ignored = 4, /* build_gs_simple_font */
+ bf_CharStrings_optional = 8, /* build_gs_primitive_font */
+ bf_notdef_required = 16 /* build_gs_primitive_font */
+} build_font_options_t;
+
+/* In zfont2.c */
+int build_proc_name_refs(P3(build_proc_refs * pbuild,
+ const char *bcstr,
+ const char *bgstr));
+int build_gs_font_procs(P2(os_ptr, build_proc_refs *));
+int build_gs_primitive_font(P6(os_ptr, gs_font_base **, font_type,
+ gs_memory_type_ptr_t, const build_proc_refs *,
+ build_font_options_t));
+int build_gs_simple_font(P6(os_ptr, gs_font_base **, font_type,
+ gs_memory_type_ptr_t, const build_proc_refs *,
+ build_font_options_t));
+void lookup_gs_simple_font_encoding(P1(gs_font_base *));
+int build_gs_font(P6(os_ptr, gs_font **, font_type,
+ gs_memory_type_ptr_t, const build_proc_refs *,
+ build_font_options_t));
+int define_gs_font(P1(gs_font *));
+
+#endif /* bfont_INCLUDED */
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..ac798ef9c
--- /dev/null
+++ b/pstoraster/btoken.h
@@ -0,0 +1,41 @@
+/* Copyright (C) 1990, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Definitions for Level 2 binary tokens */
+
+#ifndef btoken_INCLUDED
+# define btoken_INCLUDED
+
+/* Define accessors for pointers to the system and user name tables. */
+extern ref binary_token_names; /* array of size 2 */
+
+#define system_names_p (binary_token_names.value.refs)
+#define user_names_p (binary_token_names.value.refs + 1)
+
+/* Convert an object to its representation in a binary object sequence. */
+int encode_binary_token(P4(const ref * obj, long *ref_offset, long *char_offset,
+ byte * str));
+
+#endif /* btoken_INCLUDED */
diff --git a/pstoraster/ctype_.h b/pstoraster/ctype_.h
new file mode 100644
index 000000000..c120d647f
--- /dev/null
+++ b/pstoraster/ctype_.h
@@ -0,0 +1,37 @@
+/* Copyright (C) 1993, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Wrapper for ctype.h */
+
+#ifndef ctype__INCLUDED
+# define ctype__INCLUDED
+
+/* 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>
+
+#endif /* ctype__INCLUDED */
diff --git a/pstoraster/dirent_.h b/pstoraster/dirent_.h
new file mode 100644
index 000000000..4248f12cf
--- /dev/null
+++ b/pstoraster/dirent_.h
@@ -0,0 +1,60 @@
+/*
+ Copyright 1993-2000 by Easy Software Products.
+ Copyright 1993, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Generic substitute for Unix dirent.h */
+
+#ifndef dirent__INCLUDED
+# define dirent__INCLUDED
+
+/* 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 /* sys/ndir or ndir or sys/dir, i.e., no dirent */
+# ifdef HAVE_SYS_DIR_H
+# include <sys/dir.h>
+# endif
+# ifdef HAVE_SYS_NDIR_H
+# include <sys/ndir.h>
+# endif
+# ifdef HAVE_NDIR_H
+# include <ndir.h>
+# endif
+typedef struct direct dir_entry;
+
+#endif /* sys/ndir or ndir or sys/dir */
+
+#endif /* dirent__INCLUDED */
diff --git a/pstoraster/dstack.h b/pstoraster/dstack.h
new file mode 100644
index 000000000..a9480a9e9
--- /dev/null
+++ b/pstoraster/dstack.h
@@ -0,0 +1,236 @@
+/* Copyright (C) 1992, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Definitions for the interpreter's dictionary stack */
+
+#ifndef dstack_INCLUDED
+# define dstack_INCLUDED
+
+#include "idstack.h"
+
+/* Define the (currently static) dictionary stack instance. */
+extern dict_stack_t idict_stack;
+
+#define d_stack (idict_stack.stack)
+
+/* Define the interpreter-specific versions of the generic dstack API. */
+#define min_dstack_size (idict_stack.min_size)
+#define dstack_userdict_index (idict_stack.userdict_index)
+#define dsspace (idict_stack.def_space)
+#define dtop_can_store(pvalue) ((int)r_space(pvalue) <= dsspace)
+#define dtop_keys (idict_stack.top_keys)
+#define dtop_npairs (idict_stack.top_npairs)
+#define dtop_values (idict_stack.top_values)
+#define dict_set_top() dstack_set_top(&idict_stack);
+#define dict_is_permanent_on_dstack(pdict)\
+ dstack_dict_is_permanent(&idict_stack, pdict)
+#define dicts_gc_cleanup() dstack_gc_cleanup(&idict_stack)
+#define systemdict (&idict_stack.system_dict)
+
+/* Define the dictionary stack pointers. */
+#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); }
+
+/*
+ * 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.
+ */
+
+/* Name lookup */
+#define dict_find_name_by_index(nidx)\
+ dstack_find_name_by_index(&idict_stack, nidx)
+#define dict_find_name(pnref) dict_find_name_by_index(name_index(pnref))
+#define dict_find_name_by_index_inline(nidx, htemp)\
+ dstack_find_name_by_index_inline(&idict_stack, nidx, htemp)
+#define if_dict_find_name_by_index_top(nidx, htemp, pvslot)\
+ if_dstack_find_name_by_index_top(&idict_stack, nidx, htemp, pvslot)
+
+/*
+ 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
+ */
+
+#endif /* dstack_INCLUDED */
diff --git a/pstoraster/errno_.h b/pstoraster/errno_.h
new file mode 100644
index 000000000..b2a2ae561
--- /dev/null
+++ b/pstoraster/errno_.h
@@ -0,0 +1,42 @@
+/* Copyright (C) 1993, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Generic substitute for Unix errno.h */
+
+#ifndef errno__INCLUDED
+# define errno__INCLUDED
+
+/* 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
+
+#endif /* errno__INCLUDED */
diff --git a/pstoraster/errors.h b/pstoraster/errors.h
new file mode 100644
index 000000000..b5fd1844b
--- /dev/null
+++ b/pstoraster/errors.h
@@ -0,0 +1,148 @@
+/* Copyright (C) 1989, 1995, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Definition of error codes */
+
+#ifndef errors_INCLUDED
+# define errors_INCLUDED
+
+/*
+ * 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 the error name table */
+extern const char *const gs_error_names[];
+
+ /* ------ PostScript Level 1 errors ------ */
+
+#define e_unknownerror (-1) /* unknown error */
+#define e_dictfull (-2)
+#define e_dictstackoverflow (-3)
+#define e_dictstackunderflow (-4)
+#define e_execstackoverflow (-5)
+#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
+#define e_invalidaccess (-7)
+#define e_invalidexit (-8)
+#define e_invalidfileaccess (-9)
+#define e_invalidfont (-10)
+#define e_invalidrestore (-11)
+#define e_ioerror (-12)
+#define e_limitcheck (-13)
+#define e_nocurrentpoint (-14)
+#define e_rangecheck (-15)
+#define e_stackoverflow (-16)
+#define e_stackunderflow (-17)
+#define e_syntaxerror (-18)
+#define e_timeout (-19)
+#define e_typecheck (-20)
+#define e_undefined (-21)
+#define e_undefinedfilename (-22)
+#define e_undefinedresult (-23)
+#define e_unmatchedmark (-24)
+#define e_VMerror (-25)
+
+#define LEVEL1_ERROR_NAMES\
+ "unknownerror", "dictfull", "dictstackoverflow", "dictstackunderflow",\
+ "execstackoverflow", "interrupt", "invalidaccess", "invalidexit",\
+ "invalidfileaccess", "invalidfont", "invalidrestore", "ioerror",\
+ "limitcheck", "nocurrentpoint", "rangecheck", "stackoverflow",\
+ "stackunderflow", "syntaxerror", "timeout", "typecheck", "undefined",\
+ "undefinedfilename", "undefinedresult", "unmatchedmark", "VMerror"
+
+ /* ------ Additional Level 2 and DPS errors ------ */
+
+#define e_configurationerror (-26)
+#define e_invalidcontext (-27)
+#define e_undefinedresource (-28)
+#define e_unregistered (-29)
+/* invalidid is for the NeXT DPS extension. */
+#define e_invalidid (-30)
+
+#define LEVEL2_ERROR_NAMES\
+ "configurationerror", "invalidcontext", "undefinedresource",\
+ "unregistered", "invalidid"
+
+#define ERROR_NAMES LEVEL1_ERROR_NAMES, LEVEL2_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)
+
+#endif /* errors_INCLUDED */
diff --git a/pstoraster/estack.h b/pstoraster/estack.h
new file mode 100644
index 000000000..2c42beace
--- /dev/null
+++ b/pstoraster/estack.h
@@ -0,0 +1,139 @@
+/* Copyright (C) 1989, 1992, 1993, 1994, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Definitions for the execution stack */
+
+#ifndef estack_INCLUDED
+# define estack_INCLUDED
+
+#include "iestack.h"
+
+/* There's only one exec stack right now.... */
+#define esfile (iexec_stack.current_file)
+#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)
+
+/* Define the execution stack pointers. */
+extern exec_stack_t iexec_stack;
+
+#define e_stack (iexec_stack.stack)
+#define esbot (e_stack.bot)
+#define esp (e_stack.p)
+#define estop (e_stack.top)
+
+/*
+ * 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 whatever state
+ * they need to save or keep track of and then 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 because of an error, stop, exit,
+ * or quit. (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 operators that need to be handled like
+ * looping operators -- for example, all the 'show' operators, 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)
+#define set_estack_mark_index(ep, es_idx) r_set_size(ep, es_idx)
+
+/* 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 e-stack, must take this into account. These are:
+ * exit .stop .instopped countexecstack execstack currentfile
+ * .execn
+ * 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.
+ */
+
+#endif /* estack_INCLUDED */
diff --git a/pstoraster/files.h b/pstoraster/files.h
new file mode 100644
index 000000000..bca695d4c
--- /dev/null
+++ b/pstoraster/files.h
@@ -0,0 +1,157 @@
+/* Copyright (C) 1989, 1995, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Requires stream.h */
+
+#ifndef files_INCLUDED
+# define files_INCLUDED
+
+/*
+ * 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;
+
+/* Export the stdio refs for switching contexts. */
+extern ref ref_stdio[3];
+
+#define ref_stdin ref_stdio[0]
+#define ref_stdout ref_stdio[1]
+#define ref_stderr ref_stdio[2]
+/* An invalid (closed) file. */
+extern stream *invalid_file_entry;
+
+/*
+ * Macros for checking file validity.
+ * NOTE: in order to work around a bug in the Borland 5.0 compiler,
+ * you must use file_is_invalid rather than !file_is_valid.
+ */
+#define file_is_valid(svar,op)\
+ (svar = fptr(op), (svar->read_id | svar->write_id) == r_size(op))
+#define file_is_invalid(svar,op)\
+ (svar = fptr(op), (svar->read_id | svar->write_id) != r_size(op))
+#define check_file(svar,op)\
+ BEGIN\
+ check_type(*(op), t_file);\
+ if ( file_is_invalid(svar, op) ) return_error(e_invalidaccess);\
+ END
+
+/*
+ * 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)\
+ BEGIN\
+ check_read_type(*(op), t_file);\
+ check_read_known_file(svar, op, return);\
+ END
+#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)\
+ BEGIN\
+ 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 BEGIN invalid_action; END; /* closed or reopened file */\
+ }\
+ END
+int file_switch_to_write(P1(const ref *));
+
+#define check_write_file(svar,op)\
+ BEGIN\
+ check_write_type(*(op), t_file);\
+ check_write_known_file(svar, op, return);\
+ END
+#define check_write_known_file(svar,op,error_return)\
+ BEGIN\
+ svar = fptr(op);\
+ if ( svar->write_id != r_size(op) )\
+ { int fcode = file_switch_to_write(op);\
+ if ( fcode < 0 ) error_return(fcode);\
+ }\
+ END
+
+/* Data exported by zfile.c. */
+ /* for zfilter.c and ziodev.c */
+extern const uint file_default_buffer_size;
+
+/* Procedures exported by zfile.c. */
+ /* for imainarg.c */
+FILE *lib_fopen(P1(const char *));
+
+ /* for imain.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 *,
+ 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 zfproc.c, ziodev.c */
+stream *file_alloc_stream(P2(gs_memory_t *, client_name_t));
+
+/* Procedures exported by zfileio.c. */
+ /* for ziodev.c */
+int zreadline_from(P5(stream *, byte *, uint, uint *, bool *));
+
+#endif /* files_INCLUDED */
diff --git a/pstoraster/fname.h b/pstoraster/fname.h
new file mode 100644
index 000000000..f6eeb8c73
--- /dev/null
+++ b/pstoraster/fname.h
@@ -0,0 +1,54 @@
+/* Copyright (C) 1993, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Requires gxiodev.h */
+
+#ifndef fname_INCLUDED
+# define fname_INCLUDED
+
+/*
+ * Define a structure for representing a parsed file name, consisting of
+ * an IODevice name in %'s, a file name, or both. 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;
+
+/* Parse a file name into device and individual name. */
+int parse_file_name(P2(const ref *, parsed_file_name *));
+
+/* Parse a real (non-device) file name and convert to a C string. */
+int parse_real_file_name(P3(const ref *, parsed_file_name *, client_name_t));
+
+/* Convert a file name to a C string by adding a null terminator. */
+int terminate_file_name(P2(parsed_file_name *, client_name_t));
+
+/* Free a file name that was copied to a C string. */
+void free_file_name(P2(parsed_file_name *, client_name_t));
+
+#endif /* fname_INCLUDED */
diff --git a/pstoraster/gconf.h b/pstoraster/gconf.h
new file mode 100644
index 000000000..0317aa903
--- /dev/null
+++ b/pstoraster/gconf.h
@@ -0,0 +1,43 @@
+/* Copyright (C) 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Wrapper for gconfig.h or a substitute. */
+
+/*
+ * NOTA BENE: This file, unlike all other header files, must *not* have
+ * double-inclusion protection, since it is used in peculiar ways.
+ */
+
+/*
+ * Since not all C preprocessors implement #include with a non-quoted
+ * argument, we arrange things so that we can still compile with such
+ * compilers as long as GCONFIG_H isn't defined.
+ */
+
+#ifndef GCONFIG_H
+# include "gconfig.h"
+#else
+# include GCONFIG_H
+#endif
diff --git a/pstoraster/gconfig.c b/pstoraster/gconfig.c
new file mode 100644
index 000000000..71d5b106f
--- /dev/null
+++ b/pstoraster/gconfig.c
@@ -0,0 +1,127 @@
+/* Copyright (C) 1989, 1995, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Configuration tables */
+#include "memory_.h"
+#include "gx.h"
+#include "gscdefs.h" /* interface */
+#include "gconf.h" /* for #defines */
+#include "gxdevice.h"
+#include "gxiodev.h"
+
+/*
+ * The makefile generates the file gconfig.h, which consists of
+ * lines of the form
+ * device_(gs_xxx_device)
+ * or
+ * device2_(gs_xxx_device)
+ * for each installed device;
+ * emulator_("emulator", strlen("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", strlen("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 device2_(dev) extern const gx_device dev;
+#define init_(proc) extern void proc(P1(gs_memory_t *));
+#define io_device_(iodev) extern const gx_io_device iodev;
+#include "gconf.h"
+#undef init_
+#undef io_device_
+#undef device2_
+#undef device_
+
+/* Set up the initialization procedure table. */
+extern_gx_init_table();
+private void gconf_init(P1(gs_memory_t *));
+#define init_(proc) proc,
+const gx_init_proc gx_init_table[] = {
+#include "gconf.h"
+ gconf_init,
+ 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,
+const gx_io_device *const gx_io_device_table[] = {
+ &gs_iodev_os,
+#include "gconf.h"
+ 0
+};
+#undef io_device_
+const uint gx_io_device_table_count = countof(gx_io_device_table) - 1;
+
+/* Set up the device table. */
+#define device_(dev) (const gx_device *)&dev,
+#define device2_(dev) &dev,
+private const gx_device *const gx_device_list[] = {
+#include "gconf.h"
+ 0
+};
+#undef device2_
+#undef device_
+
+/* Allocate and initialize structure descriptors for the devices. */
+private gs_memory_struct_type_t gx_device_st_list[countof(gx_device_list) - 1];
+private void
+gconf_init(gs_memory_t *mem)
+{
+ int i;
+
+ for (i = 0; i < countof(gx_device_list) - 1; ++i)
+ gx_device_make_struct_type(&gx_device_st_list[i], gx_device_list[i]);
+}
+
+/* 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 * const **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..5f30f9ef1
--- /dev/null
+++ b/pstoraster/gconfig.h
@@ -0,0 +1,257 @@
+/*
+ * "$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-2000 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 device2_
+device2_(gs_cups_device)
+device2_(gs_pswrite_device)
+device2_(gs_bbox_device)
+device2_(gs_nullpage_device)
+#endif
+#ifdef oper_
+oper_(zchar1_op_defs)
+oper_(zfont1_op_defs)
+oper_(zmisc1_op_defs)
+#endif
+#ifdef psfile_
+psfile_("gs_type1.ps",11)
+#endif
+#ifdef oper_
+oper_(zvmem2_op_defs)
+oper_(zdps1_l2_op_defs)
+#endif
+#ifdef psfile_
+psfile_("gs_dps1.ps",10)
+#endif
+#ifdef oper_
+oper_(zusparam_op_defs)
+oper_(zmisc2_op_defs)
+#endif
+#ifdef psfile_
+psfile_("gs_lev2.ps",10)
+psfile_("gs_res.ps",9)
+#endif
+#ifdef oper_
+oper_(zchar42_op_defs)
+oper_(zfont42_op_defs)
+#endif
+#ifdef psfile_
+psfile_("gs_typ42.ps",11)
+psfile_("gs_cidfn.ps",11)
+#endif
+#ifdef oper_
+oper_(zcid_op_defs)
+oper_(zcie_l2_op_defs)
+oper_(zcrd_l2_op_defs)
+oper_(zfcmap_op_defs)
+#endif
+#ifdef psfile_
+psfile_("gs_cmap.ps",10)
+#endif
+#ifdef oper_
+oper_(zcfont_op_defs)
+oper_(zfont0_op_defs)
+# ifdef HAVE_LIBJPEG
+oper_(zfdcte_op_defs)
+oper_(zfdctd_op_defs)
+# endif /* HAVE_LIBJPEG */
+oper_(zbseq_l2_op_defs)
+#endif
+#ifdef psfile_
+psfile_("gs_btokn.ps",11)
+#endif
+#ifdef init_
+init_(gs_gxicolor_init)
+#endif
+#ifdef oper_
+oper_(zcolor1_op_defs)
+oper_(zht1_op_defs)
+oper_(zupath_l2_op_defs)
+oper_(ireclaim_l2_op_defs)
+oper_(zchar2_op_defs)
+#endif
+#ifdef psfile_
+psfile_("gs_dps2.ps",10)
+#endif
+#ifdef oper_
+oper_(zfdecode_op_defs)
+oper_(zfilter2_op_defs)
+oper_(ziodev2_l2_op_defs)
+#endif
+#ifdef io_device_
+io_device_(gs_iodev_null)
+io_device_(gs_iodev_ram)
+io_device_(gs_iodev_calendar)
+#endif
+#ifdef oper_
+oper_(zdevice2_l2_op_defs)
+oper_(zmedia2_l2_op_defs)
+#endif
+#ifdef psfile_
+psfile_("gs_setpd.ps",11)
+#endif
+#ifdef oper_
+oper_(zpcolor_l2_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_(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_(zimage_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)
+#endif
+#ifdef init_
+init_(gs_gstype1_init)
+#endif
+#ifdef emulator_
+emulator_("PostScript",10)
+emulator_("PostScriptLevel1",16)
+#endif
+#ifdef init_
+init_(gs_gxi12bit_init)
+init_(gs_gxiscale_init)
+#endif
+#ifdef oper_
+oper_(zcolor2_l2_op_defs)
+oper_(zcsindex_l2_op_defs)
+oper_(zht2_l2_op_defs)
+oper_(zimage2_l2_op_defs)
+oper_(zcssepr_l2_op_defs)
+oper_(zchar32_op_defs)
+oper_(zfont32_op_defs)
+#endif
+#ifdef psfile_
+psfile_("gs_typ32.ps",11)
+#endif
+#ifdef oper_
+oper_(zfilterx_op_defs)
+#endif
+#ifdef emulator_
+emulator_("PostScriptLevel2",16)
+#endif
+#ifdef oper_
+oper_(zcspixel_op_defs)
+oper_(zfunc_op_defs)
+oper_(zfunc0_op_defs)
+oper_(zcsdevn_op_defs)
+oper_(zfreuse_op_defs)
+oper_(zfunc3_op_defs)
+oper_(zimage3_op_defs)
+oper_(zmisc3_op_defs)
+oper_(zshade_op_defs)
+oper_(ztrap_op_defs)
+#endif
+#ifdef psfile_
+psfile_("gs_ll3.ps",9)
+#endif
+#ifdef oper_
+# ifdef HAVE_LIBZ
+oper_(zfzlib_op_defs)
+# endif /* HAVE_LIBZ */
+#endif
+#ifdef psfile_
+psfile_("gs_mex_e.ps",11)
+psfile_("gs_mro_e.ps",11)
+psfile_("gs_pdf_e.ps",11)
+psfile_("gs_wan_e.ps",11)
+psfile_("pdf_ops.ps",10)
+psfile_("gs_l2img.ps",11)
+psfile_("pdf_base.ps",11)
+psfile_("pdf_draw.ps",11)
+psfile_("pdf_font.ps",11)
+psfile_("pdf_main.ps",11)
+psfile_("pdf_sec.ps",10)
+#endif
+#ifdef emulator_
+emulator_("PDF",3)
+#endif
+#ifdef psfile_
+psfile_("gs_cff.ps",9)
+psfile_("gs_ttf.ps",9)
+#endif
+#ifdef init_
+init_(gs_gstype2_init)
+#endif
+#ifdef io_device_
+io_device_(gs_iodev_pipe)
+#endif
+#ifdef psfile_
+psfile_("gs_epsf.ps",10)
+#endif
+#ifdef init_
+# ifdef HAVE_LIBZ
+init_(gs_cl_zlib_init)
+# endif /* HAVE_LIBZ */
+init_(gs_gshtscr_init)
+init_(gs_gsutil_init)
+init_(gs_gxcht_init)
+init_(gs_gxifast_init)
+init_(gs_gximono_init)
+#endif
+#define SEARCH_HERE_FIRST 1
+
+/*
+ * End of "$Id$".
+ */
diff --git a/pstoraster/gconfig_.h b/pstoraster/gconfig_.h
new file mode 100644
index 000000000..518b79020
--- /dev/null
+++ b/pstoraster/gconfig_.h
@@ -0,0 +1,5 @@
+/* This file was generated automatically. */
+#define HAVE_DIRENT_H
+#define HAVE_SYS_DIR_H
+#define HAVE_SYS_TIME_H
+#define HAVE_SYS_TIMES_H
diff --git a/pstoraster/gconfigv.h b/pstoraster/gconfigv.h
new file mode 100644
index 000000000..a9ae98b88
--- /dev/null
+++ b/pstoraster/gconfigv.h
@@ -0,0 +1,4 @@
+#define USE_ASM (-0)
+#define USE_FPU (1-0)
+#define EXTEND_NAMES 0
+#define SYSTEM_CONSTANTS_ARE_WRITABLE 0
diff --git a/pstoraster/gdebug.h b/pstoraster/gdebug.h
new file mode 100644
index 000000000..9b1480248
--- /dev/null
+++ b/pstoraster/gdebug.h
@@ -0,0 +1,132 @@
+/* Copyright (C) 1989, 1992, 1993, 1994, 1995, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Debugging machinery definitions */
+
+#ifndef gdebug_INCLUDED
+# define gdebug_INCLUDED
+
+/*
+ * The compile-time DEBUG symbol determines whether debugging/tracing
+ * code is included in the compiled code. DEBUG may be set or not set
+ * independently for every compilation; however, a small amount of support
+ * machinery in gsmisc.c is always included in the executable, just
+ * in case *some* file was compiled with DEBUG set.
+ *
+ * When DEBUG is set, it does not cause debugging/tracing printout to occur.
+ * Rather, it includes code that produces such printout *if* (a) given
+ * one(s) of 128 debugging flags is set. In this way, one can selectively
+ * turn printout on and off during debugging. (In fact, we even provide a
+ * PostScript operator, .setdebug, that does this.)
+ *
+ * The debugging flags are normally indexed by character code. This is more
+ * than a convention: gs_debug_c, which tests whether a given flag is set,
+ * considers that if a flag named by a given upper-case letter is set, the
+ * flag named by the corresponding lower-case letter is also set.
+ *
+ * If the output selected by a given flag can be printed by a single
+ * printf, the conventional way to produce the output is
+ * if_debugN('x', "...format...", v1, ..., vN);
+ * Usually the flag appears in the output explicitly:
+ * if_debugN('x', "[x]...format...", v1, ..., vN);
+ * If the output is more complex, the conventional way to produce the
+ * output is
+ * if ( gs_debug_c('x') ) {
+ * ... start each line with dlprintfN(...)
+ * ... produce additional output within a line with dprintfN(...)
+ * } */
+
+/* Define the array of debugging flags, indexed by character code. */
+extern char gs_debug[128];
+bool gs_debug_c(P1(int /*char */ ));
+
+/*
+ * 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
+
+/* Debugging printout macros. */
+#ifdef DEBUG
+# define if_debug0(c,s)\
+ BEGIN if (gs_debug_c(c)) dlprintf(s); END
+# define if_debug1(c,s,a1)\
+ BEGIN if (gs_debug_c(c)) dlprintf1(s,a1); END
+# define if_debug2(c,s,a1,a2)\
+ BEGIN if (gs_debug_c(c)) dlprintf2(s,a1,a2); END
+# define if_debug3(c,s,a1,a2,a3)\
+ BEGIN if (gs_debug_c(c)) dlprintf3(s,a1,a2,a3); END
+# define if_debug4(c,s,a1,a2,a3,a4)\
+ BEGIN if (gs_debug_c(c)) dlprintf4(s,a1,a2,a3,a4); END
+# define if_debug5(c,s,a1,a2,a3,a4,a5)\
+ BEGIN if (gs_debug_c(c)) dlprintf5(s,a1,a2,a3,a4,a5); END
+# define if_debug6(c,s,a1,a2,a3,a4,a5,a6)\
+ BEGIN if (gs_debug_c(c)) dlprintf6(s,a1,a2,a3,a4,a5,a6); END
+# define if_debug7(c,s,a1,a2,a3,a4,a5,a6,a7)\
+ BEGIN if (gs_debug_c(c)) dlprintf7(s,a1,a2,a3,a4,a5,a6,a7); END
+# define if_debug8(c,s,a1,a2,a3,a4,a5,a6,a7,a8)\
+ BEGIN if (gs_debug_c(c)) dlprintf8(s,a1,a2,a3,a4,a5,a6,a7,a8); END
+# define if_debug9(c,s,a1,a2,a3,a4,a5,a6,a7,a8,a9)\
+ BEGIN if (gs_debug_c(c)) dlprintf9(s,a1,a2,a3,a4,a5,a6,a7,a8,a9); END
+# define if_debug10(c,s,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10)\
+ BEGIN if (gs_debug_c(c)) dlprintf10(s,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10); END
+# define if_debug11(c,s,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11)\
+ BEGIN if (gs_debug_c(c)) dlprintf11(s,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11); END
+# define if_debug12(c,s,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12)\
+ BEGIN if (gs_debug_c(c)) dlprintf12(s,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12); END
+#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/gdev8bcm.h b/pstoraster/gdev8bcm.h
new file mode 100644
index 000000000..f9f678114
--- /dev/null
+++ b/pstoraster/gdev8bcm.h
@@ -0,0 +1,78 @@
+/* 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 supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Requires gxdevice.h (for gx_color_value) */
+
+#ifndef gdev8bcm_INCLUDED
+# define gdev8bcm_INCLUDED
+
+/*
+ * The MS-DOS, MS Windows, and X Windows drivers all use (at least on
+ * some platforms) an 8-bit color map in which some fraction is reserved
+ * for a pre-allocated cube and some or all of the remainder is
+ * allocated dynamically. Since looking up colors in this map can be
+ * a major performance bottleneck, we provide an efficient implementation
+ * that can be shared among drivers.
+ *
+ * As a performance compromise, we only look up the top 5 bits of the
+ * RGB value in the color map. This compromises color quality very little,
+ * and allows substantial optimizations.
+ */
+
+#define gx_8bit_map_size 323
+#define gx_8bit_map_spreader 123 /* approx. 323 - (1.618 * 323) */
+typedef struct gx_8bit_map_entry_s {
+ ushort rgb; /* key = 0rrrrrgggggbbbbb */
+#define gx_8bit_no_rgb ((ushort)0xffff)
+#define gx_8bit_rgb_key(r, g, b)\
+ (((r >> (gx_color_value_bits - 5)) << 10) +\
+ ((g >> (gx_color_value_bits - 5)) << 5) +\
+ (b >> (gx_color_value_bits - 5)))
+ short index; /* value */
+} gx_8bit_map_entry;
+typedef struct gx_8bit_color_map_s {
+ int count; /* # of occupied entries */
+ int max_count; /* max # of occupied entries */
+ gx_8bit_map_entry map[gx_8bit_map_size + 1];
+} gx_8bit_color_map;
+
+/* Initialize an 8-bit color map. */
+void gx_8bit_map_init(P2(gx_8bit_color_map *, int));
+
+/* Look up a color in an 8-bit color map. */
+/* Return -1 if not found. */
+int gx_8bit_map_rgb_color(P4(const gx_8bit_color_map *, gx_color_value,
+ gx_color_value, gx_color_value));
+
+/* Test whether an 8-bit color map has room for more entries. */
+#define gx_8bit_map_is_full(pcm)\
+ ((pcm)->count == (pcm)->max_count)
+
+/* Add a color to an 8-bit color map. */
+/* Return -1 if the map is full. */
+int gx_8bit_add_rgb_color(P4(gx_8bit_color_map *, gx_color_value,
+ gx_color_value, gx_color_value));
+
+#endif /* gdev8bcm_INCLUDED */
diff --git a/pstoraster/gdevabuf.c b/pstoraster/gdevabuf.c
new file mode 100644
index 000000000..991584c04
--- /dev/null
+++ b/pstoraster/gdevabuf.c
@@ -0,0 +1,404 @@
+/* Copyright (C) 1994, 1995, 1996, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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_device_memory * const mdev = (gx_device_memory *)dev;
+ 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_device_memory * const mdev = (gx_device_memory *)dev;
+ 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)
+{
+ gx_device_memory * const mdev = (gx_device_memory *)dev;
+
+ 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);
+private dev_proc_get_clipping_box(mem_abuf_get_clipping_box);
+
+/* The device descriptor. */
+private const gx_device_memory 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);
+ set_dev_proc(adev, get_clipping_box, mem_abuf_get_clipping_box);
+}
+
+/* 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)
+{
+ gx_device_memory * const mdev = (gx_device_memory *)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)
+{
+ gx_device_memory * const mdev = (gx_device_memory *)dev;
+ 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)
+{
+ gx_device_memory * const mdev = (gx_device_memory *)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)
+{
+ gx_device_memory * const mdev = (gx_device_memory *)dev;
+ 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_xyw(dev, base, sourcex, sraster, id, x, y, w, h); /* don't limit h */
+ if (w <= 0 || h <= 0)
+ return 0;
+ 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)
+{
+ gx_device_memory * const mdev = (gx_device_memory *)dev;
+ y_transfer yt;
+
+ x -= mdev->mapped_x;
+ fit_fill_xy(dev, x, y, w, h);
+ fit_fill_w(dev, x, w); /* 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;
+}
+
+/* Get the clipping box. We must scale this up by the number of alpha bits. */
+private void
+mem_abuf_get_clipping_box(gx_device * dev, gs_fixed_rect * pbox)
+{
+ gx_device_memory * const mdev = (gx_device_memory *)dev;
+ gx_device *tdev = mdev->target;
+
+ (*dev_proc(tdev, get_clipping_box)) (tdev, pbox);
+ pbox->p.x <<= mdev->log2_scale.x;
+ pbox->p.y <<= mdev->log2_scale.y;
+ pbox->q.x <<= mdev->log2_scale.x;
+ pbox->q.y <<= mdev->log2_scale.y;
+}
diff --git a/pstoraster/gdevbbox.c b/pstoraster/gdevbbox.c
new file mode 100644
index 000000000..5641b3675
--- /dev/null
+++ b/pstoraster/gdevbbox.c
@@ -0,0 +1,1069 @@
+/* Copyright (C) 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Device for tracking bounding box */
+#include "math_.h"
+#include "memory_.h"
+#include "gx.h"
+#include "gserrors.h"
+#include "gsparam.h"
+#include "gxdevice.h"
+#include "gsdevice.h" /* requires gsmatrix.h */
+#include "gdevbbox.h"
+#include "gxdcolor.h" /* for gx_device_black/white */
+#include "gxiparam.h" /* for image source size */
+#include "gxistate.h"
+#include "gxpaint.h"
+#include "gxpath.h"
+#include "gxcpath.h"
+
+/* GC descriptor */
+public_st_device_bbox();
+
+/* Device procedures */
+private dev_proc_open_device(bbox_open_device);
+private dev_proc_close_device(bbox_close_device);
+private dev_proc_output_page(bbox_output_page);
+private dev_proc_fill_rectangle(bbox_fill_rectangle);
+private dev_proc_copy_mono(bbox_copy_mono);
+private dev_proc_copy_color(bbox_copy_color);
+private dev_proc_get_params(bbox_get_params);
+private dev_proc_put_params(bbox_put_params);
+private dev_proc_copy_alpha(bbox_copy_alpha);
+private dev_proc_fill_path(bbox_fill_path);
+private dev_proc_stroke_path(bbox_stroke_path);
+private dev_proc_fill_mask(bbox_fill_mask);
+private dev_proc_fill_trapezoid(bbox_fill_trapezoid);
+private dev_proc_fill_parallelogram(bbox_fill_parallelogram);
+private dev_proc_fill_triangle(bbox_fill_triangle);
+private dev_proc_draw_thin_line(bbox_draw_thin_line);
+private dev_proc_strip_tile_rectangle(bbox_strip_tile_rectangle);
+private dev_proc_strip_copy_rop(bbox_strip_copy_rop);
+private dev_proc_begin_typed_image(bbox_begin_typed_image);
+private dev_proc_create_compositor(bbox_create_compositor);
+private dev_proc_text_begin(bbox_text_begin);
+
+/* The device prototype */
+/*
+ * Normally this would be private, but if the device is going to be used
+ * stand-alone, it has to be public.
+ */
+/*private */ const
+/*
+ * The bbox device sets the resolution to some value R (currently 4000), and
+ * the page size in device pixels to slightly smaller than the largest
+ * representable values (around 500K), leaving a little room for stroke
+ * widths, rounding, etc. If an input file (or the command line) resets the
+ * resolution to a value R' > R, the page size in pixels will get multiplied
+ * by R'/R, and will thereby exceed the representable range, causing a
+ * limitcheck. That is why the bbox device must set the resolution to a
+ * value larger than that of any real device. A consequence of this is that
+ * the page size in inches is limited to the maximum representable pixel
+ * size divided by R, which gives a limit of about 120" in each dimension.
+ */
+#define max_coord (max_int_in_fixed - 1000)
+#define max_resolution 4000
+gx_device_bbox far_data gs_bbox_device =
+{
+ std_device_std_body(gx_device_bbox, 0, "bbox",
+ max_coord, max_coord,
+ max_resolution, max_resolution),
+ {bbox_open_device,
+ NULL, /* get_initial_matrix */
+ NULL, /* sync_output */
+ bbox_output_page,
+ bbox_close_device,
+ NULL, /* map_rgb_color */
+ NULL, /* map_color_rgb */
+ bbox_fill_rectangle,
+ NULL, /* tile_rectangle */
+ bbox_copy_mono,
+ bbox_copy_color,
+ NULL, /* draw_line */
+ NULL, /* get_bits */
+ bbox_get_params,
+ bbox_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 */
+ bbox_copy_alpha,
+ NULL, /* get_band */
+ NULL, /* copy_rop */
+ bbox_fill_path,
+ bbox_stroke_path,
+ bbox_fill_mask,
+ bbox_fill_trapezoid,
+ bbox_fill_parallelogram,
+ bbox_fill_triangle,
+ bbox_draw_thin_line,
+ gx_default_begin_image,
+ NULL, /* image_data */
+ NULL, /* end_image */
+ bbox_strip_tile_rectangle,
+ bbox_strip_copy_rop,
+ NULL, /* get_clipping_box */
+ bbox_begin_typed_image,
+ NULL, /* get_bits_rectangle */
+ NULL, /* map_color_rgb_alpha */
+ bbox_create_compositor,
+ NULL, /* get_hardware_params */
+ bbox_text_begin
+ },
+ 0, /* target */
+ 1 /*true *//* free_standing */
+};
+
+#undef max_coord
+#undef max_resolution
+
+/* Copy device parameters back from the target. */
+private void
+bbox_copy_params(gx_device_bbox * bdev, bool remap_white)
+{
+ gx_device *tdev = bdev->target;
+
+ if (tdev != 0)
+ gx_device_copy_params((gx_device *)bdev, tdev);
+ if (remap_white)
+ bdev->white = gx_device_white((gx_device *)bdev);
+}
+
+#define gx_dc_is_white(pdevc, bdev)\
+ (gx_dc_is_pure(pdevc) && gx_dc_pure_color(pdevc) == (bdev)->white)
+
+private int
+bbox_close_device(gx_device * dev)
+{
+ gx_device_bbox *const bdev = (gx_device_bbox *) dev;
+ gx_device *tdev = bdev->target;
+
+ if ((gx_device *) bdev->box_device != dev) {
+ /*
+ * This device was created as a wrapper for a compositor.
+ * Just free the devices.
+ */
+ int code = (*dev_proc(tdev, close_device)) (tdev);
+
+ gs_free_object(dev->memory, dev, "bbox_close_device(composite)");
+ return code;
+ } else {
+ return (tdev == 0 ? 0 : (*dev_proc(tdev, close_device)) (tdev));
+ }
+}
+
+/* Bounding box utilities */
+
+private void near
+bbox_initialize(gs_fixed_rect * pr)
+{
+ pr->p.x = pr->p.y = max_fixed;
+ pr->q.x = pr->q.y = min_fixed;
+}
+
+private void near
+bbox_add_rect(gs_fixed_rect * pr, fixed x0, fixed y0, fixed x1, fixed y1)
+{
+ if (x0 < pr->p.x)
+ pr->p.x = x0;
+ if (y0 < pr->p.y)
+ pr->p.y = y0;
+ if (x1 > pr->q.x)
+ pr->q.x = x1;
+ if (y1 > pr->q.y)
+ pr->q.y = y1;
+}
+private void near
+bbox_add_point(gs_fixed_rect * pr, fixed x, fixed y)
+{
+ bbox_add_rect(pr, x, y, x, y);
+}
+private void near
+bbox_add_int_rect(gs_fixed_rect * pr, int x0, int y0, int x1, int y1)
+{
+ bbox_add_rect(pr, int2fixed(x0), int2fixed(y0), int2fixed(x1),
+ int2fixed(y1));
+}
+
+#define rect_is_page(dev, x, y, w, h)\
+ (x <= 0 && y <= 0 && w >= x + dev->width && h >= y + dev->height)
+
+ /* ---------------- Open/close/page ---------------- */
+
+/* Initialize a bounding box device. */
+void
+gx_device_bbox_init(gx_device_bbox * dev, gx_device * target)
+{
+ gx_device_init((gx_device *) dev, (const gx_device *)&gs_bbox_device,
+ (target ? target->memory : NULL), true);
+ gx_device_forward_fill_in_procs((gx_device_forward *) dev);
+ dev->target = target;
+ dev->box_device = dev;
+ bbox_copy_params(dev, false);
+ dev->free_standing = false; /* being used as a component */
+}
+
+/* Read back the bounding box in 1/72" units. */
+void
+gx_device_bbox_bbox(gx_device_bbox * dev, gs_rect * pbbox)
+{
+ const gx_device_bbox *bbdev = dev->box_device;
+ gs_matrix mat;
+ gs_rect dbox;
+
+ gs_deviceinitialmatrix((gx_device *) dev, &mat);
+ dbox.p.x = fixed2float(bbdev->bbox.p.x);
+ dbox.p.y = fixed2float(bbdev->bbox.p.y);
+ dbox.q.x = fixed2float(bbdev->bbox.q.x);
+ dbox.q.y = fixed2float(bbdev->bbox.q.y);
+ gs_bbox_transform_inverse(&dbox, &mat, pbbox);
+}
+
+
+private int
+bbox_open_device(gx_device * dev)
+{
+ gx_device_bbox *const bdev = (gx_device_bbox *) dev;
+
+ if (bdev->free_standing) {
+ gx_device_forward_fill_in_procs((gx_device_forward *) dev);
+ bdev->box_device = bdev;
+ }
+ if (bdev->box_device == bdev)
+ bbox_initialize(&bdev->bbox);
+ /* gx_forward_open_device doesn't exist */
+ {
+ gx_device *tdev = bdev->target;
+ int code = (tdev == 0 ? 0 : (*dev_proc(tdev, open_device)) (tdev));
+
+ bbox_copy_params(bdev, true);
+ return code;
+ }
+}
+
+private int
+bbox_output_page(gx_device * dev, int num_copies, int flush)
+{
+ gx_device_bbox *const bdev = (gx_device_bbox *) dev;
+
+ if (bdev->free_standing) {
+ /*
+ * This is a free-standing device. Print the page bounding box.
+ */
+ gs_rect bbox;
+
+ gx_device_bbox_bbox(bdev, &bbox);
+ dlprintf4("%%%%BoundingBox: %d %d %d %d\n",
+ (int)floor(bbox.p.x), (int)floor(bbox.p.y),
+ (int)ceil(bbox.q.x), (int)ceil(bbox.q.y));
+ dlprintf4("%%%%HiResBoundingBox: %f %f %f %f\n",
+ bbox.p.x, bbox.p.y, bbox.q.x, bbox.q.y);
+ }
+ /*
+ * Propagate the PageCount to the target,
+ * since it changes every time gs_output_page is called.
+ */
+ if (bdev->target)
+ bdev->target->PageCount = dev->PageCount;
+ return gx_forward_output_page(dev, num_copies, flush);
+}
+
+/* ---------------- Low-level drawing ---------------- */
+
+private int
+bbox_fill_rectangle(gx_device * dev, int x, int y, int w, int h,
+ gx_color_index color)
+{
+ gx_device_bbox *const bdev = (gx_device_bbox *) dev;
+ gx_device_bbox *bbdev = bdev->box_device;
+
+ /* Check for erasing the entire page. */
+ if (rect_is_page(dev, x, y, w, h))
+ bbox_initialize(&bbdev->bbox);
+ else if (color != bdev->white)
+ bbox_add_int_rect(&bbdev->bbox, x, y, x + w, y + h);
+ /* gx_forward_fill_rectangle doesn't exist */
+ {
+ gx_device *tdev = bdev->target;
+
+ return (tdev == 0 ? 0 :
+ (*dev_proc(tdev, fill_rectangle)) (tdev, x, y, w, h, color));
+ }
+}
+
+private int
+bbox_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)
+{
+ gx_device_bbox *const bdev = (gx_device_bbox *) dev;
+ gx_device_bbox *bbdev = bdev->box_device;
+
+ if ((one != gx_no_color_index && one != bdev->white) ||
+ (zero != gx_no_color_index && zero != bdev->white)
+ )
+ bbox_add_int_rect(&bbdev->bbox, x, y, x + w, y + h);
+ /* gx_forward_copy_mono doesn't exist */
+ {
+ gx_device *tdev = bdev->target;
+
+ return (tdev == 0 ? 0 :
+ (*dev_proc(tdev, copy_mono))
+ (tdev, data, dx, raster, id, x, y, w, h, zero, one));
+ }
+}
+
+private int
+bbox_copy_color(gx_device * dev, const byte * data,
+ int dx, int raster, gx_bitmap_id id, int x, int y, int w, int h)
+{
+ gx_device_bbox *const bdev = (gx_device_bbox *) dev;
+ gx_device_bbox *bbdev = bdev->box_device;
+
+ bbox_add_int_rect(&bbdev->bbox, x, y, x + w, y + h);
+ /* gx_forward_copy_color doesn't exist */
+ {
+ gx_device *tdev = bdev->target;
+
+ return (tdev == 0 ? 0 :
+ (*dev_proc(tdev, copy_color))
+ (tdev, data, dx, raster, id, x, y, w, h));
+ }
+}
+
+private int
+bbox_copy_alpha(gx_device * dev, const byte * data, int data_x,
+ int raster, gx_bitmap_id id, int x, int y, int w, int h,
+ gx_color_index color, int depth)
+{
+ gx_device_bbox *const bdev = (gx_device_bbox *) dev;
+ gx_device_bbox *bbdev = bdev->box_device;
+
+ bbox_add_int_rect(&bbdev->bbox, x, y, x + w, y + h);
+ /* gx_forward_copy_alpha doesn't exist */
+ {
+ gx_device *tdev = bdev->target;
+
+ return (tdev == 0 ? 0 :
+ (*dev_proc(tdev, copy_alpha))
+ (tdev, data, data_x, raster, id, x, y, w, h, color, depth));
+ }
+}
+
+private int
+bbox_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_bbox *const bdev = (gx_device_bbox *) dev;
+ gx_device_bbox *bbdev = bdev->box_device;
+
+ if (rect_is_page(dev, x, y, w, h))
+ bbox_initialize(&bbdev->bbox);
+ else
+ bbox_add_int_rect(&bbdev->bbox, x, y, x + w, y + h);
+ /* Skip the call if there is no target. */
+ {
+ gx_device *tdev = bdev->target;
+
+ return (tdev == 0 ? 0 :
+ (*dev_proc(tdev, strip_tile_rectangle))
+ (tdev, tiles, x, y, w, h, color0, color1, px, py));
+ }
+}
+
+private int
+bbox_strip_copy_rop(gx_device * dev,
+ const byte * sdata, int sourcex, uint sraster,
+ gx_bitmap_id id,
+ const gx_color_index * scolors,
+ const gx_strip_bitmap * textures,
+ const gx_color_index * tcolors,
+ int x, int y, int w, int h,
+ int phase_x, int phase_y, gs_logical_operation_t lop)
+{
+ gx_device_bbox *const bdev = (gx_device_bbox *) dev;
+ gx_device_bbox *bbdev = bdev->box_device;
+
+ bbox_add_int_rect(&bbdev->bbox, x, y, x + w, y + h);
+ /* gx_forward_strip_copy_rop doesn't exist */
+ {
+ gx_device *tdev = bdev->target;
+
+ return (tdev == 0 ? 0 :
+ (*dev_proc(tdev, strip_copy_rop))
+ (tdev, sdata, sourcex, sraster, id, scolors,
+ textures, tcolors, x, y, w, h, phase_x, phase_y, lop));
+ }
+}
+
+/* ---------------- Parameters ---------------- */
+
+/* We implement get_params to provide a way to read out the bounding box. */
+private int
+bbox_get_params(gx_device * dev, gs_param_list * plist)
+{
+ gx_device_bbox *const bdev = (gx_device_bbox *) dev;
+ const gx_device_bbox *bbdev = bdev->box_device;
+ int code = gx_forward_get_params(dev, plist);
+ gs_param_float_array bba;
+ float bbox[4];
+
+ if (code < 0)
+ return code;
+ /*
+ * We might be calling get_params before the device has been
+ * initialized: in this case, bbdev = 0.
+ */
+ if (bbdev == 0)
+ bbdev = (const gx_device_bbox *)dev;
+ bbox[0] = fixed2float(bbdev->bbox.p.x);
+ bbox[1] = fixed2float(bbdev->bbox.p.y);
+ bbox[2] = fixed2float(bbdev->bbox.q.x);
+ bbox[3] = fixed2float(bbdev->bbox.q.y);
+ bba.data = bbox, bba.size = 4, bba.persistent = false;
+ return param_write_float_array(plist, "PageBoundingBox", &bba);
+}
+
+/* We implement put_params to ensure that we keep the important */
+/* device parameters up to date, and to prevent an /undefined error */
+/* from PageBoundingBox. */
+private int
+bbox_put_params(gx_device * dev, gs_param_list * plist)
+{
+ gx_device_bbox *const bdev = (gx_device_bbox *) dev;
+ int code;
+ int ecode = 0;
+ gs_param_name param_name;
+ gs_param_float_array bba;
+
+ code = param_read_float_array(plist, (param_name = "PageBoundingBox"),
+ &bba);
+ switch (code) {
+ case 0:
+ if (bba.size != 4) {
+ ecode = gs_note_error(gs_error_rangecheck);
+ goto e;
+ }
+ break;
+ default:
+ ecode = code;
+ e:param_signal_error(plist, param_name, ecode);
+ case 1:
+ bba.data = 0;
+ }
+
+ code = gx_forward_put_params(dev, plist);
+ if (ecode < 0)
+ code = ecode;
+ if (code >= 0 && bba.data != 0) {
+ gx_device_bbox *bbdev = bdev->box_device;
+
+ bbdev->bbox.p.x = float2fixed(bba.data[0]);
+ bbdev->bbox.p.y = float2fixed(bba.data[1]);
+ bbdev->bbox.q.x = float2fixed(bba.data[2]);
+ bbdev->bbox.q.y = float2fixed(bba.data[3]);
+ }
+ bbox_copy_params(bdev, true);
+ return code;
+}
+
+/* ---------------- Polygon drawing ---------------- */
+
+private fixed
+edge_x_at_y(const gs_fixed_edge * edge, fixed y)
+{
+ return fixed_mult_quo(edge->end.x - edge->start.x,
+ y - edge->start.y,
+ edge->end.y - edge->start.y) + edge->start.x;
+}
+private int
+bbox_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)
+{
+ gx_device_bbox *const bdev = (gx_device_bbox *) dev;
+
+ if (!gx_dc_is_white(pdevc, bdev)) {
+ gx_device_bbox *bbdev = bdev->box_device;
+ fixed x0l =
+ (left->start.y == ybot ? left->start.x :
+ edge_x_at_y(left, ybot));
+ fixed x1l =
+ (left->end.y == ytop ? left->end.x :
+ edge_x_at_y(left, ytop));
+ fixed x0r =
+ (right->start.y == ybot ? right->start.x :
+ edge_x_at_y(right, ybot));
+ fixed x1r =
+ (right->end.y == ytop ? right->end.x :
+ edge_x_at_y(right, ytop));
+ fixed xminl = min(x0l, x1l), xmaxl = max(x0l, x1l);
+ fixed xminr = min(x0r, x1r), xmaxr = max(x0r, x1r);
+ fixed x0 = min(xminl, xminr), x1 = max(xmaxl, xmaxr);
+
+ if (swap_axes)
+ bbox_add_rect(&bbdev->bbox, ybot, x0, ytop, x1);
+ else
+ bbox_add_rect(&bbdev->bbox, x0, ybot, x1, ytop);
+ }
+ /* Skip the call if there is no target. */
+ {
+ gx_device *tdev = bdev->target;
+
+ return (tdev == 0 ? 0 :
+ (*dev_proc(tdev, fill_trapezoid))
+ (tdev, left, right, ybot, ytop, swap_axes, pdevc, lop));
+ }
+}
+
+private int
+bbox_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)
+{
+ gx_device_bbox *const bdev = (gx_device_bbox *) dev;
+
+ if (!gx_dc_is_white(pdevc, bdev)) {
+ gx_device_bbox *bbdev = bdev->box_device;
+ fixed pax = px + ax, pay = py + ay;
+
+ bbox_add_rect(&bbdev->bbox, px, py, px + bx, py + by);
+ bbox_add_rect(&bbdev->bbox, pax, pay, pax + bx, pay + by);
+ }
+ /* Skip the call if there is no target. */
+ {
+ gx_device *tdev = bdev->target;
+
+ return (tdev == 0 ? 0 :
+ (*dev_proc(tdev, fill_parallelogram))
+ (tdev, px, py, ax, ay, bx, by, pdevc, lop));
+ }
+}
+
+private int
+bbox_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)
+{
+ gx_device_bbox *const bdev = (gx_device_bbox *) dev;
+
+ if (!gx_dc_is_white(pdevc, bdev)) {
+ gx_device_bbox *bbdev = bdev->box_device;
+
+ bbox_add_rect(&bbdev->bbox, px, py, px + bx, py + by);
+ bbox_add_point(&bbdev->bbox, px + ax, py + ay);
+ }
+ /* Skip the call if there is no target. */
+ {
+ gx_device *tdev = bdev->target;
+
+ return (tdev == 0 ? 0 :
+ (*dev_proc(tdev, fill_triangle))
+ (tdev, px, py, ax, ay, bx, by, pdevc, lop));
+ }
+}
+
+private int
+bbox_draw_thin_line(gx_device * dev,
+ fixed fx0, fixed fy0, fixed fx1, fixed fy1,
+ const gx_device_color * pdevc, gs_logical_operation_t lop)
+{
+ gx_device_bbox *const bdev = (gx_device_bbox *) dev;
+
+ if (!gx_dc_is_white(pdevc, bdev)) {
+ gx_device_bbox *bbdev = bdev->box_device;
+
+ bbox_add_rect(&bbdev->bbox, fx0, fy0, fx1, fy1);
+ }
+ /* Skip the call if there is no target. */
+ {
+ gx_device *tdev = bdev->target;
+
+ return (tdev == 0 ? 0 :
+ (*dev_proc(tdev, draw_thin_line))
+ (tdev, fx0, fy0, fx1, fy0, pdevc, lop));
+ }
+}
+
+/* ---------------- High-level drawing ---------------- */
+
+#define adjust_box(pbox, adj)\
+((pbox)->p.x -= (adj).x, (pbox)->p.y -= (adj).y,\
+ (pbox)->q.x += (adj).x, (pbox)->q.y += (adj).y)
+
+private int
+bbox_fill_path(gx_device * dev, const gs_imager_state * pis, gx_path * ppath,
+ const gx_fill_params * params, const gx_device_color * pdevc,
+ const gx_clip_path * pcpath)
+{
+ gx_device_bbox *const bdev = (gx_device_bbox *) dev;
+ gx_device *tdev = bdev->target;
+
+ if (!gx_dc_is_white(pdevc, bdev)) {
+ gs_fixed_rect ibox;
+ gs_fixed_point adjust;
+
+ if (gx_path_bbox(ppath, &ibox) < 0)
+ return 0;
+ adjust = params->adjust;
+ if (params->fill_zero_width)
+ gx_adjust_if_empty(&ibox, &adjust);
+ adjust_box(&ibox, adjust);
+ if (pcpath != NULL &&
+ !gx_cpath_includes_rectangle(pcpath, ibox.p.x, ibox.p.y,
+ ibox.q.x, ibox.q.y)
+ ) {
+ /* Let the target do the drawing, but break down the */
+ /* fill path into pieces for computing the bounding box. */
+ bdev->target = NULL;
+ gx_default_fill_path(dev, pis, ppath, params, pdevc, pcpath);
+ bdev->target = tdev;
+ } else { /* Just use the path bounding box. */
+ bbox_add_rect(&bdev->bbox, ibox.p.x, ibox.p.y, ibox.q.x,
+ ibox.q.y);
+ }
+ }
+ /* Skip the call if there is no target. */
+ return (tdev == 0 ? 0 :
+ (*dev_proc(tdev, fill_path))
+ (tdev, pis, ppath, params, pdevc, pcpath));
+}
+
+private int
+bbox_stroke_path(gx_device * dev, const gs_imager_state * pis, gx_path * ppath,
+ const gx_stroke_params * params,
+ const gx_drawing_color * pdevc, const gx_clip_path * pcpath)
+{
+ gx_device_bbox *const bdev = (gx_device_bbox *) dev;
+ gx_device *tdev = bdev->target;
+
+ if (!gx_dc_is_white(pdevc, bdev)) {
+ gs_fixed_rect ibox;
+ gs_fixed_point expand;
+
+ if (gx_path_bbox(ppath, &ibox) < 0)
+ return 0;
+ if (gx_stroke_path_expansion(pis, ppath, &expand) < 0)
+ ibox.p.x = ibox.p.y = min_fixed, ibox.q.x = ibox.q.y = max_fixed;
+ else
+ adjust_box(&ibox, expand);
+ if (pcpath != NULL &&
+ !gx_cpath_includes_rectangle(pcpath, ibox.p.x, ibox.p.y,
+ ibox.q.x, ibox.q.y)
+ ) {
+ /* Let the target do the drawing, but break down the */
+ /* fill path into pieces for computing the bounding box. */
+ bdev->target = NULL;
+ gx_default_stroke_path(dev, pis, ppath, params, pdevc, pcpath);
+ bdev->target = tdev;
+ } else {
+ /* Just use the path bounding box. */
+ gx_device_bbox *bbdev = bdev->box_device;
+
+ bbox_add_rect(&bbdev->bbox, ibox.p.x, ibox.p.y, ibox.q.x,
+ ibox.q.y);
+ }
+ }
+ /* Skip the call if there is no target. */
+ return (tdev == 0 ? 0 :
+ (*dev_proc(tdev, stroke_path))
+ (tdev, pis, ppath, params, pdevc, pcpath));
+}
+
+private int
+bbox_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_bbox *const bdev = (gx_device_bbox *) dev;
+ gx_device *tdev = bdev->target;
+
+ if (pcpath != NULL &&
+ !gx_cpath_includes_rectangle(pcpath, int2fixed(x), int2fixed(y),
+ int2fixed(x + w),
+ int2fixed(y + h))
+ ) {
+ /* Let the target do the drawing, but break down the */
+ /* image into pieces for computing the bounding box. */
+ bdev->target = NULL;
+ gx_default_fill_mask(dev, data, dx, raster, id, x, y, w, h,
+ pdcolor, depth, lop, pcpath);
+ bdev->target = tdev;
+ } else {
+ /* Just use the mask bounding box. */
+ gx_device_bbox *bbdev = bdev->box_device;
+
+ bbox_add_int_rect(&bbdev->bbox, x, y, x + w, y + h);
+ }
+ /* Skip the call if there is no target. */
+ return (tdev == 0 ? 0 :
+ (*dev_proc(tdev, fill_mask))
+ (tdev, data, dx, raster, id, x, y, w, h,
+ pdcolor, depth, lop, pcpath));
+}
+
+/* ------ Bitmap imaging ------ */
+
+typedef struct bbox_image_enum_s {
+ gx_image_enum_common;
+ gs_memory_t *memory;
+ gs_matrix matrix; /* map from image space to device space */
+ const gx_clip_path *pcpath;
+ gx_image_enum_common_t *target_info;
+ int x0, x1;
+ int y, height;
+} bbox_image_enum;
+
+gs_private_st_ptrs2(st_bbox_image_enum, bbox_image_enum, "bbox_image_enum",
+bbox_image_enum_enum_ptrs, bbox_image_enum_reloc_ptrs, pcpath, target_info);
+
+private image_enum_proc_plane_data(bbox_image_plane_data);
+private image_enum_proc_end_image(bbox_image_end_image);
+private const gx_image_enum_procs_t bbox_image_enum_procs =
+{
+ bbox_image_plane_data, bbox_image_end_image
+};
+
+private int
+bbox_image_begin(const gs_imager_state * pis, const gs_matrix * pmat,
+ const gs_image_common_t * pic, const gs_int_rect * prect,
+ const gx_clip_path * pcpath, gs_memory_t * memory,
+ bbox_image_enum ** ppbe)
+{
+ int code;
+ gs_matrix mat;
+ bbox_image_enum *pbe;
+
+ if (pmat == 0)
+ pmat = &ctm_only(pis);
+ if ((code = gs_matrix_invert(&pic->ImageMatrix, &mat)) < 0 ||
+ (code = gs_matrix_multiply(&mat, pmat, &mat)) < 0
+ )
+ return code;
+ pbe = gs_alloc_struct(memory, bbox_image_enum, &st_bbox_image_enum,
+ "bbox_image_begin");
+ if (pbe == 0)
+ return_error(gs_error_VMerror);
+ pbe->memory = memory;
+ pbe->matrix = mat;
+ pbe->pcpath = pcpath;
+ pbe->target_info = 0; /* in case no target */
+ if (prect) {
+ pbe->x0 = prect->p.x, pbe->x1 = prect->q.x;
+ pbe->y = prect->p.y, pbe->height = prect->q.y - prect->p.y;
+ } else {
+ gs_int_point size;
+ int code = (*pic->type->source_size) (pis, pic, &size);
+
+ if (code < 0) {
+ gs_free_object(memory, pbe, "bbox_image_begin");
+ return code;
+ }
+ pbe->x0 = 0, pbe->x1 = size.x;
+ pbe->y = 0, pbe->height = size.y;
+ }
+ *ppbe = pbe;
+ return 0;
+}
+
+private void
+bbox_image_copy_target_info(bbox_image_enum * pbe, gx_device_bbox * dev)
+{
+ const gx_image_enum_common_t *target_info = pbe->target_info;
+
+ pbe->num_planes = target_info->num_planes;
+ memcpy(pbe->plane_depths, target_info->plane_depths,
+ pbe->num_planes * sizeof(pbe->plane_depths[0]));
+ if (dev->target == 0) {
+ gx_image_end(pbe->target_info, false);
+ pbe->target_info = 0;
+ }
+}
+
+private int
+bbox_begin_typed_image(gx_device * dev,
+ const gs_imager_state * pis, const gs_matrix * pmat,
+ const gs_image_common_t * pic, const gs_int_rect * prect,
+ const gx_drawing_color * pdcolor,
+ const gx_clip_path * pcpath,
+ gs_memory_t * memory, gx_image_enum_common_t ** pinfo)
+{
+ bbox_image_enum *pbe;
+ int code =
+ bbox_image_begin(pis, pmat, pic, prect, pcpath, memory, &pbe);
+
+ if (code < 0)
+ return code;
+ /* We fill in num_planes and plane_depths later. */
+ /* format is irrelevant. */
+ code = gx_image_enum_common_init((gx_image_enum_common_t *) pbe, pic,
+ &bbox_image_enum_procs, dev,
+ 0, 0, gs_image_format_chunky);
+ if (code < 0)
+ return code;
+ *pinfo = (gx_image_enum_common_t *) pbe;
+ /*
+ * If there is no target, we still have to call default_begin_image
+ * to get the correct num_planes and plane_depths.
+ */
+ {
+ gx_device_bbox *const bdev = (gx_device_bbox *) dev;
+ gx_device *tdev = bdev->target;
+
+ dev_proc_begin_typed_image((*begin_typed_image));
+
+ if (tdev == 0) {
+ tdev = dev;
+ begin_typed_image = gx_default_begin_typed_image;
+ } else {
+ begin_typed_image = dev_proc(tdev, begin_typed_image);
+ }
+ code = (*begin_typed_image)
+ (tdev, pis, pmat, pic, prect, pdcolor, pcpath, memory,
+ &pbe->target_info);
+ if (code < 0)
+ return code;
+ bbox_image_copy_target_info(pbe, bdev);
+ }
+ return 0;
+}
+
+private int
+bbox_image_plane_data(gx_device * dev,
+ gx_image_enum_common_t * info, const gx_image_plane_t * planes, int height)
+{
+
+ gx_device_bbox *const bdev = (gx_device_bbox *) dev;
+ gx_device *tdev = bdev->target;
+ bbox_image_enum *pbe = (bbox_image_enum *) info;
+ const gx_clip_path *pcpath = pbe->pcpath;
+ gs_rect sbox, dbox;
+ gs_point corners[4];
+ gs_fixed_rect ibox;
+
+ sbox.p.x = pbe->x0;
+ sbox.p.y = pbe->y;
+ sbox.q.x = pbe->x1;
+ sbox.q.y = pbe->y += height;
+ gs_bbox_transform_only(&sbox, &pbe->matrix, corners);
+ gs_points_bbox(corners, &dbox);
+ ibox.p.x = float2fixed(dbox.p.x);
+ ibox.p.y = float2fixed(dbox.p.y);
+ ibox.q.x = float2fixed(dbox.q.x);
+ ibox.q.y = float2fixed(dbox.q.y);
+ if (pcpath != NULL &&
+ !gx_cpath_includes_rectangle(pcpath, ibox.p.x, ibox.p.y,
+ ibox.q.x, ibox.q.y)
+ ) {
+ /* Let the target do the drawing, but drive two triangles */
+ /* through the clipping path to get an accurate bounding box. */
+ gx_device_clip cdev;
+ gx_drawing_color devc;
+ fixed x0 = float2fixed(corners[0].x), y0 = float2fixed(corners[0].y);
+ fixed bx2 = float2fixed(corners[2].x) - x0, by2 = float2fixed(corners[2].y) - y0;
+
+ gx_make_clip_path_device(&cdev, pcpath);
+ cdev.target = dev;
+ (*dev_proc(&cdev, open_device)) ((gx_device *) & cdev);
+ color_set_pure(&devc, 0); /* any color will do */
+ bdev->target = NULL;
+ gx_default_fill_triangle((gx_device *) & cdev, x0, y0,
+ float2fixed(corners[1].x) - x0,
+ float2fixed(corners[1].y) - y0,
+ bx2, by2, &devc, lop_default);
+ gx_default_fill_triangle((gx_device *) & cdev, x0, y0,
+ float2fixed(corners[3].x) - x0,
+ float2fixed(corners[3].y) - y0,
+ bx2, by2, &devc, lop_default);
+ bdev->target = tdev;
+ } else {
+ /* Just use the bounding box. */
+ gx_device_bbox *bbdev = bdev->box_device;
+
+ bbox_add_rect(&bbdev->bbox, ibox.p.x, ibox.p.y, ibox.q.x, ibox.q.y);
+ }
+ /* Skip the call if there is no target. */
+ return (tdev == 0 ? pbe->y >= pbe->height :
+ gx_image_plane_data(pbe->target_info, planes, height));
+}
+
+private int
+bbox_image_end_image(gx_device * dev, gx_image_enum_common_t * info,
+ bool draw_last)
+{
+ gx_device_bbox *const bdev = (gx_device_bbox *) dev;
+ bbox_image_enum *pbe = (bbox_image_enum *) info;
+ void *target_info = pbe->target_info;
+
+ /* Skip the call if there is no target. */
+ gx_device *tdev = bdev->target;
+ int code =
+ (tdev == 0 ? 0 : gx_image_end(target_info, draw_last));
+
+ gs_free_object(pbe->memory, pbe, "bbox_end_image");
+ return code;
+}
+
+private int
+bbox_create_compositor(gx_device * dev,
+ gx_device ** pcdev, const gs_composite_t * pcte,
+ const gs_imager_state * pis, gs_memory_t * memory)
+{
+ gx_device_bbox *const bdev = (gx_device_bbox *) dev;
+ gx_device *target = bdev->target;
+
+ /*
+ * If there isn't a target, all we care about is the bounding box,
+ * so don't bother with actually compositing.
+ */
+ if (target == 0) {
+ *pcdev = dev;
+ return 0;
+ }
+ /*
+ * Create a compositor for the target, and then wrap another
+ * bbox device around it, but still accumulating the bounding
+ * box in the same place.
+ */
+ {
+ gx_device *cdev;
+ gx_device_bbox *bbcdev;
+ int code = (*dev_proc(target, create_compositor))
+ (target, &cdev, pcte, pis, memory);
+
+ if (code < 0)
+ return code;
+ bbcdev = gs_alloc_struct_immovable(memory, gx_device_bbox,
+ &st_device_bbox,
+ "bbox_create_compositor");
+ if (bbcdev == 0) {
+ (*dev_proc(cdev, close_device)) (cdev);
+ return_error(gs_error_VMerror);
+ }
+ gx_device_bbox_init(bbcdev, target);
+ bbcdev->target = cdev;
+ bbcdev->box_device = bdev;
+ *pcdev = (gx_device *) bbcdev;
+ return 0;
+ }
+}
+
+/* ------ Text imaging ------ */
+
+extern_st(st_gs_text_enum);
+
+typedef struct bbox_text_enum_s {
+ gs_text_enum_common;
+ gs_text_enum_t *target_info;
+} bbox_text_enum;
+
+gs_private_st_suffix_add1(st_bbox_text_enum, bbox_text_enum, "bbox_text_enum",
+ bbox_text_enum_enum_ptrs, bbox_text_enum_reloc_ptrs,
+ st_gs_text_enum, target_info);
+
+private text_enum_proc_process(bbox_text_process);
+private text_enum_proc_set_cache(bbox_text_set_cache);
+private rc_free_proc(bbox_text_free);
+
+private const gs_text_enum_procs_t bbox_text_procs =
+{
+ bbox_text_process, bbox_text_set_cache
+};
+
+private int
+bbox_text_begin(gx_device * dev, gs_imager_state * pis,
+ const gs_text_params_t * text, const gs_font * font,
+gx_path * path, const gx_device_color * pdcolor, const gx_clip_path * pcpath,
+ gs_memory_t * memory, gs_text_enum_t ** ppenum)
+{
+ gx_device_bbox *const bdev = (gx_device_bbox *) dev;
+ gx_device *tdev = bdev->target;
+ bbox_text_enum *pbte;
+ int code;
+
+ if (tdev == 0)
+ return gx_default_text_begin(dev, pis, text, font, path, pdcolor,
+ pcpath, memory, ppenum);
+ rc_alloc_struct_1(pbte, bbox_text_enum, &st_bbox_text_enum, memory,
+ return_error(gs_error_VMerror),
+ "bbox_text_begin");
+ pbte->rc.free = bbox_text_free;
+ code =
+ (*dev_proc(tdev, text_begin))
+ (tdev, pis, text, font, path, pdcolor, pcpath, memory,
+ &pbte->target_info);
+ if (code < 0) {
+ gs_free_object(memory, pbte, "bbox_text_begin");
+ return code;
+ }
+ *(gs_text_enum_t *) pbte = *pbte->target_info; /* copy common info */
+ pbte->procs = &bbox_text_procs;
+ *ppenum = (gs_text_enum_t *) pbte;
+ return code;
+}
+
+private int
+bbox_text_process(gs_text_enum_t * pte)
+{
+ bbox_text_enum *const pbte = (bbox_text_enum *) pte;
+ int code = gs_text_process(pbte->target_info);
+
+ if (code < 0)
+ return code;
+ /* Copy back the dynamic information for the client. */
+ pte->index = pbte->target_info->index;
+ return code;
+}
+
+private int
+bbox_text_set_cache(gs_text_enum_t * pte, const double *values,
+ gs_text_cache_control_t control)
+{
+ bbox_text_enum *const pbte = (bbox_text_enum *) pte;
+ gs_text_enum_t *tpte = pbte->target_info;
+ int code = tpte->procs->set_cache(tpte, values, control);
+
+ if (code < 0)
+ return code;
+ /* Copy back the dynamic information for the client. */
+ pte->index = tpte->index;
+ return code;
+}
+
+private void
+bbox_text_free(gs_memory_t * memory, void *vpte, client_name_t cname)
+{
+ bbox_text_enum *const pbte = (bbox_text_enum *) vpte;
+
+ gs_text_release(pbte->target_info, cname);
+ rc_free_struct_only(memory, vpte, cname);
+}
diff --git a/pstoraster/gdevbbox.h b/pstoraster/gdevbbox.h
new file mode 100644
index 000000000..5b647347b
--- /dev/null
+++ b/pstoraster/gdevbbox.h
@@ -0,0 +1,100 @@
+/* Copyright (C) 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Requires gxdevice.h */
+
+#ifndef gdevbbox_INCLUDED
+# define gdevbbox_INCLUDED
+
+/*
+ * This device keeps track of the per-page bounding box, and also optionally
+ * forwards all drawing commands to a target. It can be used either as a
+ * free-standing device or as a component (e.g., by the EPS writer).
+ *
+ * One way to use a bounding box device is simply to include bbox.dev in the
+ * value of DEVICE_DEVSn in the makefile. This produces a free-standing
+ * device named 'bbox' that can be selected in the usual way (-sDEVICE=bbox)
+ * and that prints out the bounding box at each showpage or copypage without
+ * doing any drawing.
+ *
+ * The other way to use a bounding box device is from C code as a component
+ * in a device pipeline. To set up a bounding box device that doesn't do
+ * any drawing:
+ * gx_device_bbox *bdev =
+ * gs_alloc_struct_immovable(some_memory,
+ * gx_device_bbox, &st_device_bbox,
+ * "some identifying string for debugging");
+ * gx_device_bbox_init(bdev, NULL);
+ * Non-drawing bounding box devices have an "infinite" page size.
+ *
+ * To set up a bounding box device that draws to another device tdev:
+ * gx_device_bbox *bdev =
+ * gs_alloc_struct_immovable(some_memory,
+ * gx_device_bbox, &st_device_bbox,
+ * "some identifying string for debugging");
+ * gx_device_bbox_init(bdev, tdev);
+ * Bounding box devices that draw to a real device appear to have the
+ * same page size as that device.
+ *
+ * To intercept the end-of-page to call a routine eop of your own, after
+ * setting up the device:
+ * dev_proc_output_page(eop); -- declare a prototype for eop
+ * ...
+ * set_dev_proc(bdev, output_page, eop);
+ * ...
+ * int eop(gx_device *dev, int num_copies, int flush)
+ * { gs_rect bbox;
+ * gx_device_bbox_bbox((gx_device_bbox *)dev, &bbox);
+ * << do whatever you want >>
+ * return gx_forward_output_page(dev, num_copies, flush);
+ * }
+ */
+#define gx_device_bbox_common\
+ gx_device_forward_common;\
+ bool free_standing;\
+ /* In order to handle compositors, we provide a separate pointer */\
+ /* to the bbox device instance that holds the actual box. */\
+ gx_device_bbox *box_device;\
+ /* The following are updated dynamically. */\
+ gs_fixed_rect bbox;\
+ gx_color_index white
+typedef struct gx_device_bbox_s gx_device_bbox;
+struct gx_device_bbox_s {
+ gx_device_bbox_common;
+};
+
+extern_st(st_device_bbox);
+#define public_st_device_bbox() /* in gdevbbox.c */\
+ gs_public_st_suffix_add1_final(st_device_bbox, gx_device_bbox,\
+ "gx_device_bbox", device_bbox_enum_ptrs, device_bbox_reloc_ptrs,\
+ gx_device_finalize, st_device_forward, box_device)
+
+/* Initialize a bounding box device. */
+void gx_device_bbox_init(P2(gx_device_bbox * dev, gx_device * target));
+
+/* Read back the bounding box in 1/72" units. */
+void gx_device_bbox_bbox(P2(gx_device_bbox * dev, gs_rect * pbbox));
+
+#endif /* gdevbbox_INCLUDED */
diff --git a/pstoraster/gdevcmap.h b/pstoraster/gdevcmap.h
new file mode 100644
index 000000000..abfa9266f
--- /dev/null
+++ b/pstoraster/gdevcmap.h
@@ -0,0 +1,74 @@
+/* Copyright (C) 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Interface to special color mapping device */
+
+#ifndef gdevcmap_INCLUDED
+# define gdevcmap_INCLUDED
+
+/* Define the color mapping algorithms. */
+typedef enum {
+
+ /* Don't change the color. */
+
+ device_cmap_identity = 0,
+
+ /* Snap each RGB primary component to 0 or 1 individually. */
+
+ device_cmap_snap_to_primaries,
+
+ /* Snap black to white, other colors to black. */
+
+ device_cmap_color_to_black_over_white,
+
+ /* Convert to a gray shade of the correct brightness. */
+
+ device_cmap_monochrome
+
+} gx_device_color_mapping_method_t;
+
+#define device_cmap_max_method device_cmap_monochrome
+
+/* Define the color mapping forwarding device. */
+typedef struct gx_device_cmap_s {
+ gx_device_forward_common;
+ gx_device_color_mapping_method_t mapping_method;
+} gx_device_cmap;
+
+extern_st(st_device_cmap);
+#define public_st_device_cmap() /* in gdevcmap.c */\
+ gs_public_st_suffix_add0_final(st_device_cmap, gx_device_cmap,\
+ "gx_device_cmap", device_cmap_enum_ptrs, device_cmap_reloc_ptrs,\
+ gx_device_finalize, st_device_forward)
+
+/* Initialize a color mapping device. Do this just once after allocation. */
+int gdev_cmap_init(P3(gx_device_cmap * dev, gx_device * target,
+ gx_device_color_mapping_method_t mapping_method));
+
+/* Set the color mapping method. This may be called at any time. */
+int gdev_cmap_set_method(P2(gx_device_cmap * dev,
+ gx_device_color_mapping_method_t mapping_method));
+
+#endif /* gdevcmap_INCLUDED */
diff --git a/pstoraster/gdevcups.c b/pstoraster/gdevcups.c
new file mode 100644
index 000000000..ff8ebc649
--- /dev/null
+++ b/pstoraster/gdevcups.c
@@ -0,0 +1,2426 @@
+/*
+ * "$Id$"
+ *
+ * GNU Ghostscript raster output driver for the Common UNIX Printing
+ * System (CUPS).
+ *
+ * Copyright 1993-2000 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 "gsexit.h"
+
+#include <stdlib.h>
+#include <filter/raster.h>
+#include <cups/ppd.h>
+#include <math.h>
+
+
+/*
+ * Globals...
+ */
+
+extern const char *cupsProfile;
+
+
+/*
+ * 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_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...
+ */
+
+ if (cups->header.Duplex && cups->ppd->flip_duplex && !(cups->page & 1))
+ {
+ 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] *
+ ((float)cups->header.PageSize[0] - pdev->HWMargins[2]) / 72.0;
+ pmat->ty = -(float)cups->header.HWResolution[1] * pdev->HWMargins[1] / 72.0;
+ }
+ else
+ {
+ 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 */
+ gs_param_string s; /* Temporary string value */
+
+
+#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 parameters...
+ */
+
+ param_string_from_string(s, cups->header.MediaClass);
+ if ((code = param_write_string(plist, "MediaClass", &s)) < 0)
+ return (code);
+
+ if ((code = param_write_int(plist, "AdvanceDistance",
+ (int *)&(cups->header.AdvanceDistance))) < 0)
+ return (code);
+
+ if ((code = param_write_int(plist, "AdvanceMedia",
+ (int *)&(cups->header.AdvanceMedia))) < 0)
+ return (code);
+
+ if ((code = param_write_int(plist, "Collate",
+ (int *)&(cups->header.Collate))) < 0)
+ return (code);
+
+ if ((code = param_write_int(plist, "CutMedia",
+ (int *)&(cups->header.CutMedia))) < 0)
+ return (code);
+
+ if ((code = param_write_int(plist, "InsertSheet",
+ (int *)&(cups->header.InsertSheet))) < 0)
+ return (code);
+
+ if ((code = param_write_int(plist, "Jog",
+ (int *)&(cups->header.Jog))) < 0)
+ return (code);
+
+ if ((code = param_write_int(plist, "LeadingEdge",
+ (int *)&(cups->header.LeadingEdge))) < 0)
+ return (code);
+
+ if ((code = param_write_int(plist, "MediaPosition",
+ (int *)&(cups->header.MediaPosition))) < 0)
+ return (code);
+
+ if ((code = param_write_int(plist, "MirrorPrint",
+ (int *)&(cups->header.MirrorPrint))) < 0)
+ return (code);
+
+ if ((code = param_write_int(plist, "NegativePrint",
+ (int *)&(cups->header.NegativePrint))) < 0)
+ return (code);
+
+ if ((code = param_write_int(plist, "OutputFaceUp",
+ (int *)&(cups->header.OutputFaceUp))) < 0)
+ return (code);
+
+ if ((code = param_write_int(plist, "Separations",
+ (int *)&(cups->header.Separations))) < 0)
+ return (code);
+
+ if ((code = param_write_int(plist, "TraySwitch",
+ (int *)&(cups->header.TraySwitch))) < 0)
+ return (code);
+
+ if ((code = param_write_int(plist, "Tumble",
+ (int *)&(cups->header.Tumble))) < 0)
+ return (code);
+
+ 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, mk; /* Integral CMYK values */
+ int tc, tm, ty; /* 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 :
+ 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];
+
+ 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 :
+ 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];
+
+ 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));
+
+ mk = max(ic, max(im, iy));
+ if (mk > ik)
+ ik = ik * ik / mk;
+
+ ic = lut_rgb_color[ic - ik];
+ im = lut_rgb_color[im - ik];
+ iy = lut_rgb_color[iy - ik];
+ 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));
+
+ mk = max(ic, max(im, iy));
+ if (mk > ik)
+ ik = ik * ik / mk;
+
+ ic = lut_rgb_color[ic - ik];
+ im = lut_rgb_color[im - ik];
+ iy = lut_rgb_color[iy - ik];
+ 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));
+
+ 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));
+
+ mk = max(ic, max(im, iy));
+ if (mk > ik)
+ ik = ik * ik / mk;
+
+ ic = lut_rgb_color[ic - ik];
+ im = lut_rgb_color[im - ik];
+ iy = lut_rgb_color[iy - ik];
+ 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 */
+ 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 :
+ if (cups->header.cupsColorSpace == CUPS_CSPACE_KCMYcm &&
+ cups->header.cupsBitsPerColor == 1)
+ cups->header.cupsBytesPerLine = (cups->header.cupsBitsPerColor *
+ cups->header.cupsWidth + 7) / 8 * 6;
+ else
+ cups->header.cupsBytesPerLine = (cups->header.cupsBitsPerColor *
+ cups->header.cupsWidth + 7) / 8 *
+ cups->color_info.num_components;
+ break;
+
+ case CUPS_ORDER_PLANAR :
+ cups->header.cupsBytesPerLine = (cups->header.cupsBitsPerColor *
+ 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, 2,
+ "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) /* Compatibility */
+ intoption(MediaPosition, "MediaPosition", 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 */
+ float m[3][3]; /* Color correction matrix */
+ 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 = 8;
+ 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 (cupsProfile && cups->header.cupsBitsPerColor == 8)
+ {
+ fprintf(stderr, "DEBUG: Using user-defined profile \"%s\"...\n", cupsProfile);
+
+ if (sscanf(cupsProfile, "%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f", &d, &g,
+ m[0] + 0, m[0] + 1, m[0] + 2,
+ m[1] + 0, m[1] + 1, m[1] + 2,
+ m[2] + 0, m[2] + 1, m[2] + 2) != 11)
+ fputs("DEBUG: User-defined profile does not contain 11 integers!\n", stderr);
+ else
+ {
+ cupsHaveProfile = 1;
+
+ d *= 0.001f;
+ g *= 0.001f;
+ m[0][0] *= 0.001f;
+ m[0][1] *= 0.001f;
+ m[0][2] *= 0.001f;
+ m[1][0] *= 0.001f;
+ m[1][1] *= 0.001f;
+ m[1][2] *= 0.001f;
+ m[2][0] *= 0.001f;
+ m[2][1] *= 0.001f;
+ m[2][2] *= 0.001f;
+ }
+ }
+ else 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;
+
+ d = profile->density;
+ g = profile->gamma;
+
+ memcpy(m, profile->matrix, sizeof(m));
+ }
+ }
+
+ if (cupsHaveProfile)
+ {
+ 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 * m[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 */
+ }
+
+
+ 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.cupsWidth * cups->header.cupsBitsPerColor + 7) / 8;
+
+ 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, kptr = dst, cptr = kptr + bandbytes,
+ mptr = cptr + bandbytes, yptr = mptr + bandbytes,
+ lcptr = yptr + bandbytes, lmptr = lcptr + bandbytes,
+ bit = 128;
+ x > 0;
+ x --, srcptr ++)
+ {
+ if (*srcptr & 0x20)
+ *kptr |= bit;
+ if (*srcptr & 0x10)
+ *cptr |= bit;
+ if (*srcptr & 0x08)
+ *mptr |= bit;
+ if (*srcptr & 0x04)
+ *yptr |= 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/gdevdbit.c b/pstoraster/gdevdbit.c
new file mode 100644
index 000000000..0e2180f0f
--- /dev/null
+++ b/pstoraster/gdevdbit.c
@@ -0,0 +1,706 @@
+/* Copyright (C) 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Default device bitmap copying implementation */
+#include "gx.h"
+#include "gpcheck.h"
+#include "gserrors.h"
+#include "gsbittab.h"
+#include "gsrect.h"
+#include "gsropt.h"
+#include "gxdcolor.h"
+#include "gxdevice.h"
+#include "gxdevmem.h"
+#include "gdevmem.h"
+#undef mdev
+#include "gxcpath.h"
+
+/* 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)
+{
+ bool invert;
+ gx_color_index color;
+ gx_device_color devc;
+
+ fit_copy(dev, data, dx, raster, id, x, y, w, h);
+ if (one != gx_no_color_index) {
+ invert = false;
+ color = one;
+ if (zero != gx_no_color_index) {
+ int code = (*dev_proc(dev, fill_rectangle))
+ (dev, x, y, w, h, zero);
+
+ if (code < 0)
+ return code;
+ }
+ } else {
+ invert = true;
+ color = zero;
+ }
+ color_set_pure(&devc, color);
+ return gx_dc_default_fill_masked
+ (&devc, data, dx, raster, id, x, y, w, h, dev, rop3_T, invert);
+}
+
+/* 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_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 = dev->memory;
+ 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_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_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] = gx_device_white(dev);
+ scolors[1] = gx_device_black(dev);
+ if (tile == 0)
+ colors[0] = colors[1]; /* pure color */
+ /*
+ * We want to write only where the mask is a 1, so enable source
+ * transparency. We have to include S in the operation,
+ * otherwise S_transparent will be ignored.
+ */
+ 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 | (rop3_S | lop_S_transparent));
+ }
+ 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 = data + (dx >> 3);
+ int dx_bit = dx & 7;
+ int wdx = w + dx_bit;
+ int iy;
+
+ for (row = data, iy = 0; iy < h; row += raster, iy++) {
+ int ix;
+
+ for (ix = dx_bit; ix < wdx;) {
+ int i0;
+ uint b;
+ uint len;
+ int code;
+
+ /* Skip 0-bits. */
+ b = row[ix >> 3];
+ len = byte_bit_run_length[ix & 7][b ^ 0xff];
+ if (len) {
+ ix += ((len - 1) & 7) + 1;
+ continue;
+ }
+ /* Scan 1-bits. */
+ i0 = ix;
+ for (;;) {
+ b = row[ix >> 3];
+ len = byte_bit_run_length[ix & 7][b];
+ if (!len)
+ break;
+ ix += ((len - 1) & 7) + 1;
+ if (ix >= wdx) {
+ ix = wdx;
+ break;
+ }
+ if (len < 8)
+ break;
+ }
+ /* Now color the run from i0 to ix. */
+ code = (*tile_proc)
+ (dev, tile, i0 - dx_bit + 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;
+
+ fit_fill_xy(dev, x, y, w, h);
+
+#ifdef DEBUG
+ if (gs_debug_c('t')) {
+ int ptx, pty;
+ const byte *ptp = tiles->data;
+
+ dlprintf3("[t]tile %dx%d raster=%d;",
+ tiles->size.x, tiles->size.y, tiles->raster);
+ dlprintf6(" x,y=%d,%d w,h=%d,%d p=%d,%d\n",
+ x, y, w, h, px, py);
+ dlputs("");
+ for (pty = 0; pty < tiles->size.y; pty++) {
+ dprintf(" ");
+ for (ptx = 0; ptx < tiles->raster; ptx++)
+ dprintf1("%3x", *ptp++);
+ }
+ dputc('\n');
+ }
+#endif
+
+ 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. */
+ } { /*
+ * Note: we can't do the following computations until after
+ * the fit_fill_xy.
+ */
+ 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;
+
+ 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_debug5('t', " 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 */
+}
+
+/* ---------------- 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; 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 = alignment_mod(data, align_bitmap_mod);
+ 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/gdevddrw.c b/pstoraster/gdevddrw.c
new file mode 100644
index 000000000..c02c0c1d4
--- /dev/null
+++ b/pstoraster/gdevddrw.c
@@ -0,0 +1,635 @@
+/* Copyright (C) 1989, 1995, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Default polygon and image drawing device procedures */
+#include "math_.h"
+#include "memory_.h"
+#include "gx.h"
+#include "gpcheck.h"
+#include "gserrors.h"
+#include "gxfixed.h"
+#include "gxmatrix.h"
+#include "gxdcolor.h"
+#include "gxdevice.h"
+#include "gxiparam.h"
+#include "gxistate.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 that the 'a' line is to the left of
+ * the 'b' line. Testing ax <= bx is neither sufficient nor
+ * necessary: in general, we need to compare the slopes.
+ */
+#define swap(r, s) (t = r, r = s, s = t)
+ if ((ax ^ bx) < 0) { /* In this case, the test ax <= bx is sufficient. */
+ if (ax > bx)
+ swap(ax, bx), swap(ay, by);
+ } else { /*
+ * Compare the slopes. We know that ay >= 0, by >= 0,
+ * and ax and bx have the same sign; the lines are in the
+ * correct order iff
+ * ay/ax >= by/bx, or
+ * ay*bx >= by*ax
+ * Eventually we can probably find a better way to test this,
+ * without using floating point.
+ */
+ if ((double)ay * bx < (double)by * ax)
+ 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 ay <= by. */
+#define swap(r, s) (t = r, r = s, s = t)
+ if (ay > by)
+ swap(ax, bx), swap(ay, by);
+#undef swap
+ /*
+ * Make a special check for a flat bottom or top,
+ * which we can handle with a single call on fill_trapezoid.
+ */
+ left.start.x = right.start.x = px;
+ left.start.y = right.start.y = py;
+ if (ay == 0) {
+ /* Flat top */
+ if (ax < 0)
+ left.start.x = px + ax;
+ else
+ right.start.x = px + ax;
+ left.end.x = right.end.x = px + bx;
+ left.end.y = right.end.y = py + by;
+ ym = py;
+ } else if (ay == by) {
+ /* Flat bottom */
+ if (ax < bx)
+ left.end.x = px + ax, right.end.x = px + bx;
+ else
+ left.end.x = px + bx, right.end.x = px + ax;
+ left.end.y = right.end.y = py + by;
+ ym = py;
+ } else {
+ ym = py + ay;
+ if (fixed_mult_quo(bx, ay, by) < ax) {
+ /* The 'b' line is to the left of the 'a' line. */
+ left.end.x = px + bx, left.end.y = py + by;
+ right.end.x = px + ax, right.end.y = py + ay;
+ code = (*fill_trapezoid) (dev, &left, &right, py, ym,
+ false, pdevc, lop);
+ right.start = right.end;
+ right.end = left.end;
+ } else {
+ /* The 'a' line is to the left of the 'b' line. */
+ left.end.x = px + ax, left.end.y = py + ay;
+ right.end.x = px + bx, right.end.y = py + by;
+ code = (*fill_trapezoid) (dev, &left, &right, py, ym,
+ false, pdevc, lop);
+ left.start = left.end;
+ left.end = right.end;
+ }
+ if (code < 0)
+ return code;
+ }
+ 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)
+ );
+ } {
+ 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;
+}
+
+/* ---------------- Image drawing ---------------- */
+
+/* GC structures for image enumerator */
+public_st_gx_image_enum_common();
+
+#define eptr ((gx_image_enum_common_t *)vptr)
+
+private
+ENUM_PTRS_BEGIN(image_enum_common_enum_ptrs) return 0;
+
+case 0:
+return ENUM_OBJ(gx_device_enum_ptr(eptr->dev));
+ENUM_PTRS_END
+
+private RELOC_PTRS_BEGIN(image_enum_common_reloc_ptrs)
+{
+ eptr->dev = gx_device_reloc_ptr(eptr->dev, gcst);
+}
+RELOC_PTRS_END
+
+#undef eptr
+
+/*
+ * gx_default_begin_image is only invoked for ImageType 1 images. However,
+ * the argument types are different, and if the device provides a
+ * begin_typed_image procedure, we should use it. See gxdevice.h.
+ */
+private int
+gx_no_begin_image(gx_device * dev,
+ const gs_imager_state * pis, const gs_image_t * pim,
+ gs_image_format_t format, const gs_int_rect * prect,
+ const gx_drawing_color * pdcolor, const gx_clip_path * pcpath,
+ gs_memory_t * memory, gx_image_enum_common_t ** pinfo)
+{
+ return -1;
+}
+int
+gx_default_begin_image(gx_device * dev,
+ const gs_imager_state * pis, const gs_image_t * pim,
+ gs_image_format_t format, const gs_int_rect * prect,
+ const gx_drawing_color * pdcolor, const gx_clip_path * pcpath,
+ gs_memory_t * memory, gx_image_enum_common_t ** pinfo)
+{
+ /*
+ * Hand off to begin_typed_image, being careful to avoid a
+ * possible recursion loop.
+ */
+ dev_proc_begin_image((*save_begin_image)) = dev_proc(dev, begin_image);
+ gs_image_t image;
+ const gs_image_t *ptim;
+ int code;
+
+ set_dev_proc(dev, begin_image, gx_no_begin_image);
+ if (pim->format == format)
+ ptim = pim;
+ else {
+ image = *pim;
+ image.format = format;
+ ptim = &image;
+ }
+ code = (*dev_proc(dev, begin_typed_image))
+ (dev, pis, NULL, (const gs_image_common_t *)ptim, prect, pdcolor,
+ pcpath, memory, pinfo);
+ set_dev_proc(dev, begin_image, save_begin_image);
+ return code;
+}
+
+int
+gx_default_begin_typed_image(gx_device * dev,
+ const gs_imager_state * pis, const gs_matrix * pmat,
+ const gs_image_common_t * pic, const gs_int_rect * prect,
+ const gx_drawing_color * pdcolor, const gx_clip_path * pcpath,
+ gs_memory_t * memory, gx_image_enum_common_t ** pinfo)
+{ /*
+ * If this is an ImageType 1 image using the imager's CTM,
+ * defer to begin_image.
+ */
+ if (pic->type->begin_typed_image == gx_begin_image1) {
+ const gs_image_t *pim = (const gs_image_t *)pic;
+
+ if (pmat == 0 ||
+ (pis != 0 && !memcmp(pmat, &ctm_only(pis), sizeof(*pmat)))
+ ) {
+ int code = (*dev_proc(dev, begin_image))
+ (dev, pis, pim, pim->format, prect, pdcolor,
+ pcpath, memory, pinfo);
+
+ if (code >= 0)
+ return code;
+ }
+ }
+ return (*pic->type->begin_typed_image)
+ (dev, pis, pmat, pic, prect, pdcolor, pcpath, memory, pinfo);
+}
+
+int
+gx_image_data(gx_image_enum_common_t * info, const byte ** plane_data,
+ int data_x, uint raster, int height)
+{
+ int num_planes = info->num_planes;
+ gx_image_plane_t planes[gs_image_max_components];
+ int i;
+
+#ifdef DEBUG
+ if (num_planes > gs_image_max_components) {
+ lprintf2("num_planes=%d > gs_image_max_components=%d!\n",
+ num_planes, gs_image_max_components);
+ return_error(gs_error_Fatal);
+ }
+#endif
+ for (i = 0; i < num_planes; ++i) {
+ planes[i].data = plane_data[i];
+ planes[i].data_x = data_x;
+ planes[i].raster = raster;
+ }
+ return gx_image_plane_data(info, planes, height);
+}
+
+int
+gx_image_plane_data(gx_image_enum_common_t * info,
+ const gx_image_plane_t * planes, int height)
+{
+ return info->procs->plane_data(info->dev, info, planes, height);
+}
+
+int
+gx_image_end(gx_image_enum_common_t * info, bool draw_last)
+{
+ return info->procs->end_image(info->dev, info, draw_last);
+}
+
+/* Backward compatibility for obsolete driver procedures. */
+
+int
+gx_default_image_data(gx_device *dev, gx_image_enum_common_t * info,
+ const byte ** plane_data,
+ int data_x, uint raster, int height)
+{
+ return gx_image_data(info, plane_data, data_x, raster, height);
+}
+
+int
+gx_default_end_image(gx_device *dev, gx_image_enum_common_t * info,
+ bool draw_last)
+{
+ return gx_image_end(info, draw_last);
+}
diff --git a/pstoraster/gdevdflt.c b/pstoraster/gdevdflt.c
new file mode 100644
index 000000000..7633b7b43
--- /dev/null
+++ b/pstoraster/gdevdflt.c
@@ -0,0 +1,270 @@
+/* Copyright (C) 1995, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Default device implementation */
+#include "gx.h"
+#include "gserrors.h"
+#include "gsropt.h"
+#include "gxcomp.h"
+#include "gxdevice.h"
+#include "gxdevmem.h"
+#undef mdev
+
+/* ---------------- Default device procedures ---------------- */
+
+/* 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, obsolete_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);
+ 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);
+ /*
+ * We always replace image_data and end_image with the new
+ * procedures, and, if in a DEBUG configuration, print a warning
+ * if the definitions aren't the default ones.
+ */
+#ifdef DEBUG
+# define CHECK_NON_DEFAULT(proc, default, procname)\
+ BEGIN\
+ if ( dev_proc(dev, proc) != NULL && dev_proc(dev, proc) != default )\
+ dprintf2("**** Warning: device %s implements obsolete procedure %s\n",\
+ dev->dname, procname);\
+ END
+#else
+# define CHECK_NON_DEFAULT(proc, default, procname)\
+ DO_NOTHING
+#endif
+ CHECK_NON_DEFAULT(image_data, gx_default_image_data, "image_data");
+ set_dev_proc(dev, image_data, gx_default_image_data);
+ CHECK_NON_DEFAULT(end_image, gx_default_end_image, "end_image");
+ set_dev_proc(dev, end_image, gx_default_end_image);
+#undef CHECK_NON_DEFAULT
+ fill_dev_proc(dev, strip_tile_rectangle, gx_default_strip_tile_rectangle);
+ fill_dev_proc(dev, strip_copy_rop, gx_default_strip_copy_rop);
+ fill_dev_proc(dev, get_clipping_box, gx_default_get_clipping_box);
+ fill_dev_proc(dev, begin_typed_image, gx_default_begin_typed_image);
+ fill_dev_proc(dev, get_bits_rectangle, gx_default_get_bits_rectangle);
+ fill_dev_proc(dev, map_color_rgb_alpha, gx_default_map_color_rgb_alpha);
+ fill_dev_proc(dev, create_compositor, gx_default_create_compositor);
+ fill_dev_proc(dev, get_hardware_params, gx_default_get_hardware_params);
+ fill_dev_proc(dev, text_begin, gx_default_text_begin);
+}
+
+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;
+}
+
+const 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_default_get_band(gx_device * dev, int y, int *band_start)
+{
+ return 0;
+}
+
+void
+gx_default_get_clipping_box(gx_device * dev, gs_fixed_rect * pbox)
+{
+ pbox->p.x = 0;
+ pbox->p.y = 0;
+ pbox->q.x = int2fixed(dev->width);
+ pbox->q.y = int2fixed(dev->height);
+}
+void
+gx_get_largest_clipping_box(gx_device * dev, gs_fixed_rect * pbox)
+{
+ pbox->p.x = min_fixed;
+ pbox->p.y = min_fixed;
+ pbox->q.x = max_fixed;
+ pbox->q.y = max_fixed;
+}
+
+int
+gx_no_create_compositor(gx_device * dev, gx_device ** pcdev,
+ const gs_composite_t * pcte, const gs_imager_state * pis, gs_memory_t * memory)
+{
+ return_error(gs_error_unknownerror); /* not implemented */
+}
+int
+gx_default_create_compositor(gx_device * dev, gx_device ** pcdev,
+ const gs_composite_t * pcte, const gs_imager_state * pis, gs_memory_t * memory)
+{
+ return (*pcte->type->procs.create_default_compositor)
+ (pcte, pcdev, dev, pis, memory);
+}
+int
+gx_non_imaging_create_compositor(gx_device * dev, gx_device ** pcdev,
+ const gs_composite_t * pcte, const gs_imager_state * pis, gs_memory_t * memory)
+{
+ *pcdev = dev;
+ return 0;
+}
+
+/* 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)
+ assign_dev_procs(mdev, mdproto);
+ 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);
+}
diff --git a/pstoraster/gdevdgbr.c b/pstoraster/gdevdgbr.c
new file mode 100644
index 000000000..c15669695
--- /dev/null
+++ b/pstoraster/gdevdgbr.c
@@ -0,0 +1,586 @@
+/* Copyright (C) 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Default implementation of device get_bits[_rectangle] */
+#include "gx.h"
+#include "gserrors.h"
+#include "gxdevice.h"
+#include "gxdevmem.h"
+#include "gxgetbit.h"
+#include "gxlum.h"
+#include "gdevmem.h"
+
+int
+gx_no_get_bits(gx_device * dev, int y, byte * data, byte ** actual_data)
+{
+ return_error(gs_error_unknownerror);
+}
+int
+gx_default_get_bits(gx_device * dev, int y, byte * data, byte ** actual_data)
+{ /*
+ * Hand off to get_bits_rectangle, being careful to avoid a
+ * possible recursion loop.
+ */
+ dev_proc_get_bits((*save_get_bits)) = dev_proc(dev, get_bits);
+ gs_int_rect rect;
+ gs_get_bits_params_t params;
+ int code;
+
+ rect.p.x = 0, rect.p.y = y;
+ rect.q.x = dev->width, rect.q.y = y + 1;
+ params.options =
+ (actual_data ? GB_RETURN_POINTER : 0) | GB_RETURN_COPY |
+ (GB_ALIGN_STANDARD | GB_OFFSET_0 | GB_RASTER_STANDARD |
+ /* No depth specified, we always use native colors. */
+ GB_PACKING_CHUNKY | GB_COLORS_NATIVE | GB_ALPHA_NONE);
+ params.x_offset = 0;
+ params.raster = bitmap_raster(dev->width * dev->color_info.depth);
+ params.data[0] = data;
+ set_dev_proc(dev, get_bits, gx_no_get_bits);
+ code = (*dev_proc(dev, get_bits_rectangle))
+ (dev, &rect, &params, NULL);
+ if (actual_data)
+ *actual_data = params.data[0];
+ set_dev_proc(dev, get_bits, save_get_bits);
+ return code;
+}
+
+/*
+ * Determine whether we can satisfy a request by simply using the stored
+ * representation.
+ */
+private bool
+requested_includes_stored(gs_get_bits_options_t requested,
+ gs_get_bits_options_t stored)
+{
+ gs_get_bits_options_t both = requested & stored;
+
+ if (!(both & GB_PACKING_ALL))
+ return false;
+ if (both & GB_COLORS_NATIVE)
+ return true;
+ if (both & GB_COLORS_STANDARD_ALL) {
+ if ((both & GB_ALPHA_ALL) && (both & GB_DEPTH_ALL))
+ return true;
+ }
+ return false;
+}
+
+/*
+ * Try to implement get_bits_rectangle by returning a pointer.
+ * Note that dev is used only for computing the default raster
+ * and for color_info.depth.
+ * This routine does not check x or h for validity.
+ */
+int
+gx_get_bits_return_pointer(gx_device * dev, int x, int h,
+ gs_get_bits_params_t * params, gs_get_bits_options_t stored,
+ byte * stored_base)
+{
+ gs_get_bits_options_t options = params->options;
+
+ if (!(options & GB_RETURN_POINTER) ||
+ !requested_includes_stored(options, stored)
+ )
+ return -1;
+ /*
+ * See whether we can return the bits in place. Note that even if
+ * offset_any isn't set, x_offset and x don't have to be equal: their
+ * bit offsets only have to match modulo align_bitmap_mod * 8 (to
+ * preserve alignment) if align_any isn't set, or mod 8 (since
+ * byte alignment is always required) if align_any is set.
+ */
+ {
+ int depth = dev->color_info.depth;
+ uint dev_raster = gx_device_raster(dev, 1);
+ uint raster =
+ (options & (GB_RASTER_STANDARD | GB_RASTER_ANY) ? dev_raster :
+ params->raster);
+
+ if (h <= 1 || raster == dev_raster) {
+ int x_offset =
+ (options & GB_OFFSET_ANY ? x :
+ options & GB_OFFSET_0 ? 0 : params->x_offset);
+
+ if (x_offset == x) {
+ params->data[0] = stored_base;
+ params->x_offset = x;
+ } else {
+ uint align_mod =
+ (options & GB_ALIGN_ANY ? 8 : align_bitmap_mod * 8);
+ int bit_offset = x - x_offset;
+ int bytes;
+
+ if (bit_offset & (align_mod - 1))
+ return -1; /* can't align */
+ if (depth & (depth - 1)) {
+ /* step = lcm(depth, align_mod) */
+ int step = depth / igcd(depth, align_mod) * align_mod;
+
+ bytes = bit_offset / step * step;
+ } else {
+ /* Use a faster algorithm if depth is a power of 2. */
+ bytes = bit_offset & (-depth & -align_mod);
+ }
+ params->data[0] = stored_base + arith_rshift(bytes, 3);
+ params->x_offset = (bit_offset - bytes) / depth;
+ }
+ params->options =
+ GB_ALIGN_STANDARD | GB_RETURN_POINTER | GB_RASTER_STANDARD |
+ GB_PACKING_CHUNKY | stored |
+ (params->x_offset == 0 ? GB_OFFSET_0 : GB_OFFSET_SPECIFIED);
+ return 0;
+ }
+ }
+ return -1;
+}
+
+/*
+ * Convert pixels between representations, primarily for get_bits_rectangle.
+ * stored indicates how the data are actually stored, and includes:
+ * - one option from the GB_PACKING group;
+ * - if h > 1, one option from the GB_RASTER group;
+ * - optionally (and normally), GB_COLORS_NATIVE;
+ * - optionally, one option each from the GB_COLORS_STANDARD, GB_DEPTH,
+ * and GB_ALPHA groups.
+ * Note that dev is used only for color mapping. This routine assumes that
+ * the stored data are aligned.
+ *
+ * Note: this routine does not check x, w, h for validity.
+ */
+int
+gx_get_bits_copy(gx_device * dev, int x, int w, int h,
+ gs_get_bits_params_t * params, gs_get_bits_options_t stored,
+ const byte * src_base, uint dev_raster)
+{
+ gs_get_bits_options_t options = params->options;
+ byte *data = params->data[0];
+ int depth = dev->color_info.depth;
+ int bit_x = x * depth;
+ const byte *src = src_base;
+
+ /*
+ * If the stored representation matches a requested representation,
+ * we can copy the data without any transformations.
+ */
+ bool direct_copy = requested_includes_stored(options, stored);
+
+ /*
+ * The request must include GB_PACKING_CHUNKY, GB_RETURN_COPY,
+ * and an offset and raster specification.
+ */
+ if ((~options & (GB_PACKING_CHUNKY | GB_RETURN_COPY)) ||
+ !(options & (GB_OFFSET_0 | GB_OFFSET_SPECIFIED)) ||
+ !(options & (GB_RASTER_STANDARD | GB_RASTER_SPECIFIED))
+ )
+ return_error(gs_error_rangecheck);
+ {
+ int x_offset = (options & GB_OFFSET_0 ? 0 : params->x_offset);
+ int end_bit = (x_offset + w) * depth;
+ uint std_raster =
+ (options & GB_ALIGN_STANDARD ? bitmap_raster(end_bit) :
+ (end_bit + 7) >> 3);
+ uint raster =
+ (options & GB_RASTER_STANDARD ? std_raster : params->raster);
+ int dest_bit_x = x_offset * depth;
+ int skew = bit_x - dest_bit_x;
+
+ /*
+ * If the bit positions line up, use bytes_copy_rectangle.
+ * Since bytes_copy_rectangle doesn't require alignment,
+ * the bit positions only have to match within a byte,
+ * not within align_bitmap_mod bytes.
+ */
+ if (!(skew & 7) && direct_copy) {
+ int bit_w = w * depth;
+
+ bytes_copy_rectangle(data + (dest_bit_x >> 3), raster,
+ src + (bit_x >> 3), dev_raster,
+ ((bit_x + bit_w + 7) >> 3) - (bit_x >> 3), h);
+ } else if (direct_copy) {
+ /*
+ * Use the logic already in mem_mono_copy_mono to copy the
+ * bits to the destination. We do this one line at a time,
+ * to avoid having to allocate a line pointer table.
+ */
+ gx_device_memory tdev;
+ byte *line_ptr = data;
+
+ tdev.line_ptrs = &tdev.base;
+ for (; h > 0; line_ptr += raster, src += dev_raster, --h) {
+ /* Make sure the destination is aligned. */
+ int align = alignment_mod(line_ptr, align_bitmap_mod);
+
+ tdev.base = line_ptr - align;
+ (*dev_proc(&mem_mono_device, copy_mono))
+ ((gx_device *) & tdev, src, bit_x, dev_raster, gx_no_bitmap_id,
+ dest_bit_x + (align << 3), 0, w, 1,
+ (gx_color_index) 0, (gx_color_index) 1);
+ }
+ } else if (options & ~stored & GB_COLORS_NATIVE) {
+ /*
+ * Convert standard colors to native. Note that the source
+ * may have depths other than 8 bits per component.
+ */
+ int dest_bit_offset = x_offset * depth;
+ byte *dest_line = data + (dest_bit_offset >> 3);
+ int ncolors =
+ (stored & GB_COLORS_RGB ? 3 : stored & GB_COLORS_CMYK ? 4 :
+ stored & GB_COLORS_GRAY ? 1 : -1);
+ int ncomp = ncolors +
+ ((stored & (GB_ALPHA_FIRST | GB_ALPHA_LAST)) != 0);
+ int src_depth = GB_OPTIONS_DEPTH(stored);
+ int src_bit_offset = x * src_depth * ncomp;
+ const byte *src_line = src_base + (src_bit_offset >> 3);
+ gx_color_value src_max = (1 << src_depth) - 1;
+
+#define v2cv(value) ((ulong)(value) * gx_max_color_value / src_max)
+ gx_color_value alpha_default = src_max;
+
+ options &= ~GB_COLORS_ALL | GB_COLORS_NATIVE;
+ for (; h > 0; dest_line += raster, src_line += dev_raster, --h) {
+ int i;
+
+ sample_load_declare_setup(src, sbit, src_line,
+ src_bit_offset & 7, src_depth);
+ sample_store_declare_setup(dest, dbit, dbyte, dest_line,
+ dest_bit_offset & 7, depth);
+
+ for (i = 0; i < w; ++i) {
+ int j;
+ gx_color_value v[4], va = alpha_default;
+ gx_color_index pixel;
+
+ /* Fetch the source data. */
+ if (stored & GB_ALPHA_FIRST) {
+ sample_load_next16(va, src, sbit, src_depth);
+ va = v2cv(va);
+ }
+ for (j = 0; j < ncolors; ++j) {
+ gx_color_value vj;
+
+ sample_load_next16(vj, src, sbit, src_depth);
+ v[j] = v2cv(vj);
+ }
+ if (stored & GB_ALPHA_LAST) {
+ sample_load_next16(va, src, sbit, src_depth);
+ va = v2cv(va);
+ }
+ /* Convert and store the pixel value. */
+ switch (ncolors) {
+ case 1:
+ v[2] = v[1] = v[0];
+ case 3:
+ pixel = (*dev_proc(dev, map_rgb_alpha_color))
+ (dev, v[0], v[1], v[2], va);
+ break;
+ case 4:
+ /****** NO ALPHA FOR CMYK ******/
+ pixel = (*dev_proc(dev, map_cmyk_color))
+ (dev, v[0], v[1], v[2], v[3]);
+ break;
+ default:
+ return_error(gs_error_rangecheck);
+ }
+ sample_store_next32(pixel, dest, dbit, depth, dbyte);
+ }
+ sample_store_flush(dest, dbit, depth, dbyte);
+ }
+ } else if (!(options & GB_DEPTH_8)) {
+ /*
+ * We don't support general depths yet, or conversion between
+ * different formats. Punt.
+ */
+ return_error(gs_error_rangecheck);
+ } else {
+ /*
+ * We have to do some conversion to each pixel. This is the
+ * slowest, most general case.
+ */
+ int src_bit_offset = x * depth;
+ const byte *src_line = src_base + (src_bit_offset >> 3);
+ int ncomp =
+ (options & (GB_ALPHA_FIRST | GB_ALPHA_LAST) ? 4 : 3);
+ byte *dest_line = data + x_offset * ncomp;
+
+ /* Pick the representation that's most likely to be useful. */
+ if (options & GB_COLORS_RGB)
+ options &= ~GB_COLORS_STANDARD_ALL | GB_COLORS_RGB;
+ else if (options & GB_COLORS_CMYK)
+ options &= ~GB_COLORS_STANDARD_ALL | GB_COLORS_CMYK;
+ else if (options & GB_COLORS_GRAY)
+ options &= ~GB_COLORS_STANDARD_ALL | GB_COLORS_GRAY;
+ else
+ return_error(gs_error_rangecheck);
+ for (; h > 0; dest_line += raster, src_line += dev_raster, --h) {
+ int i;
+
+ sample_load_declare_setup(src, bit, src_line, src_bit_offset & 7,
+ depth);
+ byte *dest = dest_line;
+
+ for (i = 0; i < w; ++i) {
+ gx_color_index pixel = 0;
+ gx_color_value rgba[4];
+
+ sample_load_next32(pixel, src, bit, depth);
+ (*dev_proc(dev, map_color_rgb_alpha)) (dev, pixel, rgba);
+ if (options & GB_ALPHA_FIRST)
+ *dest++ = gx_color_value_to_byte(rgba[3]);
+ /* Convert to the requested color space. */
+ if (options & GB_COLORS_RGB) {
+ dest[0] = gx_color_value_to_byte(rgba[0]);
+ dest[1] = gx_color_value_to_byte(rgba[1]);
+ dest[2] = gx_color_value_to_byte(rgba[2]);
+ dest += 3;
+ } else if (options & GB_COLORS_CMYK) {
+ /* Use the standard RGB to CMYK algorithm, */
+ /* with maximum black generation and undercolor removal. */
+ gx_color_value white = max(rgba[0], max(rgba[1], rgba[2]));
+
+ dest[0] = gx_color_value_to_byte(white - rgba[0]);
+ dest[1] = gx_color_value_to_byte(white - rgba[1]);
+ dest[2] = gx_color_value_to_byte(white - rgba[2]);
+ dest[3] = gx_color_value_to_byte(gx_max_color_value - white);
+ dest += 4;
+ } else { /* GB_COLORS_GRAY */
+ /* Use the standard RGB to Gray algorithm. */
+ *dest++ = gx_color_value_to_byte(
+ ((rgba[0] * (ulong) lum_red_weight) +
+ (rgba[1] * (ulong) lum_green_weight) +
+ (rgba[2] * (ulong) lum_blue_weight) +
+ (lum_all_weights / 2))
+ / lum_all_weights);
+ }
+ if (options & GB_ALPHA_LAST)
+ *dest++ = gx_color_value_to_byte(rgba[3]);
+ }
+ }
+ }
+ params->options =
+ (options & (GB_COLORS_ALL | GB_ALPHA_ALL)) | GB_PACKING_CHUNKY |
+ (options & GB_COLORS_NATIVE ? 0 : options & GB_DEPTH_ALL) |
+ (options & GB_ALIGN_STANDARD ? GB_ALIGN_STANDARD : GB_ALIGN_ANY) |
+ GB_RETURN_COPY |
+ (x_offset == 0 ? GB_OFFSET_0 : GB_OFFSET_SPECIFIED) |
+ (raster == std_raster ? GB_RASTER_STANDARD : GB_RASTER_SPECIFIED);
+ }
+ return 0;
+}
+
+int
+gx_no_get_bits_rectangle(gx_device * dev, const gs_int_rect * prect,
+ gs_get_bits_params_t * params, gs_int_rect ** unread)
+{
+ return_error(gs_error_unknownerror);
+}
+int
+gx_default_get_bits_rectangle(gx_device * dev, const gs_int_rect * prect,
+ gs_get_bits_params_t * params, gs_int_rect ** unread)
+{
+ dev_proc_get_bits_rectangle((*save_get_bits_rectangle)) =
+ dev_proc(dev, get_bits_rectangle);
+ int depth = dev->color_info.depth;
+ uint min_raster = (dev->width * depth + 7) >> 3;
+ gs_get_bits_options_t options = params->options;
+ int code;
+
+ /* Avoid a recursion loop. */
+ set_dev_proc(dev, get_bits_rectangle, gx_no_get_bits_rectangle);
+ /*
+ * If the parameters are right, try to call get_bits directly. Note
+ * that this may fail if a device only implements get_bits_rectangle
+ * (not get_bits) for a limited set of options. Note also that this
+ * must handle the case of the recursive call from within
+ * get_bits_rectangle (see below): because of this, and only because
+ * of this, it must handle partial scan lines.
+ */
+ if (prect->q.y == prect->p.y + 1 &&
+ !(~options &
+ (GB_RETURN_COPY | GB_PACKING_CHUNKY | GB_COLORS_NATIVE)) &&
+ (options & (GB_ALIGN_STANDARD | GB_ALIGN_ANY)) &&
+ ((options & (GB_OFFSET_0 | GB_OFFSET_ANY)) ||
+ ((options & GB_OFFSET_SPECIFIED) && params->x_offset == 0)) &&
+ ((options & (GB_RASTER_STANDARD | GB_RASTER_ANY)) ||
+ ((options & GB_RASTER_SPECIFIED) &&
+ params->raster >= min_raster)) &&
+ unread == NULL
+ ) {
+ byte *data = params->data[0];
+ byte *row = data;
+
+ if (!(prect->p.x == 0 && prect->q.x == dev->width)) {
+ /* Allocate an intermediate row buffer. */
+ row = gs_alloc_bytes(dev->memory, min_raster,
+ "gx_default_get_bits_rectangle");
+
+ if (row == 0) {
+ code = gs_note_error(gs_error_VMerror);
+ goto ret;
+ }
+ }
+ code = (*dev_proc(dev, get_bits))
+ (dev, prect->p.y, row, &params->data[0]);
+ if (code >= 0) {
+ if (row != data) {
+ if (prect->p.x == 0 && params->data[0] != row) {
+ /*
+ * get_bits returned an appropriate pointer: we can
+ * avoid doing any copying.
+ */
+ DO_NOTHING;
+ } else {
+ /* Copy the partial row into the supplied buffer. */
+ int width_bits = (prect->q.x - prect->p.x) * depth;
+ gx_device_memory tdev;
+
+ tdev.width = width_bits;
+ tdev.height = 1;
+ tdev.line_ptrs = &tdev.base;
+ tdev.base = data;
+ code = (*dev_proc(&mem_mono_device, copy_mono))
+ ((gx_device *) & tdev, params->data[0], prect->p.x * depth,
+ min_raster, gx_no_bitmap_id, 0, 0, width_bits, 1,
+ (gx_color_index) 0, (gx_color_index) 1);
+ params->data[0] = data;
+ }
+ gs_free_object(dev->memory, row,
+ "gx_default_get_bits_rectangle");
+ }
+ params->options =
+ GB_ALIGN_STANDARD | GB_OFFSET_0 | GB_PACKING_CHUNKY |
+ GB_ALPHA_NONE | GB_COLORS_NATIVE | GB_RASTER_STANDARD |
+ (params->data[0] == data ? GB_RETURN_COPY : GB_RETURN_POINTER);
+ goto ret;
+ }
+ } {
+ /* Do the transfer row-by-row using a buffer. */
+ int x = prect->p.x, w = prect->q.x - x;
+ int bits_per_pixel = depth;
+ byte *row;
+
+ if (options & GB_COLORS_STANDARD_ALL) {
+ /*
+ * Make sure the row buffer can hold the standard color
+ * representation, in case the device decides to use it.
+ */
+ int bpc = GB_OPTIONS_MAX_DEPTH(options);
+ int nc =
+ (options & GB_COLORS_CMYK ? 4 :
+ options & GB_COLORS_RGB ? 3 : 1) +
+ (options & (GB_ALPHA_ALL - GB_ALPHA_NONE) ? 1 : 0);
+ int bpp = bpc * nc;
+
+ if (bpp > bits_per_pixel)
+ bits_per_pixel = bpp;
+ }
+ row = gs_alloc_bytes(dev->memory, (bits_per_pixel * w + 7) >> 3,
+ "gx_default_get_bits_rectangle");
+ if (row == 0) {
+ code = gs_note_error(gs_error_VMerror);
+ } else {
+ uint dev_raster = gx_device_raster(dev, true);
+ uint raster =
+ (options & GB_RASTER_SPECIFIED ? params->raster :
+ options & GB_ALIGN_STANDARD ? bitmap_raster(depth * w) :
+ (depth * w + 7) >> 3);
+ gs_int_rect rect;
+ gs_get_bits_params_t copy_params;
+ gs_get_bits_options_t copy_options =
+ GB_ALIGN_ANY | (GB_RETURN_COPY | GB_RETURN_POINTER) |
+ (GB_OFFSET_0 | GB_OFFSET_ANY) |
+ (GB_RASTER_STANDARD | GB_RASTER_ANY) | GB_PACKING_CHUNKY |
+ GB_COLORS_NATIVE | (options & (GB_DEPTH_ALL | GB_COLORS_ALL)) |
+ GB_ALPHA_ALL;
+ byte *dest = params->data[0];
+ int y;
+
+ rect.p.x = x, rect.q.x = x + w;
+ code = 0;
+ for (y = prect->p.y; y < prect->q.y; ++y) {
+ rect.p.y = y, rect.q.y = y + 1;
+ copy_params.options = copy_options;
+ copy_params.data[0] = row;
+ code = (*save_get_bits_rectangle)
+ (dev, &rect, &copy_params, NULL);
+ if (code < 0)
+ break;
+ if (copy_params.options & GB_OFFSET_0)
+ copy_params.x_offset = 0;
+ params->data[0] = dest + (y - prect->p.y) * raster;
+ code = gx_get_bits_copy(dev, copy_params.x_offset, w, 1,
+ params, copy_params.options,
+ copy_params.data[0], dev_raster);
+ if (code < 0)
+ break;
+ }
+ gs_free_object(dev->memory, row, "gx_default_get_bits_rectangle");
+ params->data[0] = dest;
+ }
+ }
+ ret:set_dev_proc(dev, get_bits_rectangle, save_get_bits_rectangle);
+ return (code < 0 ? code : 0);
+}
+
+/* ------ Debugging printout ------ */
+
+#ifdef DEBUG
+
+void
+debug_print_gb_options(gx_bitmap_format_t options)
+{
+ static const char *const option_names[] =
+ {
+ GX_BITMAP_FORMAT_NAMES
+ };
+ const char *prev = " ";
+ int i;
+
+ dlprintf1("0x%lx", (ulong) options);
+ for (i = 0; i < sizeof(options) * 8; ++i)
+ if ((options >> i) & 1) {
+ dprintf2("%c%s",
+ (!memcmp(prev, option_names[i], 3) ? '|' : ','),
+ option_names[i]);
+ prev = option_names[i];
+ }
+ dputc('\n');
+}
+
+void
+debug_print_gb_params(gs_get_bits_params_t * params)
+{
+ gs_get_bits_options_t options = params->options;
+
+ debug_print_gb_options(options);
+ dprintf1("data[0]=0x%lx", (ulong) params->data[0]);
+ if (options & GB_OFFSET_SPECIFIED)
+ dprintf1(" x_offset=%d", params->x_offset);
+ if (options & GB_RASTER_SPECIFIED)
+ dprintf1(" raster=%u", params->raster);
+ dputc('\n');
+}
+
+#endif /* DEBUG */
diff --git a/pstoraster/gdevhit.c b/pstoraster/gdevhit.c
new file mode 100644
index 000000000..9e16a2a0d
--- /dev/null
+++ b/pstoraster/gdevhit.c
@@ -0,0 +1,98 @@
+/* Copyright (C) 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Hit detection device */
+#include "std.h"
+#include "gserror.h"
+#include "gserrors.h"
+#include "gstypes.h"
+#include "gsmemory.h"
+#include "gxdevice.h"
+
+/* Define the value returned for a detected hit. */
+const int gs_hit_detected = gs_error_hit_detected;
+
+/*
+ * 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);
+const gx_device gs_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,
+ NULL, /* tile_rectangle */
+ NULL, /* copy_mono */
+ NULL, /* copy_color */
+ gx_default_draw_line,
+ NULL, /* get_bits */
+ NULL, /* get_params */
+ NULL, /* put_params */
+ gx_default_map_cmyk_color,
+ NULL, /* get_xfont_procs */
+ NULL, /* get_xfont_device */
+ gx_default_map_rgb_alpha_color,
+ gx_default_get_page_device,
+ gx_default_get_alpha_bits,
+ NULL, /* copy_alpha */
+ gx_default_get_band,
+ NULL, /* copy_rop */
+ gx_default_fill_path,
+ NULL, /* stroke_path */
+ NULL, /* 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,
+ gx_default_strip_copy_rop,
+ gx_get_largest_clipping_box,
+ gx_default_begin_typed_image,
+ NULL, /* get_bits_rectangle */
+ gx_default_map_color_rgb_alpha,
+ gx_non_imaging_create_compositor,
+ NULL /* get_hardware_params */
+ }
+};
+
+/* 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)
+{
+ if (w > 0 && h > 0)
+ return_error(gs_error_hit_detected);
+ return 0;
+}
diff --git a/pstoraster/gdevht.h b/pstoraster/gdevht.h
new file mode 100644
index 000000000..a2f665f12
--- /dev/null
+++ b/pstoraster/gdevht.h
@@ -0,0 +1,51 @@
+/* Copyright (C) 1995, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Requires gxdevice.h */
+
+#ifndef gdevht_INCLUDED
+# define gdevht_INCLUDED
+
+#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).
+ * We represent colors by packing the two colors being halftoned and the
+ * halftone level into a gx_color_index.
+ */
+typedef struct gx_device_ht_s {
+ gx_device_forward_common;
+ /* Following + target 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. */
+ int color_shift; /* # of bits of color */
+ int level_shift; /* = color_shift * 2 */
+ gx_color_index color_mask; /* (1 << color_shift) - 1 */
+ gs_int_point phase; /* halftone tile offset */
+} gx_device_ht;
+
+#endif /* gdevht_INCLUDED */
diff --git a/pstoraster/gdevm1.c b/pstoraster/gdevm1.c
new file mode 100644
index 000000000..d47496711
--- /dev/null
+++ b/pstoraster/gdevm1.c
@@ -0,0 +1,759 @@
+/* Copyright (C) 1989, 1995, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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 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,
+ gx_default_map_cmyk_color, gx_no_copy_alpha,
+ mem_mono_strip_tile_rectangle, mem_mono_strip_copy_rop,
+ mem_get_bits_rectangle);
+
+/* 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)
+{
+ gx_device_memory * const mdev = (gx_device_memory *)dev;
+
+ 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])
+{
+ gx_device_memory * const mdev = (gx_device_memory *)dev;
+
+ 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)
+{
+ gx_device_memory * const mdev = (gx_device_memory *)dev;
+
+#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.
+ *
+ * Since source and destination are both always big-endian,
+ * fetching an aligned chunk never requires byte swapping.
+ */
+#define CFETCH_ALIGNED(cptr)\
+ (*(const chunk *)(cptr))
+
+/*
+ * 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
+
+typedef enum {
+ COPY_OR = 0, COPY_STORE, COPY_AND, COPY_FUNNY
+} copy_function;
+typedef struct {
+ uint invert;
+ copy_function op;
+} copy_mode;
+
+/*
+ * Map from <color0,color1> to copy_mode.
+ * Logically, this is a 2-D array.
+ * The indexing is (transparent, 0, 1, unused). */
+private const copy_mode copy_modes[16] =
+{
+ {~0, COPY_FUNNY}, /* NN */
+ {~0, COPY_AND}, /* N0 */
+ {0, COPY_OR}, /* N1 */
+ {0, 0}, /* unused */
+ {0, COPY_AND}, /* 0N */
+ {0, COPY_FUNNY}, /* 00 */
+ {0, COPY_STORE}, /* 01 */
+ {0, 0}, /* unused */
+ {~0, COPY_OR}, /* 1N */
+ {~0, COPY_STORE}, /* 10 */
+ {0, COPY_FUNNY}, /* 11 */
+ {0, 0}, /* unused */
+ {0, 0}, /* unused */
+ {0, 0}, /* unused */
+ {0, 0}, /* unused */
+ {0, 0}, /* unused */
+};
+
+/* Handle the funny cases that aren't supposed to happen. */
+#define FUNNY_CASE()\
+ (invert ? gs_note_error(-1) :\
+ mem_mono_fill_rectangle(dev, x, y, w, h, color0))
+
+private int
+mem_mono_copy_mono(gx_device * dev,
+ const byte * source_data, int source_x, int source_raster, gx_bitmap_id id,
+ int x, int y, int w, int h, gx_color_index color0, gx_color_index color1)
+{
+ gx_device_memory * const mdev = (gx_device_memory *)dev;
+
+#ifdef USE_COPY_ROP
+ return mem_mono_copy_rop(dev, source_data, source_x, source_raster,
+ id, NULL, NULL, NULL,
+ x, y, w, h, 0, 0,
+ ((color0 == gx_no_color_index ? rop3_D :
+ color0 == 0 ? rop3_0 : rop3_1) & ~rop3_S) |
+ ((color1 == gx_no_color_index ? rop3_D :
+ color1 == 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;
+
+ DECLARE_SCAN_PTR_VARS(dbptr, byte *, dest_raster);
+#define optr ((chunk *)dbptr)
+ register int skew;
+ register uint invert;
+
+ fit_copy(dev, source_data, source_x, source_raster, id, x, y, w, h);
+#if gx_no_color_index_value != -1 /* hokey! */
+ if (color0 == gx_no_color_index)
+ color0 = -1;
+ if (color1 == gx_no_color_index)
+ color1 = -1;
+#endif
+ mode = copy_modes[((int)color0 << 2) + (int)color1 + 5];
+ invert = mode.invert; /* load register */
+ SETUP_RECT_VARS(dbptr, byte *, dest_raster);
+ bptr = source_data + ((source_x & ~chunk_align_bit_mask) >> 3);
+ dbit = x & chunk_align_bit_mask;
+ skew = dbit - (source_x & 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)\
+ 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);\
+ if ( --h == 0 ) break;\
+ END_Y_LOOP(source_raster, dest_raster);\
+ }
+
+#define WRITE1_LOOP(src)\
+ switch ( mode.op ) {\
+ 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: return FUNNY_CASE();\
+ }
+
+ 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);\
+ if ( --h == 0 ) break;\
+ END_Y_LOOP(source_raster, dest_raster);\
+ }
+#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);\
+ if ( --h == 0 ) break;\
+ END_Y_LOOP(source_raster, dest_raster);\
+ }
+#endif
+
+ switch (mode.op) {
+ 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:
+ return FUNNY_CASE();
+ }
+#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 = source_raster - words;
+ uint dskip = dest_raster - 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); }\
+ if ( --h == 0 ) break;\
+ END_Y_LOOP(sskip, dskip);\
+ }
+
+ switch (mode.op) {
+ 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:
+ return FUNNY_CASE();
+ }
+#undef WRITE_ALIGNED
+ } else { /* not aligned */
+ int cskew = -skew & chunk_bit_mask;
+ bool case_right =
+ (skew >= 0 ? true :
+ ((bptr += chunk_bytes), false));
+
+ skew &= chunk_bit_mask;
+
+#define WRITE_UNALIGNED(wr_op, wr_op_masked)\
+ /* Prefetch partial word. */\
+ bits =\
+ (case_right ? CFETCH_RIGHT(bptr, skew, cskew) :\
+ CFETCH2(bptr - chunk_bytes, cskew, skew));\
+ 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 (mode.op) {
+ case COPY_OR:
+ for (;;) {
+ int count = wleft;
+
+ WRITE_UNALIGNED(WRITE_OR, WRITE_OR_MASKED);
+ if (--h == 0)
+ break;
+ END_Y_LOOP(sskip, dskip);
+ }
+ break;
+ case COPY_STORE:
+ for (;;) {
+ int count = wleft;
+
+ WRITE_UNALIGNED(WRITE_STORE, WRITE_STORE_MASKED);
+ if (--h == 0)
+ break;
+ END_Y_LOOP(sskip, dskip);
+ }
+ break;
+ case COPY_AND:
+ for (;;) {
+ int count = wleft;
+
+ WRITE_UNALIGNED(WRITE_AND, WRITE_AND_MASKED);
+ if (--h == 0)
+ break;
+ END_Y_LOOP(sskip, dskip);
+ }
+ break;
+ default /*case COPY_FUNNY */ :
+ return FUNNY_CASE();
+ }
+#undef WRITE_UNALIGNED
+ }
+ }
+#undef END_Y_LOOP
+#undef NEXT_X_CHUNK
+ return 0;
+#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)
+{
+ gx_device_memory * const mdev = (gx_device_memory *)dev;
+
+#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 source_raster;
+ uint tile_bits_size;
+ const byte *source_data;
+ const byte *end;
+ int x, rw, w, h;
+ register const byte *bptr; /* actually chunk * */
+ int dbit, wleft;
+ uint mask;
+ byte *dbase;
+
+ DECLARE_SCAN_PTR_VARS(dbptr, byte *, dest_raster);
+#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;
+ source_raster = tiles->raster;
+ source_data = tiles->data + ((y + py) % tiles->rep_height) * source_raster;
+ tile_bits_size = tiles->size.y * source_raster;
+ end = tiles->data + tile_bits_size;
+#undef END_Y_LOOP
+#define END_Y_LOOP(sdelta, ddelta)\
+ if ( end - bptr <= sdelta ) /* wrap around */\
+ bptr -= tile_bits_size;\
+ bptr += sdelta; dbptr += ddelta
+ dest_raster = 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 source_x = 0.
+ */
+ {
+ int source_x = (x + px) % tiles->rep_width;
+
+ w = tiles->size.x - source_x;
+ bptr = source_data + ((source_x & ~chunk_align_bit_mask) >> 3);
+ dbit = x & chunk_align_bit_mask;
+ skew = dbit - (source_x & 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);\
+ if ( --h == 0 ) break;\
+ END_Y_LOOP(source_raster, dest_raster);\
+ }
+ 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);
+ if (--h == 0)
+ break;
+ END_Y_LOOP(source_raster, dest_raster);
+ }
+#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);
+ if (--h == 0)
+ break;
+ END_Y_LOOP(source_raster, dest_raster);
+ }
+#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 = source_raster - words;
+ uint dskip = dest_raster - 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);
+ }
+ if (--h == 0)
+ break;
+ 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);
+ }
+ if (--h == 0)
+ break;
+ 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 = source_data;
+ 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 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,
+ gx_default_map_cmyk_color, gx_no_copy_alpha,
+ mem1_word_strip_tile_rectangle, gx_no_strip_copy_rop,
+ mem_word_get_bits_rectangle);
+
+/* 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)
+{
+ gx_device_memory * const mdev = (gx_device_memory *)dev;
+ 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 * source_data, int source_x, int source_raster, gx_bitmap_id id,
+ int x, int y, int w, int h, gx_color_index color0, gx_color_index color1)
+{
+ gx_device_memory * const mdev = (gx_device_memory *)dev;
+ byte *row;
+ uint raster;
+ bool store;
+
+ fit_copy(dev, source_data, source_x, source_raster, id, x, y, w, h);
+ row = scan_line_base(mdev, y);
+ raster = mdev->raster;
+ store = (color0 != gx_no_color_index && color1 != gx_no_color_index);
+ mem_swap_byte_rect(row, raster, x, w, h, store);
+ mem_mono_copy_mono(dev, source_data, source_x, source_raster, id,
+ x, y, w, h, color0, color1);
+ 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..9a9413a94
--- /dev/null
+++ b/pstoraster/gdevm16.c
@@ -0,0 +1,168 @@
+/* Copyright (C) 1994, 1996, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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 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_default_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 >> 5) & 0x3f;
+ prgb[1] = ((value << 10) + (value << 4) + (value >> 2))
+ >> (16 - gx_color_value_bits);
+ value = color & 0x1f;
+ prgb[2] = ((value << 11) + (value << 6) + (value << 1) + (value >> 4))
+ >> (16 - gx_color_value_bits);
+ 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)
+{
+ gx_device_memory * const mdev = (gx_device_memory *)dev;
+#if arch_is_big_endian
+ const ushort color16 = (ushort)color;
+#else
+ const ushort color16 = (ushort)((color << 8) | (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;
+}
+
+/* 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)
+{
+ gx_device_memory * const mdev = (gx_device_memory *)dev;
+#if arch_is_big_endian
+ const ushort zero16 = (ushort)zero;
+ const ushort 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;
+}
+
+/* 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)
+{
+ gx_device_memory * const mdev = (gx_device_memory *)dev;
+
+ 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..12cf8ec9b
--- /dev/null
+++ b/pstoraster/gdevm2.c
@@ -0,0 +1,259 @@
+/* Copyright (C) 1994, 1995, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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 */
+
+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 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)
+{
+ gx_device_memory * const mdev = (gx_device_memory *)dev;
+
+ 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)
+{
+ gx_device_memory * const mdev = (gx_device_memory *)dev;
+ 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 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,
+ gx_default_map_cmyk_color, gx_default_strip_tile_rectangle,
+ gx_no_strip_copy_rop, mem_word_get_bits_rectangle);
+
+/* 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)
+{
+ gx_device_memory * const mdev = (gx_device_memory *)dev;
+ 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)
+{
+ gx_device_memory * const mdev = (gx_device_memory *)dev;
+ 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..1e3a4b932
--- /dev/null
+++ b/pstoraster/gdevm24.c
@@ -0,0 +1,526 @@
+/* Copyright (C) 1994, 1995, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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 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,
+ gx_default_map_cmyk_color, mem_true24_copy_alpha,
+ gx_default_strip_tile_rectangle, mem_true24_strip_copy_rop,
+ mem_get_bits_rectangle);
+
+/* 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)
+{
+ gx_device_memory * const mdev = (gx_device_memory *)dev;
+ declare_unpack_color(r, g, b, color);
+ declare_scan_ptr(dest);
+
+ /*
+ * In order to avoid testing w > 0 and h > 0 twice, we defer
+ * executing setup_rect, and use fit_fill_xywh instead of
+ * fit_fill.
+ */
+ fit_fill_xywh(dev, x, y, w, h);
+ if (w >= 5) {
+ if (h <= 0)
+ return 0;
+ setup_rect(dest);
+ 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 */
+ setup_rect(dest);
+ 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)
+{
+ gx_device_memory * const mdev = (gx_device_memory *)dev;
+ 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)
+{
+ gx_device_memory * const mdev = (gx_device_memory *)dev;
+
+ 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)
+{
+ gx_device_memory * const mdev = (gx_device_memory *)dev;
+ 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 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,
+ gx_default_map_cmyk_color, gx_default_strip_tile_rectangle,
+ gx_no_strip_copy_rop, mem_word_get_bits_rectangle);
+
+/* 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)
+{
+ gx_device_memory * const mdev = (gx_device_memory *)dev;
+ 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)
+{
+ gx_device_memory * const mdev = (gx_device_memory *)dev;
+ 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)
+{
+ gx_device_memory * const mdev = (gx_device_memory *)dev;
+ 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..d35483323
--- /dev/null
+++ b/pstoraster/gdevm32.c
@@ -0,0 +1,249 @@
+/* Copyright (C) 1994, 1995, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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 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,
+ gx_default_cmyk_map_cmyk_color, gx_default_strip_tile_rectangle,
+ gx_default_strip_copy_rop, mem_get_bits_rectangle);
+
+/* 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)
+{
+ gx_device_memory * const mdev = (gx_device_memory *)dev;
+ 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)
+{
+ gx_device_memory * const mdev = (gx_device_memory *)dev;
+ 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)
+{
+ gx_device_memory * const mdev = (gx_device_memory *)dev;
+
+ 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 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,
+ gx_default_cmyk_map_cmyk_color, gx_default_strip_tile_rectangle,
+ gx_no_strip_copy_rop, mem_word_get_bits_rectangle);
+
+/* 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)
+{
+ gx_device_memory * const mdev = (gx_device_memory *)dev;
+ 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..e0df0e13f
--- /dev/null
+++ b/pstoraster/gdevm4.c
@@ -0,0 +1,319 @@
+/* Copyright (C) 1992, 1995, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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 */
+
+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 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)
+{
+ gx_device_memory * const mdev = (gx_device_memory *)dev;
+
+ 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)
+{
+ gx_device_memory * const mdev = (gx_device_memory *)dev;
+ const byte *line;
+ declare_scan_ptr(dest);
+ byte invert, bb;
+
+ fit_copy(dev, base, sourcex, sraster, id, x, y, w, h);
+ setup_rect(dest);
+ line = base + (sourcex >> 3);
+ /* Divide into opaque and masked cases. */
+ if (one == gx_no_color_index) {
+ if (zero == gx_no_color_index)
+ return 0; /* nothing to do */
+ invert = 0xff;
+ bb = ((byte) zero << 4) | (byte) zero;
+ } else if (zero == gx_no_color_index) {
+ invert = 0;
+ bb = ((byte) one << 4) | (byte) one;
+ } else {
+ /* Opaque case. */
+ int shift = ~(sourcex ^ x) & 1;
+ byte oz[4];
+
+ oz[0] = (byte)((zero << 4) | zero);
+ oz[1] = (byte)((zero << 4) | one);
+ oz[2] = (byte)((one << 4) | zero);
+ oz[3] = (byte)((one << 4) | one);
+ do {
+ register byte *dptr = (byte *) dest;
+ const byte *sptr = line;
+ register uint sbyte = *sptr++;
+ register int sbit = ~sourcex & 7;
+ int count = w;
+
+ /*
+ * If the first source bit corresponds to an odd X in the
+ * destination, process it now.
+ */
+ if (x & 1) {
+ *dptr = (*dptr & 0xf0) |
+ ((sbyte >> sbit) & 1 ? one : zero);
+ --count; /* may now be 0 */
+ if (--sbit < 0)
+ sbit = 7, sbyte = *sptr++;
+ ++dptr;
+ }
+ /*
+ * Now we know the next destination X is even. We want to
+ * process 2 source bits at a time from now on, so set things up
+ * properly depending on whether the next source X (bit) is even
+ * or odd. In both even and odd cases, the active source bits
+ * are in bits 8..1 of sbyte.
+ */
+ sbyte <<= shift;
+ sbit += shift - 1;
+ /*
+ * Now bit # sbit+1 is the most significant unprocessed bit
+ * in sbyte. -1 <= sbit <= 7; sbit is odd.
+ * Note that if sbit = -1, all of sbyte has been processed.
+ *
+ * Continue processing pairs of bits in the first source byte.
+ */
+ while (count >= 2 && sbit >= 0) {
+ *dptr++ = oz[(sbyte >> sbit) & 3];
+ sbit -= 2, count -= 2;
+ }
+ /*
+ * Now sbit = -1 iff we have processed the entire first source
+ * byte.
+ *
+ * Process full source bytes.
+ */
+ if (shift) {
+ sbyte >>= 1; /* in case count < 8 */
+ for (; count >= 8; dptr += 4, count -= 8) {
+ sbyte = *sptr++;
+ dptr[0] = oz[sbyte >> 6];
+ dptr[1] = oz[(sbyte >> 4) & 3];
+ dptr[2] = oz[(sbyte >> 2) & 3];
+ dptr[3] = oz[sbyte & 3];
+ }
+ sbyte <<= 1;
+ } else {
+ for (; count >= 8; dptr += 4, count -= 8) {
+ sbyte = (sbyte << 8) | *sptr++;
+ dptr[0] = oz[(sbyte >> 7) & 3];
+ dptr[1] = oz[(sbyte >> 5) & 3];
+ dptr[2] = oz[(sbyte >> 3) & 3];
+ dptr[3] = oz[(sbyte >> 1) & 3];
+ }
+ }
+ if (!count)
+ continue;
+ /*
+ * Process pairs of bits in the final source byte. Note that
+ * if sbit > 0, this is still the first source byte (the
+ * full-byte loop wasn't executed).
+ */
+ if (sbit < 0) {
+ sbyte = (sbyte << 8) | (*sptr << shift);
+ sbit = 7;
+ }
+ while (count >= 2) {
+ *dptr++ = oz[(sbyte >> sbit) & 3];
+ sbit -= 2, count -= 2;
+ }
+ /*
+ * If the final source bit corresponds to an even X value,
+ * process it now.
+ */
+ if (count) {
+ *dptr = (*dptr & 0x0f) |
+ (((sbyte >> sbit) & 2 ? one : zero) << 4);
+ }
+ } while ((line += sraster, inc_ptr(dest, draster), --h) > 0);
+ return 0;
+ }
+ /* Masked case. */
+ do {
+ register byte *dptr = (byte *) dest;
+ const byte *sptr = line;
+ register int sbyte = *sptr++ ^ invert;
+ register int sbit = 0x80 >> (sourcex & 7);
+ register byte mask = (x & 1 ? 0x0f : 0xf0);
+ int count = w;
+
+ do {
+ if (sbyte & sbit)
+ *dptr = (*dptr & ~mask) | (bb & mask);
+ if ((sbit >>= 1) == 0)
+ sbit = 0x80, sbyte = *sptr++ ^ invert;
+ dptr += (mask = ~mask) >> 7;
+ } while (--count > 0);
+ line += sraster;
+ inc_ptr(dest, draster);
+ } while (--h > 0);
+ 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)
+{
+ /* Use monobit copy_mono. */
+ int code;
+
+ /* 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 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,
+ gx_default_map_cmyk_color, gx_default_strip_tile_rectangle,
+ gx_no_strip_copy_rop, mem_word_get_bits_rectangle);
+
+/* 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)
+{
+ gx_device_memory * const mdev = (gx_device_memory *)dev;
+ 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)
+{
+ gx_device_memory * const mdev = (gx_device_memory *)dev;
+ 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..875bcaddb
--- /dev/null
+++ b/pstoraster/gdevm8.c
@@ -0,0 +1,247 @@
+/* Copyright (C) 1994, 1995, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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 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)
+{
+ gx_device_memory * const mdev = (gx_device_memory *)dev;
+
+ 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)
+{
+ gx_device_memory * const mdev = (gx_device_memory *)dev;
+ 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 */
+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)
+{
+ gx_device_memory * const mdev = (gx_device_memory *)dev;
+
+ 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 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,
+ gx_default_map_cmyk_color, gx_default_strip_tile_rectangle,
+ gx_no_strip_copy_rop, mem_word_get_bits_rectangle);
+
+/* 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)
+{
+ gx_device_memory * const mdev = (gx_device_memory *)dev;
+ 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)
+{
+ gx_device_memory * const mdev = (gx_device_memory *)dev;
+ 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)
+{
+ gx_device_memory * const mdev = (gx_device_memory *)dev;
+ 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..231e41920
--- /dev/null
+++ b/pstoraster/gdevmem.c
@@ -0,0 +1,498 @@
+/* Copyright (C) 1989, 1995, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Generic "memory" (stored bitmap) device */
+#include "memory_.h"
+#include "gx.h"
+#include "gserrors.h"
+#include "gsrect.h"
+#include "gsstruct.h"
+#include "gxarith.h"
+#include "gxdevice.h"
+#include "gxgetbit.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 ENUM_USING(st_device_forward, vptr, sizeof(gx_device_forward), index - 2);
+}
+case 0:
+ENUM_RETURN((mptr->foreign_bits ? NULL : (void *)mptr->base));
+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);
+ RELOC_USING(st_device_forward, vptr, sizeof(gx_device_forward));
+}
+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)
+{
+ gx_device_init((gx_device *) dev, (const gx_device *)mdproto,
+ mem, true);
+ dev->stype = &st_device_memory;
+ switch (page_device) {
+ case -1:
+ set_dev_proc(dev, get_page_device, gx_default_get_page_device);
+ break;
+ case 1:
+ set_dev_proc(dev, 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)
+{
+ gx_device_memory * const mdev = (gx_device_memory *)dev;
+
+ *dev = mem_mono_device;
+ dev->memory = mem;
+ set_dev_proc(dev, get_page_device, gx_default_get_page_device);
+ mdev->target = target;
+ gdev_mem_mono_set_inverted(dev, true);
+ rc_init(dev, mem, 0);
+}
+
+
+/* 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, int width, int height)
+{
+ return round_up((ulong) height *
+ bitmap_raster(width * dev->color_info.depth),
+ max(align_bitmap_mod, arch_align_ptr_mod));
+}
+ulong
+gdev_mem_data_size(const gx_device_memory * dev, int width, int height)
+{
+ return mem_bitmap_bits_size(dev, width, height) +
+ (ulong) height *sizeof(byte *);
+
+}
+/*
+ * Do the inverse computation: given a width (in pixels) and a buffer size,
+ * compute the maximum height.
+ */
+int
+gdev_mem_max_height(const gx_device_memory * dev, int width, ulong size)
+{
+ ulong max_height = size /
+ (bitmap_raster(width * dev->color_info.depth) + sizeof(byte *));
+ int height = (int)min(max_height, max_int);
+
+ /*
+ * Because of alignment rounding, the just-computed height might
+ * be too large by a small amount. Adjust it the easy way.
+ */
+ while (gdev_mem_data_size(dev, width, height) > size)
+ --height;
+ return height;
+}
+
+/* Open a memory device, allocating the data area if appropriate, */
+/* and create the scan line table. */
+private void mem_set_line_ptrs(P4(gx_device_memory *, byte **, byte *, int));
+int
+mem_open(gx_device * dev)
+{
+ return gdev_mem_open_scan_lines((gx_device_memory *)dev, dev->height);
+}
+int
+gdev_mem_open_scan_lines(gx_device_memory *mdev, int setup_height)
+{
+ if (setup_height < 0 || setup_height > mdev->height)
+ return_error(gs_error_rangecheck);
+ 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->width,
+ mdev->height)),
+ mdev->base, setup_height);
+ return 0;
+}
+/* Set up the scan line pointers of a memory device. */
+/* Sets line_ptrs, base, raster; uses width, color_info.depth. */
+private void
+mem_set_line_ptrs(gx_device_memory * mdev, byte ** line_ptrs, byte * base,
+ int count /* >= 0 */)
+{
+ byte **pptr = mdev->line_ptrs = line_ptrs;
+ byte **pend = pptr + count;
+ byte *scan_line = mdev->base = base;
+ uint raster = mdev->raster = gdev_mem_raster(mdev);
+
+ 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)
+{
+ gx_device_memory * const mdev = (gx_device_memory *)dev;
+
+ 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)
+{
+ gx_device_memory * const mdev = (gx_device_memory *)dev;
+
+ if (mdev->bitmap_memory != 0)
+ gs_free_object(mdev->bitmap_memory, mdev->base, "mem_close");
+ return 0;
+}
+
+/* Copy bits to a client. */
+#undef chunk
+#define chunk byte
+int
+mem_get_bits_rectangle(gx_device * dev, const gs_int_rect * prect,
+ gs_get_bits_params_t * params, gs_int_rect ** unread)
+{
+ gx_device_memory * const mdev = (gx_device_memory *)dev;
+ gs_get_bits_options_t options = params->options;
+ int x = prect->p.x, w = prect->q.x - x, y = prect->p.y, h = prect->q.y - y;
+
+ if (options == 0) {
+ params->options =
+ (GB_ALIGN_STANDARD | GB_ALIGN_ANY) |
+ (GB_RETURN_COPY | GB_RETURN_POINTER) |
+ (GB_OFFSET_0 | GB_OFFSET_SPECIFIED | GB_OFFSET_ANY) |
+ (GB_RASTER_STANDARD | GB_RASTER_SPECIFIED | GB_RASTER_ANY) |
+ GB_PACKING_CHUNKY | GB_COLORS_NATIVE | GB_ALPHA_NONE;
+ return_error(gs_error_rangecheck);
+ }
+ if ((w <= 0) | (h <= 0)) {
+ if ((w | h) < 0)
+ return_error(gs_error_rangecheck);
+ return 0;
+ }
+ if (x < 0 || w > dev->width - x ||
+ y < 0 || h > dev->height - y
+ )
+ return_error(gs_error_rangecheck);
+ {
+ byte *base = scan_line_base(mdev, y);
+ int code = gx_get_bits_return_pointer(dev, x, h, params,
+ GB_COLORS_NATIVE | GB_PACKING_CHUNKY |
+ GB_ALPHA_NONE, base);
+
+ if (code >= 0)
+ return code;
+ return gx_get_bits_copy(dev, x, w, h, params,
+ GB_COLORS_NATIVE | GB_PACKING_CHUNKY |
+ GB_ALPHA_NONE, base,
+ gx_device_raster(dev, true));
+ }
+}
+
+#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.
+ * Note that the coordinates are specified in bits, not in terms of the
+ * actual device depth.
+ */
+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 rectangle to the client, swapping bytes as needed. */
+int
+mem_word_get_bits_rectangle(gx_device * dev, const gs_int_rect * prect,
+ gs_get_bits_params_t * params, gs_int_rect ** unread)
+{
+ gx_device_memory * const mdev = (gx_device_memory *)dev;
+ byte *src;
+ uint dev_raster = gx_device_raster(dev, 1);
+ int x = prect->p.x;
+ int w = prect->q.x - x;
+ int y = prect->p.y;
+ int h = prect->q.y - y;
+ int bit_x, bit_w;
+ int code;
+
+ fit_fill_xywh(dev, x, y, w, h);
+ if (w <= 0 || h <= 0) {
+ /*
+ * It's easiest to just keep going with an empty rectangle.
+ * We pass the original rectangle to mem_get_bits_rectangle,
+ * so unread will be filled in correctly.
+ */
+ x = y = w = h = 0;
+ }
+ bit_x = x * dev->color_info.depth;
+ bit_w = w * dev->color_info.depth;
+ src = scan_line_base(mdev, y);
+ mem_swap_byte_rect(src, dev_raster, bit_x, bit_w, h, false);
+ code = mem_get_bits_rectangle(dev, prect, params, unread);
+ mem_swap_byte_rect(src, dev_raster, bit_x, bit_w, h, false);
+ return code;
+}
+
+#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)
+{
+ gx_device_memory * const mdev = (gx_device_memory *)dev;
+ 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])
+{
+ gx_device_memory * const mdev = (gx_device_memory *)dev;
+ 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..17dc5c059
--- /dev/null
+++ b/pstoraster/gdevmem.h
@@ -0,0 +1,233 @@
+/* Copyright (C) 1991, 1995, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Private definitions for memory devices. */
+
+#ifndef gdevmem_INCLUDED
+# define gdevmem_INCLUDED
+
+#include "gxbitops.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_VARS(ptr, chunk *, draster)
+#define DECLARE_SCAN_PTR_VARS(ptr, ptype, draster)\
+ register ptype ptr;\
+ uint draster
+#define setup_rect(ptr)\
+ SETUP_RECT_VARS(ptr, chunk *, draster)
+#define SETUP_RECT_VARS(ptr, ptype, draster)\
+ 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_rectangle(mem_get_bits_rectangle);
+/* The following are for word-oriented devices. */
+#if arch_is_big_endian
+# define mem_word_get_bits_rectangle mem_get_bits_rectangle
+#else
+dev_proc_get_bits_rectangle(mem_word_get_bits_rectangle);
+#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, map_cmyk_color, copy_alpha, strip_tile_rectangle, strip_copy_rop, get_bits_rectangle)\
+{ 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,\
+ gx_default_get_bits,\
+ 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 */\
+ gx_default_get_clipping_box,\
+ gx_default_begin_typed_image,\
+ get_bits_rectangle, /* differs */\
+ gx_default_map_color_rgb_alpha,\
+ gx_default_create_compositor,\
+ gx_default_get_hardware_params,\
+ gx_default_text_begin\
+ },\
+ 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, map_cmyk_color, strip_tile_rectangle, strip_copy_rop, get_bits_rectangle)\
+ mem_full_alpha_device(name, rgb_depth, gray_depth, open, map_rgb_color,\
+ map_color_rgb, copy_mono, copy_color, fill_rectangle,\
+ map_cmyk_color, gx_default_copy_alpha,\
+ strip_tile_rectangle, strip_copy_rop,\
+ get_bits_rectangle)
+#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,\
+ gx_default_map_cmyk_color, gx_default_strip_tile_rectangle,\
+ strip_copy_rop, mem_get_bits_rectangle)
+
+/* 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)
+
+/* ------ Implementations ------ */
+
+extern const gx_device_memory mem_mono_device;
+extern const gx_device_memory mem_mapped2_device;
+extern const gx_device_memory mem_mapped4_device;
+extern const gx_device_memory mem_mapped8_device;
+extern const gx_device_memory mem_true16_device;
+extern const gx_device_memory mem_true24_device;
+extern const gx_device_memory mem_true32_device;
+extern const gx_device_memory 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 mem_mono_word_device;
+extern const gx_device_memory mem_mapped2_word_device;
+extern const gx_device_memory mem_mapped4_word_device;
+extern const gx_device_memory mem_mapped8_word_device;
+extern const gx_device_memory mem_true24_word_device;
+extern const gx_device_memory 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 */
+
+#endif /* gdevmem_INCLUDED */
diff --git a/pstoraster/gdevmgr.h b/pstoraster/gdevmgr.h
new file mode 100644
index 000000000..0cab14414
--- /dev/null
+++ b/pstoraster/gdevmgr.h
@@ -0,0 +1,127 @@
+/* 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 supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$*/
+/* Common header file for MGR devices */
+
+#ifndef gdevmgr_INCLUDED
+# define gdevmgr_INCLUDED
+
+#define MGR_RESERVEDCOLORS 16
+
+/* Color mapping routines for 8-bit color (with a fixed palette). */
+dev_proc_map_rgb_color(mgr_8bit_map_rgb_color);
+dev_proc_map_color_rgb(mgr_8bit_map_color_rgb);
+
+
+/* extract from dump.h */
+
+/*
+ * format for saved bitmaps
+ */
+
+#define B_PUTHDR8(hdr, w, h, d) ( \
+ (hdr)->magic[0] = 'y', (hdr)->magic[1] = 'z', \
+ (hdr)->h_wide = (((w) >> 6) & 0x3f) + ' ', \
+ (hdr)->l_wide = ((w) & 0x3f) + ' ', \
+ (hdr)->h_high = (((h) >> 6) & 0x3f) + ' ', \
+ (hdr)->l_high = ((h) & 0x3f) + ' ', \
+ (hdr)->depth = ((d) & 0x3f) + ' ', \
+ (hdr)->_reserved = ' ' )
+
+struct b_header {
+ char magic[2]; /* magics */
+ char h_wide; /* upper byte width (biased with 0x20) */
+ char l_wide; /* lower byte width (biased with 0x20) */
+ char h_high; /* upper byte height (biased with 0x20) */
+ char l_high; /* lower byte height (biased with 0x20) */
+ char depth; /* depth (biased with 0x20) */
+ char _reserved; /* for alignment */
+};
+
+/*
+ * Color lookup table information
+ */
+struct nclut {
+ unsigned short colnum;
+ unsigned short red, green, blue;
+} ;
+
+
+/* extract from color.h */
+
+/*
+ * MGR Color Definitions
+ */
+
+#define LUT_BW 0
+#define LUT_GREY 1
+#define LUT_BGREY 2
+#define LUT_VGA 3
+#define LUT_BCT 4
+#define LUT_USER 5
+#define LUT 6
+#define LUT_8 LUT
+
+#define RGB_RED 0
+#define RGB_GREEN 1
+#define RGB_BLUE 2
+#define RGB 3
+
+#define LUTENTRIES 16
+
+#define BW_RED 15, 0, 15, 0, 15, 0, 15, 0, 15, 0, 15, 0, 15, 0, 15, 0
+#define BW_GREEN BW_RED
+#define BW_BLUE BW_RED
+
+#define GREY_RED 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15
+#define GREY_GREEN GREY_RED
+#define GREY_BLUE GREY_RED
+
+#define BGREY_RED 1, 0, 2, 8, 4, 3, 13, 11, 7, 6, 10, 12, 14, 5, 9, 15
+#define BGREY_GREEN BGREY_RED
+#define BGREY_BLUE BGREY_RED
+
+#define VGA_RED 0, 0, 0, 0, 8, 8, 8, 12, 8, 0, 0, 0, 15, 15, 15, 15
+#define VGA_GREEN 0, 0, 8, 8, 0, 0, 8, 12, 8, 0, 15, 15, 0, 0, 15, 15
+#define VGA_BLUE 0, 8, 0, 8, 0, 8, 0, 12, 8, 15, 0, 15, 0, 15, 0, 15
+
+#define BCT_RED 1, 7, 6, 15, 14, 3, 13, 11, 7, 13, 13, 15, 15, 5, 9, 15
+#define BCT_GREEN 1, 7, 13, 12, 5, 3, 13, 11, 7, 14, 15, 15, 14, 5, 9, 15
+#define BCT_BLUE 1, 14, 6, 8, 5, 3, 13, 11, 7, 15, 14, 12, 13, 5, 9, 15
+
+#define USER_RED 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+#define USER_GREEN USER_RED
+#define USER_BLUE USER_RED
+
+static char mgrlut[LUT][RGB][LUTENTRIES] = {
+ { { BW_RED }, { BW_GREEN }, { BW_BLUE } },
+ { { GREY_RED }, { GREY_GREEN }, { GREY_BLUE } },
+ { { BGREY_RED }, { BGREY_GREEN }, { BGREY_BLUE } },
+ { { VGA_RED }, { VGA_GREEN }, { VGA_BLUE } },
+ { { BCT_RED }, { BCT_GREEN }, { BCT_BLUE } },
+ { { USER_RED }, { USER_GREEN }, { USER_BLUE } }
+};
+
+#endif /* gdevmgr_INCLUDED */
diff --git a/pstoraster/gdevmpla.c b/pstoraster/gdevmpla.c
new file mode 100644
index 000000000..239ffbcc8
--- /dev/null
+++ b/pstoraster/gdevmpla.c
@@ -0,0 +1,200 @@
+/* Copyright (C) 1993, 1994, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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_rectangle(mem_planar_get_bits_rectangle);
+const gx_device_memory 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,
+ gx_default_map_cmyk_color, gx_default_strip_tile_rectangle,
+ gx_no_strip_copy_rop, mem_planar_get_bits_rectangle);
+
+/* 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)
+{
+ gx_device_memory * const mdev = (gx_device_memory *)dev;
+ 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)
+{
+ gx_device_memory * const mdev = (gx_device_memory *)dev;
+ 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)
+{
+ gx_device_memory * const mdev = (gx_device_memory *)dev;
+ 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_rectangle(gx_device * dev, const gs_int_rect * prect,
+ gs_get_bits_params_t * params, gs_int_rect ** unread)
+{
+ return_error(-1);
+}
diff --git a/pstoraster/gdevmrop.h b/pstoraster/gdevmrop.h
new file mode 100644
index 000000000..3c143c921
--- /dev/null
+++ b/pstoraster/gdevmrop.h
@@ -0,0 +1,97 @@
+/* Copyright (C) 1995, 1996, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Definitions for device RasterOp implementations. */
+/* Requires gxdevmem.h, gsropt.h */
+
+#ifndef gdevmrop_INCLUDED
+# define gdevmrop_INCLUDED
+
+/* Define the table of RasterOp implementation procedures. */
+extern const rop_proc rop_proc_table[256];
+
+/* Define the table of RasterOp operand usage. */
+extern const byte /*rop_usage_t */ rop_usage_table[256];
+
+/*
+ * Compute the effective RasterOp for the 1-bit case,
+ * taking transparency into account.
+ */
+gs_rop3_t gs_transparent_rop(P1(gs_logical_operation_t lop));
+
+#ifdef DEBUG
+/* Trace a [strip_]copy_rop call. */
+void trace_copy_rop(P16(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));
+#endif
+
+/*
+ * 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 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;
+ gx_device_color texture;
+};
+
+#define private_st_device_rop_texture() /* in gdevrops.c */\
+ gs_private_st_composite(st_device_rop_texture, gx_device_rop_texture,\
+ "gx_device_rop_texture", device_rop_texture_enum_ptrs, device_rop_texture_reloc_ptrs)
+
+/* Create a RasterOp source device. */
+int gx_alloc_rop_texture_device(P3(gx_device_rop_texture ** prsdev,
+ gs_memory_t * mem,
+ client_name_t cname));
+
+/* 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));
+
+#endif /* gdevmrop_INCLUDED */
diff --git a/pstoraster/gdevnfwd.c b/pstoraster/gdevnfwd.c
new file mode 100644
index 000000000..e8cbb0fd9
--- /dev/null
+++ b/pstoraster/gdevnfwd.c
@@ -0,0 +1,797 @@
+/* Copyright (C) 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Null and forwarding device implementation */
+#include "gx.h"
+#include "gserrors.h"
+#include "gxdevice.h"
+
+/* ---------------- Forwarding procedures ---------------- */
+
+/* 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);
+ 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);
+ /* NOT image_data (OBSOLETE) */
+ /* NOT end_image (OBSOLETE) */
+ /* NOT strip_tile_rectangle */
+ fill_dev_proc(dev, strip_copy_rop, gx_forward_strip_copy_rop);
+ fill_dev_proc(dev, get_clipping_box, gx_forward_get_clipping_box);
+ fill_dev_proc(dev, begin_typed_image, gx_forward_begin_typed_image);
+ fill_dev_proc(dev, get_bits_rectangle, gx_forward_get_bits_rectangle);
+ fill_dev_proc(dev, map_color_rgb_alpha, gx_forward_map_color_rgb_alpha);
+ fill_dev_proc(dev, create_compositor, gx_no_create_compositor);
+ fill_dev_proc(dev, get_hardware_params, gx_forward_get_hardware_params);
+ fill_dev_proc(dev, text_begin, gx_forward_text_begin);
+ 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);
+ set_dev_proc(dev, map_color_rgb_alpha, gx_forward_map_color_rgb_alpha);
+}
+
+void
+gx_forward_get_initial_matrix(gx_device * dev, gs_matrix * pmat)
+{
+ gx_device_forward * const fdev = (gx_device_forward *)dev;
+ 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_forward * const fdev = (gx_device_forward *)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_forward * const fdev = (gx_device_forward *)dev;
+ 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_forward * const fdev = (gx_device_forward *)dev;
+ 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_forward * const fdev = (gx_device_forward *)dev;
+ 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_fill_rectangle(gx_device * dev, int x, int y, int w, int h,
+ gx_color_index color)
+{
+ gx_device_forward * const fdev = (gx_device_forward *)dev;
+ gx_device *tdev = fdev->target;
+
+ if (tdev == 0)
+ return_error(gs_error_Fatal);
+ return (*dev_proc(tdev, fill_rectangle)) (tdev, x, y, w, h, color);
+}
+
+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_forward * const fdev = (gx_device_forward *)dev;
+ 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_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)
+{
+ gx_device_forward * const fdev = (gx_device_forward *)dev;
+ gx_device *tdev = fdev->target;
+
+ if (tdev == 0)
+ return_error(gs_error_Fatal);
+ return (*dev_proc(tdev, copy_mono))
+ (tdev, data, dx, raster, id, x, y, w, h, zero, one);
+}
+
+int
+gx_forward_copy_color(gx_device * dev, const byte * data,
+ int dx, int raster, gx_bitmap_id id, int x, int y, int w, int h)
+{
+ gx_device_forward * const fdev = (gx_device_forward *)dev;
+ gx_device *tdev = fdev->target;
+
+ if (tdev == 0)
+ return_error(gs_error_Fatal);
+ return (*dev_proc(tdev, copy_color))
+ (tdev, data, dx, raster, id, x, y, w, h);
+}
+
+int
+gx_forward_get_bits(gx_device * dev, int y, byte * data, byte ** actual_data)
+{
+ gx_device_forward * const fdev = (gx_device_forward *)dev;
+ 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_forward * const fdev = (gx_device_forward *)dev;
+ 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_forward * const fdev = (gx_device_forward *)dev;
+ 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_forward * const fdev = (gx_device_forward *)dev;
+ 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));
+}
+
+const gx_xfont_procs *
+gx_forward_get_xfont_procs(gx_device * dev)
+{
+ gx_device_forward * const fdev = (gx_device_forward *)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_forward * const fdev = (gx_device_forward *)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_forward * const fdev = (gx_device_forward *)dev;
+ 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_forward * const fdev = (gx_device_forward *)dev;
+ gx_device *tdev = fdev->target;
+ gx_device *pdev;
+
+ if (tdev == 0)
+ return gx_default_get_page_device(dev);
+ pdev = (*dev_proc(tdev, get_page_device)) (tdev);
+ return (pdev == tdev ? dev : pdev);
+}
+
+int
+gx_forward_get_alpha_bits(gx_device * dev, graphics_object_type type)
+{
+ gx_device_forward * const fdev = (gx_device_forward *)dev;
+ 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_forward * const fdev = (gx_device_forward *)dev;
+ 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_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_forward * const fdev = (gx_device_forward *)dev;
+ gx_device *tdev = fdev->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_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_forward * const fdev = (gx_device_forward *)dev;
+ 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_forward * const fdev = (gx_device_forward *)dev;
+ 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_forward * const fdev = (gx_device_forward *)dev;
+ 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) (tdev, 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_forward * const fdev = (gx_device_forward *)dev;
+ 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_forward * const fdev = (gx_device_forward *)dev;
+ 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_forward * const fdev = (gx_device_forward *)dev;
+ 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_forward * const fdev = (gx_device_forward *)dev;
+ 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, const gs_int_rect * prect,
+ const gx_drawing_color * pdcolor, const gx_clip_path * pcpath,
+ gs_memory_t * memory, gx_image_enum_common_t ** pinfo)
+{
+ gx_device_forward * const fdev = (gx_device_forward *)dev;
+ 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, prect, pdcolor, pcpath,
+ memory, pinfo);
+}
+
+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_forward * const fdev = (gx_device_forward *)dev;
+ 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);
+}
+
+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_forward * const fdev = (gx_device_forward *)dev;
+ gx_device *tdev = fdev->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);
+}
+
+void
+gx_forward_get_clipping_box(gx_device * dev, gs_fixed_rect * pbox)
+{
+ gx_device_forward * const fdev = (gx_device_forward *)dev;
+ gx_device *tdev = fdev->target;
+
+ if (tdev == 0)
+ gx_default_get_clipping_box(dev, pbox);
+ else
+ (*dev_proc(tdev, get_clipping_box)) (tdev, pbox);
+}
+
+int
+gx_forward_begin_typed_image(gx_device * dev,
+ const gs_imager_state * pis, const gs_matrix * pmat,
+ const gs_image_common_t * pim, const gs_int_rect * prect,
+ const gx_drawing_color * pdcolor, const gx_clip_path * pcpath,
+ gs_memory_t * memory, gx_image_enum_common_t ** pinfo)
+{
+ gx_device_forward * const fdev = (gx_device_forward *)dev;
+ gx_device *tdev = fdev->target;
+
+ dev_proc_begin_typed_image((*proc));
+
+ if (tdev == 0)
+ tdev = dev, proc = gx_default_begin_typed_image;
+ else
+ proc = dev_proc(tdev, begin_typed_image);
+ return (*proc) (tdev, pis, pmat, pim, prect, pdcolor, pcpath,
+ memory, pinfo);
+}
+
+int
+gx_forward_get_bits_rectangle(gx_device * dev, const gs_int_rect * prect,
+ gs_get_bits_params_t * params, gs_int_rect ** unread)
+{
+ gx_device_forward * const fdev = (gx_device_forward *)dev;
+ gx_device *tdev = fdev->target;
+
+ dev_proc_get_bits_rectangle((*proc));
+
+ if (tdev == 0)
+ tdev = dev, proc = gx_default_get_bits_rectangle;
+ else
+ proc = dev_proc(tdev, get_bits_rectangle);
+ return (*proc) (tdev, prect, params, unread);
+}
+
+int
+gx_forward_map_color_rgb_alpha(gx_device * dev, gx_color_index color,
+ gx_color_value prgba[4])
+{
+ gx_device_forward * const fdev = (gx_device_forward *)dev;
+ gx_device *tdev = fdev->target;
+
+ return (tdev == 0 ? gx_default_map_color_rgb_alpha(dev, color, prgba) :
+ (*dev_proc(tdev, map_color_rgb_alpha)) (tdev, color, prgba));
+}
+
+int
+gx_forward_get_hardware_params(gx_device * dev, gs_param_list * plist)
+{
+ gx_device_forward * const fdev = (gx_device_forward *)dev;
+ gx_device *tdev = fdev->target;
+
+ return (tdev == 0 ? gx_default_get_hardware_params(dev, plist) :
+ (*dev_proc(tdev, get_hardware_params)) (tdev, plist));
+}
+
+int
+gx_forward_text_begin(gx_device * dev, gs_imager_state * pis,
+ const gs_text_params_t * text, const gs_font * font,
+gx_path * path, const gx_device_color * pdcolor, const gx_clip_path * pcpath,
+ gs_memory_t * memory, gs_text_enum_t ** ppenum)
+{
+ gx_device_forward * const fdev = (gx_device_forward *)dev;
+ gx_device *tdev = fdev->target;
+
+ dev_proc_text_begin((*proc));
+
+ if (tdev == 0)
+ tdev = dev, proc = gx_default_text_begin;
+ else
+ proc = dev_proc(tdev, text_begin);
+ return (*proc) (tdev, pis, text, font, path, pdcolor, pcpath,
+ memory, ppenum);
+}
+
+/* ---------------- 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);
+
+/* We would like to have null implementations of begin/data/end image, */
+/* but we can't do this, because image_data must keep track of the */
+/* Y position so it can return 1 when done. */
+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,\
+ gx_default_begin_image,\
+ gx_default_image_data,\
+ gx_default_end_image,\
+ gx_default_strip_tile_rectangle,\
+ null_strip_copy_rop,\
+ gx_default_get_clipping_box,\
+ gx_default_begin_typed_image,\
+ gx_default_get_bits_rectangle,\
+ gx_forward_map_color_rgb_alpha,\
+ gx_non_imaging_create_compositor,\
+ gx_forward_get_hardware_params,\
+ gx_default_text_begin\
+}
+
+const gx_device_null gs_null_device =
+{
+ std_device_std_body_type_open(gx_device_null, 0, "null", &st_device_null,
+ 0, 0, 72, 72),
+ null_procs(gx_default_get_page_device /* not a page device */ ),
+ 0 /* target */
+};
+
+const gx_device_null gs_nullpage_device =
+{
+std_device_std_body_type_open(gx_device_null, 0, "nullpage", &st_device_null,
+ 72 /*nominal */ , 72 /*nominal */ , 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)
+{
+ /*
+ * If this is not a page device, we must defeat attempts to reset
+ * the size; otherwise this is equivalent to gx_forward_put_params.
+ */
+ gx_device_forward * const fdev = (gx_device_forward *)dev;
+ 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 || (*dev_proc(dev, get_page_device)) (dev) == dev)
+ 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_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;
+}
diff --git a/pstoraster/gdevpccm.h b/pstoraster/gdevpccm.h
new file mode 100644
index 000000000..1322594d2
--- /dev/null
+++ b/pstoraster/gdevpccm.h
@@ -0,0 +1,44 @@
+/* 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 supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Requires gxdevice.h */
+
+#ifndef gdevpccm_INCLUDED
+# define gdevpccm_INCLUDED
+
+/* Color mapping routines for EGA/VGA-style color. */
+dev_proc_map_rgb_color(pc_4bit_map_rgb_color);
+dev_proc_map_color_rgb(pc_4bit_map_color_rgb);
+#define dci_pc_4bit { 3, 4, 3, 2, 4, 3 }
+
+/* Color mapping routines for 8-bit color (with a fixed palette). */
+dev_proc_map_rgb_color(pc_8bit_map_rgb_color);
+dev_proc_map_color_rgb(pc_8bit_map_color_rgb);
+#define dci_pc_8bit { 3, 8, 6, 6, 7, 7 }
+
+/* Write the palette on a file. */
+int pc_write_palette(P3(gx_device *, uint, FILE *));
+
+#endif /* gdevpccm_INCLUDED */
diff --git a/pstoraster/gdevpcfb.h b/pstoraster/gdevpcfb.h
new file mode 100644
index 000000000..21bde1bb9
--- /dev/null
+++ b/pstoraster/gdevpcfb.h
@@ -0,0 +1,209 @@
+/* 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 supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* IBM PC frame buffer definitions */
+
+#ifndef gdevpcfb_INCLUDED
+# define gdevpcfb_INCLUDED
+
+#ifdef __MSDOS__
+# include "dos_.h"
+typedef union REGS registers;
+
+#endif
+
+/* For testing, the 16-color display may be defined as a monochrome, */
+/* 8-color, or 16-color device. */
+#define ega_bits_of_color 2 /* 0, 1, or 2 */
+#define rgb_max ega_bits_of_color
+
+/* Define the short (integer) version of "transparent" color. */
+/* ****** Depends on gx_no_color_index being all 1's. ***** */
+#define no_color ((int)gx_no_color_index)
+
+/* Procedures */
+
+ /* See gxdevice.h for the definitions of the procedures. */
+
+dev_proc_open_device(ega_open);
+dev_proc_close_device(ega_close);
+dev_proc_fill_rectangle(ega_fill_rectangle);
+dev_proc_tile_rectangle(ega_tile_rectangle);
+dev_proc_copy_mono(ega_copy_mono);
+dev_proc_copy_color(ega_copy_color);
+dev_proc_get_bits(ega_get_bits);
+
+/* Structure for saving state of BIOS variables. */
+typedef struct pcfb_bios_state_s {
+ int display_mode; /* must be first, see pcfb_save_state */
+ /* in gdevpcfb.c */
+ byte text_page;
+ uint text_cursor_mode;
+ uint text_font;
+ byte text_attribute;
+ byte border_color;
+} pcfb_bios_state;
+
+/* Procedures used by gdevpcfb.c */
+void pcfb_set_signals(P1(gx_device *));
+void pcfb_get_state(P1(pcfb_bios_state *));
+void pcfb_set_mode(P1(int));
+void pcfb_set_state(P1(const pcfb_bios_state *));
+
+/* Types for frame buffer pointers. */
+typedef byte *fb_ptr;
+typedef volatile byte *volatile_fb_ptr;
+
+/* Define the nominal page height in inches. */
+#ifdef A4
+# define PAGE_HEIGHT_INCHES 11.69
+#else
+# define PAGE_HEIGHT_INCHES 11.0
+#endif
+
+/* The device descriptor */
+typedef struct gx_device_ega_s gx_device_ega;
+struct gx_device_ega_s {
+ gx_device_common;
+ int raster; /* frame buffer bytes per line */
+ int fb_seg_mult; /* multiplier for segment part */
+ /* of frame buffer pointer */
+ int fb_byte_mult; /* multiplier for word part ditto */
+#define mk_fb_ptr(x, y)\
+ (fb_dev->fb_byte_mult == 0 ?\
+ (fb_ptr)MK_PTR(regen + (y) * (fb_dev->fb_seg_mult), (x) >> 3) :\
+ (fb_ptr)MK_PTR(regen + ((y) >> 4) * (fb_dev->fb_seg_mult),\
+ (((y) & 15) * fb_dev->fb_byte_mult) + ((x) >> 3)))
+ int video_mode;
+};
+
+/* Macro for creating instances */
+/* The initial parameters map an appropriate fraction of */
+/* the screen to a full-page coordinate space. */
+/* This may or may not be what is desired! */
+#define ega_device(dev_name, procs, fb_raster, screen_height, aspect_ratio, video_mode)\
+ { std_device_dci_body(gx_device_ega, &procs, dev_name,\
+ fb_raster * 8, screen_height,\
+ (screen_height * (aspect_ratio)) / PAGE_HEIGHT_INCHES, /* x dpi */\
+ screen_height / PAGE_HEIGHT_INCHES, /* y dpi */\
+ (rgb_max ? 3 : 1), /* num_components */\
+ 4, /* depth */\
+ (rgb_max ? rgb_max : 1), /* max_gray */\
+ rgb_max,\
+ (rgb_max ? rgb_max + 1 : 2), /* dither_grays */\
+ (rgb_max ? rgb_max + 1 : 0) /* dither_colors */\
+ ),\
+ { 0 }, /* std_procs */\
+ fb_raster,\
+ (fb_raster & 15 ? fb_raster : fb_raster >> 4),\
+ (fb_raster & 15 ? fb_raster : 0),\
+ video_mode\
+ }
+
+/* Define the device port and register numbers, and the regen map base */
+#define seq_addr 0x3c4
+#define s_map 2
+#define set_s_map(mask) outport2(seq_addr, s_map, mask)
+#define graph_addr 0x3ce
+#define g_const 0 /* set/reset */
+#define set_g_const(color) outport2(graph_addr, g_const, color)
+#define g_const_map 1 /* enable set/reset */
+#define set_g_const_map(map) outport2(graph_addr, g_const_map, map)
+#define g_function 3
+# define gf_WRITE 0
+# define gf_AND 8
+# define gf_OR 0x10
+# define gf_XOR 0x18
+#define set_g_function(func) outport2(graph_addr, g_function, func)
+#define g_read_plane 4
+#define set_g_read_plane(plane) outport2(graph_addr, g_read_plane, plane)
+#define g_mode 5
+# define gm_DATA 0
+# define gm_FILL 2
+#define set_g_mode(mode) outport2(graph_addr, g_mode, mode)
+#define g_mask 8
+#define set_g_mask(mask) outport2(graph_addr, g_mask, mask)
+#define select_g_mask() outportb(graph_addr, g_mask)
+#define out_g_mask(mask) outportb(graph_addr+1, mask)
+#define regen 0xa000
+
+/* Define access to the frame buffer and the video registers */
+/* according to whether we are on a DOS system or a Unix system. */
+
+#if defined(M_UNIX) || defined(M_XENIX) || defined(UNIX) || defined(SYSV) || defined(__linux__)
+
+ /* SCO Unix/Xenix, AT&T SVR4, or Linux. */
+
+#undef outportb
+
+#if defined(__GNUC__)
+ /* Inline assembly version for gcc */
+ /* Under SCO, requires installing the gnu assembler as "as" */
+static inline void
+outportb(int port, int data)
+{
+ __asm__ volatile ("outb %0,%1"::
+ "a" ((unsigned char)data),
+ "d" ((unsigned short)port));
+}
+static inline void
+outport2(int port, int index, int data)
+{
+ __asm__ volatile ("movb %0,%%ah; movb %1,%%al; outw %%ax,%2"::
+ "qmi" ((unsigned char)data),
+ "qmi" ((unsigned char)index),
+ "d" ((unsigned short)port):
+ "eax");
+}
+#else
+void outportb(P2(uint, byte));
+void outport2(P3(uint, byte, byte));
+
+#endif
+
+/* Redefine mk_fb_ptr -- no segmented addressing. */
+
+#undef mk_fb_ptr
+extern fb_ptr fb_addr;
+
+#define mk_fb_ptr(x, y) (fb_addr + (y) * (fb_dev->raster) + ((x) >> 3))
+
+#else
+
+ /* MS-DOS */
+
+/* outportb is defined in dos_.h */
+#define outport2(port, index, data)\
+ (outportb(port, index), outportb((port)+1, data))
+
+#endif
+
+/* Fetch and discard a byte. Prevent the compiler from */
+/* optimizing this away. */
+static unsigned char byte_discard_;
+
+#define byte_discard(expr) byte_discard_ = (expr)
+
+#endif /* gdevpcfb_INCLUDED */
diff --git a/pstoraster/gdevpcl.h b/pstoraster/gdevpcl.h
new file mode 100644
index 000000000..b6954f159
--- /dev/null
+++ b/pstoraster/gdevpcl.h
@@ -0,0 +1,52 @@
+/* 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 supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Requires gdevprn.h */
+
+#ifndef gdevpcl_INCLUDED
+# define gdevpcl_INCLUDED
+
+/* Define the PCL paper size codes. */
+#define PAPER_SIZE_LETTER 2
+#define PAPER_SIZE_LEGAL 3
+#define PAPER_SIZE_A4 26
+#define PAPER_SIZE_A3 27
+#define PAPER_SIZE_A2 28
+#define PAPER_SIZE_A1 29
+#define PAPER_SIZE_A0 30
+
+/* Get the paper size code, based on width and height. */
+int gdev_pcl_paper_size(P1(gx_device *));
+
+/* Color mapping procedures for 3-bit-per-pixel RGB printers */
+dev_proc_map_rgb_color(gdev_pcl_3bit_map_rgb_color);
+dev_proc_map_color_rgb(gdev_pcl_3bit_map_color_rgb);
+
+/* Row compression routines */
+typedef ulong word;
+int gdev_pcl_mode2compress(P3(const word * row, const word * end_row, byte * compressed)),
+ gdev_pcl_mode3compress(P4(int bytecount, const byte * current, byte * previous, byte * compressed));
+
+#endif /* gdevpcl_INCLUDED */
diff --git a/pstoraster/gdevpdfx.h b/pstoraster/gdevpdfx.h
new file mode 100644
index 000000000..22dbf4dbc
--- /dev/null
+++ b/pstoraster/gdevpdfx.h
@@ -0,0 +1,555 @@
+/* Copyright (C) 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Internal definitions for PDF-writing driver. */
+
+#ifndef gdevpdfx_INCLUDED
+# define gdevpdfx_INCLUDED
+
+#include "gsparam.h"
+#include "gxdevice.h"
+#include "gxline.h"
+#include "stream.h"
+#include "gdevpstr.h"
+#include "gdevpsdf.h"
+
+/* ---------------- Statically allocated sizes ---------------- */
+/* These should all really be dynamic.... */
+
+/* Define the maximum number of contents fragments on a page. */
+#define max_contents_ids 300
+
+/* Define the maximum depth of an outline tree. */
+/* Note that there is no limit on the breadth of the tree. */
+#define max_outline_depth 8
+
+/* Define the maximum size of a destination array string. */
+#define max_dest_string 80
+
+/* ================ Types and structures ================ */
+
+/* ---------------- Resources ---------------- */
+
+typedef enum {
+ /* Standard PDF resources. */
+ resourceFont,
+ resourceEncoding,
+ resourceFontDescriptor,
+ resourceColorSpace,
+ resourceImageXObject,
+ /* Internally used resources. */
+ resourceCharProc,
+ resourceNamedObject,
+ num_resource_types
+} pdf_resource_type;
+
+#define pdf_resource_type_names\
+ "Font", "Encoding", "FontDescriptor", "ColorSpace", "XObject",\
+ 0, 0, 0
+#define pdf_resource_type_structs\
+ &st_pdf_font, &st_pdf_resource, &st_pdf_resource, &st_pdf_resource,\
+ &st_pdf_resource, &st_pdf_char_proc, &st_pdf_named_object
+
+#define pdf_resource_common(typ)\
+ typ *next; /* next resource of this type */\
+ pdf_resource *prev; /* previously allocated resource */\
+ gs_id rid; /* optional key */\
+ long id
+typedef struct pdf_resource_s pdf_resource;
+struct pdf_resource_s {
+ pdf_resource_common(pdf_resource);
+};
+
+#define private_st_pdf_resource()\
+ gs_private_st_ptrs2(st_pdf_resource, pdf_resource, "pdf_resource",\
+ pdf_resource_enum_ptrs, pdf_resource_reloc_ptrs, next, prev)
+
+/* Font resources */
+typedef struct pdf_char_proc_s pdf_char_proc; /* forward reference */
+typedef struct pdf_font_s pdf_font;
+typedef struct pdf_font_name_s {
+ byte chars[40]; /* arbitrary, must be large enough for */
+ /* the 14 built-in fonts */
+ uint size;
+} pdf_font_name;
+struct pdf_font_s {
+ pdf_resource_common(pdf_font);
+ pdf_font_name fname;
+ bool used_on_page;
+ char frname[6 + 1]; /* xxxxxx\0 */
+ /* Encoding differences for base fonts. */
+ byte chars_used[32]; /* 1 bit per character code */
+ gs_const_string *differences;
+ long diff_id;
+ /* Bookkeeping for embedded fonts. */
+ int num_chars;
+#define font_is_embedded(font) ((font)->num_chars != 0)
+ pdf_char_proc *char_procs;
+ int max_y_offset;
+ /* Pseudo-characters for spacing. */
+ /* The range should be determined by the device resolution.... */
+#define x_space_min 24
+#define x_space_max 150
+ byte spaces[x_space_max - x_space_min + 1];
+};
+
+#define private_st_pdf_font()\
+ gs_private_st_suffix_add2(st_pdf_font, pdf_font, "pdf_font",\
+ pdf_font_enum_ptrs, pdf_font_reloc_ptrs, st_pdf_resource,\
+ differences, char_procs)
+
+/* CharProc pseudo-resources for embedded fonts */
+struct pdf_char_proc_s {
+ pdf_resource_common(pdf_char_proc);
+ pdf_font *font;
+ pdf_char_proc *char_next; /* next char_proc for same font */
+ int width, height;
+ int x_width; /* X escapement */
+ int y_offset; /* of character (0,0) */
+ byte char_code;
+};
+
+#define private_st_pdf_char_proc()\
+ gs_private_st_suffix_add2(st_pdf_char_proc, pdf_char_proc,\
+ "pdf_char_proc", pdf_char_proc_enum_ptrs,\
+ pdf_char_proc_reloc_ptrs, st_pdf_resource, font, char_next)
+
+/* Named object pseudo-resources. */
+/*
+ * The elements of arrays are stored sorted in decreasing index order.
+ * The elements of dictionaries are not sorted.
+ * The elements of streams don't use the key, and are stored in
+ * reverse order.
+ */
+typedef struct pdf_named_element_s pdf_named_element;
+struct pdf_named_element_s {
+ pdf_named_element *next;
+ gs_string key; /* if array, data = 0, size = index */
+ gs_string value;
+};
+typedef enum {
+ named_unknown, /* forward reference */
+ named_array, named_dict, named_stream, /* OBJ or predefined */
+ named_graphics, /* BP/EP */
+ named_other /* ANN, DEST, LNK, PS */
+} pdf_named_object_type;
+
+#define private_st_pdf_named_element() /* in gdevpdfo.c */\
+ gs_private_st_composite(st_pdf_named_element, pdf_named_element,\
+ "pdf_named_element", pdf_named_elt_enum_ptrs, pdf_named_elt_reloc_ptrs)
+typedef struct pdf_named_object_s pdf_named_object;
+struct pdf_named_object_s {
+ pdf_resource_common(pdf_named_object);
+ pdf_named_object_type type;
+ gs_string key;
+ pdf_named_element *elements; /* (extra key/value pairs for graphics) */
+ bool open; /* stream, graphics */
+ struct gr_ { /* graphics only */
+ pdf_named_object *enclosing;
+ } graphics;
+};
+
+#define public_st_pdf_named_object() /* in gdevpdfo.c */\
+ gs_public_st_composite(st_pdf_named_object, pdf_named_object,\
+ "pdf_named_object", pdf_named_obj_enum_ptrs, pdf_named_obj_reloc_ptrs)
+
+/* ---------------- Other auxiliary structures ---------------- */
+
+/* Outline nodes and levels */
+typedef struct pdf_outline_node_s {
+ long id, parent_id, prev_id, first_id, last_id;
+ int count;
+ gs_string action_string;
+} pdf_outline_node;
+typedef struct pdf_outline_level_s {
+ pdf_outline_node first;
+ pdf_outline_node last;
+ int left;
+} pdf_outline_level;
+
+/* Articles */
+typedef struct pdf_bead_s {
+ long id, article_id, prev_id, next_id, page_id;
+ gs_rect rect;
+} pdf_bead;
+typedef struct pdf_article_s pdf_article;
+struct pdf_article_s {
+ pdf_article *next;
+ gs_string title;
+ gs_string info;
+ long id;
+ pdf_bead first;
+ pdf_bead last;
+};
+
+#define private_st_pdf_article()\
+ gs_private_st_ptrs1_strings2(st_pdf_article, pdf_article, "pdf_article",\
+ pdf_article_enum_ptrs, pdf_article_reloc_ptrs, next, title, info)
+
+/* Named destinations */
+typedef struct pdf_named_dest_s pdf_named_dest;
+struct pdf_named_dest_s {
+ pdf_named_dest *next;
+ gs_string key;
+ char dest[max_dest_string];
+};
+
+#define private_st_pdf_named_dest()\
+ gs_private_st_ptrs1_strings1(st_pdf_named_dest, pdf_named_dest,\
+ "pdf_named_dest", pdf_named_dest_enum_ptrs, pdf_named_dest_reloc_ptrs,\
+ next, key)
+
+/* ---------------- The device structure ---------------- */
+
+/* Text state */
+typedef struct pdf_text_state_s {
+ /* State parameters */
+ float character_spacing;
+ pdf_font *font;
+ floatp size;
+ float word_spacing;
+ float horizontal_scaling;
+ /* Bookkeeping */
+ gs_matrix matrix; /* relative to device space, not user space */
+ gs_point line_start;
+ gs_point current;
+#define max_text_buffer 200 /* arbitrary, but overflow costs 5 chars */
+ byte buffer[max_text_buffer];
+ int buffer_count;
+} pdf_text_state;
+
+#define pdf_text_state_default\
+ 0, NULL, 0, 0, 100,\
+ { identity_matrix_body }, { 0, 0 }, { 0, 0 }, { 0 }, 0
+
+/* Resource lists */
+#define num_resource_chains 16
+typedef struct pdf_resource_list_s {
+ pdf_resource *chains[num_resource_chains];
+} pdf_resource_list;
+
+/* Define the hash function for gs_ids. */
+#define gs_id_hash(rid) ((rid) + ((rid) / num_resource_chains))
+
+/* Define the bookkeeping for an open stream. */
+typedef struct pdf_stream_position_s {
+ long length_id;
+ long start_pos;
+} pdf_stream_position;
+
+/* Define the device structure. */
+typedef enum {
+ NoMarks = 0,
+ ImageB = 1,
+ ImageC = 2,
+ ImageI = 4,
+ Text = 8
+} pdf_procset;
+typedef enum {
+ pdf_in_none,
+ pdf_in_stream,
+ pdf_in_text,
+ pdf_in_string
+} pdf_context;
+typedef struct gx_device_pdf_s {
+ gx_device_psdf_common;
+ /* PDF-specific distiller parameters */
+ float CompatibilityLevel;
+ /* End of distiller parameters */
+ /* Other parameters */
+ bool ReAssignCharacters;
+ bool ReEncodeCharacters;
+ long FirstObjectNumber;
+ /* End of parameters */
+ /* Following are set when device is opened. */
+ enum {
+ pdf_compress_none,
+ pdf_compress_LZW, /* not currently used, thanks to Unisys */
+ pdf_compress_Flate
+ } compression;
+#define pdf_memory v_memory
+ char tfname[gp_file_name_sizeof];
+ FILE *tfile;
+ char rfname[gp_file_name_sizeof];
+ FILE *rfile;
+ stream *rstrm;
+ byte *rstrmbuf;
+ stream *rsave_strm;
+ pdf_font *open_font;
+ long embedded_encoding_id;
+ /* ................ */
+ long next_id;
+ /* The following 2 IDs, and only these, are allocated */
+ /* when the file is opened. */
+ long root_id;
+ long info_id;
+#define pdf_num_initial_ids 2
+ long pages_id;
+ long outlines_id;
+ int next_page;
+ long contents_id;
+ pdf_context context;
+ long contents_length_id;
+ long contents_pos;
+ pdf_procset procsets; /* used on this page */
+ float flatness;
+/****** SHOULD USE state ******/
+ /* The line width, dash offset, and dash pattern */
+ /* are in default user space units. */
+ gx_line_params line_params; /* current values */
+/****** SHOULD USE state ******/
+ pdf_text_state text;
+ long space_char_ids[x_space_max - x_space_min + 1];
+#define initial_num_page_ids 50
+ long *page_ids;
+ int num_page_ids;
+ int pages_referenced;
+ pdf_resource_list resources[num_resource_types];
+ pdf_resource *annots; /* rid = page # */
+ pdf_resource *last_resource;
+ gs_string catalog_string;
+ gs_string pages_string;
+ gs_string page_string;
+ pdf_outline_level outline_levels[max_outline_depth];
+ int outline_depth;
+ int closed_outline_depth;
+ int outlines_open;
+ pdf_article *articles;
+ pdf_named_dest *named_dests;
+ pdf_named_object *named_objects;
+ pdf_named_object *open_graphics;
+} gx_device_pdf;
+
+#define is_in_page(pdev)\
+ ((pdev)->contents_id != 0)
+#define is_in_document(pdev)\
+ (is_in_page(pdev) || (pdev)->last_resource != 0)
+
+/* Enumerate the individual pointers in a gx_device_pdf */
+#define gx_device_pdf_do_ptrs(m)\
+ m(0,rstrm) m(1,rstrmbuf) m(2,rsave_strm) m(3,open_font)\
+ m(4,line_params.dash.pattern) m(5,text.font) m(6,page_ids) m(7,annots)\
+ m(8,last_resource) m(9,articles) m(10,named_dests)\
+ m(11,named_objects) m(12,open_graphics)
+#define gx_device_pdf_num_ptrs 13 /* + num_resource_types */
+#define gx_device_pdf_do_strings(m)\
+ m(0,catalog_string) m(1,pages_string) m(2,page_string)
+#define gx_device_pdf_num_strings 3 /* + max_outline_depth * 2 */
+#define st_device_pdf_max_ptrs\
+ (st_device_psdf_max_ptrs + gx_device_pdf_num_ptrs +\
+ gx_device_pdf_num_strings + num_resource_types * num_resource_chains +\
+ max_outline_depth * 2)
+
+#define private_st_device_pdfwrite() /* in gdevpdf.c */\
+ gs_private_st_composite_final(st_device_pdfwrite, gx_device_pdf,\
+ "gx_device_pdf", device_pdfwrite_enum_ptrs, device_pdfwrite_reloc_ptrs,\
+ device_pdfwrite_finalize)
+
+/* ================ Utility procedures ================ */
+
+/* ---------------- Exported by gdevpdf.c ---------------- */
+
+/* ------ Document ------ */
+
+/* Initialize the IDs allocated at startup. */
+void pdf_initialize_ids(P1(gx_device_pdf * pdev));
+
+/* Open the document if necessary. */
+void pdf_open_document(P1(gx_device_pdf * pdev));
+
+/* ------ Objects ------ */
+
+/* Allocate an ID for a future object. */
+long pdf_obj_ref(P1(gx_device_pdf * pdev));
+
+/* Read the current position in the output stream. */
+long pdf_stell(P1(gx_device_pdf * pdev));
+
+/* Begin an object, optionally allocating an ID. */
+long pdf_open_obj(P2(gx_device_pdf * pdev, long id));
+
+/* Begin an object, allocating an ID. */
+#define pdf_begin_obj(pdev) pdf_open_obj(pdev, 0)
+
+/* End an object. */
+int pdf_end_obj(P1(gx_device_pdf * pdev));
+
+/* ------ Graphics ------ */
+
+/* Reset the graphics state parameters to initial values. */
+void pdf_reset_graphics(P1(gx_device_pdf * pdev));
+
+/* Set the fill or stroke color. */
+int pdf_set_color(P4(gx_device_pdf * pdev, gx_color_index color,
+ gx_drawing_color * pdcolor, const char *rgs));
+
+/* Write matrix values. */
+void pdf_put_matrix(P4(gx_device_pdf * pdev, const char *before,
+ const gs_matrix * pmat, const char *after));
+
+/* Write a name, with escapes for unusual characters. */
+void pdf_put_name(P3(const gx_device_pdf * pdev, const byte * nstr, uint size));
+
+/* Write a string in its shortest form ( () or <> ). */
+void pdf_put_string(P3(const gx_device_pdf * pdev, const byte * str, uint size));
+
+/* Write a value, treating names specially. */
+void pdf_put_value(P3(const gx_device_pdf * pdev, const byte * vstr, uint size));
+
+/* ------ Page contents ------ */
+
+/* Open a page contents part. */
+/* Return an error if the page has too many contents parts. */
+int pdf_open_contents(P2(gx_device_pdf * pdev, pdf_context context));
+
+/* Close the current contents part if we are in one. */
+int pdf_close_contents(P2(gx_device_pdf * pdev, bool last));
+
+/* ------ Resources et al ------ */
+
+/* Begin an object logically separate from the contents. */
+/* (I.e., an object in the resource file.) */
+long pdf_open_separate(P2(gx_device_pdf * pdev, long id));
+
+#define pdf_begin_separate(pdev) pdf_open_separate(pdev, 0L)
+
+/* Begin an aside (resource, annotation, ...). */
+int pdf_begin_aside(P4(gx_device_pdf * pdev, pdf_resource ** plist,
+ const gs_memory_struct_type_t * pst,
+ pdf_resource ** ppres));
+
+/* Begin a resource of a given type. */
+int pdf_begin_resource(P4(gx_device_pdf * pdev, pdf_resource_type type,
+ gs_id rid, pdf_resource ** ppres));
+
+/* Allocate a resource, but don't open the stream. */
+int pdf_alloc_resource(P4(gx_device_pdf * pdev, pdf_resource_type type,
+ gs_id rid, pdf_resource ** ppres));
+
+/* Find a resource of a given type by gs_id. */
+pdf_resource *pdf_find_resource_by_gs_id(P3(gx_device_pdf * pdev,
+ pdf_resource_type type,
+ gs_id rid));
+
+/* End a separate object. */
+#define pdf_end_separate(pdev) pdf_end_aside(pdev)
+
+/* End an aside. */
+int pdf_end_aside(P1(gx_device_pdf * pdev));
+
+/* End a resource. */
+int pdf_end_resource(P1(gx_device_pdf * pdev));
+
+/* ------ Pages ------ */
+
+/* Get or assign the ID for a page. */
+/* Returns 0 if the page number is out of range. */
+long pdf_page_id(P2(gx_device_pdf * pdev, int page_num));
+
+/* Open a page for writing. */
+int pdf_open_page(P2(gx_device_pdf * pdev, pdf_context context));
+
+/* Write saved page- or document-level information. */
+int pdf_write_saved_string(P2(gx_device_pdf * pdev, gs_string * pstr));
+
+/* Write the default entries of the Info dictionary. */
+int pdf_write_default_info(P1(gx_device_pdf * pdev));
+
+/* ------ Path drawing ------ */
+
+bool pdf_must_put_clip_path(P2(gx_device_pdf * pdev, const gx_clip_path * pcpath));
+
+int pdf_put_clip_path(P2(gx_device_pdf * pdev, const gx_clip_path * pcpath));
+
+/* ---------------- Exported by gdevpdfm.c ---------------- */
+
+/* Compare a C string and a gs_param_string. */
+bool pdf_key_eq(P2(const gs_param_string * pcs, const char *str));
+
+/* Scan an integer out of a parameter string. */
+int pdfmark_scan_int(P2(const gs_param_string * pstr, int *pvalue));
+
+/* Define the type for a pdfmark-processing procedure. */
+/* If nameable is false, the objname argument is always NULL. */
+#define pdfmark_proc(proc)\
+ int proc(P5(gx_device_pdf *pdev, gs_param_string *pairs, uint count,\
+ const gs_matrix *pctm, const gs_param_string *objname))
+/* Define an entry in a table of pdfmark-processing procedures. */
+#define pdfmark_nameable 1 /* allows _objdef */
+#define pdfmark_odd_ok 2 /* OK if odd # of parameters */
+#define pdfmark_keep_name 4 /* don't substitute reference for name */
+ /* in 1st argument */
+typedef struct pdfmark_name_s {
+ const char *mname;
+ pdfmark_proc((*proc));
+ byte options;
+} pdfmark_name;
+
+/* Process a pdfmark (called from pdf_put_params). */
+int pdfmark_process(P2(gx_device_pdf * pdev, const gs_param_string_array * pma));
+
+/* Close the current level of the outline tree. */
+int pdfmark_close_outline(P1(gx_device_pdf * pdev));
+
+/* Finish writing an article. */
+int pdfmark_write_article(P2(gx_device_pdf * pdev, const pdf_article * part));
+
+/* ---------------- Exported by gdevpdfo.c ---------------- */
+
+/* Define the syntax of object names. */
+#define pdfmark_objname_is_valid(data, size)\
+ ((size) >= 2 && (data)[0] == '{' &&\
+ memchr(data, '}', size) == (data) + (size) - 1)
+
+/* Define the table of named-object pdfmark types. */
+extern const pdfmark_name pdfmark_names_named[];
+
+/* Replace object names with object references in a (parameter) string. */
+int pdfmark_replace_names(P3(gx_device_pdf * pdev, const gs_param_string * from,
+ gs_param_string * to));
+
+/* Write and free an entire list of named objects. */
+int pdfmark_write_and_free_named(P2(gx_device_pdf * pdev,
+ pdf_named_object ** ppno));
+
+/* ---------------- Exported by gdevpdft.c ---------------- */
+
+/* Process a show operation (called from pdf_put_params). */
+int pdfshow_process(P3(gx_device_pdf * pdev, gs_param_list * plist,
+ const gs_param_string * pts));
+
+/* Begin a CharProc for an embedded (bitmap) font. */
+int pdf_begin_char_proc(P8(gx_device_pdf * pdev, int w, int h, int x_width,
+ int y_offset, gs_id id, pdf_char_proc ** ppcp,
+ pdf_stream_position * ppos));
+
+/* End a CharProc. */
+int pdf_end_char_proc(P2(gx_device_pdf * pdev, pdf_stream_position * ppos));
+
+/* Put out a reference to an image as a character in an embedded font. */
+int pdf_do_char_image(P3(gx_device_pdf * pdev, const pdf_char_proc * pcp,
+ const gs_matrix * pimat));
+
+#endif /* gdevpdfx_INCLUDED */
diff --git a/pstoraster/gdevpipe.c b/pstoraster/gdevpipe.c
new file mode 100644
index 000000000..03909db6a
--- /dev/null
+++ b/pstoraster/gdevpipe.c
@@ -0,0 +1,72 @@
+/* Copyright (C) 1993, 1994, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* %pipe% IODevice */
+#include "errno_.h"
+#include "pipe_.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"
+
+/* The pipe IODevice */
+private iodev_proc_fopen(pipe_fopen);
+private iodev_proc_fclose(pipe_fclose);
+const 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/gdevpm.h b/pstoraster/gdevpm.h
new file mode 100644
index 000000000..f1f7b3cd8
--- /dev/null
+++ b/pstoraster/gdevpm.h
@@ -0,0 +1,46 @@
+/* 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 supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Defines common to gdevpm.c, gspmdrv.c and PM GSview */
+
+#ifndef gdevpm_INCLUDED
+# define gdevpm_INCLUDED
+
+#define SHARED_NAME "\\SHAREMEM\\%s"
+#define SYNC_NAME "\\SEM32\\SYNC_%s"
+#define NEXT_NAME "\\SEM32\\NEXT_%s"
+#define MUTEX_NAME "\\SEM32\\MUTEX_%s"
+#define QUEUE_NAME "\\QUEUES\\%s"
+
+#define GS_UPDATING 1
+#define GS_SYNC 2
+#define GS_PAGE 3
+#define GS_CLOSE 4
+#define GS_ERROR 5
+#define GS_PALCHANGE 6
+#define GS_BEGIN 7
+#define GS_END 8
+
+#endif /* gdevpm_INCLUDED */
diff --git a/pstoraster/gdevprn.c b/pstoraster/gdevprn.c
new file mode 100644
index 000000000..7a28c2c65
--- /dev/null
+++ b/pstoraster/gdevprn.c
@@ -0,0 +1,923 @@
+/*
+ Copyright 1993-2000 by Easy Software Products.
+ Copyright 1990, 1995, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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 ---------------- */
+
+/* Define the standard printer procedure vector. */
+const gx_device_procs prn_std_procs =
+ prn_procs(gdev_prn_open, gdev_prn_output_page, gdev_prn_close);
+
+/* Forward references */
+int gdev_prn_maybe_reallocate_memory(P4(gx_device_printer *pdev,
+ gdev_prn_space_params *old_space,
+ int old_width, int old_height));
+
+/* ------ Open/close ------ */
+
+/* Open a generic printer device. */
+/* Specific devices may wish to extend this. */
+int
+gdev_prn_open(gx_device * pdev)
+{
+ gx_device_printer * const ppdev = (gx_device_printer *)pdev;
+ int code;
+
+ ppdev->file = NULL;
+ code = gdev_prn_allocate_memory(pdev, NULL, 0, 0);
+ if (code < 0)
+ return code;
+ if (ppdev->OpenOutputFile)
+ code = gdev_prn_open_printer(pdev, 1);
+ return code;
+}
+
+/* Generic closing for the printer device. */
+/* Specific devices may wish to extend this. */
+int
+gdev_prn_close(gx_device * pdev)
+{
+ gx_device_printer * const ppdev = (gx_device_printer *)pdev;
+
+ gdev_prn_free_memory(pdev);
+ if (ppdev->file != NULL) {
+ if (ppdev->file != stdout)
+ gp_close_printer(ppdev->file, ppdev->fname);
+ ppdev->file = NULL;
+ }
+ return 0;
+}
+
+private int /* returns 0 ok, else -ve error cde */
+gdev_prn_setup_as_command_list(gx_device *pdev, gs_memory_t *buffer_memory,
+ byte **the_memory,
+ const gdev_prn_space_params *space_params,
+ bool bufferSpace_is_exact)
+{
+ gx_device_printer * const ppdev = (gx_device_printer *)pdev;
+ uint space;
+ int code;
+ gx_device_clist *const pclist_dev = (gx_device_clist *)pdev;
+ gx_device_clist_common * const pcldev = &pclist_dev->common;
+ bool reallocate = *the_memory != 0;
+ byte *base;
+
+ /* Try to allocate based simply on param-requested buffer size */
+ for ( space = space_params->BufferSpace; ; ) {
+ base = (reallocate ?
+ gs_resize_object(buffer_memory, *the_memory, space,
+ "cmd list buffer") :
+ gs_alloc_bytes(buffer_memory, space,
+ "cmd list buffer"));
+ if (base != 0)
+ break;
+ if (bufferSpace_is_exact || (space >>= 1) < PRN_MIN_BUFFER_SPACE)
+ break;
+ }
+ if (base == 0)
+ return_error(gs_error_VMerror);
+ *the_memory = base;
+
+ /* Try opening the command list, to see if we allocated */
+ /* enough buffer space. */
+open_c:
+ ppdev->buf = base;
+ ppdev->buffer_space = space;
+ clist_init_params(pclist_dev, base, space, pdev,
+ ppdev->printer_procs.make_buffer_device,
+ space_params->band, ppdev->is_async_renderer,
+ (ppdev->bandlist_memory == 0 ? &gs_memory_default :
+ ppdev->bandlist_memory),
+ ppdev->free_up_bandlist_memory,
+ ppdev->clist_disable_mask);
+ 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 >= space_params->BufferSpace &&
+ !bufferSpace_is_exact
+ ) {
+ space <<= 1;
+ if (reallocate) {
+ base = gs_resize_object(buffer_memory,
+ *the_memory, space,
+ "cmd list buf(retry open)");
+ if (base != 0)
+ *the_memory = base;
+ } else {
+ gs_free_object(buffer_memory, base,
+ "cmd list buf(retry open)");
+ *the_memory = base =
+ gs_alloc_bytes(buffer_memory, space,
+ "cmd list buf(retry open)");
+ }
+ ppdev->buf = *the_memory;
+ if (base != 0)
+ goto open_c;
+ }
+ /* Failure. */
+ if (!reallocate) {
+ gs_free_object(buffer_memory, base, "cmd list buf");
+ ppdev->buffer_space = 0;
+ *the_memory = 0;
+ }
+ }
+ return code;
+}
+
+private bool /* ret true if device was cmd list, else false */
+gdev_prn_tear_down(gx_device *pdev, byte **the_memory)
+{
+ gx_device_printer * const ppdev = (gx_device_printer *)pdev;
+ gx_device_memory * const pmemdev = (gx_device_memory *)pdev;
+ gx_device_clist *const pclist_dev = (gx_device_clist *)pdev;
+ gx_device_clist_common * const pcldev = &pclist_dev->common;
+ bool is_command_list;
+
+ if (ppdev->buffer_space != 0) {
+ /* Close cmd list device & point to the storage */
+ (*gs_clist_device_procs.close_device)( (gx_device *)pcldev );
+ *the_memory = ppdev->buf;
+ ppdev->buf = 0;
+ ppdev->buffer_space = 0;
+ is_command_list = true;
+ } else {
+ /* point at the device bitmap, no need to close mem dev */
+ *the_memory = pmemdev->base;
+ pmemdev->base = 0;
+ is_command_list = false;
+ }
+
+ /* Reset device proc vector to default */
+ if (ppdev->orig_procs.open_device != 0)
+ pdev->procs = ppdev->orig_procs;
+ ppdev->orig_procs.open_device = 0; /* prevent uninit'd restore of procs */
+
+ return is_command_list;
+}
+
+private int
+gdev_prn_allocate(gx_device *pdev, gdev_prn_space_params *new_space_params,
+ int new_width, int new_height, bool reallocate)
+{
+ gx_device_printer * const ppdev = (gx_device_printer *)pdev;
+ gx_device_memory * const pmemdev = (gx_device_memory *)pdev;
+ byte *the_memory = 0;
+ gdev_prn_space_params save_params;
+ int save_width, save_height;
+ bool is_command_list;
+ bool save_is_command_list;
+ int ecode = 0;
+ int pass;
+ gs_memory_t *buffer_memory =
+ (ppdev->buffer_memory == 0 ? &gs_memory_default :
+ ppdev->buffer_memory);
+
+ /* If reallocate, find allocated memory & tear down buffer device */
+ if (reallocate)
+ save_is_command_list = gdev_prn_tear_down(pdev, &the_memory);
+
+ /* Re/allocate memory */
+ ppdev->orig_procs = pdev->procs;
+ for ( pass = 1; pass <= (reallocate ? 2 : 1); ++pass ) {
+ ulong mem_space;
+ byte *base = 0;
+ bool bufferSpace_is_default = false;
+ gdev_prn_space_params space_params = ppdev->space_params;
+
+ if (reallocate)
+ switch (pass)
+ {
+ case 1:
+ /* Setup device to get reallocated */
+ save_params = ppdev->space_params;
+ ppdev->space_params = *new_space_params;
+ save_width = ppdev->width;
+ ppdev->width = new_width;
+ save_height = ppdev->height;
+ ppdev->height = new_height;
+ break;
+ case 2: /* only comes here if reallocate */
+ /* Restore device to previous contents */
+ ppdev->space_params = save_params;
+ ppdev->width = save_width;
+ ppdev->height = save_height;
+ break;
+ }
+
+ /* Init clist/mem device-specific fields */
+ memset(ppdev->skip, 0, sizeof(ppdev->skip));
+ mem_space = gdev_mem_bitmap_size(pmemdev);
+
+ /* Compute desired space params: never use the space_params as-is. */
+ /* Rather, give the dev-specific driver a chance to adjust them. */
+ space_params.BufferSpace = 0;
+ (*ppdev->printer_procs.get_space_params)(ppdev, &space_params);
+ if (ppdev->is_async_renderer && space_params.band.BandBufferSpace != 0)
+ space_params.BufferSpace = space_params.band.BandBufferSpace;
+ else if (space_params.BufferSpace == 0) {
+ if (space_params.band.BandBufferSpace > 0)
+ space_params.BufferSpace = space_params.band.BandBufferSpace;
+ else {
+ space_params.BufferSpace = ppdev->space_params.BufferSpace;
+ bufferSpace_is_default = true;
+ }
+ }
+
+ /* Determine if we can use a full bitmap buffer, or have to use banding */
+ if (pass > 1)
+ is_command_list = save_is_command_list;
+ else {
+ is_command_list = space_params.banding_type == BandingAlways ||
+ mem_space >= space_params.MaxBitmap ||
+ mem_space != (uint)mem_space; /* too big to allocate */
+ }
+ if (!is_command_list) {
+ /* Try to allocate memory for full memory buffer */
+ base = reallocate
+ ? gs_resize_object( buffer_memory, the_memory,
+ (uint)mem_space, "printer buffer" )
+ : gs_alloc_bytes( buffer_memory, (uint)mem_space,
+ "printer_buffer" );
+ if (base == 0)
+ is_command_list = true;
+ else
+ the_memory = base;
+ }
+ if (!is_command_list && pass == 1 && PRN_MIN_MEMORY_LEFT != 0
+ && buffer_memory == &gs_memory_default) {
+ /* before using full memory buffer, ensure enough working mem left */
+ byte * left = gs_alloc_bytes( buffer_memory,
+ PRN_MIN_MEMORY_LEFT, "printer mem left");
+ if (left == 0)
+ is_command_list = true;
+ else
+ gs_free_object(buffer_memory, left, "printer mem left");
+ }
+
+ if (is_command_list) {
+ /* Buffer the image in a command list. */
+ /* Release the buffer if we allocated it. */
+ int code;
+ if (!reallocate) {
+ gs_free_object(buffer_memory, the_memory,
+ "printer buffer(open)");
+ the_memory = 0;
+ }
+ if (space_params.banding_type == BandingNever) {
+ ecode = gs_note_error(gs_error_VMerror);
+ continue;
+ }
+ code = gdev_prn_setup_as_command_list(pdev, buffer_memory,
+ &the_memory, &space_params,
+ !bufferSpace_is_default);
+ if (ecode == 0)
+ ecode = code;
+
+ if ( code >= 0 || (reallocate && pass > 1) )
+ ppdev->procs = gs_clist_device_procs;
+ } else {
+ /* Render entirely in memory. */
+ int code;
+
+ ppdev->buffer_space = 0;
+ code = (*ppdev->printer_procs.make_buffer_device)
+ (pmemdev, pdev, buffer_memory, false);
+ if (code < 0) { /* Catastrophic. Shouldn't ever happen */
+ gs_free_object(buffer_memory, base, "printer buffer");
+ pdev->procs = ppdev->orig_procs;
+ ppdev->orig_procs.open_device = 0; /* prevent uninit'd restore of procs */
+ return_error(code);
+ }
+ pmemdev->base = base;
+ }
+ if (ecode == 0)
+ break;
+ }
+
+ if (ecode >= 0 || reallocate) { /* even if realloc failed */
+ /* 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);
+ COPY_PROC(get_clipping_box);
+ COPY_PROC(map_color_rgb_alpha);
+ COPY_PROC(get_hardware_params);
+#undef COPY_PROC
+ /* If using a command list, already opened the device. */
+ if (is_command_list)
+ return ecode;
+ else
+ return (*dev_proc(pdev, open_device))(pdev);
+ } else {
+ pdev->procs = ppdev->orig_procs;
+ ppdev->orig_procs.open_device = 0; /* prevent uninit'd restore of procs */
+ return ecode;
+ }
+}
+
+int
+gdev_prn_allocate_memory(gx_device *pdev,
+ gdev_prn_space_params *new_space_params,
+ int new_width, int new_height)
+{
+ return gdev_prn_allocate(pdev, new_space_params,
+ new_width, new_height, false);
+}
+
+int
+gdev_prn_reallocate_memory(gx_device *pdev,
+ gdev_prn_space_params *new_space_params,
+ int new_width, int new_height)
+{
+ return gdev_prn_allocate(pdev, new_space_params,
+ new_width, new_height, true);
+}
+
+int
+gdev_prn_free_memory(gx_device *pdev)
+{
+ gx_device_printer * const ppdev = (gx_device_printer *)pdev;
+ byte *the_memory = 0;
+ gs_memory_t *buffer_memory =
+ (ppdev->buffer_memory == 0 ? &gs_memory_default :
+ ppdev->buffer_memory);
+
+ gdev_prn_tear_down(pdev, &the_memory);
+ gs_free_object(buffer_memory, the_memory, "gdev_prn_free_memory");
+ return 0;
+}
+
+/* ------------- Stubs related only to async rendering ------- */
+
+int /* rets 0 ok, -ve error if couldn't start thread */
+gx_default_start_render_thread(gdev_prn_start_render_params *params)
+{
+ return gs_error_unknownerror;
+}
+
+/* Open the renderer's copy of a device. */
+/* This is overriden in gdevprna.c */
+int
+gx_default_open_render_device(gx_device_printer *pdev)
+{
+ return gs_error_unknownerror;
+}
+
+/* Close the renderer's copy of a device. */
+int
+gx_default_close_render_device(gx_device_printer *pdev)
+{
+ return gdev_prn_close( (gx_device *)pdev );
+}
+
+/* ------ 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)
+{
+ gx_device_printer * const ppdev = (gx_device_printer *)pdev;
+ int code = gx_default_get_params(pdev, plist);
+ gs_param_string ofns;
+
+ if (code < 0 ||
+ (code = param_write_long(plist, "MaxBitmap", &ppdev->space_params.MaxBitmap)) < 0 ||
+ (code = param_write_long(plist, "BufferSpace", &ppdev->space_params.BufferSpace)) < 0 ||
+ (code = param_write_int(plist, "BandWidth", &ppdev->space_params.band.BandWidth)) < 0 ||
+ (code = param_write_int(plist, "BandHeight", &ppdev->space_params.band.BandHeight)) < 0 ||
+ (code = param_write_long(plist, "BandBufferSpace", &ppdev->space_params.band.BandBufferSpace)) < 0 ||
+ (code = param_write_bool(plist, "OpenOutputFile", &ppdev->OpenOutputFile)) < 0 ||
+ (code = param_write_bool(plist, "ReopenPerPage", &ppdev->ReopenPerPage)) < 0 ||
+ (ppdev->Duplex_set >= 0 &&
+ (code = (ppdev->Duplex_set ?
+ param_write_bool(plist, "Duplex", &ppdev->Duplex) :
+ param_write_null(plist, "Duplex"))) < 0)
+ )
+ return code;
+
+ ofns.data = (const byte *)ppdev->fname,
+ ofns.size = strlen(ppdev->fname),
+ ofns.persistent = false;
+ return param_write_string(plist, "OutputFile", &ofns);
+}
+
+/* Validate an OutputFile name by checking any %-formats. */
+private int
+validate_output_file(const gs_param_string * ofs)
+{
+ const byte *data = ofs->data;
+ uint size = ofs->size;
+ bool have_format = false;
+ uint i;
+
+ if (size >= prn_fname_sizeof)
+ return_error(gs_error_limitcheck);
+ for (i = 0; i < size; ++i)
+ if (data[i] == '%') {
+ if (i + 1 < size && data[i + 1] == '%')
+ continue;
+ if (have_format) /* more than one % */
+ return_error(gs_error_rangecheck);
+ have_format = true;
+ sw:if (++i == size)
+ return_error(gs_error_rangecheck);
+ switch (data[i]) {
+ case ' ':
+ 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:
+ return_error(gs_error_rangecheck);
+ }
+ }
+ return 0;
+}
+
+/* Put parameters. */
+int
+gdev_prn_put_params(gx_device * pdev, gs_param_list * plist)
+{
+ gx_device_printer * const ppdev = (gx_device_printer *)pdev;
+ int ecode = 0;
+ int code;
+ const char *param_name;
+ bool is_open = pdev->is_open;
+ bool oof = ppdev->OpenOutputFile;
+ bool rpp = ppdev->ReopenPerPage;
+ bool duplex;
+ int duplex_set = -1;
+ int width = pdev->width;
+ int height = pdev->height;
+ gdev_prn_space_params sp, save_sp;
+ gs_param_string ofs;
+ gs_param_dict mdict;
+
+ sp = ppdev->space_params;
+ save_sp = sp;
+
+ 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;
+ }
+
+ switch (code = param_read_bool(plist, (param_name = "ReopenPerPage"), &rpp)) {
+ 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:
+ ;
+ }
+#define CHECK_PARAM_CASES(member, bad, label)\
+ case 0:\
+ if ((sp.params_are_read_only ? sp.member != save_sp.member : bad))\
+ ecode = gs_error_rangecheck;\
+ else\
+ break;\
+ goto label;\
+ default:\
+ ecode = code;\
+label:\
+ param_signal_error(plist, param_name, ecode);\
+ case 1:\
+ break
+
+ switch (code = param_read_long(plist, (param_name = "MaxBitmap"), &sp.MaxBitmap)) {
+ CHECK_PARAM_CASES(MaxBitmap, sp.MaxBitmap < 10000, mbe);
+ }
+
+ switch (code = param_read_long(plist, (param_name = "BufferSpace"), &sp.BufferSpace)) {
+ CHECK_PARAM_CASES(BufferSpace, sp.BufferSpace < 10000, bse);
+ }
+
+ switch (code = param_read_int(plist, (param_name = "BandWidth"), &sp.band.BandWidth)) {
+ CHECK_PARAM_CASES(band.BandWidth, sp.band.BandWidth < 0, bwe);
+ }
+
+ switch (code = param_read_int(plist, (param_name = "BandHeight"), &sp.band.BandHeight)) {
+ CHECK_PARAM_CASES(band.BandHeight, sp.band.BandHeight < 0, bhe);
+ }
+
+ switch (code = param_read_long(plist, (param_name = "BandBufferSpace"), &sp.band.BandBufferSpace)) {
+ CHECK_PARAM_CASES(band.BandBufferSpace, sp.band.BandBufferSpace < 0, bbse);
+ }
+
+ switch (code = param_read_string(plist, (param_name = "OutputFile"), &ofs)) {
+ case 0:
+ code = validate_output_file(&ofs);
+ if (code >= 0)
+ break;
+ /* falls through */
+ default:
+ ecode = code;
+ 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->OpenOutputFile = oof;
+ ppdev->ReopenPerPage = rpp;
+ if (duplex_set >= 0) {
+ ppdev->Duplex = duplex;
+ ppdev->Duplex_set = duplex_set;
+ }
+ ppdev->space_params = sp;
+
+ /* If necessary, free and reallocate the printer memory. */
+ /* Formerly, would not reallocate if device is not open: */
+ /* we had to patch this out (see News for 5.50). */
+ code = gdev_prn_maybe_reallocate_memory(ppdev, &save_sp, width, height);
+ if (code < 0)
+ return code;
+
+ /* If filename changed, close file. */
+ 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;
+}
+
+/* ------ Others ------ */
+
+/* Default routine to (not) override current space_params. */
+void
+gdev_prn_default_get_space_params(const gx_device_printer *printer_dev,
+ gdev_prn_space_params *space_params)
+{
+ return;
+}
+
+/* Generic routine to send the page to the printer. */
+int /* 0 ok, -ve error, or 1 if successfully upgraded to buffer_page */
+gdev_prn_output_page(gx_device * pdev, int num_copies, int flush)
+{
+ gx_device_printer * const ppdev = (gx_device_printer *)pdev;
+ int outcode = 0, closecode = 0, errcode = 0, endcode;
+ bool upgraded_copypage = false;
+
+ if (num_copies > 0 || !flush) {
+ int code = gdev_prn_open_printer(pdev, 1);
+
+ if (code < 0)
+ return code;
+
+ /* If copypage request, try to do it using buffer_page */
+ if ( !flush &&
+ (*ppdev->printer_procs.buffer_page)
+ (ppdev, ppdev->file, num_copies) >= 0
+ ) {
+ upgraded_copypage = true;
+ flush = true;
+ }
+ else if (num_copies > 0)
+ /* Print the accumulated page description. */
+ outcode =
+ (*ppdev->printer_procs.print_page_copies)(ppdev, ppdev->file,
+ num_copies);
+ if (ppdev->file)
+ {
+ fflush(ppdev->file);
+ errcode =
+ (ferror(ppdev->file) ? gs_note_error(gs_error_ioerror) : 0);
+ }
+ else
+ errcode = 0;
+
+ if (!upgraded_copypage)
+ closecode = gdev_prn_close_printer(pdev);
+ }
+ endcode = (ppdev->buffer_space ? clist_finish_page(pdev, flush) : 0);
+
+ if (outcode < 0)
+ return outcode;
+ if (errcode < 0)
+ return errcode;
+ if (closecode < 0)
+ return closecode;
+ if (endcode < 0)
+ return endcode;
+ return (upgraded_copypage ? 1 : 0);
+}
+
+/* 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;
+}
+
+/*
+ * Buffer a (partial) rasterized page & optionally print result multiple times.
+ * The default implementation returns error, since the driver needs to override
+ * this (in procedure vector) in configurations where this call may occur.
+ */
+int
+gx_default_buffer_page(gx_device_printer *pdev, FILE *prn_stream,
+ int num_copies)
+{
+ return gs_error_unknownerror;
+}
+
+/* ---------------- 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_positionable(gx_device *pdev, bool binary_mode,
+ bool positionable)
+{
+ gx_device_printer * const ppdev = (gx_device_printer *)pdev;
+
+ if (ppdev->file != 0) {
+ ppdev->file_is_new = false;
+ return 0;
+ }
+ {
+ int code = gx_device_open_output_file(pdev, ppdev->fname,
+ binary_mode, positionable,
+ &ppdev->file);
+ if (code < 0)
+ return code;
+ }
+ ppdev->file_is_new = true;
+ return 0;
+}
+int
+gdev_prn_open_printer(gx_device *pdev, bool binary_mode)
+{
+ return gdev_prn_open_printer_positionable(pdev, binary_mode, false);
+}
+
+/* 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;
+}
+
+/* Like get_bits, but accepts initial raster contents */
+int
+gdev_prn_get_overlay_bits(gx_device_printer *pdev, int y, int lineCount,
+ byte *data)
+{
+ if (pdev->buffer_space) {
+ /* Command lists have built-in support for this function */
+ return clist_get_overlay_bits(pdev, y, lineCount, data);
+ } else {
+ /* Memory devices cannot support this function. */
+ return_error(gs_error_unknownerror);
+ }
+}
+
+/* Find out where the band buffer for a given line is going to fall on the */
+/* next call to get_bits. */
+int /* rets # lines from y till end of buffer, or -ve error code */
+gdev_prn_locate_overlay_buffer(gx_device_printer *pdev, int y, byte **data)
+{
+ gx_device_printer * const ppdev = (gx_device_printer *)pdev;
+
+ if (ppdev->buffer_space) {
+ /* Command lists have built-in support for this function */
+ return clist_locate_overlay_buffer(pdev, y, data);
+ } else {
+ /* Memory devices cannot support this function. */
+ return_error(gs_error_unknownerror);
+ }
+}
+
+/* Close the current page. */
+int
+gdev_prn_close_printer(gx_device * pdev)
+{
+ gx_device_printer * const ppdev = (gx_device_printer *)pdev;
+
+ if (strchr(ppdev->fname, '%') /* file per page */ ||
+ ppdev->ReopenPerPage /* close and reopen for each page */
+ ) {
+ gp_close_printer(ppdev->file, ppdev->fname);
+ ppdev->file = NULL;
+ }
+ return 0;
+}
+
+/* If necessary, free and reallocate the printer memory after changing params */
+int
+gdev_prn_maybe_reallocate_memory(gx_device_printer *prdev,
+ gdev_prn_space_params *old_sp,
+ int old_width, int old_height)
+{
+ int code = 0;
+ gx_device *const pdev = (gx_device *)prdev;
+ gx_device_memory * const mdev = (gx_device_memory *)prdev;
+
+ /* The first test here used to be prdev->open. See News for 5.50. */
+ if (mdev->base != 0 &&
+ (memcmp(&prdev->space_params, old_sp, sizeof(*old_sp)) != 0 ||
+ prdev->width != old_width || prdev->height != old_height )
+ ) {
+ int new_width = prdev->width;
+ int new_height = prdev->height;
+ gdev_prn_space_params new_sp = prdev->space_params;
+
+ prdev->width = old_width;
+ prdev->height = old_height;
+ prdev->space_params = *old_sp;
+ code = gdev_prn_reallocate_memory(pdev, &new_sp,
+ new_width, new_height);
+ /* If this fails, device should be usable w/old params, but */
+ /* band files may not be open. */
+ }
+ return code;
+}
diff --git a/pstoraster/gdevprn.h b/pstoraster/gdevprn.h
new file mode 100644
index 000000000..68c09d6b5
--- /dev/null
+++ b/pstoraster/gdevprn.h
@@ -0,0 +1,560 @@
+/* Copyright (C) 1989, 1995, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Common header file for memory-buffered printers */
+
+#ifndef gdevprn_INCLUDED
+# define gdevprn_INCLUDED
+
+#include "memory_.h"
+#include "string_.h"
+#include "gx.h"
+#include "gp.h" /* for gp_file_name_sizeof */
+#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 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. */
+#ifndef gx_device_printer_DEFINED
+# define gx_device_printer_DEFINED
+typedef struct gx_device_printer_s gx_device_printer;
+#endif
+
+/* Define the abstract type for some band device procedures' arguments. */
+typedef struct gdev_prn_start_render_params_s gdev_prn_start_render_params;
+
+/* Define the abstract type for a page queue for async rendering. */
+#ifndef gx_page_queue_DEFINED
+# define gx_page_queue_DEFINED
+typedef struct gx_page_queue_s gx_page_queue;
+#endif
+
+/* Define the abstract type for parameters describing buffer space. */
+#ifndef gdev_prn_space_params_DEFINED
+# define gdev_prn_space_params_DEFINED
+typedef struct gdev_prn_space_params_s gdev_prn_space_params;
+#endif
+
+/*
+ * 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 prn_dev_proc_print_page(proc)\
+ int proc(P2(gx_device_printer *, FILE *))
+ prn_dev_proc_print_page((*print_page));
+/* BACKWARD COMPATIBILITY */
+#define dev_proc_print_page(proc) prn_dev_proc_print_page(proc)
+
+ /* Print the page on the output file, with a given # of copies. */
+
+#define prn_dev_proc_print_page_copies(proc)\
+ int proc(P3(gx_device_printer *, FILE *, int))
+ prn_dev_proc_print_page_copies((*print_page_copies));
+/* BACKWARD COMPATIBILITY */
+#define dev_proc_print_page_copies(proc) prn_dev_proc_print_page_copies(proc)
+
+ /* Initialize the memory device for a page or a band. */
+ /* (The macro definition is in gxdevcli.h.) */
+
+ dev_proc_make_buffer_device((*make_buffer_device));
+
+ /*
+ * Compute effective space params. These results effectively override
+ * the space_params in the device, but does not replace them; that is to
+ * say that computed space params are temps used for computation.
+ * Procedure must fill in only those space_params that it wishes to
+ * override, using curr width, height, margins, etc.
+ *
+ * Caller is gdevprn.open & gdevprn.put_params, calls driver or
+ * default.
+ */
+
+#define prn_dev_proc_get_space_params(proc)\
+ void proc(P2(const gx_device_printer *, gdev_prn_space_params *))
+ prn_dev_proc_get_space_params((*get_space_params));
+
+ /*
+ * Only for gx_device_printer devices that overlap interpreting and
+ * rasterizing. Since there are 2 instances of the device (1 for writing
+ * the cmd list & 1 for rasterizing it), and each device is associated
+ * with an different thread, this function is called to start the
+ * rasterizer's thread. Once started, the rasterizer thread must call
+ * down to gdev_prn_asnyc_render_thread, which will only return after
+ * device closes.
+ *
+ * Caller is gdevprna.open, calls driver implementation or default.
+ */
+
+#define prn_dev_proc_start_render_thread(proc)\
+ int proc(P1(gdev_prn_start_render_params *))
+ prn_dev_proc_start_render_thread((*start_render_thread));
+
+ /*
+ * Only for gx_device_printer devices that overlap interpreting and
+ * rasterizing. Since there are 2 instances of the device (1 for writing
+ * the cmd list & 1 for rasterizing it), these fns are called to
+ * open/close the rasterizer's instance, once the writer's instance has
+ * been created & init'd. These procs must cascade down to
+ * gdev_prn_async_render_open/close.
+ *
+ * Caller is gdevprna, calls driver implementation or default.
+ */
+
+#define prn_dev_proc_open_render_device(proc)\
+ int proc(P1(gx_device_printer *))
+ prn_dev_proc_open_render_device((*open_render_device));
+
+#define prn_dev_proc_close_render_device(proc)\
+ int proc(P1(gx_device_printer *))
+ prn_dev_proc_close_render_device((*close_render_device));
+
+ /*
+ * Buffer a page on the output device. A page may or may not have been
+ * fully rendered, but the rasterizer needs to realize the page to free
+ * up resources or support copypage. Printing a page may involve zero or
+ * more buffer_pages. All buffer_page output is overlaid in the buffer
+ * until a terminating print_page or print_page_copies clears the
+ * buffer. Note that, after the first buffer_page, the driver must use
+ * the get_overlay_bits function instead of get_bits. The difference is
+ * that get_overlay_bits requires the caller to supply the same buffered
+ * bitmap that was computed as a result of a previous buffer_page, so
+ * that get_overlay_bits can add further marks to the existing buffered
+ * image. NB that output must be accumulated in buffer even if
+ * num_copies == 0.
+ *
+ * Caller is expected to be gdevprn, calls driver implementation or
+ * default.
+ */
+
+#define prn_dev_proc_buffer_page(proc)\
+ int proc(P3(gx_device_printer *, FILE *, int))
+ prn_dev_proc_buffer_page((*buffer_page));
+
+ /*
+ * Transform a given set of bits by marking it per the current page
+ * description. This is a different version of get_bits, where this
+ * procedure accepts a bitmap and merely adds further marks, without
+ * clearing the bits.
+ *
+ * Driver implementation is expected to be the caller.
+ */
+
+#define prn_dev_proc_get_overlay_bits(proc)\
+ int proc(P4(gx_device_printer *, int, int, byte *))
+ prn_dev_proc_get_overlay_bits((*get_overlay_bits));
+
+ /*
+ * Find out where the band buffer for a given line is going to fall on
+ * the next call to get_bits. This is an alternative to get_overlay_bits
+ * in cases where the client doesn't own a suitably formatted buffer to
+ * deposit bits into. When using this function, do a
+ * locate_overlay_buffer, copy the background data into the returned
+ * buffer, then do get_bits to get the transformed data. IMPORTANT: the
+ * locate_overlay_buffer for a specific range of lines must immediately
+ * be followed by one or more get_bits for the same line range with no
+ * other intervening driver calls. If this condition is violated,
+ * results are undefined.
+ */
+
+#define prn_dev_proc_locate_overlay_buffer(proc)\
+ int proc(P3(gx_device_printer *, int, byte **))
+ prn_dev_proc_locate_overlay_buffer((*locate_overlay_buffer));
+
+} 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 gp_file_name_sizeof
+typedef enum {
+ BandingAuto = 0,
+ BandingAlways,
+ BandingNever
+} gdev_prn_banding_type;
+struct gdev_prn_space_params_s {
+ long MaxBitmap; /* max size of non-buffered bitmap */
+ long BufferSpace; /* space to use for buffer */
+ gx_band_params band; /* see gxclist.h */
+ bool params_are_read_only; /* true if put_params may not modify this struct */
+ gdev_prn_banding_type banding_type; /* used to force banding or bitmap */
+};
+
+#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;\
+ /* ------ Device parameters that must be set ------ */\
+ /* ------ before calling the device open routine. ------ */\
+ gdev_prn_space_params space_params;\
+ char fname[prn_fname_sizeof]; /* OutputFile */\
+ /* ------ Other device parameters ------ */\
+ bool OpenOutputFile;\
+ bool ReopenPerPage;\
+ bool Duplex;\
+ int Duplex_set; /* -1 = not supported */\
+ /* ------ End of parameters ------ */\
+ bool file_is_new; /* true iff file just opened */\
+ FILE *file; /* output file */\
+ long buffer_space; /* amount of space for clist buffer, */\
+ /* 0 means not using clist */\
+ byte *buf; /* buffer for rendering */\
+ /* ---- Begin async rendering support --- */\
+ gs_memory_t *buffer_memory; /* allocator for command list */\
+ gs_memory_t *bandlist_memory; /* allocator for bandlist files */\
+ proc_free_up_bandlist_memory((*free_up_bandlist_memory)); /* if nz, proc to free some bandlist memory */\
+ gx_page_queue *page_queue; /* if <> 0,page queue for gdevprna NOT GC'd */\
+ bool is_async_renderer; /* device is only the rendering part of async device */\
+ gx_device_printer *async_renderer; /* in async writer, pointer to async renderer */\
+ uint clist_disable_mask; /* mask of clist options to disable */\
+ /* ---- End async rendering support --- */\
+ gx_device_procs orig_procs /* original (std_)procs */
+
+/* The device descriptor */
+struct gx_device_printer_s {
+ gx_device_common;
+ gx_prn_device_common;
+};
+
+/* 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);
+
+/* Default printer-specific procedures */
+prn_dev_proc_get_space_params(gdev_prn_default_get_space_params);
+prn_dev_proc_start_render_thread(gx_default_start_render_thread); /* for async rendering only, see gdevprna.c */
+prn_dev_proc_open_render_device(gx_default_open_render_device);
+prn_dev_proc_close_render_device(gx_default_close_render_device);
+prn_dev_proc_buffer_page(gx_default_buffer_page); /* returns an error */
+prn_dev_proc_get_overlay_bits(gdev_prn_get_overlay_bits);
+prn_dev_proc_locate_overlay_buffer(gdev_prn_locate_overlay_buffer);
+
+/* 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, */\
+ NULL, /* get_clipping_box */\
+ NULL, /* begin_typed_image */\
+ NULL, /* map_color_rgb_alpha */\
+ NULL, /* create_compositor */\
+ NULL, /* get_hardware_params */\
+ NULL /* text_begin */\
+}
+
+/* The standard printer device procedures */
+/* (using gdev_prn_open/output_page/close). */
+extern const 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,\
+ gdev_prn_default_get_space_params,\
+ gx_default_start_render_thread,\
+ gx_default_open_render_device,\
+ gx_default_close_render_device,\
+ gx_default_buffer_page,\
+ gdev_prn_get_overlay_bits,\
+ gdev_prn_locate_overlay_buffer\
+ },\
+ { PRN_MAX_BITMAP, PRN_BUFFER_SPACE,\
+ { band_params_initial_values },\
+ 0/*false*/, /* params_are_read_only */\
+ BandingAuto /* banding_type */\
+ },\
+ { 0 }, /* fname */\
+ 0/*false*/, /* OpenOutputFile */\
+ 0/*false*/, /* ReopenPerPage */\
+ 0/*false*/, -1, /* Duplex[_set] */\
+ 0/*false*/, 0, 0, 0, /* file_is_new ... buf */\
+ 0, 0, 0, 0, 0/*false*/, 0, 0, /* buffer_memory ... clist_dis'_mask */\
+ { 0 } /* ... 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,\
+ gdev_prn_default_get_space_params,\
+ gx_default_start_render_thread,\
+ gx_default_open_render_device,\
+ gx_default_close_render_device,\
+ gx_default_buffer_page,\
+ gdev_prn_get_overlay_bits,\
+ gdev_prn_locate_overlay_buffer\
+ },\
+ { PRN_MAX_BITMAP, PRN_BUFFER_SPACE,\
+ { band_params_initial_values },\
+ 0/*false*/, /* params_are_read_only */\
+ BandingAuto /* banding_type */\
+ },\
+ { 0 }, /* fname */\
+ 0/*false*/, /* OpenOutputFile */\
+ 0/*false*/, /* ReopenPerPage */\
+ 0/*false*/, -1, /* Duplex[_set] */\
+ 0/*false*/, 0, 0, 0, /* file_is_new ... buf */\
+ 0, 0, 0, 0, 0/*false*/, 0, 0, /* buffer_memory ... clist_dis'_mask */\
+ { 0 } /* ... 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_positionable(P3(gx_device *dev, bool binary_mode,
+ bool positionable));
+/* open_printer defaults positionable = false */
+int gdev_prn_open_printer(P2(gx_device * dev, bool 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 *));
+
+/* The default print_page_copies procedure just calls print_page */
+/* the given number of times. */
+prn_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 *));
+
+/* Allocate / reallocate / free printer memory. */
+int gdev_prn_allocate_memory(P4(gx_device *pdev,
+ gdev_prn_space_params *space,
+ int new_width, int new_height));
+int gdev_prn_reallocate_memory(P4(gx_device *pdev,
+ gdev_prn_space_params *space,
+ int new_width, int new_height));
+int gdev_prn_free_memory(P1(gx_device *pdev));
+
+/* 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 *, 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/gdevprna.h b/pstoraster/gdevprna.h
new file mode 100644
index 000000000..799a8dc12
--- /dev/null
+++ b/pstoraster/gdevprna.h
@@ -0,0 +1,190 @@
+/* Copyright (C) 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Generic asynchronous printer driver support */
+
+/* Initial version 2/1/1998 by John Desrosiers (soho@crl.com) */
+/* 7/28/98 ghost@aladdin.com - Updated to Ghostscript coding standards. */
+
+#ifndef gdevprna_INCLUDED
+# define gdevprna_INCLUDED
+
+# include "gdevprn.h"
+# include "gxsync.h"
+
+/*
+ * General
+ * -------
+ * Async drivers actually create two separate instances of the device at
+ * the same time. The first (the writer instance) is only used in the
+ * interpretation operation; it feeds rendering commands into the command
+ * lists. The second device instance is used only for rendering the
+ * commands placed into the command list by the writer.
+
+ * The writer builds a command list for an entire page; the command list
+ * is only queued for rendering once a page's command list is completely
+ * built. The only exception to this rule is when the interpreter runs
+ * out of memory, or when no free command list memory is available. In
+ * such cases, the interpreter queues a "partial page" consisting of all
+ * command list data written so far, plus a command indicating that the
+ * page description is not complete. After queuing the partial page, the
+ * interpereter waits until the rendering process has freed enough
+ * command list memory to enable the interpreter to proceed.
+
+ * To avoid deadlocks when the system runs out of memory, special
+ * memory allocation provisions are made on both the writer and
+ * renderer sides. On the writer side, enough "reserve" bandlist
+ * memory is set aside at startup time to cover the needs of queuing a
+ * partial page to the renderer. The renderer operates out of a fixed
+ * memory space; that way, it can always complete rendering pages with
+ * the memory it has. To this end, the writer protects the renderer
+ * from consuming unbounded amounts of memory by a) never putting
+ * complex paths into the command list, b) pre-clipping any output
+ * unless the clip path consists of a single rectangle, c) never putting
+ * high-level images into the clip path unless the image in question
+ * meets some very stringent requirements, such as only being rotated by
+ * even multiples of 90 degrees and having source-image data rows which
+ * fit into the command buffer in one piece. These restrictions are what
+ * dictate the "restricted bandlist format."
+
+ * Note that the renderer's instance of the device driver uses the
+ * renderer's memory. That implies that it must also operate in a small,
+ * fixed amount of memory, and must do all memory allocation using the
+ * memory allocator pointed to by the render device's ->memory member.
+
+ * Opening the Device
+ * ------------------
+ * The writer instance is opened first. This occurs when the system
+ * calls the "standard" open procedure via the device's procedure
+ * vector. The driver must implement the open function, but must call
+ * down to gdev_prn_async_write_open instead of calling down to
+ * gdev_prn_open. Before calling down to gdev_prn_async_write_open, the
+ * driver must:
+ * a - init several procedure vectors, to wit: start_render_thread,
+ * buffer_page, print_page_copies,
+ * b - init space_params.band.BandWidth, space_params.band.BandHeight,
+ * space_params.BufferSpace (see extended comments in gdevasyn.c
+ * for details on computing appropriate values).
+ * c - if it implements those functions, the driver must init the
+ * procedure vectors for: put_params, get_hardware_params,
+ * output_page, open_render_device.
+ * Notice that there are two procedure vectors: the usual std_procs, and
+ * the printer-specific printer_procs.
+
+ * Since partial page support imposes extra requirements on drivers,
+ * such support can be disabled by zeroing out (in the async writer open
+ * routine, after calling down to gdev_prn_async_write_open) the
+ * free_up_bandlist_memory member of the driver structure. Doing so
+ * will, of course, cause interpretation to fail if memory runs out.
+
+ * Once the driver calls down to gdev_prn_async_write_open, the async
+ * support logic will create a second instance of the driver for
+ * rendering, but will not open it just yet. Instead, the async logic
+ * will attempt to synchronize the two device instances.
+
+ * Synchrnonizing the instances
+ * ----------------------------
+ * While still in the gdev_prn_async_write_open routine, the async logic
+ * will call printer_procs.start_render_thread (which the driver is
+ * required to implement). start_render_thread must somehow either start a new
+ * thread or rendez-vous with an existing thread for use in rendering,
+ * then return. start_render_thread must also have caused the render thread
+ * to call gdev_prn_async_render_thread, passing it as an argument a magic
+ * cookie passed to start_render_thread. start_render_thread will only
+ * return once the device has been closed and all renering has been
+ * completed.
+
+ * The render device will be opened on the render device's thread, by
+ * calling printer_procs.open_render_device.
+
+ * Rendering Operation
+ * -------------------
+ * During rendering, the device will not see rendering operations -- the
+ * first "rendering" operations the driver will see is when the renderer
+ * instance's print_page_copies or buffer_page routines get called. In
+ * both cases, the appropriate routine must then perform get_bits calls
+ * on the async logic in order to retrieve rendered bits, then transmit
+ * them to the appropriate device buffers.
+
+ * The complication that is introduced is that which is related to
+ * partial pages: A buffer_page call instructs the driver to grab the
+ * rendered bits, but to keep the rendered bits available for later
+ * instead of marking on media. This implies that a buffer_page call
+ * opens a context where subsequent buffer_page's and print_page_copies'
+ * must first initialize the rendering buffers with the previous
+ * rendering results before calling get_bits. Drivers use the
+ * locate_overlay_buffer function to initialize the driver's rendering
+ * buffers. The first print_page_copies closes the context that was
+ * opened by the initial buffer_page -- the driver must go back to
+ * normal rendering until a new buffer_page comes along.
+ */
+
+/* -------------- Type declarations --------------- */
+
+/* typedef is in gdevprn.h */
+/* typedef struct gdev_prn_start_render_params_s gdev_prn_start_render_params;*/
+struct gdev_prn_start_render_params_s {
+ gx_device_printer *writer_device;/* writer dev that points to render dev */
+ gx_semaphore_t *open_semaphore; /* signal this once open_code is set */
+ int open_code; /* RETURNS status of open of reader device */
+};
+
+/* -------- Macros used to initialize render-specific structures ------ */
+
+#define init_async_render_procs(xpdev, xstart_render_thread,\
+ xbuffer_page, xprint_page_copies)\
+ BEGIN\
+ (xpdev)->printer_procs.start_render_thread = (xstart_render_thread);\
+ (xpdev)->printer_procs.buffer_page = (xbuffer_page);\
+ (xpdev)->printer_procs.print_page_copies = (xprint_page_copies);\
+ END
+
+/* -------------- Global procedure declarations --------- */
+
+/* Open this printer device in ASYNC (overlapped) mode.
+ *
+ * This routine is always called by the concrete device's xx_open routine
+ * in lieu of gdev_prn_open.
+ */
+int gdev_prn_async_write_open(P4(gx_device_printer *pdev, int max_raster,
+ int min_band_height, int max_src_image_row));
+
+/* Open the render portion of a printer device in ASYNC (overlapped) mode.
+ *
+ * This routine is always called by concrete device's xx_open_render_device
+ * in lieu of gdev_prn_open.
+ */
+int gdev_prn_async_render_open(P1(gx_device_printer *prdev));
+
+/*
+ * Must be called by async device driver implementation (see
+ * gdevprna.h under "Synchronizing the Instances"). This is the
+ * rendering loop, which requires its own thread for as long as
+ * the device is open. This proc only returns after the device is closed.
+ */
+int /* rets 0 ok, -ve error code */
+gdev_prn_async_render_thread(P1(gdev_prn_start_render_params *));
+
+#endif /* gdevprna_INCLUDED */
diff --git a/pstoraster/gdevps.c b/pstoraster/gdevps.c
new file mode 100644
index 000000000..5f461193a
--- /dev/null
+++ b/pstoraster/gdevps.c
@@ -0,0 +1,1119 @@
+/* Copyright (C) 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* PostScript-writing driver */
+#include "math_.h"
+#include "memory_.h"
+#include "time_.h"
+#include "gx.h"
+#include "gserrors.h"
+#include "gscdefs.h"
+#include "gsmatrix.h" /* for gsiparam.h */
+#include "gsiparam.h"
+#include "gsline.h"
+#include "gsparam.h"
+#include "gxdevice.h"
+#include "gscspace.h"
+#include "gxdcolor.h"
+#include "gzpath.h"
+#include "gdevpsdf.h"
+#include "gdevpstr.h"
+#include "strimpl.h"
+#include "sa85x.h"
+
+/****************************************************************
+ * Notes:
+ * ASCII85EncodePages should use ASCIIHexEncode if LanguageLevel < 2.
+ * Images are never compressed; in fact, none of the other
+ * Distiller parameters do anything.
+ ****************************************************************/
+
+/* ---------------- Device definition ---------------- */
+
+/* Device procedures */
+private dev_proc_open_device(psw_open);
+private dev_proc_output_page(psw_output_page);
+private dev_proc_close_device(psw_close);
+private dev_proc_copy_mono(psw_copy_mono);
+private dev_proc_copy_color(psw_copy_color);
+private dev_proc_put_params(psw_put_params);
+private dev_proc_get_params(psw_get_params);
+private dev_proc_fill_path(psw_fill_path);
+private dev_proc_stroke_path(psw_stroke_path);
+private dev_proc_fill_mask(psw_fill_mask);
+private dev_proc_begin_image(psw_begin_image);
+
+#define X_DPI 720
+#define Y_DPI 720
+
+typedef struct psw_path_state_s {
+ int num_points; /* # of points since last non-lineto */
+ bool move; /* true iff last non-lineto was moveto */
+ gs_point dprev[2]; /* line deltas before previous point, */
+ /* if num_points - move >= 2 */
+} psw_path_state_t;
+
+typedef struct psw_image_params_s {
+ gx_bitmap_id id;
+ ushort width, height;
+} psw_image_params_t;
+
+typedef struct gx_device_pswrite_s {
+ gx_device_psdf_common;
+ /* Settable parameters */
+#define LanguageLevel_default 2.0
+#define psdf_version_default psdf_version_level2
+ float LanguageLevel;
+ /* End of parameters */
+ bool ProduceEPS;
+ bool first_page;
+ long bbox_position;
+ psdf_binary_writer image_writer;
+#define image_stream image_writer.strm
+#define image_cache_size 197
+#define image_cache_reprobe_step 121
+ psw_image_params_t image_cache[image_cache_size];
+ bool cache_toggle;
+ /* Temporary state while writing a path */
+ psw_path_state_t path_state;
+} gx_device_pswrite;
+
+gs_private_st_suffix_add1_final(st_device_pswrite, gx_device_pswrite,
+ "gx_device_pswrite", device_pswrite_enum_ptrs, device_pswrite_reloc_ptrs,
+ gx_device_finalize, st_device_psdf, image_stream);
+
+#define psw_procs\
+ { psw_open,\
+ gx_upright_get_initial_matrix,\
+ NULL, /* sync_output */\
+ psw_output_page,\
+ psw_close,\
+ gx_default_rgb_map_rgb_color,\
+ gx_default_rgb_map_color_rgb,\
+ gdev_vector_fill_rectangle,\
+ NULL, /* tile_rectangle */\
+ psw_copy_mono,\
+ psw_copy_color,\
+ NULL, /* draw_line */\
+ NULL, /* get_bits */\
+ psw_get_params,\
+ psw_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 */\
+ psw_fill_path,\
+ psw_stroke_path,\
+ psw_fill_mask,\
+ gdev_vector_fill_trapezoid,\
+ gdev_vector_fill_parallelogram,\
+ gdev_vector_fill_triangle,\
+ NULL /****** WRONG ******/, /* draw_thin_line */\
+ psw_begin_image,\
+ NULL, /* image_data */\
+ NULL, /* end_image */\
+ NULL, /* strip_tile_rectangle */\
+ NULL/******psw_strip_copy_rop******/\
+ }
+
+const gx_device_pswrite gs_pswrite_device =
+{std_device_dci_type_body(gx_device_pswrite, 0, "pswrite",
+ &st_device_pswrite,
+ DEFAULT_WIDTH_10THS * X_DPI / 10, DEFAULT_HEIGHT_10THS * Y_DPI / 10,
+ X_DPI, Y_DPI, 3, 24, 255, 255, 256, 256),
+ psw_procs,
+ psdf_initial_values(psdf_version_default, 1 /*true */ ), /* (ASCII85EncodePages) */
+ LanguageLevel_default, /* LanguageLevel */
+ 0 /*false *//* ProduceEPS */
+};
+
+const gx_device_pswrite gs_epswrite_device =
+{std_device_dci_type_body(gx_device_pswrite, 0, "epswrite",
+ &st_device_pswrite,
+ DEFAULT_WIDTH_10THS * X_DPI / 10, DEFAULT_HEIGHT_10THS * Y_DPI / 10,
+ X_DPI, Y_DPI, 3, 24, 255, 255, 256, 256),
+ psw_procs,
+ psdf_initial_values(psdf_version_default, 1 /*true */ ), /* (ASCII85EncodePages) */
+ LanguageLevel_default, /* LanguageLevel */
+ 1 /*true *//* ProduceEPS */
+};
+
+/* Vector device implementation */
+private int
+ psw_beginpage(P1(gx_device_vector * vdev)), psw_setlinewidth(P2(gx_device_vector * vdev, floatp width)),
+ psw_setcolors(P2(gx_device_vector * vdev, const gx_drawing_color * pdc)),
+ psw_dorect(P6(gx_device_vector * vdev, fixed x0, fixed y0, fixed x1, fixed y1,
+ gx_path_type_t type)), psw_beginpath(P2(gx_device_vector * vdev, gx_path_type_t type)),
+ psw_moveto(P6(gx_device_vector * vdev, floatp x0, floatp y0,
+ floatp x, floatp y, gx_path_type_t type)), psw_lineto(P6(gx_device_vector * vdev, floatp x0, floatp y0,
+ floatp x, floatp y, gx_path_type_t type)),
+ psw_curveto(P10(gx_device_vector * vdev, floatp x0, floatp y0,
+ floatp x1, floatp y1, floatp x2, floatp y2,
+ floatp x3, floatp y3, gx_path_type_t type)), psw_closepath(P6(gx_device_vector * vdev, floatp x0, floatp y0,
+ floatp x_start, floatp y_start, gx_path_type_t type)),
+ psw_endpath(P2(gx_device_vector * vdev, gx_path_type_t type));
+private const gx_device_vector_procs psw_vector_procs =
+{
+ /* Page management */
+ psw_beginpage,
+ /* Imager state */
+ psw_setlinewidth,
+ psdf_setlinecap,
+ psdf_setlinejoin,
+ psdf_setmiterlimit,
+ psdf_setdash,
+ psdf_setflat,
+ psdf_setlogop,
+ /* Other state */
+ psw_setcolors, /* fill & stroke colors are the same */
+ psw_setcolors,
+ /* Paths */
+ psdf_dopath,
+ psw_dorect,
+ psw_beginpath,
+ psw_moveto,
+ psw_lineto,
+ psw_curveto,
+ psw_closepath,
+ psw_endpath
+};
+
+/* ---------------- File header ---------------- */
+
+private const char *const psw_ps_header[] =
+{
+ "%!PS-Adobe-3.0",
+ "%%Pages: (atend)",
+ 0
+};
+
+private const char *const psw_eps_header[] =
+{
+ "%!PS-Adobe-3.0 EPSF-3.0",
+ 0
+};
+
+private const char *const psw_header[] =
+{
+ "%%EndComments",
+ "%%BeginProlog",
+ 0
+};
+
+private const char *const psw_prolog[] =
+{
+ "%%BeginResource: procset GS_pswrite_ProcSet",
+ "/GS_pswrite_ProcSet 40 dict dup begin",
+ "/!{bind def}bind def/#{load def}!",
+ /* <rbyte> <gbyte> <bbyte> rG - */
+ /* <graybyte> G - */
+ "/rG{3{3 -1 roll 255 div}repeat setrgbcolor}!/G{255 div setgray}!/K{0 G}!",
+ /* <bbyte> <rgbyte> r6 - */
+ /* <gbyte> <rbbyte> r5 - */
+ /* <rbyte> <gbbyte> r3 - */
+ "/r6{dup 3 -1 roll rG}!/r5{dup 3 1 roll rG}!/r3{dup rG}!",
+ "/w/setlinewidth #/J/setlinecap #",
+ "/j/setlinejoin #/M/setmiterlimit #/d/setdash #/i/setflat #",
+ "/m/moveto #/l/lineto #/c/rcurveto #/h{p closepath}!/H{P closepath}!",
+ /* <dx> lx - */
+ /* <dy> ly - */
+ /* <dx2> <dy2> <dx3> <dy3> v - */
+ /* <dx1> <dy1> <dx2> <dy2> y - */
+ "/lx{0 rlineto}!/ly{0 exch rlineto}!/v{0 0 6 2 roll c}!/y{2 copy c}!",
+ /* <x> <y> <dx> <dy> re - */
+ "/re{4 -2 roll m exch dup lx exch ly neg lx h}!",
+ /* <x> <y> <a> <b> ^ <x> <y> <a> <b> <-a> <-y> */
+ "/^{3 index neg 3 index neg}!",
+ /* <x> <y> <dx1> <dy1> ... <dxn> <dyn> P - */
+ "/P{count 0 gt{count -2 roll moveto p}if}!",
+ /* <dx1> <dy1> ... <dxn> <dyn> p - */
+ "/p{count 2 idiv{count -2 roll rlineto}repeat}!",
+"/f{P fill}!/f*{P eofill}!/S{P stroke}!/q/gsave #/Q/grestore #/rf{re fill}!",
+ "/Y{initclip P clip newpath}!/Y*{initclip P eoclip newpath}!/rY{re Y}!",
+ /* <w> <h> <name> <length> <src> | <w> <h> <data> */
+ "/|{exch string readstring pop exch 4 1 roll 3 packedarray cvx exch 1 index def exec}!",
+ /* <w> <?> <name> (<length>|) + <w> <?> <name> <length> */
+ "/+{dup type/nametype eq{2 index 7 add -3 bitshift 2 index mul}if}!",
+ /* <w> <h> <name> (<length>|) $ <w> <h> <data> */
+ "/@/currentfile #/${+ @ |}!",
+ /* <x> <y> <w> <h> <bpc/inv> <src> Ix <w> <h> <bps/inv> <mtx> <src> */
+ "/Ix{[1 0 0 1 11 -2 roll exch neg exch neg]exch}!",
+ /* <x> <y> <h> <src> , - */
+ /* <x> <y> <h> <src> If - */
+ /* <x> <y> <h> <src> I - */
+"/,{true exch Ix imagemask}!/If{false exch Ix imagemask}!/I{exch Ix image}!",
+ 0
+};
+
+private const char *const psw_1_prolog[] =
+{
+ 0
+};
+
+private const char *const psw_1_5_prolog[] =
+{
+ "/Ic{exch Ix false 3 colorimage}!",
+ 0
+};
+
+private const char *const psw_2_prolog[] =
+{
+ /* <src> <w> <h> F <g4src> */
+ "/F{<</Columns 4 2 roll/Rows exch/K -1/BlackIs1 true >>/CCITTFaxDecode filter}!",
+ /* <src> X <a85src> */
+ /* - @X <a85src> */
+ /* <w> <h> <src> +F <w> <h> <g4src> */
+ /* <w> <h> +F <w> <h> <g4src> */
+ /* <w> <h> @F <w> <h> <g4src> */
+ /* <w> <h> @C <w> <h> <g4a85src> */
+ "/X{/ASCII85Decode filter}!/@X{@ X}!/+F{2 index 2 index F}!/@F{@ +F}!/@C{@X +F}!",
+ /* <w> <h> <name> (<length>|) $X <w> <h> <data> */
+ /* <w> <h> <?> <?> <src> -F <w> <h> <?> <?> <g4src> */
+ /* <w> <h> <name> (<length>|) $F <w> <h> <data> */
+ /* <w> <h> <name> (<length>|) $C <w> <h> <data> */
+ "/$X{+ @X |}!/-F{4 index 4 index F}!/$F{+ @ -F |}!/$C{+ @X -F |}!",
+ 0
+};
+
+private const char *const psw_end_prolog[] =
+{
+ "end def",
+ "%%EndResource",
+ "%%EndProlog",
+ 0
+};
+
+private void
+psw_put_lines(stream * s, const char *const lines[])
+{
+ int i;
+
+ for (i = 0; lines[i] != 0; ++i)
+ pprints1(s, "%s\n", lines[i]);
+}
+
+/* ---------------- Utilities ---------------- */
+
+/* Reset the image cache. */
+private void
+image_cache_reset(gx_device_pswrite * pdev)
+{
+ int i;
+
+ for (i = 0; i < image_cache_size; ++i)
+ pdev->image_cache[i].id = gx_no_bitmap_id;
+ pdev->cache_toggle = false;
+}
+
+/* Look up or enter image parameters in the cache. */
+/* Return -1 if the key is not in the cache, or its index. */
+/* If id is gx_no_bitmap_id or enter is false, do not enter it. */
+private int
+image_cache_lookup(gx_device_pswrite * pdev, gx_bitmap_id id,
+ int width, int height, bool enter)
+{
+ int i1, i2;
+ psw_image_params_t *pip1;
+ psw_image_params_t *pip2;
+
+ if (id == gx_no_bitmap_id)
+ return -1;
+ i1 = id % image_cache_size;
+ pip1 = &pdev->image_cache[i1];
+ if (pip1->id == id && pip1->width == width && pip1->height == height) {
+ return i1;
+ }
+ i2 = (i1 + image_cache_reprobe_step) % image_cache_size;
+ pip2 = &pdev->image_cache[i2];
+ if (pip2->id == id && pip2->width == width && pip2->height == height) {
+ return i2;
+ }
+ if (enter) {
+ int i = ((pdev->cache_toggle = !pdev->cache_toggle) ? i2 : i1);
+ psw_image_params_t *pip = &pdev->image_cache[i];
+
+ pip->id = id, pip->width = width, pip->height = height;
+ return i;
+ }
+ return -1;
+}
+
+/* Prepare the encoding stream for image data. */
+/* Return 1 if we are using ASCII85 encoding. */
+private int
+psw_image_stream_setup(gx_device_pswrite * pdev)
+{
+ int code =
+ psdf_begin_binary((gx_device_psdf *) pdev, &pdev->image_writer);
+
+ return
+ (code < 0 ? code :
+ pdev->image_stream->state->template == &s_A85E_template ? 1 : 0);
+}
+
+/* Clean up after writing an image. */
+private void
+psw_image_cleanup(gx_device_pswrite * pdev)
+{
+ if (pdev->image_stream != 0) {
+ psdf_end_binary(&pdev->image_writer);
+ pdev->image_stream = 0;
+ }
+}
+
+/* Write data for an image. Assumes width > 0, height > 0. */
+/****** IGNORES data_x ******/
+private void
+psw_put_bits(stream * s, const byte * data, int data_x_bit, uint raster,
+ uint width_bits, int height)
+{
+ int y;
+
+ for (y = 0; y < height; ++y)
+ pwrite(s, data + (data_x_bit >> 3) + y * raster,
+ (width_bits + 7) >> 3);
+}
+private int
+psw_image_write(gx_device_pswrite * pdev, const char *imagestr,
+ const byte * data, int data_x, uint raster, gx_bitmap_id id,
+ int x, int y, int width, int height, int depth)
+{
+ stream *s = gdev_vector_stream((gx_device_vector *) pdev);
+ uint width_bits = width * depth;
+ int data_x_bit = data_x * depth;
+ int index = image_cache_lookup(pdev, id, width_bits, height, false);
+ char str[40];
+ int code, encode;
+
+ if (index >= 0) {
+ sprintf(str, "%d%c", index / 26, index % 26 + 'A');
+ pprintd2(s, "%d %d ", x, y);
+ pprints2(s, "%s %s\n", str, imagestr);
+ return 0;
+ }
+ pprintd4(s, "%d %d %d %d ", x, y, width, height);
+ encode = code = psw_image_stream_setup(pdev);
+ if (code < 0)
+ return code;
+ if (depth == 1 && width > 16) {
+ /*
+ * We should really look at the statistics of the image before
+ * committing to using G4 encoding....
+ */
+ code = psdf_CFE_binary(&pdev->image_writer, width, height, false);
+ if (code < 0)
+ return code;
+ encode += 2;
+ }
+ if (id == gx_no_bitmap_id || width_bits * (ulong) height > 8000) {
+ const char *const uncached[4] =
+ {
+ "@", "@X", "@F", "@C"
+ };
+
+ pprints2(s, "%s %s\n", uncached[encode], imagestr);
+ psw_put_bits(pdev->image_stream, data, data_x_bit, raster,
+ width_bits, height);
+ psw_image_cleanup(pdev);
+ spputc(s, '\n');
+ } else {
+ const char *const cached[4] =
+ {
+ "$", "$X", "$F", "$C"
+ };
+
+ index = image_cache_lookup(pdev, id, width_bits, height, true);
+ sprintf(str, "/%d%c ", index / 26, index % 26 + 'A');
+ pputs(s, str);
+ if (depth != 1)
+ pprintld1(s, "%ld ", ((width_bits + 7) >> 3) * (ulong) height);
+ pprints1(s, "%s\n", cached[encode]);
+ psw_put_bits(pdev->image_stream, data, data_x_bit, raster,
+ width_bits, height);
+ psw_image_cleanup(pdev);
+ pprints1(s, "\n%s\n", imagestr);
+ }
+ return 0;
+}
+
+/* Print a matrix. */
+private void
+psw_put_matrix(stream * s, const gs_matrix * pmat)
+{
+ pprintg6(s, "[%g %g %g %g %g %g]",
+ pmat->xx, pmat->xy, pmat->yx, pmat->yy, pmat->tx, pmat->ty);
+}
+
+/* ---------------- Vector device implementation ---------------- */
+
+#define pdev ((gx_device_pswrite *)vdev)
+
+private int
+psw_beginpage(gx_device_vector * vdev)
+{
+ stream *s = vdev->strm;
+ long page = vdev->PageCount + 1;
+
+ if (pdev->first_page) {
+ psw_put_lines(s,
+ (pdev->ProduceEPS ? psw_eps_header : psw_ps_header));
+ if (ftell(vdev->file) < 0) { /* File is not seekable. */
+ pdev->bbox_position = -1;
+ pputs(s, "%%BoundingBox: (atend)\n");
+ pputs(s, "%%HiResBoundingBox: (atend)\n");
+ } else { /* File is seekable, leave room to rewrite bbox. */
+ pdev->bbox_position = stell(s);
+ pputs(s, "%...............................................................\n");
+ pputs(s, "%...............................................................\n");
+ }
+ pprints1(s, "%%%%Creator: %s ", gs_product);
+ pprintld1(s, "%ld ", (long)gs_revision);
+ pprints1(s, "(%s)\n", vdev->dname);
+ {
+ struct tm tms;
+ time_t t;
+ char date_str[25];
+
+ time(&t);
+ tms = *localtime(&t);
+ sprintf(date_str, "%d/%02d/%02d %02d:%02d:%02d",
+ tms.tm_year + 1900, tms.tm_mon + 1, tms.tm_mday,
+ tms.tm_hour, tms.tm_min, tms.tm_sec);
+ pprints1(s, "%%%%CreationDate: %s\n", date_str);
+ }
+ if (pdev->params.ASCII85EncodePages)
+ pputs(s, "%%DocumentData: Clean7Bit\n");
+ if (pdev->LanguageLevel == 2.0)
+ pputs(s, "%%LanguageLevel: 2\n");
+ else if (pdev->LanguageLevel == 1.5)
+ pputs(s, "%%Extensions: CMYK\n");
+ psw_put_lines(s, psw_header);
+ psw_put_lines(s, psw_prolog);
+ if (pdev->LanguageLevel < 1.5)
+ psw_put_lines(s, psw_1_prolog);
+ else {
+ psw_put_lines(s, psw_1_5_prolog);
+ if (pdev->LanguageLevel > 1.5)
+ psw_put_lines(s, psw_2_prolog);
+ }
+ psw_put_lines(s, psw_end_prolog);
+ }
+ pprintld2(s, "%%%%Page: %ld %ld\n%%%%BeginPageSetup\n", page, page);
+ pprintg2(s, "/pagesave save def GS_pswrite_ProcSet begin %g %g scale\n%%%%EndPageSetup\n",
+ 72.0 / vdev->HWResolution[0], 72.0 / vdev->HWResolution[1]);
+ return 0;
+}
+
+private int
+psw_setlinewidth(gx_device_vector * vdev, floatp width)
+{ /*
+ * The vector scale is 1, but we have to rescale the line width
+ * (which is given in device pixels) to account for the actual
+ * page scaling in effect.
+ */
+ return psdf_setlinewidth(vdev, width * 72.0 / vdev->HWResolution[1]);
+}
+
+private int
+psw_setcolors(gx_device_vector * vdev, const gx_drawing_color * pdc)
+{
+ if (!gx_dc_is_pure(pdc))
+ return_error(gs_error_rangecheck);
+ /* PostScript only keeps track of a single color. */
+ vdev->fill_color = *pdc;
+ vdev->stroke_color = *pdc;
+ {
+ stream *s = gdev_vector_stream(vdev);
+ gx_color_index color = gx_dc_pure_color(pdc);
+ int r = color >> 16;
+ int g = (color >> 8) & 0xff;
+ int b = color & 0xff;
+
+ if (r == g && g == b) {
+ if (r == 0)
+ pputs(s, "K\n");
+ else
+ pprintd1(s, "%d G\n", r);
+ } else if (r == g)
+ pprintd2(s, "%d %d r6\n", b, r);
+ else if (g == b)
+ pprintd2(s, "%d %d r3\n", r, g);
+ else if (r == b)
+ pprintd2(s, "%d %d r5\n", g, b);
+ else
+ pprintd3(s, "%d %d %d rG\n", r, g, b);
+ }
+ return 0;
+}
+
+/* Redefine dorect to recognize rectangle fills. */
+private int
+psw_dorect(gx_device_vector * vdev, fixed x0, fixed y0, fixed x1, fixed y1,
+ gx_path_type_t type)
+{
+ if ((type & ~gx_path_type_rule) != gx_path_type_fill)
+ return psdf_dorect(vdev, x0, y0, x1, y1, type);
+ pprintg4(gdev_vector_stream(vdev), "%g %g %g %g rf\n",
+ fixed2float(x0), fixed2float(y0),
+ fixed2float(x1 - x0), fixed2float(y1 - y0));
+ return 0;
+}
+
+/*
+ * We redefine path tracing to use a compact form for polygons; also,
+ * we only need to write coordinates with 2 decimals of precision,
+ * since this is 10 times more precise than any existing output device.
+ */
+#define round_coord(v) (floor((v) * 100 + 0.5) / 100.0)
+private void
+print_coord2(stream * s, floatp x, floatp y, const char *str)
+{
+ pprintg2(s, "%g %g ", round_coord(x), round_coord(y));
+ if (str != 0)
+ pputs(s, str);
+}
+#undef round_coord
+
+private int
+psw_beginpath(gx_device_vector * vdev, gx_path_type_t type)
+{
+ pdev->path_state.num_points = 0;
+ pdev->path_state.move = false;
+ return 0;
+}
+
+private int
+psw_moveto(gx_device_vector * vdev, floatp x0, floatp y0, floatp x, floatp y,
+ gx_path_type_t type)
+{
+ stream *s = gdev_vector_stream(vdev);
+
+ if (pdev->path_state.num_points > 1)
+ pputs(s, (pdev->path_state.move ? "P\n" : "p\n"));
+ print_coord2(s, x, y, NULL);
+ pdev->path_state.num_points = 1;
+ pdev->path_state.move = true;
+ return 0;
+}
+
+private int
+psw_lineto(gx_device_vector * vdev, floatp x0, floatp y0, floatp x, floatp y,
+ gx_path_type_t type)
+{
+ double dx = x - x0, dy = y - y0;
+
+ /*
+ * Omit null lines when filling.
+ ****** MAYBE WRONG IF PATH CONSISTS ONLY OF NULL LINES. ******
+ */
+ if (dx != 0 || dy != 0) {
+ stream *s = gdev_vector_stream(vdev);
+
+ if (pdev->path_state.num_points - pdev->path_state.move >= 2 &&
+ dx == -pdev->path_state.dprev[1].x &&
+ dy == -pdev->path_state.dprev[1].y
+ )
+ pputs(s, "^ ");
+ else
+ print_coord2(s, dx, dy, NULL);
+ pdev->path_state.num_points++;
+ pdev->path_state.dprev[1] = pdev->path_state.dprev[0];
+ pdev->path_state.dprev[0].x = dx;
+ pdev->path_state.dprev[0].y = dy;
+ }
+ return 0;
+}
+
+private int
+psw_curveto(gx_device_vector * vdev, floatp x0, floatp y0,
+ floatp x1, floatp y1, floatp x2, floatp y2, floatp x3, floatp y3,
+ gx_path_type_t type)
+{
+ stream *s = gdev_vector_stream(vdev);
+ double dx1 = x1 - x0, dy1 = y1 - y0;
+ double dx2 = x2 - x0, dy2 = y2 - y0;
+ double dx3 = x3 - x0, dy3 = y3 - y0;
+
+ if (pdev->path_state.num_points > 0)
+ pputs(s, (pdev->path_state.move ?
+ (pdev->path_state.num_points == 1 ? "m\n" : "P\n") :
+ "p\n"));
+ if (dx1 == 0 && dy1 == 0) {
+ print_coord2(s, dx2, dy2, NULL);
+ print_coord2(s, dx3, dy3, "v\n");
+ } else if (x3 == x2 && y3 == y2) {
+ print_coord2(s, dx1, dy1, NULL);
+ print_coord2(s, dx2, dy2, "y\n");
+ } else {
+ print_coord2(s, dx1, dy1, NULL);
+ print_coord2(s, dx2, dy2, NULL);
+ print_coord2(s, dx3, dy3, "c\n");
+ }
+ pdev->path_state.num_points = 0;
+ pdev->path_state.move = false;
+ return 0;
+}
+
+private int
+psw_closepath(gx_device_vector * vdev, floatp x0, floatp y0,
+ floatp x_start, floatp y_start, gx_path_type_t type)
+{
+ pputs(gdev_vector_stream(vdev),
+ (pdev->path_state.num_points > 0 && pdev->path_state.move ?
+ "H\n" : "h\n"));
+ pdev->path_state.num_points = 0;
+ pdev->path_state.move = false;
+ return 0;
+}
+
+private int
+psw_endpath(gx_device_vector * vdev, gx_path_type_t type)
+{
+ stream *s = vdev->strm;
+ const char *star = (type & gx_path_type_even_odd ? "*" : "");
+
+ if (pdev->path_state.num_points > 1 && !pdev->path_state.move)
+ pputs(s, "p ");
+ if (type & gx_path_type_fill) {
+ if (type & (gx_path_type_stroke | gx_path_type_clip))
+ pprints1(s, "q f%s Q ", star);
+ else
+ pprints1(s, "f%s\n", star);
+ }
+ if (type & gx_path_type_stroke) {
+ if (type & gx_path_type_clip)
+ pputs(s, "q S Q ");
+ else
+ pputs(s, "S\n");
+ }
+ if (type & gx_path_type_clip)
+ pprints1(s, "Y%s\n", star);
+ return 0;
+}
+
+#undef pdev
+
+/* ---------------- Driver procedures ---------------- */
+
+#define vdev ((gx_device_vector *)dev)
+#define pdev ((gx_device_pswrite *)dev)
+
+/* ------ Open/close/page ------ */
+
+/* Open the device. */
+private int
+psw_open(gx_device * dev)
+{
+ vdev->v_memory = dev->memory;
+/****** WRONG ******/
+ vdev->vec_procs = &psw_vector_procs;
+ {
+ int code = gdev_vector_open_file_bbox(vdev, 512, true);
+
+ if (code < 0)
+ return code;
+ }
+ gdev_vector_init(vdev);
+ pdev->first_page = true;
+ pdev->binary_ok = !pdev->params.ASCII85EncodePages;
+ image_cache_reset(pdev);
+ return 0;
+}
+
+/* Wrap up ("output") a page. */
+private int
+psw_output_page(gx_device * dev, int num_copies, int flush)
+{
+ stream *s = gdev_vector_stream(vdev);
+
+ if (num_copies != 1)
+ pprintd1(s, "userdict /#copies %d put\n", num_copies);
+ pprints1(s, "end %s pagesave restore\n%%%%PageTrailer\n",
+ (flush ? "showpage" : "copypage"));
+ sflush(s);
+ vdev->in_page = false;
+ pdev->first_page = false;
+ gdev_vector_reset(vdev);
+ image_cache_reset(pdev);
+ return 0;
+}
+
+/* Close the device. */
+/* Note that if this is being called as a result of finalization, */
+/* the stream may no longer exist; but the file will still be open. */
+private int
+psw_close(gx_device * dev)
+{
+ FILE *f = vdev->file;
+
+ fprintf(f, "%%%%Trailer\n%%%%Pages: %ld\n", dev->PageCount);
+ {
+ gs_rect bbox;
+ long save_pos;
+
+ gx_device_bbox_bbox(vdev->bbox_device, &bbox);
+ if (pdev->bbox_position >= 0) {
+ save_pos = ftell(f);
+ fseek(f, pdev->bbox_position, SEEK_SET);
+ }
+ fprintf(f, "%%%%BoundingBox: %d %d %d %d\n",
+ (int)floor(bbox.p.x), (int)floor(bbox.p.y),
+ (int)ceil(bbox.q.x), (int)ceil(bbox.q.y));
+ fprintf(f, "%%%%HiResBoundingBox: %f %f %f %f\n",
+ bbox.p.x, bbox.p.y, bbox.q.x, bbox.q.y);
+ if (pdev->bbox_position >= 0) {
+ fputc('%', f);
+ fseek(f, save_pos, SEEK_SET);
+ }
+ }
+ if (!pdev->ProduceEPS)
+ fputs("%%EOF\n", f);
+ gdev_vector_close_file(vdev);
+ return 0;
+}
+
+/* ---------------- Get/put parameters ---------------- */
+
+/* Get parameters. */
+private int
+psw_get_params(gx_device * dev, gs_param_list * plist)
+{
+ int code = gdev_psdf_get_params(dev, plist);
+ int ecode;
+
+ if (code < 0)
+ return code;
+ if ((ecode = param_write_float(plist, "LanguageLevel", &pdev->LanguageLevel)) < 0)
+ return ecode;
+ return code;
+}
+
+/* Put parameters. */
+private int
+psw_put_params(gx_device * dev, gs_param_list * plist)
+{
+ int ecode = 0;
+ int code;
+ gs_param_name param_name;
+ float ll = pdev->LanguageLevel;
+ psdf_version save_version = pdev->version;
+
+ switch (code = param_read_float(plist, (param_name = "LanguageLevel"), &ll)) {
+ case 0:
+ if (ll == 1.0 || ll == 1.5 || ll == 2.0)
+ break;
+ code = gs_error_rangecheck;
+ default:
+ ecode = code;
+ param_signal_error(plist, param_name, ecode);
+ case 1:
+ ;
+ }
+
+ if (ecode < 0)
+ return ecode;
+ /*
+ * We have to set version to the new value, because the set of
+ * legal parameter values for psdf_put_params varies according to
+ * the version.
+ */
+ {
+ static const psdf_version vv[3] =
+ {
+ psdf_version_level1, psdf_version_level1_color,
+ psdf_version_level2
+ };
+
+ pdev->version = vv[(int)(ll * 2) - 2];
+ }
+ code = gdev_psdf_put_params(dev, plist);
+ if (code < 0) {
+ pdev->version = save_version;
+ return code;
+ }
+ pdev->LanguageLevel = ll;
+ return code;
+}
+
+/* ---------------- Images ---------------- */
+
+/* Copy a monochrome bitmap. */
+private int
+psw_copy_mono(gx_device * dev, const byte * data,
+ int data_x, int raster, gx_bitmap_id id, int x, int y, int w, int h,
+ gx_color_index zero, gx_color_index one)
+{
+ gx_drawing_color color;
+ const char *op;
+ int code = 0;
+
+ if (w <= 0 || h <= 0)
+ return 0;
+ (*dev_proc(vdev->bbox_device, copy_mono))
+ ((gx_device *) vdev->bbox_device, data, data_x, raster, id,
+ x, y, w, h, zero, one);
+ if (one == gx_no_color_index) {
+ color_set_pure(&color, zero);
+ code = gdev_vector_update_fill_color((gx_device_vector *) pdev,
+ &color);
+ op = "If";
+ } else if (zero == vdev->black && one == vdev->white)
+ op = "1 I";
+ else {
+ if (zero != gx_no_color_index) {
+ code = (*dev_proc(dev, fill_rectangle)) (dev, x, y, w, h, zero);
+ if (code < 0)
+ return code;
+ }
+ color_set_pure(&color, one);
+ code = gdev_vector_update_fill_color((gx_device_vector *) pdev,
+ &color);
+ op = ",";
+ }
+ if (code < 0)
+ return 0;
+ return psw_image_write(pdev, op, data, data_x, raster, id,
+ x, y, w, h, 1);
+}
+
+/* Copy a color bitmap. */
+private int
+psw_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)
+{
+ int depth = dev->color_info.depth;
+ const byte *bits = data + data_x * 3;
+ char op[6];
+
+ if (w <= 0 || h <= 0)
+ return 0;
+ (*dev_proc(vdev->bbox_device, copy_color))
+ ((gx_device *) vdev->bbox_device, data, data_x, raster, id,
+ x, y, w, h);
+ /*
+ * If this is a 1-pixel-high image, check for it being all the
+ * same color, and if so, fill it as a rectangle.
+ */
+ if (h == 1 && !memcmp(bits, bits + 3, (w - 1) * 3)) {
+ return (*dev_proc(dev, fill_rectangle))
+ (dev, x, y, w, h, (bits[0] << 16) + (bits[1] << 8) + bits[2]);
+ }
+ sprintf(op, "%d Ic", depth / 3); /* RGB */
+ return psw_image_write(pdev, op, data, data_x, raster, id,
+ x, y, w, h, depth);
+}
+
+/* Fill or stroke a path. */
+/* We redefine these to skip empty paths. */
+private int
+psw_fill_path(gx_device * dev, const gs_imager_state * pis,
+ gx_path * ppath, const gx_fill_params * params,
+ const gx_device_color * pdevc, const gx_clip_path * pcpath)
+{
+ if (gx_path_is_void(ppath))
+ return 0;
+ return gdev_vector_fill_path(dev, pis, ppath, params, pdevc, pcpath);
+}
+private int
+psw_stroke_path(gx_device * dev, const gs_imager_state * pis,
+ gx_path * ppath, const gx_stroke_params * params,
+ const gx_device_color * pdevc, const gx_clip_path * pcpath)
+{
+ if (gx_path_is_void(ppath) &&
+ (gx_path_is_null(ppath) ||
+ gs_currentlinecap((const gs_state *)pis) != gs_cap_round)
+ )
+ return 0;
+ return gdev_vector_stroke_path(dev, pis, ppath, params, pdevc, pcpath);
+}
+
+/* Fill a mask. */
+private int
+psw_fill_mask(gx_device * dev,
+ const byte * data, int data_x, int raster, gx_bitmap_id id,
+ int x, int y, int w, int h,
+ const gx_drawing_color * pdcolor, int depth,
+ gs_logical_operation_t lop, const gx_clip_path * pcpath)
+{
+ if (w <= 0 || h <= 0)
+ return 0;
+ if (depth > 1 ||
+ gdev_vector_update_fill_color(vdev, pdcolor) < 0 ||
+ gdev_vector_update_clip_path(vdev, pcpath) < 0 ||
+ gdev_vector_update_log_op(vdev, lop) < 0
+ )
+ return gx_default_fill_mask(dev, data, data_x, raster, id,
+ x, y, w, h, pdcolor, depth, lop, pcpath);
+ (*dev_proc(vdev->bbox_device, fill_mask))
+ ((gx_device *) vdev->bbox_device, data, data_x, raster, id,
+ x, y, w, h, pdcolor, depth, lop, pcpath);
+ return psw_image_write(pdev, ",", data, data_x, raster, id,
+ x, y, w, h, 1);
+}
+
+/* ---------------- High-level images ---------------- */
+
+private image_enum_proc_plane_data(psw_image_plane_data);
+private image_enum_proc_end_image(psw_image_end_image);
+private const gx_image_enum_procs_t psw_image_enum_procs =
+{
+ psw_image_plane_data, psw_image_end_image
+};
+
+/* Start processing an image. */
+private int
+psw_begin_image(gx_device * dev,
+ const gs_imager_state * pis, const gs_image_t * pim,
+ gs_image_format_t format, const gs_int_rect * prect,
+ const gx_drawing_color * pdcolor, const gx_clip_path * pcpath,
+ gs_memory_t * mem, gx_image_enum_common_t ** pinfo)
+{
+ gdev_vector_image_enum_t *pie =
+ gs_alloc_struct(mem, gdev_vector_image_enum_t,
+ &st_vector_image_enum, "psw_begin_image");
+ const gs_color_space *pcs = pim->ColorSpace;
+ gs_color_space_index index;
+ int num_components;
+ bool can_do = prect == 0;
+ int code;
+
+ if (pie == 0)
+ return_error(gs_error_VMerror);
+ pie->memory = mem;
+ *pinfo = (gx_image_enum_common_t *) pie;
+ if (!pim->ImageMask) {
+ index = gs_color_space_get_index(pcs);
+ num_components = gs_color_space_num_components(pcs);
+ }
+ if (pdev->LanguageLevel < 2 && !pim->ImageMask) { /*
+ * Restrict ourselves to Level 1 images: device color spaces, [0
+ * 1] decode, bits per component <= 8, no CombineWithColor.
+ */
+ if (pim->BitsPerComponent > 8 || pim->CombineWithColor)
+ can_do = false;
+ else {
+ int i;
+
+ switch (index) {
+ case gs_color_space_index_DeviceGray:
+ case gs_color_space_index_DeviceRGB:
+ case gs_color_space_index_DeviceCMYK:
+ for (i = 0; i < num_components * 2; ++i)
+ if (pim->Decode[i] != (i & 1))
+ can_do = false;
+ break;
+ default:
+ can_do = false;
+ }
+ }
+ }
+ if (!can_do ||
+ gdev_vector_begin_image(vdev, pis, pim, format, prect, pdcolor,
+ pcpath, mem, &psw_image_enum_procs, pie) < 0 ||
+ (code = psw_image_stream_setup(pdev)) < 0
+ )
+ return gx_default_begin_image(dev, pis, pim, format, prect,
+ pdcolor, pcpath, mem,
+ &pie->default_info);
+ /* Write the image/colorimage/imagemask preamble. */
+ {
+ stream *s = gdev_vector_stream((gx_device_vector *) pdev);
+ const char *source = (code ? "@X" : "@");
+ gs_matrix imat;
+
+ pputs(s, "q");
+ (*dev_proc(dev, get_initial_matrix)) (dev, &imat);
+ gs_matrix_scale(&imat, 72.0 / dev->HWResolution[0],
+ 72.0 / dev->HWResolution[1], &imat);
+ gs_matrix_invert(&imat, &imat);
+ gs_matrix_multiply(&ctm_only(pis), &imat, &imat);
+ psw_put_matrix(s, &imat);
+ pprintd2(s, "concat\n%d %d ", pie->width, pie->height);
+ if (pim->ImageMask) {
+ pputs(s, (pim->Decode[0] == 0 ? "false" : "true"));
+ psw_put_matrix(s, &pim->ImageMatrix);
+ pprints1(s, "%s imagemask\n", source);
+ } else {
+ pprintd1(s, "%d", pim->BitsPerComponent);
+ psw_put_matrix(s, &pim->ImageMatrix);
+ if (index == gs_color_space_index_DeviceGray)
+ pprints1(s, "%s image\n", source);
+ else {
+ if (format == gs_image_format_chunky)
+ pprints1(s, "%s false", source);
+ else
+ pprints2(s, "%s %strue", source,
+ "dup dup dup " + (16 - num_components * 4));
+ pprintd1(s, " %d colorimage\n", num_components);
+ }
+ }
+ }
+ return 0;
+}
+
+/* Process the next piece of an image. */
+private int
+psw_image_plane_data(gx_device * dev,
+ gx_image_enum_common_t * info, const gx_image_plane_t * planes, int height)
+{
+ gdev_vector_image_enum_t *pie = (gdev_vector_image_enum_t *) info;
+
+ if (pie->default_info)
+ return gx_image_plane_data(pie->default_info, planes, height);
+ gx_image_plane_data(pie->bbox_info, planes, height);
+ {
+ int pi;
+
+ for (pi = 0; pi < pie->num_planes; ++pi)
+ psw_put_bits(pdev->image_stream, planes[pi].data,
+ planes[pi].data_x * info->plane_depths[pi],
+ planes[pi].raster,
+ pie->width * info->plane_depths[pi],
+ height);
+ }
+ return (pie->y += height) >= pie->height;
+}
+
+/* Clean up by releasing the buffers. */
+private int
+psw_image_end_image(gx_device * dev, gx_image_enum_common_t * info,
+ bool draw_last)
+{
+ gdev_vector_image_enum_t *pie = (gdev_vector_image_enum_t *) info;
+ int code;
+
+ code = gdev_vector_end_image(vdev, pie, draw_last, pdev->white);
+ if (code > 0) {
+ psw_image_cleanup(pdev);
+ pputs(pdev->strm, "\nQ\n");
+ }
+ return code;
+}
diff --git a/pstoraster/gdevpsde.c b/pstoraster/gdevpsde.c
new file mode 100644
index 000000000..9389b60ab
--- /dev/null
+++ b/pstoraster/gdevpsde.c
@@ -0,0 +1,282 @@
+/* Copyright (C) 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Embedded font writing */
+#include "memory_.h"
+#include "gx.h"
+#include "gserrors.h"
+#include "gsccode.h"
+#include "gsmatrix.h"
+#include "gxfixed.h"
+#include "gxfont.h"
+#include "gxfont1.h"
+#include "stream.h"
+#include "gdevpstr.h"
+#include "gdevpsdf.h"
+
+private int
+embed_table(gs_param_list * plist, const char *key, const float *values,
+ int count)
+{
+ if (count != 0) {
+ gs_param_float_array fa;
+
+ fa.size = count;
+ fa.data = values;
+ return param_write_float_array(plist, key, &fa);
+ }
+ return 0;
+}
+
+private void
+embed_uid(stream * s, const gs_uid * puid)
+{
+ if (uid_is_UniqueID(puid))
+ pprintld1(s, "/UniqueID %ld def\n", puid->id);
+ else if (uid_is_XUID(puid)) {
+ uint i, n = uid_XUID_size(puid);
+
+ pputs(s, "/XUID [");
+ for (i = 0; i < n; ++i)
+ pprintld1(s, "%ld ", uid_XUID_values(puid)[i]);
+ pputs(s, "] def\n");
+ }
+}
+
+/* Write an embedded Type 1 font. */
+int
+psdf_embed_type1_font(stream * s, gs_font_type1 * pfont)
+{
+ const gs_type1_data *const pdata = &pfont->data;
+ gs_param_list *plist;
+ param_printer_params_t ppp;
+ int code;
+
+ ppp = param_printer_params_default;
+ ppp.item_suffix = " def\n";
+ code = psdf_alloc_param_printer(&plist, &ppp, s,
+ print_binary_ok, s->memory);
+ if (code < 0)
+ return 0;
+
+ /* Write the font header. */
+
+ pputs(s, "%!PS-AdobeFont-1.0: ");
+ pwrite(s, pfont->font_name.chars, pfont->font_name.size);
+ pputs(s, "\n11 dict begin\n");
+
+ /* Write FontInfo. Currently we don't write anything there. */
+
+ pputs(s, "/FontInfo 1 dict dup begin\n");
+ pputs(s, "end readonly def\n");
+
+ /* Write the main font dictionary. */
+
+ pputs(s, "/FontName /");
+ pwrite(s, pfont->font_name.chars, pfont->font_name.size);
+ pputs(s, " def\n");
+ pputs(s, "/Encoding ");
+ switch (pfont->encoding_index) {
+ case 0:
+ pputs(s, "StandardEncoding");
+ break;
+ case 1:
+ pputs(s, "ISOLatin1Encoding");
+ break;
+ default:{
+ gs_char i;
+
+ pputs(s, "256 array\n");
+ pputs(s, "0 1 255 {1 index exch /.notdef put} for\n");
+ for (i = 0; i < 256; ++i) {
+ gs_glyph glyph =
+ (*pfont->procs.encode_char) (NULL, (gs_font *) pfont, &i);
+ const char *namestr;
+ uint namelen;
+
+ if (glyph != gs_no_glyph &&
+ (namestr = (*pfont->procs.callbacks.glyph_name) (glyph, &namelen)) != 0 &&
+ !(namelen == 7 && !memcmp(namestr, ".notdef", 7))
+ ) {
+ pprintd1(s, "dup %d /", (int)i);
+ pwrite(s, namestr, namelen);
+ pputs(s, " put\n");
+ }
+ }
+ pputs(s, "readonly");
+ }
+ }
+ pputs(s, " def\n");
+ pprintg6(s, "/FontMatrix [%g %g %g %g %g %g] readonly def\n",
+ pfont->FontMatrix.xx, pfont->FontMatrix.xy,
+ pfont->FontMatrix.yx, pfont->FontMatrix.yy,
+ pfont->FontMatrix.tx, pfont->FontMatrix.ty);
+ embed_uid(s, &pfont->UID);
+ pprintg4(s, "/FontBBox {%g %g %g %g} readonly def\n",
+ pfont->FontBBox.p.x, pfont->FontBBox.p.y,
+ pfont->FontBBox.q.x, pfont->FontBBox.q.y);
+ {
+ private const gs_param_item_t font_items[] =
+ {
+ {"FontType", gs_param_type_int,
+ offset_of(gs_font_type1, FontType)},
+ {"PaintType", gs_param_type_int,
+ offset_of(gs_font_type1, PaintType)},
+ {"StrokeWidth", gs_param_type_float,
+ offset_of(gs_font_type1, StrokeWidth)},
+ gs_param_item_end
+ };
+
+ code = gs_param_write_items(plist, pfont, NULL, font_items);
+ if (code < 0)
+ return code;
+ }
+ pputs(s, "currentdict end\n");
+
+ /* Write the Private dictionary. */
+
+ pputs(s, "dup /Private 17 dict dup begin\n");
+ pputs(s, "/-|{string currentfile exch readstring pop}executeonly def\n");
+ pputs(s, "/|-{noaccess def}executeonly def\n");
+ pputs(s, "/|{noaccess put}executeonly def\n");
+ {
+ private const gs_param_item_t private_items[] =
+ {
+ {"lenIV", gs_param_type_int,
+ offset_of(gs_type1_data, lenIV)},
+ {"BlueFuzz", gs_param_type_int,
+ offset_of(gs_type1_data, BlueFuzz)},
+ {"BlueScale", gs_param_type_float,
+ offset_of(gs_type1_data, BlueScale)},
+ {"BlueShift", gs_param_type_float,
+ offset_of(gs_type1_data, BlueShift)},
+ {"ExpansionFactor", gs_param_type_float,
+ offset_of(gs_type1_data, ExpansionFactor)},
+ {"ForceBold", gs_param_type_bool,
+ offset_of(gs_type1_data, ForceBold)},
+ {"LanguageGroup", gs_param_type_int,
+ offset_of(gs_type1_data, LanguageGroup)},
+ {"RndStemUp", gs_param_type_bool,
+ offset_of(gs_type1_data, RndStemUp)},
+ gs_param_item_end
+ };
+ gs_type1_data defaults;
+
+ defaults.lenIV = 4;
+ defaults.BlueFuzz = 1;
+ defaults.BlueScale = 0.039625;
+ defaults.BlueShift = 7.0;
+ defaults.ExpansionFactor = 0.06;
+ defaults.ForceBold = false;
+ defaults.LanguageGroup = 0;
+ defaults.RndStemUp = true;
+ code = gs_param_write_items(plist, pdata, &defaults, private_items);
+ if (code < 0)
+ return code;
+ embed_table(plist, "BlueValues", pdata->BlueValues.values,
+ pdata->BlueValues.count);
+ embed_table(plist, "OtherBlues", pdata->OtherBlues.values,
+ pdata->OtherBlues.count);
+ embed_table(plist, "FamilyBlues", pdata->FamilyBlues.values,
+ pdata->FamilyBlues.count);
+ embed_table(plist, "FamilyOtherBlues", pdata->FamilyOtherBlues.values,
+ pdata->FamilyOtherBlues.count);
+ embed_table(plist, "StdHW", pdata->StdHW.values,
+ pdata->StdHW.count);
+ embed_table(plist, "StemSnapH", pdata->StemSnapH.values,
+ pdata->StemSnapH.count);
+ embed_table(plist, "StemSnapV", pdata->StemSnapV.values,
+ pdata->StemSnapV.count);
+ }
+ embed_uid(s, &pfont->UID);
+ pputs(s, "/MinFeature{16 16} |-\n");
+ pputs(s, "/password 5839 def\n");
+
+ /* Write the Subrs. */
+
+ {
+ int n, i;
+ gs_const_string str;
+
+ for (n = 0;
+ (*pdata->procs->subr_data) (pfont, n, false, &str) !=
+ gs_error_rangecheck;
+ )
+ ++n;
+ pprintd1(s, "/Subrs %d array\n", n);
+ for (i = 0; i < n; ++i)
+ if ((*pdata->procs->subr_data) (pfont, i, false, &str) >= 0) {
+ char buf[50];
+
+ sprintf(buf, "dup %d %u -| ", i, str.size);
+ pputs(s, buf);
+ pwrite(s, str.data, str.size);
+ pputs(s, " |\n");
+ }
+ pputs(s, "|-\n");
+ }
+
+ /* We don't write OtherSubrs -- there had better not be any! */
+
+ /* Write the CharStrings. */
+
+ {
+ int num_chars = 0;
+ gs_glyph glyph;
+ int index = 0;
+ gs_const_string gdata;
+ int code;
+
+ for (glyph = gs_no_glyph, index = 0;
+ code = (*pdata->procs->next_glyph) (pfont, &index, &glyph),
+ index != 0;
+ )
+ if (code == 0 && (*pdata->procs->glyph_data) (pfont, glyph, &gdata) >= 0)
+ ++num_chars;
+ pprintd1(s, "2 index /CharStrings %d dict dup begin\n", num_chars);
+ for (glyph = gs_no_glyph, index = 0;
+ code = (*pdata->procs->next_glyph) (pfont, &index, &glyph),
+ index != 0;
+ )
+ if (code == 0 && (*pdata->procs->glyph_data) (pfont, glyph, &gdata) >= 0) {
+ uint gssize;
+ const char *gstr =
+ (*pfont->procs.callbacks.glyph_name) (glyph, &gssize);
+
+ pputs(s, "/");
+ pwrite(s, gstr, gssize);
+ pprintd1(s, " %d -| ", gdata.size);
+ pwrite(s, gdata.data, gdata.size);
+ pputs(s, " |-\n");
+ }
+ }
+
+ /* Wrap up. */
+
+ pputs(s, "end\nend\nreadonly put\nnoaccess put\n");
+ pputs(s, "dup/FontName get exch definefont pop\n");
+ psdf_free_param_printer(plist);
+ return 0;
+}
diff --git a/pstoraster/gdevpsdf.c b/pstoraster/gdevpsdf.c
new file mode 100644
index 000000000..38e5f4098
--- /dev/null
+++ b/pstoraster/gdevpsdf.c
@@ -0,0 +1,514 @@
+/* Copyright (C) 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Common utilities for PostScript and PDF writers */
+#include "string_.h"
+#include "gx.h"
+#include "gserrors.h"
+#include "gdevpsdf.h"
+#include "gdevpstr.h"
+#include "scanchar.h"
+#include "strimpl.h"
+#include "sa85x.h"
+#include "scfx.h"
+#include "sstring.h"
+
+/* Structure descriptor */
+public_st_device_psdf();
+
+/* ---------------- Vector implementation procedures ---------------- */
+
+int
+psdf_setlinewidth(gx_device_vector * vdev, floatp width)
+{
+ pprintg1(gdev_vector_stream(vdev), "%g w\n", width);
+ return 0;
+}
+
+int
+psdf_setlinecap(gx_device_vector * vdev, gs_line_cap cap)
+{
+ pprintd1(gdev_vector_stream(vdev), "%d J\n", cap);
+ return 0;
+}
+
+int
+psdf_setlinejoin(gx_device_vector * vdev, gs_line_join join)
+{
+ pprintd1(gdev_vector_stream(vdev), "%d j\n", join);
+ return 0;
+}
+
+int
+psdf_setmiterlimit(gx_device_vector * vdev, floatp limit)
+{
+ pprintg1(gdev_vector_stream(vdev), "%g M\n", limit);
+ return 0;
+}
+
+int
+psdf_setdash(gx_device_vector * vdev, const float *pattern, uint count,
+ floatp offset)
+{
+ stream *s = gdev_vector_stream(vdev);
+ int i;
+
+ pputs(s, "[ ");
+ for (i = 0; i < count; ++i)
+ pprintg1(s, "%g ", pattern[i]);
+ pprintg1(s, "] %g d\n", offset);
+ return 0;
+}
+
+int
+psdf_setflat(gx_device_vector * vdev, floatp flatness)
+{
+ pprintg1(gdev_vector_stream(vdev), "%g i\n", flatness);
+ return 0;
+}
+
+int
+psdf_setlogop(gx_device_vector * vdev, gs_logical_operation_t lop,
+ gs_logical_operation_t diff)
+{
+/****** SHOULD AT LEAST DETECT SET-0 & SET-1 ******/
+ return 0;
+}
+
+int
+psdf_setfillcolor(gx_device_vector * vdev, const gx_drawing_color * pdc)
+{
+ return psdf_set_color(vdev, pdc, "rg");
+}
+
+int
+psdf_setstrokecolor(gx_device_vector * vdev, const gx_drawing_color * pdc)
+{
+ return psdf_set_color(vdev, pdc, "RG");
+}
+
+int
+psdf_dorect(gx_device_vector * vdev, fixed x0, fixed y0, fixed x1, fixed y1,
+ gx_path_type_t type)
+{
+ int code = (*vdev_proc(vdev, beginpath)) (vdev, type);
+
+ if (code < 0)
+ return code;
+ pprintg4(gdev_vector_stream(vdev), "%g %g %g %g re\n",
+ fixed2float(x0), fixed2float(y0),
+ fixed2float(x1 - x0), fixed2float(y1 - y0));
+ return (*vdev_proc(vdev, endpath)) (vdev, type);
+}
+
+int
+psdf_beginpath(gx_device_vector * vdev, gx_path_type_t type)
+{
+ return 0;
+}
+
+int
+psdf_moveto(gx_device_vector * vdev, floatp x0, floatp y0, floatp x, floatp y,
+ bool first, gx_path_type_t type)
+{
+ pprintg2(gdev_vector_stream(vdev), "%g %g m\n", x, y);
+ return 0;
+}
+
+int
+psdf_lineto(gx_device_vector * vdev, floatp x0, floatp y0, floatp x, floatp y,
+ gx_path_type_t type)
+{
+ pprintg2(gdev_vector_stream(vdev), "%g %g l\n", x, y);
+ return 0;
+}
+
+int
+psdf_curveto(gx_device_vector * vdev, floatp x0, floatp y0,
+ floatp x1, floatp y1, floatp x2, floatp y2, floatp x3, floatp y3,
+ gx_path_type_t type)
+{
+ if (x1 == x0 && y1 == y0)
+ pprintg4(gdev_vector_stream(vdev), "%g %g %g %g v\n",
+ x2, y2, x3, y3);
+ else if (x3 == x2 && y3 == y2)
+ pprintg4(gdev_vector_stream(vdev), "%g %g %g %g y\n",
+ x1, y1, x2, y2);
+ else
+ pprintg6(gdev_vector_stream(vdev), "%g %g %g %g %g %g c\n",
+ x1, y1, x2, y2, x3, y3);
+ return 0;
+}
+
+int
+psdf_closepath(gx_device_vector * vdev, floatp x0, floatp y0,
+ floatp x_start, floatp y_start, gx_path_type_t type)
+{
+ pputs(gdev_vector_stream(vdev), "h\n");
+ return 0;
+}
+
+/* endpath is deliberately omitted. */
+
+/* ---------------- Utilities ---------------- */
+
+int
+psdf_set_color(gx_device_vector * vdev, const gx_drawing_color * pdc,
+ const char *rgs)
+{
+ if (!gx_dc_is_pure(pdc))
+ return_error(gs_error_rangecheck);
+ {
+ stream *s = gdev_vector_stream(vdev);
+ gx_color_index color = gx_dc_pure_color(pdc);
+ float r = (color >> 16) / 255.0;
+ float g = ((color >> 8) & 0xff) / 255.0;
+ float b = (color & 0xff) / 255.0;
+
+ if (r == g && g == b)
+ pprintg1(s, "%g", r), pprints1(s, " %s\n", rgs + 1);
+ else
+ pprintg3(s, "%g %g %g", r, g, b), pprints1(s, " %s\n", rgs);
+ }
+ return 0;
+}
+
+/* ---------------- Binary data writing ---------------- */
+
+/* Begin writing binary data. */
+int
+psdf_begin_binary(gx_device_psdf * pdev, psdf_binary_writer * pbw)
+{
+ pbw->strm = pdev->strm;
+ pbw->dev = pdev;
+ /* If not binary, set up the encoding stream. */
+ if (!pdev->binary_ok)
+ psdf_encode_binary(pbw, &s_A85E_template, NULL);
+ return 0;
+}
+
+/* Add an encoding filter. The client must have allocated the stream state, */
+/* if any, using pdev->v_memory. */
+int
+psdf_encode_binary(psdf_binary_writer * pbw, const stream_template * template,
+ stream_state * ss)
+{
+ gx_device_psdf *pdev = pbw->dev;
+ gs_memory_t *mem = pdev->v_memory;
+ stream *es = s_alloc(mem, "psdf_encode_binary(stream)");
+ stream_state *ess = (ss == 0 ? (stream_state *) es : ss);
+ uint bsize = max(template->min_out_size, 256); /* arbitrary */
+ byte *buf = gs_alloc_bytes(mem, bsize, "psdf_encode_binary(buf)");
+
+ if (es == 0 || buf == 0) {
+ gs_free_object(mem, buf, "psdf_encode_binary(buf)");
+ gs_free_object(mem, es, "psdf_encode_binary(stream)");
+ return_error(gs_error_VMerror);
+ }
+ if (ess == 0)
+ ess = (stream_state *) es;
+ s_std_init(es, buf, bsize, &s_filter_write_procs, s_mode_write);
+ ess->template = template;
+ ess->memory = mem;
+ es->procs.process = template->process;
+ es->memory = mem;
+ es->state = ess;
+ if (template->init)
+ (*template->init) (ess);
+ es->strm = pbw->strm;
+ pbw->strm = es;
+ return 0;
+}
+
+/* Add a 2-D CCITTFax encoding filter. */
+int
+psdf_CFE_binary(psdf_binary_writer * pbw, int w, int h, bool invert)
+{
+ gx_device_psdf *pdev = pbw->dev;
+ gs_memory_t *mem = pdev->v_memory;
+ const stream_template *template = &s_CFE_template;
+ stream_CFE_state *st =
+ gs_alloc_struct(mem, stream_CFE_state, template->stype,
+ "psdf_CFE_binary");
+ int code;
+
+ if (st == 0)
+ return_error(gs_error_VMerror);
+ (*template->set_defaults) ((stream_state *) st);
+ st->K = -1;
+ st->Columns = w;
+ st->Rows = h;
+ st->BlackIs1 = !invert;
+ code = psdf_encode_binary(pbw, template, (stream_state *) st);
+ if (code < 0)
+ gs_free_object(mem, st, "psdf_CFE_binary");
+ return code;
+}
+
+/* Finish writing binary data. */
+int
+psdf_end_binary(psdf_binary_writer * pbw)
+{
+ gx_device_psdf *pdev = pbw->dev;
+
+ /* Close the filters in reverse order. */
+ /* Stop before we try to close the file stream. */
+ while (pbw->strm != pdev->strm) {
+ stream *next = pbw->strm->strm;
+
+ sclose(pbw->strm);
+ pbw->strm = next;
+ }
+ return 0;
+}
+
+/*
+ * Write a string in its shortest form ( () or <> ). Note that
+ * this form is different depending on whether binary data are allowed.
+ * Currently we don't support ASCII85 strings ( <~ ~> ).
+ */
+void
+psdf_write_string(stream * s, const byte * str, uint size, int print_ok)
+{
+ uint added = 0;
+ uint i;
+ const stream_template *template;
+ stream_AXE_state state;
+ stream_state *st = NULL;
+
+ if (print_ok & print_binary_ok) { /* Only need to escape (, ), \, CR, EOL. */
+ pputc(s, '(');
+ for (i = 0; i < size; ++i) {
+ byte ch = str[i];
+
+ switch (ch) {
+ case char_CR:
+ pputs(s, "\\r");
+ continue;
+ case char_EOL:
+ pputs(s, "\\n");
+ continue;
+ case '(':
+ case ')':
+ case '\\':
+ pputc(s, '\\');
+ }
+ pputc(s, ch);
+ }
+ pputc(s, ')');
+ return;
+ }
+ for (i = 0; i < size; ++i) {
+ byte ch = str[i];
+
+ if (ch == 0 || ch >= 127)
+ added += 3;
+ else if (strchr("()\\\n\r\t\b\f", ch) != 0)
+ ++added;
+ else if (ch < 32)
+ added += 3;
+ }
+
+ if (added < size) { /* More efficient to represent as PostScript string. */
+ template = &s_PSSE_template;
+ pputc(s, '(');
+ } else { /* More efficient to represent as hex string. */
+ template = &s_AXE_template;
+ st = (stream_state *) & state;
+ s_AXE_init_inline(&state);
+ pputc(s, '<');
+ }
+
+ {
+ byte buf[100]; /* size is arbitrary */
+ stream_cursor_read r;
+ stream_cursor_write w;
+ int status;
+
+ r.ptr = str - 1;
+ r.limit = r.ptr + size;
+ w.limit = buf + sizeof(buf) - 1;
+ do {
+ w.ptr = buf - 1;
+ status = (*template->process) (st, &r, &w, true);
+ pwrite(s, buf, (uint) (w.ptr + 1 - buf));
+ }
+ while (status == 1);
+ }
+}
+
+/* Set up a write stream that just keeps track of the position. */
+int
+psdf_alloc_position_stream(stream ** ps, gs_memory_t * mem)
+{
+ stream *s = *ps = s_alloc(mem, "psdf_alloc_position_stream");
+
+ if (s == 0)
+ return_error(gs_error_VMerror);
+ swrite_position_only(s);
+ return 0;
+}
+
+/* ---------------- Parameter printing ---------------- */
+
+typedef struct printer_param_list_s {
+ gs_param_list_common;
+ stream *strm;
+ param_printer_params_t params;
+ int print_ok;
+ bool any;
+} printer_param_list_t;
+
+gs_private_st_ptrs1(st_printer_param_list, printer_param_list_t,
+ "printer_param_list_t", printer_plist_enum_ptrs, printer_plist_reloc_ptrs,
+ strm);
+const param_printer_params_t param_printer_params_default =
+{
+ param_printer_params_default_values
+};
+
+/* We'll implement the other printers later if we have to. */
+private param_proc_xmit_typed(param_print_typed);
+/*private param_proc_begin_xmit_collection(param_print_begin_collection); */
+/*private param_proc_end_xmit_collection(param_print_end_collection); */
+private const gs_param_list_procs printer_param_list_procs = {
+ param_print_typed,
+ NULL /* begin_collection */ ,
+ NULL /* end_collection */ ,
+ NULL /* get_next_key */ ,
+ gs_param_request_default,
+ gs_param_requested_default
+};
+
+int
+psdf_alloc_param_printer(gs_param_list ** pplist,
+ const param_printer_params_t * ppp, stream * s,
+ int print_ok, gs_memory_t * mem)
+{
+ printer_param_list_t *prlist =
+ gs_alloc_struct(mem, printer_param_list_t, &st_printer_param_list,
+ "psdf_alloc_param_printer");
+
+ *pplist = (gs_param_list *) prlist;
+ if (prlist == 0)
+ return_error(gs_error_VMerror);
+ prlist->procs = &printer_param_list_procs;
+ prlist->memory = mem;
+ prlist->strm = s;
+ prlist->params = *ppp;
+ prlist->print_ok = print_ok;
+ prlist->any = false;
+ return 0;
+}
+
+void
+psdf_free_param_printer(gs_param_list * plist)
+{
+ if (plist) {
+ printer_param_list_t *prlist = (printer_param_list_t *) plist;
+
+ if (prlist->any && prlist->params.suffix)
+ pputs(prlist->strm, prlist->params.suffix);
+ gs_free_object(prlist->memory, plist, "psdf_free_param_printer");
+ }
+}
+
+#define prlist ((printer_param_list_t *)plist)
+private int
+param_print_typed(gs_param_list * plist, gs_param_name pkey,
+ gs_param_typed_value * pvalue)
+{
+ stream *s = prlist->strm;
+
+ if (!prlist->any) {
+ if (prlist->params.prefix)
+ pputs(s, prlist->params.prefix);
+ prlist->any = true;
+ }
+ if (prlist->params.item_prefix)
+ pputs(s, prlist->params.item_prefix);
+ pprints1(s, "/%s", pkey);
+ switch (pvalue->type) {
+ case gs_param_type_null:
+ pputs(s, " null");
+ break;
+ case gs_param_type_bool:
+ pputs(s, (pvalue->value.b ? " true" : " false"));
+ break;
+ case gs_param_type_int:
+ pprintd1(s, " %d", pvalue->value.i);
+ break;
+ case gs_param_type_long:
+ pprintld1(s, " %l", pvalue->value.l);
+ break;
+ case gs_param_type_float:
+ pprintg1(s, " %g", pvalue->value.f);
+ break;
+ case gs_param_type_string:
+ psdf_write_string(s, pvalue->value.s.data, pvalue->value.s.size,
+ prlist->print_ok);
+ break;
+ case gs_param_type_name:
+/****** SHOULD USE #-ESCAPES FOR PDF ******/
+ pputc(s, '/');
+ pwrite(s, pvalue->value.n.data, pvalue->value.n.size);
+ break;
+ case gs_param_type_int_array:
+ {
+ uint i;
+ char sepr = (pvalue->value.ia.size <= 10 ? ' ' : '\n');
+
+ pputc(s, '[');
+ for (i = 0; i < pvalue->value.ia.size; ++i) {
+ pprintd1(s, "%d", pvalue->value.ia.data[i]);
+ pputc(s, sepr);
+ }
+ pputc(s, ']');
+ }
+ break;
+ case gs_param_type_float_array:
+ {
+ uint i;
+ char sepr = (pvalue->value.fa.size <= 10 ? ' ' : '\n');
+
+ pputc(s, '[');
+ for (i = 0; i < pvalue->value.fa.size; ++i) {
+ pprintg1(s, "%g", pvalue->value.fa.data[i]);
+ pputc(s, sepr);
+ }
+ pputc(s, ']');
+ }
+ break;
+ /*case gs_param_type_string_array: */
+ /*case gs_param_type_name_array: */
+ default:
+ return_error(gs_error_typecheck);
+ }
+ if (prlist->params.item_suffix)
+ pputs(s, prlist->params.item_suffix);
+ return 0;
+}
+
+#undef prlist
diff --git a/pstoraster/gdevpsdf.h b/pstoraster/gdevpsdf.h
new file mode 100644
index 000000000..1e814a52f
--- /dev/null
+++ b/pstoraster/gdevpsdf.h
@@ -0,0 +1,292 @@
+/* Copyright (C) 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Common output syntax and parameters for PostScript and PDF writers */
+
+#ifndef gdevpsdf_INCLUDED
+# define gdevpsdf_INCLUDED
+
+#include "gdevvec.h"
+#include "gsparam.h"
+#include "strimpl.h"
+#include "scfx.h"
+
+/* ---------------- Distiller parameters ---------------- */
+
+/* Parameters for controlling distillation of images. */
+typedef struct psdf_image_params_s {
+ stream_state *ACSDict; /* JPEG */
+ bool AntiAlias;
+ bool AutoFilter;
+ int Depth;
+ stream_state *Dict; /* JPEG or CCITTFax */
+ bool Downsample;
+ enum psdf_downsample_type {
+ ds_Average,
+ ds_Subsample
+ } DownsampleType;
+ bool Encode;
+ const char *Filter;
+ int Resolution;
+ const stream_template *filter_template;
+} psdf_image_params;
+
+#define psdf_image_param_defaults(af, res, f, ft)\
+ NULL/*ACSDict*/, 0/*false*/, af, -1, NULL/*Dict*/, 0/*false*/,\
+ ds_Subsample, 1/*true*/, f, res, ft
+
+/* Declare templates for default image compression filters. */
+extern const stream_template s_CFE_template;
+
+/* Complete distiller parameters. */
+typedef struct psdf_distiller_params_s {
+
+ /* General parameters */
+
+ bool ASCII85EncodePages;
+ enum psdf_auto_rotate_pages {
+ arp_None,
+ arp_All,
+ arp_PageByPage
+ } AutoRotatePages;
+ bool CompressPages;
+ long ImageMemory;
+ bool LZWEncodePages;
+ bool PreserveHalftoneInfo;
+ bool PreserveOPIComments;
+ bool PreserveOverprintSettings;
+ enum psdf_transfer_function_info {
+ tfi_Preserve,
+ tfi_Apply,
+ tfi_Remove
+ } TransferFunctionInfo;
+ enum psdf_ucr_and_bg_info {
+ ucrbg_Preserve,
+ ucrbg_Remove
+ } UCRandBGInfo;
+ bool UseFlateCompression;
+#define psdf_general_param_defaults(ascii)\
+ ascii, arp_None, 1/*true*/, 250000, 0/*false*/,\
+ 0/*false*/, 0/*false*/, 0/*false*/, tfi_Apply, ucrbg_Remove, 1 /*true */
+
+ /* Color sampled image parameters */
+
+ psdf_image_params ColorImage;
+ enum psdf_color_conversion_strategy {
+ ccs_LeaveColorUnchanged,
+ ccs_UseDeviceDependentColor,
+ ccs_UseDeviceIndependentColor
+ } ColorConversionStrategy;
+ bool ConvertCMYKImagesToRGB;
+ bool ConvertImagesToIndexed;
+#define psdf_color_image_param_defaults\
+ { psdf_image_param_defaults(1/*true*/, 72, 0, 0) },\
+ ccs_LeaveColorUnchanged, 1/*true*/, 0 /*false */
+
+ /* Grayscale sampled image parameters */
+
+ psdf_image_params GrayImage;
+#define psdf_gray_image_param_defaults\
+ { psdf_image_param_defaults(1/*true*/, 72, 0, 0) }
+
+ /* Monochrome sampled image parameters */
+
+ psdf_image_params MonoImage;
+#define psdf_mono_image_param_defaults\
+ { psdf_image_param_defaults(0/*false*/, 300, "CCITTFaxEncode", &s_CFE_template) }
+
+ /* Font embedding parameters */
+
+ gs_param_string_array AlwaysEmbed;
+ gs_param_string_array NeverEmbed;
+ bool EmbedAllFonts;
+ bool SubsetFonts;
+ int MaxSubsetPct;
+#define psdf_font_param_defaults\
+ { 0, 0, 1/*true*/ }, { 0, 0, 1/*true*/ },\
+ 1/*true*/, 1/*true*/, 20
+
+} psdf_distiller_params;
+
+/* Define PostScript/PDF versions, corresponding roughly to Adobe versions. */
+typedef enum {
+ psdf_version_level1 = 1000, /* Red Book Level 1 */
+ psdf_version_level1_color = 1100, /* Level 1 + colorimage + CMYK color */
+ psdf_version_level2 = 2000, /* Red Book Level 2 */
+ psdf_version_level2_plus = 2017, /* Adobe release 2017 */
+ psdf_version_ll3 = 3010 /* LanguageLevel 3, release 3010 */
+} psdf_version;
+
+/* Define the extended device structure. */
+#define gx_device_psdf_common\
+ gx_device_vector_common;\
+ psdf_version version;\
+ bool binary_ok; /* derived from ASCII85EncodePages */\
+ psdf_distiller_params params
+typedef struct gx_device_psdf_s {
+ gx_device_psdf_common;
+} gx_device_psdf;
+
+#define psdf_initial_values(version, ascii)\
+ vector_initial_values,\
+ version,\
+ !(ascii),\
+ { psdf_general_param_defaults(ascii),\
+ psdf_color_image_param_defaults,\
+ psdf_gray_image_param_defaults,\
+ psdf_mono_image_param_defaults,\
+ psdf_font_param_defaults\
+ }
+
+/* st_device_psdf is never instantiated per se, but we still need to */
+/* extern its descriptor for the sake of subclasses. */
+extern_st(st_device_psdf);
+#define public_st_device_psdf() /* in gdevpsdf.c */\
+ gs_public_st_suffix_add0_final(st_device_psdf, gx_device_psdf,\
+ "gx_device_psdf", device_psdf_enum_ptrs,\
+ device_psdf_reloc_ptrs, gx_device_finalize, st_device_vector)
+#define st_device_psdf_max_ptrs (st_device_vector_max_ptrs)
+
+/* Get/put parameters. */
+dev_proc_get_params(gdev_psdf_get_params);
+dev_proc_put_params(gdev_psdf_put_params);
+
+/* Put a Boolean or integer parameter. */
+int psdf_put_bool_param(P4(gs_param_list * plist, gs_param_name param_name,
+ bool * pval, int ecode));
+int psdf_put_int_param(P4(gs_param_list * plist, gs_param_name param_name,
+ int *pval, int ecode));
+
+/* ---------------- Vector implementation procedures ---------------- */
+
+ /* Imager state */
+int psdf_setlinewidth(P2(gx_device_vector * vdev, floatp width));
+int psdf_setlinecap(P2(gx_device_vector * vdev, gs_line_cap cap));
+int psdf_setlinejoin(P2(gx_device_vector * vdev, gs_line_join join));
+int psdf_setmiterlimit(P2(gx_device_vector * vdev, floatp limit));
+int psdf_setdash(P4(gx_device_vector * vdev, const float *pattern,
+ uint count, floatp offset));
+int psdf_setflat(P2(gx_device_vector * vdev, floatp flatness));
+int psdf_setlogop(P3(gx_device_vector * vdev, gs_logical_operation_t lop,
+ gs_logical_operation_t diff));
+
+ /* Other state */
+int psdf_setfillcolor(P2(gx_device_vector * vdev, const gx_drawing_color * pdc));
+int psdf_setstrokecolor(P2(gx_device_vector * vdev, const gx_drawing_color * pdc));
+
+ /* Paths */
+#define psdf_dopath gdev_vector_dopath
+int psdf_dorect(P6(gx_device_vector * vdev, fixed x0, fixed y0, fixed x1,
+ fixed y1, gx_path_type_t type));
+int psdf_beginpath(P2(gx_device_vector * vdev, gx_path_type_t type));
+int psdf_moveto(P7(gx_device_vector * vdev, floatp x0, floatp y0,
+ floatp x, floatp y, bool first, gx_path_type_t type));
+int psdf_lineto(P6(gx_device_vector * vdev, floatp x0, floatp y0,
+ floatp x, floatp y, gx_path_type_t type));
+int psdf_curveto(P10(gx_device_vector * vdev, floatp x0, floatp y0,
+ floatp x1, floatp y1, floatp x2,
+ floatp y2, floatp x3, floatp y3, gx_path_type_t type));
+int psdf_closepath(P6(gx_device_vector * vdev, floatp x0, floatp y0,
+ floatp x_start, floatp y_start, gx_path_type_t type));
+
+/* ---------------- Binary (image) data procedures ---------------- */
+
+/* Define the structure for writing binary data. */
+typedef struct psdf_binary_writer_s {
+ stream *strm;
+ gx_device_psdf *dev;
+} psdf_binary_writer;
+
+/* Begin writing binary data. */
+int psdf_begin_binary(P2(gx_device_psdf * pdev, psdf_binary_writer * pbw));
+
+/* Add an encoding filter. The client must have allocated the stream state, */
+/* if any, using pdev->v_memory. */
+int psdf_encode_binary(P3(psdf_binary_writer * pbw,
+ const stream_template * template, stream_state * ss));
+
+/* Add a 2-D CCITTFax encoding filter. */
+int psdf_CFE_binary(P4(psdf_binary_writer * pbw, int w, int h, bool invert));
+
+/* Set up compression and downsampling filters for an image. */
+/* Note that this may modify the image parameters. */
+/* If pctm is NULL, downsampling is not used. */
+/* pis only provides UCR and BG information for CMYK => RGB conversion. */
+int psdf_setup_image_filters(P5(gx_device_psdf * pdev, psdf_binary_writer * pbw,
+ gs_image_t * pim, const gs_matrix * pctm,
+ const gs_imager_state * pis));
+
+/* Finish writing binary data. */
+int psdf_end_binary(P1(psdf_binary_writer * pbw));
+
+/* ------ Symbolic data printing ------ */
+
+/* Print a PostScript string in the most efficient form. */
+#define print_binary_ok 1
+#define print_ASCII85_ok 2
+void psdf_write_string(P4(stream * s, const byte * str, uint size,
+ int print_ok));
+
+/*
+ * Create a stream that just keeps track of how much has been written
+ * to it. We use this for measuring data that will be stored rather
+ * than written to an actual stream. This too should probably migrate
+ * to stream.c....
+ */
+int psdf_alloc_position_stream(P2(stream ** ps, gs_memory_t * mem));
+
+/*
+ * Create/release a parameter list for printing (non-default) filter
+ * parameters. This should probably migrate to a lower level....
+ */
+typedef struct param_printer_params_s {
+ const char *prefix; /* before entire object, if any params */
+ const char *suffix; /* after entire object, if any params */
+ const char *item_prefix; /* before each param */
+ const char *item_suffix; /* after each param */
+} param_printer_params_t;
+
+#define param_printer_params_default_values 0, 0, 0, "\n"
+extern const param_printer_params_t param_printer_params_default;
+int psdf_alloc_param_printer(P5(gs_param_list ** pplist,
+ const param_printer_params_t * ppp, stream * s,
+ int print_ok, gs_memory_t * mem));
+void psdf_free_param_printer(P1(gs_param_list * plist));
+
+/* Write out a Type 1 font definition. */
+#ifndef gs_font_type1_DEFINED
+# define gs_font_type1_DEFINED
+typedef struct gs_font_type1_s gs_font_type1;
+
+#endif
+int psdf_embed_type1_font(P2(stream * s, gs_font_type1 * pfont));
+
+/* ---------------- Other procedures ---------------- */
+
+/* Set the fill or stroke color. rgs is "rg" or "RG". */
+int psdf_set_color(P3(gx_device_vector * vdev, const gx_drawing_color * pdc,
+ const char *rgs));
+
+#endif /* gdevpsdf_INCLUDED */
diff --git a/pstoraster/gdevpsdi.c b/pstoraster/gdevpsdi.c
new file mode 100644
index 000000000..9ba6c1614
--- /dev/null
+++ b/pstoraster/gdevpsdi.c
@@ -0,0 +1,349 @@
+/*
+ Copyright 1993-2000 by Easy Software Products.
+ Copyright 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Image compression for PostScript and PDF writers */
+#include "math_.h"
+#include "gx.h"
+#include "gserrors.h"
+#include "gscspace.h"
+#include "gdevpsdf.h"
+#include "gdevpsds.h"
+#include "strimpl.h"
+#include "scfx.h"
+#include <config.h>
+#ifdef HAVE_LIBJPEG
+#include "jpeglib.h" /* for sdct.h */
+#include "sdct.h"
+#endif /* HAVE_LIBJPEG */
+#include "slzwx.h"
+#include "spngpx.h"
+#include "srlx.h"
+#ifdef HAVE_LIBZ
+#include "szlibx.h"
+#endif /* HAVE_LIBZ */
+
+/* ---------------- Image compression ---------------- */
+
+/* Add a filter to expand or reduce the pixel width if needed. */
+/* At least one of bpc_in and bpc_out is 8; the other is 1, 2, 4, or 8. */
+private int
+pixel_resize(psdf_binary_writer * pbw, int width, int num_components,
+ int bpc_in, int bpc_out)
+{
+ gs_memory_t *mem = pbw->dev->v_memory;
+ const stream_template *template;
+ stream_1248_state *st;
+ int code;
+
+ if (bpc_out == bpc_in)
+ return 0;
+ if (bpc_in < 8) {
+ static const stream_template *const exts[5] =
+ {
+ 0, &s_1_8_template, &s_2_8_template, 0, &s_4_8_template
+ };
+
+ template = exts[bpc_in];
+ } else {
+ static const stream_template *const rets[5] =
+ {
+ 0, &s_8_1_template, &s_8_2_template, 0, &s_8_4_template
+ };
+
+ template = rets[bpc_out];
+ }
+ st = (stream_1248_state *)
+ s_alloc_state(mem, template->stype, "pixel_resize state");
+ if (st == 0)
+ return_error(gs_error_VMerror);
+ code = psdf_encode_binary(pbw, template, (stream_state *) st);
+ if (code < 0) {
+ gs_free_object(mem, st, "pixel_resize state");
+ return code;
+ }
+ s_1248_init(st, width, num_components);
+ return 0;
+}
+
+/* Add the appropriate image compression filter, if any. */
+private int
+setup_image_compression(psdf_binary_writer * pbw, const psdf_image_params * pdip,
+ const gs_image_t * pim)
+{
+ gx_device_psdf *pdev = pbw->dev;
+ const stream_template *template = pdip->filter_template;
+ stream_state *st;
+
+ if (pdip->AutoFilter) {
+ /****** AutoFilter IS NYI ******/
+ /*
+ * Even though this isn't obvious from the Adobe Tech Note,
+ * it appears that if UseFlateCompression is true, the default
+ * compressor for AutoFilter is FlateEncode, not LZWEncode.
+ */
+#ifdef HAVE_LIBZ
+ template =
+ (pdev->params.UseFlateCompression &&
+ pdev->version >= psdf_version_ll3 ?
+ &s_zlibE_template : &s_LZWE_template);
+#else
+ template = &s_LZWE_template;
+#endif /* HAVE_LIBZ */
+ }
+ if (!pdip->Encode || template == 0) /* no compression */
+ return 0;
+#ifdef HAVE_LIBJPEG
+ /* Only use DCTE for 8-bit data. */
+ if (template == &s_DCTE_template &&
+ !(pdip->Downsample ?
+ pdip->Depth == 8 ||
+ (pdip->Depth == -1 && pim->BitsPerComponent == 8) :
+ pim->BitsPerComponent == 8)
+ ) {
+ /* Use LZW instead. */
+ template = &s_LZWE_template;
+ }
+#endif /* HAVE_LIBJPEG */
+ st = s_alloc_state(pdev->v_memory, template->stype,
+ "setup_image_compression");
+ if (st == 0)
+ return_error(gs_error_VMerror);
+ if (template->set_defaults)
+ (*template->set_defaults) (st);
+ if (template == &s_CFE_template) {
+ stream_CFE_state *const ss = (stream_CFE_state *) st;
+
+ if (pdip->Dict != 0 && pdip->Dict->template == &s_CFE_template) {
+ stream_state common;
+
+ common = *st; /* save generic info */
+ *ss = *(const stream_CFE_state *)pdip->Dict;
+ *st = common;
+ } else {
+ ss->K = -1;
+ ss->BlackIs1 = true;
+ }
+ ss->Columns = pim->Width;
+ ss->Rows = (ss->EndOfBlock ? 0 : pim->Height);
+#ifdef HAVE_LIBZ
+ } else if (template == &s_LZWE_template ||
+ template == &s_zlibE_template) {
+#else
+ } else if (template == &s_LZWE_template) {
+#endif /* HAVE_LIBZ */
+ /* Add a PNGPredictor filter. */
+ int code = psdf_encode_binary(pbw, template, st);
+
+ if (code < 0) {
+ gs_free_object(pdev->v_memory, st, "setup_image_compression");
+ return code;
+ }
+ template = &s_PNGPE_template;
+ st = s_alloc_state(pdev->v_memory, template->stype,
+ "setup_image_compression");
+ if (st == 0)
+ return_error(gs_error_VMerror);
+ if (template->set_defaults)
+ (*template->set_defaults) (st);
+ {
+ stream_PNGP_state *const ss = (stream_PNGP_state *) st;
+
+ ss->Colors = gs_color_space_num_components(pim->ColorSpace);
+ ss->Columns = pim->Width;
+ }
+#ifdef HAVE_LIBJPEG
+ } else if (template == &s_DCTE_template) {
+ /****** ADD PARAMETERS FROM pdip->Dict ******/
+#endif /* HAVE_LIBJPEG */
+ } {
+ int code = psdf_encode_binary(pbw, template, st);
+
+ if (code < 0) {
+ gs_free_object(pdev->v_memory, st, "setup_image_compression");
+ return code;
+ }
+ }
+ return 0;
+}
+
+/* Add downsampling, antialiasing, and compression filters. */
+/* Uses AntiAlias, Depth, DownsampleType, Resolution. */
+private int
+setup_downsampling(psdf_binary_writer * pbw, const psdf_image_params * pdip,
+ gs_image_t * pim, floatp resolution)
+{
+ gx_device_psdf *pdev = pbw->dev;
+ const stream_template *template =
+ (pdip->DownsampleType == ds_Average ?
+ &s_Average_template : &s_Subsample_template);
+ int factor = (int)(resolution / pdip->Resolution);
+ int orig_bpc = pim->BitsPerComponent;
+ int orig_width = pim->Width;
+ int orig_height = pim->Height;
+ stream_state *st;
+ int code;
+
+ if (factor <= 1 || pim->Width < factor || pim->Height < factor)
+ return setup_image_compression(pbw, pdip, pim); /* no downsampling */
+ st = s_alloc_state(pdev->v_memory, template->stype,
+ "setup_downsampling");
+ if (st == 0)
+ return_error(gs_error_VMerror);
+ if (template->set_defaults)
+ (*template->set_defaults) (st);
+ {
+ stream_Downsample_state *const ss = (stream_Downsample_state *) st;
+
+ ss->Colors = gs_color_space_num_components(pim->ColorSpace);
+ ss->Columns = pim->Width;
+ ss->XFactor = ss->YFactor = factor;
+ ss->AntiAlias = pdip->AntiAlias;
+ if (template->init)
+ (*template->init) (st);
+ pim->Width /= factor;
+ pim->Height /= factor;
+ pim->BitsPerComponent = pdip->Depth;
+ gs_matrix_scale(&pim->ImageMatrix, (double)pim->Width / orig_width,
+ (double)pim->Height / orig_height,
+ &pim->ImageMatrix);
+ /****** NO ANTI-ALIASING YET ******/
+ if ((code = setup_image_compression(pbw, pdip, pim)) < 0 ||
+ (code = pixel_resize(pbw, pim->Width, ss->Colors,
+ 8, pdip->Depth)) < 0 ||
+ (code = psdf_encode_binary(pbw, template, st)) < 0 ||
+ (code = pixel_resize(pbw, orig_width, ss->Colors,
+ orig_bpc, 8)) < 0
+ ) {
+ gs_free_object(pdev->v_memory, st, "setup_image_compression");
+ return code;
+ }
+ }
+ return 0;
+}
+
+/* Set up compression and downsampling filters for an image. */
+/* Note that this may modify the image parameters. */
+int
+psdf_setup_image_filters(gx_device_psdf * pdev, psdf_binary_writer * pbw,
+ gs_image_t * pim, const gs_matrix * pctm,
+ const gs_imager_state * pis)
+{ /*
+ * The following algorithms are per Adobe Tech Note # 5151,
+ * "Acrobat Distiller Parameters", revised 16 September 1996
+ * for Acrobat(TM) Distiller(TM) 3.0.
+ *
+ * The control structure is a little tricky, because filter
+ * pipelines must be constructed back-to-front.
+ */
+ int code = 0;
+ psdf_image_params params;
+
+ if (pim->ImageMask) {
+ params = pdev->params.MonoImage;
+ params.Depth = 1;
+ } else {
+ int ncomp = gs_color_space_num_components(pim->ColorSpace);
+ int bpc = pim->BitsPerComponent;
+
+ /*
+ * We can compute the image resolution by:
+ * W / (W * ImageMatrix^-1 * CTM / HWResolution).
+ * We can replace W by 1 to simplify the computation.
+ */
+ double resolution;
+
+ if (pctm == 0)
+ resolution = -1;
+ else {
+ gs_point pt;
+
+ /* We could do both X and Y, but why bother? */
+ gs_distance_transform_inverse(1.0, 0.0, &pim->ImageMatrix, &pt);
+ gs_distance_transform(pt.x, pt.y, pctm, &pt);
+ resolution = 1.0 / hypot(pt.x / pdev->HWResolution[0],
+ pt.y / pdev->HWResolution[1]);
+ }
+ if (ncomp == 1) {
+ /* Monochrome or gray */
+ if (bpc == 1)
+ params = pdev->params.MonoImage;
+ else
+ params = pdev->params.GrayImage;
+ if (params.Depth == -1)
+ params.Depth = bpc;
+ /* Check for downsampling. */
+ if (params.Downsample && params.Resolution <= resolution / 2) {
+ /* Use the downsampled depth, not the original data depth. */
+ if (params.Depth == 1) {
+ params.Filter = pdev->params.MonoImage.Filter;
+ params.filter_template = pdev->params.MonoImage.filter_template;
+ params.Dict = pdev->params.MonoImage.Dict;
+ } else {
+ params.Filter = pdev->params.GrayImage.Filter;
+ params.filter_template = pdev->params.GrayImage.filter_template;
+ params.Dict = pdev->params.GrayImage.Dict;
+ }
+ code = setup_downsampling(pbw, &params, pim, resolution);
+ } else {
+ code = setup_image_compression(pbw, &params, pim);
+ }
+ } else {
+ /* Color */
+ bool cmyk_to_rgb =
+ pdev->params.ConvertCMYKImagesToRGB &&
+ pis != 0 &&
+ gs_color_space_get_index(pim->ColorSpace) ==
+ gs_color_space_index_DeviceCMYK;
+
+ if (cmyk_to_rgb)
+ pim->ColorSpace = gs_cspace_DeviceRGB(pis);
+ params = pdev->params.ColorImage;
+ if (params.Depth == -1)
+ params.Depth = (cmyk_to_rgb ? 8 : bpc);
+ if (params.Downsample && params.Resolution <= resolution / 2) {
+ code = setup_downsampling(pbw, &params, pim, resolution);
+ } else {
+ code = setup_image_compression(pbw, &params, pim);
+ }
+ if (cmyk_to_rgb) {
+ gs_memory_t *mem = pdev->v_memory;
+ stream_C2R_state *ss = (stream_C2R_state *)
+ s_alloc_state(mem, s_C2R_template.stype, "C2R state");
+ int code = pixel_resize(pbw, pim->Width, 3, 8, bpc);
+
+ if (code < 0 ||
+ (code = psdf_encode_binary(pbw, &s_C2R_template,
+ (stream_state *) ss)) < 0 ||
+ (code = pixel_resize(pbw, pim->Width, 4, bpc, 8)) < 0
+ )
+ return code;
+ s_C2R_init(ss, pis);
+ }
+ }
+ }
+ return code;
+}
diff --git a/pstoraster/gdevpsdp.c b/pstoraster/gdevpsdp.c
new file mode 100644
index 000000000..d264b9484
--- /dev/null
+++ b/pstoraster/gdevpsdp.c
@@ -0,0 +1,703 @@
+/*
+ Copyright 1993-2000 by Easy Software Products.
+ Copyright 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* (Distiller) parameter handling for PostScript and PDF writers */
+#include "string_.h"
+#include "gx.h"
+#include "gserrors.h"
+#include "gxdevice.h"
+#include "gdevpsdf.h"
+#include "gdevpstr.h"
+#include "strimpl.h" /* for short-sighted compilers */
+#include "scfx.h"
+#include <config.h>
+#ifdef HAVE_LIBJPEG
+#include "jpeglib.h" /* for sdct.h */
+#include "sdct.h"
+#endif /* HAVE_LIBJPEG */
+#include "slzwx.h"
+#include "srlx.h"
+#ifdef HAVE_LIBZ
+#include "szlibx.h"
+#endif /* HAVE_LIBZ */
+
+/* ---------------- Get/put Distiller parameters ---------------- */
+
+/*
+ * This code handles all the Distiller parameters except the *ACSDict and
+ * *ImageDict parameter dictionaries. (It doesn't cause any of the
+ * parameters actually to have any effect.)
+ */
+
+typedef struct psdf_image_filter_name_s {
+ const char *pname;
+ const stream_template *template;
+ psdf_version min_version;
+} psdf_image_filter_name;
+typedef struct psdf_image_param_names_s {
+ const char *ACSDict; /* not used for mono */
+ const char *AntiAlias;
+ const char *AutoFilter; /* not used for mono */
+ const char *Depth;
+ const char *Dict;
+ const char *Downsample;
+ const char *DownsampleType;
+ const char *Encode;
+ const char *Filter;
+ const char *Resolution;
+} psdf_image_param_names;
+private const psdf_image_param_names Color_names =
+{
+ "ColorACSImageDict", "AntiAliasColorImages", "AutoFilterColorImages",
+ "ColorImageDepth", "ColorImageDict",
+ "DownsampleColorImages", "ColorImageDownsampleType", "EncodeColorImages",
+ "ColorImageFilter", "ColorImageResolution"
+};
+private const psdf_image_filter_name Poly_filters[] =
+{
+#ifdef HAVE_LIBJPEG
+ {"DCTEncode", &s_DCTE_template},
+#endif /* HAVE_LIBJPEG */
+#ifdef HAVE_LIBZ
+ {"FlateEncode", &s_zlibE_template, psdf_version_ll3},
+#endif /* HAVE_LIBZ */
+ {"LZWEncode", &s_LZWE_template},
+ {0, 0}
+};
+private const psdf_image_param_names Gray_names =
+{
+ "GrayACSImageDict", "AntiAliasGrayImages", "AutoFilterGrayImages",
+ "GrayImageDepth", "GrayImageDict",
+ "DownsampleGrayImages", "GrayImageDownsampleType", "EncodeGrayImages",
+ "GrayImageFilter", "GrayImageResolution"
+};
+private const psdf_image_param_names Mono_names =
+{
+ 0, "AntiAliasMonoImages", 0,
+ "MonoImageDepth", "MonoImageDict",
+ "DownsampleMonoImages", "MonoImageDownsampleType", "EncodeMonoImages",
+ "MonoImageFilter", "MonoImageResolution"
+};
+private const psdf_image_filter_name Mono_filters[] =
+{
+ {"CCITTFaxEncode", &s_CFE_template},
+#ifdef HAVE_LIBZ
+ {"FlateEncode", &s_zlibE_template, psdf_version_ll3},
+#endif /* HAVE_LIBZ */
+ {"LZWEncode", &s_LZWE_template},
+ {"RunLengthEncode", &s_RLE_template},
+ {0, 0}
+};
+private const char *const AutoRotatePages_names[] =
+{
+ "None", "All", "PageByPage", 0
+};
+private const char *const ColorConversionStrategy_names[] =
+{
+ "LeaveColorUnchanged", "UseDeviceDependentColor",
+ "UseDeviceIndependentColor", 0
+};
+private const char *const DownsampleType_names[] =
+{
+ "Average", "Subsample", 0
+};
+private const char *const TransferFunctionInfo_names[] =
+{
+ "Preserve", "Apply", "Remove", 0
+};
+private const char *const UCRandBGInfo_names[] =
+{
+ "Preserve", "Remove", 0
+};
+
+/* -------- Get parameters -------- */
+
+#ifdef HAVE_LIBJPEG
+extern stream_state_proc_get_params(s_DCTE_get_params, stream_DCT_state);
+#endif /* HAVE_LIBJPEG */
+extern stream_state_proc_get_params(s_CF_get_params, stream_CF_state);
+typedef stream_state_proc_get_params((*ss_get_params_t), stream_state);
+
+private int
+psdf_CF_get_params(gs_param_list * plist, const stream_state * ss, bool all)
+{
+ return (ss == 0 ? 0 :
+ s_CF_get_params(plist, (const stream_CF_state *)ss, all));
+}
+#ifdef HAVE_LIBJPEG
+private int
+psdf_DCT_get_params(gs_param_list * plist, const stream_state * ss, bool all)
+{
+ int code = (ss == 0 ? 0 :
+ s_DCTE_get_params(plist, (const stream_DCT_state *)ss, all));
+ /*
+ * Add dummy Columns, Rows, and Colors parameters so that put_params
+ * won't complain.
+ */
+ int dummy_size = 8, dummy_colors = 3;
+
+ if (code < 0 ||
+ (code = param_write_int(plist, "Columns", &dummy_size)) < 0 ||
+ (code = param_write_int(plist, "Rows", &dummy_size)) < 0 ||
+ (code = param_write_int(plist, "Colors", &dummy_colors)) < 0
+ )
+ return code;
+ return 0;
+}
+#endif /* HAVE_LIBJPEG */
+
+/*
+ * Get an image Dict parameter. Note that we return a default (usually
+ * empty) dictionary if the parameter has never been set.
+ */
+private int
+psdf_get_image_dict_param(gs_param_list * plist, const gs_param_name pname,
+ stream_state * ss, ss_get_params_t get_params)
+{
+ gs_param_dict dict;
+ int code;
+
+ if (pname == 0)
+ return 0;
+ dict.size = 12; /* enough for all param dicts we know about */
+ if ((code = param_begin_write_dict(plist, pname, &dict, false)) < 0)
+ return code;
+ code = (*get_params)(dict.list, ss, false);
+ param_end_write_dict(plist, pname, &dict);
+ return code;
+}
+
+/* Get a set of image-related parameters. */
+private int
+psdf_get_image_params(gs_param_list * plist,
+ const psdf_image_param_names * pnames, psdf_image_params * params)
+{
+ int code;
+ gs_param_string dsts, fs;
+
+ param_string_from_string(dsts,
+ DownsampleType_names[params->DownsampleType]);
+ if (
+#ifdef HAVE_LIBJPEG
+ (code = psdf_get_image_dict_param(plist, pnames->ACSDict,
+ params->ACSDict,
+ psdf_DCT_get_params)) < 0 ||
+#endif /* HAVE_LIBJPEG */
+ (code = param_write_bool(plist, pnames->AntiAlias,
+ &params->AntiAlias)) < 0 ||
+ (pnames->AutoFilter != 0 &&
+ (code = param_write_bool(plist, pnames->AutoFilter,
+ &params->AutoFilter)) < 0) ||
+ (code = param_write_int(plist, pnames->Depth,
+ &params->Depth)) < 0 ||
+ (code = psdf_get_image_dict_param(plist, pnames->Dict,
+ params->Dict,
+#ifdef HAVE_LIBJPEG
+ (params->Dict == 0 ||
+ params->Dict->template ==
+ &s_CFE_template ?
+ psdf_CF_get_params :
+ psdf_DCT_get_params))) < 0 ||
+#else
+ psdf_CF_get_params)) < 0 ||
+#endif /* HAVE_LIBJPEG */
+ (code = param_write_bool(plist, pnames->Downsample,
+ &params->Downsample)) < 0 ||
+ (code = param_write_name(plist, pnames->DownsampleType,
+ &dsts)) < 0 ||
+ (code = param_write_bool(plist, pnames->Encode,
+ &params->Encode)) < 0 ||
+ (code = (params->Filter == 0 ? 0 :
+ (param_string_from_string(fs, params->Filter),
+ param_write_name(plist, pnames->Filter, &fs)))) < 0 ||
+ (code = param_write_int(plist, pnames->Resolution,
+ &params->Resolution)) < 0
+ )
+ DO_NOTHING;
+ return code;
+}
+
+/* Get parameters. */
+int
+gdev_psdf_get_params(gx_device * dev, gs_param_list * plist)
+{
+ gx_device_psdf *pdev = (gx_device_psdf *) dev;
+ int code = gdev_vector_get_params(dev, plist);
+ gs_param_string arps, ccss, tfis, ucrbgis;
+
+ if (code < 0)
+ return code;
+ param_string_from_string(arps,
+ AutoRotatePages_names[(int)pdev->params.AutoRotatePages]);
+ param_string_from_string(ccss,
+ ColorConversionStrategy_names[(int)pdev->params.ColorConversionStrategy]);
+ param_string_from_string(tfis,
+ TransferFunctionInfo_names[(int)pdev->params.TransferFunctionInfo]);
+ param_string_from_string(ucrbgis,
+ UCRandBGInfo_names[(int)pdev->params.UCRandBGInfo]);
+ if (
+ /* General parameters */
+
+ (code = param_write_bool(plist, "ASCII85EncodePages",
+ &pdev->params.ASCII85EncodePages)) < 0 ||
+ (code = param_write_name(plist, "AutoRotatePages",
+ &arps)) < 0 ||
+ (code = param_write_bool(plist, "CompressPages",
+ &pdev->params.CompressPages)) < 0 ||
+ (code = param_write_long(plist, "ImageMemory",
+ &pdev->params.ImageMemory)) < 0 ||
+ (code = param_write_bool(plist, "LZWEncodePages",
+ &pdev->params.LZWEncodePages)) < 0 ||
+ (code = param_write_bool(plist, "PreserveHalftoneInfo",
+ &pdev->params.PreserveHalftoneInfo)) < 0 ||
+ (code = param_write_bool(plist, "PreserveOPIComments",
+ &pdev->params.PreserveOPIComments)) < 0 ||
+ (code = param_write_bool(plist, "PreserveOverprintSettings",
+ &pdev->params.PreserveOverprintSettings)) < 0 ||
+ (code = param_write_name(plist, "TransferFunctionInfo", &tfis)) < 0 ||
+ (code = param_write_name(plist, "UCRandBGInfo", &ucrbgis)) < 0 ||
+ (code = param_write_bool(plist, "UseFlateCompression",
+ &pdev->params.UseFlateCompression)) < 0 ||
+
+ /* Color sampled image parameters */
+
+ (code = psdf_get_image_params(plist, &Color_names, &pdev->params.ColorImage)) < 0 ||
+ (code = param_write_name(plist, "ColorConversionStrategy",
+ &ccss)) < 0 ||
+ (code = param_write_bool(plist, "ConvertCMYKImagesToRGB",
+ &pdev->params.ConvertCMYKImagesToRGB)) < 0 ||
+ (code = param_write_bool(plist, "ConvertImagesToIndexed",
+ &pdev->params.ConvertImagesToIndexed)) < 0 ||
+
+ /* Gray sampled image parameters */
+
+ (code = psdf_get_image_params(plist, &Gray_names, &pdev->params.GrayImage)) < 0 ||
+
+ /* Mono sampled image parameters */
+
+ (code = psdf_get_image_params(plist, &Mono_names, &pdev->params.MonoImage)) < 0 ||
+
+ /* Font embedding parameters */
+
+ (code = param_write_name_array(plist, "AlwaysEmbed", &pdev->params.AlwaysEmbed)) < 0 ||
+ (code = param_write_name_array(plist, "NeverEmbed", &pdev->params.NeverEmbed)) < 0 ||
+ (code = param_write_bool(plist, "EmbedAllFonts", &pdev->params.EmbedAllFonts)) < 0 ||
+ (code = param_write_bool(plist, "SubsetFonts", &pdev->params.SubsetFonts)) < 0 ||
+ (code = param_write_int(plist, "MaxSubsetPct", &pdev->params.MaxSubsetPct)) < 0
+ );
+ return code;
+}
+
+/* -------- Put parameters -------- */
+
+#ifdef HAVE_LIBJPEG
+extern stream_state_proc_put_params(s_DCTE_put_params, stream_DCT_state);
+#endif /* HAVE_LIBJPEG */
+extern stream_state_proc_put_params(s_CF_put_params, stream_CF_state);
+typedef stream_state_proc_put_params((*ss_put_params_t), stream_state);
+
+private int
+psdf_CF_put_params(gs_param_list * plist, stream_state * st)
+{
+ stream_CFE_state *const ss = (stream_CFE_state *) st;
+
+ (*s_CFE_template.set_defaults) (st);
+ ss->K = -1;
+ ss->BlackIs1 = true;
+ return s_CF_put_params(plist, (stream_CF_state *) ss);
+}
+#ifdef HAVE_LIBJPEG
+private int
+psdf_DCT_put_params(gs_param_list * plist, stream_state * ss)
+{
+ return s_DCTE_put_params(plist, (stream_DCT_state *) ss);
+}
+#endif /* HAVE_LIBJPEG */
+
+/* Compare a C string and a gs_param_string. */
+bool
+psdf_key_eq(const gs_param_string * pcs, const char *str)
+{
+ return (strlen(str) == pcs->size &&
+ !strncmp(str, (const char *)pcs->data, pcs->size));
+}
+
+/* Put an enumerated value. */
+private int
+psdf_put_enum_param(gs_param_list * plist, gs_param_name param_name,
+ int *pvalue, const char *const pnames[], int ecode)
+{
+ gs_param_string ens;
+ int code = param_read_name(plist, param_name, &ens);
+
+ switch (code) {
+ case 1:
+ return ecode;
+ case 0:
+ {
+ int i;
+
+ for (i = 0; pnames[i] != 0; ++i)
+ if (psdf_key_eq(&ens, pnames[i])) {
+ *pvalue = i;
+ return 0;
+ }
+ }
+ code = gs_error_rangecheck;
+ default:
+ ecode = code;
+ param_signal_error(plist, param_name, code);
+ }
+ return code;
+}
+
+/* Put a Boolean or integer parameter. */
+int
+psdf_put_bool_param(gs_param_list * plist, gs_param_name param_name,
+ bool * pval, int ecode)
+{
+ int code;
+
+ switch (code = param_read_bool(plist, param_name, pval)) {
+ default:
+ ecode = code;
+ param_signal_error(plist, param_name, ecode);
+ case 0:
+ case 1:
+ break;
+ }
+ return ecode;
+}
+int
+psdf_put_int_param(gs_param_list * plist, gs_param_name param_name,
+ int *pval, int ecode)
+{
+ int code;
+
+ switch (code = param_read_int(plist, param_name, pval)) {
+ default:
+ ecode = code;
+ param_signal_error(plist, param_name, ecode);
+ case 0:
+ case 1:
+ break;
+ }
+ return ecode;
+}
+
+/* Put [~](Always|Never)Embed parameters. */
+private int
+psdf_put_embed_param(gs_param_list * plist, gs_param_name notpname,
+ gs_param_string_array * psa, int ecode)
+{
+ gs_param_name pname = notpname + 1;
+ int code;
+ gs_param_string_array nsa;
+
+/***** Storage management is incomplete ******/
+/***** Doesn't do incremental add/delete ******/
+ switch (code = param_read_name_array(plist, pname, psa)) {
+ default:
+ ecode = code;
+ param_signal_error(plist, pname, ecode);
+ case 0:
+ case 1:
+ break;
+ }
+ switch (code = param_read_name_array(plist, notpname, &nsa)) {
+ default:
+ ecode = code;
+ param_signal_error(plist, notpname, ecode);
+ case 0:
+ case 1:
+ break;
+ }
+ return ecode;
+}
+
+/* Put an image Dict parameter. */
+private int
+psdf_put_image_dict_param(gs_param_list * plist, const gs_param_name pname,
+ stream_state ** pss, const stream_template * template,
+ ss_put_params_t put_params, gs_memory_t * mem)
+{
+ gs_param_dict dict;
+ stream_state *ss = *pss;
+ int code;
+
+ switch (code = param_begin_read_dict(plist, pname, &dict, false)) {
+ default:
+ param_signal_error(plist, pname, code);
+ return code;
+ case 1:
+ ss = 0;
+ break;
+ case 0:{
+ /******
+ ****** THIS CAUSES A SEGV FOR DCT FILTERS, BECAUSE
+ ****** THEY DON'T INTIALIZE PROPERLY.
+ ******/
+#ifdef HAVE_LIBJPEG
+ if (template != &s_DCTE_template)
+#endif /* HAVE_LIBJPEG */
+ {
+ stream_state *ss_new =
+ s_alloc_state(mem, template->stype, pname);
+
+ if (ss_new == 0)
+ return_error(gs_error_VMerror);
+ ss_new->template = template;
+ if (template->set_defaults)
+ (*template->set_defaults)(ss_new);
+ code = (*put_params)(dict.list, ss_new);
+ if (code < 0) {
+ param_signal_error(plist, pname, code);
+ /* Make sure we free the new state. */
+ *pss = ss_new;
+ } else
+ ss = ss_new;
+ }
+ }
+ param_end_read_dict(plist, pname, &dict);
+ }
+ if (*pss != ss) {
+ if (ss) {
+ /****** FREE SUBSIDIARY OBJECTS -- HOW? ******/
+ gs_free_object(mem, *pss, pname);
+ }
+ *pss = ss;
+ }
+ return code;
+}
+
+/* Put a set of image-related parameters. */
+private int
+psdf_put_image_params(const gx_device_psdf * pdev, gs_param_list * plist,
+ const psdf_image_param_names * pnames, const psdf_image_filter_name * pifn,
+ psdf_image_params * params, int ecode)
+{
+ gs_param_string fs;
+
+ /*
+ * Since this procedure can be called before the device is open,
+ * we must use pdev->memory rather than pdev->v_memory.
+ */
+ gs_memory_t *mem = pdev->memory;
+ gs_param_name pname;
+ int dsti = params->DownsampleType;
+ int code;
+
+ if ((pname = pnames->ACSDict) != 0) {
+ code = psdf_put_image_dict_param(plist, pname, &params->ACSDict,
+ &s_DCTE_template,
+ psdf_DCT_put_params, mem);
+ if (code < 0)
+ ecode = code;
+ }
+ ecode = psdf_put_bool_param(plist, pnames->AntiAlias,
+ &params->AntiAlias, ecode);
+ if (pnames->AutoFilter)
+ ecode = psdf_put_bool_param(plist, pnames->AutoFilter,
+ &params->AutoFilter, ecode);
+ ecode = psdf_put_int_param(plist, pnames->Depth,
+ &params->Depth, ecode);
+ if ((pname = pnames->Dict) != 0) {
+ const stream_template *template;
+ ss_put_params_t put_params;
+
+ /* Hack to determine what kind of a Dict we want: */
+ if (pnames->Dict[0] == 'M')
+ template = &s_CFE_template,
+ put_params = psdf_CF_put_params;
+#ifdef HAVE_LIBJPEG
+ else
+ template = &s_DCTE_template,
+ put_params = psdf_DCT_put_params;
+#endif /* HAVE_LIBJPEG */
+ code = psdf_put_image_dict_param(plist, pname, &params->Dict,
+ template, put_params, mem);
+ if (code < 0)
+ ecode = code;
+ }
+ ecode = psdf_put_bool_param(plist, pnames->Downsample,
+ &params->Downsample, ecode);
+ if ((ecode = psdf_put_enum_param(plist, pnames->DownsampleType,
+ &dsti, DownsampleType_names,
+ ecode)) >= 0
+ )
+ params->DownsampleType = (enum psdf_downsample_type)dsti;
+ ecode = psdf_put_bool_param(plist, pnames->Encode,
+ &params->Encode, ecode);
+ switch (code = param_read_string(plist, pnames->Filter, &fs)) {
+ case 0:
+ {
+ const psdf_image_filter_name *pn = pifn;
+
+ while (pn->pname != 0 && !psdf_key_eq(&fs, pn->pname))
+ pn++;
+ if (pn->pname == 0 || pn->min_version > pdev->version) {
+ ecode = gs_error_rangecheck;
+ goto ipe;
+ }
+ params->Filter = pn->pname;
+ params->filter_template = pn->template;
+ break;
+ }
+ default:
+ ecode = code;
+ ipe:param_signal_error(plist, pnames->Filter, ecode);
+ case 1:
+ break;
+ }
+ ecode = psdf_put_int_param(plist, pnames->Resolution,
+ &params->Resolution, ecode);
+ if (ecode >= 0) { /* Force parameters to acceptable values. */
+ if (params->Resolution < 1)
+ params->Resolution = 1;
+ switch (params->Depth) {
+ default:
+ params->Depth = -1;
+ case 1:
+ case 2:
+ case 4:
+ case 8:
+ case -1:
+ break;
+ }
+ }
+ return ecode;
+}
+
+/* Put parameters. */
+int
+gdev_psdf_put_params(gx_device * dev, gs_param_list * plist)
+{
+ gx_device_psdf *pdev = (gx_device_psdf *) dev;
+ int ecode = 0;
+ int code;
+ gs_param_name param_name;
+ psdf_distiller_params params;
+
+ /* General parameters. */
+
+ params = pdev->params;
+
+ ecode = psdf_put_bool_param(plist, "ASCII85EncodePages",
+ &params.ASCII85EncodePages, ecode);
+ {
+ int arpi = params.AutoRotatePages;
+
+ ecode = psdf_put_enum_param(plist, "AutoRotatePages", &arpi,
+ AutoRotatePages_names, ecode);
+ params.AutoRotatePages = (enum psdf_auto_rotate_pages)arpi;
+ }
+ ecode = psdf_put_bool_param(plist, "CompressPages",
+ &params.CompressPages, ecode);
+ switch (code = param_read_long(plist, (param_name = "ImageMemory"), &params.ImageMemory)) {
+ default:
+ ecode = code;
+ param_signal_error(plist, param_name, ecode);
+ case 0:
+ case 1:
+ break;
+ }
+ ecode = psdf_put_bool_param(plist, "LZWEncodePages",
+ &params.LZWEncodePages, ecode);
+ ecode = psdf_put_bool_param(plist, "PreserveHalftoneInfo",
+ &params.PreserveHalftoneInfo, ecode);
+ ecode = psdf_put_bool_param(plist, "PreserveOPIComments",
+ &params.PreserveOPIComments, ecode);
+ ecode = psdf_put_bool_param(plist, "PreserveOverprintSettings",
+ &params.PreserveOverprintSettings, ecode);
+ {
+ int tfii = params.TransferFunctionInfo;
+
+ ecode = psdf_put_enum_param(plist, "TransferFunctionInfo", &tfii,
+ TransferFunctionInfo_names, ecode);
+ params.TransferFunctionInfo = (enum psdf_transfer_function_info)tfii;
+ }
+ {
+ int ucrbgi = params.UCRandBGInfo;
+
+ ecode = psdf_put_enum_param(plist, "UCRandBGInfo", &ucrbgi,
+ UCRandBGInfo_names, ecode);
+ params.UCRandBGInfo = (enum psdf_ucr_and_bg_info)ucrbgi;
+ }
+#ifdef HAVE_LIBZ
+ ecode = psdf_put_bool_param(plist, "UseFlateCompression",
+ &params.UseFlateCompression, ecode);
+#endif /* HAVE_LIBZ */
+
+ /* Color sampled image parameters */
+
+ ecode = psdf_put_image_params(pdev, plist, &Color_names, Poly_filters,
+ &params.ColorImage, ecode);
+ {
+ int ccsi = params.ColorConversionStrategy;
+
+ ecode = psdf_put_enum_param(plist, "ColorConversionStrategy", &ccsi,
+ ColorConversionStrategy_names, ecode);
+ params.ColorConversionStrategy =
+ (enum psdf_color_conversion_strategy)ccsi;
+ }
+ ecode = psdf_put_bool_param(plist, "ConvertCMYKImagesToRGB",
+ &params.ConvertCMYKImagesToRGB, ecode);
+ ecode = psdf_put_bool_param(plist, "ConvertImagesToIndexed",
+ &params.ConvertImagesToIndexed, ecode);
+
+ /* Gray sampled image parameters */
+
+ ecode = psdf_put_image_params(pdev, plist, &Gray_names, Poly_filters,
+ &params.GrayImage, ecode);
+
+ /* Mono sampled image parameters */
+
+ ecode = psdf_put_image_params(pdev, plist, &Mono_names, Mono_filters,
+ &params.MonoImage, ecode);
+
+ /* Font embedding parameters */
+
+ ecode = psdf_put_embed_param(plist, "~AlwaysEmbed",
+ &params.AlwaysEmbed, ecode);
+ ecode = psdf_put_embed_param(plist, "~NeverEmbed",
+ &params.NeverEmbed, ecode);
+ ecode = psdf_put_bool_param(plist, "EmbedAllFonts",
+ &params.EmbedAllFonts, ecode);
+ ecode = psdf_put_bool_param(plist, "SubsetFonts",
+ &params.SubsetFonts, ecode);
+ ecode = psdf_put_int_param(plist, "MaxSubsetPct",
+ &params.MaxSubsetPct, ecode);
+
+ if (ecode < 0)
+ return ecode;
+ code = gdev_vector_put_params(dev, plist);
+ if (code < 0)
+ return code;
+
+ pdev->params = params; /* OK to update now */
+ return 0;
+}
diff --git a/pstoraster/gdevpsds.c b/pstoraster/gdevpsds.c
new file mode 100644
index 000000000..da128b5b7
--- /dev/null
+++ b/pstoraster/gdevpsds.c
@@ -0,0 +1,470 @@
+/* Copyright (C) 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Image processing streams for PostScript and PDF writers */
+#include "gx.h"
+#include "memory_.h"
+#include "gserrors.h"
+#include "gxdcconv.h"
+#include "gdevpsds.h"
+
+/* ---------------- Convert between 1/2/4 and 8 bits ---------------- */
+gs_private_st_simple(st_1248_state, stream_1248_state, "stream_1248_state");
+
+/* Initialize the state. */
+private int
+s_1_init(stream_state * st)
+{
+ stream_1248_state *const ss = (stream_1248_state *) st;
+
+ ss->left = ss->samples_per_row;
+ ss->bits_per_sample = 1;
+ return 0;
+}
+private int
+s_2_init(stream_state * st)
+{
+ stream_1248_state *const ss = (stream_1248_state *) st;
+
+ ss->left = ss->samples_per_row;
+ ss->bits_per_sample = 2;
+ return 0;
+}
+private int
+s_4_init(stream_state * st)
+{
+ stream_1248_state *const ss = (stream_1248_state *) st;
+
+ ss->left = ss->samples_per_row;
+ ss->bits_per_sample = 4;
+ return 0;
+}
+
+/* Process one buffer. */
+#define BEGIN_1248\
+ stream_1248_state * const ss = (stream_1248_state *)st;\
+ const byte *p = pr->ptr;\
+ const byte *rlimit = pr->limit;\
+ byte *q = pw->ptr;\
+ byte *wlimit = pw->limit;\
+ uint left = ss->left;\
+ int status;\
+ int n
+#define END_1248\
+ pr->ptr = p;\
+ pw->ptr = q;\
+ ss->left = left;\
+ return status
+
+/* N-to-8 expansion */
+#define FOREACH_N_8(in, nout)\
+ status = 0;\
+ for ( ; p < rlimit; left -= n, q += n, ++p ) {\
+ byte in = p[1];\
+ n = min(left, nout);\
+ if ( wlimit - q < n ) {\
+ status = 1;\
+ break;\
+ }\
+ switch ( n ) {\
+ case 0: left = ss->samples_per_row; --p; continue;
+#define END_FOREACH_N_8\
+ }\
+ }
+private int
+s_N_8_process(stream_state * st, stream_cursor_read * pr,
+ stream_cursor_write * pw, bool last)
+{
+ BEGIN_1248;
+
+ switch (ss->bits_per_sample) {
+
+ case 1:{
+ FOREACH_N_8(in, 8)
+ case 8:
+ q[8] = (byte) - (in & 1);
+ case 7:
+ q[7] = (byte) - ((in >> 1) & 1);
+ case 6:
+ q[6] = (byte) - ((in >> 2) & 1);
+ case 5:
+ q[5] = (byte) - ((in >> 3) & 1);
+ case 4:
+ q[4] = (byte) - ((in >> 4) & 1);
+ case 3:
+ q[3] = (byte) - ((in >> 5) & 1);
+ case 2:
+ q[2] = (byte) - ((in >> 6) & 1);
+ case 1:
+ q[1] = (byte) - (in >> 7);
+ END_FOREACH_N_8;
+ }
+ break;
+
+ case 2:{
+ static const byte b2[4] =
+ {0x00, 0x55, 0xaa, 0xff};
+
+ FOREACH_N_8(in, 4)
+ case 4:
+ q[4] = b2[in & 3];
+ case 3:
+ q[3] = b2[(in >> 2) & 3];
+ case 2:
+ q[2] = b2[(in >> 4) & 3];
+ case 1:
+ q[1] = b2[in >> 6];
+ END_FOREACH_N_8;
+ }
+ break;
+
+ case 4:{
+ static const byte b4[16] =
+ {
+ 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
+ 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff
+ };
+
+ FOREACH_N_8(in, 2)
+ case 2:
+ q[2] = b4[in & 0xf];
+ case 1:
+ q[1] = b4[in >> 4];
+ END_FOREACH_N_8;
+ }
+ break;
+
+ default:
+ return ERRC;
+ }
+
+ END_1248;
+}
+
+/* 8-to-N reduction */
+#define FOREACH_8_N(out, nin)\
+ byte out;\
+ status = 1;\
+ for ( ; q < wlimit; left -= n, p += n, ++q ) {\
+ n = min(left, nin);\
+ if ( rlimit - p < n ) {\
+ status = 0;\
+ break;\
+ }\
+ out = 0;\
+ switch ( n ) {\
+ case 0: left = ss->samples_per_row; --q; continue;
+#define END_FOREACH_8_N\
+ q[1] = out;\
+ }\
+ }
+private int
+s_8_N_process(stream_state * st, stream_cursor_read * pr,
+ stream_cursor_write * pw, bool last)
+{
+ BEGIN_1248;
+
+ switch (ss->bits_per_sample) {
+
+ case 1:{
+ FOREACH_8_N(out, 8)
+ case 8:
+ out = p[8] >> 7;
+ case 7:
+ out |= (p[7] >> 7) << 1;
+ case 6:
+ out |= (p[6] >> 7) << 2;
+ case 5:
+ out |= (p[5] >> 7) << 3;
+ case 4:
+ out |= (p[4] >> 7) << 4;
+ case 3:
+ out |= (p[3] >> 7) << 5;
+ case 2:
+ out |= (p[2] >> 7) << 6;
+ case 1:
+ out |= p[1] & 0x80;
+ END_FOREACH_8_N;
+ }
+ break;
+
+ case 2:{
+ FOREACH_8_N(out, 4)
+ case 4:
+ out |= p[4] >> 6;
+ case 3:
+ out |= (p[3] >> 6) << 2;
+ case 2:
+ out |= (p[2] >> 6) << 4;
+ case 1:
+ out |= p[1] & 0xc0;
+ END_FOREACH_8_N;
+ }
+ break;
+
+ case 4:{
+ FOREACH_8_N(out, 2)
+ case 2:
+ out |= p[2] >> 4;
+ case 1:
+ out |= p[1] & 0xf0;
+ END_FOREACH_8_N;
+ }
+ break;
+
+ default:
+ return ERRC;
+ }
+
+ END_1248;
+}
+
+const stream_template s_1_8_template =
+{
+ &st_1248_state, s_1_init, s_N_8_process, 1, 8
+};
+const stream_template s_2_8_template =
+{
+ &st_1248_state, s_2_init, s_N_8_process, 1, 4
+};
+const stream_template s_4_8_template =
+{
+ &st_1248_state, s_4_init, s_N_8_process, 1, 2
+};
+
+const stream_template s_8_1_template =
+{
+ &st_1248_state, s_1_init, s_8_N_process, 8, 1
+};
+const stream_template s_8_2_template =
+{
+ &st_1248_state, s_2_init, s_8_N_process, 4, 1
+};
+const stream_template s_8_4_template =
+{
+ &st_1248_state, s_4_init, s_8_N_process, 2, 1
+};
+
+/* ---------------- CMYK => RGB conversion ---------------- */
+
+private_st_C2R_state();
+
+/* Process one buffer. */
+private int
+s_C2R_process(stream_state * st, stream_cursor_read * pr,
+ stream_cursor_write * pw, bool last)
+{
+ stream_C2R_state *const ss = (stream_C2R_state *) st;
+ const byte *p = pr->ptr;
+ const byte *rlimit = pr->limit;
+ byte *q = pw->ptr;
+ byte *wlimit = pw->limit;
+
+ for (; rlimit - p >= 4 && wlimit - q >= 3; p += 4, q += 3) {
+ byte bc = p[1], bm = p[2], by = p[3], bk = p[4];
+ frac rgb[3];
+
+ color_cmyk_to_rgb(byte2frac(bc), byte2frac(bm), byte2frac(by),
+ byte2frac(bk), ss->pis, rgb);
+ q[1] = frac2byte(rgb[0]);
+ q[2] = frac2byte(rgb[1]);
+ q[3] = frac2byte(rgb[2]);
+ }
+ pr->ptr = p;
+ pw->ptr = q;
+ return (rlimit - p < 4 ? 0 : 1);
+}
+
+const stream_template s_C2R_template =
+{
+ &st_C2R_state, 0 /*NULL */ , s_C2R_process, 4, 3
+};
+
+/* ---------------- Downsampling ---------------- */
+
+private void
+s_Downsample_set_defaults(register stream_state * st)
+{
+ stream_Downsample_state *const ss =
+ (stream_Downsample_state *) st;
+
+ s_Downsample_set_defaults_inline(ss);
+}
+
+/* Subsample */
+/****** DOESN'T IMPLEMENT padY YET ******/
+
+gs_private_st_simple(st_Subsample_state, stream_Subsample_state,
+ "stream_Subsample_state");
+
+/* Initialize the state. */
+private int
+s_Subsample_init(stream_state * st)
+{
+ stream_Subsample_state *const ss = (stream_Subsample_state *) st;
+
+ ss->x = ss->y = 0;
+ return 0;
+}
+
+/* Process one buffer. */
+private int
+s_Subsample_process(stream_state * st, stream_cursor_read * pr,
+ stream_cursor_write * pw, bool last)
+{
+ stream_Subsample_state *const ss = (stream_Subsample_state *) st;
+ const byte *p = pr->ptr;
+ const byte *rlimit = pr->limit;
+ byte *q = pw->ptr;
+ byte *wlimit = pw->limit;
+ int spp = ss->Colors;
+ int width = ss->Columns;
+ int xf = ss->XFactor, yf = ss->YFactor;
+ int xf2 = xf / 2, yf2 = yf / 2;
+ int xlimit = (width / xf) * xf;
+ int xlast = (ss->padX && xlimit < width ? xlimit + (width % xf) / 2 : -1);
+ int x = ss->x, y = ss->y;
+ int status = 0;
+
+ for (; rlimit - p >= spp; p += spp) {
+ if (y == yf2 && ((x % xf == xf2 && x < xlimit) || x == xlast)) {
+ if (wlimit - q < spp) {
+ status = 1;
+ break;
+ }
+ memcpy(q + 1, p + 1, spp);
+ q += spp;
+ }
+ if (++x == width) {
+ x = 0;
+ if (++y == yf) {
+ y = 0;
+ }
+ }
+ }
+ pr->ptr = p;
+ pw->ptr = q;
+ ss->x = x, ss->y = y;
+ return status;
+}
+
+const stream_template s_Subsample_template =
+{
+ &st_Subsample_state, s_Subsample_init, s_Subsample_process, 4, 4,
+ 0 /* NULL */, s_Downsample_set_defaults
+};
+
+/* Average */
+
+private_st_Average_state();
+
+/* Initialize the state. */
+private int
+s_Average_init(stream_state * st)
+{
+ stream_Average_state *const ss = (stream_Average_state *) st;
+
+ ss->sum_size =
+ ss->Colors * ((ss->Columns + ss->XFactor - 1) / ss->XFactor);
+ ss->copy_size = ss->sum_size -
+ (ss->padX || (ss->Columns % ss->XFactor == 0) ? 0 : ss->Colors);
+ ss->sums =
+ (uint *)gs_alloc_byte_array(st->memory, ss->sum_size,
+ sizeof(uint), "Average sums");
+ if (ss->sums == 0)
+ return ERRC; /****** WRONG ******/
+ memset(ss->sums, 0, ss->sum_size * sizeof(uint));
+ return s_Subsample_init(st);
+}
+
+/* Release the state. */
+private void
+s_Average_release(stream_state * st)
+{
+ stream_Average_state *const ss = (stream_Average_state *) st;
+
+ gs_free_object(st->memory, ss->sums, "Average sums");
+}
+
+/* Process one buffer. */
+private int
+s_Average_process(stream_state * st, stream_cursor_read * pr,
+ stream_cursor_write * pw, bool last)
+{
+ stream_Average_state *const ss = (stream_Average_state *) st;
+ const byte *p = pr->ptr;
+ const byte *rlimit = pr->limit;
+ byte *q = pw->ptr;
+ byte *wlimit = pw->limit;
+ int spp = ss->Colors;
+ int width = ss->Columns;
+ int xf = ss->XFactor, yf = ss->YFactor;
+ int x = ss->x, y = ss->y;
+ uint *sums = ss->sums;
+ int status = 0;
+
+top:
+ if (y == yf || (last && p >= rlimit && ss->padY && y != 0)) {
+ /* We're copying averaged values to the output. */
+ int ncopy = min(ss->copy_size - x, wlimit - q);
+
+ if (ncopy) {
+ int scale = xf * y;
+
+ while (--ncopy >= 0)
+ *++q = (byte) (sums[x++] / scale);
+ }
+ if (x < ss->copy_size) {
+ status = 1;
+ goto out;
+ }
+ /* Done copying. */
+ x = y = 0;
+ memset(sums, 0, ss->sum_size * sizeof(uint));
+ }
+ while (rlimit - p >= spp) {
+ uint *bp = sums + x / xf * spp;
+ int i;
+
+ for (i = spp; --i >= 0;)
+ *bp++ += *++p;
+ if (++x == width) {
+ x = 0;
+ ++y;
+ goto top;
+ }
+ }
+out:
+ pr->ptr = p;
+ pw->ptr = q;
+ ss->x = x, ss->y = y;
+ return status;
+}
+
+const stream_template s_Average_template =
+{
+ &st_Average_state, s_Average_init, s_Average_process, 4, 4,
+ s_Average_release, s_Downsample_set_defaults
+};
diff --git a/pstoraster/gdevpsds.h b/pstoraster/gdevpsds.h
new file mode 100644
index 000000000..772c1ff53
--- /dev/null
+++ b/pstoraster/gdevpsds.h
@@ -0,0 +1,110 @@
+/* Copyright (C) 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Image processing stream interface for PostScript and PDF writers */
+
+#ifndef gdevpsds_INCLUDED
+# define gdevpsds_INCLUDED
+
+#include "strimpl.h"
+
+/* Convert between 1/2/4 bits and 8 bits. */
+typedef struct stream_1248_state_s {
+ stream_state_common;
+ /* The following are set at initialization time. */
+ uint samples_per_row; /* >0 */
+ int bits_per_sample; /* 1, 2, 4 */
+ /* The following are updated dynamically. */
+ uint left; /* # of samples left in current row */
+} stream_1248_state;
+
+/* Expand N (1, 2, 4) bits to 8. */
+extern const stream_template s_1_8_template;
+extern const stream_template s_2_8_template;
+extern const stream_template s_4_8_template;
+
+/* Reduce 8 bits to N (1, 2, 4) */
+extern const stream_template s_8_1_template;
+extern const stream_template s_8_2_template;
+extern const stream_template s_8_4_template;
+
+/* Initialize an expansion or reduction stream. */
+#define s_1248_init(ss, Columns, samples_per_pixel)\
+ ((ss)->samples_per_row = (Columns) * (samples_per_pixel),\
+ (*(ss)->template->init)((stream_state *)(ss)))
+
+/* Convert (8-bit) CMYK to RGB. */
+typedef struct stream_C2R_state_s {
+ stream_state_common;
+ /* The following are set at initialization time. */
+ const gs_imager_state *pis; /* for UCR & BG */
+} stream_C2R_state;
+
+#define private_st_C2R_state() /* in gdevpsds.c */\
+ gs_private_st_ptrs1(st_C2R_state, stream_C2R_state, "stream_C2R_state",\
+ c2r_enum_ptrs, c2r_reloc_ptrs, pis)
+extern const stream_template s_C2R_template;
+
+#define s_C2R_init(ss, pisv)\
+ ((ss)->pis = (pisv), 0)
+
+/* Downsample, possibly with anti-aliasing. */
+#define stream_Downsample_state_common\
+ stream_state_common;\
+ /* The client sets the following before initialization. */\
+ int Colors;\
+ int Columns; /* # of input columns */\
+ int XFactor, YFactor;\
+ bool AntiAlias;\
+ bool padX, padY; /* keep excess samples */\
+ /* The following are updated dynamically. */\
+ int x, y /* position within input image */
+#define s_Downsample_set_defaults_inline(ss)\
+ ((ss)->AntiAlias = (ss)->padX = (ss)->padY = false)
+typedef struct stream_Downsample_state_s {
+ stream_Downsample_state_common;
+} stream_Downsample_state;
+
+/* Subsample */
+/****** Subsample DOESN'T IMPLEMENT padY YET ******/
+typedef struct stream_Subsample_state_s {
+ stream_Downsample_state_common;
+} stream_Subsample_state;
+extern const stream_template s_Subsample_template;
+
+/* Average */
+typedef struct stream_Average_state_s {
+ stream_Downsample_state_common;
+ uint sum_size;
+ uint copy_size;
+ uint *sums; /* accumulated sums for average */
+} stream_Average_state;
+
+#define private_st_Average_state() /* in gdevpsds.c */\
+ gs_private_st_ptrs1(st_Average_state, stream_Average_state,\
+ "stream_Average_state", avg_enum_ptrs, avg_reloc_ptrs, sums)
+extern const stream_template s_Average_template;
+
+#endif /* gdevpsds_INCLUDED */
diff --git a/pstoraster/gdevpstr.c b/pstoraster/gdevpstr.c
new file mode 100644
index 000000000..48f5780d3
--- /dev/null
+++ b/pstoraster/gdevpstr.c
@@ -0,0 +1,197 @@
+/* Copyright (C) 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Stream output for PostScript- and PDF-writing drivers */
+#include "math_.h" /* for fabs */
+#include "stdio_.h" /* for stream.h */
+#include "string_.h" /* for strchr */
+#include "gdevpstr.h"
+#include "stream.h"
+
+/* ------ Output ------ */
+
+/* Put a byte array on a stream. */
+int
+pwrite(stream * s, const void *ptr, uint count)
+{
+ uint used;
+
+ sputs(s, (const byte *)ptr, count, &used);
+ return (int)used;
+}
+
+/* Put a string on a stream. */
+int
+pputs(stream * s, const char *str)
+{
+ uint len = strlen(str);
+ uint used;
+ int status = sputs(s, (const byte *)str, len, &used);
+
+ return (status >= 0 && used == len ? 0 : EOF);
+}
+
+/* Print a format string up to the first variable substitution. */
+/* Return a pointer to the %, or to the terminating 0 if no % found. */
+private const char *
+pprintf_scan(stream * s, const char *format)
+{
+ const char *fp = format;
+
+ for (; *fp != 0; ++fp) {
+ if (*fp == '%') {
+ if (fp[1] != '%')
+ break;
+ ++fp;
+ }
+ sputc(s, *fp);
+ }
+ return fp;
+}
+
+/* Print (an) int value(s) using a format. */
+const char *
+pprintd1(stream * s, const char *format, int v)
+{
+ const char *fp = pprintf_scan(s, format);
+ char str[25];
+
+#ifdef DEBUG
+ if (*fp == 0 || fp[1] != 'd') /* shouldn't happen! */
+ lprintf1("Bad format in pprintd1: %s\n", format);
+#endif
+ sprintf(str, "%d", v);
+ pputs(s, str);
+ return pprintf_scan(s, fp + 2);
+}
+const char *
+pprintd2(stream * s, const char *format, int v1, int v2)
+{
+ return pprintd1(s, pprintd1(s, format, v1), v2);
+}
+const char *
+pprintd3(stream * s, const char *format, int v1, int v2, int v3)
+{
+ return pprintd2(s, pprintd1(s, format, v1), v2, v3);
+}
+const char *
+pprintd4(stream * s, const char *format, int v1, int v2, int v3, int v4)
+{
+ return pprintd2(s, pprintd2(s, format, v1, v2), v3, v4);
+}
+
+/* Print (a) floating point number(s) using a format. */
+/* See gdevpdfx.h for why this is needed. */
+const char *
+pprintg1(stream * s, const char *format, floatp v)
+{
+ const char *fp = pprintf_scan(s, format);
+ char str[50];
+
+#ifdef DEBUG
+ if (*fp == 0 || fp[1] != 'g') /* shouldn't happen! */
+ lprintf1("Bad format in pprintg: %s\n", format);
+#endif
+ sprintf(str, "%g", v);
+ if (strchr(str, 'e')) {
+ /* Bad news. Try again using f-format. */
+ sprintf(str, (fabs(v) > 1 ? "%1.1f" : "%1.8f"), v);
+ }
+ pputs(s, str);
+ return pprintf_scan(s, fp + 2);
+}
+const char *
+pprintg2(stream * s, const char *format, floatp v1, floatp v2)
+{
+ return pprintg1(s, pprintg1(s, format, v1), v2);
+}
+const char *
+pprintg3(stream * s, const char *format, floatp v1, floatp v2, floatp v3)
+{
+ return pprintg2(s, pprintg1(s, format, v1), v2, v3);
+}
+const char *
+pprintg4(stream * s, const char *format, floatp v1, floatp v2, floatp v3,
+ floatp v4)
+{
+ return pprintg2(s, pprintg2(s, format, v1, v2), v3, v4);
+}
+const char *
+pprintg6(stream * s, const char *format, floatp v1, floatp v2, floatp v3,
+ floatp v4, floatp v5, floatp v6)
+{
+ return pprintg3(s, pprintg3(s, format, v1, v2, v3), v4, v5, v6);
+}
+
+/* Print a long value using a format. */
+const char *
+pprintld1(stream * s, const char *format, long v)
+{
+ const char *fp = pprintf_scan(s, format);
+ char str[25];
+
+#ifdef DEBUG
+ if (*fp == 0 || fp[1] != 'l' || fp[2] != 'd') /* shouldn't happen! */
+ lprintf1("Bad format in pprintld: %s\n", format);
+#endif
+ sprintf(str, "%ld", v);
+ pputs(s, str);
+ return pprintf_scan(s, fp + 3);
+}
+const char *
+pprintld2(stream * s, const char *format, long v1, long v2)
+{
+ return pprintld1(s, pprintld1(s, format, v1), v2);
+}
+const char *
+pprintld3(stream * s, const char *format, long v1, long v2, long v3)
+{
+ return pprintld2(s, pprintld1(s, format, v1), v2, v3);
+}
+
+/* Print (a) string(s) using a format. */
+const char *
+pprints1(stream * s, const char *format, const char *str)
+{
+ const char *fp = pprintf_scan(s, format);
+
+#ifdef DEBUG
+ if (*fp == 0 || fp[1] != 's') /* shouldn't happen! */
+ lprintf1("Bad format in pprints: %s\n", format);
+#endif
+ pputs(s, str);
+ return pprintf_scan(s, fp + 2);
+}
+const char *
+pprints2(stream * s, const char *format, const char *str1, const char *str2)
+{
+ return pprints1(s, pprints1(s, format, str1), str2);
+}
+const char *
+pprints3(stream * s, const char *format, const char *str1, const char *str2,
+ const char *str3)
+{
+ return pprints2(s, pprints1(s, format, str1), str2, str3);
+}
diff --git a/pstoraster/gdevpstr.h b/pstoraster/gdevpstr.h
new file mode 100644
index 000000000..12156c39a
--- /dev/null
+++ b/pstoraster/gdevpstr.h
@@ -0,0 +1,90 @@
+/* Copyright (C) 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Stream output for PostScript- and PDF-writing drivers. */
+
+#ifndef gdevpstr_INCLUDED
+# define gdevpstr_INCLUDED
+
+/* Define an opaque type for streams. */
+#ifndef stream_DEFINED
+# define stream_DEFINED
+typedef struct stream_s stream;
+
+#endif
+
+/* Put a character on a stream. */
+#define pputc(s, c) spputc(s, c)
+
+/* Put a byte array on a stream. */
+int pwrite(P3(stream * s, const void *ptr, uint count));
+
+/* Put a string on a stream. */
+int pputs(P2(stream * s, const char *str));
+
+/*
+ * Print (a) floating point number(s) using a format. This is needed
+ * because %f format always prints a fixed number of digits after the
+ * decimal point, and %g format may use %e format, which PDF disallows.
+ * These functions return a pointer to the next %-element of the format, or
+ * to the terminating 0.
+ */
+const char *pprintg1(P3(stream * s, const char *format, floatp v));
+const char *pprintg2(P4(stream * s, const char *format, floatp v1, floatp v2));
+const char *pprintg3(P5(stream * s, const char *format,
+ floatp v1, floatp v2, floatp v3));
+const char *pprintg4(P6(stream * s, const char *format,
+ floatp v1, floatp v2, floatp v3, floatp v4));
+const char *pprintg6(P8(stream * s, const char *format,
+ floatp v1, floatp v2, floatp v3, floatp v4,
+ floatp v5, floatp v6));
+
+/*
+ * The rest of these printing functions exist solely because the ANSI C
+ * "standard" for functions with a variable number of arguments is not
+ * implemented properly or consistently across compilers.
+ */
+/* Print (an) int value(s) using a format. */
+const char *pprintd1(P3(stream * s, const char *format, int v));
+const char *pprintd2(P4(stream * s, const char *format, int v1, int v2));
+const char *pprintd3(P5(stream * s, const char *format,
+ int v1, int v2, int v3));
+const char *pprintd4(P6(stream * s, const char *format,
+ int v1, int v2, int v3, int v4));
+
+/* Print a long value using a format. */
+const char *pprintld1(P3(stream * s, const char *format, long v));
+const char *pprintld2(P4(stream * s, const char *format, long v1, long v2));
+const char *pprintld3(P5(stream * s, const char *format,
+ long v1, long v2, long v3));
+
+/* Print (a) string(s) using a format. */
+const char *pprints1(P3(stream * s, const char *format, const char *str));
+const char *pprints2(P4(stream * s, const char *format,
+ const char *str1, const char *str2));
+const char *pprints3(P5(stream * s, const char *format,
+ const char *str1, const char *str2, const char *str3));
+
+#endif /* gdevpstr_INCLUDED */
diff --git a/pstoraster/gdevpxat.h b/pstoraster/gdevpxat.h
new file mode 100644
index 000000000..bca49fa72
--- /dev/null
+++ b/pstoraster/gdevpxat.h
@@ -0,0 +1,149 @@
+/* Copyright (C) 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Attribute ID definitions for PCL XL */
+
+#ifndef gdevpxat_INCLUDED
+# define gdevpxat_INCLUDED
+
+typedef enum {
+
+ pxaPaletteDepth = 2,
+ pxaColorSpace,
+ pxaNullBrush,
+ pxaNullPen,
+ pxaPaletteData,
+
+ pxaPatternSelectID = 8,
+ pxaGrayLevel,
+ pxaLightness, /* 2.0 */
+ pxaRGBColor,
+ pxaPatternOrigin,
+ pxaNewDestinationSize,
+ pxaPrimaryArray, /* 2.0 */
+ pxaPrimaryDepth, /* 2.0 */
+ pxaSaturation, /* 2.0 */
+ pxaColorimetricColorSpace, /* 2.0 */
+ pxaXYChromaticities, /* 2.0 */
+ pxaWhiteReferencePoint, /* 2.0 */
+ pxaCRGBMinMax, /* 2.0 */
+ pxaGammaGain, /* 2.0 */
+
+ pxaDeviceMatrix = 33,
+ pxaDitherMatrixDataType,
+ pxaDitherOrigin,
+ pxaMediaDestination,
+ pxaMediaSize,
+ pxaMediaSource,
+ pxaMediaType,
+ pxaOrientation,
+ pxaPageAngle,
+ pxaPageOrigin,
+ pxaPageScale,
+ pxaROP3,
+ pxaTxMode,
+
+ pxaCustomMediaSize = 47,
+ pxaCustomMediaSizeUnits,
+ pxaPageCopies,
+ pxaDitherMatrixSize,
+ pxaDitherMatrixDepth,
+ pxaSimplexPageMode,
+ pxaDuplexPageMode,
+ pxaDuplexPageSide,
+
+ pxaArcDirection = 65,
+ pxaBoundingBox,
+ pxaDashOffset,
+ pxaEllipseDimension,
+ pxaEndPoint,
+ pxaFillMode,
+ pxaLineCapStyle,
+ pxaLineJoinStyle,
+ pxaMiterLength,
+ pxaLineDashStyle,
+ pxaPenWidth,
+ pxaPoint,
+ pxaNumberOfPoints,
+ pxaSolidLine,
+ pxaStartPoint,
+ pxaPointType,
+ pxaControlPoint1,
+ pxaControlPoint2,
+ pxaClipRegion,
+ pxaClipMode,
+
+ pxaColorDepth = 98,
+ pxaBlockHeight,
+ pxaColorMapping,
+ pxaCompressMode,
+ pxaDestinationBox,
+ pxaDestinationSize,
+ pxaPatternPersistence,
+ pxaPatternDefineID,
+
+ pxaSourceHeight = 107,
+ pxaSourceWidth,
+ pxaStartLine,
+ pxaPadBytesMultiple, /* 2.0 */
+ pxaBlockByteLength, /* 2.0 */
+
+ pxaNumberOfScanLines = 115,
+
+ pxaCommentData = 129,
+ pxaDataOrg,
+
+ pxaMeasure = 134,
+
+ pxaSourceType = 136,
+ pxaUnitsPerMeasure,
+
+ pxaStreamName = 139,
+ pxaStreamDataLength,
+
+ pxaErrorReport = 143,
+
+ pxaCharAngle = 161,
+ pxaCharCode,
+ pxaCharDataSize,
+ pxaCharScale,
+ pxaCharShear,
+ pxaCharSize,
+ pxaFontHeaderLength,
+ pxaFontName,
+ pxaFontFormat,
+ pxaSymbolSet,
+ pxaTextData,
+ pxaCharSubModeArray,
+
+ pxaXSpacingData = 175,
+ pxaYSpacingData,
+ pxaCharBoldValue,
+
+ px_attribute_next
+
+} px_attribute_t;
+
+#endif /* gdevpxat_INCLUDED */
diff --git a/pstoraster/gdevpxen.h b/pstoraster/gdevpxen.h
new file mode 100644
index 000000000..25eb1e30c
--- /dev/null
+++ b/pstoraster/gdevpxen.h
@@ -0,0 +1,269 @@
+/* Copyright (C) 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Enumerated attribute value definitions for PCL XL */
+
+#ifndef gdevpxen_INCLUDED
+# define gdevpxen_INCLUDED
+
+typedef enum {
+ eClockWise = 0,
+ eCounterClockWise,
+ pxeArcDirection_next
+} pxeArcDirection_t;
+
+typedef enum {
+ eNoSubstitution = 0,
+ eVerticalSubstitution,
+ pxeCharSubModeArray_next
+} pxeCharSubModeArray_t;
+
+typedef enum {
+ eNonZeroWinding = 0,
+ eEvenOdd,
+ pxeClipMode_next,
+ pxeFillMode_next = pxeClipMode_next /* see pxeFillMode_t below */
+} pxeClipMode_t;
+
+typedef enum {
+ eInterior = 0,
+ eExterior,
+ pxeClipRegion_next
+} pxeClipRegion_t;
+
+typedef enum {
+ e1Bit = 0,
+ e4Bit,
+ e8Bit,
+ pxeColorDepth_next
+} pxeColorDepth_t;
+
+typedef enum {
+ eCRGB = 5, /* Note: for this enumeration, 0 is not a valid value */
+ pxeColorimetricColorSpace_next
+} pxeColorimetricColorSpace_t; /* 2.0 */
+
+typedef enum {
+ eDirectPixel = 0,
+ eIndexedPixel,
+ pxeColorMapping_next
+} pxeColorMapping_t;
+
+typedef enum {
+ eNoColorSpace = 0, /* Note: for this enumeration, 0 is not a valid value */
+ eGray,
+ eRGB,
+ eSRGB, /* 2.0 */
+ pxeColorSpace_next
+} pxeColorSpace_t;
+
+typedef enum {
+ eNoCompression = 0,
+ eRLECompression,
+ eJPEGCompression, /* 2.0 */
+ pxeCompressMode_next
+} pxeCompressMode_t;
+
+typedef enum {
+ eBinaryHighByteFirst = 0,
+ eBinaryLowByteFirst,
+ pxeDataOrg_next /* is this DataOrg or DataOrganization? */
+} pxeDataOrg_t;
+
+typedef enum {
+ eDefault = 0, /* bad choice of name! */
+ pxeDataSource_next
+} pxeDataSource_t;
+
+typedef enum {
+ eUByte = 0,
+ eSByte,
+ eUInt16,
+ eSInt16,
+ pxeDataType_next
+} pxeDataType_t;
+
+typedef enum {
+ eDownloaded = -1, /* Not a real value, indicates a downloaded matrix */
+ eDeviceBest = 0,
+ pxeDitherMatrix_next
+} pxeDitherMatrix_t;
+
+typedef enum {
+ eDuplexHorizontalBinding = 0,
+ eDuplexVerticalBinding,
+ pxeDuplexPageMode_next
+} pxeDuplexPageMode_t;
+
+typedef enum {
+ eFrontMediaSide = 0,
+ eBackMediaSide,
+ pxeDuplexPageSide_next
+} pxeDuplexPageSide_t;
+
+typedef enum {
+ /* Several pieces of code know that this is a bit mask. */
+ eNoReporting = 0,
+ eBackChannel,
+ eErrorPage,
+ eBackChAndErrPage,
+ eNWBackChannel, /* 2.0 */
+ eNWErrorPage, /* 2.0 */
+ eNWBackChAndErrPage, /* 2.0 */
+ pxeErrorReport_next
+} pxeErrorReport_t;
+
+typedef pxeClipMode_t pxeFillMode_t;
+
+typedef enum {
+ eButtCap = 0,
+ eRoundCap,
+ eSquareCap,
+ eTriangleCap,
+ pxeLineCap_next
+} pxeLineCap_t;
+
+#define pxeLineCap_to_library\
+ { gs_cap_butt, gs_cap_round, gs_cap_square, gs_cap_triangle }
+
+typedef enum {
+ eMiterJoin = 0,
+ eRoundJoin,
+ eBevelJoin,
+ eNoJoin,
+ pxeLineJoin_next
+} pxeLineJoin_t;
+
+#define pxeLineJoin_to_library\
+ { gs_join_miter, gs_join_round, gs_join_bevel, gs_join_none }
+
+typedef enum {
+ eInch = 0,
+ eMillimeter,
+ eTenthsOfAMillimeter,
+ pxeMeasure_next
+} pxeMeasure_t;
+
+#define pxeMeasure_to_points { 72.0, 72.0 / 25.4, 72.0 / 254.0 }
+
+typedef enum {
+ eDefaultDestination = 0,
+ eFaceDownBin, /* 2.0 */
+ eFaceUpBin, /* 2.0 */
+ eJobOffsetBin, /* 2.0 */
+ pxeMediaDestination_next
+} pxeMediaDestination_t;
+
+typedef enum {
+ eLetterPaper = 0,
+ eLegalPaper,
+ eA4Paper,
+ eExecPaper,
+ eLedgerPaper,
+ eA3Paper,
+ eCOM10Envelope,
+ eMonarchEnvelope,
+ eC5Envelope,
+ eDLEnvelope,
+ eJB4Paper,
+ eJB5Paper,
+ eB5Envelope,
+ eJPostcard,
+ eJDoublePostcard,
+ eA5Paper,
+ eA6Paper, /* 2.0 */
+ eJB6Paper, /* 2.0 */
+ pxeMediaSize_next
+} pxeMediaSize_t;
+
+/*
+ * Apply a macro (or procedure) to all known paper sizes.
+ * The arguments are:
+ * media size code, resolution for width/height, width, height.
+ */
+#define px_enumerate_media(m)\
+ m(eLetterPaper, 300, 2550, 3300)\
+ m(eLegalPaper, 300, 2550, 5300)\
+ m(eA4Paper, 300, 2480, 3507)\
+ m(eExecPaper, 300, 2175, 3150)\
+ m(eLedgerPaper, 300, 3300, 5100)\
+ m(eA3Paper, 300, 3507, 4960)\
+ m(eCOM10Envelope, 300, 1237, 2850)\
+ m(eMonarchEnvelope, 300, 1162, 2250)\
+ m(eC5Envelope, 300, 1913, 2704)\
+ m(eDLEnvelope, 300, 1299, 2598)\
+ m(eB5Envelope, 300, 2078, 2952)
+
+typedef enum {
+ eDefaultSource = 0,
+ eAutoSelect,
+ eManualFeed,
+ eMultiPurposeTray,
+ eUpperCassette,
+ eLowerCassette,
+ eEnvelopeTray,
+ eThirdCassette, /* 2.0 */
+ pxeMediaSource_next
+} pxeMediaSource_t;
+
+/**** MediaType is not documented. ****/
+typedef enum {
+ eDefaultType = 0,
+ pxeMediaType_next
+} pxeMediaType_t;
+
+typedef enum {
+ ePortraitOrientation = 0,
+ eLandscapeOrientation,
+ eReversePortrait,
+ eReverseLandscape,
+ pxeOrientation_next
+} pxeOrientation_t;
+
+typedef enum {
+ eTempPattern = 0,
+ ePagePattern,
+ eSessionPattern,
+ pxePatternPersistence_next
+} pxePatternPersistence_t;
+
+typedef enum {
+ eSimplexFrontSide = 0,
+ pxeSimplexPageMode_next
+} pxeSimplexPageMode_t;
+
+typedef enum {
+ eOpaque = 0,
+ eTransparent,
+ pxeTxMode_next
+} pxeTxMode_t;
+
+typedef enum {
+ eHorizontal = 0,
+ eVertical,
+ pxeWritingMode_next
+} pxeWritingMode_t; /* 2.0 */
+
+#endif /* gdevpxen_INCLUDED */
diff --git a/pstoraster/gdevpxop.h b/pstoraster/gdevpxop.h
new file mode 100644
index 000000000..9b61a01e0
--- /dev/null
+++ b/pstoraster/gdevpxop.h
@@ -0,0 +1,114 @@
+/* Copyright (C) 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Operator and other tag definitions for PCL XL */
+
+#ifndef gdevpxop_INCLUDED
+# define gdevpxop_INCLUDED
+
+typedef enum {
+/*0x */
+ pxtNull = 0x00, pxt01, pxt02, pxt03,
+ pxt04, pxt05, pxt06, pxt07,
+ pxt08, pxtHT, pxtLF, pxtVT,
+ pxtFF, pxtCR, pxt0e, pxt0f,
+/*1x */
+ pxt10, pxt11, pxt12, pxt13,
+ pxt14, pxt15, pxt16, pxt17,
+ pxt18, pxt19, pxt1a, pxt1b,
+ pxt1c, pxt1d, pxt1e, pxt1f,
+/*2x */
+ pxtSpace, pxt21, pxt22, pxt23,
+ pxt24, pxt25, pxt26, pxt_beginASCII,
+ pxt_beginBinaryMSB, pxt_beginBinaryLSB, pxt2a, pxt2b,
+ pxt2c, pxt2d, pxt2e, pxt2f,
+/*3x */
+ pxt30, pxt31, pxt32, pxt33,
+ pxt34, pxt35, pxt36, pxt37,
+ pxt38, pxt39, pxt3a, pxt3b,
+ pxt3c, pxt3d, pxt3e, pxt3f,
+/*4x */
+ pxt40, pxtBeginSession, pxtEndSession, pxtBeginPage,
+ pxtEndPage, pxt45, pxt46, pxtComment,
+ pxtOpenDataSource, pxtCloseDataSource, pxt4a, pxt4b,
+ pxt4c, pxt4d, pxt4e, pxtBeginFontHeader,
+/*5x */
+ pxtReadFontHeader, pxtEndFontHeader, pxtBeginChar, pxtReadChar,
+ pxtEndChar, pxtRemoveFont, pxtSetCharAttributes /*2.0 */ , pxt57,
+ pxt58, pxt59, pxt5a, pxtBeginStream,
+ pxtReadStream, pxtEndStream, pxtExecStream, pxtRemoveStream /*2.0 */ ,
+/*6x */
+ pxtPopGS, pxtPushGS, pxtSetClipReplace, pxtSetBrushSource,
+ pxtSetCharAngle, pxtSetCharScale, pxtSetCharShear, pxtSetClipIntersect,
+ pxtSetClipRectangle, pxtSetClipToPage, pxtSetColorSpace, pxtSetCursor,
+ pxtSetCursorRel, pxtSetHalftoneMethod, pxtSetFillMode, pxtSetFont,
+/*7x */
+ pxtSetLineDash, pxtSetLineCap, pxtSetLineJoin, pxtSetMiterLimit,
+ pxtSetPageDefaultCTM, pxtSetPageOrigin, pxtSetPageRotation, pxtSetPageScale,
+ pxtSetPaintTxMode, pxtSetPenSource, pxtSetPenWidth, pxtSetROP,
+ pxtSetSourceTxMode, pxtSetCharBoldValue, pxt7e, pxtSetClipMode,
+/*8x */
+ pxtSetPathToClip, pxtSetCharSubMode, pxt82, pxt83,
+ pxtCloseSubPath, pxtNewPath, pxtPaintPath, pxt87,
+ pxt88, pxt89, pxt8a, pxt8b,
+ pxt8c, pxt8d, pxt8e, pxt8f,
+/*9x */
+ pxt90, pxtArcPath, pxt92, pxtBezierPath,
+ pxt94, pxtBezierRelPath, pxtChord, pxtChordPath,
+ pxtEllipse, pxtEllipsePath, pxt9a, pxtLinePath,
+ pxt9c, pxtLineRelPath, pxtPie, pxtPiePath,
+/*ax */
+ pxtRectangle, pxtRectanglePath, pxtRoundRectangle, pxtRoundRectanglePath,
+ pxta4, pxta5, pxta6, pxta7,
+ pxtText, pxtTextPath, pxtaa, pxtab,
+ pxtac, pxtad, pxtae, pxtaf,
+/*bx */
+ pxtBeginImage, pxtReadImage, pxtEndImage, pxtBeginRastPattern,
+ pxtReadRastPattern, pxtEndRastPattern, pxtBeginScan, pxtb7,
+ pxtEndScan, pxtScanLineRel, pxtba, pxtbb,
+ pxtbc, pxtbd, pxtbe, pxtbf,
+/*cx */
+ pxt_ubyte, pxt_uint16, pxt_uint32, pxt_sint16,
+ pxt_sint32, pxt_real32, pxtc6, pxtc7,
+ pxt_ubyte_array, pxt_uint16_array, pxt_uint32_array, pxt_sint16_array,
+ pxt_sint32_array, pxt_real32_array, pxtce, pxtcf,
+/*dx */
+ pxt_ubyte_xy, pxt_uint16_xy, pxt_uint32_xy, pxt_sint16_xy,
+ pxt_sint32_xy, pxt_real32_xy, pxtd6, pxtd7,
+ pxtd8, pxtd9, pxtda, pxtdb,
+ pxtdc, pxtdd, pxtde, pxtdf,
+/*ex */
+ pxt_ubyte_box, pxt_uint16_box, pxt_uint32_box, pxt_sint16_box,
+ pxt_sint32_box, pxt_real32_box, pxte6, pxte7,
+ pxte8, pxte9, pxtea, pxteb,
+ pxtec, pxted, pxtee, pxtef,
+/*fx */
+ pxtf0, pxtf1, pxtf2, pxtf3,
+ pxtf4, pxtf5, pxtf6, pxtf7,
+ pxt_attr_ubyte, pxt_attr_uint16, pxt_dataLength, pxt_dataLengthByte,
+ pxtfc, pxtfd, pxtfe, pxtff
+} px_tag_t;
+
+#endif /* gdevpxop_INCLUDED */
diff --git a/pstoraster/gdevvec.c b/pstoraster/gdevvec.c
new file mode 100644
index 000000000..80adbb7e8
--- /dev/null
+++ b/pstoraster/gdevvec.c
@@ -0,0 +1,905 @@
+/* Copyright (C) 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Utilities for "vector" devices */
+#include "math_.h"
+#include "memory_.h"
+#include "string_.h"
+#include "gx.h"
+#include "gp.h"
+#include "gserrors.h"
+#include "gsparam.h"
+#include "gsutil.h"
+#include "gxfixed.h"
+#include "gdevvec.h"
+#include "gscspace.h"
+#include "gxdcolor.h"
+#include "gxpaint.h" /* requires gx_path, ... */
+#include "gzpath.h"
+#include "gzcpath.h"
+
+/******
+ ****** NOTE: EVERYTHING IN THIS FILE IS SUBJECT TO CHANGE WITHOUT NOTICE.
+ ****** USE AT YOUR OWN RISK.
+ ******/
+
+/* Structure descriptors */
+public_st_device_vector();
+public_st_vector_image_enum();
+
+/* ================ Default implementations of vector procs ================ */
+
+int
+gdev_vector_setflat(gx_device_vector * vdev, floatp flatness)
+{
+ return 0;
+}
+
+int
+gdev_vector_dopath(gx_device_vector * vdev, const gx_path * ppath,
+ gx_path_type_t type)
+{
+ bool do_close = (type & gx_path_type_stroke) != 0;
+ gs_fixed_rect rect;
+ gs_point scale;
+ double x_start = 0, y_start = 0, x_prev, y_prev;
+ bool first = true;
+ gs_path_enum cenum;
+ int code;
+
+ if (gx_path_is_rectangle(ppath, &rect))
+ return (*vdev_proc(vdev, dorect)) (vdev, rect.p.x, rect.p.y, rect.q.x,
+ rect.q.y, type);
+ scale = vdev->scale;
+ code = (*vdev_proc(vdev, beginpath)) (vdev, type);
+ gx_path_enum_init(&cenum, ppath);
+ for (;;) {
+ fixed vs[6];
+ int pe_op = gx_path_enum_next(&cenum, (gs_fixed_point *) vs);
+ double x, y;
+
+ sw:switch (pe_op) {
+ case 0: /* done */
+ return (*vdev_proc(vdev, endpath)) (vdev, type);
+ case gs_pe_moveto:
+ code = (*vdev_proc(vdev, moveto))
+ (vdev, x_prev, y_prev, (x = fixed2float(vs[0]) / scale.x),
+ (y = fixed2float(vs[1]) / scale.y), type);
+ if (first)
+ x_start = x, y_start = y, first = false;
+ break;
+ case gs_pe_lineto:
+ code = (*vdev_proc(vdev, lineto))
+ (vdev, x_prev, y_prev, (x = fixed2float(vs[0]) / scale.x),
+ (y = fixed2float(vs[1]) / scale.y), type);
+ break;
+ case gs_pe_curveto:
+ code = (*vdev_proc(vdev, curveto))
+ (vdev, x_prev, y_prev,
+ fixed2float(vs[0]) / scale.x,
+ fixed2float(vs[1]) / scale.y,
+ fixed2float(vs[2]) / scale.x,
+ fixed2float(vs[3]) / scale.y,
+ (x = fixed2float(vs[4]) / scale.x),
+ (y = fixed2float(vs[5]) / scale.y),
+ type);
+ break;
+ case gs_pe_closepath:
+ x = x_start, y = y_start;
+ if (do_close) {
+ code = (*vdev_proc(vdev, closepath))
+ (vdev, x_prev, y_prev, x_start, y_start, type);
+ break;
+ }
+ pe_op = gx_path_enum_next(&cenum, (gs_fixed_point *) vs);
+ if (pe_op != 0) {
+ code = (*vdev_proc(vdev, closepath))
+ (vdev, x_prev, y_prev, x_start, y_start, type);
+ if (code < 0)
+ return code;
+ goto sw;
+ }
+ return (*vdev_proc(vdev, endpath)) (vdev, type);
+ default: /* can't happen */
+ return_error(gs_error_unknownerror);
+ }
+ if (code < 0)
+ return code;
+ x_prev = x, y_prev = y;
+ }
+}
+
+int
+gdev_vector_dorect(gx_device_vector * vdev, fixed x0, fixed y0, fixed x1,
+ fixed y1, gx_path_type_t type)
+{
+ int code = (*vdev_proc(vdev, beginpath)) (vdev, type);
+
+ if (code < 0)
+ return code;
+ code = gdev_vector_write_rectangle(vdev, x0, y0, x1, y1,
+ (type & gx_path_type_stroke) != 0,
+ gx_rect_x_first);
+ if (code < 0)
+ return code;
+ return (*vdev_proc(vdev, endpath)) (vdev, type);
+}
+
+/* ================ Utility procedures ================ */
+
+/* Recompute the cached color values. */
+private void
+gdev_vector_load_cache(gx_device_vector * vdev)
+{
+ vdev->black = gx_device_black((gx_device *)vdev);
+ vdev->white = gx_device_white((gx_device *)vdev);
+}
+
+/* Initialize the state. */
+void
+gdev_vector_init(gx_device_vector * vdev)
+{
+ gdev_vector_reset(vdev);
+ vdev->scale.x = vdev->scale.y = 1.0;
+ vdev->in_page = false;
+ gdev_vector_load_cache(vdev);
+}
+
+/* Reset the remembered graphics state. */
+void
+gdev_vector_reset(gx_device_vector * vdev)
+{
+ static const gs_imager_state state_initial =
+ {gs_imager_state_initial(1)};
+
+ vdev->state = state_initial;
+ color_unset(&vdev->fill_color);
+ color_unset(&vdev->stroke_color);
+ vdev->clip_path_id =
+ vdev->no_clip_path_id = gs_next_ids(1);
+}
+
+/* Open the output file and stream. */
+int
+gdev_vector_open_file_bbox(gx_device_vector * vdev, uint strmbuf_size,
+ bool bbox)
+{ /* Open the file as positionable if possible. */
+ int code = gx_device_open_output_file((gx_device *) vdev, vdev->fname,
+ true, true, &vdev->file);
+
+ if (code < 0)
+ return code;
+ if ((vdev->strmbuf = gs_alloc_bytes(vdev->v_memory, strmbuf_size,
+ "vector_open(strmbuf)")) == 0 ||
+ (vdev->strm = s_alloc(vdev->v_memory,
+ "vector_open(strm)")) == 0 ||
+ (bbox &&
+ (vdev->bbox_device =
+ gs_alloc_struct_immovable(vdev->v_memory,
+ gx_device_bbox, &st_device_bbox,
+ "vector_open(bbox_device)")) == 0)
+ ) {
+ if (vdev->bbox_device)
+ gs_free_object(vdev->v_memory, vdev->bbox_device,
+ "vector_open(bbox_device)");
+ vdev->bbox_device = 0;
+ if (vdev->strm)
+ gs_free_object(vdev->v_memory, vdev->strm,
+ "vector_open(strm)");
+ vdev->strm = 0;
+ if (vdev->strmbuf)
+ gs_free_object(vdev->v_memory, vdev->strmbuf,
+ "vector_open(strmbuf)");
+ vdev->strmbuf = 0;
+ fclose(vdev->file);
+ vdev->file = 0;
+ return_error(gs_error_VMerror);
+ }
+ vdev->strmbuf_size = strmbuf_size;
+ swrite_file(vdev->strm, vdev->file, vdev->strmbuf, strmbuf_size);
+ /*
+ * We don't want finalization to close the file, but we do want it
+ * to flush the stream buffer.
+ */
+ vdev->strm->procs.close = vdev->strm->procs.flush;
+ if (vdev->bbox_device) {
+ gx_device_bbox_init(vdev->bbox_device, NULL);
+ gx_device_set_resolution((gx_device *) vdev->bbox_device,
+ vdev->HWResolution[0],
+ vdev->HWResolution[1]);
+ /* Do the right thing about upright vs. inverted. */
+ /* (This is dangerous in general, since the procedure */
+ /* might reference non-standard elements.) */
+ set_dev_proc(vdev->bbox_device, get_initial_matrix,
+ dev_proc(vdev, get_initial_matrix));
+ (*dev_proc(vdev->bbox_device, open_device))
+ ((gx_device *) vdev->bbox_device);
+ }
+ return 0;
+}
+
+/* Get the current stream, calling beginpage if in_page is false. */
+stream *
+gdev_vector_stream(gx_device_vector * vdev)
+{
+ if (!vdev->in_page) {
+ (*vdev_proc(vdev, beginpage)) (vdev);
+ vdev->in_page = true;
+ }
+ return vdev->strm;
+}
+
+/* Compare two drawing colors. */
+/* Right now we don't attempt to handle non-pure colors. */
+private bool
+drawing_color_eq(const gx_drawing_color * pdc1, const gx_drawing_color * pdc2)
+{
+ return (gx_dc_is_pure(pdc1) ?
+ gx_dc_is_pure(pdc2) &&
+ gx_dc_pure_color(pdc1) == gx_dc_pure_color(pdc2) :
+ gx_dc_is_null(pdc1) ?
+ gx_dc_is_null(pdc2) :
+ false);
+}
+
+/* Update the logical operation. */
+int
+gdev_vector_update_log_op(gx_device_vector * vdev, gs_logical_operation_t lop)
+{
+ gs_logical_operation_t diff = lop ^ vdev->state.log_op;
+
+ if (diff != 0) {
+ int code = (*vdev_proc(vdev, setlogop)) (vdev, lop, diff);
+
+ if (code < 0)
+ return code;
+ vdev->state.log_op = lop;
+ }
+ return 0;
+}
+
+/* Update the fill color. */
+int
+gdev_vector_update_fill_color(gx_device_vector * vdev,
+ const gx_drawing_color * pdcolor)
+{
+ if (!drawing_color_eq(pdcolor, &vdev->fill_color)) {
+ int code = (*vdev_proc(vdev, setfillcolor)) (vdev, pdcolor);
+
+ if (code < 0)
+ return code;
+ vdev->fill_color = *pdcolor;
+ }
+ return 0;
+}
+
+/* Update the state for filling a region. */
+private int
+update_fill(gx_device_vector * vdev, const gx_drawing_color * pdcolor,
+ gs_logical_operation_t lop)
+{
+ int code = gdev_vector_update_fill_color(vdev, pdcolor);
+
+ if (code < 0)
+ return code;
+ return gdev_vector_update_log_op(vdev, lop);
+}
+
+/* Bring state up to date for filling. */
+int
+gdev_vector_prepare_fill(gx_device_vector * vdev, const gs_imager_state * pis,
+ const gx_fill_params * params, const gx_drawing_color * pdcolor)
+{
+ if (params->flatness != vdev->state.flatness) {
+ int code = (*vdev_proc(vdev, setflat)) (vdev, params->flatness);
+
+ if (code < 0)
+ return code;
+ vdev->state.flatness = params->flatness;
+ }
+ return update_fill(vdev, pdcolor, pis->log_op);
+}
+
+/* Compare two dash patterns. */
+private bool
+dash_pattern_eq(const float *stored, const gx_dash_params * set, floatp scale)
+{
+ int i;
+
+ for (i = 0; i < set->pattern_size; ++i)
+ if (stored[i] != (float)(set->pattern[i] * scale))
+ return false;
+ return true;
+}
+
+/* Bring state up to date for stroking. */
+int
+gdev_vector_prepare_stroke(gx_device_vector * vdev, const gs_imager_state * pis,
+ const gx_stroke_params * params, const gx_drawing_color * pdcolor,
+ floatp scale)
+{
+ int pattern_size = pis->line_params.dash.pattern_size;
+ float dash_offset = pis->line_params.dash.offset * scale;
+ float half_width = pis->line_params.half_width * scale;
+
+ if (pattern_size > max_dash)
+ return_error(gs_error_limitcheck);
+ if (dash_offset != vdev->state.line_params.dash.offset ||
+ pattern_size != vdev->state.line_params.dash.pattern_size ||
+ (pattern_size != 0 &&
+ !dash_pattern_eq(vdev->dash_pattern, &pis->line_params.dash,
+ scale))
+ ) {
+ float pattern[max_dash];
+ int i, code;
+
+ for (i = 0; i < pattern_size; ++i)
+ pattern[i] = pis->line_params.dash.pattern[i] * scale;
+ code = (*vdev_proc(vdev, setdash))
+ (vdev, pattern, pattern_size, dash_offset);
+ if (code < 0)
+ return code;
+ memcpy(vdev->dash_pattern, pattern, pattern_size * sizeof(float));
+
+ vdev->state.line_params.dash.pattern_size = pattern_size;
+ vdev->state.line_params.dash.offset = dash_offset;
+ }
+ if (params->flatness != vdev->state.flatness) {
+ int code = (*vdev_proc(vdev, setflat)) (vdev, params->flatness);
+
+ if (code < 0)
+ return code;
+ vdev->state.flatness = params->flatness;
+ }
+ if (half_width != vdev->state.line_params.half_width) {
+ int code = (*vdev_proc(vdev, setlinewidth))
+ (vdev, pis->line_params.half_width * 2);
+
+ if (code < 0)
+ return code;
+ vdev->state.line_params.half_width = half_width;
+ }
+ if (pis->line_params.miter_limit != vdev->state.line_params.miter_limit) {
+ int code = (*vdev_proc(vdev, setmiterlimit))
+ (vdev, pis->line_params.miter_limit);
+
+ if (code < 0)
+ return code;
+ gx_set_miter_limit(&vdev->state.line_params,
+ pis->line_params.miter_limit);
+ }
+ if (pis->line_params.cap != vdev->state.line_params.cap) {
+ int code = (*vdev_proc(vdev, setlinecap))
+ (vdev, pis->line_params.cap);
+
+ if (code < 0)
+ return code;
+ vdev->state.line_params.cap = pis->line_params.cap;
+ }
+ if (pis->line_params.join != vdev->state.line_params.join) {
+ int code = (*vdev_proc(vdev, setlinejoin))
+ (vdev, pis->line_params.join);
+
+ if (code < 0)
+ return code;
+ vdev->state.line_params.join = pis->line_params.join;
+ } {
+ int code = gdev_vector_update_log_op(vdev, pis->log_op);
+
+ if (code < 0)
+ return code;
+ }
+ if (!drawing_color_eq(pdcolor, &vdev->stroke_color)) {
+ int code = (*vdev_proc(vdev, setstrokecolor)) (vdev, pdcolor);
+
+ if (code < 0)
+ return code;
+ vdev->stroke_color = *pdcolor;
+ }
+ return 0;
+}
+
+/* Write a polygon as part of a path. */
+/* May call beginpath, moveto, lineto, closepath, endpath. */
+int
+gdev_vector_write_polygon(gx_device_vector * vdev, const gs_fixed_point * points,
+ uint count, bool close, gx_path_type_t type)
+{
+ int code = 0;
+
+ if (type != gx_path_type_none &&
+ (code = (*vdev_proc(vdev, beginpath)) (vdev, type)) < 0
+ )
+ return code;
+ if (count > 0) {
+ double x = fixed2float(points[0].x) / vdev->scale.x, y = fixed2float(points[0].y) / vdev->scale.y;
+ double x_start = x, y_start = y, x_prev, y_prev;
+ uint i;
+
+ code = (*vdev_proc(vdev, moveto))
+ (vdev, 0.0, 0.0, x, y, type);
+ if (code >= 0)
+ for (i = 1; i < count && code >= 0; ++i) {
+ x_prev = x, y_prev = y;
+ code = (*vdev_proc(vdev, lineto))
+ (vdev, x_prev, y_prev,
+ (x = fixed2float(points[i].x) / vdev->scale.x),
+ (y = fixed2float(points[i].y) / vdev->scale.y),
+ type);
+ }
+ if (code >= 0 && close)
+ code = (*vdev_proc(vdev, closepath))
+ (vdev, x, y, x_start, y_start, type);
+ }
+ return (code >= 0 && type != gx_path_type_none ?
+ (*vdev_proc(vdev, endpath)) (vdev, type) : code);
+}
+
+/* Write a rectangle as part of a path. */
+/* May call moveto, lineto, closepath. */
+int
+gdev_vector_write_rectangle(gx_device_vector * vdev, fixed x0, fixed y0,
+ fixed x1, fixed y1, bool close, gx_rect_direction_t direction)
+{
+ gs_fixed_point points[4];
+
+ points[0].x = x0, points[0].y = y0;
+ points[2].x = x1, points[2].y = y1;
+ if (direction == gx_rect_x_first)
+ points[1].x = x1, points[1].y = y0,
+ points[3].x = x0, points[3].y = y1;
+ else
+ points[1].x = x0, points[1].y = y1,
+ points[3].x = x1, points[3].y = y0;
+ return gdev_vector_write_polygon(vdev, points, 4, close,
+ gx_path_type_none);
+}
+
+/* Write a clipping path by calling the path procedures. */
+int
+gdev_vector_write_clip_path(gx_device_vector * vdev, const gx_clip_path * pcpath)
+{
+ const gx_clip_rect *prect;
+ gx_clip_rect page_rect;
+ int code;
+
+ if (pcpath == 0) { /* There's no special provision for initclip. */
+ /* Write a rectangle that covers the entire page. */
+ page_rect.xmin = page_rect.ymin = 0;
+ page_rect.xmax = vdev->width;
+ page_rect.ymax = vdev->height;
+ page_rect.next = 0;
+ prect = &page_rect;
+ } else if (pcpath->path_valid)
+ return (*vdev_proc(vdev, dopath)) (vdev, &pcpath->path,
+ gx_path_type_clip);
+ else {
+ const gx_clip_list *list = gx_cpath_list(pcpath);
+
+ prect = list->head;
+ if (prect == 0)
+ prect = &list->single;
+ }
+ /* Write out the rectangles. */
+ code = (*vdev_proc(vdev, beginpath)) (vdev, gx_path_type_clip);
+ for (; code >= 0 && prect != 0; prect = prect->next)
+ if (prect->xmax > prect->xmin && prect->ymax > prect->ymin)
+ code = gdev_vector_write_rectangle
+ (vdev, int2fixed(prect->xmin), int2fixed(prect->ymin),
+ int2fixed(prect->xmax), int2fixed(prect->ymax),
+ false, gx_rect_x_first);
+ if (code >= 0)
+ code = (*vdev_proc(vdev, endpath)) (vdev, gx_path_type_clip);
+ return code;
+}
+
+/* Update the clipping path if needed. */
+int
+gdev_vector_update_clip_path(gx_device_vector * vdev,
+ const gx_clip_path * pcpath)
+{
+ if (pcpath) {
+ if (pcpath->id != vdev->clip_path_id) {
+ int code = gdev_vector_write_clip_path(vdev, pcpath);
+
+ if (code < 0)
+ return code;
+ vdev->clip_path_id = pcpath->id;
+ }
+ } else {
+ if (vdev->clip_path_id != vdev->no_clip_path_id) {
+ int code = gdev_vector_write_clip_path(vdev, NULL);
+
+ if (code < 0)
+ return code;
+ vdev->clip_path_id = vdev->no_clip_path_id;
+ }
+ }
+ return 0;
+}
+
+/* Close the output file and stream. */
+void
+gdev_vector_close_file(gx_device_vector * vdev)
+{
+ gs_free_object(vdev->v_memory, vdev->bbox_device,
+ "vector_close(bbox_device)");
+ vdev->bbox_device = 0;
+ sclose(vdev->strm);
+ gs_free_object(vdev->v_memory, vdev->strm, "vector_close(strm)");
+ vdev->strm = 0;
+ gs_free_object(vdev->v_memory, vdev->strmbuf, "vector_close(strmbuf)");
+ vdev->strmbuf = 0;
+ fclose(vdev->file); /* we prevented sclose from doing this */
+ vdev->file = 0;
+}
+
+/* ---------------- Image enumeration ---------------- */
+
+/* Initialize for enumerating an image. */
+int
+gdev_vector_begin_image(gx_device_vector * vdev,
+ const gs_imager_state * pis, const gs_image_t * pim,
+ gs_image_format_t format, const gs_int_rect * prect,
+ const gx_drawing_color * pdcolor, const gx_clip_path * pcpath,
+ gs_memory_t * mem, const gx_image_enum_procs_t * pprocs,
+ gdev_vector_image_enum_t * pie)
+{
+ const gs_color_space *pcs = pim->ColorSpace;
+ int num_components;
+ int bits_per_pixel;
+ int code;
+
+ if (pim->ImageMask)
+ bits_per_pixel = num_components = 1;
+ else
+ num_components = gs_color_space_num_components(pcs),
+ bits_per_pixel = pim->BitsPerComponent;
+ code = gx_image_enum_common_init((gx_image_enum_common_t *) pie,
+ (const gs_image_common_t *)pim,
+ pprocs, (gx_device *) vdev,
+ bits_per_pixel, num_components,
+ format);
+ if (code < 0)
+ return code;
+ pie->bits_per_pixel = bits_per_pixel * num_components /
+ pie->num_planes;
+ pie->default_info = 0;
+ pie->bbox_info = 0;
+ if ((code = gdev_vector_update_log_op(vdev, pis->log_op)) < 0 ||
+ (code = gdev_vector_update_clip_path(vdev, pcpath)) < 0 ||
+ ((pim->ImageMask ||
+ (pim->CombineWithColor && rop3_uses_T(pis->log_op))) &&
+ (code = gdev_vector_update_fill_color(vdev, pdcolor)) < 0) ||
+ (vdev->bbox_device &&
+ (code = (*dev_proc(vdev->bbox_device, begin_image))
+ ((gx_device *) vdev->bbox_device, pis, pim, format, prect,
+ pdcolor, pcpath, mem, &pie->bbox_info)) < 0)
+ )
+ return code;
+ pie->memory = mem;
+ if (prect)
+ pie->width = prect->q.x - prect->p.x,
+ pie->height = prect->q.y - prect->p.y;
+ else
+ pie->width = pim->Width, pie->height = pim->Height;
+ pie->bits_per_row = pie->width * pie->bits_per_pixel;
+ pie->y = 0;
+ return 0;
+}
+
+/* End an image, optionally supplying any necessary blank padding rows. */
+/* Return 0 if we used the default implementation, 1 if not. */
+int
+gdev_vector_end_image(gx_device_vector * vdev,
+ gdev_vector_image_enum_t * pie, bool draw_last, gx_color_index pad)
+{
+ int code;
+
+ if (pie->default_info) {
+ code = gx_default_end_image((gx_device *) vdev, pie->default_info,
+ draw_last);
+ if (code >= 0)
+ code = 0;
+ } else { /* Fill out to the full image height. */
+ if (pie->y < pie->height && pad != gx_no_color_index) {
+ uint bytes_per_row = (pie->bits_per_row + 7) >> 3;
+ byte *row = gs_alloc_bytes(pie->memory, bytes_per_row,
+ "gdev_vector_end_image(fill)");
+
+ if (row == 0)
+ return_error(gs_error_VMerror);
+/****** FILL VALUE IS WRONG ******/
+ memset(row, (byte) pad, bytes_per_row);
+ for (; pie->y < pie->height; pie->y++)
+ gx_image_data((gx_image_enum_common_t *) pie,
+ (const byte **)&row, 0,
+ bytes_per_row, 1);
+ gs_free_object(pie->memory, row,
+ "gdev_vector_end_image(fill)");
+ }
+ code = 1;
+ }
+ if (vdev->bbox_device) {
+ int bcode = gx_image_end(pie->bbox_info, draw_last);
+
+ if (bcode < 0)
+ code = bcode;
+ }
+ gs_free_object(pie->memory, pie, "gdev_vector_end_image");
+ return code;
+}
+
+/* ================ Device procedures ================ */
+
+#define vdev ((gx_device_vector *)dev)
+
+/* Get parameters. */
+int
+gdev_vector_get_params(gx_device * dev, gs_param_list * plist)
+{
+ int code = gx_default_get_params(dev, plist);
+ int ecode;
+ gs_param_string ofns;
+
+ if (code < 0)
+ return code;
+ ofns.data = (const byte *)vdev->fname,
+ ofns.size = strlen(vdev->fname),
+ ofns.persistent = false;
+ if ((ecode = param_write_string(plist, "OutputFile", &ofns)) < 0)
+ return ecode;
+ return code;
+}
+
+/* Put parameters. */
+int
+gdev_vector_put_params(gx_device * dev, gs_param_list * plist)
+{
+ int ecode = 0;
+ int code;
+ gs_param_name param_name;
+ gs_param_string ofns;
+
+ switch (code = param_read_string(plist, (param_name = "OutputFile"), &ofns)) {
+ case 0:
+ if (ofns.size > fname_size)
+ ecode = gs_error_limitcheck;
+ else
+ break;
+ goto ofe;
+ default:
+ ecode = code;
+ ofe:param_signal_error(plist, param_name, ecode);
+ case 1:
+ ofns.data = 0;
+ break;
+ }
+
+ if (ecode < 0)
+ return ecode;
+ {
+ bool open = dev->is_open;
+
+ /* Don't let gx_default_put_params close the device. */
+ dev->is_open = false;
+ code = gx_default_put_params(dev, plist);
+ dev->is_open = open;
+ }
+ if (code < 0)
+ return code;
+
+ if (ofns.data != 0 &&
+ bytes_compare(ofns.data, ofns.size,
+ (const byte *)vdev->fname, strlen(vdev->fname))
+ ) {
+ memcpy(vdev->fname, ofns.data, ofns.size);
+ vdev->fname[ofns.size] = 0;
+ if (vdev->file != 0) {
+ gdev_vector_close_file(vdev);
+ return gdev_vector_open_file(vdev, vdev->strmbuf_size);
+ }
+ }
+ gdev_vector_load_cache(vdev); /* in case color mapping changed */
+ return 0;
+}
+
+/* ---------------- Defaults ---------------- */
+
+int
+gdev_vector_fill_rectangle(gx_device * dev, int x, int y, int w, int h,
+ gx_color_index color)
+{
+ gx_drawing_color dcolor;
+
+ /* Ignore the initial fill with white. */
+ if (!vdev->in_page && color == vdev->white)
+ return 0;
+ color_set_pure(&dcolor, color);
+ {
+ int code = update_fill(vdev, &dcolor, rop3_T);
+
+ if (code < 0)
+ return code;
+ }
+ if (vdev->bbox_device) {
+ int code = (*dev_proc(vdev->bbox_device, fill_rectangle))
+ ((gx_device *) vdev->bbox_device, x, y, w, h, color);
+
+ if (code < 0)
+ return code;
+ }
+ return (*vdev_proc(vdev, dorect)) (vdev, int2fixed(x), int2fixed(y),
+ int2fixed(x + w), int2fixed(y + h),
+ gx_path_type_fill);
+}
+
+int
+gdev_vector_fill_path(gx_device * dev, const gs_imager_state * pis,
+ gx_path * ppath, const gx_fill_params * params,
+ const gx_device_color * pdevc, const gx_clip_path * pcpath)
+{
+ int code;
+
+ if ((code = gdev_vector_prepare_fill(vdev, pis, params, pdevc)) < 0 ||
+ (code = gdev_vector_update_clip_path(vdev, pcpath)) < 0 ||
+ (vdev->bbox_device &&
+ (code = (*dev_proc(vdev->bbox_device, fill_path))
+ ((gx_device *) vdev->bbox_device, pis, ppath, params,
+ pdevc, pcpath)) < 0) ||
+ (code = (*vdev_proc(vdev, dopath))
+ (vdev, ppath,
+ (params->rule > 0 ? gx_path_type_even_odd :
+ gx_path_type_winding_number) | gx_path_type_fill)) < 0
+ )
+ return gx_default_fill_path(dev, pis, ppath, params, pdevc, pcpath);
+ return code;
+}
+
+int
+gdev_vector_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 code;
+
+/****** HANDLE SCALE ******/
+ if ((code = gdev_vector_prepare_stroke(vdev, pis, params, pdcolor,
+ dev->HWResolution[0])) < 0 ||
+ (code = gdev_vector_update_clip_path(vdev, pcpath)) < 0 ||
+ (vdev->bbox_device &&
+ (code = (*dev_proc(vdev->bbox_device, stroke_path))
+ ((gx_device *) vdev->bbox_device, pis, ppath, params,
+ pdcolor, pcpath)) < 0) ||
+ (code = (*vdev_proc(vdev, dopath))
+ (vdev, ppath, gx_path_type_stroke)) < 0
+ )
+ return gx_default_stroke_path(dev, pis, ppath, params, pdcolor, pcpath);
+ return code;
+}
+
+int
+gdev_vector_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)
+{
+ fixed xl = left->start.x;
+ fixed wl = left->end.x - xl;
+ fixed yl = left->start.y;
+ fixed hl = left->end.y - yl;
+ fixed xr = right->start.x;
+ fixed wr = right->end.x - xr;
+ fixed yr = right->start.y;
+ fixed hr = right->end.y - yr;
+ fixed x0l = xl + fixed_mult_quo(wl, ybot - yl, hl);
+ fixed x1l = xl + fixed_mult_quo(wl, ytop - yl, hl);
+ fixed x0r = xr + fixed_mult_quo(wr, ybot - yr, hr);
+ fixed x1r = xr + fixed_mult_quo(wr, ytop - yr, hr);
+
+#define y0 ybot
+#define y1 ytop
+ int code = update_fill(vdev, pdevc, lop);
+ gs_fixed_point points[4];
+
+ if (code < 0)
+ return gx_default_fill_trapezoid(dev, left, right, ybot, ytop,
+ swap_axes, pdevc, lop);
+ if (swap_axes)
+ points[0].y = x0l, points[1].y = x0r,
+ points[0].x = points[1].x = y0,
+ points[2].y = x1r, points[3].y = x1l,
+ points[2].x = points[3].x = y1;
+ else
+ points[0].x = x0l, points[1].x = x0r,
+ points[0].y = points[1].y = y0,
+ points[2].x = x1r, points[3].x = x1l,
+ points[2].y = points[3].y = y1;
+#undef y0
+#undef y1
+ if (vdev->bbox_device) {
+ int code = (*dev_proc(vdev->bbox_device, fill_trapezoid))
+ ((gx_device *) vdev->bbox_device, left, right, ybot, ytop,
+ swap_axes, pdevc, lop);
+
+ if (code < 0)
+ return code;
+ }
+ return gdev_vector_write_polygon(vdev, points, 4, true,
+ gx_path_type_fill);
+}
+
+int
+gdev_vector_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 pax = px + ax, pay = py + ay;
+ int code = update_fill(vdev, pdevc, lop);
+ gs_fixed_point points[4];
+
+ if (code < 0)
+ return gx_default_fill_parallelogram(dev, px, py, ax, ay, bx, by,
+ pdevc, lop);
+ if (vdev->bbox_device) {
+ code = (*dev_proc(vdev->bbox_device, fill_parallelogram))
+ ((gx_device *) vdev->bbox_device, px, py, ax, ay, bx, by,
+ pdevc, lop);
+ if (code < 0)
+ return code;
+ }
+ points[0].x = px, points[0].y = py;
+ points[1].x = pax, points[0].y = pay;
+ points[2].x = pax + bx, points[2].y = pay + by;
+ points[3].x = px + bx, points[3].y = py + by;
+ return gdev_vector_write_polygon(vdev, points, 4, true,
+ gx_path_type_fill);
+}
+
+int
+gdev_vector_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)
+{
+ int code = update_fill(vdev, pdevc, lop);
+ gs_fixed_point points[3];
+
+ if (code < 0)
+ return gx_default_fill_triangle(dev, px, py, ax, ay, bx, by,
+ pdevc, lop);
+ if (vdev->bbox_device) {
+ code = (*dev_proc(vdev->bbox_device, fill_triangle))
+ ((gx_device *) vdev->bbox_device, px, py, ax, ay, bx, by,
+ pdevc, lop);
+ if (code < 0)
+ return code;
+ }
+ points[0].x = px, points[0].y = py;
+ points[1].x = px + ax, points[1].y = py + ay;
+ points[2].x = px + bx, points[2].y = py + by;
+ return gdev_vector_write_polygon(vdev, points, 3, true,
+ gx_path_type_fill);
+}
+
+#undef vdev
diff --git a/pstoraster/gdevvec.h b/pstoraster/gdevvec.h
new file mode 100644
index 000000000..f4a2aebb4
--- /dev/null
+++ b/pstoraster/gdevvec.h
@@ -0,0 +1,349 @@
+/* Copyright (C) 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Common definitions for "vector" devices */
+
+#ifndef gdevvec_INCLUDED
+# define gdevvec_INCLUDED
+
+#include "gp.h" /* for gp_file_name_sizeof */
+#include "gsropt.h"
+#include "gxdevice.h"
+#include "gdevbbox.h"
+#include "gxiparam.h"
+#include "gxistate.h"
+#include "stream.h"
+
+/******
+ ****** NOTE: EVERYTHING IN THIS FILE IS SUBJECT TO CHANGE WITHOUT NOTICE.
+ ****** USE AT YOUR OWN RISK.
+ ******/
+
+/*
+ * "Vector" devices produce a stream of higher-level drawing commands rather
+ * than a raster image. (We don't like the term "vector", since the command
+ * vocabulary typically includes text and raster images as well as actual
+ * vectors, but it's widely used in the industry, and we weren't able to
+ * find one that read better.) Some examples of "vector" formats are PDF,
+ * PostScript, PCL XL, HP-GL/2 + RTL, CGM, Windows Metafile, and Macintosh
+ * PICT.
+ *
+ * This file extends the basic driver structure with elements likely to be
+ * useful to vector devices. These include:
+ *
+ * - Tracking whether any marks have been made on the page;
+ *
+ * - Keeping track of the page bounding box;
+ *
+ * - A copy of the most recently written current graphics state
+ * parameters;
+ *
+ * - An output stream (for drivers that compress or otherwise filter
+ * their output);
+ *
+ * - A vector of procedures for writing changes to the graphics state.
+ *
+ * - The ability to work with scaled output coordinate systems.
+ *
+ * We expect to add more elements and procedures as we gain more experience
+ * with this kind of driver.
+ */
+
+/* ================ Types and structures ================ */
+
+/* Define the abstract type for a vector device. */
+typedef struct gx_device_vector_s gx_device_vector;
+
+/* Define the maximum size of the output file name. */
+#define fname_size (gp_file_name_sizeof - 1)
+
+/* Define the longest dash pattern we can remember. */
+#define max_dash 11
+
+/*
+ * Define procedures for writing common output elements. Not all devices
+ * will support all of these elements. Note that these procedures normally
+ * only write out commands, and don't update the driver state itself. All
+ * of them are optional, called only as indicated under the utility
+ * procedures below.
+ */
+typedef enum {
+ gx_path_type_none = 0,
+ /*
+ * All combinations of flags are legal. Multiple commands are
+ * executed in the order fill, stroke, clip.
+ */
+ gx_path_type_fill = 1,
+ gx_path_type_stroke = 2,
+ gx_path_type_clip = 4,
+ gx_path_type_winding_number = 0,
+ gx_path_type_even_odd = 8,
+ gx_path_type_rule = gx_path_type_winding_number | gx_path_type_even_odd
+} gx_path_type_t;
+typedef enum {
+ gx_rect_x_first,
+ gx_rect_y_first
+} gx_rect_direction_t;
+typedef struct gx_device_vector_procs_s {
+ /* Page management */
+ int (*beginpage) (P1(gx_device_vector * vdev));
+ /* Imager state */
+ int (*setlinewidth) (P2(gx_device_vector * vdev, floatp width));
+ int (*setlinecap) (P2(gx_device_vector * vdev, gs_line_cap cap));
+ int (*setlinejoin) (P2(gx_device_vector * vdev, gs_line_join join));
+ int (*setmiterlimit) (P2(gx_device_vector * vdev, floatp limit));
+ int (*setdash) (P4(gx_device_vector * vdev, const float *pattern,
+ uint count, floatp offset));
+ int (*setflat) (P2(gx_device_vector * vdev, floatp flatness));
+ int (*setlogop) (P3(gx_device_vector * vdev, gs_logical_operation_t lop,
+ gs_logical_operation_t diff));
+ /* Other state */
+ int (*setfillcolor) (P2(gx_device_vector * vdev, const gx_drawing_color * pdc));
+ int (*setstrokecolor) (P2(gx_device_vector * vdev, const gx_drawing_color * pdc));
+ /* Paths */
+ /* dopath and dorect are normally defaulted */
+ int (*dopath) (P3(gx_device_vector * vdev, const gx_path * ppath,
+ gx_path_type_t type));
+ int (*dorect) (P6(gx_device_vector * vdev, fixed x0, fixed y0, fixed x1,
+ fixed y1, gx_path_type_t type));
+ int (*beginpath) (P2(gx_device_vector * vdev, gx_path_type_t type));
+ int (*moveto) (P6(gx_device_vector * vdev, floatp x0, floatp y0,
+ floatp x, floatp y, gx_path_type_t type));
+ int (*lineto) (P6(gx_device_vector * vdev, floatp x0, floatp y0,
+ floatp x, floatp y, gx_path_type_t type));
+ int (*curveto) (P10(gx_device_vector * vdev, floatp x0, floatp y0,
+ floatp x1, floatp y1, floatp x2, floatp y2,
+ floatp x3, floatp y3, gx_path_type_t type));
+ int (*closepath) (P6(gx_device_vector * vdev, floatp x0, floatp y0,
+ floatp x_start, floatp y_start, gx_path_type_t type));
+ int (*endpath) (P2(gx_device_vector * vdev, gx_path_type_t type));
+} gx_device_vector_procs;
+
+/* Default implementations of procedures */
+/* setflat does nothing */
+int gdev_vector_setflat(P2(gx_device_vector * vdev, floatp flatness));
+
+/* dopath may call dorect, beginpath, moveto/lineto/curveto/closepath, */
+/* endpath */
+int gdev_vector_dopath(P3(gx_device_vector * vdev, const gx_path * ppath,
+ gx_path_type_t type));
+
+/* dorect may call beginpath, moveto, lineto, closepath */
+int gdev_vector_dorect(P6(gx_device_vector * vdev, fixed x0, fixed y0,
+ fixed x1, fixed y1, gx_path_type_t type));
+
+/* Finally, define the extended device structure. */
+#define gx_device_vector_common\
+ gx_device_common;\
+ gs_memory_t *v_memory;\
+ /* Output element writing procedures */\
+ const gx_device_vector_procs *vec_procs;\
+ /* Output file */\
+ char fname[fname_size + 1];\
+ FILE *file;\
+ stream *strm;\
+ byte *strmbuf;\
+ uint strmbuf_size;\
+ /* Graphics state */\
+ gs_imager_state state;\
+ float dash_pattern[max_dash];\
+ gx_drawing_color fill_color, stroke_color;\
+ gs_id no_clip_path_id; /* indicates no clipping */\
+ gs_id clip_path_id;\
+ /* Other state */\
+ gs_point scale; /* device coords / scale => output coords */\
+ bool in_page; /* true if any marks on this page */\
+ gx_device_bbox *bbox_device; /* for tracking bounding box */\
+ /* Cached values */\
+ gx_color_index black, white
+#define vdev_proc(vdev, p) ((vdev)->vec_procs->p)
+
+#define vector_initial_values\
+ 0, /* v_memory */\
+ 0, /* vec_procs */\
+ { 0 }, /* fname */\
+ 0, /* file */\
+ 0, /* strm */\
+ 0, /* strmbuf */\
+ 0, /* strmbuf_size */\
+ { 0 }, /* state */\
+ { 0 }, /* dash_pattern */\
+ { 0 }, /* fill_color ****** WRONG ****** */\
+ { 0 }, /* stroke_color ****** WRONG ****** */\
+ gs_no_id, /* clip_path_id */\
+ gs_no_id, /* no_clip_path_id */\
+ { X_DPI/72.0, Y_DPI/72.0 }, /* scale */\
+ 0/*false*/, /* in_page */\
+ 0, /* bbox_device */\
+ gx_no_color_index, /* black */\
+ gx_no_color_index /* white */
+
+struct gx_device_vector_s {
+ gx_device_vector_common;
+};
+
+/* st_device_vector is never instantiated per se, but we still need to */
+/* extern its descriptor for the sake of subclasses. */
+extern_st(st_device_vector);
+#define public_st_device_vector() /* in gdevvec.c */\
+ gs_public_st_suffix_add3_final(st_device_vector, gx_device_vector,\
+ "gx_device_vector", device_vector_enum_ptrs,\
+ device_vector_reloc_ptrs, gx_device_finalize, st_device, strm, strmbuf,\
+ bbox_device)
+#define st_device_vector_max_ptrs (st_device_max_ptrs + 3)
+
+/* ================ Utility procedures ================ */
+
+/* Initialize the state. */
+void gdev_vector_init(P1(gx_device_vector * vdev));
+
+/* Reset the remembered graphics state. */
+void gdev_vector_reset(P1(gx_device_vector * vdev));
+
+/* Open the output file and stream, with optional bbox tracking. */
+int gdev_vector_open_file_bbox(P3(gx_device_vector * vdev, uint strmbuf_size,
+ bool bbox));
+
+#define gdev_vector_open_file(vdev, strmbuf_size)\
+ gdev_vector_open_file_bbox(vdev, strmbuf_size, false)
+
+/* Get the current stream, calling beginpage if in_page is false. */
+stream *gdev_vector_stream(P1(gx_device_vector * vdev));
+
+/* Bring the logical operation up to date. */
+/* May call setlogop. */
+int gdev_vector_update_log_op(P2(gx_device_vector * vdev,
+ gs_logical_operation_t lop));
+
+/* Bring the fill color up to date. */
+/* May call setfillcolor. */
+int gdev_vector_update_fill_color(P2(gx_device_vector * vdev,
+ const gx_drawing_color * pdcolor));
+
+/* Bring state up to date for filling. */
+/* May call setflat, setfillcolor, setlogop. */
+int gdev_vector_prepare_fill(P4(gx_device_vector * vdev,
+ const gs_imager_state * pis,
+ const gx_fill_params * params,
+ const gx_drawing_color * pdcolor));
+
+/* Bring state up to date for stroking. Note that we pass the scale */
+/* for the line width and dash offset explicitly. */
+/* May call setlinewidth, setlinecap, setlinejoin, setmiterlimit, */
+/* setdash, setflat, setstrokecolor, setlogop. */
+int gdev_vector_prepare_stroke(P5(gx_device_vector * vdev,
+ const gs_imager_state * pis,
+ const gx_stroke_params * params,
+ const gx_drawing_color * pdcolor,
+ floatp scale));
+
+/* Write a polygon as part of a path (type = gx_path_type_none) */
+/* or as a path. */
+/* May call moveto, lineto, closepath (if close); */
+/* may call beginpath & endpath if type != none. */
+int gdev_vector_write_polygon(P5(gx_device_vector * vdev,
+ const gs_fixed_point * points, uint count,
+ bool close, gx_path_type_t type));
+
+/* Write a rectangle. This is just a special case of write_polygon. */
+int gdev_vector_write_rectangle(P7(gx_device_vector * vdev,
+ fixed x0, fixed y0, fixed x1, fixed y1,
+ bool close, gx_rect_direction_t dir));
+
+/* Write a clipping path by calling the path procedures. */
+/* May call the same procedures as writepath. */
+int gdev_vector_write_clip_path(P2(gx_device_vector * vdev,
+ const gx_clip_path * pcpath));
+
+/* Bring the clipping state up to date. */
+/* May call write_rectangle (q.v.), write_clip_path (q.v.). */
+int gdev_vector_update_clip_path(P2(gx_device_vector * vdev,
+ const gx_clip_path * pcpath));
+
+/* Close the output file and stream. */
+void gdev_vector_close_file(P1(gx_device_vector * vdev));
+
+/* ---------------- Image enumeration ---------------- */
+
+/* Define a common set of state parameters for enumerating images. */
+#define gdev_vector_image_enum_common\
+ gx_image_enum_common;\
+ /* Set by begin_image */\
+ gs_memory_t *memory; /* from begin_image */\
+ gx_image_enum_common_t *default_info; /* non-0 iff using default implementation */\
+ gx_image_enum_common_t *bbox_info; /* non-0 iff passing image data to bbox dev */\
+ int width, height;\
+ int bits_per_pixel; /* (per plane) */\
+ uint bits_per_row; /* (per plane) */\
+ /* Updated dynamically by image_data */\
+ int y /* 0 <= y < height */
+typedef struct gdev_vector_image_enum_s {
+ gdev_vector_image_enum_common;
+} gdev_vector_image_enum_t;
+
+extern_st(st_vector_image_enum);
+#define public_st_vector_image_enum() /* in gdevvec.c */\
+ gs_public_st_ptrs2(st_vector_image_enum, gdev_vector_image_enum_t,\
+ "gdev_vector_image_enum_t", vector_image_enum_enum_ptrs,\
+ vector_image_enum_reloc_ptrs, default_info, bbox_info)
+
+/*
+ * Initialize for enumerating an image. Note that the last argument is an
+ * already-allocated enumerator, not a pointer to the place to store the
+ * enumerator.
+ */
+int gdev_vector_begin_image(P10(gx_device_vector * vdev,
+ const gs_imager_state * pis, const gs_image_t * pim,
+ gs_image_format_t format, const gs_int_rect * prect,
+ const gx_drawing_color * pdcolor, const gx_clip_path * pcpath,
+ gs_memory_t * mem, const gx_image_enum_procs_t * pprocs,
+ gdev_vector_image_enum_t * pie));
+
+/* End an image, optionally supplying any necessary blank padding rows. */
+/* Return 0 if we used the default implementation, 1 if not. */
+int gdev_vector_end_image(P4(gx_device_vector * vdev,
+ gdev_vector_image_enum_t * pie, bool draw_last, gx_color_index pad));
+
+/* ================ Device procedures ================ */
+
+/* Redefine get/put_params to handle OutputFile. */
+dev_proc_put_params(gdev_vector_put_params);
+dev_proc_get_params(gdev_vector_get_params);
+
+/* ---------------- Defaults ---------------- */
+
+/* fill_rectangle may call setfillcolor, dorect. */
+dev_proc_fill_rectangle(gdev_vector_fill_rectangle);
+/* fill_path may call prepare_fill, writepath, write_clip_path. */
+dev_proc_fill_path(gdev_vector_fill_path);
+/* stroke_path may call prepare_stroke, write_path, write_clip_path. */
+dev_proc_stroke_path(gdev_vector_stroke_path);
+/* fill_trapezoid, fill_parallelogram, and fill_triangle may call */
+/* setfillcolor, setlogop, beginpath, moveto, lineto, endpath. */
+dev_proc_fill_trapezoid(gdev_vector_fill_trapezoid);
+dev_proc_fill_parallelogram(gdev_vector_fill_parallelogram);
+dev_proc_fill_triangle(gdev_vector_fill_triangle);
+
+#endif /* gdevvec_INCLUDED */
diff --git a/pstoraster/genarch.c b/pstoraster/genarch.c
new file mode 100644
index 000000000..a8f308bff
--- /dev/null
+++ b/pstoraster/genarch.c
@@ -0,0 +1,163 @@
+/* Copyright (C) 1989, 1995, 1996, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Generate a header file (arch.h) with parameters */
+/* reflecting the machine architecture and compiler characteristics. */
+
+#include "stdpre.h"
+#include <stdio.h>
+
+/* We should write the result on stdout, but the original Turbo C 'make' */
+/* can't handle output redirection (sigh). */
+
+private void
+section(FILE * f, char *str)
+{
+ fprintf(f, "\n\t /* ---------------- %s ---------------- */\n\n", str);
+}
+
+int
+main(int argc, char *argv[])
+{
+ char *fname = argv[1];
+ long one = 1;
+ char *ffs = "ffffffffffffffff"; /* 8 bytes */
+ int ffs_strlen = strlen(ffs);
+ 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;
+ 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");
+
+ section(f, "Scalar alignments");
+
+#define OFFSET_IN(s, e) (int)((char *)&s.e - (char *)&s)
+ fprintf(f, "#define arch_align_short_mod %d\n", OFFSET_IN(ss, s));
+ fprintf(f, "#define arch_align_int_mod %d\n", OFFSET_IN(si, i));
+ fprintf(f, "#define arch_align_long_mod %d\n", OFFSET_IN(sl, l));
+ fprintf(f, "#define arch_align_ptr_mod %d\n", OFFSET_IN(sp, p));
+ fprintf(f, "#define arch_align_float_mod %d\n", OFFSET_IN(sf, f));
+ fprintf(f, "#define arch_align_double_mod %d\n", OFFSET_IN(sd, d));
+#undef OFFSET_IN
+
+ section(f, "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_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));
+
+ section(f, "Unsigned max values");
+
+#define PRINT_MAX(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)
+ PRINT_MAX("uchar", unsigned char, "unsigned char", "");
+ PRINT_MAX("ushort", unsigned short, "unsigned short", "");
+ PRINT_MAX("uint", unsigned int, "unsigned int", "");
+ PRINT_MAX("ulong", unsigned long, "unsigned long", "L");
+
+#undef PRINT_MAX
+
+ section(f, "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..5df931e78
--- /dev/null
+++ b/pstoraster/ghost.h
@@ -0,0 +1,34 @@
+/* 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 supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Common definitions for interpreter */
+
+#ifndef ghost_INCLUDED
+# define ghost_INCLUDED
+
+#include "gx.h"
+#include "iref.h"
+
+#endif /* ghost_INCLUDED */
diff --git a/pstoraster/gp.h b/pstoraster/gp.h
new file mode 100644
index 000000000..39aa8c4aa
--- /dev/null
+++ b/pstoraster/gp.h
@@ -0,0 +1,233 @@
+/* Copyright (C) 1991, 1995, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Interface to platform-specific routines */
+/* Requires gsmemory.h, gstypes.h */
+
+#ifndef gp_INCLUDED
+# define gp_INCLUDED
+
+/*
+ * This file defines the interface to ***ALL*** platform-specific routines,
+ * with the exception of the thread/synchronization interface (gpsync.h).
+ * The routines are implemented in a gp_*.c file specific to each platform.
+ * We try very hard to keep this list short!
+ */
+/*
+ * gp_getenv is declared in a separate file, because a few places need it
+ * and don't want to include any of the other gs definitions.
+ */
+#include "gpgetenv.h"
+
+/* ------ 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());
+
+/* ------ File naming and accessing ------ */
+
+/*
+ * Define the maximum size of a file name returned by gp_open_scratch_file
+ * or gp_open_printer. (This should really be passed as an additional
+ * parameter, but it would break too many clients to make this change now.)
+ * Note that this is the size of the buffer, not the maximum number of
+ * characters: the latter is one less, because of the terminating \0.
+ */
+#define gp_file_name_sizeof 128
+
+/* 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[gp_file_name_sizeof],
+ 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));
+
+/* Force given file into binary mode (no eol translations, etc) */
+/* if 2nd param true, text mode if 2nd param false */
+bool gp_setmode_binary(P2(FILE * pfile, bool 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));
+
+/* ------ 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.
+ *
+ * Note that if the file name is null (0-length), it may be replaced with
+ * the name of a scratch file.
+ */
+FILE *gp_open_printer(P2(char fname[gp_file_name_sizeof], int binary_mode));
+
+/* Close the connection to the printer. */
+void gp_close_printer(P2(FILE * pfile, const char *fname));
+
+/* ------ 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));
+
+#endif /* gp_INCLUDED */
diff --git a/pstoraster/gp_getnv.c b/pstoraster/gp_getnv.c
new file mode 100644
index 000000000..fbf997f32
--- /dev/null
+++ b/pstoraster/gp_getnv.c
@@ -0,0 +1,60 @@
+/* Copyright (C) 1997 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Standard implementation of gp_getenv */
+#include "stdio_.h"
+#include "string_.h"
+#include "gsmemory.h"
+#include "gstypes.h"
+#include "gp.h"
+
+/* Import the C getenv function. */
+extern char *getenv(P1(const char *));
+
+/* Get the value of an environment variable. See gp.h for details. */
+int
+gp_getenv(const char *key, char *ptr, int *plen)
+{
+ const char *str = getenv(key);
+
+ if (str) {
+ int len = strlen(str);
+
+ if (len < *plen) {
+ /* string fits */
+ strcpy(ptr, str);
+ *plen = len + 1;
+ return 0;
+ }
+ /* string doesn't fit */
+ *plen = len + 1;
+ return -1;
+ }
+ /* missing key */
+ if (*plen > 0)
+ *ptr = 0;
+ *plen = 1;
+ return 1;
+}
diff --git a/pstoraster/gp_nofb.c b/pstoraster/gp_nofb.c
new file mode 100644
index 000000000..49c50ae8a
--- /dev/null
+++ b/pstoraster/gp_nofb.c
@@ -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 supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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_nsync.c b/pstoraster/gp_nsync.c
new file mode 100644
index 000000000..0163cf5a2
--- /dev/null
+++ b/pstoraster/gp_nsync.c
@@ -0,0 +1,120 @@
+/* Copyright (C) 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Dummy thread / semaphore / monitor implementation */
+#include "std.h"
+#include "gserror.h"
+#include "gserrors.h"
+#include "gpsync.h"
+
+/* ------- Synchronization primitives -------- */
+
+/* Semaphores */
+
+uint
+gp_semaphore_sizeof(void)
+{
+ return sizeof(gp_semaphore);
+}
+
+int
+gp_semaphore_open(gp_semaphore * sema)
+{
+ if (sema)
+ *(int *)sema = 0;
+ return 0;
+}
+
+int
+gp_semaphore_close(gp_semaphore * sema)
+{
+ return 0;
+}
+
+int
+gp_semaphore_wait(gp_semaphore * sema)
+{
+ if (*(int *)sema == 0)
+ return_error(gs_error_unknownerror);
+ --(*(int *)sema);
+ return 0;
+}
+
+int
+gp_semaphore_signal(gp_semaphore * sema)
+{
+ ++(*(int *)sema);
+ return 0;
+}
+
+/* Monitors */
+
+uint
+gp_monitor_sizeof(void)
+{
+ return sizeof(gp_monitor);
+}
+
+int
+gp_monitor_open(gp_monitor * mon)
+{
+ if (mon)
+ mon->dummy_ = 0;
+ return 0;
+}
+
+int
+gp_monitor_close(gp_monitor * mon)
+{
+ return 0;
+}
+
+int
+gp_monitor_enter(gp_monitor * mon)
+{
+ if (mon->dummy_ != 0)
+ return_error(gs_error_unknownerror);
+ mon->dummy_ = &mon;
+ return 0;
+}
+
+int
+gp_monitor_leave(gp_monitor * mon)
+{
+ if (mon->dummy_ != &mon)
+ return_error(gs_error_unknownerror);
+ mon->dummy_ = 0;
+ return 0;
+}
+
+/* Thread creation */
+
+int
+gp_create_thread(gp_thread_creation_callback_t proc, void *proc_data)
+{
+ /* Just call the procedure now. */
+ (*proc)(proc_data);
+ return 0;
+}
diff --git a/pstoraster/gp_unifn.c b/pstoraster/gp_unifn.c
new file mode 100644
index 000000000..aa128faab
--- /dev/null
+++ b/pstoraster/gp_unifn.c
@@ -0,0 +1,61 @@
+/* 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 supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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..7d6bf0ba8
--- /dev/null
+++ b/pstoraster/gp_unifs.c
@@ -0,0 +1,440 @@
+/*Copyright 1993-2000 by Easy Software Products.
+ Copyright 1993, 1995, 1996, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* "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 *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[gp_file_name_sizeof],
+ const char *mode)
+{ /* The -8 is for XXXXXX plus a possible final / and -. */
+ int len = gp_file_name_sizeof - strlen(prefix) - 8;
+
+ /*
+ * MRS - Hello? TEMP is a DOS thing, TMPDIR is the UNIX thing.
+ * Also, we should default to /var/tmp, since the root
+ * partition is often small.
+ */
+
+ if (gp_getenv("TMPDIR", fname, &len) != 0)
+ strcpy(fname, "/var/tmp/");
+ else {
+ 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);
+}
+
+/* Set a file into binary or text mode. */
+int
+gp_setmode_binary(FILE * pfile, bool mode)
+{
+ return 0; /* Noop under Unix */
+}
+
+/* ------ 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')) {
+ dlputs("[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)
+{
+ const 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..e705af635
--- /dev/null
+++ b/pstoraster/gp_unix.c
@@ -0,0 +1,173 @@
+/* Copyright (C) 1989, 1995, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Unix-specific routines for Ghostscript */
+#include "pipe_.h"
+#include "string_.h"
+#include "time_.h"
+#include "gx.h"
+#include "gsexit.h"
+#include "gp.h"
+
+/*
+ * 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;
+ const long 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[gp_file_name_sizeof], 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..aaaf2b734
--- /dev/null
+++ b/pstoraster/gpcheck.h
@@ -0,0 +1,65 @@
+/* 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 supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Interrupt check interface */
+
+#ifndef gpcheck_INCLUDED
+# define gpcheck_INCLUDED
+
+/*
+ * 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
+
+#endif /* gpcheck_INCLUDED */
diff --git a/pstoraster/gpgetenv.h b/pstoraster/gpgetenv.h
new file mode 100644
index 000000000..61b75c388
--- /dev/null
+++ b/pstoraster/gpgetenv.h
@@ -0,0 +1,50 @@
+/* Copyright (C) 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Interface to platform-specific getenv routine */
+
+#ifndef gpgetenv_INCLUDED
+# define gpgetenv_INCLUDED
+
+/*
+ * Get a value from the environment (getenv).
+ *
+ * If the key is missing, set *ptr = 0 (if *plen > 0), set *plen = 1,
+ * and return 1.
+ *
+ * If the key is present and the length len of the value (not counting
+ * the terminating \0) is less than *plen, copy the value to ptr, set
+ * *plen = len + 1, and return 0.
+ *
+ * If the key is present and len >= *plen, set *plen = len + 1,
+ * don't store anything at ptr, and return -1.
+ *
+ * Note that *plen is the size of the buffer, not the length of the string:
+ * because of the terminating \0, the maximum string length is 1 less than
+ * the size of the buffer.
+ */
+int gp_getenv(P3(const char *key, char *ptr, int *plen));
+
+#endif /* gpgetenv_INCLUDED */
diff --git a/pstoraster/gpsync.h b/pstoraster/gpsync.h
new file mode 100644
index 000000000..5a16f28d1
--- /dev/null
+++ b/pstoraster/gpsync.h
@@ -0,0 +1,81 @@
+/* Copyright (C) 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Interface to platform-dependent synchronization primitives */
+
+#if !defined(gpsync_INCLUDED)
+ #define gpsync_INCLUDED
+
+/* Initial version 4/1/98 by John Desrosiers (soho@crl.com). */
+/* 8/9/98 L. Peter Deutsch (ghost@aladdin.com) Changed ...sizeof to
+ procedures, added some comments. */
+
+/* -------- Synchronization primitives ------- */
+
+/*
+ * Semaphores support wait/signal semantics: a wait operation will allow
+ * control to proceed iff the number of signals since semaphore creation
+ * is greater than the number of waits.
+ */
+typedef struct {
+ void *dummy_;
+} gp_semaphore;
+
+uint gp_semaphore_sizeof(P0());
+/*
+ * Hack: gp_semaphore_open(0) succeeds iff it's OK for the memory manager
+ * to move a gp_semaphore in memory.
+ */
+int gp_semaphore_open(P1(gp_semaphore * sema));
+int gp_semaphore_close(P1(gp_semaphore * sema));
+int gp_semaphore_wait(P1(gp_semaphore * sema));
+int gp_semaphore_signal(P1(gp_semaphore * sema));
+
+/*
+ * Monitors support enter/leave semantics: at most one thread can have
+ * entered and not yet left a given monitor.
+ */
+typedef struct {
+ void *dummy_;
+} gp_monitor;
+
+uint gp_monitor_sizeof(P0());
+/*
+ * Hack: gp_monitor_open(0) succeeds iff it's OK for the memory manager
+ * to move a gp_monitor in memory.
+ */
+int gp_monitor_open(P1(gp_monitor * mon));
+int gp_monitor_close(P1(gp_monitor * mon));
+int gp_monitor_enter(P1(gp_monitor * mon));
+int gp_monitor_leave(P1(gp_monitor * mon));
+
+/*
+ * A new thread starts by calling a procedure, passing it a void * that
+ * allows it to gain access to whatever data it needs.
+ */
+typedef void (*gp_thread_creation_callback_t) (P1(void *));
+int gp_create_thread(P2(gp_thread_creation_callback_t, void *));
+
+#endif /* !defined(gpsync_INCLUDED) */
diff --git a/pstoraster/gs_btokn.ps b/pstoraster/gs_btokn.ps
new file mode 100644
index 000000000..7ad61f8a5
--- /dev/null
+++ b/pstoraster/gs_btokn.ps
@@ -0,0 +1,313 @@
+% Copyright 1993-2000 by Easy Software Products.
+% Copyright 1994, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+%
+% This file is part of GNU Ghostscript.
+%
+% GNU Ghostscript is distributed in the hope that it will be useful, but
+% WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+% to anyone for the consequences of using it or for whether it serves any
+% particular purpose or works at all, unless he says so in writing. Refer
+% to the GNU General Public License for full details.
+%
+% Everyone is granted permission to copy, modify and redistribute GNU
+% Ghostscript, but only under the conditions described in the GNU General
+% Public License. A copy of this license is supposed to have been given
+% to you along with GNU Ghostscript so you can know your rights and
+% responsibilities. It should be in a file named COPYING. Among other
+% things, the copyright notice and this notice must be preserved on all
+% copies.
+%
+% Aladdin Enterprises supports the work of the GNU Project, but is not
+% affiliated with the Free Software Foundation or the GNU Project. GNU
+% Ghostscript, as distributed by Aladdin Enterprises, does not require any
+% GNU software to build or run it.
+
+% $Id: gs_btokn.ps 956 2000-03-08 23:15:43Z mike $
+% 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
+dup /SystemNames exch def .installsystemnames
+
+% 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
+
+/.bosheader { % <top_length> <total_length> <string8> .bosheader
+ % <string4|8>
+ dup 0 currentobjectformat 127 add put % object format => BOS tag
+ 2 index 255 le 2 index 65531 le and {
+ % Use the short header format: tag toplen(1) totlen(2)
+ exch 4 add exch
+ 0 4 getinterval
+ dup 1 5 -1 roll put
+ } {
+ % Use the long header format: tag 0(1) toplen(2) totlen(4)
+ exch 8 add exch
+ 0 0 4 2 roll .bosobject exch pop exch pop % store with byte swapping
+ } ifelse % Stack: shortlen str
+ exch dup -8 bitshift exch 255 and % str hibyte lobyte
+ currentobjectformat 1 and 0 eq { % lsb first
+ exch
+ } if
+ 2 index 3 3 -1 roll put
+ 1 index 2 3 -1 roll put
+} .bind 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
+
+%%%% MRS - stderr instead of stdout, which is used for output...
+/printobject { % <obj> <tag> printobject -
+ (%stderr) (w) file 2 index 2 index writeobject pop pop
+} odef
+/writeobject { % <file> <obj> <tag> writeobject -
+ 3 copy exch
+ % We must allocate the array in local VM
+ % to avoid a possible invalidaccess.
+ .currentglobal false .setglobal exch 1 array astore exch .setglobal
+ .writeobjects pop pop pop
+} odef
+
+% Implement binary error message output.
+ /.printerror
+ { $error /binary get .languagelevel 2 ge 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..5d4ada0e4
--- /dev/null
+++ b/pstoraster/gs_ccfnt.ps
@@ -0,0 +1,100 @@
+% 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 supports the work of the GNU Project, but is not
+% affiliated with the Free Software Foundation or the GNU Project. GNU
+% Ghostscript, as distributed by Aladdin Enterprises, does not require any
+% GNU software to build or run it.
+
+% $Id: gs_ccfnt.ps 956 2000-03-08 23:15:43Z mike $
+% 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_cff.ps b/pstoraster/gs_cff.ps
new file mode 100644
index 000000000..6ac0ea3e8
--- /dev/null
+++ b/pstoraster/gs_cff.ps
@@ -0,0 +1,614 @@
+% Copyright (C) 1997 Aladdin Enterprises. All rights reserved.
+%
+% This file is part of GNU Ghostscript.
+%
+% GNU Ghostscript is distributed in the hope that it will be useful, but
+% WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+% to anyone for the consequences of using it or for whether it serves any
+% particular purpose or works at all, unless he says so in writing. Refer
+% to the GNU General Public License for full details.
+%
+% Everyone is granted permission to copy, modify and redistribute GNU
+% Ghostscript, but only under the conditions described in the GNU General
+% Public License. A copy of this license is supposed to have been given
+% to you along with GNU Ghostscript so you can know your rights and
+% responsibilities. It should be in a file named COPYING. Among other
+% things, the copyright notice and this notice must be preserved on all
+% copies.
+%
+% Aladdin Enterprises supports the work of the GNU Project, but is not
+% affiliated with the Free Software Foundation or the GNU Project. GNU
+% Ghostscript, as distributed by Aladdin Enterprises, does not require any
+% GNU software to build or run it.
+
+% $Id: gs_cff.ps 956 2000-03-08 23:15:43Z mike $
+% Loader for CFF (compressed) fonts.
+% The following are not implemented yet:
+% Deleted entries in the Name Index
+% Embedded PostScript
+% Multiple Master fonts
+% CIDFonts
+% Chameleon fonts
+% Synthetic fonts
+% Also, Type 2 charstrings are converted into Type 1 fonts with
+% CharstringType = 2, which may or may not be supported.
+
+30 dict begin
+
+% ---------------- Standard strings (actually names) ---------------- %
+
+/StandardStrings mark
+% 0
+ /.notdef /space /exclam /quotedbl /numbersign
+ /dollar /percent /ampersand /quoteright /parenleft
+ /parenright /asterisk /plus /comma /hyphen
+ /period /slash /zero /one /two
+ /three /four /five /six /seven
+ /eight /nine /colon /semicolon /less
+ /equal /greater /question /at /A
+ /B /C /D /E /F
+ /G /H /I /J /K
+ /L /M /N /O /P
+% 50
+ /Q /R /S /T /U
+ /V /W /X /Y /Z
+ /bracketleft /backslash /bracketright /asciicircum /underscore
+ /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 /exclamdown /cent /sterling /fraction
+% 100
+ /yen /florin /section /currency /quotesingle
+ /quotedblleft /guillemotleft /guilsinglleft /guilsinglright /fi
+ /fl /endash /dagger /daggerdbl /periodcentered
+ /paragraph /bullet /quotesinglbase /quotedblbase /quotedblright
+ /guillemotright /ellipsis /perthousand /questiondown /grave
+ /acute /circumflex /tilde /macron /breve
+ /dotaccent /dieresis /ring /cedilla /hungarumlaut
+ /ogonek /caron /emdash /AE /ordfeminine
+ /Lslash /Oslash /OE /ordmasculine /ae
+ /dotlessi /lslash /oslash /oe /germandbls
+% 150
+ /onesuperior /logicalnot /mu /trademark /Eth
+ /onehalf /plusminus /Thorn /onequarter /divide
+ /brokenbar /degree /thorn /threequarters /twosuperior
+ /registered /minus /eth /multiply /threesuperior
+ /copyright /Aacute /Acircumflex /Adieresis /Agrave
+ /Aring /Atilde /Ccedilla /Eacute /Ecircumflex
+ /Edieresis /Egrave /Iacute /Icircumflex /Idieresis
+ /Igrave /Ntilde /Oacute /Ocircumflex /Odieresis
+ /Ograve /Otilde /Scaron /Uacute /Ucircumflex
+ /Udieresis /Ugrave /Yacute /Ydieresis /Zcaron
+% 200
+ /aacute /acircumflex /adieresis /agrave /aring
+ /atilde /ccedilla /eacute /ecircumflex /edieresis
+ /egrave /iacute /icircumflex /idieresis /igrave
+ /ntilde /oacute /ocircumflex /odieresis /ograve
+ /otilde /scaron /uacute /ucircumflex /udieresis
+ /ugrave /yacute /ydieresis /zcaron /exclamsmall
+ /Hungarumlautsmall /dollaroldstyle /dollarsuperior /ampersandsmall /Acutesmall
+ /parenleftsuperior /parenrightsuperior /twodotenleader /onedotenleader /zerooldstyle
+ /oneoldstyle /twooldstyle /threeoldstyle /fouroldstyle /fiveoldstyle
+ /sixoldstyle /sevenoldstyle /eightoldstyle /nineoldstyle /commasuperior
+% 250
+ /threequartersemdash /periodsuperior /questionsmall /asuperior /bsuperior
+ /centsuperior /dsuperior /esuperior /isuperior /lsuperior
+ /msuperior /nsuperior /osuperior /rsuperior /ssuperior
+ /tsuperior /ff /ffi /ffl /parenleftinferior
+ /parenrightinferior /Circumflexsmall /hyphensuperior /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
+% 300
+ /colonmonetary /onefitted /rupiah /Tildesmall /exclamdownsmall
+ /centoldstyle /Lslashsmall /Scaronsmall /Zcaronsmall /Dieresissmall
+ /Brevesmall /Caronsmall /Dotaccentsmall /Macronsmall /figuredash
+ /hypheninferior /Ogoneksmall /Ringsmall /Cedillasmall /questiondownsmall
+ /oneeighth /threeeighths /fiveeighths /seveneighths /onethird
+ /twothirds /zerosuperior /foursuperior /fivesuperior /sixsuperior
+ /sevensuperior /eightsuperior /ninesuperior /zeroinferior /oneinferior
+ /twoinferior /threeinferior /fourinferior /fiveinferior /sixinferior
+ /seveninferior /eightinferior /nineinferior /centinferior /dollarinferior
+ /periodinferior /commainferior /Agravesmall /Aacutesmall /Acircumflexsmall
+% 350
+ /Atildesmall /Adieresissmall /Aringsmall /AEsmall /Ccedillasmall
+ /Egravesmall /Eacutesmall /Ecircumflexsmall /Edieresissmall /Igravesmall
+ /Iacutesmall /Icircumflexsmall /Idieresissmall /Ethsmall /Ntildesmall
+ /Ogravesmall /Oacutesmall /Ocircumflexsmall /Otildesmall /Odieresissmall
+ /OEsmall /Oslashsmall /Ugravesmall /Uacutesmall /Ucircumflexsmall
+ /Udieresissmall /Yacutesmall /Thornsmall /Ydieresissmall (001.000)
+ (001.001) (001.002) (001.003) /Black /Bold
+ /Book /Light /Medium /Regular /Roman
+ /Semibold
+.packtomark def
+
+% ---------------- Standard encodings ---------------- %
+
+/StandardEncodings [
+
+% StandardEncoding
+mark
+ 0 0 0 0 0 0 0 0 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 2 3 4 5 6 7 8 9 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 36 37 38 39 40 41 42 43 44 45 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 0
+ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110
+ 0 111 112 113 114 0 115 116 117 118 119 120 121 122 0 123
+ 0 124 125 126 127 128 129 130 131 0 132 133 0 134 135 136
+ 137 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 138 0 139 0 0 0 0 140 141 142 143 0 0 0 0
+ 0 144 0 0 0 145 0 0 146 147 148 149 0 0 0 0
+.packtomark
+
+% ExpertEncoding
+mark
+ 0 0 0 0 0 0 0 0 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 229 230 0 231 232 233 234 235 236 237 238 13 14 15 99
+ 239 240 241 242 243 244 245 246 247 248 27 28 249 250 251 252
+ 0 253 254 255 256 257 0 0 0 258 0 0 259 260 261 262
+ 0 0 263 264 265 0 266 109 110 267 268 269 0 270 271 272
+ 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288
+ 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 0
+ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 304 305 306 0 0 307 308 309 310 311 0 312 0 0 313
+ 0 0 314 315 0 0 316 317 318 0 0 0 158 155 163 319
+ 320 321 322 323 324 325 0 0 326 150 164 169 327 328 329 330
+ 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346
+ 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362
+ 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378
+.packtomark
+
+] readonly def
+
+% ---------------- Standard Charsets ---------------- %
+
+% We include an explicit 0 at the beginning of each charset.
+
+/StandardCharsets [
+
+% ISOAdobe
+mark
+ 0
+ 1 1 228 { } for
+.packtomark
+
+% Expert
+mark
+ 0
+ 1 229 230 231 232 233 234 235 236 237 238 13 14 15 99 239
+ 240 241 242 243 244 245 246 247 248 27 28 249 250 251 252 253
+ 254 255 256 257 258 259 260 261 262 263 264 265 266 109 110 267
+ 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283
+ 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299
+ 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315
+ 316 317 318 158 155 163 319 320 321 322 323 324 325 326 150 164
+ 169 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341
+ 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357
+ 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373
+ 374 375 376 377 378
+.packtomark
+
+% ExpertSubset
+mark
+ 0
+ 1 231 232 235 236 237 238 13 14 15 99 239 240 241 242 243
+ 244 245 246 247 248 27 28 249 250 251 253 254 255 256 257 258
+ 259 260 261 262 263 264 265 266 109 110 267 268 269 270 272 300
+ 301 302 305 314 315 158 155 163 320 321 322 323 324 325 326 150
+ 164 169 327 328 329 330 331 332 333 334 335 336 337 338 339 340
+ 341 342 343 344 345 346
+.packtomark
+
+] readonly def
+
+% ---------------- Font loading ---------------- %
+
+% ------ Utilities ------ %
+
+/advance { % <n> advance -
+ f cff eq { /pos pos 3 -1 roll add store } { pop } ifelse
+} def
+/next { % - next <byte>
+ f read { 1 advance } if
+} def
+/nextstring { % <length> nextstring <string>
+ dup 0 eq {
+ pop ()
+ } {
+ string f exch readstring pop dup length advance
+ } ifelse
+} def
+/card8 % - card8 <card8>
+ /next load
+def
+/card16 { % - card16 <card16>
+ card8 8 bitshift card8 add
+} def
+/offset { % <offsize> offset <offset>
+ 0 exch { 8 bitshift next add } repeat
+} def
+/sid % - <sid> sid
+ /card16 load
+def
+/Index { % - Index <array>
+ mark card16 dup 0 ne {
+ 1 exch next dup offset pop exch {
+ dup offset dup 4 -1 roll sub 3 1 roll exch
+ } repeat pop
+ } if pop .packtomark
+ [ exch { nextstring } forall ] readonly
+} def
+/tokens { % - tokens <num1> ... <op#> (op# = 12 means EOF)
+ {
+ f read not { 12 exit } if
+ 1 advance
+ dup 12 eq { pop next 32 add exit } if
+ dup 28 lt { exit } if
+ dup 32 lt {
+ 28 sub {
+ { card16 32768 xor 32768 sub }
+ { 4 offset dup 16#7fffffff gt { -1 32 bitshift add } if }
+ { tokenreal }
+ { 31 exit }
+ } exch get exec
+ } {
+ dup 247 lt {
+ 139 sub
+ } {
+ 247 sub {
+ { next 108 add }
+ { next 364 add }
+ { next 620 add }
+ { next 876 add }
+ { next 108 add neg }
+ { next 364 add neg }
+ { next 620 add neg }
+ { next 876 add neg }
+ % 255 is deliberately omitted and will cause a rangecheck
+ } exch get exec
+ } ifelse
+ } ifelse
+ } loop
+} def
+/tokenbuf 100 string def
+/tokenput { % <index> <char> tokenput <index+1>
+ tokenbuf 2 index 3 -1 roll put 1 add
+} def
+/tokenrealarray [
+ (0123456789.E) { } forall
+ [(E) 0 get /tokenput cvx (-) 0 get] cvx
+ null % will give an error
+ (-) 0 get
+ { exit }
+] readonly def
+/tokenreal { % - tokenreal <float>
+ 0 {
+ next exch 1 index -4 bitshift tokenrealarray exch get exec tokenput
+ % We must leave the byte on the stack temporarily so that
+ % the exit will see a consistent stack state.
+ 1 index 15 and tokenrealarray exch get exec tokenput exch pop
+ } loop
+ tokenbuf 0 3 -1 roll getinterval cvr exch pop
+} def
+/Dict { % <opsdict> Dict -
+ /opdict exch store {
+ mark tokens opdict exch .knownget { exec } if cleartomark
+ } loop cleartomark
+} def
+/idstring { % <sid> idstring <string|name>
+ dup 391 lt { StandardStrings } { 391 sub strings } ifelse exch get
+} def
+/idname { % <sid> idname <name>
+ idstring dup type /nametype ne { cvn } if
+} def
+
+% ------ Top dictionary ------ %
+
+/offput { % <offset> <proc> offput -
+ currentdict exch aload length 1 add packedarray cvx
+ offsets 3 1 roll put
+} def
+/queueput { % <font> <proc> queueput -
+ 16#7fffffff offsets { pop .min } forall
+ pos sub nextstring
+ 3 1 roll aload length 2 add packedarray cvx
+ [ queued aload pop counttomark 2 add -1 roll ]
+ /queued exch store
+} def
+/xxput { % <value> <key> <dict> xxput -
+ 3 1 roll exch put
+} def
+/putfi { % <value> <key> putfi -
+ FontInfo xxput
+} def
+/xdef { % <value> <key> xdef -
+ exch def
+} def
+/topdictops mark
+ 12 { exit }
+ 0 { idstring /version putfi }
+ 1 { idstring /Notice putfi }
+ 32 { idstring /Copyright putfi }
+ 2 { idstring /FullName putfi }
+ 3 { idstring /FamilyName putfi }
+ 4 { idstring /Weight putfi }
+ 33 { 0 ne /isFixedPitch putfi }
+ 34 { /ItalicAngle putfi }
+ 35 { /UnderlinePosition putfi }
+ 36 { /UnderlineThickness putfi }
+ 37 { /PaintType xdef }
+ 38 { /CharstringType xdef }
+ 39 { counttomark array astore /FontMatrix xdef }
+ 13 { /UniqueID xdef }
+ 5 { counttomark array astore /FontBBox xdef }
+ 40 { /StrokeWidth xdef }
+ 14 { counttomark array astore /XUID xdef }
+ 15 {
+ dup StandardCharsets length lt {
+ StandardCharsets exch get /charset xdef
+ } {
+ { queuecharset } offput
+ } ifelse
+ }
+ 16 {
+ dup StandardEncodings length lt {
+ /Encoding xdef
+ } {
+ { queueEncoding } offput
+ } ifelse
+ }
+ 17 { { readCharStrings } offput }
+ 18 { exch /readPrivate cvx 2 packedarray offput }
+.dicttomark readonly def
+
+/readCharStrings { % <font> readCharStrings -
+ /CharStringArray Index put
+} def
+
+% ------ Charsets and encodings ------ %
+
+% Note: formats 1 and 2 can overflow the operand stack.
+% We'll fix this if it ever becomes necessary.
+/charsetformats [
+{ [ 0 CharStringArray length 1 sub { sid } repeat ]
+}
+{ [ 0 CharStringArray length 1 sub {
+ dup 0 eq { pop exit } if
+ sid card8 1 add 2 index .min { exch 1 sub 1 index 1 add } repeat pop
+ } loop ]
+}
+{ [ 0 CharStringArray length 1 sub {
+ dup 0 eq { pop exit } if
+ sid card16 1 add 2 index .min { exch 1 sub 1 index 1 add } repeat pop
+ } loop ]
+}
+] readonly def
+/queuecharset { % <font> queuecharset -
+ { readcharset } queueput
+} def
+/readcharset { % <data> <font> readcharset -
+ begin 0 () /SubFileDecode filter /f exch store
+ charsetformats next get exec /charset exch def end
+} def
+
+/encodingformats [
+{ 1 1 next { next exch Encoding 3 1 roll put } for
+}
+{ 1 next {
+ next next 1 add {
+ % Stack: gid code
+ Encoding 1 index 3 index put
+ exch 1 add exch 1 add
+ } repeat pop
+ } repeat pop
+}
+] readonly def
+/queueEncoding { % <font> queueEncoding -
+ { readEncoding } queueput
+} def
+/readEncoding { % <data> <font> readEncoding -
+ begin 0 () /SubFileDecode filter /f exch store
+ /Encoding [ 256 { /.notdef } repeat ] def
+ next encodingformats 1 index 127 and get exec
+ 128 ge {
+ % Read supplementary encodings.
+ next {
+ Encoding next sid idname put
+ } repeat
+ } if end
+} def
+
+% ------ Private dictionary ------ %
+
+/deltarray { % -mark- <num1> ... deltarray <num1'> ...
+ 0 counttomark 1 sub { counttomark -1 roll add dup } repeat pop
+ counttomark array astore
+} def
+
+/privatedictops mark
+ 12 { exit }
+ 6 { deltarray /BlueValues xdef }
+ 7 { deltarray /OtherBlues xdef }
+ 8 { deltarray /FamilyBlues xdef }
+ 9 { deltarray /FamilyOtherBlues xdef }
+ 41 { /BlueScale xdef }
+ 42 { /BlueShift xdef }
+ 43 { /BlueFuzz xdef }
+ 10 { 1 array astore /StdHW xdef }
+ 11 { 1 array astore /StdVW xdef }
+ 44 { deltarray /StemSnapH xdef }
+ 45 { deltarray /StemSnapV xdef }
+ 46 { 0 ne /ForceBold xdef }
+ 47 { /ForceBoldThreshold xdef }
+ 48 { /lenIV xdef }
+ 49 { /LanguageGroup xdef }
+ 50 { /ExpansionFactor xdef }
+ 51 { /initialRandomSeed xdef }
+ 19 { { readSubrs } offput }
+ 20 { /defaultWidthX xdef }
+ 21 { /nominalWidthX xdef }
+ % Multiple Master fonts only
+ 59 { /NDV xdef }
+ 60 { /CDV xdef }
+ 61 { /lenBuildCharArray xdef }
+.dicttomark readonly def
+
+/readPrivate { % <font> <size> readPrivate -
+ exch 1 index f exch () /SubFileDecode filter /f exch def
+ /Private get begin //privatedictops Dict end
+ /f cff def advance
+} def
+
+% ------ Main program ------ %
+
+% We need to pass the file as a parameter for the sake of the PDF
+% interpreter.
+/StartData { % <resname> <nbytes> StartData -
+ currentfile exch () /SubFileDecode filter ReadData
+} def
+/ReadData { % <resname> <file> ReadData -
+
+ % Initialize.
+
+ 30 dict begin
+ /cff exch def
+ /pos 0 def
+ /resname exch cvlit def
+
+ % Read the header.
+
+ /f cff def
+ /vmajor next def
+ /vminor next def
+ /hdrsize next def
+ /aoffsize next def
+
+ % Read the Indexes.
+
+ /names Index def
+ /topdicts Index def
+ /strings Index def
+ /gsubrs Index def
+
+ % Read the top Dicts.
+
+ /offsets 50 dict def
+ /queued [] def
+ /opdict null def % reserve a slot
+ /fonts [ topdicts {
+ 0 () /SubFileDecode filter /f exch def
+ 40 dict begin
+ % Preload defaults that differ from PostScript defaults,
+ % or that are required.
+ /FontType 1 def
+ /PaintType 0 def
+ /CharstringType 2 def
+ /FontMatrix [0.001 0 0 0.001 0 0] def
+ /charset StandardCharsets 0 get def
+ /Encoding 0 def
+ /FontInfo 10 dict
+ dup /UnderlinePosition -100 put
+ dup /UnderlineThickness 50 put
+ def
+ /Private 20 dict
+ gsubrs length 0 ne { dup /GlobalSubrs gsubrs put } if
+ def
+ //topdictops Dict
+ currentdict end
+ } forall ] def
+
+ % Read other tables with queued offsets.
+
+ DEBUG { offsets length =only ( offsets) = flush } if
+ { /f cff def
+ offsets pos 2 copy .knownget not { pop pop exit } if
+ 3 1 roll undef exec
+ } loop
+ offsets length 0 ne {
+ (Error: missing tables at ) print [ offsets { pop } forall ] ==
+ (Current position is ) print pos ==
+ flush stop
+ } if
+
+ % Process out-of-order tables.
+
+ DEBUG { queued length =only ( queued) = flush } if
+ queued { exec } forall
+
+ % Update Encoding and CharStrings.
+
+ fonts {
+ begin
+ % Construct the real Encoding.
+ % The value of Encoding is either a number, for predefined
+ % encodings, or an array of mixed GIDs and names.
+ /Encoding mark Encoding
+ DEBUG { (Encoding: ) print dup === flush } if
+ dup type /integertype eq {
+ StandardEncodings exch get { idname } forall
+ } {
+ {
+ dup type /integertype eq { charset exch get idname } if
+ } forall
+ } ifelse .packtomark def
+ % Construct the CharStrings.
+ % Note that they may only correspond to an initial
+ % subset of the charset.
+ /CharStrings charset length CharStringArray length .min dict def
+ DEBUG {
+ charset length =only ( charset ) print
+ CharStringArray length =only ( CharStringArray) =
+ charset == flush
+ } if
+ 0 1 CharStrings maxlength 1 sub {
+ dup CharStringArray exch get
+ exch charset exch get idstring
+ CharStrings xxput
+ } for
+ % Remove unwanted entries.
+ currentdict /charset undef
+ currentdict /CharStringArray undef
+ end
+ } forall
+
+ % Wrap up.
+
+ resname mark 0 1 fonts length 1 sub {
+ DEBUG { dup =only ( ) print flush } if
+ dup names exch get
+ DEBUG { dup == flush } if
+ exch fonts exch get
+ dup /FontName 3 index put
+ 1 index exch definefont
+ } for .dicttomark
+ end % temporary dict
+ end % FontSetInit ProcSet
+ /FontSet defineresource pop
+
+} bind def
+
+% ---------------- Resource category definition ---------------- %
+
+currentdict end readonly
+
+languagelevel exch 2 .setlanguagelevel
+
+/FontSet /Generic /Category findresource dup length dict .copydict
+/Category defineresource pop
+
+/FontSetInit exch /ProcSet defineresource pop
+
+.setlanguagelevel
diff --git a/pstoraster/gs_cidfn.ps b/pstoraster/gs_cidfn.ps
new file mode 100644
index 000000000..f7f926502
--- /dev/null
+++ b/pstoraster/gs_cidfn.ps
@@ -0,0 +1,466 @@
+% Copyright (C) 1995, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+%
+% This file is part of GNU Ghostscript.
+%
+% GNU Ghostscript is distributed in the hope that it will be useful, but
+% WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+% to anyone for the consequences of using it or for whether it serves any
+% particular purpose or works at all, unless he says so in writing. Refer
+% to the GNU General Public License for full details.
+%
+% Everyone is granted permission to copy, modify and redistribute GNU
+% Ghostscript, but only under the conditions described in the GNU General
+% Public License. A copy of this license is supposed to have been given
+% to you along with GNU Ghostscript so you can know your rights and
+% responsibilities. It should be in a file named COPYING. Among other
+% things, the copyright notice and this notice must be preserved on all
+% copies.
+%
+% Aladdin Enterprises supports the work of the GNU Project, but is not
+% affiliated with the Free Software Foundation or the GNU Project. GNU
+% Ghostscript, as distributed by Aladdin Enterprises, does not require any
+% GNU software to build or run it.
+
+% $Id: gs_cidfn.ps 956 2000-03-08 23:15:43Z mike $
+% ProcSet for implementing CIDFont and CIDMap resources.
+% When this is run, systemdict is still writable.
+
+% ---------------- Defining CIDFont resources ---------------- %
+
+% Define a CIDFont resource. This is the defineresource implementation for
+% the CIDFont resource category.
+
+/.cidfonttypes where { pop } { /.cidfonttypes 6 dict def } ifelse
+.cidfonttypes begin
+
+% The key in .cidfonttypes is the CIDFontType value;
+% the value is a procedure that takes a font name and the CIDFont dictionary
+% and replaces the latter with a real font.
+
+0 { % CIDFontType 0 = FontType 9
+ currentglobal 3 1 roll dup gcheck setglobal
+ dup /FontType 9 put
+ dup /FontMatrix known not {
+ dup /FontMatrix [0.001 0 0 0.001 0 0] put
+ dup /FDArray get {
+ /FontMatrix get [1000 0 0 1000 0 0] 1 index concatmatrix pop
+ } forall
+ } if
+ dup /FDArray get mark exch {
+ % Add pro forma entries
+ currentglobal exch dup gcheck setglobal
+ dup /FontType 1 put
+ dup /CharStrings mark /.notdef () .dicttomark put
+ dup /Encoding [] put
+ % Create a dummy Subrs array now, if there isn't one here
+ % already (which can only happen if we're giving another
+ % name to an existing font).
+ dup /Private get dup /Subrs known not {
+ dup /SubrCount .knownget {
+ array 1 index /Subrs 3 -1 roll put
+ } if readonly
+ } if pop
+ exch setglobal
+ dup /FontName .knownget not { () } if exch .buildfont1 exch pop
+ } forall ] 1 index /FDepVector 3 -1 roll put
+ 3 -1 roll setglobal
+ 1 index exch .buildfont9 exch pop
+} bind def
+
+1 { % CIDFontType 1 = FontType 10
+ dup /FontType 10 put
+ 1 index exch .buildfont10 exch pop
+} bind def
+
+2 { % CIDFontType 2 = FontType 11
+ dup /FontType 11 put
+ 1 index exch .buildfont11 exch pop
+} bind def
+
+end % .cidfonttypes
+
+% ---------------- Reading CIDFontType 0 files ---------------- %
+
+30 dict begin
+
+% We add the following entries to the CIDFont dictionary, in addition to
+% the ones documented by Adobe:
+% ReadString - procedure for reading a string from the binary data
+% SubrCache - dictionary for caching Subr arrays
+% For CIDFonts where we read the data from disk incrementally:
+% DataOffset - starting position of data in file
+% (if data are in hex) OffsetMap - map from logical data positions to
+% physical positions in file
+
+/StartData % <(Binary)|(Hex)> <datalength> StartData -
+ % (currentdict is CID font dict)
+{ % If we're loading a resource file, we can just save a
+ % pointer to the binary data and load it incrementally.
+ % Check for this by opening the resource file,
+ % positioning it to currentfile's position plus the
+ % data length, and checking for %%EndData.
+ mark
+ { currentfile fileposition
+ CIDFontName 100 string ResourceFileName (r) file
+ mark
+ { % Stack: (Binary)|(Hex) length -mark- pos resfile
+ % -mark-
+ 5 index (Hex) eq
+ { 1 index 3 index setfileposition
+ 1 index 5 index .skiphex
+ %**************** SKIP > AND WHITESPACE SOMEHOW
+ }
+ { 1 index 3 index 6 index add setfileposition
+ }
+ ifelse
+ 1 index 9 string readstring pop (%%EndData) ne { stop } if
+ }
+ .internalstopped { cleartomark closefile stop } if
+ pop % pop the mark
+ }
+ .internalstopped
+ { % File is not positionable, load the data now.
+ cleartomark exch (Hex) eq
+ { { currentfile exch readhexstring pop } }
+ { { currentfile exch readstring pop } }
+ ifelse /ReadString exch def
+ dup 65535 le
+ { string ReadString
+ }
+ { mark exch
+ { dup 0 eq { pop exit } if
+ dup 65535 min dup string ReadString
+ 3 1 roll sub
+ }
+ loop ]
+ }
+ ifelse
+ /GlyphData exch def
+ % If we were reading hex data, skip past the >.
+ /ReadString load 2 get { readhexstring } 0 get eq {
+ currentfile 0 (>) /SubFileDecode filter dup flushfile closefile
+ } if
+ /.vmreadstring cvx
+ }
+ { % File is positionable, just save a pointer.
+ % Stack: (Binary)|(Hex) length -mark- pos file
+ 4 1 roll
+ /DataOffset exch def
+ pop /GlyphData exch def
+ exch (Hex) eq
+ { % Hex data, build the offset map.
+ .buildoffsetmap
+ /.hexreadstring
+ }
+ { % Binary data, just skip over it.
+ currentfile DataOffset GlyphData add setfileposition
+ /.binaryreadstring
+ }
+ ifelse cvx
+ 2 packedarray cvx
+ }
+ ifelse /ReadString exch def
+ /SubrCache 10 dict def
+ CIDFontName currentdict /CIDFont defineresource pop
+ end % CID font dict
+ end % resource category dict
+} bind def
+
+% Skip a given distance in an ASCIIHex encoded file. We use this at
+% rendering time as well.
+/.skiphex % <file> <count> .skiphex -
+{ exch /ASCIIHexDecode filter dup 3 -1 roll () /SubFileDecode filter
+ dup flushfile closefile closefile
+} bind def
+
+% Build the map from logical offsets to physical offsets in ASCIIHex
+% encoded data.
+/.buildoffsetmap
+{ /OffsetMap GlyphData 256 idiv 8000 min array def
+ 2 dict begin
+ /block GlyphData OffsetMap length idiv def
+ 0 1 OffsetMap length 1 sub
+ { OffsetMap exch currentfile fileposition put
+ currentfile block .skiphex
+ }
+ for
+ GlyphData block mod dup 0 eq
+ { pop }
+ { currentfile exch .skiphex }
+ ifelse
+ end % scratch dict
+} bind def
+
+currentdict end
+
+% ---------------- Rendering ---------------- %
+
+% ------ Generic ------ %
+
+% Read a string at a given offset in a "file" (binary file, ASCII hex file,
+% or GlyphData in RAM).
+/.binaryreadstring % <pos> <string> <file> .binaryreadstring <string>
+ { dup 4 -1 roll DataOffset add setfileposition exch readstring pop
+ } bind def
+/.hexreadstring % <pos> <string> <file> .hexreadstring <string>
+{ % Use the OffsetMap to get to the block of hex data,
+ % then skip to the correct position by reading.
+ GlyphData OffsetMap length idiv
+ % Stack: pos string file blocklen
+ 3 index 1 index idiv OffsetMap exch get
+ 2 index exch setfileposition
+ % Skip the next (pos % blocklen) hex bytes.
+ 4 -1 roll exch mod 1 index exch .skiphex
+ % Stack: string file
+ exch readhexstring pop
+} bind def
+/.vmreadstring % <pos> <string> .vmreadstring <vmstring>
+{ GlyphData .stringsreadstring
+} bind def
+/.stringsreadstring % <pos> <string> <strings> .stringsreadstring
+ % <vmstring>
+{ dup type /stringtype eq
+ { 3 1 roll length getinterval
+ }
+ { { % Stack: pos string glyphdata
+ dup 0 get length dup 4 index gt { exit } if
+ 4 -1 roll exch sub 3 1 roll
+ dup length 1 sub 1 exch getinterval
+ }
+ loop
+ % Stack: pos string glyphdata glyphdata[0]length
+ % We know no request can span more than 2 strings.
+ 3 index 3 index length add 1 index le
+ { % Request fits in a single string: just return a substring.
+ pop 0 get 3 1 roll length getinterval
+ }
+ { % Request spans 2 strings. Copy the first part.
+ 1 index 0 get 4 index 3 -1 roll 1 index sub getinterval
+ 2 index copy
+ % Copy the second part.
+ % Stack: pos str glyphdata str1
+ length exch 1 get 0 3 index length
+ 3 index sub getinterval 2 index 3 1 roll putinterval
+ exch pop
+ }
+ ifelse
+ }
+ ifelse
+} bind def
+
+% Interpret a byte string as a (big-endian) integer.
+/.cvbsi % <bytes> .cvbsi <int>
+{ 0 exch { exch 8 bitshift add } forall
+} bind def
+
+% Read an integer from binary data.
+/.readint % <pos> <nbytes> .readint <int>
+{ string ReadString .cvbsi
+} bind def
+
+% Read the glyph data for a given CID. The CIDFont is currentdict.
+% Note that the data must be read into the same VM as the CharStrings
+% dictionary of the selected subfont.
+/.readglyphdata { % <cid> .readglyphdata <subfont> <string|null>
+ currentdict /GlyphDirectory .knownget {
+ dup type /arraytype eq {
+ 1 index exch get
+ } {
+ 1 index exch .knownget not { null } if
+ } ifelse
+ dup null eq {
+ FDepVector 0 get exch
+ } {
+ FDBytes 0 eq {
+ FDepVector 0 get exch
+ } {
+ % Note: FDBytes > 1 is not supported.
+ dup 0 get FDepVector exch get
+ exch dup length 1 sub 1 exch getinterval
+ } ifelse
+ } ifelse
+ } {
+ FDBytes GDBytes add mul CIDMapOffset add
+ dup FDBytes .readint exch
+ FDBytes add dup GDBytes .readint
+ exch GDBytes add FDBytes add GDBytes .readint
+ % Stack: fd pos nextpos
+ 1 index sub dup 0 eq {
+ pop pop pop FDepVector 0 get null
+ } {
+ % Stack: fd pos len
+ FDepVector 4 -1 roll get
+ dup /CharStrings get gcheck .currentglobal exch .setglobal
+ % Stack: pos len subfont global
+ 4 2 roll string ReadString exch .setglobal
+ } ifelse
+ } ifelse
+} bind def
+
+% ------ CIDFontType 0 ------ %
+
+% Read some Subrs for the current Type 1 subfont.
+% The subfont's Private dict is currentdict; the CIDFont itself is the
+% next dictionary on the stack.
+/.readsubrs { % <Subrs> <start> .readsubrs <Subrs>
+ 1 SubrCount 1 sub {
+ dup SDBytes mul SubrMapOffset add
+ dup SDBytes .readint exch SDBytes add SDBytes .readint
+ 1 index sub string ReadString 2 index 3 1 roll put
+ } for
+} bind def
+
+% Ensure that all the Subrs for the current Type 1 subfont are loaded.
+% The subfont's Private dict is currentdict; the CIDFont itself is the
+% next dictionary on the stack.
+/.loadsubrs {
+ currentdict /SubrMapOffset .knownget {
+ Subrs 0 get null ne {
+ pop % We've already loaded the Subrs.
+ } {
+ currentglobal exch currentdict gcheck setglobal
+ SubrCache 1 index .knownget {
+ % We've already loaded some Subrs at this offset.
+ % Make sure we've got as many as we need.
+ dup length SubrCount lt {
+ % We need to load more.
+ SubrCount array exch 1 index copy length .readsubrs
+ SubrCache 3 -1 roll 2 index put
+ } if
+ } {
+ % We haven't loaded any Subrs at this offset yet.
+ SubrCount array 0 .readsubrs
+ SubrCache 3 -1 roll 2 index put
+ } ifelse
+ Subrs copy pop setglobal
+ } ifelse
+ } if
+} bind def
+
+% BuildGlyph procedure for CIDFontType 0.
+% ****** WHY NOT USE .type1execchar FOR THIS? ******
+% The name %Type9BuildGlyph is known to the interpreter.
+/.cid0buildstring 10 string def
+(%Type9BuildGlyph) cvn { % <cidfont> <cid> %Type9BuildGlyph -
+ .currentglobal 3 1 roll 1 index gcheck .setglobal
+ 1 index begin
+ dup .readglyphdata dup null eq
+ { %**** HANDLE NOTDEF ****
+ }
+ if
+ % Stack: cidfont cid subfont charstring
+dup null eq { pop pop pop pop } { %**** WRONG ****
+ 4 -1 roll pop
+ exch dup /Private get begin .loadsubrs end
+ 3 -1 roll //.cid0buildstring cvs cvn 3 1 roll
+ dup /CharStrings get 3 index 4 -1 roll put
+ setfont
+ 1000 0 setcharwidth %**** WRONG ****
+ 0 0 moveto glyphshow
+} ifelse %**** WRONG ****
+ end
+ .setglobal
+} bind def
+
+% ------ CIDFontType 2 ------ %
+
+% BuildGlyph procedure for CIDFontType 2.
+% ****** ADD THE OUTLINE STRING AS AN ARGUMENT TO .type42execchar. ******
+% The name %Type11BuildGlyph is known to the interpreter.
+(%Type11BuildGlyph) cvn { % <cidfont> <cid> %Type11BuildGlyph -
+ .currentglobal 3 1 roll 1 index gcheck .setglobal
+ 1 index begin
+ % We must be prepared for out-of-range CIDs.
+ dup GDBytes mul GDBytes string CIDMap
+ mark 4 1 roll { .stringsreadstring } .internalstopped {
+ %**** 0 IS WRONG
+ cleartomark 0 GDBytes string CIDMap .stringsreadstring
+ } {
+ exch pop
+ } ifelse .cvbsi
+ % Stack: cidfont cid glyphindex
+%**************** GlyphDirectory is not supported yet.
+(
+ currentdict /GlyphDirectory .knownget
+) pop false
+ { dup type /arraytype eq
+ { 1 index exch get }
+ { 1 index exch .knownget not { null } if }
+ ifelse
+ dup null eq
+ { %**** HANDLE NOTDEF
+ }
+ if
+ 1 index exch .type42execchar
+ }
+ { 1 index exch .type42execchar
+ }
+ ifelse
+ end
+ .setglobal
+} bind def
+
+% ---------------- Define resources ---------------- %
+
+languagelevel exch 2 .setlanguagelevel
+
+% Define the CIDInit ProcSet resource.
+% The ProcSet dictionary is still on the stack.
+
+/CMap /Generic /Category findresource dup length dict .copydict
+/Category defineresource pop
+ % We might have loaded CMap support already.
+/CIDInit /ProcSet 2 copy resourcestatus {
+ pop pop findresource dup length 4 index length add dict .copydict
+ 4 -1 roll exch .copydict
+} {
+ 3 -1 roll
+} ifelse exch defineresource pop
+
+% Define the CIDFont resource category.
+% We break out .buildcidfont because it appears that at least for
+% Type 32 (CIDFontType 4) fonts, the font can be registered in the Font
+% category with only a CIDFontType and no FontType.
+/.buildcidfont { % <name> <fontdict> .buildcidfont
+ % <name> <cidfont>
+ dup /CIDFontType get //.cidfonttypes exch get exec
+} odef
+
+/CIDFont /Generic /Category findresource dup length dict .copydict
+dup /InstanceType /dicttype put
+dup /DefineResource {
+ .buildcidfont
+ /Generic /Category findresource /DefineResource get exec
+} put
+/Category defineresource pop
+
+% Add the new FontType resources.
+
+9 1 11 { dup /FontType defineresource pop } for
+
+% Add the new FMapType resource.
+
+9 dup /FMapType defineresource pop
+
+% Define the CIDMap resource category.
+% These aren't documented, but it's clear what they are for:
+% to give names to CIDMaps for CIDFontType 2 fonts.
+
+/CIDMap /Generic /Category findresource dup length dict .copydict
+dup /.CheckResource {
+ % Allow either a string or an array of strings.
+ dup type dup /stringtype eq
+ { pop true
+ }
+ { dup /arraytype eq exch /packedarraytype eq or
+ { true exch { type /stringtype ne { pop false exit } if } forall
+ }
+ { false
+ }
+ ifelse
+ }
+ ifelse
+} bind put
+/Category defineresource pop
+
+.setlanguagelevel
diff --git a/pstoraster/gs_cmap.ps b/pstoraster/gs_cmap.ps
new file mode 100644
index 000000000..e2ce96d65
--- /dev/null
+++ b/pstoraster/gs_cmap.ps
@@ -0,0 +1,256 @@
+% Copyright (C) 1995, 1996, 1997 Aladdin Enterprises. All rights reserved.
+%
+% This file is part of GNU Ghostscript.
+%
+% GNU Ghostscript is distributed in the hope that it will be useful, but
+% WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+% to anyone for the consequences of using it or for whether it serves any
+% particular purpose or works at all, unless he says so in writing. Refer
+% to the GNU General Public License for full details.
+%
+% Everyone is granted permission to copy, modify and redistribute GNU
+% Ghostscript, but only under the conditions described in the GNU General
+% Public License. A copy of this license is supposed to have been given
+% to you along with GNU Ghostscript so you can know your rights and
+% responsibilities. It should be in a file named COPYING. Among other
+% things, the copyright notice and this notice must be preserved on all
+% copies.
+%
+% Aladdin Enterprises supports the work of the GNU Project, but is not
+% affiliated with the Free Software Foundation or the GNU Project. GNU
+% Ghostscript, as distributed by Aladdin Enterprises, does not require any
+% GNU software to build or run it.
+
+% $Id: gs_cmap.ps 956 2000-03-08 23:15:43Z mike $
+% ProcSet for implementing CMap resources.
+% When this is run, systemdict is still writable.
+
+% NOTE: Rearranged fonts are not implemented yet.
+
+% ---------------- Public 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>
+ 10 dict begin
+ /CMap 2 index dup type /dicttype ne { /CMap findresource } if def
+ /Encoding [ 0 1 4 index length 1 sub { } for ] def
+ /FDepVector [ 2 index {
+ dup type /dicttype ne {
+ dup /CIDFont resourcestatus {
+ pop pop /CIDFont findresource
+ } {
+ /Font findresource
+ } ifelse
+ } if
+ } forall ] readonly def
+ /FMapType 9 def
+ /FontMatrix matrix def
+ /FontName 3 index def
+ /CMap load /WMode .knownget { /WMode exch def } if
+ /FontType 0 def
+ pop pop currentdict end /Font defineresource
+} bind odef
+
+% ---------------- CMap operators ---------------- %
+
+30 dict begin
+
+% Our internal .CodeMaps structure is an array of two arrays: array 0
+% is the map for defined characters, array 1 is the map for notdefs.
+% Both are multi-level arrays indexed by the successive bytes of the
+% character code. Each value is either a sub-array, null, a character name,
+% a CID (an integer), or a character code (expressed as a byte string).
+% All of the arrays are read-only after they have been built.
+%
+% Note that the code in zfcmap.c that constructs the C structures from
+% the PostScript structures has intimate knowledge of the above format.
+
+/.getmap { .CodeMaps exch get } bind def
+/.putmap { .CodeMaps exch 3 -1 roll put } bind def
+
+% ------ Font-level operators ------ %
+
+/begincmap % - begincmap -
+ { /.CodeMaps [256 array 256 array] def
+ } bind def
+/endcmap % - endcmap -
+ { /.CodeMaps .CodeMaps .endmap def
+ /CodeMap null def % for .buildcmap
+ currentdict end .buildcmap begin
+ } bind def
+
+/begincodespacerange % <count> begincodespacerange -
+ { pop mark
+ } bind def
+/endcodespacerange % <code_lo> <code_hi> ... endcodespacerange -
+ { counttomark 2 idiv
+ { .CodeMaps { 3 copy .addcodespacerange pop } forall pop pop
+ } 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
+ { 0 .getmap .endmapchar 0 .putmap
+ } bind def
+
+/beginbfrange % <count> beginbfrange -
+ { pop mark
+ } bind def
+/endbfrange % <code_lo> <code_hi> <to_code|(charname*)> ...
+ % endbfrange -
+ { 0 .getmap counttomark 3 idiv { .addbfrange } repeat 0 .putmap 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 -
+ { 0 .getmap .endmapchar 0 .putmap
+ } bind def
+
+/begincidrange % <count> begincidrange -
+ { pop mark
+ } bind def
+/endcidrange % <code_lo> <code_hi> <cid_base> ... endcidrange -
+ { 0 .getmap counttomark 3 idiv { { 1 add } exch .addmaprange exch pop } repeat
+ 0 .putmap pop
+ } bind def
+
+/.endmapchar % -mark- <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 -
+ { { } 1 .getmap .addmaprange 1 .putmap pop
+ } bind def
+
+% ---------------- Resource category definition ---------------- %
+
+currentdict end
+
+languagelevel exch 2 .setlanguagelevel
+
+/CMap /Generic /Category findresource dup length dict .copydict
+/Category defineresource pop
+ % We might have loaded CID font support already.
+/CIDInit /ProcSet 2 copy { findresource } .internalstopped
+ % An interior `stopped' might have reset VM allocation to local.
+true .setglobal
+ { pop pop 3 -1 roll }
+ { dup length 4 index length add dict .copydict 4 -1 roll exch .copydict }
+ifelse exch defineresource pop
+
+.setlanguagelevel
diff --git a/pstoraster/gs_cmdl.ps b/pstoraster/gs_cmdl.ps
new file mode 100644
index 000000000..7293e0923
--- /dev/null
+++ b/pstoraster/gs_cmdl.ps
@@ -0,0 +1,188 @@
+% 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 supports the work of the GNU Project, but is not
+% affiliated with the Free Software Foundation or the GNU Project. GNU
+% Ghostscript, as distributed by Aladdin Enterprises, does not require any
+% GNU software to build or run it.
+
+% $Id: gs_cmdl.ps 956 2000-03-08 23:15:43Z mike $
+% 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..b31a39228
--- /dev/null
+++ b/pstoraster/gs_dbt_e.ps
@@ -0,0 +1,67 @@
+% 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 supports the work of the GNU Project, but is not
+% affiliated with the Free Software Foundation or the GNU Project. GNU
+% Ghostscript, as distributed by Aladdin Enterprises, does not require any
+% GNU software to build or run it.
+
+% $Id: gs_dbt_e.ps 956 2000-03-08 23:15:43Z mike $
+% 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..eedd98082
--- /dev/null
+++ b/pstoraster/gs_diskf.ps
@@ -0,0 +1,232 @@
+% 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 supports the work of the GNU Project, but is not
+% affiliated with the Free Software Foundation or the GNU Project. GNU
+% Ghostscript, as distributed by Aladdin Enterprises, does not require any
+% GNU software to build or run it.
+
+% $Id: gs_diskf.ps 956 2000-03-08 23:15:43Z mike $
+% 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_dpnxt.ps b/pstoraster/gs_dpnxt.ps
new file mode 100644
index 000000000..2d2573ff7
--- /dev/null
+++ b/pstoraster/gs_dpnxt.ps
@@ -0,0 +1,120 @@
+% Copyright (C) 1997, 1998 Aladdin Enterprises. All rights reserved.
+%
+% This file is part of GNU Ghostscript.
+%
+% GNU Ghostscript is distributed in the hope that it will be useful, but
+% WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+% to anyone for the consequences of using it or for whether it serves any
+% particular purpose or works at all, unless he says so in writing. Refer
+% to the GNU General Public License for full details.
+%
+% Everyone is granted permission to copy, modify and redistribute GNU
+% Ghostscript, but only under the conditions described in the GNU General
+% Public License. A copy of this license is supposed to have been given
+% to you along with GNU Ghostscript so you can know your rights and
+% responsibilities. It should be in a file named COPYING. Among other
+% things, the copyright notice and this notice must be preserved on all
+% copies.
+%
+% Aladdin Enterprises supports the work of the GNU Project, but is not
+% affiliated with the Free Software Foundation or the GNU Project. GNU
+% Ghostscript, as distributed by Aladdin Enterprises, does not require any
+% GNU software to build or run it.
+
+% $Id: gs_dpnxt.ps 956 2000-03-08 23:15:43Z mike $
+% gs_dpnxt.ps
+% NeXT Display PostScript extensions
+
+% Define the operation values for compositing. These must match the values
+% in gsdpnext.h, which also are the ones from the NeXT documentation.
+% We put them in systemdict, which seems like as good a place as any.
+mark
+ /Clear /Copy /Sover /Sin /Sout /Satop /Dover /Din /Dout /Datop /Xor
+ /PlusD /PlusL /Highlight % not sure about Highlight
+counttomark { counttomark 1 sub def } repeat pop
+
+% We implement readimage and sizeimage using the following 3 otherwise
+% undocumented lower-level operators:
+%
+% <x> <y> <width> <height> <matrix> .sizeimagebox
+% <dev_x> <dev_y> <dev_width> <dev_height> <matrix>
+%
+% - .sizeimageparams <bits/sample> <multiproc> <ncolors>
+%
+% <device> <x> <y> <width> <max_height> <alpha?> <std_depth|null>
+% <string> .getbitsrect <height> <substring>
+%
+% NOTE: These operators are subject to change without notice!
+
+% Implement readimage using .getbitsrect. Experimentation on a NeXT system
+% shows that the data is always returned in order of increasing device Y,
+% regardless of the CTM.
+%
+% Note that we can't make stack protection work for this operator,
+% because it must remove its operands from the stack before calling
+% the supplied procedure(s).
+
+/readimage { % <x> <y> <width> <height> <proc> [... <procN-1>]
+ % <string> <alpha?> readimage -
+ .sizeimageparams exch {
+ % multiproc = true. If N > 1, store the procedures in an array.
+ exch pop 1 index { 1 add } if
+ % Stack: ... string alpha? nprocs
+ dup 1 eq {
+ pop false % only 1 procedure, multiproc is irrelevant
+ } {
+ dup array 4 1 roll 3 add 2 roll astore 3 1 roll true
+ } ifelse
+ } {
+ % multiproc = false.
+ pop pop false
+ } ifelse
+ % Map the rectangle to device coordinates.
+ % Stack: x y w h proc(s) str alpha? multi?
+ 8 -4 roll matrix .sizeimagebox pop 8 4 roll
+ % Make sure we allocate the operand array in local VM
+ % to avoid a possible invalidaccess.
+ .currentglobal false .setglobal 9 1 roll
+ exch { 1 } { 0 } ifelse exch % alpha is last, if present
+ exch 4 1 roll 8 array astore exch .setglobal
+ { % Read out a block of scan lines and pass them to the procedure.
+ % Stack: [x y w h alpha? proc(s) str multi?] -- we must consume this.
+ dup 3 get 0 eq { pop exit } if
+ aload 9 1 roll pop exch pop currentdevice 7 1 roll
+ % Always read out the data as standard (not native) pixels.
+ .sizeimageparams pop pop exch .getbitsrect
+ % Stack: [x y w h alpha? proc(s) str multi?] hread substr
+ 3 -1 roll
+ % Stack: hread substr [x y w h alpha? proc(s) str multi?]
+ dup 1 2 copy get 5 index add put
+ % Stack: hread substr [x y' w h alpha? proc(s) str multi?]
+ dup 3 2 copy get 6 -1 roll sub put
+ % Stack: substr [x y' w h' alpha? proc(s) str multi?]
+ dup 5 get exch 7 get {
+ % multiproc = true, pass each plane to a different procedure.
+ % Stack: substr procs
+ 0 1 2 index length 1 sub {
+ % Push 1 plane and its procedure under the top 2 elements.
+ % Stack: ... substr procs plane#
+ 2 index length 2 index length idiv % bytes per plane
+ dup 2 index mul exch
+ % Stack: ... substr procs plane# start length
+ 4 index 3 1 roll getinterval 4 1 roll
+ 2 copy get 4 1 roll pop
+ } for
+ exch pop length 2 mul .execn
+ } {
+ % multiproc = false, just call the procedure.
+ exec
+ } ifelse
+ } //systemdict /exec get 3 packedarray cvx loop
+} bind odef
+
+% Implement sizeimage using lower-level operators.
+
+/sizeimage { % <x> <y> <width> <height> <matrix> sizeimage
+ % <devwidth> <devheight> <bits/sample> <matrix>
+ % <multiproc> <ncolors>
+ .sizeimagebox 5 -2 roll pop pop
+ .sizeimageparams 3 -1 roll 4 1 roll
+} bind odef
diff --git a/pstoraster/gs_dps.ps b/pstoraster/gs_dps.ps
new file mode 100644
index 000000000..354d8155b
--- /dev/null
+++ b/pstoraster/gs_dps.ps
@@ -0,0 +1,205 @@
+% Copyright (C) 1997, 1998 Aladdin Enterprises. All rights reserved.
+%
+% This file is part of GNU Ghostscript.
+%
+% GNU Ghostscript is distributed in the hope that it will be useful, but
+% WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+% to anyone for the consequences of using it or for whether it serves any
+% particular purpose or works at all, unless he says so in writing. Refer
+% to the GNU General Public License for full details.
+%
+% Everyone is granted permission to copy, modify and redistribute GNU
+% Ghostscript, but only under the conditions described in the GNU General
+% Public License. A copy of this license is supposed to have been given
+% to you along with GNU Ghostscript so you can know your rights and
+% responsibilities. It should be in a file named COPYING. Among other
+% things, the copyright notice and this notice must be preserved on all
+% copies.
+%
+% Aladdin Enterprises supports the work of the GNU Project, but is not
+% affiliated with the Free Software Foundation or the GNU Project. GNU
+% Ghostscript, as distributed by Aladdin Enterprises, does not require any
+% GNU software to build or run it.
+
+% $Id: gs_dps.ps 956 2000-03-08 23:15:43Z mike $
+% Initialization file for Display PostScript functions.
+
+% ------ Contexts ------ %
+
+% To create a context with private local VM, we use the .localfork
+% operator to actually create the context, the new VM, and an empty
+% userdict, and then we call the .initlocaldicts procedure to make
+% local copies of the initial contents of the dictionaries in local VM.
+% savedlocaldicts in systemdict is a global read-only dictionary whose
+% elements are global read-only copies of these initial contents;
+% we just copy its elements into local VM and install them in systemdict.
+% userdict and internaldict require special handling.
+
+% Switching between contexts with different local VMs requires
+% changing the bindings in systemdict that reference local objects.
+% For this purpose, each userdict has an entry called localdicts
+% which holds the local copies of the elements of savedlocaldicts,
+% plus internaldict. The context switching code in the interpreter
+% effectively copies this dictionary into systemdict.
+% NOTE: the name localdicts is known to the interpreter.
+
+% Switching between contexts also requires resetting the user parameters.
+% The interpreter records the value of userparams (a local dictionary
+% referenced from systemdict) for each context, and uses it for this.
+% See gs_lev2.ps for more details.
+% NOTE: the name userparams is known to the interpreter.
+
+% Save copies of local dictionaries at the end of system initialization.
+% Also save the initial gstate.
+/.savelocalstate {
+ .currentglobal true .setglobal
+ //systemdict /savedlocaldicts mark //systemdict {
+ dup gcheck {
+ pop pop
+ } {
+ dup type /dicttype eq {
+ % Save a copy of this dictionary in global VM.
+ dup maxlength dict .copydict readonly
+ } {
+ pop pop
+ } ifelse
+ } ifelse
+ } forall .dicttomark readonly .forceput % systemdict is read-only
+ % Create localdicts for the current context.
+ false .setglobal
+ userdict /localdicts mark savedlocaldicts {
+ pop dup load
+ } forall /internaldict dup load
+ .dicttomark readonly put
+ % Save a copy of the initial gstate.
+ true .setglobal
+ //systemdict /savedinitialgstate gstate readonly put
+ .setglobal
+} .bind def
+
+% Initialize local dictionaries and gstate when creating a new context.
+% Note that until this completes, we are in the anomalous situation of
+% having systemdict point to dictionaries that are in a non-current
+% local VM. Because of this, we turn off garbage collection temporarily.
+/.copylocal { % <name> <dict> .copylocal <name> <dict'>
+ % Copy a dictionary to the current (local) VM,
+ % and make it read-only if its current definition is.
+ dup maxlength dict .copydict
+ 1 index load wcheck not { readonly } if
+} .bind def
+% When this is called, the dictionary stack is in its initial state,
+% and there is (anomalously) only one gstate on the gstate stack.
+/.initlocaldicts { % - .initlocaldicts -
+ -2 vmreclaim
+ .currentglobal //systemdict begin
+ false .setglobal
+ % Since localdicts doesn't exist yet, references from
+ % systemdict to local objects won't get restored if
+ % a context switch happens in this code. Therefore,
+ % until localdicts is defined, we have to keep all our
+ % state on the operand stack.
+
+ % Acquire userdict.
+ %****** WRONG IF NON-STANDARD INITIAL DSTACK ******
+ countdictstack array dictstack
+ { dup gcheck not { exit } if pop } forall
+ % Create localdicts with a local copy of each dictionary,
+ % except for userdict and userparams, which just need
+ % to be filled in.
+ mark savedlocaldicts {
+ 1 index /userdict eq {
+ % Stack: userdict mark ... /userdict inituserdict
+ counttomark 1 add index exch .copydict
+ } {
+ 1 index /userparams eq {
+ % Stack: userparams mark ... /userparams inituserparams
+ userparams .copydict
+ } {
+ .copylocal
+ } ifelse
+ } ifelse
+ } forall /internaldict dup .makeinternaldict .makeoperator
+ .dicttomark readonly /localdicts exch put
+ % localdicts is now defined in userdict.
+ % Copy the definitions into systemdict.
+ localdicts { .forcedef } forall
+ % Set the user parameters.
+ userparams readonly .setuserparams
+ % Establish the initial gstate(s).
+ /savedinitialgstate .systemvar setgstate gsave
+ % Wrap up.
+ end .setglobal
+} odef
+
+% Create a context with private local VM.
+% The .localfork operator does all the work, but we must ensure that
+% .initlocaldicts gets called when the new context starts up.
+/localfork { % <mark> <obj1> ... <objN> <proc>
+ % <stdin|null> <stdout|null>
+ % localfork <context>
+ .currentglobal true .setglobal 3 index
+ dup dup xcheck
+ exch type dup /arraytype eq exch /packedarraytype eq or and not {
+ pop .setglobal /localfork cvx /typecheck signalerror
+ } if
+ {exec .initlocaldicts} aload pop
+ 3 1 roll 3 packedarray cvx
+ 4 1 roll 5 -1 roll pop .setglobal .localfork
+} odef
+
+% Fork a context that shares VM. We still need to fill in userparams
+% when the new context starts up.
+/.postfork { % - .postfork -
+ % Initialize the user parameters.
+ savedlocaldicts /userparams get userparams .copydict readonly pop
+} odef
+/fork { % <mark> <obj1> ... <objN> <proc> fork <context>
+ .currentglobal false .setglobal 1 index
+ dup dup xcheck
+ exch type dup /arraytype eq exch /packedarraytype eq or and not {
+ pop .setglobal /fork cvx /typecheck signalerror
+ } if
+ {exec .postfork} aload pop
+ 3 1 roll 3 packedarray cvx
+ 3 1 roll exch pop .setglobal .fork
+} odef
+
+% ------ Halftone phase ------ %
+
+/sethalftonephase { % <x> <y> sethalftonephase -
+ -1 2 index 2 index .setscreenphase pop pop
+} odef
+/currenthalftonephase { % - currenthalftonephase <x> <y>
+ 0 .currentscreenphase
+} odef
+
+% ------ Device-source images ------ */
+
+.imagetypes 2 /.image2 load put
+
+% ------ Device information ------ %
+
+/.deviceinfodict mark
+ /Colors null /GrayValues null /RedValues null /GreenValues null
+ /BlueValues null /ColorValues null
+.dicttomark readonly def
+/deviceinfo { % - deviceinfo <dict>
+ currentdevice //.deviceinfodict .getdeviceparams .dicttomark readonly
+} odef
+
+% The current implementation allocates a 2-element array each time.
+% Perhaps we should change this to 2 separate parameters for X and Y?
+/.wtdict mark
+ /wtranslation null
+.dicttomark readonly def
+/wtranslation { % - wtranslation <x> <y>
+ currentdevice //.wtdict .getdeviceparams exch pop exch pop aload pop
+} odef
+currentdict /.wtdict .undef
+
+% ------ View clipping ------ %
+
+/rectviewclip { % <x> <y> <width> <height> rectviewclip -
+ % <numarray|numstring> rectviewclip -
+ newpath .rectappend viewclip
+} odef
diff --git a/pstoraster/gs_dps1.ps b/pstoraster/gs_dps1.ps
new file mode 100644
index 000000000..f343b571a
--- /dev/null
+++ b/pstoraster/gs_dps1.ps
@@ -0,0 +1,147 @@
+% Copyright (C) 1997 Aladdin Enterprises. All rights reserved.
+%
+% This file is part of GNU Ghostscript.
+%
+% GNU Ghostscript is distributed in the hope that it will be useful, but
+% WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+% to anyone for the consequences of using it or for whether it serves any
+% particular purpose or works at all, unless he says so in writing. Refer
+% to the GNU General Public License for full details.
+%
+% Everyone is granted permission to copy, modify and redistribute GNU
+% Ghostscript, but only under the conditions described in the GNU General
+% Public License. A copy of this license is supposed to have been given
+% to you along with GNU Ghostscript so you can know your rights and
+% responsibilities. It should be in a file named COPYING. Among other
+% things, the copyright notice and this notice must be preserved on all
+% copies.
+%
+% Aladdin Enterprises supports the work of the GNU Project, but is not
+% affiliated with the Free Software Foundation or the GNU Project. GNU
+% Ghostscript, as distributed by Aladdin Enterprises, does not require any
+% GNU software to build or run it.
+
+% $Id: gs_dps1.ps 956 2000-03-08 23:15:43Z mike $
+% Initialization file for most of the Display PostScript functions
+% that are also included in Level 2.
+
+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
+ .forcedef % LocalFontDirectory is local, systemdict is global
+ .setglobal .FontDirectory
+ }
+ { /LocalFontDirectory .FontDirectory
+ .forcedef % LocalFontDirectory is local, systemdict is global
+ 50 dict
+ }
+ifelse def
+
+end % systemdict
+level2dict begin
+
+% setshared must rebind FontDirectory to the appropriate one of
+% Local or SharedFontDirectory.
+
+/.setglobal % <bool> .setglobal -
+ { dup .setglobal
+ //systemdict /FontDirectory .currentglobal
+ { //SharedFontDirectory }
+ { /LocalFontDirectory .systemvar } % can't embed ref to local VM
+ ifelse .forceput pop % LocalFontDirectory is local, systemdict is global
+ } .bind odef % must bind .forceput and .setglobal
+ % even if NOBIND in effect
+/setshared /.setglobal load def
+.currentglobal setshared
+
+% See below for changes in save and restore.
+
+% ------ Fonts ------ %
+
+/selectfont % <fontname> <size> selectfont -
+ { 1 index findfont
+ 1 index dup type /arraytype eq { makefont } { scalefont } ifelse
+ setfont pop pop
+ } odef
+% undefinefont has to take local/global VM into account.
+/undefinefont % <fontname> undefinefont -
+ { .FontDirectory 1 index .undef
+ .currentglobal
+ { % Current mode is global; delete from local directory too.
+ //systemdict /LocalFontDirectory .knownget
+ { 1 index .undef }
+ if
+ }
+ { % Current mode is local; if there was a shadowed global
+ % definition, copy it into the local directory.
+ //systemdict /SharedFontDirectory .knownget
+ { 1 index .knownget
+ { .FontDirectory 2 index 3 -1 roll put }
+ if
+ }
+ if
+ }
+ ifelse pop
+ } 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 % <save> restore -
+ { dup //restore % bind even if NOBIND
+ /LocalFontDirectory .systemvar
+ 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
+ .currentglobal .setglobal % Rebind FontDirectory according to current VM.
+ pop
+ } bind odef
+
+% ------ Miscellaneous ------ %
+
+/undef /.undef load def
+
+end % level2dict
diff --git a/pstoraster/gs_dps2.ps b/pstoraster/gs_dps2.ps
new file mode 100644
index 000000000..e375505ab
--- /dev/null
+++ b/pstoraster/gs_dps2.ps
@@ -0,0 +1,200 @@
+% Copyright (C) 1990, 1996, 1997 Aladdin Enterprises. All rights reserved.
+%
+% This file is part of GNU Ghostscript.
+%
+% GNU Ghostscript is distributed in the hope that it will be useful, but
+% WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+% to anyone for the consequences of using it or for whether it serves any
+% particular purpose or works at all, unless he says so in writing. Refer
+% to the GNU General Public License for full details.
+%
+% Everyone is granted permission to copy, modify and redistribute GNU
+% Ghostscript, but only under the conditions described in the GNU General
+% Public License. A copy of this license is supposed to have been given
+% to you along with GNU Ghostscript so you can know your rights and
+% responsibilities. It should be in a file named COPYING. Among other
+% things, the copyright notice and this notice must be preserved on all
+% copies.
+%
+% Aladdin Enterprises supports the work of the GNU Project, but is not
+% affiliated with the Free Software Foundation or the GNU Project. GNU
+% Ghostscript, as distributed by Aladdin Enterprises, does not require any
+% GNU software to build or run it.
+
+% $Id: gs_dps2.ps 956 2000-03-08 23:15:43Z mike $
+% Initialization file for basic Display PostScript functions
+% that are also included in Level 2.
+
+level2dict begin
+
+% ------ Halftones ------ %
+
+/.makestackdict
+ { { counttomark -1 roll } forall .dicttomark
+ } bind def
+/currenthalftone % - currenthalftone <dict>
+ { 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 { % <dict> sethalftone -
+ % We must create the new dictionary in the same VM as the
+ % operand; otherwise, invalidaccess errors may occur.
+ .currentglobal 1 index dup gcheck .setglobal
+ 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 .setglobal pop
+} 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 % <freq> <angle> <dict> .fix...screen
+ % <freq> <angle> <dict> <dict'>
+ { dup dup /HalftoneType get 1 eq
+ { dup wcheck not { dup length .copydict } if
+ dup /Frequency 5 index put
+ dup /Angle 4 index put
+ }
+ if
+ } bind def
+/setscreen % <ignore*2> <dict> setscreen -
+ { dup type /dicttype eq
+ { .fixsethalftonescreen sethalftone pop pop pop }
+ { //setscreen }
+ ifelse
+ } odef
+/setcolorscreen % <ignore*11> <dict> setcolorscreen -
+ { dup type /dicttype eq
+ { .fixsethalftonescreen sethalftone 12 { 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 % - currentscreen 60 0 <dict>
+ { .currenthalftone
+ { { .fixcurrenthalftonescreen } % halftone
+ { } % screen
+ { 12 3 roll 9 { pop } repeat % colorscreen
+ dup type /dicttype eq { .fixcurrenthalftonescreen } if
+ }
+ }
+ exch get exec
+ } odef
+/currentcolorscreen % - currentcolorscreen (60 0 <dict>)*4
+ { .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
+/.UserObjects {
+ .userdict /UserObjects
+} odef
+% In order to get proper error recovery behavior, we need to be careful
+% not to pop any operands from the stack until we're done.
+% The code below faithfully duplicates the apparent array-growing
+% behavior of Adobe interpreters.
+/defineuserobject { % <index> <value> defineuserobject -
+ .UserObjects .knownget {
+ length dup 3 index le {
+ % Stack: index value len
+ 2 index eq { 1 index 2 mul } { 1 index 1 add } ifelse
+ .localarray .UserObjects get
+ 1 index copy pop
+ .UserObjects 3 -1 roll put
+ } {
+ pop
+ } ifelse
+ } {
+ .UserObjects 3 index 1 add 10 .max .localarray put
+ } ifelse
+ .UserObjects get 2 index 2 index put pop pop
+} odef
+/execuserobject { % <index> execuserobject -
+ .UserObjects get 1 index get exch pop exec
+} odef
+/undefineuserobject { % <index> undefineuserobject -
+ .UserObjects get 1 index null put pop
+} odef
+
+% ------ Cache control ------ %
+
+% Dummy definitions for cache control operators
+
+/ucachestatus { % - ucachestatus -mark- ? ? ? ? <size>
+ mark 0 0 0 0 .userdict /.ucachesize .knownget not { 0 } if
+} odef
+/setucacheparams { % -mark- ... <size> setucacheparams -
+ % Provoke an appropriate error if needed.
+ counttomark 1 lt { () 0 get } if
+ 0 or .userdict /.ucachesize 2 index 0 .max put cleartomark
+} odef
+
+end % level2dict
diff --git a/pstoraster/gs_epsf.ps b/pstoraster/gs_epsf.ps
new file mode 100644
index 000000000..fcccb3e33
--- /dev/null
+++ b/pstoraster/gs_epsf.ps
@@ -0,0 +1,67 @@
+% 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 supports the work of the GNU Project, but is not
+% affiliated with the Free Software Foundation or the GNU Project. GNU
+% Ghostscript, as distributed by Aladdin Enterprises, does not require any
+% GNU software to build or run it.
+
+% $Id: gs_epsf.ps 956 2000-03-08 23:15:43Z mike $
+% Allow the interpreter to recognize MS-DOS EPSF file headers, and skip to
+% the PostScript section of the file.
+
+/.runnoepsf /run load def
+/.epsfheader <C5D0D3C6> def
+/run
+ { dup type /filetype ne { (r) file } if
+ % Check for MS-DOS EPSF file (see Red Book p. 729).
+ true exch 0 1 3
+ { % Stack: true file index
+ 1 index read dup { pop dup .epsfheader 3 index get eq } if
+ { pop pop } % if matched, don't need the character
+ { % unread characters (wasn't EPSF)
+ 2 index exch unread % unread mismatch character
+ dup { % loop unreading backwards in .epsfheader
+ 1 sub dup .epsfheader exch get 2 index exch unread
+ } repeat pop
+ exch not exch exit % change true to false
+ }
+ ifelse
+ }
+ for exch % Stack: file true/false
+ { % This block is executed if the file is MS-DOS EPSF.
+ % Build up the little-endian byte offset and length.
+ 2
+ { 1 0 4
+ { 2 index read not { pop exit } if % if EOF, let error happen
+ 2 index mul add exch 256 mul exch
+ }
+ repeat exch pop exch
+ }
+ repeat
+ % Stack: offset length file
+ % Use flushfile to skip quickly to the start of the
+ % PostScript section.
+ dup 4 -1 roll 12 sub () /SubFileDecode filter flushfile
+ % Now interpret the PostScript.
+ exch () /SubFileDecode filter cvx .runexec
+ }
+ { .runnoepsf
+ }
+ ifelse
+ } odef
diff --git a/pstoraster/gs_fform.ps b/pstoraster/gs_fform.ps
new file mode 100644
index 000000000..17dfbef74
--- /dev/null
+++ b/pstoraster/gs_fform.ps
@@ -0,0 +1,100 @@
+% Copyright (C) 1995, 1996, 1998 Aladdin Enterprises. All rights reserved.
+%
+% This file is part of GNU Ghostscript.
+%
+% GNU Ghostscript is distributed in the hope that it will be useful, but
+% WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+% to anyone for the consequences of using it or for whether it serves any
+% particular purpose or works at all, unless he says so in writing. Refer
+% to the GNU General Public License for full details.
+%
+% Everyone is granted permission to copy, modify and redistribute GNU
+% Ghostscript, but only under the conditions described in the GNU General
+% Public License. A copy of this license is supposed to have been given
+% to you along with GNU Ghostscript so you can know your rights and
+% responsibilities. It should be in a file named COPYING. Among other
+% things, the copyright notice and this notice must be preserved on all
+% copies.
+%
+% Aladdin Enterprises supports the work of the GNU Project, but is not
+% affiliated with the Free Software Foundation or the GNU Project. GNU
+% Ghostscript, as distributed by Aladdin Enterprises, does not require any
+% GNU software to build or run it.
+
+% $Id: gs_fform.ps 956 2000-03-08 23:15:43Z mike $
+% Form caching implemented in PostScript.
+
+% This implementation doesn't do the right thing about halftone or
+% Pattern phase, but the Pattern cache doesn't either....
+
+% The Form cache key is the Form dictionary; the value is an array
+% of 2 elements [CTM pattern_instance].
+%
+% In order to prevent restore from clearing the cache, we explicitly
+% push the cache entries on the stack before a restore and reinstall them.
+currentglobal false setglobal
+/.formcachedict 20 dict def % must be local
+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
+
+/.execform1 {
+ dup /Implementation known not {
+ dup /FormType get 1 ne { /rangecheck signalerror } if
+ % The Implementation is a Pattern that will draw the form.
+ currentglobal 1 index gcheck setglobal
+ % Stack: form global
+ 10 dict begin
+ /PatternType 1 def
+ /PaintType 1 def % colored
+ /TilingType 1 def % irrelevant
+ % Copy the BBox to the correct VM.
+ /BBox 2 index /BBox get 4 array copy exch 1 index def
+ % Set XStep and YStep to very large numbers,
+ % so we won't get multiple copies of the form.
+ /XStep 1 index dup 2 get exch 0 get sub 100 mul def
+ /YStep exch dup 3 get exch 1 get sub 100 mul def
+ /PaintProc 2 index /PaintProc get def
+ currentdict end readonly
+ % Stack: form global impl
+ exch setglobal
+ 1 index /Implementation 3 -1 roll .forceput
+ } if
+ .formcachedict 1 index .knownget {
+ % Check whether we can use the cached value.
+ % Stack: form cachevalue
+ matrix currentmatrix true 0 1 3 {
+ % Stack: form cachevalue curmat true index
+ 3 index 0 get 1 index get exch 3 index exch get ne {
+ pop pop false exit
+ } if
+ } for exch pop
+ } {
+ false
+ } ifelse not
+ { % Make a new cache entry.
+ gsave
+ matrix currentmatrix dup 4 0 put dup 5 0 put dup setmatrix
+ % Stack: form mat
+ 1 index /Implementation get
+ 2 index /Matrix get
+ makepattern 2 array astore
+ .formcachedict 2 index 2 index put
+ grestore
+ } if
+ % Stack: form cachevalue
+ -1 0 0 transform
+ 2 { exch round cvi } repeat .setscreenphase
+ 1 get setpattern
+ /BBox get aload pop
+ exch 3 index sub exch 2 index sub rectfill
+} .bind odef % must bind .forceput
+
+.formtypes 1 /.execform1 load put
+
+setglobal
diff --git a/pstoraster/gs_fonts.ps b/pstoraster/gs_fonts.ps
new file mode 100644
index 000000000..c413882ac
--- /dev/null
+++ b/pstoraster/gs_fonts.ps
@@ -0,0 +1,934 @@
+% Copyright (C) 1990, 1995, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+%
+% This file is part of GNU Ghostscript.
+%
+% GNU Ghostscript is distributed in the hope that it will be useful, but
+% WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+% to anyone for the consequences of using it or for whether it serves any
+% particular purpose or works at all, unless he says so in writing. Refer
+% to the GNU General Public License for full details.
+%
+% Everyone is granted permission to copy, modify and redistribute GNU
+% Ghostscript, but only under the conditions described in the GNU General
+% Public License. A copy of this license is supposed to have been given
+% to you along with GNU Ghostscript so you can know your rights and
+% responsibilities. It should be in a file named COPYING. Among other
+% things, the copyright notice and this notice must be preserved on all
+% copies.
+%
+% Aladdin Enterprises supports the work of the GNU Project, but is not
+% affiliated with the Free Software Foundation or the GNU Project. GNU
+% Ghostscript, as distributed by Aladdin Enterprises, does not require any
+% GNU software to build or run it.
+
+% $Id: gs_fonts.ps 956 2000-03-08 23:15:43Z mike $
+% 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 SUBSTFONT is defined, make it the default font.
+/SUBSTFONT where { pop /defaultfontname /SUBSTFONT load def } if
+
+% Define a reliable way of accessing FontDirectory in systemdict.
+/.FontDirectory
+{ /FontDirectory .systemvar
+} .bind odef
+
+% 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
+
+% Define a temporary string for local use, since using =string
+% interferes with some PostScript programs.
+/.fonttempstring 128 string 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 .fonttempstring 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
+ /dir true
+ /dll true
+ /doc true
+ /drv true
+ /exe true
+ /fon true
+ /fot true
+ /h true
+ /o true
+ /obj true
+ /pfm true
+ /pss true % Adobe Multiple Master font instances
+ /txt true
+.dicttomark def
+/.scan1fontstring 128 string def
+/.scanfontheaders [(%!PS-Adobe*) (%!FontType*)] 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 flush } 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
+
+% Create the dictionary that registers the .buildfont procedure (called by
+% definefont) for each FontType.
+/buildfontdict 20 dict def
+
+% Register Type 3 fonts, which are always supported, for definefont.
+buildfontdict 3 /.buildfont3 cvx put
+
+% Register Type 0 fonts if they are supported. Strictly speaking,
+% we should do this in its own file (gs_type0.ps), but since this is
+% the only thing that would be in that file, it's simpler to put it here.
+/.buildfont0 where { pop buildfontdict 0 /.buildfont0 cvx put } if
+
+% Define definefont. This is a procedure built on a set of operators
+% that do all the error checking and key insertion.
+/.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
+/.completefont {
+ { % 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.
+ % Note that some types of font don't have an Encoding.
+ dup /Encoding .knownget {
+ dup length 65 ge {
+ 64 get
+ dup /congruent eq { SymbolEncoding pop } if
+ /a9 eq { DingbatsEncoding pop } if
+ } {
+ pop
+ } ifelse
+ } if
+ }
+ ifelse
+ true exch
+ dup /FontType known not {
+ % This might be a CIDFont.
+ dup /CIDFontType known {
+ /.buildcidfont where {
+ pop exch pop false exch
+ } if
+ } if
+ } if
+ exch {
+ dup /FontType get //buildfontdict exch get exec
+ } {
+ .buildcidfont
+ } ifelse
+
+ DISKFONTS
+ { FontFileDirectory 2 index known
+ { dup /FontFile FontFileDirectory 4 index get .growput
+ }
+ if
+ }
+ if
+ readonly % stack: name fontdict
+ } stopped { /invalidfont signalerror } if
+} bind odef
+/definefont
+ { .completefont
+ % 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
+ } odef
+
+% Define a procedure for defining aliased fonts.
+% We use this only for explicitly aliased fonts, not substituted fonts:
+% we think this matches the observed behavior of Adobe interpreters.
+/.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 in this case, do a cvn.
+ % We might also be defining (as an alias) a global font
+ % whose FontName is a local non-string, if someone passed a
+ % garbage value to findfont. In this case, just don't
+ % call definefont at all.
+ 2 index dup type /stringtype eq exch .gcheck or 1 index .gcheck not or
+ { /FontName 3 index dup type /stringtype eq { cvn } if put
+ % Don't bind in definefont, since Level 2 redefines it.
+ /definefont .systemvar exec
+ }
+ { .completefont pop exch pop
+ }
+ ifelse 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 {
+ % According to Ed Taft, Adobe interpreters push userdict
+ % before loading a font, and pop it afterwards.
+ userdict begin
+ cvx exec
+ end
+} 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]
+ [(Frut) /Helvetica]
+ [(Geneva) /Helvetica]
+ [(Helv) /Helvetica]
+ [(NewYork) /Times]
+ [(Pala) /Palatino]
+ [(Sans) /Helvetica]
+ [(Schlbk) /NewCenturySchlbk]
+ [(Serif) /Times]
+ [(Swiss) /Helvetica]
+ [(Times) /Times]
+ [(Univers) /Helvetica]
+ % Substitute for Adobe Multiple Master fonts.
+ [(Minion) /Times]
+ [(Myriad) /Helvetica]
+ [(MyriadPkg) /Helvetica-Narrow]
+ % 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 type dup /stringtype eq exch /nametype eq or
+ { dup length string cvs } { () } ifelse
+ {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 exec
+ % 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. We must pop the arguments at the very end,
+% so that stack protection will be effective.
+
+/definefont { % <name> <font> definefont <font>
+ dup /FontMatrix known {
+ //definefont
+ } {
+ 2 copy /FontName get findfont //definefont exch pop exch pop
+ } ifelse
+} bind odef
+
+/scalefont { % <font> <scale> scalefont <font>
+ 1 index /FontMatrix known {
+ //scalefont
+ } {
+ 1 index /FontName get findfont 1 index //scalefont
+ exch pop exch pop
+ } ifelse
+} bind odef
+
+/makefont { % <font> <matrix> makefont <font>
+ 1 index /FontMatrix known {
+ //makefont
+ } {
+ 1 index /FontName get findfont 1 index //makefont
+ exch pop exch pop
+ } ifelse
+} bind odef
+
+/setfont { % <font> setfont -
+ dup /FontMatrix known {
+ //setfont
+ } {
+ dup /FontName get findfont //setfont pop
+ } ifelse
+} 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,
+% but it still needs to restore the stacks reliably if it fails,
+% so we do all the work in an operator.
+/.findfont {
+ mark 1 index
+ //systemdict begin .dofindfont
+ % Define any needed aliases.
+ counttomark 1 sub { .aliasfont } repeat end
+ exch pop exch pop
+} odef
+/findfont {
+ .findfont
+} 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. Make sure we're not already
+ % looking for the default 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
+ % Substitute for the font. Don't alias.
+ /SUBSTFONT where {
+ pop QUIET not {
+ (Substituting for font ) print dup =only
+ (.\n) print flush
+ } if
+ cleartomark mark defaultfontname
+ } {
+ 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
+ % Remove all the accumulated aliases.
+ counttomark 1 add 1 roll cleartomark mark exch
+ } ifelse
+ }
+ 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. Look for a file with the
+ % same name as the requested font.
+ dup dup type /nametype eq { .namestring } if .loadfontloop
+ }
+ { % Try each element of the Fontmap in turn.
+ false exch % (in case we exhaust the list)
+ % Stack: fontname false fontmaplist
+ { 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
+ % Stack: font true -or- fontname false
+ { true
+ }
+ { % None of the Fontmap entries worked.
+ % Try loading a file with the same name
+ % as the requested font.
+ dup dup type /nametype eq { .namestring } if .loadfontloop
+ }
+ ifelse
+ }
+ 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'.
+
+ {
+ % Is the font name a string?
+ dup type /stringtype ne
+ { QUIET not
+ { (Can't find font with non-string name: ) print dup =only (.\n) print flush
+ }
+ if pop false exit
+ }
+ if
+ % 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 .systemvar exec
+ % Remove the fake definition, if any.
+ .FontDirectory 3 index .undef
+ 1 index (r) file .loadfont .FontDirectory exch
+ /.setglobal .systemvar 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 and FontType defined.
+% (We define FontType only for the sake of some questionable code in the
+% Apple Printer Utility 2.0 font inquiry code.)
+%
+% Note that this procedure only creates fake fonts in the FontDirectory
+% associated with the current VM. This is because in multi-context systems,
+% creating the fake fonts in local VM leads to undesirable complications.
+/.definefakefonts
+ {
+ }
+ {
+ (gs_fonts FAKEFONTS) VMDEBUG
+ Fontmap {
+ pop dup type /stringtype eq { cvn } if
+ .FontDirectory 1 index known not {
+ 2 dict dup /FontName 3 index put
+ dup /FontType 1 put
+ .FontDirectory 3 1 roll put
+ } {
+ pop
+ } ifelse
+ } forall
+ }
+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 % current VM is global
+ } def % don't bind, .current/setglobal get redefined
+
+% ---------------- 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_init.ps b/pstoraster/gs_init.ps
new file mode 100644
index 000000000..6a670b638
--- /dev/null
+++ b/pstoraster/gs_init.ps
@@ -0,0 +1,1521 @@
+% Copyright 1993-2000 by Easy Software Products.
+% Copyright 1989, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+%
+% This file is part of GNU Ghostscript.
+%
+% GNU Ghostscript is distributed in the hope that it will be useful, but
+% WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+% to anyone for the consequences of using it or for whether it serves any
+% particular purpose or works at all, unless he says so in writing. Refer
+% to the GNU General Public License for full details.
+%
+% Everyone is granted permission to copy, modify and redistribute GNU
+% Ghostscript, but only under the conditions described in the GNU General
+% Public License. A copy of this license is supposed to have been given
+% to you along with GNU Ghostscript so you can know your rights and
+% responsibilities. It should be in a file named COPYING. Among other
+% things, the copyright notice and this notice must be preserved on all
+% copies.
+%
+% Aladdin Enterprises supports the work of the GNU Project, but is not
+% affiliated with the Free Software Foundation or the GNU Project. GNU
+% Ghostscript, as distributed by Aladdin Enterprises, does not require any
+% GNU software to build or run it.
+
+% $Id: gs_init.ps 956 2000-03-08 23:15:43Z mike $
+% 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.
+550
+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
+systemdict exch
+ { % userdict wasn't already set up by iinit.c.
+ dup /userdict
+ currentdict dup 200 .setmaxlength % userdict
+ .forceput % userdict is local, systemdict is global
+ }
+if begin
+
+% 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 /BATCH known /BATCH exch 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 /NOINTERPOLATE known /NOINTERPOLATE exch def
+currentdict /NOPAGEPROMPT known /NOPAGEPROMPT 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 /STRICT known /STRICT 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
+
+/.currentuserparams where {
+ pop mark
+ % The Adobe implementations appear to have very large maximum
+ % stack sizes. This turns out to actually make a difference,
+ % since some badly-behaved files include extremely long procedures,
+ % or construct huge arrays on the operand stack.
+ % We reset the stack sizes now so that we don't have to worry
+ % about overflowing the (rather small) built-in stack sizes
+ % during initialization.
+ /MaxDictStack 500
+ /MaxExecStack 5000
+ /MaxOpStack 50000
+ .dicttomark .setuserparams
+} if
+
+% 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
+
+% 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 100 mod dup 0 ne { 10 idiv } { pop } ifelse (.)
+ 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.
+/obind { % <name> <proc> obind <name> <oper>
+ 1 index exch .makeoperator
+} .bind def
+/odef { % <name> <proc> odef -
+ 1 index exch .makeoperator def
+} .bind def
+
+% Define a special version of def for storing local objects into global
+% dictionaries. Like .forceput, this exists only during initialization.
+/.forcedef { % <key> <value> .forcedef -
+ currentdict 3 1 roll .forceput
+} .bind odef
+
+% Define procedures for accessing variables in systemdict and userdict
+% regardless of the contents of the dictionary stack.
+/.systemvar { % <name> .systemvar <value>
+ //systemdict exch get
+} .bind odef
+/.userdict { % - .userdict <dict>
+ /userdict .systemvar
+} .bind odef
+/.uservar { % <name> .uservar <value>
+ .userdict exch get
+} .bind odef
+
+% If we're delaying binding, remember everything that needs to be bound later.
+DELAYBIND NOBIND not and
+ { .currentglobal false .setglobal
+ systemdict /.delaybind 1500 array .forceput
+ .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
+ { /.delaybind .systemvar dup length 0 ne
+ { .delaycount 2 index put
+ .userdict /.delaycount .delaycount 1 add put
+ }
+ { pop .bind
+ }
+ ifelse
+ } .bind def
+ } if
+
+%**************** BACKWARD COMPATIBILITY
+/hwsizedict mark /HWSize null .dicttomark readonly def
+/copyscanlines { % <device> <y> <string> copyscanlines <substr>
+ 0 3 1 roll 3 index //hwsizedict .getdeviceparams
+ exch pop exch pop aload pop 3 2 roll
+ 0 exch null exch .getbitsrect exch pop
+} bind odef
+currentdict /hwsizedict .undef
+/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
+/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 redefined if setpagedevice is present.
+/.beginpage { } odef
+% In LanguageLevel 3, copypage erases the page.
+/copypage {
+ .languagelevel 3 ge
+ 1 .endpage {
+ .currentnumcopies 1 index .outputpage
+ (>>copypage, press <return> to continue<<\n) .confirm
+ dup { erasepage } if
+ } if pop .beginpage
+} odef
+/currentmatrix {
+ .currentmatrix 6 index astore pop
+} 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 exec
+ % Only pop systemdict if it is still the top element,
+ % because this is apparently what Adobe interpreters do.
+ currentdict //systemdict eq { end } if
+ } odef
+% .endpage is redefined if setpagedevice is present.
+/.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
+% To satisfy the Genoa FTS, executive must be a procedure, not an operator.
+/executive
+ { { NOPROMPT not { prompt } if
+ { (%statementedit) (r) file } stopped
+ { pop pop $error /errorname get /undefinedfilename eq
+ { .clearerror exit } if % EOF
+ handleerror null % ioerror??
+ }
+ if
+ cvx { .runexec } execute
+ } loop
+ } bind def
+/filter
+ { //filterdict 1 index .knownget
+ { exch pop exec }
+ { /filter load /undefined signalerror }
+ ifelse
+ } odef
+/handleerror
+ { /errordict .systemvar /handleerror get exec } bind def
+/identmatrix [1.0 0.0 0.0 1.0 0.0 0.0] readonly def
+/identmatrix
+ { dup 0 //identmatrix putinterval } 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
+% Execute a file.
+% Level 2 uses 2 .stop to clear the e-stack for a successful startjob:
+% we detect that here, since we need to handle this even if we start out
+% without job control in effect.
+%
+% What we push on the e-stack is the following to be executed in this order:
+% <lit-file|fileproc> .runexec1 <lit-file|fileproc> .runexec2
+/.runexec1 { % <file|fileproc> .runexec1 -
+ dup type /filetype ne { cvx exec } if
+ cvx null 2 .stopped
+ % If we got back here from a startjob, just keep going.
+ % startjob replaces the null on the o-stack with a procedure
+ % to be executed when we get back here.
+ dup null ne { exec true } { pop false } ifelse
+} bind def
+/.runexec2 { % <continue> <file|fileproc> .runexec2 -
+ exch {
+ .runexec
+ } {
+ dup type /filetype ne { cvx exec } if
+ closefile
+ } ifelse
+} bind def
+/.runexec { % <file|fileproc> .runexec -
+ cvlit /.runexec1 cvx 1 index /.runexec2 cvx 4 .execn
+} bind def
+% The following is only for compatibility with Adobe interpreters.
+/setdash {
+ 1 index length 11 gt { /setdash load /limitcheck signalerror } if
+ //setdash
+} odef
+/setdevice
+ { .setdevice { erasepage } if } odef
+/setlinecap {
+ dup 2 gt { /setlinecap load /rangecheck signalerror } if
+ .setlinecap
+} odef
+/setlinejoin {
+ dup 2 gt { /setlinejoin load /rangecheck signalerror } if
+ .setlinejoin
+} odef
+/setmatrix {
+ dup aload pop .setmatrix pop
+} odef
+/showpage {
+ 0 .endpage .doneshowpage {
+ .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 { BATCH { null 0 .quit } { executive } ifelse } def
+% 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.
+/.internalstopped { null 1 .stopped null ne } bind def
+/store { % Don't alter operands before completing.
+ 1 index where { 2 index 2 index put pop pop } { def } ifelse
+} odef
+% NOTE: the name typenames is known to (initialized by) the interpreter.
+/type {
+ //typenames .type
+} odef
+% When running in Level 1 mode, this interpreter is supposed to be
+% compatible with PostScript "version" 54.0 (I think).
+/version (54.0) readonly def
+
+% internaldict is defined in systemdict, but is allocated in local VM.
+% We make a procedure for creating it, since we must create a new one
+% for each context with private local VM.
+/.makeinternaldict {
+ .currentglobal false .setglobal
+ [ /dup .systemvar 1183615869 /eq .systemvar
+ [ /pop .systemvar 10 dict ] cvx
+ [ /internaldict /cvx .systemvar /invalidaccess /signalerror cvx ] cvx
+ /ifelse .systemvar
+ ] cvx executeonly
+ exch .setglobal
+} odef
+systemdict /internaldict dup .makeinternaldict .makeoperator
+.forceput % proc is local, systemdict is global
+% Move superexec to internaldict if superexec is defined.
+currentdict /superexec .knownget {
+ 1183615869 internaldict /superexec 3 -1 roll put
+ currentdict /superexec .undef
+} 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
+ { /devicedict .systemvar 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
+ { % We don't want to bind 'run' into this procedure,
+ % since run may get redefined.
+ findlibfile
+ { exch pop /run .systemvar exec }
+ { /undefinedfilename signalerror }
+ ifelse
+ } bind def
+/selectdevice
+ { finddevice setdevice .setdefaultscreen } bind def
+/signalerror % <object> <errorname> signalerror -
+ { /errordict .systemvar 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
+} bind def
+/write= {
+ 1 index exch write=only (\n) writestring
+} bind def
+%%%% MRS - Send = output to stderr, since stdout is for output.
+/=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 NOPAGEPROMPT or NOPROMPT is true)
+ % and wait for the user to type something.
+ % If the user just types a newline, flush it.
+ NOPAGEPROMPT NOPROMPT or { 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 { % <obj> .execute <stopped>
+ stopped $error /newerror get and
+ { handleerror flush true } { false } ifelse
+} bind def
+/execute { % <obj> execute -
+ .execute pop
+} odef
+% Define an execute analogue of runlibfile0.
+/execute0 { % <obj> execute0 -
+ .execute { /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.
+% We don't use the obvious { (%stdin) run }, because we want the file to be
+% reopened if a startjob does a restore.
+/.runstdin {
+ { { (%stdin) (r) file cvx } .runexec } execute0
+} bind def
+% Define the procedure that the C code uses for running commands
+% given on the command line with -c. We turn the string into a file so that
+% .runexec can do the right thing with a startjob.
+/.runstring {
+ .currentglobal exch true .setglobal
+ 0 () .subfiledecode
+ exch .setglobal cvx { .runexec } execute
+} bind def
+% Define the procedure that the C code uses to set up for executing
+% a string that may be received in pieces.
+/.runstringbegin {
+ .currentglobal true .setglobal
+ { .needinput } bind 0 () .subfiledecode
+ exch .setglobal cvx .runexec
+} bind 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'.
+ 1 .instopped { null eq { pop pop stop } if } if
+ $error /.inerror get 1 .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 dup maxlength =only
+ } if
+ /gcheck where {
+ pop gcheck { ((G)) } { ((L)) } ifelse print
+ } {
+ 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 /errorname null put
+ $error /errorinfo .undef
+ 0 .setoserrno
+ } bind def
+
+% Define $error. This must be in local VM.
+.currentglobal false .setglobal
+/$error 40 dict .forcedef % $error is local, systemdict is global
+ % 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
+.forcedef % errordict is local, systemdict is global
+.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
+ { /.printerror .systemvar 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
+.forcedef % FontDirectory is local, systemdict is global
+
+% 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.
+% To satisfy the Genoa FTS, findencoding must be a procedure, not an operator.
+/rootfont where { pop /findencoding { .findencoding } def } if
+
+% Define .registerencoding.
+% NOTE: the name registeredencodings is known to (initialized by and shared
+% with) the interpreter.
+/.registerencoding { % <index> <array> .registerencoding -
+ % Check that the array is indexable.
+ % (It might still be a string, but then the .namestring will fail.)
+ dup 0 0 getinterval pop
+ % Check that all the elements of the array are names.
+ dup { .namestring pop } forall
+ % Do the store.
+ //registeredencodings 2 index 2 index readonly put pop pop
+} bind odef
+systemdict /registeredencodings .undef
+
+% 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} .internalstopped 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 (or higher) functionality is implemented, enable it now.
+/.setlanguagelevel where {
+ pop 2 .setlanguagelevel
+ % If the resource machinery is loaded, fix up some things now.
+ /.fixresources where { pop .fixresources } if
+} if
+/ll3dict where {
+ pop 3 .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.
+/undefinefont where {
+ pop /NullFont undefinefont
+} {
+ FontDirectory /NullFont .undef
+} ifelse
+
+(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
+% The following line used to skip setting of page size and resolution if
+% NODISPLAY was selected. We think this was only to save time and memory,
+% and it is a bad idea because it prevents setting the resolution in this
+% situation, which pstoedit (among other programs) relies on.
+%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 /Policies
+ % Stack: <pagedevice> <pagedevice> /Policies
+1 index /InputAttributes
+2 copy get dup length dict .copydict
+ % Stack: <pagedevice> <pagedevice> /Policies <pagedevice>
+ % /InputAttributes <inputattrs'>
+dup 0 2 copy get dup length dict .copydict
+ % Stack: <pagedevice> <pagedevice> /Policies <pagedevice>
+ % /InputAttributes <inputattrs'> <inputattrs'> 0 <attrs0'>
+dup /PageSize 7 index /PageSize get
+put % PageSize in 0
+put % 0 in InputAttributes
+put % InputAttributes in pagedevice
+% Also change the page size policy so we don't get an error.
+ % Stack: <pagedevice> <pagedevice> /Policies
+2 copy get dup length dict .copydict
+ % Stack: <pagedevice> <pagedevice> /Policies <policies'>
+dup /PageSize 7 put % PageSize in Policies
+put % Policies 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
+
+% Establish an appropriate halftone screen and BG/UCR functions.
+% We make this a procedure so we can call it again when switching devices.
+
+%%%% MRS - Changed default to 16x16 Floyd dither, matching the CUPS
+%%%% image RIP. Also, added missing standard transfer function,
+%%%% which is needed by output from many apps...
+
+% Set the default screen and BG/UCR based on the device resolution and
+% process color capability.
+/.setdefaultbgucr systemdict /setblackgeneration known { {
+ processcolors 1 eq { { } } { { pop 0.0 } } ifelse
+ dup setblackgeneration setundercolorremoval
+} } { {
+} } ifelse bind def
+% 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.
+<04> cvn { } def % Apple job separator
+<0404> cvn { } def % two of the same
+<1b> cvn { } def % MS Windows LaserJet 4 prologue
+ % (UEL = ESC %-12345X)
+<1b45> cvn { } def % PJL reset prologue (ESC E)
+<1b451b> cvn { } def % PJL reset epilogue (ESC E + UEL)
+<041b> cvn { } def % MS Windows LaserJet 4 epilogue (^D + UEL)
+(\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
+ 2 index (%std*) .stringmatch or
+ { 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
+ //systemdict /.delaybind {} .forceput % reclaim the space
+ //systemdict /.bindnow .forceundef % ditto
+ put
+ //systemdict /.forcedef .forceundef % remove temptation
+ //systemdict /.forceput .forceundef % ditto
+ //systemdict /.forceundef .forceundef % ditto
+} .bind odef
+
+% Turn off array packing, since some PostScript code assumes that
+% procedures are writable.
+false setpacking
+
+(END INIT) VMDEBUG
+
+/.currentuserparams where {
+ pop
+ % Remove real user params from psuserparams.
+ mark .currentuserparams counttomark 2 idiv {
+ pop psuserparams exch undef
+ } repeat pop
+ % Update the copy of the user parameters.
+ mark .currentuserparams counttomark 2 idiv {
+ userparams 3 1 roll .forceput % userparams is read-only
+ } repeat pop
+ % Turn on idiom recognition, if available.
+ currentuserparams /IdiomRecognition known {
+ /IdiomRecognition true .definepsuserparam
+ } if
+ psuserparams readonly pop
+ systemdict /.definepsuserparam undef
+ % Save a copy of userparams for use with save/restore
+ % (and, if implemented, context switching).
+ .currentglobal false .setglobal
+ mark .currentuserparams psuserparams { } forall .dicttomark readonly
+ /userparams exch .forcedef % systemdict is read-only
+ .setglobal
+} if
+/.currentsystemparams where {
+ pop
+ % Remove real system params from pssystemparams.
+ mark .currentsystemparams counttomark 2 idiv {
+ pop pssystemparams exch .forceundef
+ } repeat pop
+} if
+
+% Conditionally turn off image interpolation.
+NOINTERPOLATE not { (%END NOINTERPOLATE) .skipeof } if
+/.nointerpolate {
+ dup type /dicttype eq {
+ dup /Interpolate .knownget not { false } if {
+ dup gcheck .currentglobal exch .setglobal
+ exch dup length dict copy
+ dup /Interpolate .undef
+ exch .setglobal
+ } if
+ } if
+} bind odef
+/image { .nointerpolate image } bind odef
+/imagemask { .nointerpolate imagemask } bind odef
+%END NOINTERPOLATE
+
+% Establish local VM as the default.
+false /setglobal where { pop setglobal } { .setglobal } ifelse
+$error /.nosetlocal false put
+
+(END GLOBAL) VMDEBUG
+
+/.savelocalstate where {
+ % If we might create new contexts, save away copies of all dictionaries
+ % referenced from systemdict that are stored in local VM,
+ % and also save a copy of the initial gstate.
+ pop .savelocalstate
+} {
+ % If we're *not* running in a multi-context system and FAKEFONTS is
+ % defined, add the fake fonts to LocalFontDirectory.
+ .definefakefonts % current VM is local
+} ifelse
+
+% Close up systemdict.
+currentdict /filterdict .undef % bound in where needed
+currentdict /.cidfonttypes .undef % ditto
+currentdict /.colorrenderingtypes .undef % ditto
+currentdict /.formtypes .undef % ditto
+currentdict /.imagetypes .undef % ditto
+currentdict /.imagemasktypes .undef % ditto
+currentdict /.patterntypes .undef % ditto
+currentdict /.shadingtypes .undef % ditto
+end
+
+% Clean up VM, and enable GC.
+/vmreclaim where
+ { pop NOGC not { 2 vmreclaim 0 vmreclaim } if
+ } if
+DELAYBIND not {
+ systemdict /.forcedef .undef % remove temptation
+ systemdict /.forceput .undef % ditto
+ systemdict /.forceundef .undef % ditto
+} if
+WRITESYSTEMDICT not { systemdict readonly pop } 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..607d9714f
--- /dev/null
+++ b/pstoraster/gs_iso_e.ps
@@ -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 supports the work of the GNU Project, but is not
+% affiliated with the Free Software Foundation or the GNU Project. GNU
+% Ghostscript, as distributed by Aladdin Enterprises, does not require any
+% GNU software to build or run it.
+
+% $Id: gs_iso_e.ps 956 2000-03-08 23:15:43Z mike $
+% 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..ee4272dd4
--- /dev/null
+++ b/pstoraster/gs_kanji.ps
@@ -0,0 +1,166 @@
+% 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 supports the work of the GNU Project, but is not
+% affiliated with the Free Software Foundation or the GNU Project. GNU
+% Ghostscript, as distributed by Aladdin Enterprises, does not require any
+% GNU software to build or run it.
+
+% $Id: gs_kanji.ps 956 2000-03-08 23:15:43Z mike $
+% 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..e418ea3aa
--- /dev/null
+++ b/pstoraster/gs_ksb_e.ps
@@ -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 supports the work of the GNU Project, but is not
+% affiliated with the Free Software Foundation or the GNU Project. GNU
+% Ghostscript, as distributed by Aladdin Enterprises, does not require any
+% GNU software to build or run it.
+
+% $Id: gs_ksb_e.ps 956 2000-03-08 23:15:43Z mike $
+% 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..fc1294fd3
--- /dev/null
+++ b/pstoraster/gs_l2img.ps
@@ -0,0 +1,192 @@
+% 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 supports the work of the GNU Project, but is not
+% affiliated with the Free Software Foundation or the GNU Project. GNU
+% Ghostscript, as distributed by Aladdin Enterprises, does not require any
+% GNU software to build or run it.
+
+% $Id: gs_l2img.ps 956 2000-03-08 23:15:43Z mike $
+% 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
+ 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..94c490f8d
--- /dev/null
+++ b/pstoraster/gs_lev2.ps
@@ -0,0 +1,717 @@
+% Copyright (C) 1990, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+%
+% This file is part of GNU Ghostscript.
+%
+% GNU Ghostscript is distributed in the hope that it will be useful, but
+% WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+% to anyone for the consequences of using it or for whether it serves any
+% particular purpose or works at all, unless he says so in writing. Refer
+% to the GNU General Public License for full details.
+%
+% Everyone is granted permission to copy, modify and redistribute GNU
+% Ghostscript, but only under the conditions described in the GNU General
+% Public License. A copy of this license is supposed to have been given
+% to you along with GNU Ghostscript so you can know your rights and
+% responsibilities. It should be in a file named COPYING. Among other
+% things, the copyright notice and this notice must be preserved on all
+% copies.
+%
+% Aladdin Enterprises supports the work of the GNU Project, but is not
+% affiliated with the Free Software Foundation or the GNU Project. GNU
+% Ghostscript, as distributed by Aladdin Enterprises, does not require any
+% GNU software to build or run it.
+
+% $Id: gs_lev2.ps 956 2000-03-08 23:15:43Z mike $
+% Initialization file for Level 2 functions.
+% When this is run, systemdict is still writable,
+% but (almost) everything defined here goes into level2dict.
+
+level2dict begin
+
+% ------ System and user parameters ------ %
+
+% User parameters must obey save/restore, and must also be maintained
+% per-context. We implement the former, and some of the latter, here
+% with PostScript code. NOTE: our implementation assumes that user
+% parameters change only as a result of setuserparams -- that there are
+% no user parameters that are ever changed dynamically by the interpreter
+% (although the interpreter may adjust the value presented to setuserparams)
+%
+% There are two types of user parameters: those which are actually
+% maintained in the interpreter, and those which exist only at the
+% PostScript level. We maintain the current state of both types in
+% a read-only local dictionary named userparams, defined in systemdict.
+% In a multi-context system, each context has its own copy of this
+% dictionary. In addition, there is a constant dictionary named
+% psuserparams whose keys are the names of user parameters that exist
+% only in PostScript and whose values are (currently) arbitrary values
+% of the correct datatype: setuserparams uses this for type checking.
+% setuserparams updates userparams explicitly, in addition to setting
+% any user parameters in the interpreter; thus we can use userparams
+% to reset those parameters after a restore or a context switch.
+% NOTE: the name userparams is known to the interpreter, and in fact
+% the interpreter creates the userparams dictionary.
+
+% Check parameters that are managed at the PostScript level.
+% Currently we allow resetting them iff the new value is of the same type.
+/.checksetparams { % <newdict> <opname> <checkdict>
+ % .checksetparams <newdict>
+ 2 index {
+ % Stack: newdict opname checkdict key newvalue
+ 3 copy pop .knownget
+ { type 1 index type ne
+ { pop pop pop load /typecheck signalerror }
+ if
+ dup type /stringtype eq
+ { dup rcheck not
+ { pop pop pop load /invalidaccess signalerror }
+ if
+ }
+ if
+ }
+ if pop pop
+ } forall pop pop
+} .bind def % not odef, shouldn't reset stacks
+
+% currentuser/systemparams creates and returns a dictionary in the
+% current VM. The easiest way to make this work is to copy any composite
+% PostScript-level parameters to global VM. Currently, the only such
+% parameters are strings. In fact, we always copy string parameters,
+% so that we can be sure the contents won't be changed.
+/.copyparam { % <value> .copyparam <value'>
+ dup type /stringtype eq {
+ .currentglobal true .setglobal
+ 1 index length string exch .setglobal
+ copy readonly
+ } if
+} .bind def
+
+% Some user parameters are managed entirely at the PostScript level.
+% We take care of that here.
+systemdict begin
+/psuserparams 40 dict def
+/getuserparam { % <name> getuserparam <value>
+ /userparams .systemvar 1 index get exch pop
+} odef
+% Fill in userparams (created by the interpreter) with current values.
+mark .currentuserparams
+counttomark 2 idiv {
+ userparams 3 1 roll put
+} repeat pop
+/.definepsuserparam { % <name> <value> .definepsuserparam -
+ psuserparams 3 copy pop put
+ userparams 3 1 roll put
+} .bind def
+end
+/currentuserparams { % - currentuserparams <dict>
+ /userparams .systemvar dup length dict .copydict
+} odef
+/setuserparams { % <dict> setuserparams -
+ % Check that we will be able to set the PostScript-level
+ % user parameters.
+ /setuserparams /psuserparams .systemvar .checksetparams
+ % Set the C-level user params. If this succeeds, we know that
+ % the password check succeeded.
+ dup .setuserparams
+ % Now set the PostScript-level params.
+ % The interpreter may have adjusted the values of some of the
+ % parameters, so we have to read them back.
+ dup {
+ /userparams .systemvar 2 index known {
+ psuserparams 2 index known not {
+ pop dup .getuserparam
+ } if
+ .copyparam
+ /userparams .systemvar 3 1 roll .forceput % userparams is read-only
+ } {
+ pop pop
+ } ifelse
+ } forall
+ % A context switch might have occurred during the above loop,
+ % causing the interpreter-level parameters to be reset.
+ % Set them again to the new values. From here on, we are safe,
+ % since a context switch will consult userparams.
+ .setuserparams
+} .bind odef
+% Initialize user parameters managed here.
+/JobName () .definepsuserparam
+
+% Restore must restore the user parameters.
+% (Since userparams is in local VM, save takes care of saving them.)
+/restore { % <save> restore -
+ restore /userparams .systemvar .setuserparams
+} .bind odef
+
+% The pssystemparams dictionary holds some system parameters that
+% are managed entirely at the PostScript level.
+systemdict begin
+currentdict /pssystemparams known not {
+ /pssystemparams 40 dict readonly def
+} if
+/getsystemparam { % <name> getsystemparam <value>
+ //pssystemparams 1 index .knownget { exch pop } { .getsystemparam } ifelse
+} odef
+end
+/currentsystemparams { % - currentsystemparams <dict>
+ mark .currentsystemparams //pssystemparams { } forall .dicttomark
+} odef
+/setsystemparams { % <dict> setsystemparams -
+ % Check that we will be able to set the PostScript-level
+ % system parameters.
+ /setsystemparams //pssystemparams .checksetparams
+ % Set the C-level system params. If this succeeds, we know that
+ % the password check succeeded.
+ dup .setsystemparams
+ % Now set the PostScript-level params. We must copy local strings
+ % into global VM.
+ dup
+ { //pssystemparams 2 index known
+ { % Stack: key newvalue
+ .copyparam
+ //pssystemparams 3 1 roll .forceput % pssystemparams is read-only
+ }
+ { pop pop
+ }
+ ifelse
+ }
+ forall pop
+} .bind odef
+
+% Initialize the passwords.
+% NOTE: the names StartJobPassword and SystemParamsPassword are known to
+% the interpreter, and must be bound to noaccess strings.
+% The length of these strings must be max_password (iutil2.h) + 1.
+/StartJobPassword 65 string noaccess def
+/SystemParamsPassword 65 string noaccess def
+
+% Redefine cache parameter setting to interact properly with userparams.
+/setcachelimit {
+ mark /MaxFontItem 2 index .dicttomark setuserparams pop
+} .bind odef
+/setcacheparams {
+ % The MaxFontCache parameter is a system parameter, which we might
+ % not be able to set. Fortunately, this doesn't matter, because
+ % system parameters don't have to be synchronized between this code
+ % and the VM.
+ counttomark 1 add copy setcacheparams
+ currentcacheparams % mark size lower upper
+ 3 -1 roll pop
+ /MinFontCompress 3 1 roll
+ /MaxFontItem exch
+ .dicttomark setuserparams
+ cleartomark
+} .bind odef
+
+% Add bogus user and system parameters to satisfy badly written PostScript
+% programs that incorrectly assume the existence of all the parameters
+% listed in Appendix C of the Red Book. Note that some of these may become
+% real parameters later: code near the end of gs_init.ps takes care of
+% removing any such parameters from ps{user,system}params.
+
+psuserparams begin
+ /MaxFormItem 100000 def
+ /MaxPatternItem 20000 def
+ /MaxScreenItem 48000 def
+ /MaxUPathItem 5000 def
+end
+
+pssystemparams begin
+ /CurDisplayList 0 .forcedef
+ /CurFormCache 0 .forcedef
+ /CurOutlineCache 0 .forcedef
+ /CurPatternCache 0 .forcedef
+ /CurUPathCache 0 .forcedef
+ /CurScreenStorage 0 .forcedef
+ /CurSourceList 0 .forcedef
+ /DoPrintErrors false .forcedef
+ /MaxDisplayList 140000 .forcedef
+ /MaxFormCache 100000 .forcedef
+ /MaxOutlineCache 65000 .forcedef
+ /MaxPatternCache 100000 .forcedef
+ /MaxUPathCache 300000 .forcedef
+ /MaxScreenStorage 84000 .forcedef
+ /MaxSourceList 25000 .forcedef
+ /RamSize 4194304 .forcedef
+end
+
+% ------ Miscellaneous ------ %
+
+(<<) cvn % - << -mark-
+ /mark load def
+(>>) cvn % -mark- <key1> <value1> ... >> <dict>
+ /.dicttomark load def
+/languagelevel 2 def
+% When running in Level 2 mode, this interpreter is supposed to be
+% compatible with Adobe version 2017.
+/version (2017) readonly def
+
+% If binary tokens are supported by this interpreter,
+% set an appropriate default binary object format.
+/setobjectformat where
+ { pop
+ /RealFormat getsystemparam (IEEE) eq { 1 } { 3 } ifelse
+ /ByteOrder getsystemparam { 1 add } if
+ setobjectformat
+ } if
+
+% ------ Virtual memory ------ %
+
+/currentglobal % - currentglobal <bool>
+ /currentshared load def
+/gcheck % <obj> gcheck <bool>
+ /scheck load def
+/setglobal % <bool> 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
+
+% VMReclaim and VMThreshold are user parameters.
+/setvmthreshold { % <int> setvmthreshold -
+ mark /VMThreshold 2 index .dicttomark setuserparams pop
+} odef
+/vmreclaim { % <int> vmreclaim -
+ dup 0 gt {
+ .vmreclaim
+ } {
+ mark /VMReclaim 2 index .dicttomark setuserparams pop
+ } ifelse
+} odef
+-1 setvmthreshold
+
+% ------ IODevices ------ %
+
+/.getdevparams where {
+ pop /currentdevparams { % <iodevice> currentdevparams <dict>
+ .getdevparams .dicttomark
+ } odef
+} if
+/.putdevparams where {
+ pop /setdevparams { % <iodevice> <dict> setdevparams -
+ mark 1 index { } forall counttomark 2 add index
+ .putdevparams pop pop
+ } 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
+
+end % serverdict
+
+% Because there may be objects on the e-stack created since the job save,
+% we have to clear the e-stack before doing the end-of-job restore.
+% We do this by executing a 2 .stop, which is caught by the 2 .stopped
+% in .runexec; we leave on the o-stack a procedure to execute aftewards.
+%
+%**************** The definition of startjob is not complete yet, since
+% it doesn't reset stdin/stdout.
+/.startnewjob { % <exit_bool> <password_level>
+ % .startnewjob -
+ 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
+ % The Adobe documentation doesn't say what happens to the
+ % graphics state stack in this case, but an experiment
+ % produced results suggesting that a grestoreall occurs.
+ grestoreall
+ } {
+ % Encapsulated job
+ pop
+ serverdict /.jobsave save put
+ serverdict /.jobsavelevel 1 put
+ .userdict /quit /stop load put
+ } ifelse
+ % Reset the interpreter state.
+ clear cleardictstack
+ initgraphics
+ false setglobal
+} bind def
+/.startjob { % <exit_bool> <password> <finish_proc>
+ % .startjob <ok_bool>
+ vmstatus pop pop serverdict /.jobsavelevel get eq
+ 2 index .checkpassword 0 gt and {
+ exch .checkpassword exch count 3 roll count 3 sub { pop } repeat
+ cleardictstack
+ % Reset the e-stack back to the 2 .stopped in .runexec,
+ % passing the finish_proc to be executed afterwards.
+ 2 .stop
+ } { % Password check failed
+ pop pop pop false
+ } ifelse
+} odef
+/startjob { % <exit_bool> <password> startjob <ok_bool>
+ { .startnewjob true } .startjob
+} odef
+
+systemdict begin
+/quit { % - quit -
+ //systemdict begin serverdict /.jobsave get null eq
+ { end //quit }
+ { /quit load /invalidaccess /signalerror load end exec }
+ ifelse
+} bind odef
+end
+
+% We would like to define exitserver as a procedure, using the code
+% that the Red Book says is equivalent to it. However, since startjob
+% resets the exec stack, we can't do this, because control would never
+% proceed past the call on startjob if the exitserver is successful.
+% Instead, we need to construct exitserver out of pieces of startjob.
+
+serverdict begin
+
+/exitserver { % <password> exitserver -
+ true exch { .startnewjob } .startjob not {
+ /exitserver /invalidaccess signalerror
+ } if
+} bind def
+
+end % serverdict
+
+% ------ 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.
+
+/.dict1 { exch mark 3 1 roll .dicttomark } bind def
+
+currentglobal false setglobal 25 dict exch setglobal begin
+currentsystemparams
+
+% The following do not depend on the presence of setpagedevice.
+/buildtime 1 index /BuildTime get def
+/byteorder 1 index /ByteOrder get def
+/checkpassword { .checkpassword 0 gt } bind def
+dup /DoStartPage known
+ { /dostartpage { /DoStartPage getsystemparam } bind def
+ /setdostartpage { /DoStartPage .dict1 setsystemparams } bind def
+ } if
+dup /StartupMode known
+ { /dosysstart { /StartupMode getsystemparam 0 ne } bind def
+ /setdosysstart { { 1 } { 0 } ifelse /StartupMode .dict1 setsystemparams } bind def
+ } if
+%****** Setting jobname is supposed to set userparams.JobName, too.
+/jobname { /JobName getuserparam } bind def
+/jobtimeout { /JobTimeout getuserparam } bind def
+/ramsize { /RamSize getsystemparam } bind def
+/realformat 1 index /RealFormat get def
+dup /PrinterName known
+ { /setprintername { /PrinterName .dict1 setsystemparams } bind def
+ } if
+/printername
+ { currentsystemparams /PrinterName .knownget not { () } if exch copy
+ } bind def
+currentuserparams /WaitTimeout known
+ { /waittimeout { /WaitTimeout getuserparam } bind def
+ } if
+
+% The following do require setpagedevice.
+/.setpagedevice where { pop } { (%END PAGEDEVICE) .skipeof } ifelse
+/defaulttimeouts
+ { currentsystemparams dup
+ /JobTimeout .knownget not { 0 } if
+ exch /WaitTimeout .knownget not { 0 } if
+ currentpagedevice /ManualFeedTimeout .knownget not { 0 } if
+ } bind def
+/margins
+ { currentpagedevice /Margins .knownget { exch } { [0 0] } ifelse
+ } bind def
+/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
+/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
+/.setpagesize { 2 array astore /PageSize .dict1 setpagedevice } bind def
+/setduplexmode { /Duplex .dict1 setpagedevice } bind def
+/setmargins
+ { exch 2 array astore /Margins .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
+%END PAGEDEVICE
+
+% The following are not implemented yet.
+%manualfeed
+%manualfeedtimeout
+%pagecount
+%pagestackorder
+%setpagestackorder
+
+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 .forcedef % statusdict is local, systemdict is global
+
+% ------ Color spaces ------ %
+
+% Define the setcolorspace procedures:
+% <colorspace> proc <colorspace'|null>
+/colorspacedict mark
+ /DeviceGray { pop 0 setgray null } bind
+ /DeviceRGB { pop 0 0 0 setrgbcolor null } bind
+ /setcmykcolor where
+ { pop /DeviceCMYK { pop 0 0 0 1 setcmykcolor null } bind
+ } if
+ /.setcieaspace where
+ { pop /CIEBasedA { NOCIE { pop 0 setgray null } { dup 1 get .setcieaspace } ifelse } bind
+ } if
+ /.setcieabcspace where
+ { pop /CIEBasedABC { NOCIE { pop 0 0 0 setrgbcolor null } { dup 1 get .setcieabcspace } ifelse } bind
+ } if
+ /.setciedefspace where
+ { pop /CIEBasedDEF { NOCIE { pop 0 0 0 setrgbcolor null } { dup 1 get .setciedefspace } ifelse } bind
+ } if
+ /.setciedefgspace where
+ { pop /CIEBasedDEFG { NOCIE { pop 0 0 0 1 setcmykcolor null } { dup 1 get .setciedefgspace } ifelse } bind
+ } if
+ /.setseparationspace where
+ { pop /Separation { dup 2 get setcolorspace dup .setseparationspace } bind
+ } if
+ /.setindexedspace where
+ { pop /Indexed { dup 1 get setcolorspace dup .setindexedspace } bind
+ } if
+ /.nullpatternspace [/Pattern] readonly def
+ /.setpatternspace where
+ { pop /Pattern
+ { dup type /nametype eq { pop //.nullpatternspace } if
+ dup length 1 gt { dup 1 get setcolorspace } if
+ dup .setpatternspace
+ } bind
+ } if
+ /.setdevicenspace where
+ { pop /DeviceN { dup 2 get setcolorspace dup .setdevicenspace } bind
+ } if
+ /.setdevicepixelspace where
+ { pop /DevicePixel { dup .setdevicepixelspace } bind
+ } if
+ currentdict /.nullpatternspace .undef
+.dicttomark def
+
+/.devcs [
+ /DeviceGray /DeviceRGB /DeviceCMYK /DevicePixel
+] readonly def
+/currentcolorspace { % - currentcolorspace <array>
+ .currentcolorspace dup type /integertype eq {
+ //.devcs exch 1 getinterval
+ } if
+} odef
+currentdict /.devcs .undef
+
+/setcolorspace { % <name|array> setcolorspace -
+ dup dup dup type /nametype ne { 0 get } if
+ //colorspacedict exch get exec
+ dup null eq { pop } { .setcolorspace } ifelse pop
+} odef
+
+% ------ CIE color rendering ------ %
+
+% Define findcolorrendering and a default ColorRendering ProcSet.
+
+/findcolorrendering { % <intentname> findcolorrendering
+ % <crdname> <found>
+ /ColorRendering /ProcSet findresource
+ 1 index .namestring (.) concatstrings
+ 1 index /GetPageDeviceName get exec .namestring (.) concatstrings
+ 2 index /GetHalftoneName get exec .namestring
+ concatstrings concatstrings
+ dup /ColorRendering resourcestatus {
+ pop pop exch pop exch pop true
+ } {
+ pop /GetSubstituteCRD get exec false
+ } ifelse
+} odef
+
+5 dict dup begin
+
+/GetPageDeviceName { % - GetPageDeviceName <name>
+ currentpagedevice dup /PageDeviceName .knownget {
+ exch pop
+ } {
+ pop /none
+ } ifelse
+} bind def
+
+/GetHalftoneName { % - GetHalftoneName <name>
+ currenthalftone /HalftoneName .knownget not { /none } if
+} bind def
+
+/GetSubstituteCRD { % <intentname> GetSubstituteCRD <crdname>
+ pop /DefaultColorRendering
+} bind def
+
+end
+% The resource machinery hasn't been activated, so just save the ProcSet
+% and let .fixresources finish the installation process.
+/ColorRendering exch def
+
+% Define setcolorrendering.
+
+/.colorrenderingtypes 5 dict def
+
+/setcolorrendering { % <crd> setcolorrendering -
+ dup /ColorRenderingType get //.colorrenderingtypes exch get exec
+} odef
+
+/.setcolorrendering1 where { pop } { (%END CRD) .skipeof } ifelse
+
+.colorrenderingtypes 1 {
+ dup .buildcolorrendering1 .setcolorrendering1
+} .bind put
+
+% Initialize the default CIE rendering dictionary.
+% 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.
+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] readonly
+ /TransformPQR
+ [ {exch pop exch pop exch pop exch pop} bind dup dup ] readonly
+ /RangeLMN [0 0.9505 0 1 0 1.0890] readonly
+ /MatrixABC
+ [ 3.24063 -0.96893 0.05571
+ -1.53721 1.87576 -0.20402
+ -0.49863 0.04152 1.05700
+ ] readonly
+ /EncodeABC [ {0 .max 0.45 exp} bind dup dup] readonly
+ /WhitePoint [0.9505 1 1.0890] readonly
+ % Some Genoa tests seem to require the presence of BlackPoint.
+ /BlackPoint [0 0 0] readonly
+.dicttomark setcolorrendering
+
+%END CRD
+
+% Initialize a CIEBased color space for sRGB.
+/CIEsRGB [ /CIEBasedABC
+ mark
+ /DecodeLMN [ {
+ dup 0.03928 le { 12.92321 div } { 0.055 add 1.055 div 2.4 exp } ifelse
+ } bind dup dup ] readonly
+ /MatrixLMN [
+ 0.412457 0.212673 0.019334
+ 0.357576 0.715152 0.119192
+ 0.180437 0.072175 0.950301
+ ] readonly
+ /WhitePoint [0.9505 1.0 1.0890] readonly
+ .dicttomark readonly
+] readonly def
+
+% ------ Painting ------ %
+
+% A straightforward definition of execform that doesn't actually
+% do any caching.
+/.execform1 {
+ % This is a separate operator so that the stacks will be restored
+ % properly if an error occurs.
+ dup /Matrix get concat
+ dup /BBox get aload pop
+ exch 3 index sub exch 2 index sub rectclip
+ dup /PaintProc get
+ 1 index /Implementation known not {
+ 1 index dup /Implementation null .forceput readonly pop
+ } if
+ exec
+} .bind odef % must bind .forceput
+
+/.formtypes 5 dict
+ dup 1 /.execform1 load put
+def
+
+/execform { % <form> execform -
+ gsave {
+ dup /FormType get //.formtypes exch get exec
+ } stopped grestore { stop } if
+} odef
+
+/.patterntypes 5 dict
+ dup 1 /.buildpattern1 load put
+def
+
+/makepattern { % <proto_dict> <matrix> makepattern <pattern>
+ //.patterntypes 2 index /PatternType get get
+ .currentglobal false .setglobal exch
+ % Stack: proto matrix global buildproc
+ 3 index dup length 1 add dict .copydict
+ 3 index 3 -1 roll exec 3 -1 roll .setglobal
+ 1 index /Implementation 3 -1 roll put
+ readonly exch pop exch pop
+} odef
+
+/setpattern { % [<comp1> ...] <pattern> setpattern -
+ currentcolorspace 0 get /Pattern ne {
+ [ /Pattern currentcolorspace ] setcolorspace
+ } if setcolor
+} odef
+
+% Extend image and imagemask to accept dictionaries.
+% We must create .imagetypes and .imagemasktypes outside level2dict,
+% and leave some extra space because we're still in Level 1 mode.
+systemdict begin
+/.imagetypes 5 dict
+ dup 1 /.image1 load put
+def
+/.imagemasktypes 5 dict
+ dup 1 /.imagemask1 load put
+def
+end
+
+/.image /image load def
+/image {
+ dup type /dicttype eq {
+ dup /ImageType get //.imagetypes exch get exec
+ } {
+ //.image
+ } ifelse
+} odef
+currentdict /.image undef
+
+/.imagemask /imagemask load def
+/imagemask {
+ dup type /dicttype eq {
+ dup /ImageType get //.imagemasktypes exch get exec
+ } {
+ //.imagemask
+ } ifelse
+} odef
+currentdict /.imagemask undef
+
+end % level2dict
diff --git a/pstoraster/gs_ll3.ps b/pstoraster/gs_ll3.ps
new file mode 100644
index 000000000..acf701ec6
--- /dev/null
+++ b/pstoraster/gs_ll3.ps
@@ -0,0 +1,387 @@
+% Copyright (C) 1997, 1998 Aladdin Enterprises. All rights reserved.
+%
+% This file is part of GNU Ghostscript.
+%
+% GNU Ghostscript is distributed in the hope that it will be useful, but
+% WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+% to anyone for the consequences of using it or for whether it serves any
+% particular purpose or works at all, unless he says so in writing. Refer
+% to the GNU General Public License for full details.
+%
+% Everyone is granted permission to copy, modify and redistribute GNU
+% Ghostscript, but only under the conditions described in the GNU General
+% Public License. A copy of this license is supposed to have been given
+% to you along with GNU Ghostscript so you can know your rights and
+% responsibilities. It should be in a file named COPYING. Among other
+% things, the copyright notice and this notice must be preserved on all
+% copies.
+%
+% Aladdin Enterprises supports the work of the GNU Project, but is not
+% affiliated with the Free Software Foundation or the GNU Project. GNU
+% Ghostscript, as distributed by Aladdin Enterprises, does not require any
+% GNU software to build or run it.
+
+% $Id: gs_ll3.ps 956 2000-03-08 23:15:43Z mike $
+% Initialization file for PostScript LanguageLevel 3 functions.
+% Essentially all of these are stubs right now.
+% This file must be loaded after gs_lev2.ps and gs_res.ps.
+% These definitions go into ll3dict or various ProcSets.
+% NOTE: the interpreter creates ll3dict.
+
+ll3dict begin
+
+% We need LanguageLevel 2 or higher in order to have setuserparams and
+% defineresource.
+languagelevel dup 2 max .setlanguagelevel
+
+% ------ Idiom recognition ------ %
+
+/IdiomRecognition false .definepsuserparam
+
+% Modify `bind' to apply idiom recognition afterwards.
+/.bindscratch 128 string def
+% Do the right thing if NOBIND or DELAYBIND is in effect.
+% Note also that since this definition of `bind' may get bound in,
+% it has to function properly even at lower language levels,
+% where IdiomRecognition may not be defined.
+/bind load /.bind load ne
+/bind { % <proc> bind <proc'>
+ //.bind currentuserparams /IdiomRecognition
+ .knownget not { false } if {
+ (*) {
+ /IdiomSet findresource
+ false exch {
+ % Stack: proc false dummykey [template substitute]
+ exch pop dup 1 get exch 0 get
+ % Stack: proc false substitute template
+ 3 index .eqproc {
+ 2 index gcheck 1 index gcheck not and {
+ pop
+ } {
+ 3 -1 roll pop exch not exit
+ } ifelse
+ } {
+ pop
+ } ifelse
+ } forall { exit } if
+ } //.bindscratch /IdiomSet resourceforall
+ } if
+} odef
+{ /.bind /bind load def
+ /bind { } def
+} if
+currentdict /.bindscratch .undef
+
+% ------ HalftoneTypes 6, 10, 16 ------ %
+
+% This code depends on one new operator:
+%
+% <dict> <Width> <Height> <Thresholds> <bits> <shift> .setstriphalftone -
+%
+% <dict> is the dictionary that will be returned by .currenthalftone.
+% The operator only looks at the TransferFunction entry.
+% Width, Height: as for HalftoneType 3.
+% Thresholds: a BigStringEncode filter holding the thresholds,
+% Width x Height x BitsPerSample / 8 bytes.
+% shift: the amount of X shift per Y repetition of the halftone,
+% 0 <= Shift < Width.
+% bits: bits per sample, 8 or 16.
+%
+% Eventually the code below will have to get hooked up to sethalftone
+% and currenthalftone....
+
+/.copybytes { % <source> <dest> <count> .copybytes -
+ { 1 index read not { /sethalftone load /rangecheck signalerror exit } if
+ 1 index exch write
+ } repeat pop pop
+} bind def
+
+/.copythresholds { % <dict> <Width> <Height> <bits> .copythresholds -
+ dup 8 idiv 3 index mul 2 index mul
+ dup /BigStringEncode filter 3 1 roll
+ % Stack: dict width height dest bits nbytes
+ 5 index /Thresholds get 3 index 3 -1 roll .copybytes
+ 1 index closefile
+ 0 .setstriphalftone
+} bind def
+
+/.sethalftone6 { % <dict> .sethalftone6 -
+ % Keys: Width, Height, Thresholds, T'Function
+ dup /Width get 1 index /Height get
+ 8 .copythresholds
+} odef
+
+/.copythresholds2 { % <dict> <Width> <Height> <Width2> <Height2>
+ % <bits> .copythresholds2 -
+% The block height B is gcd(Height, Height2).
+ 3 index 2 index {
+ 2 copy lt { exch } if dup 1 eq { pop exit } if exch 1 index mod
+ } loop
+% The raster R is (Width * Height + Width2 * Height2) / B * bits/8.
+ 5 index 5 index mul 4 index 4 index mul add 1 index idiv
+ 2 index 8 idiv mul
+% Currently I don't know how to compute the stride.
+% ****** COMPUTE THE STRIDE SOMEHOW ******
+% Push additional arguments onto the stack.
+ 1 index 1 index mul /BigStringEncode filter 4 1 roll
+ 9 index /Thresholds get
+ % Stack: dict width height width2 height2 bits
+ % dest B R stride source
+% For the first rectangle, the number of blocks is Height / B;
+% the offset is 0.
+ 5 copy 14 index 5 1 roll
+ 14 index 5 index idiv 4 1 roll
+ 0 exch .copyshifted
+% For the second rectangle, the number of blocks is Height2 / B;
+% the offset is Width.
+ 5 copy 12 index 5 1 roll
+ 12 index 4 index idiv 4 1 roll
+ 16 index exch .copyshifted
+ % Stack: dict width height width2 height2 bits
+ % dest B R stride source
+ % We want: dict R/(bits/8) B dest bits stride
+ pop exch 4 index 8 idiv idiv 4 1 roll
+ % R/(bits/8) dest B stride
+ exch 3 1 roll 5 -1 roll exch
+ 9 -4 roll 4 { pop } repeat
+ .setstriphalftone
+} bind def
+
+% Copy a shifted rectangular threshold array into a BigStringEncode filter.
+% Note that the width and shift are in bytes, not samples.
+/.copyshifted { % <dest> <width> <B> <N> <R> <stride> <offset>
+ % <source> .copyshifted -
+% Copy N blocks of <width> x B bytes from <source>.
+% Row Y (0 <= Y < B) in group G (0 <= G < N) must get copied to byte position
+% Y * R + (G * stride + offset) mod R
+% in the destination.
+ 1 index % Stack: ... rowstart
+ 6 index { % iterate over rows within a block
+ 5 index { % iterate over blocks
+ 8 index 1 index setfileposition
+ 1 index 9 index 9 index .copybytes
+ 4 index add % + raster
+ } repeat % end block
+ 3 index add 4 index mod % + stride, mod raster
+ } repeat % end row in block
+ 9 { pop } repeat
+} bind def
+
+/.sethalftone10 { % <dict> .sethalftone10 -
+ % Keys: XSquare, YSquare, Thresholds, T'Function
+% ****** DOESN'T HANDLE STRING SOURCE ******
+ dup /XSquare get dup 2 index /YSquare get dup
+ 8 .copythresholds2
+} odef
+
+/.sethalftone16 { % <dict> .sethalftone16 -
+ % Keys: Width, Height, Width2, Height2,
+ % Thresholds, T'Function
+ dup /Width get 1 index /Height get
+ 2 index /Width2 .knownget { % 2-rectangle case
+ 3 index /Height2 get
+ 16 .copythresholds2
+ } { % 1-rectangle case
+ 16 .copythresholds
+ } ifelse
+} odef
+
+{6 10 16} { dup /HalftoneType defineresource pop } forall
+
+% ------ ImageTypes 3 and 4 (masked images) ------ %
+
+.imagetypes
+ dup 3 /.image3 load put
+ 4 /.image4 load put
+
+% ------ Functions ------ %
+
+% Define the FunctionType resource category.
+/Generic /Category findresource dup maxlength 3 add dict .copydict begin
+ /InstanceType /integertype def
+/FunctionType currentdict end /Category defineresource pop
+
+{0 2 3} { dup /FunctionType defineresource pop } forall
+
+% ------ Smooth shading ------ %
+
+% Define the ShadingType resource category.
+/Generic /Category findresource dup maxlength 3 add dict .copydict begin
+ /InstanceType /integertype def
+/ShadingType currentdict end /Category defineresource pop
+
+systemdict /.shadingtypes mark % not ll3dict
+ 1 /.buildshading1 load
+ 2 /.buildshading2 load
+ 3 /.buildshading3 load
+ 4 /.buildshading4 load
+ 5 /.buildshading5 load
+ 6 /.buildshading6 load
+ 7 /.buildshading7 load
+.dicttomark put
+
+/.buildshading { % <shadingdict> .buildshading <shading>
+ % The .buildshading operators use the current color space
+ % for ColorSpace.
+ dup /ShadingType get //.shadingtypes exch get
+ 1 index /ColorSpace get gsave { setcolorspace exec } stopped
+ grestore { stop } if
+} bind def
+/.buildpattern2 { % <template> <matrix> .buildpattern2
+ % <template> <pattern>
+ 1 index /Shading get .buildshading .buildshadingpattern
+} bind def
+
+.patterntypes
+ 2 /.buildpattern2 load put
+
+/shfill { % <shadingdict> shfill -
+ % Currently, .shfill requires that the color space
+ % in the pattern be the current color space.
+ dup .buildshading
+ 1 index /ColorSpace get
+ gsave { setcolorspace .shfill } stopped grestore { stop } if
+ pop
+} odef
+
+% Establish an arbitrary initial smoothness value.
+1 64 div setsmoothness
+
+% ------ Trapping ------ %
+
+% The PostScript-level trapping parameters are maintained in userdict,
+% and explicitly reinstalled upon restore.
+
+/Trapping mark
+
+/settrapparams dup { % <paramdict> settrapparams -
+ /.trapparams .uservar dup length dict .copydict
+ dup 2 index {
+ % Stack: paramdict olddict olddict key value
+ 2 index 2 index known { put dup } { pop pop } ifelse
+ } forall pop
+ dup .settrapparams % Let the operator check parameter validity.
+ .userdict /.trapparams 3 -1 roll put pop
+} bind .makeoperator
+
+/.copyparams { % <obj> .copyparams <obj'>
+ dup type /dicttype eq {
+ dup length dict .copydict
+ dup {
+ .copyparams 3 copy put pop pop
+ } forall
+ } {
+ dup type /arraytype eq {
+ [ exch { .copyparams } forall ]
+ } if
+ } ifelse
+} odef
+
+/currenttrapparams dup { % - currenttrapparams <paramdict>
+ /.trapparams .uservar .copyparams
+} bind .makeoperator
+
+/settrapzone dup { % - settrapzone -
+ % ****** DUMMY ******
+ newpath
+} bind .makeoperator
+
+% Define initial (dummy) trapping parameters.
+% These values are mostly complete guesses.
+userdict /.trapparams mark
+ /BlackColorLimit 1.0
+ /BlackDensityLimit 1.0
+ /BlackWidth 1.0
+ /ColorantZoneDetails 0 dict
+ /Enabled true
+ /HalftoneName null
+ /ImageInternalTrapping false
+ /ImageResolution 1
+ /ImageToObjectTrapping true
+ /ImageTrapPlacement /Center
+ /SlidingTrapLimit 1.0
+ /StepLimit 1.0
+ /TrapColorScaling 0.0
+ /TrapSetName null
+ /TrapWidth 1.0
+.dicttomark readonly put
+
+.dicttomark /ProcSet defineresource pop
+
+% ------ Miscellaneous ------ %
+
+% Define additional user and system parameters.
+psuserparams begin
+ /HalftoneMode 0 def
+ /MaxSuperScreen 1016 def
+end
+pssystemparams begin % read-only, so use .forcedef
+ /MaxDisplayAndSourceList 160000 .forcedef
+end
+
+% Define the IdiomSet, InkParams, and TrapParams resource categories.
+{ /IdiomSet /InkParams /TrapParams } {
+ /Generic /Category findresource dup maxlength 3 add dict .copydict begin
+ /InstanceType /dicttype def
+ currentdict end /Category defineresource pop
+} forall
+
+% Define the ReusableStreamDecode filter.
+% ****** DOESN'T WORK FOR CONTENTS >64K ******
+/.reusablestreamdecode { % <source> <dict> .reusablestreamdecode <file>
+ % <source> .reusablestreamdecode <file>
+ % Collect the filter parameters.
+ dup type /dicttype eq { 2 copy } { dup 0 dict } ifelse
+ dup .rsdparams
+ % Construct the filter pipeline.
+ % The very first filter should use the value of CloseSource
+ % from the RSD dictionary; all the others should have
+ % CloseSource = true.
+ % Stack: source dict filters parms
+ 2 index /CloseSource .knownget not { false } if 5 -1 roll
+ % Stack: dict filters parms CloseSource source
+ 0 1 5 index length 1 sub {
+ 4 index 1 index get
+ % Stack: dict filters parms CloseSource source index filtname
+ 4 index null eq {
+ 0 dict
+ } {
+ 4 index 2 index get dup null eq { pop } if
+ } ifelse
+ 3 -1 roll pop filter
+ exch pop true exch % set CloseSource for further filters
+ } for
+ % See if we can create the filter directly.
+ % Stack: dict filters parms CloseSource file
+ null 2 index { .reusablestream } .internalstopped {
+ pop pop
+ % No luck. Read the entire contents of the stream now.
+ 10 dict exch {
+ % Stack: dict filters parms CloseSource contdict file
+ dup 1000 string readstring
+ 3 index dup length 4 -1 roll put not { break } if
+ } loop pop
+ % Concatenate the contents into one big string.
+ % Stack: dict filters parms CloseSource contdict
+ 0 1 index { length exch pop add } forall string
+ exch {
+ % Stack: dict filters parms CloseSource string index substring
+ exch 1000 mul exch 2 index 3 1 roll putinterval
+ } forall
+ % Now create the stream on the string.
+ null 3 -1 roll .reusablestream
+ } if
+ % We created the stream successfully: clean up.
+ 4 { exch pop } repeat
+ dup type /dicttype eq { pop } if pop
+} odef
+filterdict /ReusableStreamDecode /.reusablestreamdecode load put
+
+/languagelevel 3 def
+% When running in LanguageLevel 3 mode, this interpreter is supposed to be
+% compatible with Adobe version 3010.
+/version (3010) readonly def
+
+.setlanguagelevel
+
+end % ll3dict
diff --git a/pstoraster/gs_mex_e.ps b/pstoraster/gs_mex_e.ps
new file mode 100644
index 000000000..7724de22d
--- /dev/null
+++ b/pstoraster/gs_mex_e.ps
@@ -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 supports the work of the GNU Project, but is not
+% affiliated with the Free Software Foundation or the GNU Project. GNU
+% Ghostscript, as distributed by Aladdin Enterprises, does not require any
+% GNU software to build or run it.
+
+% $Id: gs_mex_e.ps 956 2000-03-08 23:15:43Z mike $
+% 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..87fdf896f
--- /dev/null
+++ b/pstoraster/gs_mro_e.ps
@@ -0,0 +1,65 @@
+% 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 supports the work of the GNU Project, but is not
+% affiliated with the Free Software Foundation or the GNU Project. GNU
+% Ghostscript, as distributed by Aladdin Enterprises, does not require any
+% GNU software to build or run it.
+
+% $Id: gs_mro_e.ps 956 2000-03-08 23:15:43Z mike $
+% 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_e.ps b/pstoraster/gs_pdf_e.ps
new file mode 100644
index 000000000..7a78c1d9b
--- /dev/null
+++ b/pstoraster/gs_pdf_e.ps
@@ -0,0 +1,52 @@
+% Copyright (C) 1994, 1997 Aladdin Enterprises. All rights reserved.
+%
+% This file is part of GNU Ghostscript.
+%
+% GNU Ghostscript is distributed in the hope that it will be useful, but
+% WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+% to anyone for the consequences of using it or for whether it serves any
+% particular purpose or works at all, unless he says so in writing. Refer
+% to the GNU General Public License for full details.
+%
+% Everyone is granted permission to copy, modify and redistribute GNU
+% Ghostscript, but only under the conditions described in the GNU General
+% Public License. A copy of this license is supposed to have been given
+% to you along with GNU Ghostscript so you can know your rights and
+% responsibilities. It should be in a file named COPYING. Among other
+% things, the copyright notice and this notice must be preserved on all
+% copies.
+%
+% Aladdin Enterprises supports the work of the GNU Project, but is not
+% affiliated with the Free Software Foundation or the GNU Project. GNU
+% Ghostscript, as distributed by Aladdin Enterprises, does not require any
+% GNU software to build or run it.
+
+% $Id: gs_pdf_e.ps 956 2000-03-08 23:15:43Z mike $
+% 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 5 getinterval aload pop
+ /hyphen
+ISOLatin1Encoding 46 50 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..3cd667c25
--- /dev/null
+++ b/pstoraster/gs_pdfwr.ps
@@ -0,0 +1,411 @@
+% Copyright (C) 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+%
+% This file is part of GNU Ghostscript.
+%
+% GNU Ghostscript is distributed in the hope that it will be useful, but
+% WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+% to anyone for the consequences of using it or for whether it serves any
+% particular purpose or works at all, unless he says so in writing. Refer
+% to the GNU General Public License for full details.
+%
+% Everyone is granted permission to copy, modify and redistribute GNU
+% Ghostscript, but only under the conditions described in the GNU General
+% Public License. A copy of this license is supposed to have been given
+% to you along with GNU Ghostscript so you can know your rights and
+% responsibilities. It should be in a file named COPYING. Among other
+% things, the copyright notice and this notice must be preserved on all
+% copies.
+%
+% Aladdin Enterprises supports the work of the GNU Project, but is not
+% affiliated with the Free Software Foundation or the GNU Project. GNU
+% Ghostscript, as distributed by Aladdin Enterprises, does not require any
+% GNU software to build or run it.
+
+% $Id: gs_pdfwr.ps 956 2000-03-08 23:15:43Z mike $
+% 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.
+% We use a pseudo-parameter named /pdfmark whose value is an array:
+% key1 value1 ... CTM /type
+/.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 { % -mark- <key1> <value1> ... .pdfputparams <result...>
+ currentdevice null false counttomark 1 add 3 roll
+ % Don't allow the page device to get cleared....
+ {.putdeviceparams} .currentpagedevice pop {.setpagedevice} 3 .execn
+} bind def
+% The procedures in the following dictionary are called with the entire
+% pdfmark operand list (including the pdfmark name) on the stack;
+% they may modify this ad lib. They should return true if all operands
+% should be converted with .pdfcvs, false if the operands are key/value
+% pairs and only the even-numbered ones should be converted.
+/.pdfmarkparams mark
+ /SP { true }
+ /CLOSE { true }
+ % Unpack a dictionary for PUT.
+ % Eventually we should do this for streams too.
+ /PUT {
+ counttomark 3 eq {
+ 1 index type /dicttype eq { pop { } forall /.PUTDICT } if
+ } if true
+ } bind
+ % Unpack the array for PUTINTERVAL.
+ /PUTINTERVAL {
+ pop aload pop /.PUTINTERVAL true
+ } bind
+.dicttomark readonly def
+/pdfmark % -mark- <key> <value> ... <markname> pdfmark -
+ { counttomark 1 add copy
+ //.pdfmarkparams 1 index .knownget { exec } { false } ifelse
+ matrix currentmatrix .pdfcvs 3 1 roll
+ counttomark 1 add 1 roll ] exch
+ { 0 1 } { 1 2 } ifelse
+ 2 index length 3 sub { 2 copy 2 copy get .pdfcvs put pop } for
+ mark /pdfmark 3 -1 roll .pdfputparams
+ type /booleantype ne { cleartomark } if cleartomark
+ } odef
+userdict /pdfmark .undef
+currentdict /.pdfmarkparams .undef
+
+% Define setdistillerparams / currentdistillerparams.
+% Distiller parameters are currently treated as device parameters.
+/.distillerparamkeys mark
+ % General parameters
+ /ASCII85EncodePages { }
+ /AutoRotatePages { }
+ /CompatibilityLevel { }
+ /CompressPages { }
+ /CoreDistVersion { }
+ /ImageMemory { }
+ /LZWEncodePages { }
+ /PreserveHalftoneInfo { }
+ /PreserveOPIComments { }
+ /PreserveOverprintSettings { }
+ /TransferFunctionInfo { }
+ /UCRandBGInfo { }
+ /UseFlateCompression { }
+ % Color sampled image parameters
+ /ColorACSDict { }
+ /AntiAliasColorImages { }
+ /AutoFilterColorImages { }
+ /ColorImageDepth { }
+ /ColorImageDict { }
+ /DownsampleColorImages { }
+ /ColorImageDownsampleType { }
+ /EncodeColorImages { }
+ /ColorImageFilter { }
+ /ColorImageResolution { }
+ /ColorConversionStrategy { }
+ /ConvertCMYKImagesToRGB { }
+ /ConvertImagesToIndexed { }
+ % Grayscale sampled image parameters
+ /GrayACSDict { }
+ /AntiAliasGrayImages { }
+ /AutoFilterGrayImages { }
+ /GrayImageDepth { }
+ /GrayImageDict { }
+ /DownsampleGrayImages { }
+ /GrayImageDownsampleType { }
+ /EncodeGrayImages { }
+ /GrayImageFilter { }
+ /GrayImageResolution { }
+ % Monochrome sampled image parameters
+ /AntiAliasMonoImages { }
+ /MonoImageDepth { }
+ /MonoImageDict { }
+ /DownsampleMonoImages { }
+ /MonoImageDownsampleType { }
+ /EncodeMonoImages { }
+ /MonoImageFilter { }
+ /MonoImageResolution { }
+ % 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 4 index
+ { //.distillerparamkeys 2 index .knownget { exec } { pop pop } ifelse }
+ forall .putdeviceparams
+ type /booleantype eq { pop } { cleartomark pop pop pop } ifelse pop
+ } odef
+/currentdistillerparams % - currentdistillerparams <dict>
+ { .distillerdevice //.distillerparamkeys .getdeviceparams .dicttomark
+ } odef
+
+% Patch the 'show' operators to pass the data to the device.
+% We use pseudo-parameters named /show and /showxxx to pass the data,
+% as documented in gdevpdft.c. THIS IS A HACK.
+%
+% Note that the cx/y and ax/y values are in text space, not user space.
+% We also fill an empty path in order to get the clipping region and
+% current color set properly. THIS IS A BIG HACK.
+/.pdfknownfonts mark
+ /Courier {StandardEncoding}
+ /Courier-Bold 1 index
+ /Courier-Oblique 1 index
+ /Courier-BoldOblique 1 index
+ /Helvetica 1 index
+ /Helvetica-Bold 1 index
+ /Helvetica-Oblique 1 index
+ /Helvetica-BoldOblique 1 index
+ /Times-Roman 1 index
+ /Times-Bold 1 index
+ /Times-Italic 1 index
+ /Times-BoldItalic 1 index
+ /Symbol {SymbolEncoding}
+ /ZapfDingbats {DingbatsEncoding}
+.dicttomark readonly def
+.currentglobal false .setglobal
+/.pdfknownids 50 dict .forcedef % .pdfknownids is local, systemdict is global
+.setglobal
+/.findorigfont { % <font> .findorigfont <origfont>
+ % Check for a known font with the same name,
+ % or one of the 14 known names,
+ % and the same UniqueID.
+ dup /UniqueID .knownget {
+ .pdfknownids 1 index .knownget {
+ exch pop dup null ne { true } { pop false } ifelse
+ } { % We haven't looked up the UniqueID yet.
+ % Register the UniqueIDs of all fonts.
+ .FontDirectory {
+ exch pop dup /UniqueID .knownget {
+ % Stack: font uniqueid somefont somefontid
+ exch .pdfknownids 3 1 roll put
+ } {
+ pop
+ } ifelse
+ } forall
+ % Register the UniqueIDs of the 14 built-in fonts,
+ % to make sure they override any other fonts
+ % with the same UniqueIDs.
+ .pdfknownfonts {
+ pop
+ //systemdict /GlobalFontDirectory .knownget not { .FontDirectory } if
+ 1 index .knownget {
+ % Stack: font uniqueid knownname knownfont
+ dup /UniqueID get exch .pdfknownids 3 1 roll put
+ } if pop
+ } forall
+ % Now see if the UniqueID is known.
+ .pdfknownids 1 index .knownget {
+ exch pop true
+ } { % Record the UniqueID as not registered.
+ .pdfknownids exch null put false
+ } ifelse
+ } ifelse
+ } { % This font has no UniqueID.
+ false
+ } ifelse
+ % Stack: font knownfont -true- | font -false-
+ {
+ exch pop
+ } {
+ { dup /OrigFont .knownget not { exit } if exch pop } loop
+ } ifelse
+} .bind def
+/.pdfdoshow { % <string> -mark- {<cxd> <cyd> <char>} {<axd> <ayd>}
+ % .pdfdoshow <bool>
+%vmstatus pop =only pop (, ) print
+ ] /showValues exch
+%vmstatus pop =only pop (, ) print
+ mark /showX currentpoint transform /showY exch /show 9 -3 roll
+ currentfont .findorigfont
+ % Pass the original font name.
+ dup /FontName get
+ /showFontName exch 3 -1 roll
+ % Concatenate the "quotient" of the current FontMatrix
+ % and the FontMatrix of the original font.
+ % Be sure to include any translation.
+ /showMatrix
+ exch /FontMatrix get matrix invertmatrix
+ currentfont /FontMatrix get
+ 1 index concatmatrix
+ % The matrix on the stack is now the font scaling matrix.
+ % Use it to inverse-transform cx/y and ax/y, so they will be in
+ % text space.
+ % Stack: -mark- /showX <px> /showY <py> /show <string>
+ % /showValues <values> /showFontName <fontname> /showMatrix <matrix>
+ 4 index aload % ... {cx cy char} {ax ay} values
+ dup length {
+ {pop 6 -2 roll pop pop}
+ 1?
+ {3 -2 roll 3 index idtransform 3 2 roll astore pop}
+ {4 -2 roll 4 index idtransform 4 2 roll astore pop}
+ 4?
+ {6 -2 roll 6 index idtransform 6 2 roll
+ 3 -2 roll 6 index idtransform 3 2 roll astore pop}
+ } exch get exec
+%vmstatus pop =only pop (, ) print
+ % Now construct the combined matrix.
+ % This is equivalent to:
+ % matrix currentmatrix dup 4 0 put dup 5 0 put dup concatmatrix
+ % but avoids allocating another matrix.
+ aload 7 1 roll
+ dtransform 7 2 roll dtransform 7 2 roll dtransform 7 2 roll
+ astore
+ % Omit either encoding that is StandardEncoding.
+ /showEncoding currentfont /Encoding .knownget not { [] } if
+ dup StandardEncoding eq { pop pop } if
+ % Make a reasonable guess at the base encoding.
+ /showBaseEncoding .pdfknownfonts 6 index % FontName
+ .knownget { exec } { StandardEncoding } ifelse
+ dup StandardEncoding eq {
+ pop pop
+ } {
+ currentfont /CharStrings .knownget { /showCharStrings exch } if
+ } ifelse
+%vmstatus pop =only pop (, ) print
+ % Set the clipping region and color in the output.
+ % This is another hack!
+ gsave newpath fill grestore
+%vmstatus pop =only pop (, ) print
+ .pdfputparams
+%vmstatus pop =only pop () = flush
+ 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 -
+ { % Prevent output, but don't switch devices, since this
+ % confuses the Pattern caching mechanism.
+ gsave currentpoint newpath clip moveto exec currentpoint
+ grestore moveto
+ } .bind def
+/.pdfwrite? % - .pdfwrite? <bool>
+ { currentdevice .devicename /pdfwrite eq
+ currentfont /FontType get 1 eq and
+ } .bind def
+% .pdfshow isn't an operator per se, but it still needs to be careful with
+% the stack so that the operators will have stack protection.
+/.pdfshow % <string> -mark- {<cx> <cy> <char>} {<ax> <ay>}
+ % <showproc> .pdfshow -
+ { counttomark 2 add 1 roll .pdfwrite?
+ { .pdfdoshow }
+ { cleartomark pop false }
+ ifelse
+ { .pdfexecshow }
+ { exec }
+ ifelse
+ } .bind def
+/show
+ { dup mark { show } .pdfshow
+ } .bind odef
+/ashow
+ { dup mark 4 index 4 index { ashow } .pdfshow
+ } .bind odef
+/widthshow
+ { 4 copy mark 5 2 roll { widthshow } .pdfshow
+ } .bind odef
+/awidthshow
+ { 6 copy mark 7 2 roll { awidthshow } .pdfshow
+ } .bind odef
+/glyphshow where { pop } { (%END) .skipeof } ifelse
+/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 3 index put show pop }
+ { glyphshow }
+ ifelse
+ }
+ { glyphshow
+ }
+ ifelse
+ } .bind odef
+%END
+/.kshow1 { % <index> <proc> <string> .kshow1
+ (X) dup 0 3 index 6 index get put show
+ 2 index 1 index length 1 sub lt {
+ dup 3 index get exch 4 -1 roll 1 add get
+ 3 -1 roll exec
+ } {
+ pop pop pop
+ } ifelse
+} .bind def
+/kshow {
+ .pdfwrite? {
+ % Construct a closure, and work character by character.
+ 0 1 2 index length 1 sub 5 -2 roll
+ //.kshow1 /exec cvx 4 packedarray cvx for
+ } {
+ kshow
+ } ifelse
+} .bind odef
+% The remaining operators aren't implemented correctly.
+/xshow where {
+ pop /xshow { .pdfwrite? { 1 index show pop pop } { xshow } ifelse } .bind odef
+} if
+/yshow where {
+ pop /yshow { .pdfwrite? { 1 index show pop pop } { yshow } ifelse } .bind odef
+} if
+/xyshow where {
+ pop /xyshow { .pdfwrite? { 1 index show pop pop } { xyshow } ifelse } .bind odef
+} if
diff --git a/pstoraster/gs_pfile.ps b/pstoraster/gs_pfile.ps
new file mode 100644
index 000000000..6720cd935
--- /dev/null
+++ b/pstoraster/gs_pfile.ps
@@ -0,0 +1,135 @@
+% 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 supports the work of the GNU Project, but is not
+% affiliated with the Free Software Foundation or the GNU Project. GNU
+% Ghostscript, as distributed by Aladdin Enterprises, does not require any
+% GNU software to build or run it.
+
+% $Id: gs_pfile.ps 956 2000-03-08 23:15:43Z mike $
+% 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
diff --git a/pstoraster/gs_res.ps b/pstoraster/gs_res.ps
new file mode 100644
index 000000000..660348803
--- /dev/null
+++ b/pstoraster/gs_res.ps
@@ -0,0 +1,726 @@
+% Copyright (C) 1994, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+%
+% This file is part of GNU Ghostscript.
+%
+% GNU Ghostscript is distributed in the hope that it will be useful, but
+% WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+% to anyone for the consequences of using it or for whether it serves any
+% particular purpose or works at all, unless he says so in writing. Refer
+% to the GNU General Public License for full details.
+%
+% Everyone is granted permission to copy, modify and redistribute GNU
+% Ghostscript, but only under the conditions described in the GNU General
+% Public License. A copy of this license is supposed to have been given
+% to you along with GNU Ghostscript so you can know your rights and
+% responsibilities. It should be in a file named COPYING. Among other
+% things, the copyright notice and this notice must be preserved on all
+% copies.
+%
+% Aladdin Enterprises supports the work of the GNU Project, but is not
+% affiliated with the Free Software Foundation or the GNU Project. GNU
+% Ghostscript, as distributed by Aladdin Enterprises, does not require any
+% GNU software to build or run it.
+
+% $Id: gs_res.ps 956 2000-03-08 23:15:43Z mike $
+% Initialization file for Level 2 resource machinery.
+% When this is run, systemdict is still writable,
+% but (almost) 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
+ .forcedef % localinstancedict is local, systemdict is global
+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
+% <key> <instance> DefineResource <instance>
+% UndefineResource
+% <key> UndefineResource -
+% FindResource
+% <key> FindResource <instance>
+% ResourceStatus
+% <key> ResourceStatus <status> <size> true
+% <key> ResourceStatus false
+% ResourceForAll
+% <template> <proc> <scratch> ResourceForAll -
+% *ResourceFileName
+% <key> <scratch> ResourceFileName <filename>
+% Additional, specific to our implementation:
+% Instances (dictionary)
+% .LocalInstances
+% - .LocalInstances <dict>
+% .GetInstance
+% <key> .GetInstance <instance> -true-
+% <key> .GetInstance -false-
+% .CheckResource
+% <key> <value> .CheckResource <key> <value> <ok>
+% (or may give an error if not 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.
+
+% .findcategory and .resourceexec are only called from within the
+% implementation of the resource 'operators', so they doesn't have to worry
+% about cleaning up the stack if they fail (the interpreter's stack
+% protection machinery for pseudo-operators takes care of this).
+/.findcategory { % <name> .findcategory -
+ % (pushes the category on the dstack)
+ /Category findresource begin
+} bind def
+
+/.resourceexec { % <key> /xxxResource .resourceexec -
+ % (also pops the category from the dstack)
+ load exec end
+} bind def
+
+15 dict begin
+
+ % Standard entries
+
+/Category /Category def
+/InstanceType /dicttype def
+
+/DefineResource
+ { .CheckResource
+ { dup /Category 3 index cvlit .growput
+ % We would like to make Category dictionaries read-only,
+ % and we used to do that here, but we can't do it,
+ % because we have to be able to replace the dummy, empty
+ % Instances dictionary with the real one later.
+ dup [ exch 0 -1 ] exch
+ Instances 4 2 roll put
+ }
+ { /defineresource load /typecheck signalerror
+ }
+ ifelse
+ } bind def
+/FindResource % (redefined below)
+ { Instances exch get 0 get
+ } bind def
+
+ % Additional entries
+
+/Instances 30 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
+ not { /defineresource load /invalidaccess signalerror } if
+ true
+ } bind def
+
+Instances end begin % for the base case of findresource
+
+(END CATEGORY) VMDEBUG
+
+% Define the resource operators. We use the "stack protection" feature of
+% odef to make sure the stacks are restored properly on an error.
+% This requires that the operators not pop anything from the stack until
+% they have executed their logic successfully. We can't make this
+% work for resourceforall, because the procedure it executes mustn't see
+% the operands of resourceforall on the stack, but we can make it work for
+% the others.
+
+mark
+/defineresource { % <key> <instance> <category> defineresource <instance>
+ 3 copy .findcategory
+ currentdict /InstanceType known {
+ dup type InstanceType ne {
+ dup type /packedarraytype eq InstanceType /arraytype eq and
+ not { /defineresource load /typecheck signalerror } if
+ } if
+ } if
+ /DefineResource .resourceexec
+ 4 1 roll pop pop pop
+}
+/findresource { % <key> <category> findresource <instance>
+ 2 copy dup /Category eq
+ { pop //Category 0 get begin } { .findcategory } ifelse
+ /FindResource .resourceexec exch pop exch pop
+}
+/resourceforall { % <template> <proc> <scratch> <category> resourceforall -
+ dup /Category findresource begin
+ /ResourceForAll load
+ % Make sure we can recover the original operands.
+ % Stack: ...operands... proc
+ 5 copy pop 4 packedarray count
+ % Stack: ...operands... proc saved count
+ 4 -1 roll pop % pop the category
+ /.internalstopped load 3 1 roll
+ 3 .execn
+ % Stack: ... stopped saved count
+ 3 -1 roll {
+ % The count is the original stack depth + 2.
+ count exch 4 sub sub { exch pop } repeat
+ aload pop stop
+ } {
+ pop pop
+ } ifelse end
+}
+/resourcestatus { % <key> <category> resourcestatus <status> <size> true
+ % <key> <category> resourcestatus false
+ 2 copy .findcategory /ResourceStatus .resourceexec
+ { 4 2 roll pop pop true } { pop pop false } ifelse
+}
+/undefineresource { % <key> <category> undefineresource -
+ 2 copy .findcategory /UndefineResource .resourceexec pop pop
+}
+end % Instances of Category
+counttomark 2 idiv { bind odef } repeat pop
+
+% Define the system parameters used for the Generic implementation of
+% ResourceFileName.
+systemdict begin
+currentdict /pssystemparams known not {
+ /pssystemparams 10 dict readonly def
+} if
+pssystemparams begin
+ /FontResourceDir (/Resource/Font/) readonly .forcedef % pssys'params is r-o
+ /GenericResourceDir (/Resource/) readonly .forcedef % pssys'params is r-o
+ /GenericResourcePathSep (/) readonly .forcedef % pssys'params is r-o
+end
+end
+
+% Define the generic algorithm for computing resource file names.
+/.rfnstring 100 string def
+/.genericrfn % <key> <scratch> <prefix> .genericrfn <filename>
+ { 3 -1 roll //.rfnstring cvs concatstrings exch copy
+ } bind def
+
+% 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
+ { .CheckResource
+ { dup [ exch 0 -1 ]
+ % Stack: key value instance
+ 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
+ % Stack: key value instance instancedict
+ 3 index 2 index .growput
+ % Now make the resource value read-only.
+ 0 2 copy get { readonly } .internalstopped pop
+ dup 4 1 roll put exch pop exch pop
+ }
+ { /defineresource load /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
+ }
+ { /findresource load /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.
+ % Construct a new procedure to hold the arguments.
+ % It must be in local VM to avoid a possible invalidaccess.
+ .currentglobal 4 1 roll false .setglobal
+ 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
+ % Stack: global? iterproc
+ % We must pop the resource dictionary off the dict stack
+ % when doing the actual iteration, and restore it afterwards.
+ exch {
+ true .setglobal
+ } {
+ .LocalInstances length 0 ne {
+ % We must do local instances, and do them first.
+ .LocalInstances exch cvx /forall cvx 1 index cvlit
+ currentdict end 3 .execn begin
+ } if
+ } ifelse
+ Instances exch cvx
+ /forall cvx currentdict end 2 .execn begin
+ } bind
+/ResourceFileName
+ { /GenericResourceDir getsystemparam
+ Category .namestring concatstrings
+ /GenericResourcePathSep getsystemparam concatstrings
+ .genericrfn
+ } 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
+ { true
+ } bind
+/.DoLoadResource
+ { dup vmstatus pop exch pop exch
+ .LoadResource
+ vmstatus pop exch pop exch sub
+ 1 index .GetInstance not
+ { pop dup /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
+ }
+ { dup /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 }
+ { pop 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
+ /ResourceFileName /.ResourceFile /.LoadResource /.DoLoadResource
+ }
+ { dup load put dup } forall
+pop readonly pop end
+
+(END GENERIC) VMDEBUG
+
+% Define the fixed categories.
+
+mark
+ % Non-Type categories with existing entries.
+ /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
+ % Type categories listed in the Red Book.
+ /ColorRenderingType
+ { } % These must be deferred, because optional features may add some.
+ /FMapType
+ { } % These must be deferred, because optional features may add some.
+ /FontType
+ { } % These must be deferred, because optional features may add some.
+ /FormType
+ { } % These must be deferred, because optional features may add some.
+ /HalftoneType
+ {1 2 3 4 5}
+ /ImageType
+ { } % Deferred, optional features may add some.
+ /PatternType
+ { } % Deferred, optional features may add some.
+ % Type categories added since the Red Book.
+ /setsmoothness where {
+ pop /ShadingType { } % Deferred, optional features may add some.
+ } if
+counttomark 2 idiv
+ { mark
+
+ % Standard entries
+
+ % We'd like to prohibit defineresource,
+ % but because optional features may add entries, we can't.
+ % We can at least require that the key and value match.
+ /DefineResource
+ { currentglobal not
+ { /defineresource load /invalidaccess signalerror }
+ { 2 copy ne
+ { /defineresource load /rangecheck signalerror }
+ { dup Instances 4 -2 roll .growput }
+ ifelse
+ }
+ ifelse
+ } bind
+ /UndefineResource
+ { /undefineresource load /invalidaccess signalerror } bind
+ /FindResource
+ { Instances 1 index .knownget
+ { exch pop }
+ { /findresource load /undefinedresource signalerror }
+ ifelse
+ } 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
+ % We'd like to make the Instances readonly here,
+ % but because optional features may add entries, we can't.
+ /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
+% Added since the Red Book:
+/ControlLanguage mark /InstanceType /dicttype .definecategory
+/HWOptions mark /InstanceType /dicttype .definecategory
+/Localization mark /InstanceType /dicttype .definecategory
+/OutputDevice mark /InstanceType /dicttype .definecategory
+/PDL mark /InstanceType /dicttype .definecategory
+% CIDFont, CIDMap, and CMap are defined in gs_cidfn.ps
+% FontSet is defined in gs_cff.ps
+% IdiomSet, InkParams, and TrapParams are defined in gs_ll3.ps
+
+(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 readonly 0 -1 ] } { pop [null 2 -1] } ifelse
+ } forall
+.dicttomark
+
+/.ResourceFileDict mark
+ EncodingDirectory
+ { dup length 256 eq { pop pop } { 0 get } ifelse
+ } forall
+.dicttomark
+
+/ResourceFileName
+ { .ResourceFileDict 2 index .knownget
+ { exch copy exch pop }
+ { /Generic /Category findresource /ResourceFileName get exec }
+ ifelse
+ } bind
+
+.definecategory % Encoding
+
+% Make placeholders in level2dict for the redefined Encoding operators,
+% so that they will be swapped properly when we switch language levels.
+
+/.findencoding /.findencoding load def
+/findencoding /findencoding load def
+/.defineencoding /.defineencoding load 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 { .loadfont } { .GetInstance pop 0 get } ifelse }
+ { .loadfont }
+ ifelse
+ } bind
+/ResourceFileName
+ { /FontResourceDir getsystemparam .genericrfn
+ } bind
+
+/.loadfont
+ { dup vmstatus pop exch pop exch
+ //findfont exec exch % findfont is a procedure....
+ vmstatus pop exch pop exch sub
+ % stack: name font vmused
+ % findfont has the prerogative of not calling definefont
+ % in certain obscure cases of font substitution.
+ 2 index .GetInstance
+ { dup 1 1 put
+ 2 3 -1 roll put
+ }
+ { pop
+ }
+ ifelse exch 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
+% The Red Book requires that findfont be a procedure, not an operator,
+% but it still needs to restore the stacks reliably if it fails.
+/.findfontop {
+ /Font findresource
+} bind odef
+/findfont {
+ .findfontop
+} bind def % Must be a procedure, not an operator
+
+% Remove initialization utilities.
+currentdict /.definecategory .undef
+currentdict /.emptydict .undef
+
+end % level2dict
+
+% Convert deferred resources after we finally switch to Level 2.
+
+/.fixresources {
+ % Encoding resources
+ EncodingDirectory
+ { dup length 256 eq
+ { /Encoding defineresource pop }
+ { pop pop }
+ ifelse
+ } forall
+ /.findencoding { /Encoding findresource } bind def
+ /findencoding /.findencoding load def % must be a procedure
+ /.defineencoding { /Encoding defineresource pop } bind def
+ % ColorRendering resources and ProcSet
+ systemdict /ColorRendering .knownget {
+ /ColorRendering exch /ProcSet defineresource pop
+ systemdict /ColorRendering undef
+ /Default currentcolorrendering /ColorRendering defineresource pop
+ } if
+ % ColorSpace resources
+ systemdict /CIEsRGB .knownget {
+ /sRGB exch /ColorSpace defineresource pop
+ systemdict /CIEsRGB undef
+ } if
+ % FontType and FMapType resources
+ buildfontdict { pop dup /FontType defineresource pop } forall
+ mark
+ buildfontdict 0 known { 2 3 4 5 6 7 8 } if
+ buildfontdict 9 known { 9 } if
+ counttomark { dup /FMapType defineresource pop } repeat pop
+ % FormType resources
+ .formtypes { pop dup /FormType defineresource pop } forall
+ % ColorRenderingType resources
+ .colorrenderingtypes {pop dup /ColorRenderingType defineresource pop} forall
+ % ImageType resources
+ .imagetypes { pop dup /ImageType defineresource pop } forall
+ % PatternType resources
+ .patterntypes { pop dup /PatternType defineresource pop } forall
+ % Make the fixed resource categories immutable.
+ /.shadingtypes where {
+ pop .shadingtypes { pop dup /ShadingType defineresource pop } forall
+ } if
+ [ /ColorSpaceFamily /Emulator /Filter /IODevice /ColorRenderingType
+ /FMapType /FontType /FormType /HalftoneType /ImageType /PatternType
+ /.shadingtypes where { pop /ShadingType } if
+ ] {
+ /Category findresource
+ dup /Instances get readonly pop
+ .LocalInstances readonly pop
+ readonly pop
+ } forall
+ % clean up
+ systemdict /.fixresources undef
+} bind def
diff --git a/pstoraster/gs_setpd.ps b/pstoraster/gs_setpd.ps
new file mode 100644
index 000000000..82a6280e9
--- /dev/null
+++ b/pstoraster/gs_setpd.ps
@@ -0,0 +1,712 @@
+% Copyright (C) 1994, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+%
+% This file is part of GNU Ghostscript.
+%
+% GNU Ghostscript is distributed in the hope that it will be useful, but
+% WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+% to anyone for the consequences of using it or for whether it serves any
+% particular purpose or works at all, unless he says so in writing. Refer
+% to the GNU General Public License for full details.
+%
+% Everyone is granted permission to copy, modify and redistribute GNU
+% Ghostscript, but only under the conditions described in the GNU General
+% Public License. A copy of this license is supposed to have been given
+% to you along with GNU Ghostscript so you can know your rights and
+% responsibilities. It should be in a file named COPYING. Among other
+% things, the copyright notice and this notice must be preserved on all
+% copies.
+%
+% Aladdin Enterprises supports the work of the GNU Project, but is not
+% affiliated with the Free Software Foundation or the GNU Project. GNU
+% Ghostscript, as distributed by Aladdin Enterprises, does not require any
+% GNU software to build or run it.
+
+% $Id: gs_setpd.ps 998 2000-03-20 18:57:57Z mike $
+% 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.
+
+% We have to guard against the BeginPage procedure not popping its operand.
+% This is really stupid, but the Genoa CET does it.
+/.beginpage { % - .beginpage -
+ .currentshowpagecount {
+ .currentpagedevice pop
+ dup null ne { /BeginPage .knownget } { pop false } ifelse {
+ % Stack: ... pagecount proc
+ count 2 .execn
+ % Stack: ... ..???.. oldcount
+ count 1 add exch sub { pop } repeat
+ } {
+ pop
+ } ifelse
+ } if
+} bind odef
+
+% Guard similarly against EndPage not popping its operand.
+/.endpage { % <reason> .endpage <print_bool>
+ .currentshowpagecount {
+ 1 index .currentpagedevice pop
+ dup null ne { /EndPage .knownget } { pop false } ifelse {
+ % Stack: ... reason pagecount reason proc
+ count 2 .execn
+ % Stack: ... ..???.. print oldcount
+ count 2 add exch sub { exch pop } repeat
+ } {
+ pop 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
+ % A careful reading of the Red Book reveals that an erasepage
+ % should occur, but *not* an initgraphics.
+ erasepage .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
+
+% Redefine .currentpagedevice and .setpagedevice so they convert between
+% null and a fixed empty directionary.
+/.nullpagedevice 0 dict readonly def
+/.currentpagedevice {
+ //.currentpagedevice exch dup null eq { pop //.nullpagedevice } if exch
+} bind odef
+/.setpagedevice {
+ dup //.nullpagedevice eq { pop null } if //.setpagedevice
+} 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
+ % Since sizes match within 5 user units, we need to set the smallest
+ % PageSize to 6 units so that [0 0] will fail.
+ mark /PageSize [6 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
+ % The following are documented in Adobe's supplement for v2017.
+% /LeadingEdge /MediaClass
+ /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).
+/.policyprocs mark
+% These procedures are called with the following on the stack:
+% <orig> <merged> <failed> <Policies> <key> <policy>
+% They are expected to consume the top 2 operands.
+% NOTE: we currently treat all values other than 0, 1, or 7 (for PageSize)
+% the same as 0, i.e., we signal an error.
+ 0 { % Set errorinfo and signal a configurationerror.
+ pop dup 4 index exch get 2 array astore
+ $error /errorinfo 3 -1 roll put
+ cleartomark
+ /setpagedevice load /configurationerror signalerror
+ } bind
+ 1 { % 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
+ } bind
+ 7 { % For PageSize only, just impose the request.
+ 1 index /PageSize eq
+ { pop pop 1 index /PageSize 7 put }
+ { .policyprocs 0 get exec }
+ ifelse
+ } bind
+.dicttomark readonly def
+/.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>
+ .policyprocs 1 index .knownget not { .policyprocs 0 get } if exec
+ }
+ 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
+ 4 index /RollFedMedia .knownget not { false } if
+ matrix .matchpagesize not {
+ % This is a "can't happen" condition!
+ /setpagedevice load /rangecheck signalerror
+ } if
+ 2 array astore
+ } bind def
+
+% ---------------- setpagedevice itself ---------------- %
+
+/setpagedevice
+ { % We mustn't pop the argument until the very end,
+ % so that the pseudo-operator machinery can restore the stack
+ % if an error occurs.
+ mark 1 index 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
+ % Hack: if FIXEDMEDIA is true, discard any attempt to change
+ % PageSize or HWSize.
+ FIXEDMEDIA
+ { dup /PageSize 4 index /PageSize get put
+ dup /HWSize 4 index /HWSize get put
+ } 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,
+ % unless we're changing the OutputDevice.
+ % Stack: mark ... <merged> <failed>
+ exch currentpagedevice dup length 2 index length add dict
+ % Stack: mark ... <failed> <merged> <current> <newdict>
+ 2 index /OutputDevice .knownget {
+ 2 index /OutputDevice .knownget not { null } if eq
+ } {
+ true
+ } ifelse {
+ % Same OutputDevice, merge the dictionaries.
+ .copydict
+ } {
+ % Different OutputDevice, discard the old dictionary.
+ exch pop
+ } ifelse .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 pop
+
+ } odef
+
+end % level2dict
+.setlanguagelevel
diff --git a/pstoraster/gs_statd.ps b/pstoraster/gs_statd.ps
new file mode 100644
index 000000000..a394f8ec5
--- /dev/null
+++ b/pstoraster/gs_statd.ps
@@ -0,0 +1,296 @@
+% Copyright (C) 1989, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+%
+% This file is part of GNU Ghostscript.
+%
+% GNU Ghostscript is distributed in the hope that it will be useful, but
+% WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+% to anyone for the consequences of using it or for whether it serves any
+% particular purpose or works at all, unless he says so in writing. Refer
+% to the GNU General Public License for full details.
+%
+% Everyone is granted permission to copy, modify and redistribute GNU
+% Ghostscript, but only under the conditions described in the GNU General
+% Public License. A copy of this license is supposed to have been given
+% to you along with GNU Ghostscript so you can know your rights and
+% responsibilities. It should be in a file named COPYING. Among other
+% things, the copyright notice and this notice must be preserved on all
+% copies.
+%
+% Aladdin Enterprises supports the work of the GNU Project, but is not
+% affiliated with the Free Software Foundation or the GNU Project. GNU
+% Ghostscript, as distributed by Aladdin Enterprises, does not require any
+% GNU software to build or run it.
+
+% $Id: gs_statd.ps 956 2000-03-08 23:15:43Z mike $
+% 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 .forcedef % statusdict is local, sys'dict global
+ % To support the Level 2 job control features,
+ % serverdict must also be in local VM.
+ /serverdict 10 dict .forcedef % serverdict is local, sys'dict global
+ .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 { /statusdict .systemvar begin .setpagesize end } bind def
+userdict begin
+ % Page sizes defined by Adobe documentation
+ /11x17 {792 1224 //.setpagesize exec} bind def % 11x17 portrait
+ /a3 {842 1190 //.setpagesize exec} bind def
+ /a4 {595 842 //.setpagesize exec} bind def
+% a4small should be a4 with an ImagingBBox of [25 25 570 817].
+ /a4small /a4 load def
+ /b5 {501 709 //.setpagesize exec} bind def
+ /ledger {1224 792 //.setpagesize exec} bind def % 11x17 landscape
+ /legal {612 1008 //.setpagesize exec} bind def
+ /letter {612 792 //.setpagesize exec} bind def
+% lettersmall should be letter with an ImagingBBox of [25 25 587 767].
+ /lettersmall /letter load def
+% note should be letter (or some other size) with the ImagingBBox
+% shrunk by 25 units on all 4 sides.
+ /note /letter load def
+ % End of Adobe-defined page sizes
+STRICT { (%END) .skipeof } if
+ % Other page sizes
+ % ISO standard paper sizes
+ /a0 {2380 3368 //.setpagesize exec} bind def
+ /a1 {1684 2380 //.setpagesize exec} bind def
+ /a2 {1190 1684 //.setpagesize exec} bind def
+% /a3 {842 1190 //.setpagesize exec} bind def % defined by Adobe
+% /a4 {595 842 //.setpagesize exec} bind def % defined by Adobe
+ /a5 {421 595 //.setpagesize exec} bind def
+ /a6 {297 421 //.setpagesize exec} bind def
+ /a7 {210 297 //.setpagesize exec} bind def
+ /a8 {148 210 //.setpagesize exec} bind def
+ /a9 {105 148 //.setpagesize exec} bind def
+ /a10 {74 105 //.setpagesize exec} bind def
+ /b0 {2836 4008 //.setpagesize exec} bind def
+ /b1 {2004 2836 //.setpagesize exec} bind def
+ /b2 {1418 2004 //.setpagesize exec} bind def
+ /b3 {1002 1418 //.setpagesize exec} bind def
+ /b4 {709 1002 //.setpagesize exec} bind def
+% /b5 {501 709 //.setpagesize exec} bind def % defined by Adobe
+ /c0 {2600 3677 //.setpagesize exec} bind def
+ /c1 {1837 2600 //.setpagesize exec} bind def
+ /c2 {1298 1837 //.setpagesize exec} bind def
+ /c3 {918 1298 //.setpagesize exec} bind def
+ /c4 {649 918 //.setpagesize exec} bind def
+ /c5 {459 649 //.setpagesize exec} bind def
+ /c6 {323 459 //.setpagesize exec} bind def
+ % U.S. CAD standard paper sizes
+ /archE {2592 3456 //.setpagesize exec} bind def
+ /archD {1728 2592 //.setpagesize exec} bind def
+ /archC {1296 1728 //.setpagesize exec} bind def
+ /archB {864 1296 //.setpagesize exec} bind def
+ /archA {648 864 //.setpagesize exec} bind def
+ % Other paper sizes
+ /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
+% /tabloid {792 1224 //.setpagesize exec} bind def % 11x17 portrait
+% /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 SIZES
+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..79e1dca44
--- /dev/null
+++ b/pstoraster/gs_std_e.ps
@@ -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 supports the work of the GNU Project, but is not
+% affiliated with the Free Software Foundation or the GNU Project. GNU
+% Ghostscript, as distributed by Aladdin Enterprises, does not require any
+% GNU software to build or run it.
+
+% $Id: gs_std_e.ps 956 2000-03-08 23:15:43Z mike $
+% 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..cdd17141f
--- /dev/null
+++ b/pstoraster/gs_sym_e.ps
@@ -0,0 +1,91 @@
+% Copyright (C) 1991, 1994, 1998 Aladdin Enterprises. All rights reserved.
+%
+% This file is part of GNU Ghostscript.
+%
+% GNU Ghostscript is distributed in the hope that it will be useful, but
+% WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+% to anyone for the consequences of using it or for whether it serves any
+% particular purpose or works at all, unless he says so in writing. Refer
+% to the GNU General Public License for full details.
+%
+% Everyone is granted permission to copy, modify and redistribute GNU
+% Ghostscript, but only under the conditions described in the GNU General
+% Public License. A copy of this license is supposed to have been given
+% to you along with GNU Ghostscript so you can know your rights and
+% responsibilities. It should be in a file named COPYING. Among other
+% things, the copyright notice and this notice must be preserved on all
+% copies.
+%
+% Aladdin Enterprises supports the work of the GNU Project, but is not
+% affiliated with the Free Software Foundation or the GNU Project. GNU
+% Ghostscript, as distributed by Aladdin Enterprises, does not require any
+% GNU software to build or run it.
+
+% $Id: gs_sym_e.ps 956 2000-03-08 23:15:43Z mike $
+% 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
+ /euro /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..8fdbd2eb9
--- /dev/null
+++ b/pstoraster/gs_ttf.ps
@@ -0,0 +1,694 @@
+% Copyright (C) 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+%
+% This file is part of GNU Ghostscript.
+%
+% GNU Ghostscript is distributed in the hope that it will be useful, but
+% WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+% to anyone for the consequences of using it or for whether it serves any
+% particular purpose or works at all, unless he says so in writing. Refer
+% to the GNU General Public License for full details.
+%
+% Everyone is granted permission to copy, modify and redistribute GNU
+% Ghostscript, but only under the conditions described in the GNU General
+% Public License. A copy of this license is supposed to have been given
+% to you along with GNU Ghostscript so you can know your rights and
+% responsibilities. It should be in a file named COPYING. Among other
+% things, the copyright notice and this notice must be preserved on all
+% copies.
+%
+% Aladdin Enterprises supports the work of the GNU Project, but is not
+% affiliated with the Free Software Foundation or the GNU Project. GNU
+% Ghostscript, as distributed by Aladdin Enterprises, does not require any
+% GNU software to build or run it.
+
+% $Id: gs_ttf.ps 956 2000-03-08 23:15:43Z mike $
+% Support code for direct use of TrueType fonts.
+% (Not needed for Type 42 fonts.)
+
+% Thanks to B. Jackowski and GUST (the Polish TeX Users' Group) for
+% the glyf-splitting code.
+
+% ---------------- 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
+
+% <file> <key> .findfontvalue <value> true
+% <file> <key> .findfontvalue false
+% Closes the file in either case.
+/.findnonttfontvalue /.findfontvalue load def
+/.findfontvalue {
+ 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
+
+% <file> .findttfontname <fname> true
+% <file> .findttfontname false
+% Closes the file in either case.
+/.findttfontname {
+ .loadttfonttables
+ tabdict /name .knownget {
+ 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.
+
+% <file> .loadfontfile -
+/.loadnonttfontfile /.loadfontfile load def
+/.loadfontfile {
+ dup read pop 2 copy unread 0 eq {
+ % If this is a font at all, it's a TrueType font.
+ .loadttfont 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 2 interacting tables that affect the encoding:
+% 'cmap' provides multiple maps from character codes to glyph indices
+% '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 32000 def % half the maximum length of a PostScript string,
+ % must be a multiple of 4 (for hmtx / loca / vmtx)
+
+% 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
+
+% ---- Utilities ---- %
+
+% <string> <index> getu16 <integer>
+/getu16 {
+ 2 copy get 8 bitshift 3 1 roll 1 add get add
+} bind def
+
+% <string> <index> gets16 <integer>
+/gets16 {
+ getu16 16#8000 xor 16#8000 sub
+} bind def
+
+% <string> <index> getu32 <integer>
+/getu32 {
+ 2 copy getu16 16 bitshift 3 1 roll 2 add getu16 add
+} bind def
+
+% <string> <index> gets32 <integer>
+/gets32 {
+ 2 copy gets16 16 bitshift 3 1 roll 2 add getu16 add
+} bind def
+
+% <string> <index> <integer> putu16 -
+/putu16 {
+ 3 copy -8 bitshift put
+ exch 1 add exch 16#ff and put
+} bind def
+
+% <string> <index> <integer> putu32 -
+/putu32 {
+ 3 copy -16 bitshift putu16
+ exch 2 add exch 16#ffff and putu16
+} bind def
+
+% <nametable> <nameid> findname <string> true
+% <nametable> <nameid> findname false
+/findname {
+ 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
+
+% <namerecord> is2byte <bool>
+/is2byte {
+ 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
+
+% <string2> string2to1 <string>
+/string2to1 {
+ 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
+
+% <array> <lt-proc> sort <array>
+/sort {
+ 1 index length 1 sub -1 1 {
+ 2 index exch 2 copy get 3 copy % arr proc arr i arr[i] arr i arr[i]
+ 0 1 3 index 1 sub {
+ 3 index 1 index get % arr proc arr i arr[i] arr imax amax j arr[j]
+ 2 index 1 index 10 index exec { % ... amax < arr[j]
+ 4 2 roll
+ } if pop pop
+ } for % arr proc arr i arr[i] arr imax amax
+ 4 -1 roll exch 4 1 roll put put
+ } for pop
+} def
+
+% Each procedure in this dictionary is called as follows:
+% -mark- encodingtable <<proc>> -mark- glyphindices...
+/cmapformats mark
+ 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
+ % The following hack allows us to properly handle
+ % idiosyncratic fonts that start at 0xf000:
+ /firstcode startc 0 getu16 16#ff00 and dup 16#f000 ne { pop 0 } if def
+ pop /numcodes counttomark 1 sub def
+ 0 2 nseg2 3 sub
+ { /i2 exch def
+ /scode startc i2 getu16 def
+ /ecode endc i2 getu16 def
+ numcodes scode firstcode sub
+ % Hack for fonts that have only 0x0000 and 0xf000 ranges
+ dup 16#e000 ge { 255 and } if
+ exch sub 0 max dup { 0 exch } repeat
+ ecode scode sub 1 add add numcodes add /numcodes exch def
+ /delta iddelta i2 gets16 def
+ DEBUG {
+ (scode=) print scode =only
+ ( ecode=) print ecode =only
+ ( delta=) print delta =only
+ ( droff=) print idroff i2 getu16 =
+ } if
+ idroff i2 getu16 dup 0 eq {
+ pop scode delta add 65535 and 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 }
+ for pop
+ } bind
+.dicttomark readonly def % cmapformats
+
+% <cmaptab> cmaparray -mark- <glyphs> ...
+/cmaparray {
+ mark exch dup 0 getu16 cmapformats exch .knownget {
+ exec
+ } {
+ (Can't handle format ) print 0 getu16 = flush
+ 0 1 255 { } for
+ } ifelse
+ DEBUG {
+ ([) print counttomark 1 sub -1 0 { index =only ( ) print } for (]) =
+ } if
+} bind def
+
+% Each procedure in this dictionary is called as follows:
+% posttable <<proc>> glyphencoding
+/postformats mark
+ 16#00010000 { % 258 standard Macintosh glyphs.
+ MacGlyphEncoding
+ }
+ 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
+ 16#00030000 { % No map.
+ pop [ ]
+ } bind
+.dicttomark readonly def % postformats
+
+% Each procedure in this dictionary is called as follows:
+% <file> <length> -proc- <string|array_of_strings>
+% Note that each table must have an even length, because of a strange
+% Adobe requirement that each sfnts entry have even length.
+/readtables mark
+ % Ordinary tables
+ (cmap) { .readtable }
+ (head) 1 index
+ (hhea) 1 index
+ (name) 1 index
+ (post) 1 index
+ (vhea) 1 index
+ % Big tables
+ (glyf) { .readbigtable }
+ (loca) 1 index
+ (hmtx) 1 index
+ (vmtx) 1 index
+.dicttomark readonly def % readtables
+
+% Read a table as a single string.
+% <file> <length> .readtable <string>
+/.readtable {
+ dup dup 1 and add string
+ % Stack: f len str
+ dup 0 4 -1 roll getinterval
+ % Stack: f str str1
+ 3 -1 roll exch readstring pop pop
+} bind def
+
+% Read a big table (one that may exceed 64K).
+% <file> <length> .readbigtable <string[s]>
+/.readbigtable {
+ dup 65400 lt {
+ .readtable
+ } {
+ currentuserparams /VMReclaim get -2 vmreclaim
+ [ 4 2 roll {
+ % Stack: mark ... f left
+ dup maxstring le { exit } if
+ 1 index maxstring string readstring pop 3 1 roll maxstring sub
+ } loop .readtable ]
+ exch vmreclaim
+ } ifelse
+} bind def
+
+.dicttomark readonly def % .loadttfontdict
+
+% <tab> .printtab -
+/.printtab {
+ dup 0 4 getinterval print ( ) print
+ dup 8 getu32 =only ( ) print
+ 12 getu32 =
+} bind def
+
+% <file> .loadttfonttables -
+% Pushes .loadttfontdict & scratch dict on d-stack.
+% Defines f, offsets, tables, tabdict, tabs.
+/.loadttfonttables {
+ .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
+ /tabdict tables length 16 idiv dict def
+ % tabs = tables we want to keep, sorted by file position.
+ /tabs [ 0 16 tables length 1 sub {
+ tables exch 16 getinterval
+ DEBUG { dup .printtab } if
+ dup 0 4 getinterval readtables 1 index known {
+ tabdict exch 2 index put
+ } {
+ pop pop
+ } ifelse
+ } for ] {
+ exch 8 getu32 exch 8 getu32 lt
+ } sort def
+} bind def
+
+% - .readttdata -
+% Read data. Updates offsets, tabs; stores data in tabdict.
+/.readttdata {
+ /fpos offsets length tables length add def
+ /sfpos offsets length tabs length 16 mul add def
+ offsets 4 tabs length putu16
+ tabs {
+ dup 0 4 getinterval /tname exch def
+ dup 8 getu32 /tpos exch def
+ dup 12 getu32 /tlen exch def
+ 8 sfpos putu32
+ % Skip data between the end of the previous table and
+ % the beginning of this one, if any.
+ tpos fpos gt {
+ f tpos fpos sub () /SubFileDecode filter dup flushfile closefile
+ /fpos tpos def
+ } if
+ f tlen readtables tname get exec
+ tabdict tname 3 -1 roll put
+ /fpos fpos tlen add def
+ % Round up the table length to an even value.
+ /sfpos sfpos tlen dup 1 and add add def
+ } forall
+} bind def
+
+% Find the string in a list of strings that includes a given index.
+% <strings> <index> .findseg <string> <index'>
+/.findseg {
+ exch {
+ dup length 2 index gt { exch exit } if
+ length sub
+ } forall
+} bind def
+
+% - .makesfnts -
+% Defines checksum, getloca, head, locatable, numglyphs, post, sfnts, upem
+/.makesfnts {
+ .readttdata
+ /head tabdict /head get def
+ /checksum head 8 getu32 def
+ /locatable tabdict /loca get def
+ /post tabdict /post .knownget not { null } if def
+ /numglyphs
+ locatable dup type /stringtype eq
+ { length }
+ { 0 exch { length add } forall }
+ ifelse % no def yet
+ locatable type /stringtype eq {
+ /.indexloca {} def
+ } {
+ /.indexloca /.findseg load def
+ } ifelse
+ head 50 getu16 0 ne {
+ /getloca {
+ 2 bitshift locatable exch .indexloca getu32
+ } def
+ 4 idiv 1 sub
+ } {
+ /getloca {
+ dup add locatable exch .indexloca getu16 dup add
+ } def
+ 2 idiv 1 sub
+ } ifelse def % numglyphs
+ % If necessary, re-partition the glyfs.
+ tabdict /glyf get dup type /stringtype ne {
+ .dividesfnts tabdict /glyf 3 -1 roll put
+ } {
+ pop
+ } ifelse
+ /sfnts [
+ offsets tabs { concatstrings } forall
+ tabs {
+ 0 4 getinterval tabdict exch get
+ dup type /stringtype ne { aload pop } if
+ } forall
+ ] def
+} bind def
+
+% - .getcmap -
+% Defines cmapsub, cmaptab
+/.getcmap {
+ tabdict /cmap get
+ % 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
+} bind def
+
+% <glyfs> .dividesfnts <glyfs'>
+/.dividesfnts {
+ /glyfs exch def
+ /len1 0 glyfs { length add } forall def
+ % Determine where to split the glyfs by scanning loca.
+ % The very last entry in loca may be bogus.
+ %
+ % Construct splitarray, the array of final lengths of
+ % the sfnts entries covering the glyfs (i.e., all but
+ % the first and last sfnts entries).
+ /prevsplit 0 def
+ /prevboundary 0 def
+ /splitarray [
+ 0 1 numglyphs 1 sub {
+ getloca dup prevsplit maxstring add gt {
+ prevboundary prevsplit sub exch
+ /prevsplit prevboundary def
+ } if
+ /prevboundary exch def
+ } for
+ len1 prevsplit sub
+ ] def
+ currentuserparams /VMReclaim get -2 vmreclaim
+ [
+ % Re-split the sfnts glyfs strings according to splitarray.
+ % We do this by iterating over the final segments defined
+ % by splitarray, and constructing them from pieces of the
+ % current glyfs strings. We recycle the current strings
+ % when possible, to avoid stressing the allocator.
+ /sfnt_idx 0 def
+ /strpos 0 def
+ /avail () def
+ splitarray {
+ /seglen exch def
+ /segpos 0 def
+ avail length seglen ge
+ { avail 0 seglen getinterval /avail () def } { seglen string }
+ ifelse
+ {
+ /str glyfs sfnt_idx get def
+ /strlen str length def
+ /strleft strlen strpos sub def
+ seglen segpos sub strleft lt { exit } if
+ % Copy the (rest of the) string into the new segment.
+ % We know strleft <= segleft.
+ dup segpos str strpos strleft getinterval putinterval
+ /segpos segpos strleft add def
+ /avail str def
+ /sfnt_idx sfnt_idx 1 add def
+ /strpos 0 def
+ segpos seglen eq { exit } if
+ } loop
+ % Fill up the segment with an initial piece of the next
+ % existing glyfs string. We know strleft > segleft.
+ /segleft seglen segpos sub def
+ dup segpos str strpos segleft getinterval putinterval
+ /strpos strpos segleft add def
+ } forall
+ ]
+ exch vmreclaim
+} bind def
+
+% - .ttkeys <key> <value> ...
+/.ttkeys {
+ /upem head 18 getu16 def
+ /FontMatrix matrix
+ /FontBBox [ 36 2 42 { head exch gets16 upem div } for ]
+ tabdict /name .knownget {
+ % Find the names from the 'name' table.
+ /names exch def
+ /FontName names 6 findname not { checksum 16 8 string cvrs } if
+ /fontname 1 index def
+ /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
+ /fontname 1 index def
+ /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
+ /XUID [orgXUID 42 checksum]
+ /sfnts sfnts
+ DEBUG {
+ tabs { .printtab } forall
+ [ sfnts { length } forall ] ==
+ } if
+} bind def
+
+% -mark- <key> <value> ... .definettfont <font>
+/.definettfont {
+ /FontType 42
+ /PaintType 0
+ % See if we have PostScript glyph name information.
+ /glyphencoding post null eq {
+ [ ]
+ } {
+ postformats post 0 getu32 .knownget { post exch exec } { [ ] } ifelse
+ } ifelse
+ % If necessary, fabricate additional glyphencoding entries
+ % to cover all of loca.
+ dup length numglyphs lt {
+ [ exch aload pop
+ counttomark 1 numglyphs 1 sub {
+ =string cvs (_) exch concatstrings cvn
+ } for ]
+ } if def
+ /Encoding
+ cmaptab cmaparray
+ 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.
+ /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
+ .dicttomark
+ end end dup /FontName get exch definefont
+} bind def
+
+% Create a string with N CIDs from the top of the stack.
+% <cid1> ... <cidN> <N> .makecidmap <string>
+/.makecidmap {
+ dup 2 mul string dup 3 -1 roll 1 sub 2 mul -2 0 {
+ % Stack: cids str str i2
+ 2 copy 5 index -8 bitshift put
+ 1 add 4 -1 roll 16#ff and put dup
+ } for pop
+} bind def
+
+% -mark- <key> <value> ... .definettcidfont <font>
+/.definettcidfont {
+ /CIDFontName fontname
+ /CIDFontType 2
+ /CIDSystemInfo mark
+ /Registry (Adobe)
+ /Ordering (Japan1) % adhoc
+ /Supplement 0
+ .dicttomark
+ /CharStrings mark /.notdef 0 .dicttomark
+ cmaptab cmaparray
+ counttomark /cidcount exch def
+ cidcount maxstring le {
+ % Use a single string.
+ cidcount .makecidmap exch pop
+ } {
+ % We must use 2 strings.
+ maxstring .makecidmap counttomark 1 add 1 roll
+ counttomark .makecidmap exch pop exch 2 array astore
+ } ifelse
+ /CIDMap exch
+ /CIDCount cidcount
+ /GDBytes 2
+ .dicttomark
+ end end dup /CIDFontName get exch /CIDFont defineresource
+} bind def
+
+% <file> .loadttfont <type42font>
+/.loadttfont {
+ .loadttfonttables
+ .makesfnts
+ .getcmap
+ mark
+ .ttkeys
+ .definettfont
+} bind def
+
+% <file> .loadttcidfont <cidtype2font>
+/.loadttcidfont {
+ .loadttfonttables
+ .makesfnts
+ .getcmap
+ mark
+ .ttkeys
+ .definettcidfont
+} bind def
diff --git a/pstoraster/gs_typ32.ps b/pstoraster/gs_typ32.ps
new file mode 100644
index 000000000..444decd17
--- /dev/null
+++ b/pstoraster/gs_typ32.ps
@@ -0,0 +1,134 @@
+% Copyright (C) 1997 Aladdin Enterprises. All rights reserved.
+%
+% This file is part of GNU Ghostscript.
+%
+% GNU Ghostscript is distributed in the hope that it will be useful, but
+% WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+% to anyone for the consequences of using it or for whether it serves any
+% particular purpose or works at all, unless he says so in writing. Refer
+% to the GNU General Public License for full details.
+%
+% Everyone is granted permission to copy, modify and redistribute GNU
+% Ghostscript, but only under the conditions described in the GNU General
+% Public License. A copy of this license is supposed to have been given
+% to you along with GNU Ghostscript so you can know your rights and
+% responsibilities. It should be in a file named COPYING. Among other
+% things, the copyright notice and this notice must be preserved on all
+% copies.
+%
+% Aladdin Enterprises supports the work of the GNU Project, but is not
+% affiliated with the Free Software Foundation or the GNU Project. GNU
+% Ghostscript, as distributed by Aladdin Enterprises, does not require any
+% GNU software to build or run it.
+
+% $Id: gs_typ32.ps 956 2000-03-08 23:15:43Z mike $
+% Initialization file for Type 32 fonts.
+
+% ------ Type 32 fonts ------ %
+
+% We need LanguageLevel 2 or higher in order to have defineresource.
+languagelevel dup 2 max .setlanguagelevel
+
+/BitmapFontInit mark
+
+/.makeglyph32 systemdict /.makeglyph32 get
+systemdict /.makeglyph32 .undef
+
+/addglyph { % ([wx wy llx lly urx ury] |
+ % [w0x w0y llx lly urx ury w1x w1y vx vy])
+ % <bitmap> <cid> <type32font> addglyph -
+ 1 index dup 2 index .removeglyphs
+ 22 string .makeglyph32
+ % Stack: metrics bitmap cid font metstr
+ 3 index () ne {
+ % Use G4 encoding to compress the bitmap.
+ % Define a string large enough to hold the metrics,
+ % an uncompressed bitmap (worst case = 5x expansion),
+ % and the 2 RTC codes (3 bytes).
+ dup length 4 index length 5 mul add 10 add string
+ % Stack: metrics bitmap cid font metstr buffer
+ dup 0 3 index putinterval
+ dup 2 index length 1 index length 1 index sub getinterval
+ % Stack: metrics bitmap cid font metstr buffer bitbuf
+ mark /Columns 8 index dup 4 get exch 2 get sub
+ /Rows 10 index dup 5 get exch 3 get sub
+ /K -1 /EndOfBlock true /BlackIs1 true
+ .dicttomark /CCITTFaxEncode filter
+ % Stack: metrics bitmap cid font metstr buffer filter
+ dup 6 index writestring closefile
+ % Find the end of the data by scanning backwards for the RTC.
+ % There are 2 RTCs x 12 bits = 3 bytes to remove.
+ {
+ dup dup length 1 sub get 0 ne { exit } if
+ 0 1 index length 1 sub getinterval
+ } loop
+ 0 1 index length 3 sub getinterval
+ exch pop % metstr
+ } if
+ 1 index /CharStrings get 3 index 3 -1 roll put
+ pop pop pop pop
+} obind
+
+/removeall { % <type32font> removeall -
+ 0 65535 2 index removeglyphs pop
+} obind
+
+/.removeglyphs systemdict /.removeglyphs get
+systemdict /.removeglyphs .undef
+
+/removeglyphs { % <cid_min> <cid_max> <type32font> .removeglyphs -
+ 3 copy .removeglyphs
+ dup /CharStrings get dup {
+ % Stack: cidmin cidmax font CharStrings cid bitmap
+ pop dup 5 index ge { dup 4 index le { 2 copy undef } if } if pop
+ } forall pop pop pop pop
+} obind
+
+.dicttomark /ProcSet defineresource pop
+
+/.cidfonttypes where { pop } { /.cidfonttypes 6 dict def } ifelse
+.cidfonttypes begin
+
+4 % CIDFontType 4 = FontType 32
+{ dup /FontType 32 put
+ dup /CharStrings 20 dict put
+ 1 index exch .buildfont32 exch pop
+} bind def
+
+end % .cidfonttypes
+
+% Define the BuildGlyph procedure.
+% Since Type 32 fonts are indexed by CID, there is no BuildChar procedure.
+% The name %Type32BuildGlyph is known to the interpreter.
+(%Type32BuildGlyph) cvn { % <font> <cid> %Type32BuildGlyph -
+ 1 index /CharStrings get
+ % Stack: font cid CharStrings
+ dup 2 index .knownget { exch pop } { 0 get } ifelse
+ % Stack: font cid cstr
+ dup .getmetrics32
+ dup 14 gt {
+ 11 1 roll setcachedevice2
+ } {
+ 7 1 roll setcachedevice
+ } ifelse
+ % Stack: font cid cstr w h nmetrics
+ 4 -1 roll exch 1 index length 1 index sub getinterval
+ % Stack: font cid w h bitstr
+ dup () eq {
+ pop
+ } {
+ mark /Columns 4 index /Rows 5 index /K -1 /EndOfBlock false /BlackIs1 true
+ .dicttomark /CCITTFaxDecode filter 2 index 2 index true
+ % Stack: font cid w h filter w h true
+ [ 1 0 0 1 0 7 index ] 5 -1 roll imagemask
+ } ifelse
+ pop pop pop pop
+} bind def
+
+systemdict /.getmetrics32 .undef
+
+buildfontdict 32 /.buildfont32 cvx put
+
+32 dup /FontType defineresource pop
+
+.setlanguagelevel
diff --git a/pstoraster/gs_typ42.ps b/pstoraster/gs_typ42.ps
new file mode 100644
index 000000000..c50726802
--- /dev/null
+++ b/pstoraster/gs_typ42.ps
@@ -0,0 +1,52 @@
+% 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 supports the work of the GNU Project, but is not
+% affiliated with the Free Software Foundation or the GNU Project. GNU
+% Ghostscript, as distributed by Aladdin Enterprises, does not require any
+% GNU software to build or run it.
+
+% $Id: gs_typ42.ps 956 2000-03-08 23:15:43Z mike $
+% 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> <glyphindex> .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
+
+% Register the font type for definefont.
+buildfontdict 42 /.buildfont42 cvx put
diff --git a/pstoraster/gs_type1.ps b/pstoraster/gs_type1.ps
new file mode 100644
index 000000000..9a8a09c01
--- /dev/null
+++ b/pstoraster/gs_type1.ps
@@ -0,0 +1,145 @@
+% Copyright (C) 1994, 1995, 1996, 1998 Aladdin Enterprises. All rights reserved.
+%
+% This file is part of GNU Ghostscript.
+%
+% GNU Ghostscript is distributed in the hope that it will be useful, but
+% WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+% to anyone for the consequences of using it or for whether it serves any
+% particular purpose or works at all, unless he says so in writing. Refer
+% to the GNU General Public License for full details.
+%
+% Everyone is granted permission to copy, modify and redistribute GNU
+% Ghostscript, but only under the conditions described in the GNU General
+% Public License. A copy of this license is supposed to have been given
+% to you along with GNU Ghostscript so you can know your rights and
+% responsibilities. It should be in a file named COPYING. Among other
+% things, the copyright notice and this notice must be preserved on all
+% copies.
+%
+% Aladdin Enterprises supports the work of the GNU Project, but is not
+% affiliated with the Free Software Foundation or the GNU Project. GNU
+% Ghostscript, as distributed by Aladdin Enterprises, does not require any
+% GNU software to build or run it.
+
+% $Id: gs_type1.ps 956 2000-03-08 23:15:43Z mike $
+% 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 .knownget
+ { 1 get 2 div } % 1/2 the font descent
+ { -100 } ifelse put
+ }
+ if
+ dup /UnderlineThickness known not
+ { dup /UnderlineThickness 3 index /FontBBox .knownget
+ { dup 3 get exch 1 get sub 20 div } % 1/20 the font height
+ { 50 } ifelse put
+ }
+ 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
+ DISKFONTS { .loadfontdict begin } if
+ % In order to load fonts reliably, we should push systemdict
+ % here. However, Ed Taft says that Adobe implementations
+ % push userdict and nothing else!
+ % We really would just like systemdict on the stack,
+ % but fonts produced by Fontographer require a writable dictionary.
+ % However, we can't use any of the other well-known dictionaries
+ % (such as userdict), since the whole point of pushing systemdict
+ % is to make sure that nothing important has been redefined.
+ userdict begin
+ % We can't just use `run', because we want to check for .PFB files.
+ currentpacking
+ { false setpacking .loadfont1 true setpacking }
+ { .loadfont1 }
+ ifelse end
+ { stop } if
+ DISKFONTS { end } if
+ 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
+ /closefile .systemvar ]
+ }
+ 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
+
+% Register the font types for definefont.
+buildfontdict 1 /.buildfont1 cvx put
+buildfontdict 4 /.buildfont4 cvx put
diff --git a/pstoraster/gs_wan_e.ps b/pstoraster/gs_wan_e.ps
new file mode 100644
index 000000000..c58ab2021
--- /dev/null
+++ b/pstoraster/gs_wan_e.ps
@@ -0,0 +1,52 @@
+% Copyright (C) 1994, 1996, 1997 Aladdin Enterprises. All rights reserved.
+%
+% This file is part of GNU Ghostscript.
+%
+% GNU Ghostscript is distributed in the hope that it will be useful, but
+% WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+% to anyone for the consequences of using it or for whether it serves any
+% particular purpose or works at all, unless he says so in writing. Refer
+% to the GNU General Public License for full details.
+%
+% Everyone is granted permission to copy, modify and redistribute GNU
+% Ghostscript, but only under the conditions described in the GNU General
+% Public License. A copy of this license is supposed to have been given
+% to you along with GNU Ghostscript so you can know your rights and
+% responsibilities. It should be in a file named COPYING. Among other
+% things, the copyright notice and this notice must be preserved on all
+% copies.
+%
+% Aladdin Enterprises supports the work of the GNU Project, but is not
+% affiliated with the Free Software Foundation or the GNU Project. GNU
+% Ghostscript, as distributed by Aladdin Enterprises, does not require any
+% GNU software to build or run it.
+
+% $Id: gs_wan_e.ps 956 2000-03-08 23:15:43Z mike $
+% Define the WinAnsi encoding vector.
+/currentglobal where
+ { pop currentglobal { setglobal } true setglobal }
+ { { } }
+ifelse
+/WinAnsiEncoding
+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
+ /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..13a0e1c87
--- /dev/null
+++ b/pstoraster/gs_wl1_e.ps
@@ -0,0 +1,74 @@
+% 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 supports the work of the GNU Project, but is not
+% affiliated with the Free Software Foundation or the GNU Project. GNU
+% Ghostscript, as distributed by Aladdin Enterprises, does not require any
+% GNU software to build or run it.
+
+% $Id: gs_wl1_e.ps 956 2000-03-08 23:15:43Z mike $
+% 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..22310a977
--- /dev/null
+++ b/pstoraster/gs_wl2_e.ps
@@ -0,0 +1,74 @@
+% 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 supports the work of the GNU Project, but is not
+% affiliated with the Free Software Foundation or the GNU Project. GNU
+% Ghostscript, as distributed by Aladdin Enterprises, does not require any
+% GNU software to build or run it.
+
+% $Id: gs_wl2_e.ps 956 2000-03-08 23:15:43Z mike $
+% 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..9ea9224db
--- /dev/null
+++ b/pstoraster/gs_wl5_e.ps
@@ -0,0 +1,74 @@
+% 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 supports the work of the GNU Project, but is not
+% affiliated with the Free Software Foundation or the GNU Project. GNU
+% Ghostscript, as distributed by Aladdin Enterprises, does not require any
+% GNU software to build or run it.
+
+% $Id: gs_wl5_e.ps 956 2000-03-08 23:15:43Z mike $
+% 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..25ad5577d
--- /dev/null
+++ b/pstoraster/gsalloc.c
@@ -0,0 +1,1621 @@
+/* Copyright (C) 1995, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Standard memory allocator */
+#include "gx.h"
+#include "memory_.h"
+#include "gserrors.h"
+#include "gsmdebug.h"
+#include "gsstruct.h"
+#include "gxalloc.h"
+#include "stream.h" /* for clearing stream list */
+
+/*
+ * 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 = .}.
+ */
+#ifdef DEBUG
+private void
+alloc_trace(const char *chars, gs_ref_memory_t * imem, client_name_t cname,
+ gs_memory_type_ptr_t stype, uint size, const void *ptr)
+{
+ if_debug7('A', "[a%d%s]%s %s(%u) %s0x%lx\n",
+ imem->space, chars, client_name_string(cname),
+ (ptr == 0 || stype == 0 ? "" :
+ struct_type_name_string(stype)),
+ size, (chars[1] == '+' ? "= " : ""), (ulong) ptr);
+}
+private bool
+alloc_size_is_ok(gs_memory_type_ptr_t stype)
+{
+ return (stype->ssize > 0 && stype->ssize < 0x100000);
+}
+# define ALLOC_CHECK_SIZE(stype)\
+ BEGIN\
+ if (!alloc_size_is_ok(stype)) {\
+ lprintf2("size of struct type 0x%lx is 0x%lx!\n",\
+ (ulong)(stype), (ulong)((stype)->ssize));\
+ return 0;\
+ }\
+ END
+#else
+# define alloc_trace(chars, imem, cname, stype, size, ptr) DO_NOTHING
+# define ALLOC_CHECK_SIZE(stype) DO_NOTHING
+#endif
+
+/*
+ * 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_PTR3(0, gs_ref_memory_t, streams, changes, saved);
+ENUM_PTRS_END
+private RELOC_PTRS_BEGIN(ref_memory_reloc_ptrs)
+{
+ RELOC_PTR(gs_ref_memory_t, streams);
+ RELOC_PTR(gs_ref_memory_t, changes);
+ /* Don't relocate the saved pointer now -- see igc.c for details. */
+ mptr->reloc_saved = RELOC_OBJ(mptr->saved);
+}
+RELOC_PTRS_END
+
+/* Forward references */
+private ulong compute_free_objects(P1(gs_ref_memory_t *));
+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_acquire_chunk(P4(gs_ref_memory_t *, ulong, bool, client_name_t));
+private chunk_t *alloc_add_chunk(P3(gs_ref_memory_t *, ulong, client_name_t));
+void alloc_close_chunk(P1(gs_ref_memory_t *));
+
+/*
+ * Define the standard implementation (with garbage collection)
+ * of Ghostscript's memory manager interface.
+ */
+/* Raw memory procedures */
+private gs_memory_proc_alloc_bytes(i_alloc_bytes_immovable);
+private gs_memory_proc_resize_object(i_resize_object);
+private gs_memory_proc_free_object(i_free_object);
+private gs_memory_proc_status(i_status);
+private gs_memory_proc_free_all(i_free_all);
+private gs_memory_proc_consolidate_free(i_consolidate_free);
+
+/* Object memory procedures */
+private gs_memory_proc_alloc_bytes(i_alloc_bytes);
+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_object_size(i_object_size);
+private gs_memory_proc_object_type(i_object_type);
+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_enable_free(i_enable_free);
+
+/* We export the procedures for subclasses. */
+const gs_memory_procs_t gs_ref_memory_procs =
+{
+ /* Raw memory procedures */
+ i_alloc_bytes_immovable,
+ i_resize_object,
+ i_free_object,
+ i_status,
+ i_free_all,
+ i_consolidate_free,
+ /* Object memory procedures */
+ i_alloc_bytes,
+ 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_object_size,
+ i_object_type,
+ i_alloc_string,
+ i_alloc_string_immovable,
+ i_resize_string,
+ i_free_string,
+ i_register_root,
+ i_unregister_root,
+ 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_raw_memory_t *, gs_memory_type_ptr_t,
+ chunk_t **));
+gs_ref_memory_t *
+ialloc_alloc_state(gs_raw_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 = gs_ref_memory_procs;
+ iimem->parent = parent;
+ iimem->chunk_size = chunk_size;
+ iimem->large_size = ((chunk_size / 4) & -obj_align_mod) + 1;
+ iimem->is_controlled = false;
+ 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->streams = 0;
+ iimem->roots = 0;
+ iimem->num_contexts = 0;
+ iimem->saved = 0;
+ return iimem;
+}
+
+/* Allocate a 'solo' object with its own chunk. */
+private void *
+ialloc_solo(gs_raw_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_raw_alloc_struct_immovable(parent, &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);
+}
+
+/*
+ * Add a chunk to an externally controlled allocator. Such allocators
+ * allocate all objects as immovable, are not garbage-collected, and
+ * don't attempt to acquire additional memory on their own.
+ */
+int
+ialloc_add_chunk(gs_ref_memory_t *imem, ulong space, client_name_t cname)
+{
+ chunk_t *cp;
+
+ /* Allow acquisition of this chunk. */
+ imem->is_controlled = false;
+ imem->large_size = imem->chunk_size;
+ imem->limit = max_long;
+ imem->gc_status.max_vm = max_long;
+
+ /* Acquire the chunk. */
+ cp = alloc_add_chunk(imem, space, cname);
+
+ /*
+ * Make all allocations immovable. Since the "movable" allocators
+ * allocate within existing chunks, whereas the "immovable" ones
+ * allocate in new chunks, we equate the latter to the former, even
+ * though this seems backwards.
+ */
+ imem->procs.alloc_bytes_immovable = imem->procs.alloc_bytes;
+ imem->procs.alloc_struct_immovable = imem->procs.alloc_struct;
+ imem->procs.alloc_byte_array_immovable = imem->procs.alloc_byte_array;
+ imem->procs.alloc_struct_array_immovable = imem->procs.alloc_struct_array;
+ imem->procs.alloc_string_immovable = imem->procs.alloc_string;
+
+ /* Disable acquisition of additional chunks. */
+ imem->is_controlled = true;
+ imem->limit = 0;
+
+ return (cp ? 0 : gs_note_error(gs_error_VMerror));
+}
+
+/* Prepare for a GC by clearing the stream list. */
+/* This probably belongs somewhere else.... */
+void
+ialloc_gc_prepare(gs_ref_memory_t * mem)
+{ /*
+ * We have to unlink every stream from its neighbors,
+ * so that referenced streams don't keep all streams around.
+ */
+ while (mem->streams != 0) {
+ stream *s = mem->streams;
+
+ mem->streams = s->next;
+ s->prev = s->next = 0;
+ }
+}
+
+/* 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->lost.objects = 0;
+ mem->lost.refs = 0;
+ mem->lost.strings = 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);
+}
+
+/*
+ * Free all the memory owned by the allocator, except the allocator itself.
+ * Note that this only frees memory at the current save level: the client
+ * is responsible for restoring to the outermost level if desired.
+ */
+private void
+i_free_all(gs_memory_t * mem, uint free_mask, client_name_t cname)
+{
+ gs_ref_memory_t * const imem = (gs_ref_memory_t *)mem;
+ chunk_t *cp;
+
+ if (free_mask & FREE_ALL_DATA) {
+ chunk_t *csucc;
+
+ /*
+ * Free the chunks in reverse order, to encourage LIFO behavior.
+ * Don't free the chunk holding the allocator itself.
+ */
+ for (cp = imem->clast; cp != 0; cp = csucc) {
+ csucc = cp->cprev; /* save before freeing */
+ if (cp->cbase + sizeof(obj_header_t) != (byte *)mem)
+ alloc_free_chunk(cp, imem);
+ }
+ }
+ if (free_mask & FREE_ALL_ALLOCATOR) {
+ /* Free the chunk holding the allocator itself. */
+ for (cp = imem->clast; cp != 0; cp = cp->cprev)
+ if (cp->cbase + sizeof(obj_header_t) == (byte *)mem) {
+ alloc_free_chunk(cp, imem);
+ break;
+ }
+ }
+}
+
+/* ================ 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)
+{
+ gs_ref_memory_t * const imem = (gs_ref_memory_t *)mem;
+ obj_header_t *obj;
+ obj_header_t **pfl;
+
+ IF_FREELIST_ALLOC(obj, imem, size, &st_bytes, pfl)
+ alloc_trace(":+bF", imem, cname, NULL, size, obj);
+ ELSEIF_LIFO_ALLOC(obj, imem, size, &st_bytes)
+ alloc_trace(":+b ", imem, cname, NULL, size, obj);
+ ELSE_ALLOC
+ {
+ obj = alloc_obj(imem, size, &st_bytes, false, cname);
+ if (obj == 0)
+ return 0;
+ alloc_trace(":+b.", imem, cname, NULL, size, obj);
+ }
+ return (byte *) obj;
+}
+private byte *
+i_alloc_bytes_immovable(gs_memory_t * mem, uint size, client_name_t cname)
+{
+ gs_ref_memory_t * const imem = (gs_ref_memory_t *)mem;
+ obj_header_t *obj = alloc_obj(imem, size, &st_bytes, true, cname);
+
+ if (obj == 0)
+ return 0;
+ alloc_trace("|+b.", imem, cname, NULL, size, obj);
+ return (byte *) obj;
+}
+private void *
+i_alloc_struct(gs_memory_t * mem, gs_memory_type_ptr_t pstype,
+ client_name_t cname)
+{
+ gs_ref_memory_t * const imem = (gs_ref_memory_t *)mem;
+ uint size = pstype->ssize;
+ obj_header_t *obj;
+ obj_header_t **pfl;
+
+ ALLOC_CHECK_SIZE(pstype);
+ IF_FREELIST_ALLOC(obj, imem, size, pstype, pfl)
+ alloc_trace(":+<F", imem, cname, pstype, size, obj);
+ ELSEIF_LIFO_ALLOC(obj, imem, size, pstype)
+ alloc_trace(":+< ", imem, cname, pstype, size, obj);
+ ELSE_ALLOC
+ {
+ obj = alloc_obj(imem, size, pstype, false, cname);
+ if (obj == 0)
+ return 0;
+ alloc_trace(":+<.", imem, cname, pstype, size, obj);
+ }
+ return obj;
+}
+private void *
+i_alloc_struct_immovable(gs_memory_t * mem, gs_memory_type_ptr_t pstype,
+ client_name_t cname)
+{
+ gs_ref_memory_t * const imem = (gs_ref_memory_t *)mem;
+ uint size = pstype->ssize;
+ obj_header_t *obj;
+
+ ALLOC_CHECK_SIZE(pstype);
+ obj = alloc_obj(imem, size, pstype, true, cname);
+ alloc_trace("|+<.", imem, cname, pstype, size, obj);
+ return obj;
+}
+private byte *
+i_alloc_byte_array(gs_memory_t * mem, uint num_elements, uint elt_size,
+ client_name_t cname)
+{
+ gs_ref_memory_t * const imem = (gs_ref_memory_t *)mem;
+ 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)
+{
+ gs_ref_memory_t * const imem = (gs_ref_memory_t *)mem;
+ 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)
+{
+ gs_ref_memory_t * const imem = (gs_ref_memory_t *)mem;
+ obj_header_t *obj;
+
+ ALLOC_CHECK_SIZE(pstype);
+ 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)
+{
+ gs_ref_memory_t * const imem = (gs_ref_memory_t *)mem;
+ obj_header_t *obj;
+
+ ALLOC_CHECK_SIZE(pstype);
+ 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)
+{
+ gs_ref_memory_t * const imem = (gs_ref_memory_t *)mem;
+ 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;
+ ulong new_size_rounded;
+ void *new_obj;
+
+ if ((byte *)obj + obj_align_round(old_size) == imem->cc.cbot &&
+ imem->cc.ctop - (byte *)obj >=
+ (new_size_rounded = obj_align_round(new_size))
+ ) {
+ imem->cc.cbot = (byte *)obj + new_size_rounded;
+ pp->o_size = 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)
+{
+ gs_ref_memory_t * const imem = (gs_ref_memory_t *)mem;
+ obj_header_t *pp;
+ gs_memory_type_ptr_t pstype;
+
+ struct_proc_finalize((*finalize));
+ uint size;
+
+ if (ptr == 0)
+ return;
+ pp = (obj_header_t *) ptr - 1;
+ pstype = pp->o_type;
+#ifdef DEBUG
+ if (gs_debug_c('?')) {
+ chunk_locator_t cld;
+
+ if (pstype == &st_free) {
+ lprintf2("%s: object 0x%lx already free!\n",
+ client_name_string(cname), (ulong) ptr);
+ return; /*gs_abort(); */
+ }
+ /* Check that this allocator owns the object being freed. */
+ cld.memory = imem;
+ while ((cld.cp = cld.memory->clast),
+ !chunk_locate_ptr(ptr, &cld)
+ ) {
+ if (!cld.memory->saved) {
+ lprintf3("%s: freeing 0x%lx, not owned by memory 0x%lx!\n",
+ client_name_string(cname), (ulong) ptr,
+ (ulong) mem);
+ return; /*gs_abort(); */
+ }
+ /****** HACK: we know the saved state is the first ******
+ ****** member of an alloc_save_t. ******/
+ cld.memory = (gs_ref_memory_t *) cld.memory->saved;
+ }
+ /* Check that the object is in the allocated region. */
+ if (cld.memory == imem && cld.cp == imem->pcc)
+ cld.cp = &imem->cc;
+ if (!(ptr_between((const byte *)pp, cld.cp->cbase,
+ cld.cp->cbot))
+ ) {
+ lprintf5("%s: freeing 0x%lx,\n\toutside chunk 0x%lx cbase=0x%lx, cbot=0x%lx!\n",
+ client_name_string(cname), (ulong) ptr,
+ (ulong) cld.cp, (ulong) cld.cp->cbase,
+ (ulong) cld.cp->cbot);
+ return; /*gs_abort(); */
+ }
+ }
+#endif
+ size = pre_obj_contents_size(pp);
+ finalize = pstype->finalize;
+ if (finalize != 0) {
+ if_debug3('u', "[u]finalizing %s 0x%lx (%s)\n",
+ struct_type_name_string(pstype),
+ (ulong) ptr, client_name_string(cname));
+ (*finalize) (ptr);
+ }
+ if ((byte *) ptr + obj_align_round(size) == imem->cc.cbot) {
+ alloc_trace(":-o ", imem, cname, pstype, size, ptr);
+ 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 (gs_debug_c('a'))
+ alloc_trace(
+ (chunk_locate_ptr(ptr, &cld) ? ":-oL" : ":-o~"),
+ imem, cname, pstype, size, ptr);
+ }
+#endif
+ cl.memory = imem;
+ cl.cp = 0;
+ if (chunk_locate_ptr(ptr, &cl)) {
+ if (!imem->is_controlled)
+ alloc_free_chunk(cl.cp, imem);
+ return;
+ }
+ /* Don't overwrite even if gs_alloc_debug is set. */
+ }
+ if (size <= max_freelist_size &&
+ obj_align_round(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;
+ alloc_trace(":-oF", imem, cname, pstype, size, ptr);
+ 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);
+ }
+ alloc_trace(":-o#", imem, cname, pstype, size, ptr);
+ imem->lost.objects += obj_size_round(size);
+}
+private byte *
+i_alloc_string(gs_memory_t * mem, uint nbytes, client_name_t cname)
+{
+ gs_ref_memory_t * const imem = (gs_ref_memory_t *)mem;
+ 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_acquire_chunk(imem, (ulong) imem->chunk_size, true, "chunk");
+
+ if (cp == 0)
+ return 0;
+ alloc_close_chunk(imem);
+ imem->pcc = cp;
+ imem->cc = *imem->pcc;
+ gs_alloc_fill(imem->cc.cbase, gs_alloc_fill_free,
+ imem->cc.climit - imem->cc.cbase);
+ goto top;
+ }
+}
+private byte *
+i_alloc_string_immovable(gs_memory_t * mem, uint nbytes, client_name_t cname)
+{
+ gs_ref_memory_t * const imem = (gs_ref_memory_t *)mem;
+ byte *str;
+ /* Give it a chunk all its own. */
+ uint asize = string_chunk_space(nbytes) + sizeof(chunk_head_t);
+ chunk_t *cp = alloc_acquire_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)
+{
+ gs_ref_memory_t * const imem = (gs_ref_memory_t *)mem;
+ byte *ptr;
+
+ if (data == imem->cc.ctop &&
+ (new_num < old_num ||
+ imem->cc.ctop - imem->cc.cbot > new_num - old_num)
+ ) { /* Resize in place. */
+ 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;
+ memmove(ptr, data, min(old_num, new_num));
+#ifdef DEBUG
+ if (new_num > old_num)
+ gs_alloc_fill(ptr + old_num, gs_alloc_fill_alloc,
+ new_num - old_num);
+ else
+ gs_alloc_fill(data, gs_alloc_fill_free, old_num - new_num);
+#endif
+ } 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)
+{
+ gs_ref_memory_t * const imem = (gs_ref_memory_t *)mem;
+ 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->lost.strings += nbytes;
+ }
+ gs_alloc_fill(data, gs_alloc_fill_free, nbytes);
+}
+
+private void
+i_status(gs_memory_t * mem, gs_memory_status_t * pstat)
+{
+ gs_ref_memory_t * const imem = (gs_ref_memory_t *)mem;
+ ulong unused = imem->lost.refs + imem->lost.strings;
+ 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;
+ }
+ }
+ unused += compute_free_objects(imem);
+ 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 ------ */
+
+/* Compute the amount of free object space by scanning free lists. */
+private ulong
+compute_free_objects(gs_ref_memory_t * mem)
+{
+ ulong unused = mem->lost.objects;
+ int i;
+
+ /* Add up space on free lists. */
+ for (i = 0; i < num_freelists; i++) {
+ uint free_size =
+ (i << log2_obj_align_mod) + sizeof(obj_header_t);
+ const obj_header_t *pfree;
+
+ for (pfree = mem->freelists[i]; pfree != 0;
+ pfree = *(const obj_header_t * const *)pfree
+ )
+ unused += free_size;
+ }
+ return unused;
+}
+
+/* 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) {
+ /*
+ * Give the object a chunk all its own. Note that this case does
+ * not occur if is_controlled is true.
+ */
+ ulong asize =
+ ((lsize + obj_align_mask) & -obj_align_mod) +
+ sizeof(obj_header_t);
+ chunk_t *cp =
+ alloc_acquire_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;
+ ptr->o_large = 1;
+ pre_obj_set_large_size(ptr, lsize);
+ } else {
+ uint asize = obj_size_round((uint) lsize);
+ bool consolidate = mem->is_controlled;
+
+ while (mem->cc.ctop -
+ (byte *) (ptr = (obj_header_t *) mem->cc.cbot)
+ <= asize + sizeof(obj_header_t)) {
+ if (consolidate) {
+ /* Try consolidating free space. */
+ gs_consolidate_free((gs_memory_t *)mem);
+ consolidate = false;
+ continue;
+ } else {
+ /* Add another chunk. */
+ chunk_t *cp =
+ alloc_add_chunk(mem, (ulong)mem->chunk_size, "chunk");
+
+ if (cp == 0)
+ return 0;
+ }
+ }
+ 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;
+}
+
+/* Consolidate free objects. */
+void
+ialloc_consolidate_free(gs_ref_memory_t *mem)
+{
+ chunk_t *cp;
+ chunk_t *cprev;
+ /*
+ * We're going to recompute lost.objects, by subtracting the
+ * amount of space reclaimed minus the amount of that space that
+ * was on free lists.
+ */
+ ulong found = 0;
+
+ alloc_close_chunk(mem);
+
+ /* Visit chunks in reverse order to encourage LIFO behavior. */
+ for (cp = mem->clast; cp != 0; cp = cprev) {
+ obj_header_t *begin_free = 0;
+
+ cprev = cp->cprev;
+ SCAN_CHUNK_OBJECTS(cp)
+ DO_ALL
+ if (pre->o_type == &st_free) {
+ if (begin_free == 0)
+ begin_free = pre;
+ } else
+ begin_free = 0;
+ END_OBJECTS_SCAN
+ if (begin_free) {
+ /* We found free objects at the top of the object area. */
+ int i;
+
+ found += (byte *)cp->cbot - (byte *)begin_free;
+ /* Remove the free objects from the freelists. */
+ for (i = 0; i < num_freelists; i++) {
+ obj_header_t *pfree;
+ obj_header_t **ppfprev = &mem->freelists[i];
+ uint free_size =
+ (i << log2_obj_align_mod) + sizeof(obj_header_t);
+
+ while ((pfree = *ppfprev) != 0)
+ if (ptr_ge(pfree, begin_free) &&
+ ptr_lt(pfree, cp->cbot)
+ ) { /* We're removing an object. */
+ *ppfprev = *(obj_header_t **) pfree;
+ found -= free_size;
+ } else
+ ppfprev = (obj_header_t **) pfree;
+ }
+ } else
+ begin_free = (obj_header_t *)cp->cbot;
+ if (begin_free == (obj_header_t *) cp->cbase &&
+ cp->ctop == cp->climit
+ ) { /* The entire chunk is free. */
+ chunk_t *cnext = cp->cnext;
+
+ if (!mem->is_controlled)
+ alloc_free_chunk(cp, mem);
+ if (mem->pcc == cp)
+ mem->pcc =
+ (cnext == 0 ? cprev : cprev == 0 ? cnext :
+ cprev->cbot - cprev->ctop >
+ cnext->cbot - cnext->ctop ? cprev :
+ cnext);
+ } else if (begin_free != (obj_header_t *)cp->cbot) {
+ if_debug4('a', "[a]resetting chunk 0x%lx cbot from 0x%lx to 0x%lx (%lu free)\n",
+ (ulong) cp, (ulong) cp->cbot, (ulong) begin_free,
+ (ulong) ((byte *) cp->cbot - (byte *) begin_free));
+ cp->cbot = (byte *) begin_free;
+ }
+ }
+ mem->lost.objects -= found;
+ alloc_open_chunk(mem);
+}
+private void
+i_consolidate_free(gs_memory_t *mem)
+{
+ ialloc_consolidate_free((gs_ref_memory_t *)mem);
+}
+
+/* ================ Roots ================ */
+
+/* Register a root. */
+private int
+i_register_root(gs_memory_t * mem, gs_gc_root_t * rp, gs_ptr_type_t ptype,
+ void **up, client_name_t cname)
+{
+ gs_ref_memory_t * const imem = (gs_ref_memory_t *)mem;
+
+ if (rp == NULL) {
+ rp = gs_raw_alloc_struct_immovable(imem->parent, &st_gc_root_t,
+ "i_register_root");
+ if (rp == 0)
+ return_error(gs_error_VMerror);
+ rp->free_on_unregister = true;
+ } else
+ rp->free_on_unregister = false;
+ 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;
+ return 0;
+}
+
+/* Unregister a root. */
+private void
+i_unregister_root(gs_memory_t * mem, gs_gc_root_t * rp, client_name_t cname)
+{
+ gs_ref_memory_t * const imem = (gs_ref_memory_t *)mem;
+ 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;
+ if (rp->free_on_unregister)
+ gs_free_object(imem->parent, rp, "i_unregister_root");
+}
+
+/* ================ 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 * imem)
+{
+ byte *cdata = cp->cbase;
+ chunk_t *icp;
+ chunk_t *prev;
+
+ for (icp = imem->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;
+ }
+}
+
+/* Add a chunk for ordinary allocation. */
+private chunk_t *
+alloc_add_chunk(gs_ref_memory_t * mem, ulong csize, client_name_t cname)
+{
+ chunk_t *cp = alloc_acquire_chunk(mem, csize, true, cname);
+
+ if (cp) {
+ alloc_close_chunk(mem);
+ mem->pcc = cp;
+ mem->cc = *mem->pcc;
+ gs_alloc_fill(mem->cc.cbase, gs_alloc_fill_free,
+ mem->cc.climit - mem->cc.cbase);
+ }
+ return cp;
+}
+
+/* Acquire 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_acquire_chunk(gs_ref_memory_t * mem, ulong csize, bool has_strings,
+ client_name_t cname)
+{
+ gs_raw_memory_t *parent = mem->parent;
+ chunk_t *cp;
+ byte *cdata;
+
+#if arch_sizeof_long > arch_sizeof_int
+ /* If csize is larger than max_uint, punt. */
+ if (csize != (uint) csize)
+ return 0;
+#endif
+ cp = gs_raw_alloc_struct_immovable(parent, &st_chunk, cname);
+ 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;
+ }
+ cdata = gs_alloc_bytes_immovable(parent, csize, 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 += st_chunk.ssize + csize;
+ 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);
+ cp->sfree1 = (ushort *) cp->sreloc;
+ } else { /* No strings, don't need the string GC tables. */
+ cp->climit = cp->cend;
+ cp->sfree1 = 0;
+ cp->smark = 0;
+ cp->smark_size = 0;
+ cp->sreloc = 0;
+ }
+ cp->ctop = cp->climit;
+ alloc_init_free_strings(cp);
+}
+
+/* Initialize the string freelists in a chunk. */
+void
+alloc_init_free_strings(chunk_t * cp)
+{
+ if (cp->sfree1)
+ memset(cp->sfree1, 0,
+ ((cp->climit - csbase(cp) + 255) >> 8) *
+ sizeof(*cp->sfree1));
+ cp->sfree = 0;
+}
+
+/* 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')) {
+ dlprintf1("[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')) {
+ dlprintf1("[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 the GC. Since we eventually use
+ * this to free the chunk containing the allocator itself, we must be
+ * careful not to reference anything in the allocator after freeing the
+ * chunk data.
+ */
+void
+alloc_free_chunk(chunk_t * cp, gs_ref_memory_t * mem)
+{
+ gs_raw_memory_t *parent = mem->parent;
+
+ alloc_unlink_chunk(cp, mem);
+ mem->allocated -= st_chunk.ssize;
+ if (mem->cfreed.cp == cp)
+ mem->cfreed.cp = 0;
+ if (cp->outer == 0) {
+ byte *cdata = (byte *) cp->chead;
+
+ mem->allocated -= cp->cend - cdata;
+ gs_free_object(parent, cdata, "alloc_free_chunk(data)");
+ } else
+ cp->outer->inner_count--;
+ 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 *ptr, chunk_locator_t * clp)
+{
+ register chunk_t *cp = clp->cp;
+
+ if (cp == 0) {
+ cp = clp->memory->cfirst;
+ if (cp == 0)
+ return false;
+ }
+ 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);
+}
+
+/* ------ Debugging printout ------ */
+
+#ifdef DEBUG
+
+#include "string_.h"
+
+/*
+ * Define the options for a memory dump. These may be or'ed together.
+ */
+typedef enum {
+ dump_do_default = 0, /* pro forma */
+ dump_do_strings = 1,
+ dump_do_type_addresses = 2,
+ dump_do_no_types = 4,
+ dump_do_pointers = 8,
+ dump_do_pointed_strings = 16, /* only if do_pointers also set */
+ dump_do_contents = 32,
+ dump_do_marks = 64
+} dump_options_t;
+
+/*
+ * Define all the parameters controlling what gets dumped.
+ */
+typedef struct dump_control_s {
+ dump_options_t options;
+ const byte *bottom;
+ const byte *top;
+} dump_control_t;
+
+inline private bool
+obj_in_control_region(const void *obot, const void *otop,
+ const dump_control_t *pdc)
+{
+ return
+ ((pdc->bottom == NULL || ptr_gt(otop, pdc->bottom)) &&
+ (pdc->top == NULL || ptr_lt(obot, pdc->top)));
+}
+
+const dump_control_t dump_control_default =
+{
+ dump_do_default, NULL, NULL
+};
+const dump_control_t dump_control_all =
+{
+ dump_do_strings | dump_do_type_addresses | dump_do_pointers |
+ dump_do_pointed_strings | dump_do_contents, NULL, NULL
+};
+
+/*
+ * Internal procedure to dump a block of memory, in hex and optionally
+ * also as characters.
+ */
+private void
+debug_indent(int indent)
+{
+ int i;
+
+ for (i = indent; i > 0; --i)
+ dputc(' ');
+}
+private void
+debug_dump_contents(const byte * bot, const byte * top, int indent,
+ bool as_chars)
+{
+ const byte *block;
+
+#define block_size 16
+
+ if (bot >= top)
+ return;
+ for (block = bot - ((bot - (byte *) 0) & (block_size - 1));
+ block < top; block += block_size
+ ) {
+ int i;
+ char label[12];
+
+ /* Check for repeated blocks. */
+ if (block >= bot + block_size &&
+ block <= top - (block_size * 2) &&
+ !memcmp(block, block - block_size, block_size) &&
+ !memcmp(block, block + block_size, block_size)
+ ) {
+ if (block < bot + block_size * 2 ||
+ memcmp(block, block - block_size * 2, block_size)
+ ) {
+ debug_indent(indent);
+ dputs(" ...\n");
+ }
+ continue;
+ }
+ sprintf(label, "0x%lx:", (ulong) block);
+ debug_indent(indent);
+ dputs(label);
+ for (i = 0; i < block_size; ++i) {
+ const char *sepr = ((i & 3) == 0 && i != 0 ? " " : " ");
+
+ dputs(sepr);
+ if (block + i >= bot && block + i < top)
+ dprintf1("%02x", block[i]);
+ else
+ dputs(" ");
+ }
+ dputc('\n');
+ if (as_chars) {
+ debug_indent(indent + strlen(label));
+ for (i = 0; i < block_size; ++i) {
+ byte ch;
+
+ if ((i & 3) == 0 && i != 0)
+ dputc(' ');
+ if (block + i >= bot && block + i < top &&
+ (ch = block[i]) >= 32 && ch <= 126
+ )
+ dprintf1(" %c", ch);
+ else
+ dputs(" ");
+ }
+ dputc('\n');
+ }
+ }
+#undef block_size
+}
+
+/* Print one object with the given options. */
+/* Relevant options: type_addresses, no_types, pointers, pointed_strings, */
+/* contents. */
+void
+debug_print_object(const void *obj, const dump_control_t * control)
+{
+ const obj_header_t *pre = ((const obj_header_t *)obj) - 1;
+ ulong size = pre_obj_contents_size(pre);
+ const gs_memory_struct_type_t *type = pre->o_type;
+ dump_options_t options = control->options;
+
+ dprintf3(" pre=0x%lx(obj=0x%lx) size=%lu", (ulong) pre, (ulong) obj,
+ size);
+ switch (options & (dump_do_type_addresses | dump_do_no_types)) {
+ case dump_do_type_addresses + dump_do_no_types: /* addresses only */
+ dprintf1(" type=0x%lx", (ulong) type);
+ break;
+ case dump_do_type_addresses: /* addresses & names */
+ dprintf2(" type=%s(0x%lx)", struct_type_name_string(type),
+ (ulong) type);
+ break;
+ case 0: /* names only */
+ dprintf1(" type=%s", struct_type_name_string(type));
+ case dump_do_no_types: /* nothing */
+ ;
+ }
+ if (options & dump_do_marks) {
+ if (pre->o_large)
+ dprintf1(" lmark=%d", pre->o_lmark);
+ else
+ dprintf2(" smark/back=%u (0x%x)", pre->o_smark, pre->o_smark);
+ }
+ dputc('\n');
+ if (type == &st_free)
+ return;
+ if (options & dump_do_pointers) {
+ struct_proc_enum_ptrs((*proc)) = type->enum_ptrs;
+ uint index = 0;
+ const void *ptr;
+ gs_ptr_type_t ptype;
+
+ /*
+ * NOTE: the following cast should be unnecessary, but that
+ * will require adding 'const' to the first prototype argument
+ * of struct_proc_enum_ptrs.
+ */
+ if (proc != gs_no_struct_enum_ptrs)
+ for (; (ptype = (*proc) ((obj_header_t *) pre + 1, size, index, &ptr, type, NULL)) != 0;
+ ++index
+ ) {
+ dprintf1(" ptr %u: ", index);
+ if (ptype == ptr_string_type || ptype == ptr_const_string_type) {
+ const gs_const_string *str = (const gs_const_string *)ptr;
+
+ dprintf2("0x%lx(%u)", (ulong) str->data, str->size);
+ if (options & dump_do_pointed_strings) {
+ dputs(" =>\n");
+ debug_dump_contents(str->data, str->data + str->size, 6,
+ true);
+ } else {
+ dputc('\n');
+ }
+ } else {
+ dprintf1((ptr_between(ptr, obj, (const byte *)obj + size) ?
+ "(0x%lx)\n" : "0x%lx\n"), (ulong) ptr);
+ }
+ }
+ }
+ if (options & dump_do_contents) {
+ debug_dump_contents((const byte *)obj, (const byte *)obj + size,
+ 0, false);
+ }
+}
+
+/* Print the contents of a chunk with the given options. */
+/* Relevant options: all. */
+void
+debug_dump_chunk(const chunk_t * cp, const dump_control_t * control)
+{
+ 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"));
+
+ dprintf2(" sfree1=0x%lx sfree=0x%x\n",
+ (ulong) cp->sfree1, cp->sfree);
+ if (control->options & dump_do_strings) {
+ debug_dump_contents((control->bottom == 0 ? cp->ctop :
+ max(control->bottom, cp->ctop)),
+ (control->top == 0 ? cp->climit :
+ min(control->top, cp->climit)),
+ 0, true);
+ }
+ SCAN_CHUNK_OBJECTS(cp)
+ DO_ALL
+ if (obj_in_control_region(pre + 1,
+ (const byte *)(pre + 1) + size,
+ control)
+ )
+ debug_print_object(pre + 1, control);
+/* 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
+}
+void
+debug_print_chunk(const chunk_t * cp)
+{
+ dump_control_t control;
+
+ control = dump_control_default;
+ debug_dump_chunk(cp, &control);
+}
+
+/* Print the contents of all chunks managed by an allocator. */
+/* Relevant options: all. */
+void
+debug_dump_memory(const gs_ref_memory_t * mem, const dump_control_t * control)
+{
+ const chunk_t *mcp;
+
+ for (mcp = mem->cfirst; mcp != 0; mcp = mcp->cnext) {
+ const chunk_t *cp = (mcp == mem->pcc ? &mem->cc : mcp);
+
+ if (obj_in_control_region(cp->cbase, cp->cend, control))
+ debug_dump_chunk(cp, control);
+ }
+}
+
+#endif /* DEBUG */
diff --git a/pstoraster/gsalloc.h b/pstoraster/gsalloc.h
new file mode 100644
index 000000000..d5f855f37
--- /dev/null
+++ b/pstoraster/gsalloc.h
@@ -0,0 +1,87 @@
+/* Copyright (C) 1995, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Memory allocator extensions for standard allocator */
+
+#ifndef gsalloc_INCLUDED
+# define gsalloc_INCLUDED
+
+/* The following should not be needed at this level! */
+
+#ifndef gs_ref_memory_DEFINED
+# define gs_ref_memory_DEFINED
+typedef struct gs_ref_memory_s gs_ref_memory_t;
+#endif
+
+/*
+ * 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 *));
+
+/* ------ Initialization ------ */
+
+/*
+ * Allocate and mostly initialize the state of an allocator (system, global,
+ * or local). Does not initialize global or space.
+ */
+gs_ref_memory_t *ialloc_alloc_state(P2(gs_raw_memory_t *, uint));
+
+/*
+ * Add a chunk to an externally controlled allocator. Such allocators
+ * allocate all objects as immovable, are not garbage-collected, and
+ * don't attempt to acquire additional memory (or free chunks) on their own.
+ */
+int ialloc_add_chunk(P3(gs_ref_memory_t *, ulong, client_name_t));
+
+/* ------ Internal routines ------ */
+
+/* Prepare for a GC. */
+void ialloc_gc_prepare(P1(gs_ref_memory_t *));
+
+/* 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 *));
+
+/* Consolidate free objects. */
+void ialloc_consolidate_free(P1(gs_ref_memory_t *));
+
+#endif /* gsalloc_INCLUDED */
diff --git a/pstoraster/gsalpha.c b/pstoraster/gsalpha.c
new file mode 100644
index 000000000..45f185c73
--- /dev/null
+++ b/pstoraster/gsalpha.c
@@ -0,0 +1,48 @@
+/* Copyright (C) 1997 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Graphics state alpha value access */
+#include "gx.h"
+#include "gsalpha.h"
+#include "gxdcolor.h"
+#include "gzstate.h"
+
+/* setalpha */
+int
+gs_setalpha(gs_state * pgs, floatp alpha)
+{
+ pgs->alpha =
+ (gx_color_value) (alpha < 0 ? 0 : alpha > 1 ? gx_max_color_value :
+ 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;
+}
diff --git a/pstoraster/gsalpha.h b/pstoraster/gsalpha.h
new file mode 100644
index 000000000..772553ff8
--- /dev/null
+++ b/pstoraster/gsalpha.h
@@ -0,0 +1,41 @@
+/* Copyright (C) 1997 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* API for alpha value in graphics state */
+
+#ifndef gsalpha_INCLUDED
+# define gsalpha_INCLUDED
+
+/*
+ * This tiny little file is separate so that it can be included by
+ * gsstate.c for initializing the alpha value, even in configurations
+ * that don't have full alpha support.
+ */
+
+/* Set/read alpha value. */
+int gs_setalpha(P2(gs_state *, floatp));
+float gs_currentalpha(P1(const gs_state *));
+
+#endif /* gsalpha_INCLUDED */
diff --git a/pstoraster/gsalphac.h b/pstoraster/gsalphac.h
new file mode 100644
index 000000000..5afe0c3ef
--- /dev/null
+++ b/pstoraster/gsalphac.h
@@ -0,0 +1,71 @@
+/* Copyright (C) 1997 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Alpha-compositing interface */
+
+#ifndef gsalphac_INCLUDED
+# define gsalphac_INCLUDED
+
+#include "gscompt.h"
+
+/*
+ * Define the compositing operations. These values must match the ones in
+ * dpsNeXT.h.
+ */
+typedef enum {
+ composite_Clear = 0,
+ composite_Copy,
+ composite_Sover,
+ composite_Sin,
+ composite_Sout,
+ composite_Satop,
+ composite_Dover,
+ composite_Din,
+ composite_Dout,
+ composite_Datop,
+ composite_Xor,
+ composite_PlusD,
+ composite_PlusL,
+#define composite_last composite_PlusL
+ composite_Highlight, /* (only for compositerect) */
+#define compositerect_last composite_Highlight
+ composite_Dissolve /* (not for PostScript composite operators) */
+#define composite_op_last composite_Dissolve
+} gs_composite_op_t;
+
+/*
+ * Define parameters for alpha-compositing.
+ */
+typedef struct gs_composite_alpha_params_s {
+ gs_composite_op_t op;
+ float delta; /* only for Dissolve */
+} gs_composite_alpha_params_t;
+
+/* Create an alpha-compositing object. */
+int gs_create_composite_alpha(P3(gs_composite_t ** ppcte,
+ const gs_composite_alpha_params_t * params,
+ gs_memory_t * mem));
+
+#endif /* gsalphac_INCLUDED */
diff --git a/pstoraster/gsargs.c b/pstoraster/gsargs.c
new file mode 100644
index 000000000..7c5e3c0fe
--- /dev/null
+++ b/pstoraster/gsargs.c
@@ -0,0 +1,225 @@
+/* Copyright (C) 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Command line argument list management */
+#include "ctype_.h"
+#include "stdio_.h"
+#include "string_.h"
+#include "gsexit.h"
+#include "gsmemory.h"
+#include "gsargs.h"
+
+/* Initialize an arg list. */
+void
+arg_init(arg_list * pal, const char **argv, int argc,
+ FILE * (*arg_fopen) (P2(const char *fname, void *fopen_data)),
+ void *fopen_data)
+{
+ pal->expand_ats = true;
+ pal->arg_fopen = arg_fopen;
+ pal->fopen_data = fopen_data;
+ pal->argp = argv + 1;
+ pal->argn = argc - 1;
+ pal->depth = 0;
+}
+
+/* Push a string onto an arg list. */
+void
+arg_push_memory_string(arg_list * pal, const char *str, gs_memory_t * mem)
+{
+ arg_source *pas;
+
+ if (pal->depth == arg_depth_max) {
+ lprintf("Too much nesting of @-files.\n");
+ gs_exit(1);
+ }
+ pas = &pal->sources[pal->depth];
+ pas->is_file = false;
+ pas->u.s.chars = str;
+ pas->u.s.memory = mem;
+ pas->u.s.str = str;
+ pal->depth++;
+}
+
+/* Clean up an arg list. */
+void
+arg_finit(arg_list * pal)
+{
+ while (pal->depth) {
+ arg_source *pas = &pal->sources[--(pal->depth)];
+
+ if (pas->is_file)
+ fclose(pas->u.file);
+ else if (pas->u.s.memory)
+ gs_free_object(pas->u.s.memory, (void *)pas->u.s.chars, "arg_finit");
+ }
+}
+
+/* Get the next arg from a list. */
+/* Note that these are not copied to the heap. */
+const char *
+arg_next(arg_list * pal)
+{
+ arg_source *pas;
+ FILE *f;
+ const char *astr = 0; /* initialized only to pacify gcc */
+ char *cstr;
+ const char *result;
+ int endc;
+ int c, i;
+ bool in_quote, eol;
+
+ top:pas = &pal->sources[pal->depth - 1];
+ if (pal->depth == 0) {
+ if (pal->argn <= 0) /* all done */
+ return 0;
+ pal->argn--;
+ result = *(pal->argp++);
+ goto at;
+ }
+ if (pas->is_file)
+ f = pas->u.file, endc = EOF;
+ else
+ astr = pas->u.s.str, f = NULL, endc = 0;
+ result = cstr = pal->cstr;
+#define cfsgetc() (f == NULL ? (*astr ? *astr++ : 0) : fgetc(f))
+#define is_eol(c) (c == '\r' || c == '\n')
+ i = 0;
+ in_quote = false;
+ eol = true;
+ c = cfsgetc();
+ for (i = 0;;) {
+ if (c == endc) {
+ if (in_quote) {
+ cstr[i] = 0;
+ fprintf(stdout, "Unterminated quote in @-file: %s\n", cstr);
+ gs_exit(1);
+ }
+ if (i == 0) {
+ /* EOF before any argument characters. */
+ if (f != NULL)
+ fclose(f);
+ else if (pas->u.s.memory)
+ gs_free_object(pas->u.s.memory, (void *)pas->u.s.chars,
+ "arg_next");
+ pal->depth--;
+ goto top;
+ }
+ break;
+ }
+ /* c != endc */
+ if (isspace(c)) {
+ if (i == 0) {
+ c = cfsgetc();
+ continue;
+ }
+ if (!in_quote)
+ break;
+ }
+ /* c isn't leading or terminating whitespace. */
+ if (c == '#' && eol) {
+ /* Skip a comment. */
+ do {
+ c = cfsgetc();
+ } while (!(c == endc || is_eol(c)));
+ if (c == '\r')
+ c = cfsgetc();
+ if (c == '\n')
+ c = cfsgetc();
+ continue;
+ }
+ if (c == '\\') {
+ /* Check for \ followed by newline. */
+ c = cfsgetc();
+ if (is_eol(c)) {
+ if (c == '\r')
+ c = cfsgetc();
+ if (c == '\n')
+ c = cfsgetc();
+ eol = true;
+ continue;
+ }
+ /* \ anywhere else is treated as a printing character. */
+ /* This is different from the Unix shells. */
+ if (i == arg_str_max - 1) {
+ cstr[i] = 0;
+ fprintf(stdout, "Command too long: %s\n", cstr);
+ gs_exit(1);
+ }
+ cstr[i++] = '\\';
+ eol = false;
+ continue;
+ }
+ /* c will become part of the argument */
+ if (i == arg_str_max - 1) {
+ cstr[i] = 0;
+ fprintf(stdout, "Command too long: %s\n", cstr);
+ gs_exit(1);
+ }
+ /* If input is coming from an @-file, allow quotes */
+ /* to protect whitespace. */
+ if (c == '"' && f != NULL)
+ in_quote = !in_quote;
+ else
+ cstr[i++] = c;
+ eol = is_eol(c);
+ c = cfsgetc();
+ }
+ cstr[i] = 0;
+ if (f == NULL)
+ pas->u.s.str = astr;
+ at:if (pal->expand_ats && result[0] == '@') {
+ if (pal->depth == arg_depth_max) {
+ lprintf("Too much nesting of @-files.\n");
+ gs_exit(1);
+ }
+ result++; /* skip @ */
+ f = (*pal->arg_fopen) (result, pal->fopen_data);
+ if (f == NULL) {
+ fprintf(stdout, "Unable to open command line file %s\n", result);
+ gs_exit(1);
+ }
+ pal->depth++;
+ pas++;
+ pas->is_file = true;
+ pas->u.file = f;
+ goto top;
+ }
+ return result;
+}
+
+/* Copy an argument string to the heap. */
+char *
+arg_copy(const char *str, gs_memory_t * mem)
+{
+ char *sstr = (char *)gs_alloc_bytes(mem, strlen(str) + 1, "arg_copy");
+
+ if (sstr == 0) {
+ lprintf("Out of memory!\n");
+ gs_exit(1);
+ }
+ strcpy(sstr, str);
+ return sstr;
+}
diff --git a/pstoraster/gsargs.h b/pstoraster/gsargs.h
new file mode 100644
index 000000000..e5de18661
--- /dev/null
+++ b/pstoraster/gsargs.h
@@ -0,0 +1,89 @@
+/* Copyright (C) 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Command line argument list management */
+
+#ifndef gsargs_INCLUDED
+# define gsargs_INCLUDED
+
+/*
+ * We need to handle recursion into @-files.
+ * The following structures keep track of the state.
+ * Defining a maximum argument length and a maximum nesting depth
+ * decreases generality, but eliminates the need for dynamic allocation.
+ */
+#define arg_str_max 512
+#define arg_depth_max 10
+typedef struct arg_source_s {
+ bool is_file;
+ union _u {
+ struct _su {
+ const char *chars; /* original string */
+ gs_memory_t *memory; /* if non-0, free chars when done with it */
+ const char *str; /* string being read */
+ } s;
+ FILE *file;
+ } u;
+} arg_source;
+typedef struct arg_list_s {
+ bool expand_ats; /* if true, expand @-files */
+ FILE *(*arg_fopen) (P2(const char *fname, void *fopen_data));
+ void *fopen_data;
+ const char **argp;
+ int argn;
+ int depth; /* depth of @-files */
+ char cstr[arg_str_max + 1];
+ arg_source sources[arg_depth_max];
+} arg_list;
+
+/* Initialize an arg list. */
+void arg_init(P5(arg_list * pal, const char **argv, int argc,
+ FILE * (*arg_fopen) (P2(const char *fname, void *fopen_data)),
+ void *fopen_data));
+
+/*
+ * Push a string onto an arg list.
+ * This may also be used (once) to "unread" the last argument.
+ * If mem != 0, it is used to free the string when we are done with it.
+ */
+void arg_push_memory_string(P3(arg_list * pal, const char *str,
+ gs_memory_t * mem));
+
+#define arg_push_string(pal, str)\
+ arg_push_memory_string(pal, str, (gs_memory_t *)0);
+
+/* Clean up an arg list before exiting. */
+void arg_finit(P1(arg_list * pal));
+
+/*
+ * Get the next arg from a list.
+ * Note that these are not copied to the heap.
+ */
+const char *arg_next(P1(arg_list * pal));
+
+/* Copy an argument string to the heap. */
+char *arg_copy(P2(const char *str, gs_memory_t * mem));
+
+#endif /* gsargs_INCLUDED */
diff --git a/pstoraster/gsbitmap.h b/pstoraster/gsbitmap.h
new file mode 100644
index 000000000..bd9c7498e
--- /dev/null
+++ b/pstoraster/gsbitmap.h
@@ -0,0 +1,193 @@
+/* Copyright (C) 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Library "client" bitmap structures */
+
+#ifndef gsbitmap_INCLUDED
+#define gsbitmap_INCLUDED
+
+#include "gsstruct.h" /* for extern_st */
+
+/*
+ * The Ghostscript library stores all bitmaps 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). The first scan line
+ * corresponds to y=0 in whatever coordinate system is relevant.
+ *
+ * The structures defined here are for APIs that don't impose any alignment
+ * restrictions on either the starting address or the raster (distance
+ * between scan lines) of bitmap data. The structures defined in gxbitmap.h
+ * do impose alignment restrictions, so that the library can use more
+ * efficient algorithms; they are declared with identical contents to the
+ * ones defined here, so that one can cast between them under appropriate
+ * circumstances (aligned to unaligned is always safe; unaligned to
+ * aligned is safe if one knows somehow that the data are actually aligned.)
+ *
+ * In this file we also provide structures that include depth information.
+ * It probably was a design mistake not to include this information in the
+ * gx structures as well.
+ */
+
+/*
+ * 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 gs_bitmap_id;
+
+/* Define a special value to indicate "no identifier". */
+#define gs_no_bitmap_id gs_no_id
+
+/*
+ * In its simplest form, the client bitmap structure does not specify a
+ * depth, expecting it to be implicit in the context of use. In many cases
+ * it is possible to guess this by comparing size.x and raster, but of
+ * course code should not rely on this. See also gs_depth_bitmap below.
+ * Requirements:
+ * size.x > 0, size.y > 0
+ * If size.y > 1,
+ * raster >= (size.x * depth + 7) / 8
+ */
+#define gs_bitmap_common \
+ byte * data; /* pointer to the data */ \
+ int raster; /* increment between scanlines, bytes */ \
+ gs_int_point size; /* width and height */ \
+ gs_bitmap_id id /* usually unused */
+
+typedef struct gs_bitmap_s {
+ gs_bitmap_common;
+} gs_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:
+ * size.x % rep_width = 0
+ * size.y % rep_height = 0
+ * Unaligned bitmaps are not very likely to be used as tiles (replicated),
+ * since most of the library procedures that replicate tiles expect them
+ * to be aligned.
+ */
+#define gs_tile_bitmap_common \
+ gs_bitmap_common; \
+ ushort rep_width, rep_height /* true size of tile */
+
+typedef struct gs_tile_bitmap_s {
+ gs_tile_bitmap_common;
+} gs_tile_bitmap;
+
+/*
+ * There is no "strip" version for client bitmaps, as the strip structure is
+ * primarily used to efficiently store bitmaps rendered at an angle, and
+ * there is little reason to do so with client bitmaps.
+ *
+ * For client bitmaps it is not always apparent from context what the intended
+ * depth per sample value is. To provide for this, an extended version of the
+ * bitmap structure is provided, that handles both variable depth and
+ * interleaved color components. This structure is provided in both the
+ * normal and tiled version.
+ *
+ * Extending this line of thinking, one could also add color space information
+ * to a client bitmap structure. We have chosen not to do so, because color
+ * space is almost always derived from context, and to provide such a feature
+ * would involve additional memory-management complexity.
+ */
+#define gs_depth_bitmap_common \
+ gs_bitmap_common; \
+ byte pix_depth; /* bits per sample */ \
+ byte num_comps /* number of interleaved components */ \
+
+typedef struct gs_depth_bitmap_s {
+ gs_depth_bitmap_common;
+} gs_depth_bitmap;
+
+#define gs_tile_depth_bitmap_common \
+ gs_tile_bitmap_common; \
+ byte pix_depth; /* bits per sample */ \
+ byte num_comps /* number of interleaved components */ \
+
+typedef struct gs_tile_depth_bitmap_s {
+ gs_tile_depth_bitmap_common;
+} gs_tile_depth_bitmap;
+
+/*
+ * For reasons that are no entirely clear, no memory management routines were
+ * provided for the aligned bitmap structures provided in gxbitmap.h. Since
+ * client bitmaps will, by nature, be created by different clients, so public
+ * memory management procedures are provided. Note that the memory management
+ * structure names retain the "gs_" prefix, to distinguish these structures
+ * from those that may be provided for the gx_*_bitmap structures.
+ *
+ * For historical reasons of no particular validity (this was where the client
+ * bitmap structure was first provided), the memory managment procedures for
+ * client bitmap structures are included in gspcolor.c.
+ */
+extern_st(st_gs_bitmap);
+extern_st(st_gs_tile_bitmap);
+extern_st(st_gs_depth_bitmap);
+extern_st(st_gs_tile_depth_bitmap);
+
+#define public_st_gs_bitmap() /* in gspcolor.c */ \
+ gs_public_st_ptrs1( st_gs_bitmap, \
+ gs_bitmap, \
+ "client bitmap", \
+ bitmap_enum_ptrs, \
+ bitmap_reloc_ptrs, \
+ data \
+ )
+
+#define public_st_gs_tile_bitmap() /* in gspcolor.c */ \
+ gs_public_st_suffix_add0_local( st_gs_tile_bitmap, \
+ gs_tile_bitmap, \
+ "client tile bitmap", \
+ bitmap_enum_ptrs, \
+ bitmap_reloc_ptrs, \
+ st_gs_bitmap \
+ )
+
+#define public_st_gs_depth_bitmap() /* in gspcolor.c */ \
+ gs_public_st_suffix_add0_local( st_gs_depth_bitmap, \
+ gs_depth_bitmap, \
+ "client depth bitmap", \
+ bitmap_enum_ptrs, \
+ bitmap_reloc_ptrs, \
+ st_gs_bitmap \
+ )
+
+#define public_st_gs_tile_depth_bitmap()/* in gspcolor.c */ \
+ gs_public_st_suffix_add0_local( st_gs_tile_depth_bitmap, \
+ gs_tile_depth_bitmap, \
+ "client tile_depth bitmap", \
+ bitmap_enum_ptrs, \
+ bitmap_reloc_ptrs, \
+ st_gs_tile_bitmap \
+ )
+
+#endif /* gsbitmap_INCLUDED */
diff --git a/pstoraster/gsbitops.c b/pstoraster/gsbitops.c
new file mode 100644
index 000000000..bb6ed6308
--- /dev/null
+++ b/pstoraster/gsbitops.c
@@ -0,0 +1,666 @@
+/* Copyright (C) 1994, 1995, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Bitmap filling, copying, and transforming operations */
+#include "stdio_.h"
+#include "memory_.h"
+#include "gdebug.h"
+#include "gstypes.h"
+#include "gxbitops.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;
+ int line_count = height;
+ chunk *ptr;
+ int last_bit;
+
+#define FOR_EACH_LINE(stat)\
+ do { stat } while ( inc_ptr(ptr, draster), --line_count )
+
+ dest += (dest_bit >> 3) & -chunk_align_bytes;
+ ptr = (chunk *) dest;
+ bit = dest_bit & chunk_align_bit_mask;
+ last_bit = width_bits + bit - (chunk_bits + 1);
+
+ if (last_bit < 0) { /* <=1 chunk */
+ set_mono_thin_mask(right_mask, width_bits, bit);
+ switch ((byte) pattern) {
+ case 0:
+ FOR_EACH_LINE(*ptr &= ~right_mask;
+ );
+ break;
+ case 0xff:
+ FOR_EACH_LINE(*ptr |= right_mask;
+ );
+ break;
+ default:
+ FOR_EACH_LINE(
+ *ptr = (*ptr & ~right_mask) | (pattern & 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 */
+ switch ((byte) pattern) {
+ case 0:
+ FOR_EACH_LINE(*ptr &= ~mask;
+ ptr[1] &= ~right_mask;
+ );
+ break;
+ case 0xff:
+ FOR_EACH_LINE(*ptr |= mask;
+ ptr[1] |= right_mask;
+ );
+ break;
+ default:
+ FOR_EACH_LINE(
+ *ptr = (*ptr & ~mask) | (pattern & mask);
+ ptr[1] = (ptr[1] & ~right_mask) | (pattern & right_mask);
+ );
+ }
+ break;
+ case 1: /* 3 chunks */
+ switch ((byte) pattern) {
+ case 0:
+ FOR_EACH_LINE(
+ *ptr &= ~mask;
+ ptr[1] = 0;
+ ptr[2] &= ~right_mask;
+ );
+ break;
+ case 0xff:
+ FOR_EACH_LINE(
+ *ptr |= mask;
+ ptr[1] = ~(chunk) 0;
+ ptr[2] |= right_mask;
+ );
+ break;
+ default:
+ FOR_EACH_LINE(
+ *ptr = (*ptr & ~mask) | (pattern & mask);
+ ptr[1] = pattern;
+ ptr[2] = (ptr[2] & ~right_mask) | (pattern & right_mask);
+ );
+ }
+ break;
+ default:{ /* >3 chunks */
+ uint byte_count = (last_bit >> 3) & -chunk_bytes;
+
+ switch ((byte) pattern) {
+ case 0:
+ FOR_EACH_LINE(
+ *ptr &= ~mask;
+ memset(ptr + 1, 0, byte_count);
+ ptr[last + 1] &= ~right_mask;
+ );
+ break;
+ case 0xff:
+ FOR_EACH_LINE(
+ *ptr |= mask;
+ memset(ptr + 1, 0xff, byte_count);
+ ptr[last + 1] |= right_mask;
+ );
+ break;
+ default:
+ FOR_EACH_LINE(
+ *ptr = (*ptr & ~mask) | (pattern & mask);
+ memset(ptr + 1, (byte) pattern, byte_count);
+ ptr[last + 1] =
+ (ptr[last + 1] & ~right_mask) |
+ (pattern & right_mask);
+ );
+ }
+ }
+ }
+ }
+#undef FOR_EACH_LINE
+}
+
+/* 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 *const 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 *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..efaeef228
--- /dev/null
+++ b/pstoraster/gsbitops.h
@@ -0,0 +1,222 @@
+/* Copyright (C) 1991, 1995, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Interface for bitmap operations */
+
+#ifndef gsbitops_INCLUDED
+# define gsbitops_INCLUDED
+
+/* ---------------- Pixel processing macros ---------------- */
+
+/*
+ * These macros support code that processes data pixel-by-pixel (or, to be
+ * more accurate, packed arrays of values -- they may be complete pixels
+ * or individual components of pixels).
+ *
+ * Supported #s of bits per value (bpv) are 1, 2, 4, 8, 12[, 16[, 24, 32]].
+ * The suffix 12, 16, or 32 on a macro name indicates the maximum value
+ * of bpv that the macro is prepared to handle.
+ *
+ * The setup macros number bits within a byte in big-endian order, i.e.,
+ * 0x80 is bit 0, 0x01 is bit 7. However, sbit/dbit may use a different
+ * representation for better performance. ****** NYI ******
+ */
+
+#define sample_end_\
+ default: return_error(gs_error_rangecheck);\
+ } END
+
+/* Declare variables for loading. */
+#define sample_load_declare(sptr, sbit)\
+ const byte *sptr;\
+ int sbit
+#define sample_load_declare_setup(sptr, sbit, ptr, bitno, sbpv)\
+ const byte *sptr = (ptr);\
+ int sample_load_setup(sbit, bitno, sbpv)
+
+/* Set up to load starting at a given bit number. */
+#define sample_load_setup(sbit, bitno, sbpv)\
+ sbit = (bitno)
+
+/* Load a value from memory, without incrementing. */
+#define sample_load12_(value, sptr, sbit, sbpv)\
+ BEGIN\
+ switch ( (sbpv) >> 2 ) {\
+ case 0: value = (*(sptr) >> (8 - (sbit) - (sbpv))) & ((sbpv) - 1); break;\
+ case 1: value = (*(sptr) >> (4 - (sbit))) & 0xf; break;\
+ case 2: value = *(sptr); break;\
+ case 3:\
+ value = ((sbit) ? ((*(sptr) & 0xf) << 8) | (sptr)[1] :\
+ (*(sptr) << 4) | ((sptr)[1] >> 4));\
+ break;
+#define sample_load12(value, sptr, sbit, sbpv)\
+ sample_load12_(value, sptr, sbit, sbpv)\
+ sample_end_
+#define sample_load_next12(value, sptr, sbit, sbpv)\
+ sample_load12(value, sptr, sbit, sbpv);\
+ sample_next(sptr, sbit, sbpv)
+#define sample_load16_(value, sptr, sbit, sbpv)\
+ sample_load12_(value, sptr, sbit, sbpv)\
+ case 4: value = (*(sptr) << 8) | (sptr)[1]; break;
+#define sample_load16(value, sptr, sbit, sbpv)\
+ sample_load16_(value, sptr, sbit, sbpv)\
+ sample_end_
+#define sample_load_next16(value, sptr, sbit, sbpv)\
+ sample_load16(value, sptr, sbit, sbpv);\
+ sample_next(sptr, sbit, sbpv)
+#define sample_load32(value, sptr, sbit, sbpv)\
+ sample_load16_(value, sptr, sbit, sbpv)\
+ case 6: value = (*(sptr) << 16) | ((sptr)[1] << 8) | (sptr)[2]; break;\
+ case 8:\
+ value = (*(sptr) << 24) | ((sptr)[1] << 16) | ((sptr)[2] << 8) | sptr[3];\
+ break;\
+ sample_end_
+#define sample_load_next32(value, sptr, sbit, sbpv)\
+ sample_load32(value, sptr, sbit, sbpv);\
+ sample_next(sptr, sbit, sbpv)
+
+/* Declare variables for storing. */
+#define sample_store_declare(dptr, dbit, dbbyte)\
+ byte *dptr;\
+ int dbit;\
+ byte dbbyte /* maybe should be uint? */
+#define sample_store_declare_setup(dptr, dbit, dbbyte, ptr, bitno, dbpv)\
+ byte *dptr = (ptr);\
+ int sample_store_setup(dbit, bitno, dbpv);\
+ byte /* maybe should be uint? */\
+ sample_store_preload(dbbyte, dptr, dbit, dbpv)
+
+/* Set up to store starting at a given bit number. */
+#define sample_store_setup(dbit, bitno, dbpv)\
+ dbit = (bitno)
+
+/* Prepare for storing by preloading any partial byte. */
+#define sample_store_preload(dbbyte, dptr, dbit, dbpv)\
+ dbbyte = ((dbit) ? (byte)(*(dptr) & (0xff00 >> (dbit))) : 0)
+
+/* Store a value and increment the pointer. */
+#define sample_store_next12_(value, dptr, dbit, dbpv, dbbyte)\
+ BEGIN\
+ switch ( (dbpv) >> 2 ) {\
+ case 0:\
+ if ( (dbit += (dbpv)) == 8 )\
+ *(dptr)++ = dbbyte | (value), dbbyte = 0, dbit = 0;\
+ else dbbyte |= (value) << (8 - dbit);\
+ break;\
+ case 1:\
+ if ( dbit ^= 4 ) dbbyte = (byte)((value) << 4);\
+ else *(dptr)++ = dbbyte | (value);\
+ break;\
+ /* case 2 is deliberately omitted */\
+ case 3:\
+ if ( dbit ^= 4 ) *(dptr)++ = (value) >> 4, dbbyte = (byte)((value) << 4);\
+ else\
+ *(dptr) = dbbyte | ((value) >> 8), (dptr)[1] = (byte)(value), dptr += 2;\
+ break;
+#define sample_store_next12(value, dptr, dbit, dbpv, dbbyte)\
+ sample_store_next12_(value, dptr, dbit, dbpv, dbbyte)\
+ case 2: *(dptr)++ = (byte)(value); break;\
+ sample_end_
+#define sample_store_next16(value, dptr, dbit, dbpv, dbbyte)\
+ sample_store_next12_(value, dptr, dbit, dbpv, dbbyte)\
+ case 4: *(dptr)++ = (byte)((value) >> 8);\
+ case 2: *(dptr)++ = (byte)(value); break;\
+ sample_end_
+#define sample_store_next32(value, dptr, dbit, dbpv, dbbyte)\
+ sample_store_next12_(value, dptr, dbit, dbpv, dbbyte)\
+ case 8: *(dptr)++ = (byte)((value) >> 24);\
+ case 6: *(dptr)++ = (byte)((value) >> 16);\
+ case 4: *(dptr)++ = (byte)((value) >> 8);\
+ case 2: *(dptr)++ = (byte)(value); break;\
+ sample_end_
+
+/* Skip over storing one sample. This may or may not store into the */
+/* skipped region. */
+#define sample_store_skip_next(dptr, dbit, dbpv, dbbyte)\
+ if ( (dbpv) < 8 ) {\
+ sample_store_flush(dptr, dbit, dbpv, dbbyte);\
+ sample_next(dptr, dbit, dbpv);\
+ } else dptr += ((dbpv) >> 3)
+
+/* Finish storing by flushing any partial byte. */
+#define sample_store_flush(dptr, dbit, dbpv, dbbyte)\
+ if ( (dbit) != 0 )\
+ *(dptr) = dbbyte | (*(dptr) & (0xff >> (dbit)));
+
+/* Increment a pointer to the next sample. */
+#define sample_next(ptr, bit, bpv)\
+ BEGIN bit += (bpv); ptr += bit >> 3; bit &= 7; END
+
+/* ---------------- Definitions ---------------- */
+
+/*
+ * Define the chunk size for monobit filling operations.
+ * This is always uint, regardless of byte order.
+ */
+#define mono_fill_chunk uint
+#define mono_fill_chunk_bytes arch_sizeof_int
+
+/* ---------------- 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));
+
+#endif /* gsbitops_INCLUDED */
diff --git a/pstoraster/gsbittab.c b/pstoraster/gsbittab.c
new file mode 100644
index 000000000..ac2c5abdb
--- /dev/null
+++ b/pstoraster/gsbittab.c
@@ -0,0 +1,144 @@
+/* Copyright (C) 1995, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Tables for bit operations */
+#include "stdpre.h"
+#include "gsbittab.h"
+
+/* ---------------- Byte processing tables ---------------- */
+
+/*
+ * byte_reverse_bits[B] = the byte B with the order of bits reversed.
+ */
+const byte byte_reverse_bits[256] =
+{
+ bit_table_8(0, 1, 2, 4, 8, 0x10, 0x20, 0x40, 0x80)
+};
+
+/*
+ * byte_right_mask[N] = a byte with N trailing 1s, 0 <= N <= 8.
+ */
+const byte byte_right_mask[9] =
+{
+ 0, 1, 3, 7, 0xf, 0x1f, 0x3f, 0x7f, 0xff
+};
+
+/*
+ * byte_count_bits[B] = the number of 1-bits in a byte with value B.
+ */
+const byte byte_count_bits[256] =
+{
+ bit_table_8(0, 1, 1, 1, 1, 1, 1, 1, 1)
+};
+
+/* ---------------- 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 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 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 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 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 byte_bit_run_length_5[256] =
+{
+ rr8(0, 0, 0, 0, 1, 1, 2, 11)
+};
+const byte byte_bit_run_length_6[256] =
+{
+ rr8(0, 0, 1, 10, 0, 0, 1, 10)
+};
+const byte byte_bit_run_length_7[256] =
+{
+ rr8(0, 9, 0, 9, 0, 9, 0, 9)
+};
+
+/* Pointer tables indexed by bit number. */
+
+const byte *const 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 *const 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..f41e2ed9a
--- /dev/null
+++ b/pstoraster/gsbittab.h
@@ -0,0 +1,83 @@
+/* Copyright (C) 1995, 1996, 1997 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Interface to tables for bit operations */
+
+#ifndef gsbittab_INCLUDED
+# define gsbittab_INCLUDED
+
+/*
+ * Generate tables for transforming 2, 4, 6, or 8 bits.
+ */
+#define btab2_(v0,v2,v1)\
+ v0,v1+v0,v2+v0,v2+v1+v0
+#define bit_table_2(v0,v2,v1) btab2_(v0,v2,v1)
+#define btab4_(v0,v8,v4,v2,v1)\
+ btab2_(v0,v2,v1), btab2_(v4+v0,v2,v1),\
+ btab2_(v8+v0,v2,v1), btab2_(v8+v4+v0,v2,v1)
+#define bit_table_4(v0,v8,v4,v2,v1) btab4_(v0,v8,v4,v2,v1)
+#define btab6_(v0,v20,v10,v8,v4,v2,v1)\
+ btab4_(v0,v8,v4,v2,v1), btab4_(v10+v0,v8,v4,v2,v1),\
+ btab4_(v20+v0,v8,v4,v2,v1), btab4_(v20+v10+v0,v8,v4,v2,v1)
+#define bit_table_6(v0,v20,v10,v8,v4,v2,v1) btab6_(v0,v20,v10,v8,v4,v2,v1)
+#define bit_table_8(v0,v80,v40,v20,v10,v8,v4,v2,v1)\
+ btab6_(v0,v20,v10,v8,v4,v2,v1), btab6_(v40+v0,v20,v10,v8,v4,v2,v1),\
+ btab6_(v80+v0,v20,v10,v8,v4,v2,v1), btab6_(v80+v40+v0,v20,v10,v8,v4,v2,v1)
+
+/*
+ * 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_count_bits[B] = the number of 1-bits in a byte with value B.
+ */
+extern const byte byte_count_bits[256];
+
+/*
+ * 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 *const byte_bit_run_length[8];
+extern const byte *const byte_bit_run_length_neg[8];
+
+#endif /* gsbittab_INCLUDED */
diff --git a/pstoraster/gsccode.h b/pstoraster/gsccode.h
new file mode 100644
index 000000000..4945f5456
--- /dev/null
+++ b/pstoraster/gsccode.h
@@ -0,0 +1,72 @@
+/* Copyright (C) 1993, 1996, 1997 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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.
+ * gs_glyphs from 0 to 2^31-1 are (PostScript) names; gs_glyphs 2^31 and
+ * above are CIDs, biased by 2^31.
+ */
+typedef ulong gs_glyph;
+
+#define gs_no_glyph ((gs_glyph)0x7fffffff)
+#define gs_min_cid_glyph ((gs_glyph)0x80000000)
+#define gs_max_glyph max_ulong
+
+/* Define a procedure for marking a gs_glyph during garbage collection. */
+typedef bool(*gs_glyph_mark_proc_t) (P2(gs_glyph glyph, void *proc_data));
+
+/* 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..efe1345aa
--- /dev/null
+++ b/pstoraster/gsccolor.h
@@ -0,0 +1,58 @@
+/* Copyright (C) 1993, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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 */
+#ifndef gs_client_color_DEFINED
+# define gs_client_color_DEFINED
+typedef struct gs_client_color_s gs_client_color;
+
+#endif
+struct gs_client_color_s {
+ gs_paint_color paint; /* also color for uncolored pattern */
+ gs_pattern_instance *pattern;
+};
+
+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/gscdefs.c b/pstoraster/gscdefs.c
new file mode 100644
index 000000000..eae38b264
--- /dev/null
+++ b/pstoraster/gscdefs.c
@@ -0,0 +1,81 @@
+/*
+ Copyright 1993-2000 by Easy Software Products.
+ Copyright 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Configuration scalars */
+#include "stdpre.h"
+#include "gscdefs.h" /* interface */
+#include "gconf.h" /* for #defines */
+#include <config.h>
+
+/* ---------------- Miscellaneous system parameters ---------------- */
+
+/* All of these can be set in the makefile. */
+/* Normally they are all const; see gscdefs.h for more information. */
+
+#ifndef GS_BUILDTIME
+# define GS_BUILDTIME\
+ 0 /* should be set in the makefile */
+#endif
+CONFIG_CONST long gs_buildtime = GS_BUILDTIME;
+
+#ifndef GS_COPYRIGHT
+# define GS_COPYRIGHT\
+ "Copyright 1993-2000 Easy Software Products, All Rights Reserved.\n"\
+ "Copyright 1998 Aladdin Enterprises, Menlo Park, CA. All rights reserved."
+#endif
+const char *CONFIG_CONST gs_copyright = GS_COPYRIGHT;
+
+#ifndef GS_PRODUCT
+# define GS_PRODUCT CUPS_SVERSION
+#endif
+const char *CONFIG_CONST gs_product = GS_PRODUCT;
+
+const char *
+gs_program_name(void)
+{
+ return gs_product;
+}
+
+CONFIG_CONST long gs_revision = 550;
+CONFIG_CONST long gs_revisiondate = 20000308;
+
+#ifndef GS_SERIALNUMBER
+# define GS_SERIALNUMBER\
+ 40100
+#endif
+CONFIG_CONST long gs_serialnumber = GS_SERIALNUMBER;
+
+/* ---------------- Installation directories and files ---------------- */
+
+/* Here is where the library search path, the name of the */
+/* initialization file, and the doc directory are defined. */
+
+/* Define the default library search path. */
+const char *const gs_lib_default_path = CUPS_DATADIR "/pstoraster:" CUPS_DATADIR "/fonts";
+
+/* Define the interpreter initialization file. */
+const char *const gs_init_file = "gs_init.ps";
diff --git a/pstoraster/gscdefs.h b/pstoraster/gscdefs.h
new file mode 100644
index 000000000..463782c64
--- /dev/null
+++ b/pstoraster/gscdefs.h
@@ -0,0 +1,81 @@
+/* Copyright (C) 1994, 1996, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Prototypes for configuration definitions in gconfig.c. */
+
+#ifndef gscdefs_INCLUDED
+# define gscdefs_INCLUDED
+
+#include "gconfigv.h"
+
+/*
+ * This file may be #included in places that don't even have stdpre.h,
+ * so it mustn't use any Ghostscript definitions in any code that is
+ * actually processed here (as opposed to being part of a macro
+ * definition).
+ */
+
+/* Miscellaneous system constants (read-only systemparams). */
+/* They should all be const, but one application needs some of them */
+/* to be writable.... */
+
+#if SYSTEM_CONSTANTS_ARE_WRITABLE
+# define CONFIG_CONST /* */
+#else
+# define CONFIG_CONST const
+#endif
+
+extern CONFIG_CONST long gs_buildtime;
+extern const char *CONFIG_CONST gs_copyright;
+extern const char *CONFIG_CONST gs_product;
+extern CONFIG_CONST long gs_revision;
+extern CONFIG_CONST long gs_revisiondate;
+extern CONFIG_CONST long gs_serialnumber;
+
+/* Installation directories and files */
+extern const char *const gs_doc_directory;
+extern const char *const gs_lib_default_path;
+extern const char *const 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. */
+
+/* We need the extra typedef so that the const will apply to the table. */
+#define extern_gx_init_table()\
+ typedef void (*gx_init_proc)(P1(gs_memory_t *));\
+ extern const gx_init_proc gx_init_table[]
+
+/* We need the extra typedef so that the const will apply to the table. */
+#define extern_gx_io_device_table()\
+ extern const gx_io_device * const gx_io_device_table[]
+extern const unsigned gx_io_device_table_count;
+
+/* 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 * const **plist,\
+ gs_memory_struct_type_t **pst))
+
+#endif /* gscdefs_INCLUDED */
diff --git a/pstoraster/gscdevn.c b/pstoraster/gscdevn.c
new file mode 100644
index 000000000..af0b4847e
--- /dev/null
+++ b/pstoraster/gscdevn.c
@@ -0,0 +1,184 @@
+/* Copyright (C) 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* DeviceN color space and operation definition */
+#include "gx.h"
+#include "gserrors.h"
+#include "gsrefct.h"
+#include "gsmatrix.h" /* for gscolor2.h */
+#include "gxcspace.h"
+
+gs_private_st_composite(st_color_space_DeviceN, gs_paint_color_space,
+ "gs_color_space_DeviceN", cs_DeviceN_enum_ptrs, cs_DeviceN_reloc_ptrs);
+
+/* Define the DeviceN color space type. */
+private cs_proc_num_components(gx_num_components_DeviceN);
+private cs_proc_base_space(gx_alt_space_DeviceN);
+private cs_proc_init_color(gx_init_DeviceN);
+private cs_proc_restrict_color(gx_restrict_DeviceN);
+private cs_proc_concrete_space(gx_concrete_space_DeviceN);
+private cs_proc_concretize_color(gx_concretize_DeviceN);
+private cs_proc_remap_concrete_color(gx_remap_concrete_DeviceN);
+private cs_proc_install_cspace(gx_install_DeviceN);
+private cs_proc_adjust_cspace_count(gx_adjust_cspace_DeviceN);
+const gs_color_space_type gs_color_space_type_DeviceN = {
+ gs_color_space_index_DeviceN, true, false,
+ &st_color_space_DeviceN, gx_num_components_DeviceN,
+ gx_alt_space_DeviceN,
+ gx_init_DeviceN, gx_restrict_DeviceN,
+ gx_concrete_space_DeviceN,
+ gx_concretize_DeviceN, gx_remap_concrete_DeviceN,
+ gx_default_remap_color, gx_install_DeviceN,
+ gx_adjust_cspace_DeviceN, gx_no_adjust_color_count
+};
+
+/* ------ Internal routines ------ */
+
+/* Return the number of components of a DeviceN space. */
+private int
+gx_num_components_DeviceN(const gs_color_space * pcs)
+{
+ return pcs->params.device_n.num_components;
+}
+
+/* Return the alternate space of a DeviceN space. */
+private const gs_color_space *
+gx_alt_space_DeviceN(const gs_color_space * pcs)
+{
+ return (const gs_color_space *)&(pcs->params.device_n.alt_space);
+}
+
+/* Initialize a DeviceN color. */
+/****** DOESN'T WORK IF num_components > 4 ******/
+private void
+gx_init_DeviceN(gs_client_color * pcc, const gs_color_space * pcs)
+{
+ int i;
+
+ for (i = 0; i < pcs->params.device_n.num_components; ++i)
+ pcc->paint.values[i] = 1.0;
+}
+
+/* Force a DeviceN color into legal range. */
+private void
+gx_restrict_DeviceN(gs_client_color * pcc, const gs_color_space * pcs)
+{
+ int i;
+
+ for (i = 0; i < pcs->params.device_n.num_components; ++i) {
+ floatp value = pcc->paint.values[i];
+
+ pcc->paint.values[i] = (value <= 0 ? 0 : value >= 1 ? 1 : value);
+ }
+}
+
+/* Remap a DeviceN color. */
+private const gs_color_space *
+gx_concrete_space_DeviceN(const gs_color_space * pcs,
+ const gs_imager_state * pis)
+{ /* We don't support concrete DeviceN spaces yet. */
+ const gs_color_space *pacs =
+ (const gs_color_space *)&pcs->params.device_n.alt_space;
+
+ return cs_concrete_space(pacs, pis);
+}
+
+private int
+gx_concretize_DeviceN(const gs_client_color * pc, const gs_color_space * pcs,
+ frac * pconc, const gs_imager_state * pis)
+{
+ int code;
+ gs_client_color cc;
+ const gs_color_space *pacs =
+ (const gs_color_space *)&pcs->params.device_n.alt_space;
+
+ /* We always map into the alternate color space. */
+ code = (*pcs->params.device_n.tint_transform)
+ (&pcs->params.device_n, pc->paint.values, &cc.paint.values[0],
+ pcs->params.device_n.tint_transform_data);
+ if (code < 0)
+ return code;
+ return (*pacs->type->concretize_color) (&cc, pacs, pconc, pis);
+}
+
+private int
+gx_remap_concrete_DeviceN(const frac * pconc,
+ gx_device_color * pdc, const gs_imager_state * pis, gx_device * dev,
+ gs_color_select_t select)
+{ /* We don't support concrete DeviceN colors yet. */
+ return_error(gs_error_rangecheck);
+}
+
+/* Install a DeviceN color space. */
+private int
+gx_install_DeviceN(gs_color_space * pcs, gs_state * pgs)
+{ /*
+ * Give an error if any of the separation names are duplicated.
+ * We can't check this any earlier.
+ */
+ const gs_separation_name *names = pcs->params.device_n.names;
+ uint i, j;
+
+ for (i = 1; i < pcs->params.device_n.num_components; ++i)
+ for (j = 0; j < i; ++j)
+ if (names[i] == names[j])
+ return_error(gs_error_rangecheck);
+ return (*pcs->params.device_n.alt_space.type->install_cspace)
+ ((gs_color_space *) & pcs->params.device_n.alt_space, pgs);
+}
+
+/* Adjust the reference count of a DeviceN color space. */
+private void
+gx_adjust_cspace_DeviceN(const gs_color_space * pcs, int delta)
+{
+ (*pcs->params.device_n.alt_space.type->adjust_cspace_count)
+ ((const gs_color_space *)&pcs->params.device_n.alt_space, delta);
+}
+
+/* GC procedures */
+
+#define pcs ((gs_color_space *)vptr)
+
+private
+ENUM_PTRS_BEGIN(cs_DeviceN_enum_ptrs)
+{
+ return ENUM_USING(*pcs->params.device_n.alt_space.type->stype,
+ &pcs->params.device_n.alt_space,
+ sizeof(pcs->params.device_n.alt_space), index - 2);
+}
+ENUM_PTR(0, gs_color_space, params.device_n.names);
+ENUM_PTR(1, gs_color_space, params.device_n.tint_transform_data);
+ENUM_PTRS_END
+private RELOC_PTRS_BEGIN(cs_DeviceN_reloc_ptrs)
+{
+ RELOC_PTR(gs_color_space, params.device_n.names);
+ RELOC_PTR(gs_color_space, params.device_n.tint_transform_data);
+ RELOC_USING(*pcs->params.device_n.alt_space.type->stype,
+ &pcs->params.device_n.alt_space,
+ sizeof(gs_base_color_space));
+}
+RELOC_PTRS_END
+
+#undef pcs
diff --git a/pstoraster/gschar.c b/pstoraster/gschar.c
new file mode 100644
index 000000000..b97b6c211
--- /dev/null
+++ b/pstoraster/gschar.c
@@ -0,0 +1,1492 @@
+/* Copyright (C) 1989, 1995, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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. */
+private bool CACHE_ROTATED_CHARS = true;
+
+/* Define whether or not to oversample characters at small sizes. */
+private bool OVERSAMPLE = true;
+
+/* Define the maximum size of a full temporary bitmap when rasterizing, */
+/* in bits (not bytes). */
+private uint MAX_TEMP_BITMAP_BITS = 80000;
+
+/* Structure descriptors */
+private_st_gs_show_enum();
+extern_st(st_gs_text_params);
+#define eptr ((gs_show_enum *)vptr)
+private
+ENUM_PTRS_BEGIN(show_enum_enum_ptrs)
+{
+ index -= 5;
+ if (index <= eptr->fstack.depth)
+ ENUM_RETURN(eptr->fstack.items[index].font);
+ index -= eptr->fstack.depth + 1;
+ return ENUM_USING(st_gs_text_params, vptr, size, index);
+}
+ENUM_PTR(0, gs_show_enum, pgs);
+ENUM_PTR(1, gs_show_enum, show_gstate);
+ENUM_PTR3(2, gs_show_enum, dev_cache, dev_cache2, dev_null);
+ENUM_PTRS_END
+private RELOC_PTRS_BEGIN(show_enum_reloc_ptrs)
+{
+ int i;
+
+ RELOC_USING(st_gs_text_params, vptr, size); /* superclass */
+ RELOC_PTR(gs_show_enum, pgs);
+ RELOC_PTR(gs_show_enum, show_gstate);
+ RELOC_PTR3(gs_show_enum, dev_cache, dev_cache2, 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(P6(gs_show_enum *, gs_state *, const char *, uint,
+ uint, bool));
+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(P4(gs_show_enum *, gs_state *, const char *,
+ uint));
+
+/* Print the ctm if debugging */
+#define print_ctm(s,pgs)\
+ dlprintf7("[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)
+
+/* ------ Driver procedure ------ */
+
+/*
+ * When actually implemented, this will be moved further down in the file
+ * and will replace other code that is there now....
+ */
+
+int
+gx_default_text_begin(gx_device * dev, gs_imager_state * pis,
+ const gs_text_params_t * text, const gs_font * font,
+gx_path * path, const gx_device_color * pdcolor, const gx_clip_path * pcpath,
+ gs_memory_t * memory, gs_text_enum_t ** ppenum)
+{
+ return_error(gs_error_undefined);
+}
+
+/* ------ 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;
+
+ rc_alloc_struct_1(penum, gs_show_enum, &st_gs_show_enum, mem,
+ return 0, cname);
+ /* Initialize pointers for GC */
+ penum->text.operation = 0; /* no pointers relevant */
+ penum->dev = 0;
+ penum->pgs = pgs;
+ 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)
+{
+ penum->cc = 0;
+ if (penum->dev_cache2 != 0) {
+ rc_decrement_only(penum->dev_cache2,
+ "gs_show_enum_release(dev_cache2)");
+ penum->dev_cache2 = 0;
+ }
+ if (penum->dev_cache != 0) {
+ rc_decrement_only(penum->dev_cache,
+ "gs_show_enum_release(dev_cache)");
+ penum->dev_cache = 0;
+ }
+ if (penum->dev_null != 0) {
+ rc_decrement_only(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)");
+}
+
+/* show[_n] */
+int
+gs_show_n_init(gs_show_enum * penum, gs_state * pgs,
+ const char *str, uint size)
+{
+ return show_setup(penum, pgs, str, size,
+ TEXT_FROM_STRING | TEXT_DO_DRAW | TEXT_RETURN_WIDTH,
+ true);
+}
+
+/* ashow[_n] */
+int
+gs_ashow_n_init(gs_show_enum * penum, gs_state * pgs,
+ floatp ax, floatp ay, const char *str, uint size)
+{
+ penum->text.delta_all.x = ax;
+ penum->text.delta_all.y = ay;
+ return show_setup(penum, pgs, str, size,
+ TEXT_FROM_STRING | TEXT_ADD_TO_ALL_WIDTHS |
+ TEXT_DO_DRAW | TEXT_RETURN_WIDTH,
+ true);
+}
+
+/* widthshow[_n] */
+int
+gs_widthshow_n_init(gs_show_enum * penum, gs_state * pgs,
+ floatp cx, floatp cy, gs_char chr,
+ const char *str, uint size)
+{
+ penum->text.delta_space.x = cx;
+ penum->text.delta_space.y = cy;
+ penum->text.space.s_char = chr;
+ return show_setup(penum, pgs, str, size,
+ TEXT_FROM_STRING | TEXT_ADD_TO_SPACE_WIDTH |
+ TEXT_DO_DRAW | TEXT_RETURN_WIDTH,
+ true);
+}
+
+/* awidthshow[_n] */
+int
+gs_awidthshow_n_init(gs_show_enum * penum, gs_state * pgs,
+ floatp cx, floatp cy, gs_char chr, floatp ax, floatp ay,
+ const char *str, uint size)
+{
+ penum->text.delta_space.x = cx;
+ penum->text.delta_space.y = cy;
+ penum->text.space.s_char = chr;
+ penum->text.delta_all.x = ax;
+ penum->text.delta_all.y = ay;
+ return show_setup(penum, pgs, str, size,
+ TEXT_FROM_STRING |
+ TEXT_ADD_TO_ALL_WIDTHS | TEXT_ADD_TO_SPACE_WIDTH |
+ TEXT_DO_DRAW | TEXT_RETURN_WIDTH,
+ true);
+}
+
+/* kshow[_n] */
+int
+gs_kshow_n_init(register gs_show_enum * penum,
+ gs_state * pgs, const char *str, uint size)
+{
+ if (pgs->font->FontType == ft_composite)
+ return_error(gs_error_invalidfont);
+ return show_setup(penum, pgs, str, size,
+ TEXT_FROM_STRING | TEXT_DO_DRAW | TEXT_INTERVENE |
+ TEXT_RETURN_WIDTH,
+ true);
+}
+
+/* xyshow[_n] */
+int
+gs_xyshow_n_init(register gs_show_enum * penum,
+ gs_state * pgs, const char *str, uint size)
+{
+ return show_setup(penum, pgs, str, size,
+ TEXT_FROM_STRING |
+ TEXT_REPLACE_X_WIDTHS | TEXT_REPLACE_Y_WIDTHS |
+ TEXT_DO_DRAW | TEXT_INTERVENE | TEXT_RETURN_WIDTH,
+ true);
+}
+
+/* glyphshow */
+private int setup_glyph(P4(gs_show_enum *, gs_state *, gs_glyph, uint));
+private font_proc_encode_char(gs_glyphshow_encode_char);
+int
+gs_glyphshow_init(gs_show_enum * penum, gs_state * pgs, gs_glyph glyph)
+{
+ return setup_glyph(penum, pgs, glyph, TEXT_DO_DRAW);
+}
+int
+gs_glyphpath_init(gs_show_enum * penum, gs_state * pgs, gs_glyph glyph,
+ bool stroke_path)
+{
+ int code = setup_glyph(penum, pgs, glyph,
+ (stroke_path ? TEXT_DO_TRUE_CHARPATH :
+ TEXT_DO_FALSE_CHARPATH));
+
+ penum->can_cache = -1;
+ if_debug1('k', "[k]glyphpath, can_cache=%d", penum->can_cache);
+ return code;
+}
+private int
+setup_glyph(gs_show_enum * penum, gs_state * pgs, gs_glyph glyph,
+ uint operation)
+{
+ int code;
+
+ if (pgs->font->FontType == ft_composite)
+ return_error(gs_error_invalidfont);
+ code = show_setup(penum, pgs, "\000" /* arbitrary char */ , 1,
+ TEXT_FROM_GLYPHS | TEXT_RETURN_WIDTH | operation,
+ true);
+ 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)
+{
+ return show_setup(penum, pgs, str, size,
+ TEXT_FROM_STRING | TEXT_DO_NONE | TEXT_INTERVENE,
+ false);
+}
+
+/* stringwidth[_n] */
+int
+gs_stringwidth_n_init(gs_show_enum * penum, gs_state * pgs,
+ const char *str, uint size)
+{
+ return stringwidth_setup(penum, pgs, str, size);
+}
+
+/* Common code for stringwidth[_n] */
+private int
+stringwidth_setup(gs_show_enum * penum, gs_state * pgs, const char *str,
+ uint size)
+{
+ int code = show_setup(penum, pgs, str, size,
+ TEXT_FROM_STRING | TEXT_DO_NONE | TEXT_RETURN_WIDTH,
+ false);
+ 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);
+ /* 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->ctm_default_set = false;
+ penum->dev_null = dev_null;
+ /* Account for the extra reference from the enumerator. */
+ rc_increment(dev_null);
+ gs_setdevice_no_init(pgs, (gx_device *) 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 = show_setup(penum, pgs, str, size,
+ TEXT_FROM_STRING |
+ (stroke_path ? TEXT_DO_TRUE_CHARPATH :
+ TEXT_DO_FALSE_CHARPATH),
+ false);
+
+ penum->can_cache = -1;
+ if_debug1('k', "[k]charpath, can_cache=%d", penum->can_cache);
+ return code;
+}
+
+/* charboxpath[_n] */
+int
+gs_charboxpath_n_init(gs_show_enum * penum, gs_state * pgs,
+ const char *str, uint size, bool use_boxes)
+{
+ int code = show_setup(penum, pgs, str, size,
+ TEXT_FROM_STRING |
+ (use_boxes ? TEXT_DO_TRUE_CHARBOXPATH :
+ TEXT_DO_FALSE_CHARBOXPATH),
+ false);
+
+ penum->can_cache = 0; /* different from charpath! */
+ if_debug1('k', "[k]charboxpath, can_cache=%d", penum->can_cache);
+ return code;
+}
+
+/* ------ Width/cache operators ------ */
+
+private int set_cache_device(P6(gs_show_enum * penum, gs_state * pgs,
+ floatp llx, floatp lly, floatp urx, floatp ury));
+
+/* 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_double(gs_show_enum * penum, gs_state * pgs, const double *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], pw[3], pw[4], pw[5]);
+}
+/* The _float procedure is strictly for backward compatibility. */
+int
+gs_setcachedevice_float(gs_show_enum * penum, gs_state * pgs, const float *pw)
+{
+ double w[6];
+ int i;
+
+ for (i = 0; i < 6; ++i)
+ w[i] = pw[i];
+ return gs_setcachedevice_double(penum, pgs, w);
+}
+
+/* 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_double(gs_show_enum * penum, gs_state * pgs,
+ const double *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
+ )
+ return 0; /* don't cache */
+ if ((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], pw2[3], pw2[4], pw2[5]);
+ 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], pw2[3], pw2[4], pw2[5]);
+ }
+ return code;
+}
+/* The _float procedure is strictly for backward compatibility. */
+int
+gs_setcachedevice2_float(gs_show_enum * penum, gs_state * pgs, const float *pw2)
+{
+ double w2[10];
+ int i;
+
+ for (i = 0; i < 10; ++i)
+ w2[i] = pw2[i];
+ return gs_setcachedevice2_double(penum, pgs, w2);
+}
+
+/* Set up the cache device if relevant. */
+/* Return 1 if we just set up a cache device. */
+/* Used by setcachedevice and setcachedevice2. */
+private int
+set_cache_device(gs_show_enum * penum, gs_state * pgs, floatp llx, floatp lly,
+ floatp urx, floatp ury)
+{
+ 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 0; /* don't cache */
+ {
+ 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')) {
+ dlprintf6("[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 || pfont->PaintType != 0) {
+ /* 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 &&
+ 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 */
+ gx_set_device_only(pgs, (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 !SHOW_IS_DRAWING(penum);
+}
+
+/* ------ 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 */
+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 (!SHOW_IS_DRAWING(penum) ||
+ 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 = gs_state_color_load(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_fast_move(gs_state * pgs, gs_fixed_point * pwxy)
+{
+ int code = gx_path_add_rel_point_inline(pgs->path, pwxy->x, pwxy->y);
+
+ /* If the current position is out of range, don't try to move. */
+ if (code == gs_error_limitcheck && pgs->clamp_coordinates)
+ code = 0;
+ return code;
+}
+private int
+show_move(register gs_show_enum * penum)
+{
+ register gs_state *pgs = penum->pgs;
+
+ if (SHOW_IS_XYCSHOW(penum)) {
+ penum->continue_proc = continue_show;
+ return gs_show_move;
+ }
+ if (SHOW_IS_ADD_TO_ALL(penum))
+ gs_rmoveto(pgs, penum->text.delta_all.x, penum->text.delta_all.y);
+ if (SHOW_IS_ADD_TO_SPACE(penum)) {
+ 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->text.space.s_char)
+ gs_rmoveto(pgs, penum->text.delta_space.x,
+ penum->text.delta_space.y);
+ }
+ /* wxy is in device coordinates */
+ {
+ int code = show_fast_move(pgs, &penum->wxy);
+
+ if (code < 0)
+ return code;
+ }
+ /* Check for kerning, but not on the last character. */
+ if (SHOW_IS_DO_KERN(penum) && penum->index < penum->text.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 =
+ (penum->fstack.depth < 0 ? pgs->font : penum->fstack.items[0].font);
+ int wmode = rfont->WMode;
+
+ font_proc_next_char((*next_char)) = rfont->procs.next_char;
+ font_proc_next_glyph((*next_glyph)) = rfont->procs.next_glyph;
+#define next_char_glyph(penum, pchr, pglyph)\
+ (next_char == 0 ? (*next_glyph)(penum, pchr, pglyph) :\
+ (*(pglyph) = gs_no_glyph, (*next_char)(penum, pchr)))
+ 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 && SHOW_IS_DRAWING(penum)) {
+ code = gs_state_color_load(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_glyph(penum, &chr, &glyph))) {
+ 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;
+ if (glyph == gs_no_glyph) {
+ 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 (!SHOW_IS_DRAWING(penum) != 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_local(&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_free(&box_path, "show_proceed(box path)");
+ if (code < 0)
+ return code;
+ } else if (SHOW_IS_DRAWING(penum)) {
+ code = gx_image_cached_char(penum, cc);
+ if (code < 0)
+ return code;
+ else if (code > 0) {
+ cc = 0;
+ goto no_cache;
+ }
+ }
+ if (SHOW_IS_SLOW(penum)) {
+ /* 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(pgs, &cc->wxy);
+ if (code) {
+ /* Might be kshow, so store the state. */
+ penum->current_glyph = glyph;
+ return code;
+ }
+ }
+ }
+ } else {
+ /* Can't use cache */
+ switch ((code = next_char_glyph(penum, &chr, &glyph))) {
+ 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;
+ if (glyph == gs_no_glyph) {
+ 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 data 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->show_gstate =
+ (penum->show_gstate == pgs ? pgs->saved : penum->show_gstate);
+ 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;
+ penum->origin.x = cpt.x;
+ penum->origin.y = cpt.y;
+ /* 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);
+ }
+ 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;
+#undef next_char_glyph
+}
+
+/* Finish show or stringwidth */
+private int
+show_finish(gs_show_enum * penum)
+{
+ gs_state *pgs = penum->pgs;
+ int code, rcode;
+
+ gs_show_enum_release(penum, NULL);
+ if (!SHOW_IS_STRINGWIDTH(penum))
+ return 0;
+ /* Save the accumulated width before returning, */
+ /* and undo the extra gsave. */
+ code = gs_currentpoint(pgs, &penum->width);
+ rcode = gs_grestore(pgs);
+ return (code < 0 ? code : rcode);
+}
+
+/* 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->text.data.bytes[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);
+}
+
+/* Restore the current font after cshow. */
+int
+gs_show_restore_font(const gs_show_enum * penum)
+{
+ int fdepth = penum->fstack.depth;
+
+ if (fdepth >= 0) {
+ gs_state *pgs = penum->pgs;
+
+ gs_setfont(pgs, penum->fstack.items[0].font);
+ pgs->font = penum->fstack.items[fdepth].font;
+ }
+ return 0;
+}
+
+/* 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 ((!SHOW_IS_DRAWING(penum) || penum->cc != 0) &&
+ penum->pgs->level == penum->level + 1);
+}
+
+/* ------ Internal routines ------ */
+
+/* Initialize a show enumerator. */
+private int
+show_setup(register gs_show_enum * penum, gs_state * pgs, const char *str,
+ uint size, uint operation, bool propagate_charpath)
+{
+ int code;
+ gs_font *pfont;
+
+ /* Set rest of common members. */
+ penum->text.operation = operation;
+ penum->text.data.bytes = (const byte *)str; /* avoid signed chars */
+ penum->text.size = size;
+ penum->index = 0;
+ /* Set other members. */
+ gx_set_dev_color(pgs);
+ pfont = pgs->font;
+ penum->pgs = pgs;
+ penum->level = pgs->level;
+ if (operation & TEXT_DO_ANY_CHARPATH)
+ penum->charpath_flag =
+ (operation & TEXT_DO_FALSE_CHARPATH ? cpm_false_charpath :
+ operation & TEXT_DO_TRUE_CHARPATH ? cpm_true_charpath :
+ operation & TEXT_DO_FALSE_CHARBOXPATH ? cpm_false_charboxpath :
+ operation & TEXT_DO_TRUE_CHARBOXPATH ? cpm_true_charboxpath :
+ cpm_show /* can't happen */ );
+ else
+ penum->charpath_flag =
+ (propagate_charpath ? pgs->in_charpath : cpm_show);
+ penum->dev_cache = 0;
+ penum->dev_cache2 = 0;
+ penum->dev_null = 0;
+ penum->cc = 0;
+ penum->continue_proc = continue_show;
+ code = (*pfont->procs.init_fstack) (penum, pfont);
+ if (code < 0)
+ return code;
+ penum->can_cache = /* show_state_setup may reset */
+ (penum->charpath_flag == cpm_show ? 1 : -1);
+ code = show_state_setup(penum);
+ if (code < 0)
+ return code;
+ penum->show_gstate =
+ (propagate_charpath && (pgs->in_charpath != 0) ?
+ 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;
+ gx_clip_path *pcpath;
+ 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);
+ }
+ /* Skewing or non-rectangular rotation are not supported. */
+ if (!CACHE_ROTATED_CHARS &&
+ (is_fzero2(pgs->char_tm.xy, pgs->char_tm.yx) ||
+ is_fzero2(pgs->char_tm.xx, pgs->char_tm.yy))
+ )
+ penum->can_cache = 0;
+ if (penum->can_cache >= 0 &&
+ gx_effective_clip_path(pgs, &pcpath) >= 0
+ ) {
+ gs_fixed_rect cbox;
+
+ gx_cpath_inner_box(pcpath, &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(pcpath, &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 &&
+ SHOW_IS_DRAWING(penum) &&
+ 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;
+ /* Account for the extra references from the enumerator. */
+ rc_increment(dev);
+ rc_increment(dev2);
+ 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)
+{
+ gs_glyph ignore_glyph;
+
+ return gs_default_next_glyph(penum, pchr, &ignore_glyph);
+}
+
+/* Default next-glyph procedure. */
+int
+gs_default_next_glyph(gs_show_enum * penum, gs_char * pchr, gs_glyph * pglyph)
+{
+ if (penum->index == penum->text.size)
+ return 2;
+ *pchr = penum->text.data.bytes[penum->index++];
+ *pglyph = gs_no_glyph;
+ return 0;
+}
diff --git a/pstoraster/gschar.h b/pstoraster/gschar.h
new file mode 100644
index 000000000..4350a4c13
--- /dev/null
+++ b/pstoraster/gschar.h
@@ -0,0 +1,130 @@
+/* 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 supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Client interface to character operations */
+
+#ifndef gschar_INCLUDED
+# define gschar_INCLUDED
+
+#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 *));
+
+/* Initialize a text enumeration. */
+int gs_show_n_init(P4(gs_show_enum *, gs_state *, const char *, uint)),
+ gs_ashow_n_init(P6(gs_show_enum *, gs_state *, floatp, floatp, const char *, uint)),
+ gs_widthshow_n_init(P7(gs_show_enum *, gs_state *, floatp, floatp, gs_char, const char *, uint)),
+ gs_awidthshow_n_init(P9(gs_show_enum *, gs_state *, floatp, floatp, gs_char, floatp, floatp, const char *, uint)),
+ gs_kshow_n_init(P4(gs_show_enum *, gs_state *, const char *, uint)),
+ 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_n_init(P4(gs_show_enum *, gs_state *, const char *, uint)),
+ gs_stringwidth_n_init(P4(gs_show_enum *, gs_state *, const char *, uint)),
+ gs_charpath_n_init(P5(gs_show_enum *, gs_state *, const char *, uint, bool)),
+ gs_glyphpath_init(P4(gs_show_enum *, gs_state *, gs_glyph, 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 *));
+int gs_show_restore_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* return 1 iff the cache device was just installed. */
+int gs_setcachedevice_float(P3(gs_show_enum *, gs_state *, const float * /*[6] */ ));
+int gs_setcachedevice_double(P3(gs_show_enum *, gs_state *, const double * /*[6] */ ));
+
+#define gs_setcachedevice(penum, pgs, pw)\
+ gs_setcachedevice_float(penum, pgs, pw)
+int gs_setcachedevice2_float(P3(gs_show_enum *, gs_state *, const float * /*[10] */ ));
+int gs_setcachedevice2_double(P3(gs_show_enum *, gs_state *, const double * /*[10] */ ));
+
+#define gs_setcachedevice2(penum, pgs, pw2)\
+ gs_setcachedevice2_float(penum, pgs, pw2)
+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 *));
+
+#endif /* gschar_INCLUDED */
diff --git a/pstoraster/gschar0.c b/pstoraster/gschar0.c
new file mode 100644
index 000000000..69f207d3c
--- /dev/null
+++ b/pstoraster/gschar0.c
@@ -0,0 +1,407 @@
+/* Copyright (C) 1991, 1992, 1993, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Composite font decoding for Ghostscript library */
+#include "memory_.h"
+#include "gx.h"
+#include "gserrors.h"
+#include "gsstruct.h"
+#include "gsfcmap.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;
+
+ while (cfont->FontType == ft_composite) {
+ gs_font_type0 *const cmfont = (gs_font_type0 *) cfont;
+
+ if (!fmap_type_is_modal(cmfont->data.FMapType))
+ break;
+ 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;
+}
+/* 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 root EscChar of a composite font, which overrides the EscChar */
+/* of descendant fonts. */
+private uint
+root_esc_char(const gs_show_enum * penum)
+{
+ return ((gs_font_type0 *) (penum->fstack.items[0].font))->data.EscChar;
+}
+
+/* Get the next character or glyph 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_glyph(register gs_show_enum * penum, gs_char * pchr,
+ gs_glyph * pglyph)
+{
+ const byte *str = penum->text.data.bytes;
+ const byte *p = str + penum->index;
+ const byte *end = str + penum->text.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;
+ gs_glyph glyph = gs_no_glyph;
+ int changed = 0;
+
+#define need_left(n)\
+ if ( end - p < n ) return_error(gs_error_rangecheck)
+
+ /*
+ * 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_esc_char(penum))
+ 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_esc_char(penum))
+ 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_esc_char(penum))
+ 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_esc_char(penum))
+ 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;
+ }
+ /* At this point, chr == *p. */
+ /* (This is important to know for CMap'ed fonts.) */
+ 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;
+ }
+
+ case fmap_CMap:
+ {
+ gs_const_string cstr;
+ uint mindex = p - str - 1; /* p was incremented */
+ int code;
+
+ cstr.data = str;
+ cstr.size = end - str;
+ code = gs_cmap_decode_next(pdata->CMap, &cstr, &mindex,
+ &fidx, &chr, &glyph);
+ if (code < 0)
+ return code;
+ p = str + mindex;
+ if_debug3('J', "[J]CMap returns %d, chr=0x%lx, glyph=0x%lx\n",
+ code, (ulong) chr, (ulong) glyph);
+ if (code == 0) {
+ if (glyph == gs_no_glyph) {
+ glyph = gs_min_cid_glyph;
+ if_debug0('J', "... undefined\n");
+ goto done;
+ }
+ } else
+ chr = (gs_char) glyph, glyph = gs_no_glyph;
+/****** RESCAN chr IF DESCENDANT IS CMAP'ED ******/
+ break;
+ }
+
+ }
+
+ select_descendant(pfont, pdata, fidx, fdepth);
+ if_debug2('J', "... new depth=%d, new font=0x%lx\n",
+ fdepth, (ulong) pfont);
+ }
+done:
+ *pchr = chr;
+ *pglyph = glyph;
+ /* Update the pointer into the original string, but only if */
+ /* we didn't switch over to parsing a code from a CMap. */
+ if (str == penum->text.data.bytes)
+ penum->index = p - str;
+ 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..968b81346
--- /dev/null
+++ b/pstoraster/gscie.c
@@ -0,0 +1,1357 @@
+/* Copyright (C) 1992, 1995, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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 int cie_joint_caches_init(P3(gx_cie_joint_caches *,
+ const gs_cie_common *,
+ gs_cie_render *));
+private void cie_joint_caches_complete(P3(gx_cie_joint_caches *,
+ const gs_cie_common *, const gs_cie_render *));
+private void cie_cache_restrict(P2(cie_cache_floats *, const gs_range *));
+private void cie_mult3(P3(const gs_vector3 *, const gs_matrix3 *,
+ gs_vector3 *));
+private void cie_matrix_mult3(P3(const gs_matrix3 *, const gs_matrix3 *,
+ gs_matrix3 *));
+private void cie_invert3(P2(const gs_matrix3 *, gs_matrix3 *));
+private void 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_LOAD_CACHE_BODY(pcache, domains, rprocs, pcie, cname)\
+ BEGIN\
+ int j;\
+\
+ for (j = 0; j < countof(pcache); j++) {\
+ int i;\
+ gs_for_loop_params lp;\
+\
+ gs_cie_cache_init(&(pcache)[j].floats.params, &lp,\
+ &(domains)[j], cname);\
+ for (i = 0; i < gx_cie_cache_size; lp.init += lp.step, i++)\
+ pcache[j].floats.values[i] = (*(rprocs)->procs[j])(lp.init, pcie);\
+ }\
+ END
+
+/* Allocator structure types */
+private_st_joint_caches();
+
+/* ------ 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;
+}
+
+/* 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};
+
+/* Initialize a CIE color. */
+/* This only happens on setcolorspace. */
+void
+gx_init_CIE(gs_client_color * pcc, const gs_color_space * pcs)
+{
+ gx_init_paint_4(pcc, pcs);
+ /* (0...) may not be within the range of allowable values. */
+ (*pcs->type->restrict_color)(pcc, pcs);
+}
+
+/* Restrict CIE colors. */
+
+#define FORCE_VALUE(pcc, i, range)\
+ if ( pcc->paint.values[i] <= range.rmin )\
+ pcc->paint.values[i] = range.rmin;\
+ else if ( pcc->paint.values[i] >= range.rmax )\
+ pcc->paint.values[i] = range.rmax
+#define FORCE_RANGE(pcc, i, erange)\
+ FORCE_VALUE(pcc, i, pcie->erange.ranges[i])
+
+void
+gx_restrict_CIEDEFG(gs_client_color * pcc, const gs_color_space * pcs)
+{
+ const gs_cie_defg *pcie = pcs->params.defg;
+
+ FORCE_RANGE(pcc, 0, RangeDEFG);
+ FORCE_RANGE(pcc, 1, RangeDEFG);
+ FORCE_RANGE(pcc, 2, RangeDEFG);
+ FORCE_RANGE(pcc, 3, RangeDEFG);
+}
+void
+gx_restrict_CIEDEF(gs_client_color * pcc, const gs_color_space * pcs)
+{
+ const gs_cie_def *pcie = pcs->params.def;
+
+ FORCE_RANGE(pcc, 0, RangeDEF);
+ FORCE_RANGE(pcc, 1, RangeDEF);
+ FORCE_RANGE(pcc, 2, RangeDEF);
+}
+void
+gx_restrict_CIEABC(gs_client_color * pcc, const gs_color_space * pcs)
+{
+ const gs_cie_abc *pcie = pcs->params.abc;
+
+ FORCE_RANGE(pcc, 0, RangeABC);
+ FORCE_RANGE(pcc, 1, RangeABC);
+ FORCE_RANGE(pcc, 2, RangeABC);
+}
+void
+gx_restrict_CIEA(gs_client_color * pcc, const gs_color_space * pcs)
+{
+ const gs_cie_a *pcie = pcs->params.a;
+
+ FORCE_VALUE(pcc, 0, pcie->RangeA);
+}
+
+#undef FORCE_VALUE
+#undef FORCE_RANGE
+
+/* ================ Table setup ================ */
+
+/* ------ Install a CIE color space ------ */
+
+private int cie_load_common_cache(P3(gs_cie_common *, gs_state *,
+ client_name_t));
+private void cie_cache_mult(P3(gx_cie_vector_cache *, const gs_vector3 *,
+ const cie_cache_floats *));
+private bool cie_cache_mult3(P2(gx_cie_vector_cache *,
+ const gs_matrix3 *));
+
+private int
+gx_install_cie_abc(gs_cie_abc *pcie, gs_state * pgs)
+{
+ cie_matrix_init(&pcie->MatrixABC);
+ CIE_LOAD_CACHE_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");
+}
+
+int
+gx_install_CIEDEFG(gs_color_space * pcs, gs_state * pgs)
+{
+ gs_cie_defg *pcie = pcs->params.defg;
+
+ CIE_LOAD_CACHE_BODY(pcie->caches_defg.DecodeDEFG, pcie->RangeDEFG.ranges,
+ &pcie->DecodeDEFG, pcie, "DecodeDEFG");
+ return gx_install_cie_abc((gs_cie_abc *)pcie, pgs);
+}
+
+int
+gx_install_CIEDEF(gs_color_space * pcs, gs_state * pgs)
+{
+ gs_cie_def *pcie = pcs->params.def;
+
+ CIE_LOAD_CACHE_BODY(pcie->caches_def.DecodeDEF, pcie->RangeDEF.ranges,
+ &pcie->DecodeDEF, pcie, "DecodeDEF");
+ return gx_install_cie_abc((gs_cie_abc *)pcie, pgs);
+}
+
+int
+gx_install_CIEABC(gs_color_space * pcs, gs_state * pgs)
+{
+ return gx_install_cie_abc(pcs->params.abc, pgs);
+}
+
+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;
+ int code;
+
+ cie_matrix_init(&pcie->MatrixLMN);
+ CIE_LOAD_CACHE_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);
+ code = cie_joint_caches_init(pjc, pcie, pgs->cie_render);
+ if (code < 0)
+ return code;
+ cie_joint_caches_complete(pjc, pcie, pgs->cie_render);
+ return 0;
+}
+
+/* Restrict and scale the DecodeDEF[G] cache according to RangeHIJ[K]. */
+private void
+gs_cie_defx_scale(float *values, const gs_range *range)
+{
+ double scale = 255.0 / (range->rmax - range->rmin);
+ int i;
+
+ for (i = 0; i < gx_cie_cache_size; ++i) {
+ float value = values[i];
+
+ values[i] =
+ (value <= range->rmin ? 0 :
+ value >= range->rmax ? 255 :
+ (value - range->rmin) * scale);
+ }
+}
+
+/* Complete loading a CIEBasedDEFG color space. */
+/* This routine is not idempotent. */
+void
+gs_cie_defg_complete(gs_cie_defg * pcie)
+{
+ int j;
+
+ for (j = 0; j < 4; ++j)
+ gs_cie_defx_scale(pcie->caches_defg.DecodeDEFG[j].floats.values,
+ &pcie->RangeHIJK.ranges[j]);
+ gs_cie_abc_complete((gs_cie_abc *)pcie);
+}
+
+/* Complete loading a CIEBasedDEF color space. */
+/* This routine is not idempotent. */
+void
+gs_cie_def_complete(gs_cie_def * pcie)
+{
+ int j;
+
+ for (j = 0; j < 3; ++j)
+ gs_cie_defx_scale(pcie->caches_def.DecodeDEF[j].floats.values,
+ &pcie->RangeHIJ.ranges[j]);
+ gs_cie_abc_complete((gs_cie_abc *)pcie);
+}
+
+/* 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
+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
+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 * pcrd)
+{
+ int code = gs_cie_render_complete(pcrd);
+
+ if (code < 0)
+ return code;
+ rc_assign(pgs->cie_render, pcrd, "gs_setcolorrendering");
+ /* Initialize the joint caches if needed. */
+ code = 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, setting base and factor. */
+/* This procedure is idempotent. */
+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 the derived values in a CRD that don't involve the cached
+ * procedure values. This procedure is idempotent.
+ */
+private void cie_transform_range3(P3(const gs_range3 *, const gs_matrix3 *,
+ gs_range3 *));
+int
+gs_cie_render_init(gs_cie_render * pcrd)
+{
+ gs_matrix3 PQR_inverse;
+
+ if (pcrd->status >= CIE_RENDER_STATUS_INITED)
+ return 0; /* init already done */
+ cie_matrix_init(&pcrd->MatrixLMN);
+ cie_matrix_init(&pcrd->MatrixABC);
+ cie_matrix_init(&pcrd->MatrixPQR);
+ cie_invert3(&pcrd->MatrixPQR, &PQR_inverse);
+ cie_matrix_mult3(&pcrd->MatrixLMN, &PQR_inverse,
+ &pcrd->MatrixPQR_inverse_LMN);
+ cie_transform_range3(&pcrd->RangePQR, &pcrd->MatrixPQR_inverse_LMN,
+ &pcrd->DomainLMN);
+ cie_transform_range3(&pcrd->RangeLMN, &pcrd->MatrixABC,
+ &pcrd->DomainABC);
+ cie_mult3(&pcrd->points.WhitePoint, &pcrd->MatrixPQR, &pcrd->wdpqr);
+ cie_mult3(&pcrd->points.BlackPoint, &pcrd->MatrixPQR, &pcrd->bdpqr);
+ pcrd->status = CIE_RENDER_STATUS_INITED;
+ return 0;
+}
+
+/*
+ * Sample the EncodeLMN, EncodeABC, and RenderTableT CRD procedures, and
+ * load the caches. This procedure is idempotent.
+ */
+int
+gs_cie_render_sample(gs_cie_render * pcrd)
+{
+ int code;
+
+ if (pcrd->status >= CIE_RENDER_STATUS_SAMPLED)
+ return 0; /* sampling already done */
+ code = gs_cie_render_init(pcrd);
+ if (code < 0)
+ return code;
+ CIE_LOAD_CACHE_BODY(pcrd->caches.EncodeLMN, pcrd->DomainLMN.ranges,
+ &pcrd->EncodeLMN, pcrd, "EncodeLMN");
+ CIE_LOAD_CACHE_BODY(pcrd->caches.EncodeABC, pcrd->DomainABC.ranges,
+ &pcrd->EncodeABC, pcrd, "EncodeABC");
+ if (pcrd->RenderTable.lookup.table != 0) {
+ int i, j, m = pcrd->RenderTable.lookup.m;
+ gs_for_loop_params flp;
+
+ for (j = 0; j < m; j++)
+ gs_cie_cache_init(&pcrd->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++)
+ pcrd->caches.RenderTableT[j].fracs.values[i] =
+ (*pcrd->RenderTable.T.procs[j])((byte) i, pcrd);
+ }
+ pcrd->status = CIE_RENDER_STATUS_SAMPLED;
+ return 0;
+}
+
+/* Transform a set of ranges. */
+private void
+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;
+
+ if (umin > umax)
+ temp = umin, umin = umax, umax = temp;
+ if (vmin > vmax)
+ temp = vmin, vmin = vmax, vmax = temp;
+ if (wmin > wmax)
+ temp = wmin, wmin = wmax, wmax = temp;
+ out->rmin = umin + vmin + wmin;
+ out->rmax = umax + vmax + wmax;
+}
+private void
+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]);
+}
+
+/*
+ * Finish preparing a CRD for installation, by restricting and/or
+ * transforming the cached procedure values. The actual work done by
+ * this procedure is not idempotent, but the CRD status prevents it
+ * from being done more than once.
+ */
+int
+gs_cie_render_complete(gs_cie_render * pcrd)
+{
+ int code;
+
+ if (pcrd->status >= CIE_RENDER_STATUS_COMPLETED)
+ return 0; /* completion already done */
+ code = gs_cie_render_sample(pcrd);
+ if (code < 0)
+ return code;
+ /*
+ * Since range restriction happens immediately after
+ * the cache lookup, we can save a step by restricting
+ * the values in the cache entries.
+ *
+ * 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.
+ */
+ pcrd->MatrixABCEncode = pcrd->MatrixABC;
+ {
+ int c;
+ double f;
+
+ for (c = 0; c < 3; c++) {
+ gx_cie_scalar_cache *pcache = &pcrd->caches.EncodeABC[c];
+
+ cie_cache_restrict(&pcrd->caches.EncodeLMN[c].floats,
+ &pcrd->RangeLMN.ranges[c]);
+ cie_cache_restrict(&pcrd->caches.EncodeABC[c].floats,
+ &pcrd->RangeABC.ranges[c]);
+ if (pcrd->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 = pcrd->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 = pcrd->RenderTable.lookup.m;
+ int k =
+ (c == 0 ? 1 : c == 1 ?
+ m * pcrd->RenderTable.lookup.dims[2] : m);
+
+# define scale_index(f, n, itemp)\
+ (restrict_index(f, n, itemp) * k)
+#endif
+ const gs_range *prange =
+ pcrd->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 = pcrd->caches.EncodeABC[i].floats.params.factor;\
+ pcrd->MatrixABCEncode.cu.t *= f;\
+ pcrd->MatrixABCEncode.cv.t *= f;\
+ pcrd->MatrixABCEncode.cw.t *= f;\
+ pcrd->EncodeABC_base[i] =\
+ float2cie_cached(pcrd->caches.EncodeABC[i].floats.params.base * f)
+ mabc(0, u);
+ mabc(1, v);
+ mabc(2, w);
+ pcrd->MatrixABCEncode.is_identity = 0;
+ }
+#undef mabc
+ cie_cache_mult3(pcrd->caches.EncodeLMN, &pcrd->MatrixABCEncode);
+ pcrd->status = CIE_RENDER_STATUS_COMPLETED;
+ return 0;
+}
+
+/* Apply a range restriction to a cache. */
+private void
+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;
+
+ do {
+ switch (pcs->type->index) {
+ case gs_color_space_index_CIEDEF:
+ return &pcs->params.def->common;
+ case gs_color_space_index_CIEDEFG:
+ return &pcs->params.defg->common;
+ case gs_color_space_index_CIEABC:
+ return &pcs->params.abc->common;
+ case gs_color_space_index_CIEA:
+ return &pcs->params.a->common;
+ default:
+ pcs = gs_cspace_base_space(pcs);
+ break;
+ }
+ } while (pcs != 0);
+
+ return 0;
+}
+
+/* Finish loading the joint caches for the current color space. */
+int
+gs_cie_cs_complete(gs_state * pgs, bool init)
+{
+ const gs_cie_common *common = gs_cie_cs_common(pgs);
+
+ if (common) {
+ if (init) {
+ int code = cie_joint_caches_init(pgs->cie_joint_caches, common,
+ pgs->cie_render);
+
+ if (code < 0)
+ return code;
+ }
+ cie_joint_caches_complete(pgs->cie_joint_caches, common,
+ pgs->cie_render);
+ }
+ return 0;
+}
+
+/*
+ * Compute the source and destination WhitePoint and BlackPoint for
+ * the TransformPQR procedure.
+ */
+int
+gs_cie_compute_wbsd(gs_cie_wbsd * pwbsd,
+ const gs_vector3 * cs_WhitePoint, const gs_vector3 * cs_BlackPoint,
+ const gs_cie_render * pcrd)
+{
+ pwbsd->ws.xyz = *cs_WhitePoint;
+ cie_mult3(&pwbsd->ws.xyz, &pcrd->MatrixPQR, &pwbsd->ws.pqr);
+ pwbsd->bs.xyz = *cs_BlackPoint;
+ cie_mult3(&pwbsd->bs.xyz, &pcrd->MatrixPQR, &pwbsd->bs.pqr);
+ pwbsd->wd.xyz = pcrd->points.WhitePoint;
+ pwbsd->wd.pqr = pcrd->wdpqr;
+ pwbsd->bd.xyz = pcrd->points.BlackPoint;
+ pwbsd->bd.pqr = pcrd->bdpqr;
+ return 0;
+}
+
+/* Compute values derived from the color space and rendering parameters */
+/* other than the cached procedure values. This routine is idempotent. */
+private int
+cie_joint_caches_init(gx_cie_joint_caches * pjc,
+ const gs_cie_common * pcie,
+ gs_cie_render * pcrd)
+{
+ gs_cie_compute_wbsd(&pjc->points_sd, &pcie->points.WhitePoint,
+ &pcie->points.BlackPoint, pcrd);
+ cie_matrix_mult3(&pcrd->MatrixPQR, &pcie->MatrixLMN,
+ &pjc->MatrixLMN_PQR);
+ /* Load the TransformPQR caches. */
+ {
+ int j;
+
+ for (j = 0; j < 3; j++) {
+ int i;
+ gs_for_loop_params lp;
+
+ gs_cie_cache_init(&pjc->TransformPQR[j].floats.params, &lp,
+ &pcrd->RangePQR.ranges[j], "TransformPQR");
+ for (i = 0; i < gx_cie_cache_size; lp.init += lp.step, i++) {
+ float out;
+ int code =
+ (*pcrd->TransformPQR.proc)(j, lp.init, &pjc->points_sd,
+ pcrd, &out);
+
+ if (code < 0)
+ return code;
+ pjc->TransformPQR[j].floats.values[i] = out;
+ }
+ }
+ }
+ return 0;
+}
+
+/* Complete the loading of the joint caches. This routine is NOT */
+/* idempotent. */
+private void
+cie_joint_caches_complete(gx_cie_joint_caches * pjc,
+ const gs_cie_common * pcie, const gs_cie_render * pcrd)
+{
+ int j;
+
+ for (j = 0; j < 3; j++) {
+ cie_cache_restrict(&pjc->TransformPQR[j].floats,
+ &pcrd->RangePQR.ranges[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, &pcrd->MatrixPQR_inverse_LMN);
+}
+
+/* ================ Color rendering (using the caches) ================ */
+
+private int cie_remap_finish(P3(const cie_cached_vector3 *,
+ frac *, const gs_imager_state *));
+private void cie_lookup_mult3(P2(cie_cached_vector3 *,
+ const gx_cie_vector_cache *));
+
+#ifdef DEBUG
+private void
+cie_lookup_map3(cie_cached_vector3 * pvec,
+ const gx_cie_vector_cache * pc /*[3] */ , const char *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
+
+/* Render a CIEBasedDEFG color. */
+int
+gx_concretize_CIEDEFG(const gs_client_color * pc, const gs_color_space * pcs,
+ frac * pconc, const gs_imager_state * pis)
+{
+ const gs_cie_defg *pcie = pcs->params.defg;
+ int i;
+ fixed hijk[4];
+ frac abc[3];
+ cie_cached_vector3 vec3;
+
+ if_debug4('c', "[c]concretize DEFG [%g %g %g %g]\n",
+ pc->paint.values[0], pc->paint.values[1],
+ pc->paint.values[2], pc->paint.values[3]);
+ /* Apply DecodeDEFG (including restriction to RangeHIJK). */
+ for (i = 0; i < 4; ++i) {
+ int tmax = pcie->Table.dims[i] - 1;
+ float value = (pc->paint.values[i] - pcie->RangeDEFG.ranges[i].rmin) *
+ tmax /
+ (pcie->RangeDEFG.ranges[i].rmax - pcie->RangeDEFG.ranges[i].rmin);
+ int vi = (int)value;
+ float vf = value - vi;
+ float v = pcie->caches_defg.DecodeDEFG[i].floats.values[vi];
+
+ if (vf != 0 && vi < tmax)
+ v += vf *
+ (pcie->caches_defg.DecodeDEFG[i].floats.values[vi + 1] - v);
+ hijk[i] = float2fixed(v);
+ }
+ /* Apply Table. */
+ gx_color_interpolate_linear(hijk, &pcie->Table, abc);
+ vec3.u = float2cie_cached(frac2float(abc[0]));
+ vec3.v = float2cie_cached(frac2float(abc[1]));
+ vec3.w = float2cie_cached(frac2float(abc[2]));
+ /* Apply DecodeABC and MatrixABC. */
+ if (!pcie->caches.skipABC)
+ cie_lookup_map3(&vec3 /* ABC => LMN */, &pcie->caches.DecodeABC[0],
+ "Decode/MatrixABC");
+ cie_remap_finish(&vec3, pconc, pis);
+ return 0;
+}
+
+/* Render a CIEBasedDEF color. */
+int
+gx_concretize_CIEDEF(const gs_client_color * pc, const gs_color_space * pcs,
+ frac * pconc, const gs_imager_state * pis)
+{
+ const gs_cie_def *pcie = pcs->params.def;
+ int i;
+ fixed hij[3];
+ frac abc[3];
+ cie_cached_vector3 vec3;
+
+ if_debug3('c', "[c]concretize DEF [%g %g %g]\n",
+ pc->paint.values[0], pc->paint.values[1],
+ pc->paint.values[2]);
+ /* Apply DecodeDEF (including restriction to RangeHIJ). */
+ for (i = 0; i < 3; ++i) {
+ int tmax = pcie->Table.dims[i] - 1;
+ float value = (pc->paint.values[i] - pcie->RangeDEF.ranges[i].rmin) *
+ tmax /
+ (pcie->RangeDEF.ranges[i].rmax - pcie->RangeDEF.ranges[i].rmin);
+ int vi = (int)value;
+ float vf = value - vi;
+ float v = pcie->caches_def.DecodeDEF[i].floats.values[vi];
+
+ if (vf != 0 && vi < tmax)
+ v += vf *
+ (pcie->caches_def.DecodeDEF[i].floats.values[vi + 1] - v);
+ hij[i] = float2fixed(v);
+ }
+ /* Apply Table. */
+ gx_color_interpolate_linear(hij, &pcie->Table, abc);
+ vec3.u = float2cie_cached(frac2float(abc[0]));
+ vec3.v = float2cie_cached(frac2float(abc[1]));
+ vec3.w = float2cie_cached(frac2float(abc[2]));
+ /* Apply DecodeABC and MatrixABC. */
+ if (!pcie->caches.skipABC)
+ cie_lookup_map3(&vec3 /* ABC => LMN */, &pcie->caches.DecodeABC[0],
+ "Decode/MatrixABC");
+ cie_remap_finish(&vec3, pconc, pis);
+ return 0;
+}
+
+/* Render a CIEBasedABC color. */
+/* We provide both remap and concretize, but only the former */
+/* needs to be efficient. */
+int
+gx_remap_CIEABC(const gs_client_color * pc, const gs_color_space * pcs,
+ gx_device_color * pdc, const gs_imager_state * pis, gx_device * dev,
+ gs_color_select_t select)
+{
+ 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, pis)) {
+ 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, pis,
+ dev, select);
+ 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, pis, dev, select);
+ return 0;
+ }
+ /* Can't happen. */
+ return_error(gs_error_unknownerror);
+#undef vlmn
+}
+int
+gx_concretize_CIEABC(const gs_client_color * pc, const gs_color_space * pcs,
+ frac * pconc, const gs_imager_state * pis)
+{
+ 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, pis);
+#undef vlmn
+ return 0;
+}
+
+/* Render a CIEBasedA color. */
+int
+gx_concretize_CIEA(const gs_client_color * pc, const gs_color_space * pcs,
+ frac * pconc, const gs_imager_state * pis)
+{
+ 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, pis);
+}
+
+/* Common rendering code. */
+/* Return 3 if RGB, 4 if CMYK. */
+private int
+cie_remap_finish(const cie_cached_vector3 * plmn, frac * pconc,
+ const gs_imager_state * pis)
+{
+ const gs_cie_render *pcie = pis->cie_render;
+ const gx_cie_joint_caches *pjc = pis->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
+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
+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
+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
+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
+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..8b8e3b736
--- /dev/null
+++ b/pstoraster/gscie.h
@@ -0,0 +1,688 @@
+/* Copyright (C) 1992, 1995, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Structures for CIE color algorithms */
+/* (requires gscspace.h, gscolor2.h) */
+
+#ifndef gscie_INCLUDED
+# define gscie_INCLUDED
+
+#include "gsrefct.h"
+#include "gsstruct.h" /* for extern_st */
+#include "gxctable.h"
+
+/* ---------------- Configuration parameters ---------------- */
+
+/* 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 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
+
+/* 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
+
+/* ------ Derived values ------ */
+
+/* from CIE_LOG2_CACHE_SIZE */
+#define gx_cie_log2_cache_size CIE_LOG2_CACHE_SIZE
+#define gx_cie_cache_size (1 << gx_cie_log2_cache_size)
+
+/* From 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
+
+/* From CIE_RENDER_TABLE_INTERPOLATE */
+#ifdef CIE_RENDER_TABLE_INTERPOLATE
+# define CIE_CACHE_INTERPOLATE
+#endif
+
+#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
+
+/* ---------------- Structures ---------------- */
+
+#ifndef gs_cie_render_DEFINED
+# define gs_cie_render_DEFINED
+typedef struct gs_cie_render_s gs_cie_render;
+#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.
+ */
+
+/* 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;
+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;
+
+/*
+ * The TransformPQR procedure depends on both the color space and the
+ * CRD, so we can't simply pass it through the band list as a table of
+ * sampled values, even though such a table exists as part of an
+ * internal cache. Instead, we use two different approaches. The
+ * graphics library knows that the cache must be reloaded whenever the
+ * color space or CRD changes, so we can simply transmit the cached
+ * values through the band list whenever this occurs. However, this
+ * still leaves the issue of how to represent the procedure in the CRD
+ * per se: such a representation is required in order for
+ * currentcolorrendering and setcolorrendering to work. For this
+ * purpose, we provide a procedure name and procedure data, which
+ * drivers can supply with their default CRDs; the driver must also be
+ * prepared to map the procedure name back to an actual set of
+ * procedures.
+ *
+ * To simplify the driver-provided CRD machinery, we define TransformPQR as
+ * a single procedure taking an integer that specifies the component number,
+ * rather than an array of procedures. Note that if proc_name != 0,
+ * proc is irrelevant -- the driver will provide it by looking up proc_name.
+ * For this reason, the last argument of TransformPQR must be writable.
+ * Note also that since TransformPQR can fail (if the driver doesn't
+ * recognize the proc_name), it must return a failure code.
+ */
+typedef int (*gs_cie_transform_proc)(P5(int, floatp, const gs_cie_wbsd *,
+ gs_cie_render *, float *));
+typedef struct gs_cie_transform_proc3_s {
+ gs_cie_transform_proc proc;
+ const char *proc_name;
+ gs_const_string proc_data;
+ const char *driver_name; /* for mapping proc_name back to procs */
+} 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;
+typedef struct cie_cache_floats_s {
+ cie_cache_params params;
+ float values[gx_cie_cache_size];
+} cie_cache_floats;
+typedef struct cie_cache_fracs_s {
+ cie_cache_params params;
+ frac values[gx_cie_cache_size];
+} cie_cache_fracs;
+typedef struct cie_cache_ints_s {
+ cie_cache_params params;
+ int values[gx_cie_cache_size];
+} cie_cache_ints;
+typedef union gx_cie_scalar_cache_s {
+ cie_cache_floats floats;
+ cie_cache_fracs fracs;
+ cie_cache_ints 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 color space dictionaries. */
+struct gs_cie_common_s {
+ int (*install_cspace) (P2(gs_color_space *, gs_state *));
+ void *client_data;
+ 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;
+};
+
+#define private_st_cie_common() /* in gscscie.c */\
+ gs_private_st_ptrs1(st_cie_common, gs_cie_common, "gs_cie_common",\
+ cie_common_enum_ptrs, cie_common_reloc_ptrs, client_data)
+
+#define gs_cie_common_elements\
+ gs_cie_common common; /* must be first */\
+ rc_header rc
+typedef struct gs_cie_common_elements_s {
+ gs_cie_common_elements;
+} gs_cie_common_elements_t;
+
+#define private_st_cie_common_elements() /* in gscscie.c */ \
+ gs_private_st_suffix_add0_local(st_cie_common_elements_t,\
+ gs_cie_common_elements_t,\
+ "gs_cie_common_elements_t",\
+ cie_common_enum_ptrs,\
+ cie_common_reloc_ptrs,\
+ st_cie_common)
+
+/* A CIEBasedA dictionary. */
+struct gs_cie_a_s {
+ gs_cie_common_elements; /* must be first */
+ 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 gscscie.c */\
+ gs_private_st_suffix_add0_local(st_cie_a, gs_cie_a, "gs_cie_a",\
+ cie_common_enum_ptrs,\
+ cie_common_reloc_ptrs,\
+ st_cie_common_elements_t)
+
+/* Common elements for CIEBasedABC, DEF, and DEFG dictionaries. */
+#define gs_cie_abc_elements\
+ gs_cie_common_elements; /* must be first */\
+ 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
+
+/* A CIEBasedABC dictionary. */
+struct gs_cie_abc_s {
+ gs_cie_abc_elements;
+};
+
+#define private_st_cie_abc() /* in gscscie.c */\
+ gs_private_st_suffix_add0_local(st_cie_abc, gs_cie_abc, "gs_cie_abc",\
+ cie_common_enum_ptrs, cie_common_reloc_ptrs,\
+ st_cie_common_elements_t)
+
+/* A CIEBasedDEF dictionary. */
+struct gs_cie_def_s {
+ gs_cie_abc_elements; /* must be first */
+ 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_def;
+};
+
+#define private_st_cie_def() /* in gscscie.c */\
+ gs_private_st_suffix_add1(st_cie_def, gs_cie_def, "gs_cie_def",\
+ cie_def_enum_ptrs, cie_def_reloc_ptrs,\
+ st_cie_abc, Table.table)
+
+/* A CIEBasedDEFG dictionary. */
+struct gs_cie_defg_s {
+ gs_cie_abc_elements;
+ 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_defg;
+};
+
+#define private_st_cie_defg() /* in gscscie.c */\
+ gs_private_st_suffix_add1(st_cie_defg, gs_cie_defg, "gs_cie_defg",\
+ cie_defg_enum_ptrs, cie_defg_reloc_ptrs,\
+ st_cie_abc, Table.table)
+
+/*
+ * Default values for components. Note that for some components, there are
+ * two sets of default procedures: _default (identity procedures) and
+ * _from_cache (procedures that just return the cached values). Currently
+ * we only provide the latter for the Encode elements of the CRD.
+ */
+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_render_proc3 EncodeLMN_from_cache;
+extern const gs_cie_render_proc3 EncodeABC_from_cache;
+extern const gs_cie_transform_proc3 TransformPQR_default;
+extern const gs_cie_transform_proc TransformPQR_lookup_proc_name;
+extern const gs_cie_render_table_procs RenderTableT_default;
+extern const gs_cie_render_table_procs RenderTableT_from_cache;
+
+/* ------ Rendering dictionaries ------ */
+
+struct gs_cie_wbsd_s {
+ struct {
+ gs_vector3 xyz, pqr;
+ } ws, bs, wd, bd;
+};
+typedef struct gs_cie_render_table_s {
+ /*
+ * If lookup.table == 0, the other members (of both lookup and T) are
+ * not set. If not 0, lookup.table points to an array of
+ * st_const_string_elements.
+ */
+ gx_color_lookup_table lookup;
+ gs_cie_render_table_procs T;
+} gs_cie_render_table_t;
+typedef enum {
+ CIE_RENDER_STATUS_BUILT,
+ CIE_RENDER_STATUS_INITED,
+ CIE_RENDER_STATUS_SAMPLED,
+ CIE_RENDER_STATUS_COMPLETED
+} cie_render_status_t;
+
+/* The main dictionary */
+struct gs_cie_render_s {
+ cie_render_status_t status;
+ rc_header rc;
+ void *client_data;
+ 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;
+ gs_cie_render_table_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;
+};
+
+/* The CRD type is public only for a type test in zcrd.c. */
+extern_st(st_cie_render1);
+#define public_st_cie_render1() /* in gscrd.c */\
+ gs_public_st_composite(st_cie_render1, gs_cie_render, "gs_cie_render",\
+ cie_render1_enum_ptrs, cie_render1_reloc_ptrs)
+
+/* ------ 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_LMN */
+} 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 procedures ------ */
+
+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_defg_complete(P1(gs_cie_defg *));
+void gs_cie_def_complete(P1(gs_cie_def *));
+void gs_cie_abc_complete(P1(gs_cie_abc *));
+void gs_cie_a_complete(P1(gs_cie_a *));
+gx_cie_joint_caches *gx_currentciecaches(P1(gs_state *));
+const gs_cie_common *gs_cie_cs_common(P1(gs_state *));
+int gs_cie_cs_complete(P2(gs_state *, bool));
+
+/*
+ * Compute the source and destination WhitePoint and BlackPoint for
+ * the TransformPQR procedure.
+ */
+int gs_cie_compute_wbsd(P4(gs_cie_wbsd * pwbsd,
+ const gs_vector3 * cs_WhitePoint,
+ const gs_vector3 * cs_BlackPoint,
+ const gs_cie_render * pcrd));
+
+/*
+ * Compute the derived values in a CRD that don't involve the cached
+ * procedure values, moving the CRD from "built" to "inited" status.
+ * If the CRD is already in "inited" or a later status, do nothing.
+ */
+int gs_cie_render_init(P1(gs_cie_render *));
+
+/*
+ * Sample the EncodeLMN, EncodeABC, and RenderTableT CRD procedures, and
+ * load the caches, moving the CRD from "inited" to "sampled" status.
+ * If the CRD is already in "sampled" or a later status, do nothing;
+ * otherwise, if the CRD is not in "inited" status, return an error.
+ */
+int gs_cie_render_sample(P1(gs_cie_render *));
+
+/*
+ * Finish preparing a CRD for installation, by restricting and/or
+ * transforming the cached procedure values, moving the CRD from "sampled"
+ * to "completed" status. If the CRD is already in "completed" status, do
+ * nothing; otherwise, if the CRD is not in "sampled" status, return an
+ * error.
+ */
+int gs_cie_render_complete(P1(gs_cie_render *));
+
+/* ---------------- Procedures ---------------- */
+
+/* ------ Constructors ------ */
+
+/*
+ * Note that these procedures take a client_data pointer as an operand. The
+ * client is responsible for allocating and deleting this object; the
+ * color space machinery does not take ownership of it.
+ *
+ * Note that these procedures set the reference count of the (large)
+ * parameter structures to 1, not 0. gs_setcolorspace will increment
+ * the reference count again, so unless you want the parameter structures
+ * to stay allocated permanently (or until a garbage collection),
+ * you should call cs_adjust_count(pcspace, -1). THIS IS A BUG IN THE API.
+ */
+extern int
+ gs_cspace_build_CIEA(P3(gs_color_space ** ppcspace, void *client_data,
+ gs_memory_t * pmem)),
+ gs_cspace_build_CIEABC(P3(gs_color_space ** ppcspace, void *client_data,
+ gs_memory_t * pmem)),
+ gs_cspace_build_CIEDEF(P3(gs_color_space ** ppcspace, void *client_data,
+ gs_memory_t * pmem)),
+ gs_cspace_build_CIEDEFG(P3(gs_color_space ** ppcspace, void *client_data,
+ gs_memory_t * pmem));
+
+/* ------ Accessors ------ */
+
+/*
+ * Note that the accessors depend heavily on "puns" between the variants
+ * of pcspace->params.{a,abc,def,defg}.
+ */
+
+/* Generic CIE based color space parameters */
+#define gs_cie_RangeLMN(pcspace) (&(pcspace)->params.a->common.RangeLMN)
+#define gs_cie_DecodeLMN(pcspace) (&(pcspace)->params.a->common.DecodeLMN)
+#define gs_cie_MatrixLMN(pcspace) (&(pcspace)->params.a->common.MatrixLMN)
+#define gs_cie_WhitePoint(pcspace)\
+ ((pcspace)->params.a->common.points.WhitePoint)
+#define gs_cie_BlackPoint(pcspace)\
+ ((pcspace)->params.a->common.points.BlackPoint)
+
+/* CIEBasedA color space */
+#define gs_cie_a_RangeA(pcspace) (&(pcspace)->params.a->RangeA)
+#define gs_cie_a_DecodeA(pcspace) (&(pcspace)->params.a->DecodeA)
+#define gs_cie_a_MatrixA(pcspace) (&(pcspace)->params.a->MatrixA)
+#define gs_cie_a_RangeA(pcspace) (&(pcspace)->params.a->RangeA)
+
+/* CIEBasedABC color space */
+/* Note that these also work for CIEBasedDEF[G] spaces. */
+#define gs_cie_abc_RangeABC(pcspace) (&(pcspace)->params.abc->RangeABC)
+#define gs_cie_abc_DecodeABC(pcspace) (&(pcspace)->params.abc->DecodeABC)
+#define gs_cie_abc_MatrixABC(pcspace) (&(pcspace)->params.abc->MatrixABC)
+
+/* CIDBasedDEF color space */
+#define gs_cie_def_RangeDEF(pcspace) (&(pcspace)->params.def->RangeDEF)
+#define gs_cie_def_DecodeDEF(pcspace) (&(pcspace)->params.def->DecodeDEF)
+#define gs_cie_def_RangeHIJ(pcspace) (&(pcspace)->params.def->RangeHIJ)
+
+/* CIDBasedDEFG color space */
+#define gs_cie_defg_RangeDEFG(pcspace) (&(pcspace)->params.defg->RangeDEFG)
+#define gs_cie_defg_DecodeDEFG(pcspace) (&(pcspace)->params.defg->DecodeDEFG)
+#define gs_cie_defg_RangeHIJK(pcspace) (&(pcspace)->params.defg->RangeHIJK)
+
+/*
+ * The following routine is provided so as to avoid explicitly exporting the
+ * CIEBasedDEF[G] color lookup table structure. It is doubtful any
+ * high-level clients will ever need to get this information.
+ *
+ * The caller must make sure the number of dimensions and strings provided
+ * are the number expected given the number of components in the color space.
+ * The procedure gs_color_space_num_components is available for this purpose.
+ *
+ * For a 3 component color space (CIEBasedDEF), ptable points to an array of
+ * pdims[0] gs_const_string structures, each of which is of length
+ * 3 * pdims[1] * pdims[2].
+ *
+ * For a 4 component color space (CIEBasedDEFG), ptable points to an array of
+ * pdims[0] * pdims[1] strings, each of which is of length
+ * 3 * pdims[2] * pdims[3].
+ *
+ * NB: the caller is responsible for deallocating the color table data
+ * when no longer needed. */
+extern int
+ gs_cie_defx_set_lookup_table(P3(gs_color_space * pcspace, int *pdims,
+ const gs_const_string * ptable));
+
+#endif /* gscie_INCLUDED */
diff --git a/pstoraster/gsclipsr.c b/pstoraster/gsclipsr.c
new file mode 100644
index 000000000..5d4cdabae
--- /dev/null
+++ b/pstoraster/gsclipsr.c
@@ -0,0 +1,45 @@
+/* Copyright (C) 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* clipsave/cliprestore */
+#include "gx.h"
+#include "gserrors.h"
+#include "gsclipsr.h"
+
+/* clipsave */
+int
+gs_clipsave(gs_state *pgs)
+{
+ /****** NYI ******/
+ return_error(gs_error_undefined);
+}
+
+/* cliprestore */
+int
+gs_cliprestore(gs_state *pgs)
+{
+ /****** NYI ******/
+ return_error(gs_error_undefined);
+}
diff --git a/pstoraster/gsclipsr.h b/pstoraster/gsclipsr.h
new file mode 100644
index 000000000..17560637d
--- /dev/null
+++ b/pstoraster/gsclipsr.h
@@ -0,0 +1,34 @@
+/* Copyright (C) 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Interface to clipsave/cliprestore */
+
+#ifndef gsclipsr_INCLUDED
+# define gsclipsr_INCLUDED
+
+int gs_clipsave(P1(gs_state *));
+int gs_cliprestore(P1(gs_state *));
+
+#endif /* gsclipsr_INCLUDED */
diff --git a/pstoraster/gscolor.c b/pstoraster/gscolor.c
new file mode 100644
index 000000000..849035a06
--- /dev/null
+++ b/pstoraster/gscolor.c
@@ -0,0 +1,357 @@
+/* Copyright (C) 1989, 1992, 1993, 1994, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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 *));
+
+/* Structure descriptors */
+public_st_client_color();
+public_st_transfer_map();
+
+/* GC procedures */
+#define mptr ((gx_transfer_map *)vptr)
+private
+ENUM_PTRS_BEGIN(transfer_map_enum_ptrs) return 0;
+
+case 0:
+ENUM_RETURN((mptr->proc == 0 ? mptr->closure.data : 0));
+ENUM_PTRS_END
+private RELOC_PTRS_BEGIN(transfer_map_reloc_ptrs)
+{
+ if (mptr->proc == 0)
+ RELOC_PTR(gx_transfer_map, closure.data);
+}
+RELOC_PTRS_END
+#undef mptr
+
+/* 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);
+}
+
+/* Force a value into the range [0.0..1.0]. */
+#define FORCE_UNIT(p) (p <= 0.0 ? 0.0 : p >= 1.0 ? 1.0 : p)
+
+/* Restrict colors with 1, 3, or 4 components to the range (0,1). */
+void
+gx_restrict01_paint_1(gs_client_color * pcc, const gs_color_space * pcs)
+{
+ pcc->paint.values[0] = FORCE_UNIT(pcc->paint.values[0]);
+}
+void
+gx_restrict01_paint_3(gs_client_color * pcc, const gs_color_space * pcs)
+{
+ pcc->paint.values[2] = FORCE_UNIT(pcc->paint.values[2]);
+ pcc->paint.values[1] = FORCE_UNIT(pcc->paint.values[1]);
+ pcc->paint.values[0] = FORCE_UNIT(pcc->paint.values[0]);
+}
+void
+gx_restrict01_paint_4(gs_client_color * pcc, const gs_color_space * pcs)
+{
+ pcc->paint.values[3] = FORCE_UNIT(pcc->paint.values[3]);
+ gx_restrict01_paint_3(pcc, pcs);
+}
+
+/* Null reference count adjustment procedure. */
+void
+gx_no_adjust_color_count(const gs_client_color * pcc,
+ const gs_color_space * pcs, int delta)
+{
+}
+
+/* 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] = FORCE_UNIT(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;
+ const gs_imager_state *const pis = (const gs_imager_state *)pgs;
+
+ 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]),
+ pis));
+ 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]),
+ pis));
+ default:
+ /*
+ * Might be another convertible color space, but this is rare,
+ * so we don't care about speed or (to some extent) accuracy.
+ */
+ {
+ float rgb[3];
+
+ gs_currentrgbcolor(pgs, rgb);
+ return frac2float(
+ color_rgb_to_gray(
+ float2frac(rgb[0]), float2frac(rgb[1]), float2frac(rgb[2]),
+ pis));
+ }
+ }
+}
+
+/* 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] = FORCE_UNIT(r);
+ pcc->paint.values[1] = FORCE_UNIT(g);
+ pcc->paint.values[2] = FORCE_UNIT(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;
+ const gs_color_space *pcs = pgs->color_space;
+ const gs_color_space *pbcs = pcs;
+ const gs_imager_state *const pis = (const gs_imager_state *)pgs;
+ frac fcc[4];
+ gs_client_color cc;
+
+ sw:switch (pbcs->type->index) {
+ case gs_color_space_index_DeviceGray:
+ pr3[0] = pr3[1] = pr3[2] = pcc->paint.values[0];
+ return 0;
+ case gs_color_space_index_DeviceRGB:
+ pr3[0] = pcc->paint.values[0];
+ pr3[1] = pcc->paint.values[1];
+ pr3[2] = pcc->paint.values[2];
+ return 0;
+ case gs_color_space_index_DeviceCMYK:
+ color_cmyk_to_rgb(
+ float2frac(pcc->paint.values[0]),
+ float2frac(pcc->paint.values[1]),
+ float2frac(pcc->paint.values[2]),
+ float2frac(pcc->paint.values[3]),
+ pis, fcc);
+ pr3[0] = frac2float(fcc[0]);
+ pr3[1] = frac2float(fcc[1]);
+ pr3[2] = frac2float(fcc[2]);
+ return 0;
+ case gs_color_space_index_DeviceN:
+ case gs_color_space_index_Separation:
+ ds:if (cs_concrete_space(pbcs, pis) == pbcs)
+ break; /* not using alternative space */
+ /* (falls through) */
+ case gs_color_space_index_Indexed:
+ pbcs = gs_cspace_base_space(pbcs);
+ switch (pbcs->type->index) {
+ case gs_color_space_index_DeviceN:
+ case gs_color_space_index_Separation:
+ goto ds;
+ default: /* outer switch will catch undefined cases */
+ break;
+ }
+ if (cs_concretize_color(pcc, pcs, fcc, pis) < 0)
+ break;
+ cc.paint.values[0] = frac2float(fcc[0]);
+ cc.paint.values[1] = frac2float(fcc[1]);
+ cc.paint.values[2] = frac2float(fcc[2]);
+ cc.paint.values[3] = frac2float(fcc[3]);
+ pcc = &cc;
+ pcs = pbcs;
+ goto sw;
+ default:
+ break;
+ }
+ pr3[0] = pr3[1] = pr3[2] = 0.0;
+ return 0;
+}
+
+/* setnullcolor */
+int
+gs_setnullcolor(gs_state * pgs)
+{
+ if (pgs->in_cachedevice)
+ return_error(gs_error_undefined);
+ gs_setgray(pgs, 0.0); /* set color space to something harmless */
+ color_set_null(pgs->dev_color);
+ return 0;
+}
+
+/* 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, "gs_settransfer");
+ rc_decrement(ptran->green, "gs_settransfer");
+ rc_decrement(ptran->blue, "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. We export this for gscolor1.c.
+ * Note that we must deal with both old (proc) and new (closure) maps.
+ */
+private float
+transfer_use_proc(floatp value, const gx_transfer_map * pmap,
+ const void *ignore_proc_data)
+{
+ return (*pmap->proc) (value, pmap);
+}
+void
+load_transfer_map(gs_state * pgs, gx_transfer_map * pmap, floatp min_value)
+{
+ gs_mapping_closure_proc_t proc =
+ (pmap->proc == 0 ? pmap->closure.proc : transfer_use_proc);
+ const void *proc_data = pmap->closure.data;
+ 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, proc_data);
+
+ 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..b595a184b
--- /dev/null
+++ b/pstoraster/gscolor.h
@@ -0,0 +1,43 @@
+/* Copyright (C) 1991, 1992, 1993, 1996, 1997 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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]));
+int gs_setnullcolor(P1(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..fa7c32697
--- /dev/null
+++ b/pstoraster/gscolor1.c
@@ -0,0 +1,271 @@
+/* Copyright (C) 1989, 1992, 1993, 1994, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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 *));
+
+/* 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)
+
+/* 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, true, true,
+ &st_base_color_space, gx_num_components_4,
+ gx_no_base_space,
+ gx_init_paint_4, gx_restrict01_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
+};
+
+/* 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] = FORCE_UNIT(c);
+ pcc->paint.values[1] = FORCE_UNIT(m);
+ pcc->paint.values[2] = FORCE_UNIT(y);
+ pcc->paint.values[3] = FORCE_UNIT(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;
+ const gs_color_space *pcs = pgs->color_space;
+ const gs_color_space *pbcs = pcs;
+ const gs_imager_state *const pis = (const gs_imager_state *)pgs;
+ frac fcc[4];
+ gs_client_color cc;
+
+ sw:switch (pbcs->type->index) {
+ case gs_color_space_index_DeviceGray:
+ pr4[0] = pr4[1] = pr4[2] = 0.0;
+ pr4[3] = 1.0 - pcc->paint.values[0];
+ return 0;
+ case gs_color_space_index_DeviceRGB:
+ color_rgb_to_cmyk(float2frac(pcc->paint.values[0]),
+ float2frac(pcc->paint.values[1]),
+ float2frac(pcc->paint.values[2]),
+ pis, fcc);
+ pr4[0] = frac2float(fcc[0]);
+ pr4[1] = frac2float(fcc[1]);
+ pr4[2] = frac2float(fcc[2]);
+ pr4[3] = frac2float(fcc[3]);
+ return 0;
+ 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];
+ return 0;
+ case gs_color_space_index_DeviceN:
+ case gs_color_space_index_Separation:
+ ds:if (cs_concrete_space(pbcs, pis) == pbcs)
+ break; /* not using alternative space */
+ /* (falls through) */
+ case gs_color_space_index_Indexed:
+ pbcs = gs_cspace_base_space(pbcs);
+ switch (pbcs->type->index) {
+ case gs_color_space_index_DeviceN:
+ case gs_color_space_index_Separation:
+ goto ds;
+ default: /* outer switch will catch undefined cases */
+ break;
+ }
+ if (cs_concretize_color(pcc, pcs, fcc, pis) < 0)
+ break;
+ cc.paint.values[0] = frac2float(fcc[0]);
+ cc.paint.values[1] = frac2float(fcc[1]);
+ cc.paint.values[2] = frac2float(fcc[2]);
+ cc.paint.values[3] = frac2float(fcc[3]);
+ pcc = &cc;
+ pcs = pbcs;
+ goto sw;
+ default:
+ break;
+ }
+ 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, "setcolortransfer");
+ fgreen:
+ rc_assign(ptran->red, old.red, "setcolortransfer");
+ fred:
+ rc_assign(ptran->gray, old.gray, "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..9ced3e2d3
--- /dev/null
+++ b/pstoraster/gscolor1.h
@@ -0,0 +1,47 @@
+/* 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 supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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..f00cfceb3
--- /dev/null
+++ b/pstoraster/gscolor2.c
@@ -0,0 +1,455 @@
+/* Copyright (C) 1992, 1994, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Level 2 color operators for Ghostscript library */
+#include "memory_.h"
+#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"
+
+/* ---------------- General colors and color spaces ---------------- */
+
+/* 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, 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, -1);
+ (*cs_old.type->adjust_cspace_count)(&cs_old, -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, -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, 1);
+ (*pcs->type->adjust_color_count)(pgs->ccolor, pcs, -1);
+ *pgs->ccolor = *pcc;
+ (*pcs->type->restrict_color)(pgs->ccolor, pcs);
+ gx_unset_dev_color(pgs);
+ return 0;
+}
+
+/* currentcolor */
+const gs_client_color *
+gs_currentcolor(const gs_state * pgs)
+{
+ return pgs->ccolor;
+}
+
+/* ------ Internal procedures ------ */
+
+/* GC descriptors */
+public_st_indexed_map();
+
+/* Free an indexed map and its values when the reference count goes to 0. */
+void
+free_indexed_map(gs_memory_t * pmem, void *pmap, client_name_t cname)
+{
+ gs_free_object(pmem, ((gs_indexed_map *) pmap)->values, cname);
+ gs_free_object(pmem, pmap, cname);
+}
+
+/*
+ * Allocate an indexed map for an Indexed or Separation color space.
+ */
+int
+alloc_indexed_map(gs_indexed_map ** ppmap, int nvals, gs_memory_t * pmem,
+ client_name_t cname)
+{
+ gs_indexed_map *pimap;
+
+ rc_alloc_struct_1(pimap, gs_indexed_map, &st_indexed_map, pmem,
+ return_error(gs_error_VMerror), cname);
+ pimap->values =
+ (float *)gs_alloc_byte_array(pmem, nvals, sizeof(float), cname);
+
+ if (pimap->values == 0) {
+ gs_free_object(pmem, pimap, cname);
+ return_error(gs_error_VMerror);
+ }
+ pimap->rc.free = free_indexed_map;
+ pimap->num_values = nvals;
+ *ppmap = pimap;
+ return 0;
+}
+
+/* ---------------- Indexed color spaces ---------------- */
+
+gs_private_st_composite(st_color_space_Indexed, gs_paint_color_space,
+ "gs_color_space_Indexed", cs_Indexed_enum_ptrs, cs_Indexed_reloc_ptrs);
+
+/* ------ Color space ------ */
+
+/* Define the Indexed color space type. */
+private cs_proc_base_space(gx_base_space_Indexed);
+private cs_proc_restrict_color(gx_restrict_Indexed);
+private cs_proc_concrete_space(gx_concrete_space_Indexed);
+private cs_proc_concretize_color(gx_concretize_Indexed);
+private cs_proc_install_cspace(gx_install_Indexed);
+private cs_proc_adjust_cspace_count(gx_adjust_cspace_Indexed);
+const gs_color_space_type gs_color_space_type_Indexed = {
+ gs_color_space_index_Indexed, false, false,
+ &st_color_space_Indexed, gx_num_components_1,
+ gx_base_space_Indexed,
+ gx_init_paint_1, gx_restrict_Indexed,
+ gx_concrete_space_Indexed,
+ gx_concretize_Indexed, NULL,
+ gx_default_remap_color, gx_install_Indexed,
+ gx_adjust_cspace_Indexed, gx_no_adjust_color_count
+};
+
+/* GC procedures. */
+
+#define pcs ((gs_color_space *)vptr)
+
+private
+ENUM_PTRS_BEGIN(cs_Indexed_enum_ptrs)
+{
+ return ENUM_USING(*pcs->params.indexed.base_space.type->stype,
+ &pcs->params.indexed.base_space,
+ sizeof(pcs->params.indexed.base_space), index - 1);
+}
+case 0:
+if (pcs->params.indexed.use_proc)
+ ENUM_RETURN((void *)pcs->params.indexed.lookup.map);
+else {
+ pcs->params.indexed.lookup.table.size =
+ (pcs->params.indexed.hival + 1) *
+ cs_num_components((const gs_color_space *)
+ &pcs->params.indexed.base_space);
+ ENUM_RETURN_CONST_STRING_PTR(gs_color_space,
+ params.indexed.lookup.table);
+}
+ENUM_PTRS_END
+private RELOC_PTRS_BEGIN(cs_Indexed_reloc_ptrs)
+{
+ RELOC_USING(*pcs->params.indexed.base_space.type->stype,
+ &pcs->params.indexed.base_space,
+ sizeof(gs_base_color_space));
+ 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
+
+/* Return the base space of an Indexed color space. */
+private const gs_color_space *
+gx_base_space_Indexed(const gs_color_space * pcs)
+{
+ return (const gs_color_space *)&(pcs->params.indexed.base_space);
+}
+
+/* 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, int delta)
+{
+ if (pcs->params.indexed.use_proc) {
+ rc_adjust_const(pcs->params.indexed.lookup.map, delta,
+ "gx_adjust_Indexed");
+ }
+ (*pcs->params.indexed.base_space.type->adjust_cspace_count)
+ ((const gs_color_space *)&pcs->params.indexed.base_space, delta);
+}
+
+/*
+ * Default palette mapping functions for indexed color maps. These just
+ * return the values already in the palette.
+ *
+ * For performance reasons, we provide four functions: special cases for 1,
+ * 3, and 4 entry palettes, and a general case. Note that these procedures
+ * do not range-check their input values.
+ */
+private int
+map_palette_entry_1(const gs_indexed_params * params, int indx, float *values)
+{
+ values[0] = params->lookup.map->values[indx];
+ return 0;
+}
+
+private int
+map_palette_entry_3(const gs_indexed_params * params, int indx, float *values)
+{
+ const float *pv = &(params->lookup.map->values[3 * indx]);
+
+ values[0] = pv[0];
+ values[1] = pv[1];
+ values[2] = pv[2];
+ return 0;
+}
+
+private int
+map_palette_entry_4(const gs_indexed_params * params, int indx, float *values)
+{
+ const float *pv = &(params->lookup.map->values[4 * indx]);
+
+ values[0] = pv[0];
+ values[1] = pv[1];
+ values[2] = pv[2];
+ values[3] = pv[3];
+ return 0;
+}
+
+private int
+map_palette_entry_n(const gs_indexed_params * params, int indx, float *values)
+{
+ int m = cs_num_components((const gs_color_space *)&params->base_space);
+
+ memcpy((void *)values,
+ (const void *)(params->lookup.map->values + indx * m),
+ m * sizeof(float)
+ );
+
+ return 0;
+}
+
+/*
+ * Allocate an indexed map to be used as a palette for indexed color space.
+ */
+private gs_indexed_map *
+alloc_indexed_palette(
+ const gs_color_space * pbase_cspace,
+ int nvals,
+ gs_memory_t * pmem
+)
+{
+ int num_comps = gs_color_space_num_components(pbase_cspace);
+ gs_indexed_map *pimap;
+ int code =
+ alloc_indexed_map(&pimap, nvals * num_comps, pmem,
+ "alloc_indexed_palette");
+
+ if (code < 0)
+ return 0;
+ if (num_comps == 1)
+ pimap->proc.lookup_index = map_palette_entry_1;
+ else if (num_comps == 3)
+ pimap->proc.lookup_index = map_palette_entry_3;
+ else if (num_comps == 4)
+ pimap->proc.lookup_index = map_palette_entry_4;
+ else
+ pimap->proc.lookup_index = map_palette_entry_n;
+ return pimap;
+}
+
+/*
+ * Build an indexed color space.
+ */
+int
+gs_cspace_build_Indexed(
+ gs_color_space ** ppcspace,
+ const gs_color_space * pbase_cspace,
+ uint num_entries,
+ const gs_const_string * ptbl,
+ gs_memory_t * pmem
+)
+{
+ gs_color_space *pcspace = 0;
+ gs_indexed_params *pindexed = 0;
+ int code;
+
+ if ((pbase_cspace == 0) || !pbase_cspace->type->can_be_base_space)
+ return_error(gs_error_rangecheck);
+
+ code = gs_cspace_alloc(&pcspace, &gs_color_space_type_Indexed, pmem);
+ if (code < 0)
+ return code;
+ pindexed = &(pcspace->params.indexed);
+ if (ptbl == 0) {
+ pindexed->lookup.map =
+ alloc_indexed_palette(pbase_cspace, num_entries, pmem);
+ if (pindexed->lookup.map == 0) {
+ gs_free_object(pmem, pcspace, "gs_cspace_build_Indexed");
+ return_error(gs_error_VMerror);
+ }
+ pindexed->use_proc = true;
+ } else {
+ pindexed->lookup.table = *ptbl;
+ pindexed->use_proc = false;
+ }
+ gs_cspace_init_from((gs_color_space *) & (pindexed->base_space),
+ pbase_cspace);
+ pindexed->hival = num_entries - 1;
+ *ppcspace = pcspace;
+ return 0;
+}
+
+/*
+ * Return the number of entries in an indexed color space.
+ */
+int
+gs_cspace_indexed_num_entries(const gs_color_space * pcspace)
+{
+ if (gs_color_space_get_index(pcspace) != gs_color_space_index_Indexed)
+ return 0;
+ return pcspace->params.indexed.hival + 1;
+}
+
+/*
+ * Get the palette for an indexed color space. This will return a null
+ * pointer if the color space is not an indexed color space or if the
+ * color space does not use the mapped index palette.
+ */
+float *
+gs_cspace_indexed_value_array(const gs_color_space * pcspace)
+{
+ if ((gs_color_space_get_index(pcspace) != gs_color_space_index_Indexed) ||
+ pcspace->params.indexed.use_proc
+ )
+ return 0;
+ return pcspace->params.indexed.lookup.map->values;
+}
+
+/*
+ * Set the lookup procedure to be used with an indexed color space.
+ */
+int
+gs_cspace_indexed_set_proc(
+ gs_color_space * pcspace,
+ int (*proc) (P3(const gs_indexed_params *, int, float *))
+)
+{
+ if ((gs_color_space_get_index(pcspace) != gs_color_space_index_Indexed) ||
+ !pcspace->params.indexed.use_proc
+ )
+ return_error(gs_error_rangecheck);
+ pcspace->params.indexed.lookup.map->proc.lookup_index = proc;
+ return 0;
+}
+
+/* ------ Colors ------ */
+
+/* Force an Indexed color into legal range. */
+
+private void
+gx_restrict_Indexed(gs_client_color * pcc, const gs_color_space * pcs)
+{
+ float value = pcc->paint.values[0];
+
+ pcc->paint.values[0] =
+ (is_fneg(value) ? 0 :
+ value >= pcs->params.indexed.hival ? pcs->params.indexed.hival :
+ value);
+}
+
+/* Color remapping for Indexed color spaces. */
+
+private const gs_color_space *
+gx_concrete_space_Indexed(const gs_color_space * pcs,
+ const gs_imager_state * pis)
+{
+ const gs_color_space *pbcs =
+ (const gs_color_space *)&pcs->params.indexed.base_space;
+
+ return cs_concrete_space(pbcs, pis);
+}
+
+private int
+gx_concretize_Indexed(const gs_client_color * pc, const gs_color_space * pcs,
+ frac * pconc, const gs_imager_state * pis)
+{
+ 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 = cs_num_components((const gs_color_space *)
+ &pcs->params.indexed.base_space);
+ 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, pis);
+}
diff --git a/pstoraster/gscolor2.h b/pstoraster/gscolor2.h
new file mode 100644
index 000000000..3ae2aef14
--- /dev/null
+++ b/pstoraster/gscolor2.h
@@ -0,0 +1,106 @@
+/* Copyright (C) 1992, 1993, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Client interface to Level 2 color facilities */
+/* (requires gscspace.h, gsmatrix.h) */
+
+#ifndef gscolor2_INCLUDED
+# define gscolor2_INCLUDED
+
+#include "gsptype1.h"
+
+/* ---------------- Graphics state ---------------- */
+
+/*
+ * Note that setcolorspace and setcolor copy the (top level of) their
+ * structure argument, so if the client allocated it on the heap, the
+ * client should free it after setting it in the graphics state.
+ */
+
+/* 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 *));
+
+/* 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 *));
+
+/* ---------------- Indexed color space ---------------- */
+
+/*
+ * Indexed color spaces.
+ *
+ * If the color space will use a procedure rather than a byte table,
+ * ptbl should be set to 0.
+ *
+ * Unlike most of the other color space constructors, this one initializes
+ * some of the fields of the colorspace. In the case in which a string table
+ * is used for mapping, it initializes the entire structure. Note that the
+ * client is responsible for the table memory in that case; the color space
+ * will not free it when the color space itself is released.
+ *
+ * For the case of an indexed color space based on a procedure, a default
+ * procedure will be provided that simply echoes the color values already in
+ * the palette; the client may override these procedures by use of
+ * gs_cspace_indexed_set_proc. If the client wishes to insert values into
+ * the palette, it should do so by using gs_cspace_indexed_value_array, and
+ * directly inserting the desired values into the array.
+ *
+ * If the client does insert values into the palette directly, the default
+ * procedures provided by the client are fairly efficient, and there are
+ * few instances in which the client would need to replace them.
+ */
+extern int gs_cspace_build_Indexed(
+ gs_color_space ** ppcspace,
+ const gs_color_space * pbase_cspace,
+ uint num_entries,
+ const gs_const_string * ptbl,
+ gs_memory_t * pmem
+);
+
+/* Return the number of entries in the palette of an indexed color space. */
+extern int gs_cspace_indexed_num_entries(P1(
+ const gs_color_space * pcspace
+ ));
+
+/* In the case of a procedure-based indexed color space, get a pointer to */
+/* the array of cached values. */
+extern float *gs_cspace_indexed_value_array(P1(
+ const gs_color_space * pcspace
+ ));
+
+/* Set the lookup procedure to be used for an Indexed color space. */
+extern int gs_cspace_indexed_set_proc(P2(
+ gs_color_space * pcspace,
+ int (*proc) (P3(const gs_indexed_params *, int, float *))
+ ));
+
+#endif /* gscolor2_INCLUDED */
diff --git a/pstoraster/gscolor3.c b/pstoraster/gscolor3.c
new file mode 100644
index 000000000..980d21d4c
--- /dev/null
+++ b/pstoraster/gscolor3.c
@@ -0,0 +1,70 @@
+/* Copyright (C) 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* "Operators" for LanguageLevel 3 color facilities */
+#include "gx.h"
+#include "gserrors.h"
+#include "gscspace.h" /* for gscolor2.h */
+#include "gsmatrix.h" /* for gscolor2.h */
+#include "gscolor2.h"
+#include "gscolor3.h"
+#include "gspath.h"
+#include "gzstate.h"
+#include "gxshade.h"
+
+/* setsmoothness */
+int
+gs_setsmoothness(gs_state * pgs, floatp smoothness)
+{
+ pgs->smoothness =
+ (smoothness < 0 ? 0 : smoothness > 1 ? 1 : smoothness);
+ return 0;
+}
+
+/* currentsmoothness */
+float
+gs_currentsmoothness(const gs_state * pgs)
+{
+ return pgs->smoothness;
+}
+
+/* shfill */
+int
+gs_shfill(gs_state * pgs, const gs_shading_t * psh)
+{
+ int code = gs_gsave(pgs);
+
+ if (code < 0)
+ return code;
+ if ((code = gs_setcolorspace(pgs, psh->params.ColorSpace)) < 0 ||
+ (code = gs_clippath(pgs)) < 0 ||
+ (code = gs_shading_fill_path(psh, pgs->path,
+ gs_currentdevice(pgs),
+ (gs_imager_state *)pgs)) < 0
+ )
+ DO_NOTHING;
+ gs_grestore(pgs);
+ return code;
+}
diff --git a/pstoraster/gscolor3.h b/pstoraster/gscolor3.h
new file mode 100644
index 000000000..a166ace5a
--- /dev/null
+++ b/pstoraster/gscolor3.h
@@ -0,0 +1,41 @@
+/* Copyright (C) 1997 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Client interface to LanguageLevel 3 color facilities */
+
+#ifndef gscolor3_INCLUDED
+# define gscolor3_INCLUDED
+
+/* Smooth shading */
+#ifndef gs_shading_t_DEFINED
+# define gs_shading_t_DEFINED
+typedef struct gs_shading_s gs_shading_t;
+#endif
+
+int gs_setsmoothness(P2(gs_state *, floatp));
+float gs_currentsmoothness(P1(const gs_state *));
+int gs_shfill(P2(gs_state *, const gs_shading_t *));
+
+#endif /* gscolor3_INCLUDED */
diff --git a/pstoraster/gscompt.h b/pstoraster/gscompt.h
new file mode 100644
index 000000000..afaa3340a
--- /dev/null
+++ b/pstoraster/gscompt.h
@@ -0,0 +1,59 @@
+/* Copyright (C) 1997 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Abstract types for compositing objects */
+
+#ifndef gscompt_INCLUDED
+# define gscompt_INCLUDED
+
+/*
+ * Compositing is the next-to-last step in the rendering pipeline.
+ * It occurs after color correction but before halftoning (if needed).
+ *
+ * gs_composite_t is the abstract superclass for compositing functions such
+ * as RasterOp functions or alpha-based compositing. Concrete subclasses
+ * must provide a default implementation (presumably based on
+ * get_bits_rectangle and copy_color) for devices that provide no optimized
+ * implementation of their own.
+ *
+ * A client that wants to produce composited output asks the target device
+ * to create an appropriate compositing device based on the target device
+ * and the gs_composite_t (and possibly other elements of the imager state).
+ * If the target device doesn't have its own implementation for the
+ * requested function, format, and state, it passes the buck to the
+ * gs_composite_t, which may make further tests for special cases before
+ * creating and returning a compositing device that uses the default
+ * implementation.
+ */
+typedef struct gs_composite_s gs_composite_t;
+
+/*
+ * To enable fast cache lookup and equality testing, compositing functions,
+ * like halftones, black generation functions, etc., carry a unique ID (time
+ * stamp).
+ */
+gs_id gs_composite_id(P1(const gs_composite_t * pcte));
+
+#endif /* gscompt_INCLUDED */
diff --git a/pstoraster/gscoord.c b/pstoraster/gscoord.c
new file mode 100644
index 000000000..2ff98f9c2
--- /dev/null
+++ b/pstoraster/gscoord.c
@@ -0,0 +1,507 @@
+/* Copyright (C) 1989, 1995, 1996, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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 trace_matrix_fixed(P1(const gs_matrix_fixed *));
+private void 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') )\
+ dlprintf("[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'))
+ dlprintf("[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'))
+ dlprintf("[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'))
+ dlprintf("[x]setmatrix:\n"), trace_ctm(pgs);
+#endif
+ return 0;
+}
+
+int
+gs_imager_setmatrix(gs_imager_state * pis, const gs_matrix * pmat)
+{
+ update_matrix_fixed(pis->ctm, pmat->tx, pmat->ty);
+ set_ctm_only(pis, *pmat);
+#ifdef DEBUG
+ if (gs_debug_c('x'))
+ dlprintf("[x]imager_setmatrix:\n"), trace_ctm(pis);
+#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'))
+ dlprintf4("[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'))
+ dlprintf2("[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'))
+ dlprintf1("[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'))
+ dlprintf("[x]concat:\n"), trace_matrix(pmat), trace_ctm(pgs);
+#endif
+ return code;
+}
+
+/* ------ Coordinate transformation ------ */
+
+#define is_skewed(pmat) (!(is_xxyy(pmat) || is_xyyx(pmat)))
+
+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')) {
+ dlprintf2("[x]translate_to_fixed %g, %g:\n",
+ fixed2float(px), fixed2float(py));
+ trace_ctm(pgs);
+ dlprintf("[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')) {
+ dlprintf6("[x]ctm: [%6g %6g %6g %6g %6g %6g]\n",
+ ctm.xx, ctm.xy, ctm.yx, ctm.yy, ctm.tx, ctm.ty);
+ dlprintf6(" 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
+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
+trace_matrix(register const gs_matrix * pmat)
+{
+ dlprintf6("\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..9933182cd
--- /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 supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Requires gsmatrix.h and gsstate.h */
+
+#ifndef gscoord_INCLUDED
+# define gscoord_INCLUDED
+
+/* 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_setmatrix(P2(gs_imager_state *, const gs_matrix *));
+int gs_imager_idtransform(P4(const gs_imager_state *, floatp, floatp,
+ gs_point *));
+
+#endif /* gscoord_INCLUDED */
diff --git a/pstoraster/gscparam.c b/pstoraster/gscparam.c
new file mode 100644
index 000000000..ba951411a
--- /dev/null
+++ b/pstoraster/gscparam.c
@@ -0,0 +1,480 @@
+/* Copyright (C) 1995, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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 {
+ GS_PARAM_VALUE_UNION(gs_c_param_list);
+} gs_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();
+
+/* Lengths corresponding to various gs_param_type_xxx types */
+const byte gs_param_type_sizes[] = {
+ GS_PARAM_TYPE_SIZES(sizeof(gs_c_param_list))
+};
+
+/* Lengths of *actual* data-containing type pointed to or contained by gs_param_type_xxx's */
+const byte gs_param_type_base_sizes[] = {
+ GS_PARAM_TYPE_BASE_SIZES(0)
+};
+
+/*
+ * Define a parameter list element. We use gs_param_type_any to identify
+ * elements that have been requested but not yet written. The reading
+ * procedures must recognize such elements as undefined, and ignore them.
+ */
+struct gs_c_param_s {
+ gs_c_param *next;
+ gs_param_name key;
+ gs_c_param_value value;
+ gs_param_type type;
+ void *alternate_typed_data;
+};
+
+/* Parameter values aren't really simple, */
+/* but since parameter lists are transient, it doesn't matter. */
+gs_private_st_ptrs2(st_c_param, gs_c_param, "gs_c_param",
+ c_param_enum_ptrs, c_param_reloc_ptrs, next, alternate_typed_data);
+
+/* ---------------- Utilities ---------------- */
+
+private gs_c_param *
+c_param_find(const gs_c_param_list * plist, gs_param_name pkey, bool any)
+{
+ gs_c_param *pparam = plist->head;
+
+ for (; pparam != 0; pparam = pparam->next)
+ if (!strcmp(pparam->key, pkey))
+ return (pparam->type != gs_param_type_any || any ? pparam : 0);
+ return 0;
+}
+
+/* ---------------- Writing parameters to a list ---------------- */
+
+private param_proc_begin_xmit_collection(c_param_begin_write_collection);
+private param_proc_end_xmit_collection(c_param_end_write_collection);
+private param_proc_xmit_typed(c_param_write_typed);
+private param_proc_request(c_param_request);
+private param_proc_requested(c_param_requested);
+private const gs_param_list_procs c_write_procs =
+{
+ c_param_write_typed,
+ c_param_begin_write_collection,
+ c_param_end_write_collection,
+ NULL, /* get_next_key */
+ c_param_request,
+ 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;
+ plist->any_requested = false;
+ plist->coll_type = gs_param_collection_dict_any;
+}
+
+/* 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;
+
+ switch (pparam->type) {
+ case gs_param_type_dict:
+ case gs_param_type_dict_int_keys:
+ case gs_param_type_array:
+ gs_c_param_list_release(&pparam->value.d);
+ break;
+ case gs_param_type_string:
+ case gs_param_type_name:
+ case gs_param_type_int_array:
+ case gs_param_type_float_array:
+ case gs_param_type_string_array:
+ case gs_param_type_name_array:
+ if (!pparam->value.s.persistent)
+ gs_free_object(plist->memory, (void *)pparam->value.s.data,
+ "gs_c_param_list_release data");
+ break;
+ default:
+ break;
+ }
+ gs_free_object(plist->memory, pparam->alternate_typed_data,
+ "gs_c_param_list_release alternate data");
+ gs_free_object(plist->memory, pparam,
+ "gs_c_param_list_release entry");
+ plist->head = next;
+ plist->count--;
+ }
+}
+
+/* Add an entry to a list. Doesn't set: value, type, plist->head. */
+private gs_c_param *
+c_param_add(gs_c_param_list * plist, gs_param_name pkey)
+{
+ gs_c_param *pparam =
+ gs_alloc_struct(plist->memory, gs_c_param, &st_c_param,
+ "c_param_write entry");
+
+ if (pparam == 0)
+ return 0;
+ pparam->next = plist->head;
+ pparam->key = pkey;
+ pparam->alternate_typed_data = 0;
+ return pparam;
+}
+
+/* Write a dynamically typed parameter to a list. */
+private int
+c_param_write(gs_c_param_list * plist, gs_param_name pkey, void *pvalue,
+ gs_param_type type)
+{
+ unsigned top_level_sizeof = 0;
+ unsigned second_level_sizeof = 0;
+ gs_c_param *pparam = c_param_add(plist, pkey);
+
+ if (pparam == 0)
+ return_error(gs_error_VMerror);
+ memcpy(&pparam->value, pvalue, gs_param_type_sizes[(int)type]);
+ pparam->type = type;
+
+ /* Need deeper copies of data if it's not persistent */
+ switch (type) {
+ gs_param_string const *curr_string;
+ gs_param_string const *end_string;
+
+ case gs_param_type_string_array:
+ case gs_param_type_name_array:
+ /* Determine how much mem needed to hold actual string data */
+ curr_string = pparam->value.sa.data;
+ end_string = curr_string + pparam->value.sa.size;
+ for (; curr_string < end_string; ++curr_string)
+ if (!curr_string->persistent)
+ second_level_sizeof += curr_string->size;
+ /* fall thru */
+
+ case gs_param_type_string:
+ case gs_param_type_name:
+ case gs_param_type_int_array:
+ case gs_param_type_float_array:
+ if (!pparam->value.s.persistent) { /* Allocate & copy object pointed to by array or string */
+ byte *top_level_memory;
+
+ top_level_sizeof =
+ pparam->value.s.size * gs_param_type_base_sizes[type];
+ top_level_memory =
+ gs_alloc_bytes_immovable(plist->memory,
+ top_level_sizeof + second_level_sizeof,
+ "c_param_write data");
+ if (top_level_memory == 0) {
+ gs_free_object(plist->memory, pparam, "c_param_write entry");
+ return_error(gs_error_VMerror);
+ }
+ memcpy(top_level_memory, pparam->value.s.data, top_level_sizeof);
+ pparam->value.s.data = top_level_memory;
+
+ /* String/name arrays need to copy actual str data */
+
+ if (second_level_sizeof > 0) {
+ byte *second_level_memory =
+ top_level_memory + top_level_sizeof;
+
+ curr_string = pparam->value.sa.data;
+ end_string = curr_string + pparam->value.sa.size;
+ for (; curr_string < end_string; ++curr_string)
+ if (!curr_string->persistent) {
+ memcpy(second_level_memory,
+ curr_string->data, curr_string->size);
+ ((gs_param_string *) curr_string)->data
+ = second_level_memory;
+ second_level_memory += curr_string->size;
+ }
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+ plist->head = pparam;
+ plist->count++;
+ return 0;
+}
+
+/* Individual writing routines. */
+private int
+c_param_begin_write_collection(gs_param_list * plist, gs_param_name pkey,
+ gs_param_dict * pvalue, gs_param_collection_type_t coll_type)
+{
+ gs_c_param_list *const cplist = (gs_c_param_list *)plist;
+ gs_c_param_list *dlist =
+ gs_alloc_struct(cplist->memory, gs_c_param_list, &st_c_param_list,
+ "c_param_begin_write_collection");
+
+ if (dlist == 0)
+ return_error(gs_error_VMerror);
+ gs_c_param_list_write(dlist, cplist->memory);
+ dlist->coll_type = coll_type;
+ pvalue->list = (gs_param_list *) dlist;
+ return 0;
+}
+private int
+c_param_end_write_collection(gs_param_list * plist, gs_param_name pkey,
+ gs_param_dict * pvalue)
+{
+ gs_c_param_list *const cplist = (gs_c_param_list *)plist;
+ gs_c_param_list *dlist = (gs_c_param_list *) pvalue->list;
+
+ return c_param_write(cplist, pkey, pvalue->list,
+ (dlist->coll_type == gs_param_collection_dict_int_keys ?
+ gs_param_type_dict_int_keys :
+ dlist->coll_type == gs_param_collection_array ?
+ gs_param_type_array : gs_param_type_dict));
+}
+private int
+c_param_write_typed(gs_param_list * plist, gs_param_name pkey,
+ gs_param_typed_value * pvalue)
+{
+ gs_c_param_list *const cplist = (gs_c_param_list *)plist;
+ gs_param_collection_type_t coll_type;
+
+ switch (pvalue->type) {
+ case gs_param_type_dict:
+ coll_type = gs_param_collection_dict_any;
+ break;
+ case gs_param_type_dict_int_keys:
+ coll_type = gs_param_collection_dict_int_keys;
+ break;
+ case gs_param_type_array:
+ coll_type = gs_param_collection_array;
+ break;
+ default:
+ return c_param_write(cplist, pkey, &pvalue->value, pvalue->type);
+ }
+ return c_param_begin_write_collection
+ (plist, pkey, &pvalue->value.d, coll_type);
+}
+
+/* Other procedures */
+
+private int
+c_param_request(gs_param_list * plist, gs_param_name pkey)
+{
+ gs_c_param_list *const cplist = (gs_c_param_list *)plist;
+ gs_c_param *pparam;
+
+ cplist->any_requested = true;
+ if (c_param_find(cplist, pkey, true))
+ return 0;
+ pparam = c_param_add(cplist, pkey);
+ if (pparam == 0)
+ return_error(gs_error_VMerror);
+ pparam->type = gs_param_type_any; /* mark as undefined */
+ cplist->head = pparam;
+ return 0;
+}
+
+private int
+c_param_requested(const gs_param_list * plist, gs_param_name pkey)
+{
+ const gs_c_param_list *const cplist = (const gs_c_param_list *)plist;
+
+ return (!cplist->any_requested ? -1 :
+ c_param_find(cplist, pkey, true) != 0);
+}
+
+/* ---------------- Reading from a list to parameters ---------------- */
+
+private param_proc_begin_xmit_collection(c_param_begin_read_collection);
+private param_proc_end_xmit_collection(c_param_end_read_collection);
+private param_proc_xmit_typed(c_param_read_typed);
+private param_proc_next_key(c_param_get_next_key);
+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_typed,
+ c_param_begin_read_collection,
+ c_param_end_read_collection,
+ c_param_get_next_key,
+ NULL, /* request, N/A */
+ NULL, /* requested, N/A */
+ 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 int
+c_param_read_typed(gs_param_list * plist, gs_param_name pkey,
+ gs_param_typed_value * pvalue)
+{
+ gs_c_param_list *const cplist = (gs_c_param_list *)plist;
+ gs_param_type req_type = pvalue->type;
+ gs_c_param *pparam = c_param_find(cplist, pkey, false);
+ int code;
+
+ if (pparam == 0)
+ return 1;
+ pvalue->type = pparam->type;
+ switch (pvalue->type) {
+ case gs_param_type_dict:
+ case gs_param_type_dict_int_keys:
+ case gs_param_type_array:
+ gs_c_param_list_read(&pparam->value.d);
+ pvalue->value.d.list = (gs_param_list *) & pparam->value.d;
+ pvalue->value.d.size = pparam->value.d.count;
+ return 0;
+ default:
+ break;
+ }
+ memcpy(&pvalue->value, &pparam->value,
+ gs_param_type_sizes[(int)pparam->type]);
+ code = param_coerce_typed(pvalue, req_type, NULL);
+/****** SHOULD LET param_coerce_typed DO THIS ******/
+ if (code == gs_error_typecheck &&
+ req_type == gs_param_type_float_array &&
+ pvalue->type == gs_param_type_int_array
+ ) {
+ /* Convert int array to float dest */
+ gs_param_float_array fa;
+ int element;
+
+ fa.size = pparam->value.ia.size;
+ fa.persistent = false;
+
+ if (pparam->alternate_typed_data == 0) {
+ if ((pparam->alternate_typed_data
+ = (void *)gs_alloc_bytes_immovable(cplist->memory,
+ fa.size * sizeof(float),
+ "gs_c_param_read alternate float array")) == 0)
+ return_error(gs_error_VMerror);
+
+ for (element = 0; element < fa.size; ++element)
+ ((float *)(pparam->alternate_typed_data))[element]
+ = (float)pparam->value.ia.data[element];
+ }
+ fa.data = (float *)pparam->alternate_typed_data;
+
+ pvalue->value.fa = fa;
+ return 0;
+ }
+ return code;
+}
+
+/* Individual reading routines. */
+private int
+c_param_begin_read_collection(gs_param_list * plist, gs_param_name pkey,
+ gs_param_dict * pvalue, gs_param_collection_type_t coll_type)
+{
+ gs_c_param_list *const cplist = (gs_c_param_list *)plist;
+ gs_c_param *pparam = c_param_find(cplist, pkey, false);
+
+ if (pparam == 0)
+ return 1;
+ switch (pparam->type) {
+ case gs_param_type_dict:
+ if (coll_type != gs_param_collection_dict_any)
+ return_error(gs_error_typecheck);
+ break;
+ case gs_param_type_dict_int_keys:
+ if (coll_type == gs_param_collection_array)
+ return_error(gs_error_typecheck);
+ break;
+ case gs_param_type_array:
+ break;
+ default:
+ 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_collection(gs_param_list * plist, gs_param_name pkey,
+ gs_param_dict * pvalue)
+{
+ return 0;
+}
+
+/* Other procedures */
+private int /* ret 0 ok, 1 if EOF, or -ve err */
+c_param_get_next_key(gs_param_list * plist, gs_param_enumerator_t * penum,
+ gs_param_key_t * key)
+{
+ gs_c_param_list *const cplist = (gs_c_param_list *)plist;
+ gs_c_param *pparam =
+ (penum->pvoid ? ((gs_c_param *) (penum->pvoid))->next :
+ cplist->head);
+
+ if (pparam == 0)
+ return 1;
+ penum->pvoid = pparam;
+ key->data = (const byte *)pparam->key; /* was const char * */
+ key->size = strlen(pparam->key);
+ return 0;
+}
+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/gscpixel.c b/pstoraster/gscpixel.c
new file mode 100644
index 000000000..82212d2db
--- /dev/null
+++ b/pstoraster/gscpixel.c
@@ -0,0 +1,89 @@
+/* Copyright (C) 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* DevicePixel color space and operation definition */
+#include "gx.h"
+#include "gserrors.h"
+#include "gsrefct.h"
+#include "gxcspace.h"
+#include "gscpixel.h"
+#include "gxdevice.h"
+
+/* Define the DevicePixel color space type. */
+private cs_proc_restrict_color(gx_restrict_DevicePixel);
+private cs_proc_remap_concrete_color(gx_remap_concrete_DevicePixel);
+private cs_proc_concretize_color(gx_concretize_DevicePixel);
+private const gs_color_space_type gs_color_space_type_DevicePixel = {
+ gs_color_space_index_DevicePixel, true, false,
+ &st_base_color_space, gx_num_components_1,
+ gx_no_base_space,
+ gx_init_paint_1, gx_restrict_DevicePixel,
+ gx_same_concrete_space,
+ gx_concretize_DevicePixel, gx_remap_concrete_DevicePixel,
+ gx_default_remap_color, gx_no_install_cspace,
+ gx_no_adjust_cspace_count, gx_no_adjust_color_count
+};
+
+/* Create a DevicePixel color space. */
+void
+gs_cs_init_DevicePixel(gs_color_space * pcs, int depth)
+{
+ pcs->type = &gs_color_space_type_DevicePixel;
+ pcs->params.pixel.depth = depth;
+}
+
+/* ------ Internal routines ------ */
+
+/* Force a DevicePixel color into legal range. */
+private void
+gx_restrict_DevicePixel(gs_client_color * pcc, const gs_color_space * pcs)
+{
+ /****** NOT ENOUGH BITS IN float OR frac ******/
+ floatp pixel = pcc->paint.values[0];
+ ulong max_value = (1L << pcs->params.pixel.depth) - 1;
+
+ pcc->paint.values[0] = (pixel < 0 ? 0 : min(pixel, max_value));
+}
+
+
+/* Remap a DevicePixel color. */
+
+private int
+gx_concretize_DevicePixel(const gs_client_color * pc, const gs_color_space * pcs,
+ frac * pconc, const gs_imager_state * pis)
+{
+ /****** NOT ENOUGH BITS IN float OR frac ******/
+ pconc[0] = (frac) (ulong) pc->paint.values[0];
+ return 0;
+}
+
+private int
+gx_remap_concrete_DevicePixel(const frac * pconc,
+ gx_device_color * pdc, const gs_imager_state * pis, gx_device * dev,
+ gs_color_select_t select)
+{
+ color_set_pure(pdc, pconc[0] & ((1 << dev->color_info.depth) - 1));
+ return 0;
+}
diff --git a/pstoraster/gscpixel.h b/pstoraster/gscpixel.h
new file mode 100644
index 000000000..da6cebcd8
--- /dev/null
+++ b/pstoraster/gscpixel.h
@@ -0,0 +1,34 @@
+/* Copyright (C) 1997 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Requires gscspace.h */
+
+#ifndef gscpixel_INCLUDED
+# define gscpixel_INCLUDED
+
+/* Create a DevicePixel color space. */
+void gs_cs_init_DevicePixel(P2(gs_color_space * pcs, int depth));
+
+#endif /* gscpixel_INCLUDED */
diff --git a/pstoraster/gscpm.h b/pstoraster/gscpm.h
new file mode 100644
index 000000000..313ca6a51
--- /dev/null
+++ b/pstoraster/gscpm.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 supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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/gscrd.c b/pstoraster/gscrd.c
new file mode 100644
index 000000000..70f3517a2
--- /dev/null
+++ b/pstoraster/gscrd.c
@@ -0,0 +1,350 @@
+/* Copyright (C) 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* CIE color rendering dictionary creation */
+#include "math_.h"
+#include "memory_.h"
+#include "string_.h"
+#include "gx.h"
+#include "gscdefs.h" /* for gs_lib_device_list */
+#include "gsdevice.h"
+#include "gserrors.h"
+#include "gsmatrix.h" /* for gscolor2.h */
+#include "gsparam.h"
+#include "gxcspace.h"
+#include "gscolor2.h" /* for gs_set/currentcolorrendering */
+#include "gscrd.h"
+
+/* Import gs_lib_device_list() */
+extern_gs_lib_device_list();
+
+/* Allocator structure type */
+public_st_cie_render1();
+#define pcrd ((gs_cie_render *)vptr)
+private
+ENUM_PTRS_BEGIN(cie_render1_enum_ptrs) return 0;
+
+case 0:
+return ENUM_OBJ(pcrd->client_data);
+case 1:
+return ENUM_OBJ(pcrd->RenderTable.lookup.table);
+case 2:
+return (pcrd->RenderTable.lookup.table ?
+ ENUM_CONST_STRING(&pcrd->TransformPQR.proc_data) :
+ 0);
+ENUM_PTRS_END
+private RELOC_PTRS_BEGIN(cie_render1_reloc_ptrs);
+RELOC_OBJ_VAR(pcrd->client_data);
+if (pcrd->RenderTable.lookup.table)
+{
+RELOC_OBJ_VAR(pcrd->RenderTable.lookup.table);
+RELOC_CONST_STRING_VAR(pcrd->TransformPQR.proc_data);
+}
+RELOC_PTRS_END
+#undef pcrd
+
+/* Default CRD procedures. */
+
+private int
+tpqr_identity(int index, floatp in, const gs_cie_wbsd * pwbsd,
+ gs_cie_render * pcrd, float *out)
+{
+ *out = in;
+ return 0;
+}
+
+private float
+render_identity(floatp in, const gs_cie_render * pcrd)
+{
+ return in;
+}
+private frac
+render_table_identity(byte in, const gs_cie_render * pcrd)
+{
+ return byte2frac(in);
+}
+
+/* Transformation procedures that just consult the cache. */
+
+#define clamp_index(index)\
+ index = (index < 0 ? 0 :\
+ index >= gx_cie_cache_size ? gx_cie_cache_size - 1 : index)
+
+private float
+EncodeABC_cached(floatp in, const gs_cie_render * pcrd, int i)
+{
+ const gx_cie_scalar_cache *pcache = &pcrd->caches.EncodeABC[i];
+
+ if (pcrd->RenderTable.lookup.table == 0) {
+ int index = (in - pcache->fracs.params.base) *
+ pcache->fracs.params.factor;
+
+ clamp_index(index);
+ return frac2float(pcache->fracs.values[index]);
+ } else {
+ /****** WRONG IF INTERPOLATING ******/
+ int index = (in - pcache->ints.params.base) *
+ pcache->ints.params.factor;
+ const gs_range *prange = &pcrd->RangeABC.ranges[i];
+ int m = pcrd->RenderTable.lookup.m;
+ int k = (i == 0 ? 1 : i == 1 ?
+ m * pcrd->RenderTable.lookup.dims[2] : m);
+
+ clamp_index(index);
+ return (double)(pcache->ints.values[index]) / k *
+ (prange->rmax - prange->rmin) / (gx_cie_cache_size - 1) +
+ prange->rmin;
+ }
+}
+private float
+EncodeABC_cached_A(floatp in, const gs_cie_render * pcrd)
+{
+ return EncodeABC_cached(in, pcrd, 0);
+}
+private float
+EncodeABC_cached_B(floatp in, const gs_cie_render * pcrd)
+{
+ return EncodeABC_cached(in, pcrd, 1);
+}
+private float
+EncodeABC_cached_C(floatp in, const gs_cie_render * pcrd)
+{
+ return EncodeABC_cached(in, pcrd, 2);
+}
+private float
+EncodeLMN_cached(floatp in, const gs_cie_render * pcrd, int i)
+{
+ const gx_cie_vector_cache *pcache = &pcrd->caches.EncodeLMN[i];
+ int index = (in - pcache->floats.params.base) *
+ pcache->floats.params.factor;
+
+ clamp_index(index);
+ /* Pick any one of u, v, w. We should probably pick the one */
+ /* with the largest coefficient.... */
+ return cie_cached2float(pcache->vecs.values[index].u) /
+ (i == 0 ? pcrd->MatrixABCEncode.cu.u :
+ i == 1 ? pcrd->MatrixABCEncode.cv.u :
+ pcrd->MatrixABCEncode.cw.u);
+}
+private float
+EncodeLMN_cached_L(floatp in, const gs_cie_render * pcrd)
+{
+ return EncodeLMN_cached(in, pcrd, 0);
+}
+private float
+EncodeLMN_cached_M(floatp in, const gs_cie_render * pcrd)
+{
+ return EncodeLMN_cached(in, pcrd, 1);
+}
+private float
+EncodeLMN_cached_N(floatp in, const gs_cie_render * pcrd)
+{
+ return EncodeLMN_cached(in, pcrd, 2);
+}
+
+private frac
+RTT_cached(byte in, const gs_cie_render * pcrd, int i)
+{
+ /****** NYI ******/
+ return byte2frac(in);
+}
+private frac
+RTT_cached_0(byte in, const gs_cie_render * pcrd)
+{
+ return RTT_cached(in, pcrd, 0);
+}
+private frac
+RTT_cached_1(byte in, const gs_cie_render * pcrd)
+{
+ return RTT_cached(in, pcrd, 1);
+}
+private frac
+RTT_cached_2(byte in, const gs_cie_render * pcrd)
+{
+ return RTT_cached(in, pcrd, 2);
+}
+private frac
+RTT_cached_3(byte in, const gs_cie_render * pcrd)
+{
+ return RTT_cached(in, pcrd, 3);
+}
+
+#undef clamp_index
+
+/* Define the TransformPQR trampoline procedure that looks up proc_name. */
+
+private int
+tpqr_do_lookup(gs_cie_render *pcrd, const gx_device *dev_proto)
+{
+ gx_device *dev;
+ gs_memory_t *mem = pcrd->rc.memory;
+ gs_c_param_list list;
+ gs_param_string proc_addr;
+ int code;
+
+ /* Device prototypes are const, so we must create a copy. */
+ code = gs_copydevice(&dev, dev_proto, mem);
+ if (code < 0)
+ return code;
+ gs_c_param_list_write(&list, mem);
+ code = param_request((gs_param_list *)&list,
+ pcrd->TransformPQR.proc_name);
+ if (code >= 0) {
+ code = gs_getdeviceparams(dev, (gs_param_list *)&list);
+ if (code >= 0) {
+ gs_c_param_list_read(&list);
+ code = param_read_string((gs_param_list *)&list,
+ pcrd->TransformPQR.proc_name,
+ &proc_addr);
+ if (code == 0 && proc_addr.size == sizeof(gs_cie_transform_proc)) {
+ memcpy(&pcrd->TransformPQR.proc, proc_addr.data,
+ sizeof(gs_cie_transform_proc));
+ } else
+ code = gs_note_error(gs_error_rangecheck);
+ }
+ }
+ gs_c_param_list_release(&list);
+ gs_free_object(mem, dev, "tpqr_do_lookup(device)");
+ return code;
+}
+private int
+tpqr_lookup(int index, floatp in, const gs_cie_wbsd * pwbsd,
+ gs_cie_render * pcrd, float *out)
+{
+ const gx_device *const *dev_list;
+ int count = gs_lib_device_list(&dev_list, NULL);
+ int i;
+ int code;
+
+ for (i = 0; i < count; ++i)
+ if (!strcmp(gs_devicename(dev_list[i]),
+ pcrd->TransformPQR.driver_name))
+ break;
+ if (i < count)
+ code = tpqr_do_lookup(pcrd, dev_list[i]);
+ else
+ code = gs_note_error(gs_error_undefined);
+ if (code < 0)
+ return code;
+ return pcrd->TransformPQR.proc(index, in, pwbsd, pcrd, out);
+}
+
+
+/* Default vectors. */
+const gs_cie_transform_proc3 TransformPQR_default = {
+ tpqr_identity,
+ 0, /* proc_name */
+ {0, 0}, /* proc_data */
+ 0 /* driver_name */
+};
+const gs_cie_transform_proc TransformPQR_lookup_proc_name = tpqr_lookup;
+const gs_cie_render_proc3 Encode_default = {
+ {render_identity, render_identity, render_identity}
+};
+const gs_cie_render_proc3 EncodeLMN_from_cache = {
+ {EncodeLMN_cached_L, EncodeLMN_cached_M, EncodeLMN_cached_N}
+};
+const gs_cie_render_proc3 EncodeABC_from_cache = {
+ {EncodeABC_cached_A, EncodeABC_cached_B, EncodeABC_cached_C}
+};
+const gs_cie_render_table_procs RenderTableT_default = {
+ {render_table_identity, render_table_identity, render_table_identity,
+ render_table_identity
+ }
+};
+const gs_cie_render_table_procs RenderTableT_from_cache = {
+ {RTT_cached_0, RTT_cached_1, RTT_cached_2, RTT_cached_3}
+};
+
+/*
+ * Allocate and minimally initialize a CRD. Note that this procedure sets
+ * the reference count of the structure to 1, not 0. gs_setcolorrendering
+ * will increment the reference count again, so unless you want the
+ * structure to stay allocated permanently (or until a garbage collection),
+ * you should call rc_decrement(pcrd, "client name") *after* calling
+ * gs_setcolorrendering.
+ */
+int
+gs_cie_render1_build(gs_cie_render ** ppcrd, gs_memory_t * mem,
+ client_name_t cname)
+{
+ gs_cie_render *pcrd;
+
+ rc_alloc_struct_1(pcrd, gs_cie_render, &st_cie_render1, mem,
+ return_error(gs_error_VMerror), cname);
+ /* Initialize pointers for the GC. */
+ pcrd->client_data = 0;
+ pcrd->RenderTable.lookup.table = 0;
+ pcrd->status = CIE_RENDER_STATUS_BUILT;
+ *ppcrd = pcrd;
+ return 0;
+}
+
+/*
+ * Initialize a CRD given all of the relevant parameters.
+ * Any of the pointers except WhitePoint may be zero, meaning
+ * use the default values.
+ *
+ * The actual point, matrix, range, and procedure values are copied into the
+ * CRD, but only the pointer to the color lookup table is copied.
+ */
+int
+gs_cie_render1_initialize(gs_cie_render * pcrd, void *client_data,
+ const gs_vector3 * WhitePoint,
+ const gs_vector3 * BlackPoint,
+ const gs_matrix3 * MatrixPQR,
+ const gs_range3 * RangePQR,
+ const gs_cie_transform_proc3 * TransformPQR,
+ const gs_matrix3 * MatrixLMN,
+ const gs_cie_render_proc3 * EncodeLMN,
+ const gs_range3 * RangeLMN,
+ const gs_matrix3 * MatrixABC,
+ const gs_cie_render_proc3 * EncodeABC,
+ const gs_range3 * RangeABC,
+ const gs_cie_render_table_t * RenderTable)
+{
+ pcrd->client_data = client_data;
+ pcrd->points.WhitePoint = *WhitePoint;
+ pcrd->points.BlackPoint =
+ *(BlackPoint ? BlackPoint : &BlackPoint_default);
+ pcrd->MatrixPQR = *(MatrixPQR ? MatrixPQR : &Matrix3_default);
+ pcrd->RangePQR = *(RangePQR ? RangePQR : &Range3_default);
+ pcrd->TransformPQR =
+ *(TransformPQR ? TransformPQR : &TransformPQR_default);
+ pcrd->MatrixLMN = *(MatrixLMN ? MatrixLMN : &Matrix3_default);
+ pcrd->EncodeLMN = *(EncodeLMN ? EncodeLMN : &Encode_default);
+ pcrd->RangeLMN = *(RangeLMN ? RangeLMN : &Range3_default);
+ pcrd->MatrixABC = *(MatrixABC ? MatrixABC : &Matrix3_default);
+ pcrd->EncodeABC = *(EncodeABC ? EncodeABC : &Encode_default);
+ pcrd->RangeABC = *(RangeABC ? RangeABC : &Range3_default);
+ if (RenderTable)
+ pcrd->RenderTable = *RenderTable;
+ else {
+ pcrd->RenderTable.lookup.table = 0;
+ pcrd->RenderTable.T = RenderTableT_default;
+ }
+ pcrd->status = CIE_RENDER_STATUS_BUILT;
+ return 0;
+}
diff --git a/pstoraster/gscrd.h b/pstoraster/gscrd.h
new file mode 100644
index 000000000..05f7c4a57
--- /dev/null
+++ b/pstoraster/gscrd.h
@@ -0,0 +1,75 @@
+/* Copyright (C) 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Interface for CIE color rendering dictionary creation */
+
+#ifndef gscrd_INCLUDED
+# define gscrd_INCLUDED
+
+#include "gscie.h"
+
+/*
+ * Allocate and minimally initialize a CRD. Note that this procedure sets
+ * the reference count of the structure to 1, not 0. gs_setcolorrendering
+ * will increment the reference count again, so unless you want the
+ * structure to stay allocated permanently (or until a garbage collection),
+ * you should call rc_decrement(pcrd, "client name") *after* calling
+ * gs_setcolorrendering.
+ */
+int
+ gs_cie_render1_build(P3(gs_cie_render ** ppcrd, gs_memory_t * mem,
+ client_name_t cname));
+
+/*
+ * Initialize a CRD given all of the relevant parameters.
+ * Any of the pointers except WhitePoint may be zero, meaning
+ * use the default values.
+ *
+ * The actual point, matrix, range, and procedure values are copied into the
+ * CRD, but only the pointer to the color lookup table
+ * (RenderTable.lookup.table) is copied, not the table itself.
+ */
+int
+ gs_cie_render1_initialize(P14(gs_cie_render * pcrd, void *client_data,
+ const gs_vector3 * WhitePoint,
+ const gs_vector3 * BlackPoint,
+ const gs_matrix3 * MatrixPQR,
+ const gs_range3 * RangePQR,
+ const gs_cie_transform_proc3 * TransformPQR,
+ const gs_matrix3 * MatrixLMN,
+ const gs_cie_render_proc3 * EncodeLMN,
+ const gs_range3 * RangeLMN,
+ const gs_matrix3 * MatrixABC,
+ const gs_cie_render_proc3 * EncodeABC,
+ const gs_range3 * RangeABC,
+ const gs_cie_render_table_t * RenderTable));
+
+/*
+ * Set or access the client_data pointer in a CRD.
+ * The macro is an L-value.
+ */
+#define gs_cie_render_client_data(pcrd) ((pcrd)->client_data)
+
+#endif /* gscrd_INCLUDED */
diff --git a/pstoraster/gscrdp.c b/pstoraster/gscrdp.c
new file mode 100644
index 000000000..d3b7ce08f
--- /dev/null
+++ b/pstoraster/gscrdp.c
@@ -0,0 +1,629 @@
+/* Copyright (C) 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* CIE color rendering dictionary creation */
+#include "math_.h"
+#include "memory_.h"
+#include "gx.h"
+#include "gsdevice.h"
+#include "gserrors.h"
+#include "gsmatrix.h" /* for gscolor2.h */
+#include "gxcspace.h"
+#include "gscolor2.h" /* for gs_set/currentcolorrendering */
+#include "gscrdp.h"
+#include "gxarith.h"
+
+/* Define the CRD type that we use here. */
+#define CRD_TYPE 101
+
+/* ---------------- Writing ---------------- */
+
+/* Internal procedures for writing parameter values. */
+private void
+store_vector3(float *p, const gs_vector3 * pvec)
+{
+ p[0] = pvec->u, p[1] = pvec->v, p[2] = pvec->w;
+}
+private int
+write_floats(gs_param_list * plist, gs_param_name key,
+ const float *values, int size, gs_memory_t * mem)
+{
+ float *p = (float *)
+ gs_alloc_byte_array(mem, size, sizeof(float), "write_floats");
+ gs_param_float_array fa;
+
+ if (p == 0)
+ return_error(gs_error_VMerror);
+ memcpy(p, values, size * sizeof(float));
+
+ fa.data = p;
+ fa.size = size;
+ fa.persistent = true;
+ return param_write_float_array(plist, key, &fa);
+}
+private int
+write_vector3(gs_param_list * plist, gs_param_name key,
+ const gs_vector3 * pvec, gs_memory_t * mem)
+{
+ float values[3];
+
+ store_vector3(values, pvec);
+ return write_floats(plist, key, values, 3, mem);
+}
+private int
+write_matrix3(gs_param_list * plist, gs_param_name key,
+ const gs_matrix3 * pmat, gs_memory_t * mem)
+{
+ float values[9];
+
+ if (!memcmp(pmat, &Matrix3_default, sizeof(*pmat)))
+ return 0;
+ store_vector3(values, &pmat->cu);
+ store_vector3(values + 3, &pmat->cv);
+ store_vector3(values + 6, &pmat->cw);
+ return write_floats(plist, key, values, 9, mem);
+}
+private int
+write_range3(gs_param_list * plist, gs_param_name key,
+ const gs_range3 * prange, gs_memory_t * mem)
+{
+ float values[6];
+
+ if (!memcmp(prange, &Range3_default, sizeof(*prange)))
+ return 0;
+ values[0] = prange->ranges[0].rmin, values[1] = prange->ranges[0].rmax;
+ values[2] = prange->ranges[1].rmin, values[3] = prange->ranges[1].rmax;
+ values[4] = prange->ranges[2].rmin, values[5] = prange->ranges[2].rmax;
+ return write_floats(plist, key, values, 6, mem);
+}
+private int
+write_proc3(gs_param_list * plist, gs_param_name key,
+ const gs_cie_render * pcrd, const gs_cie_render_proc3 * procs,
+ const gs_range3 * domain, gs_memory_t * mem)
+{
+ float *values;
+ uint size = gx_cie_cache_size;
+ gs_param_float_array fa;
+ int i;
+
+ if (!memcmp(procs, &Encode_default, sizeof(*procs)))
+ return 0;
+ values = (float *)gs_alloc_byte_array(mem, size * 3, sizeof(float),
+ "write_proc3");
+
+ if (values == 0)
+ return_error(gs_error_VMerror);
+ for (i = 0; i < 3; ++i) {
+ double base = domain->ranges[i].rmin;
+ double scale = (domain->ranges[i].rmax - base) / (size - 1);
+ int j;
+
+ for (j = 0; j < size; ++j)
+ values[i * size + j] =
+ (*procs->procs[i]) (j * scale + base, pcrd);
+ }
+ fa.data = values;
+ fa.size = size * 3;
+ fa.persistent = true;
+ return param_write_float_array(plist, key, &fa);
+}
+
+/* Write a CRD as a device parameter. */
+int
+param_write_cie_render1(gs_param_list * plist, gs_param_name key,
+ const gs_cie_render * pcrd, gs_memory_t * mem)
+{
+ gs_param_dict dict;
+ int code, dcode;
+
+ dict.size = 20;
+ if ((code = param_begin_write_dict(plist, key, &dict, false)) < 0)
+ return code;
+ code = param_put_cie_render1(dict.list, pcrd, mem);
+ dcode = param_end_write_dict(plist, key, &dict);
+ return (code < 0 ? code : dcode);
+}
+
+/* Write a CRD directly to a parameter list. */
+int
+param_put_cie_render1(gs_param_list * plist, const gs_cie_render * pcrd,
+ gs_memory_t * mem)
+{
+ int crd_type = CRD_TYPE;
+ int code;
+
+ if (pcrd->TransformPQR.proc_name) {
+ gs_param_string pn, pd;
+
+ param_string_from_string(pn, pcrd->TransformPQR.proc_name);
+ pn.size++; /* include terminating null */
+ pd.data = pcrd->TransformPQR.proc_data.data;
+ pd.size = pcrd->TransformPQR.proc_data.size;
+ pd.persistent = true; /****** WRONG ******/
+ if ((code = param_write_name(plist, "TransformPQRName", &pn)) < 0 ||
+ (code = param_write_string(plist, "TransformPQRData", &pd)) < 0
+ )
+ return code;
+ }
+ else if (pcrd->TransformPQR.proc != TransformPQR_default.proc) {
+ /* We have no way to represent the procedure, so return an error. */
+ return_error(gs_error_rangecheck);
+ }
+ if ((code = param_write_int(plist, "ColorRenderingType", &crd_type)) < 0 ||
+ (code = write_vector3(plist, "WhitePoint", &pcrd->points.WhitePoint, mem)) < 0
+ )
+ return code;
+ if (memcmp(&pcrd->points.BlackPoint, &BlackPoint_default,
+ sizeof(pcrd->points.BlackPoint))) {
+ if ((code = write_vector3(plist, "BlackPoint", &pcrd->points.BlackPoint, mem)) < 0)
+ return code;
+ }
+ if ((code = write_matrix3(plist, "MatrixPQR", &pcrd->MatrixPQR, mem)) < 0 ||
+ (code = write_range3(plist, "RangePQR", &pcrd->RangePQR, mem)) < 0 ||
+ /* TransformPQR is handled separately */
+ (code = write_matrix3(plist, "MatrixLMN", &pcrd->MatrixLMN, mem)) < 0 ||
+ (code = write_proc3(plist, "EncodeLMNValues", pcrd,
+ &pcrd->EncodeLMN, &pcrd->DomainLMN, mem)) < 0 ||
+ (code = write_range3(plist, "RangeLMN", &pcrd->RangeLMN, mem)) < 0 ||
+ (code = write_matrix3(plist, "MatrixABC", &pcrd->MatrixABC, mem)) < 0 ||
+ (code = write_proc3(plist, "EncodeABCValues", pcrd,
+ &pcrd->EncodeABC, &pcrd->DomainABC, mem)) < 0 ||
+ (code = write_range3(plist, "RangeABC", &pcrd->RangeABC, mem)) < 0
+ )
+ return code;
+ if (pcrd->RenderTable.lookup.table) {
+ int n = pcrd->RenderTable.lookup.n;
+ int m = pcrd->RenderTable.lookup.m;
+ int na = pcrd->RenderTable.lookup.dims[0];
+ int *size = (int *)
+ gs_alloc_byte_array(mem, n + 1, sizeof(int), "RenderTableSize");
+
+ /*
+ * In principle, we should use gs_alloc_struct_array with a
+ * type descriptor for gs_param_string. However, it is widely
+ * assumed that parameter lists are transient, and don't require
+ * accurate GC information; so we can get away with allocating
+ * the string table as bytes.
+ */
+ gs_param_string *table =
+ (gs_param_string *)
+ gs_alloc_byte_array(mem, na, sizeof(gs_param_string),
+ "RenderTableTable");
+ gs_param_int_array ia;
+
+ if (size == 0 || table == 0)
+ code = gs_note_error(gs_error_VMerror);
+ else {
+ memcpy(size, pcrd->RenderTable.lookup.dims, sizeof(int) * n);
+
+ size[n] = m;
+ ia.data = size;
+ ia.size = n + 1;
+ ia.persistent = true;
+ code = param_write_int_array(plist, "RenderTableSize", &ia);
+ }
+ if (code >= 0) {
+ gs_param_string_array sa;
+ int a;
+
+ for (a = 0; a < na; ++a)
+ table[a].data = pcrd->RenderTable.lookup.table[a].data,
+ table[a].size = pcrd->RenderTable.lookup.table[a].size,
+ table[a].persistent = true;
+ sa.data = table;
+ sa.size = na;
+ sa.persistent = true;
+ code = param_write_string_array(plist, "RenderTableTable", &sa);
+ if (code >= 0 && !pcrd->caches.RenderTableT_is_identity) {
+ /****** WRITE RenderTableTValues LIKE write_proc3 ******/
+ uint size = gx_cie_cache_size;
+ float *values =
+ (float *)gs_alloc_byte_array(mem, size * m,
+ sizeof(float),
+ "write_proc3");
+ gs_param_float_array fa;
+ int i;
+
+ if (values == 0)
+ return_error(gs_error_VMerror);
+ for (i = 0; i < m; ++i) {
+ double scale = 255.0 / (size - 1);
+ int j;
+
+ for (j = 0; j < size; ++j)
+ values[i * size + j] =
+ frac2float((*pcrd->RenderTable.T.procs[i])
+ (j * scale, pcrd));
+ }
+ fa.data = values;
+ fa.size = size * m;
+ fa.persistent = true;
+ code = param_write_float_array(plist, "RenderTableTValues",
+ &fa);
+ }
+ }
+ if (code < 0) {
+ gs_free_object(mem, table, "RenderTableTable");
+ gs_free_object(mem, size, "RenderTableSize");
+ return code;
+ }
+ }
+ return code;
+}
+
+/* ---------------- Reading ---------------- */
+
+/* Internal procedures for reading parameter values. */
+private void
+load_vector3(gs_vector3 * pvec, const float *p)
+{
+ pvec->u = p[0], pvec->v = p[1], pvec->w = p[2];
+}
+private int
+read_floats(gs_param_list * plist, gs_param_name key, float *values, int count)
+{
+ gs_param_float_array fa;
+ int code = param_read_float_array(plist, key, &fa);
+
+ if (code)
+ return code;
+ if (fa.size != count)
+ return_error(gs_error_rangecheck);
+ memcpy(values, fa.data, sizeof(float) * count);
+
+ return 0;
+}
+private int
+read_vector3(gs_param_list * plist, gs_param_name key,
+ gs_vector3 * pvec, const gs_vector3 * dflt)
+{
+ float values[3];
+ int code = read_floats(plist, key, values, 3);
+
+ switch (code) {
+ case 1: /* not defined */
+ if (dflt)
+ *pvec = *dflt;
+ break;
+ case 0:
+ load_vector3(pvec, values);
+ default: /* error */
+ break;
+ }
+ return code;
+}
+private int
+read_matrix3(gs_param_list * plist, gs_param_name key, gs_matrix3 * pmat)
+{
+ float values[9];
+ int code = read_floats(plist, key, values, 9);
+
+ switch (code) {
+ case 1: /* not defined */
+ *pmat = Matrix3_default;
+ break;
+ case 0:
+ load_vector3(&pmat->cu, values);
+ load_vector3(&pmat->cv, values + 3);
+ load_vector3(&pmat->cw, values + 6);
+ default: /* error */
+ break;
+ }
+ return code;
+}
+private int
+read_range3(gs_param_list * plist, gs_param_name key, gs_range3 * prange)
+{
+ float values[6];
+ int code = read_floats(plist, key, values, 6);
+
+ switch (code) {
+ case 1: /* not defined */
+ *prange = Range3_default;
+ break;
+ case 0:
+ prange->ranges[0].rmin = values[0];
+ prange->ranges[0].rmax = values[1];
+ prange->ranges[1].rmin = values[2];
+ prange->ranges[1].rmax = values[3];
+ prange->ranges[2].rmin = values[4];
+ prange->ranges[2].rmax = values[5];
+ default: /* error */
+ break;
+ }
+ return code;
+}
+private int
+read_proc3(gs_param_list * plist, gs_param_name key,
+ float values[gx_cie_cache_size * 3])
+{
+ return read_floats(plist, key, values, gx_cie_cache_size * 3);
+}
+
+/* Read a CRD from a device parameter. */
+int
+gs_cie_render1_param_initialize(gs_cie_render * pcrd, gs_param_list * plist,
+ gs_param_name key, gx_device * dev)
+{
+ gs_param_dict dict;
+ int code = param_begin_read_dict(plist, key, &dict, false);
+ int dcode;
+
+ if (code < 0)
+ return code;
+ code = param_get_cie_render1(pcrd, dict.list, dev);
+ dcode = param_end_read_dict(plist, key, &dict);
+ if (code < 0)
+ return code;
+ if (dcode < 0)
+ return dcode;
+ gs_cie_render_init(pcrd);
+ gs_cie_render_sample(pcrd);
+ return gs_cie_render_complete(pcrd);
+}
+
+/* Define the structure for passing Encode values as "client data". */
+typedef struct encode_data_s {
+ float lmn[gx_cie_cache_size * 3]; /* EncodeLMN */
+ float abc[gx_cie_cache_size * 3]; /* EncodeABC */
+ float t[gx_cie_cache_size * 4]; /* RenderTable.T */
+} encode_data_t;
+
+/* Define procedures that retrieve the Encode values read from the list. */
+private float
+encode_from_data(floatp v, const float values[gx_cie_cache_size],
+ const gs_range * range)
+{
+ return (v <= range->rmin ? values[0] :
+ v >= range->rmax ? values[gx_cie_cache_size - 1] :
+ values[(int)((v - range->rmin) / (range->rmax - range->rmin) *
+ (gx_cie_cache_size - 1) + 0.5)]);
+}
+/*
+ * The repetitive boilerplate in the next 10 procedures really sticks in
+ * my craw, but I've got a mandate not to use macros....
+ */
+private float
+encode_lmn_0_from_data(floatp v, const gs_cie_render * pcrd)
+{
+ const encode_data_t *data = pcrd->client_data;
+
+ return encode_from_data(v, &data->lmn[0],
+ &pcrd->DomainLMN.ranges[0]);
+}
+private float
+encode_lmn_1_from_data(floatp v, const gs_cie_render * pcrd)
+{
+ const encode_data_t *data = pcrd->client_data;
+
+ return encode_from_data(v, &data->lmn[gx_cie_cache_size],
+ &pcrd->DomainLMN.ranges[1]);
+}
+private float
+encode_lmn_2_from_data(floatp v, const gs_cie_render * pcrd)
+{
+ const encode_data_t *data = pcrd->client_data;
+
+ return encode_from_data(v, &data->lmn[gx_cie_cache_size * 2],
+ &pcrd->DomainLMN.ranges[2]);
+}
+private float
+encode_abc_0_from_data(floatp v, const gs_cie_render * pcrd)
+{
+ const encode_data_t *data = pcrd->client_data;
+
+ return encode_from_data(v, &data->abc[0],
+ &pcrd->DomainABC.ranges[0]);
+}
+private float
+encode_abc_1_from_data(floatp v, const gs_cie_render * pcrd)
+{
+ const encode_data_t *data = pcrd->client_data;
+
+ return encode_from_data(v, &data->abc[gx_cie_cache_size],
+ &pcrd->DomainABC.ranges[1]);
+}
+private float
+encode_abc_2_from_data(floatp v, const gs_cie_render * pcrd)
+{
+ const encode_data_t *data = pcrd->client_data;
+
+ return encode_from_data(v, &data->abc[gx_cie_cache_size * 2],
+ &pcrd->DomainABC.ranges[2]);
+}
+private frac
+render_table_t_0_from_data(byte v, const gs_cie_render * pcrd)
+{
+ const encode_data_t *data = pcrd->client_data;
+
+ return float2frac(encode_from_data(v / 255.0,
+ &data->t[0],
+ &Range3_default.ranges[0]));
+}
+private frac
+render_table_t_1_from_data(byte v, const gs_cie_render * pcrd)
+{
+ const encode_data_t *data = pcrd->client_data;
+
+ return float2frac(encode_from_data(v / 255.0,
+ &data->t[gx_cie_cache_size],
+ &Range3_default.ranges[0]));
+}
+private frac
+render_table_t_2_from_data(byte v, const gs_cie_render * pcrd)
+{
+ const encode_data_t *data = pcrd->client_data;
+
+ return float2frac(encode_from_data(v / 255.0,
+ &data->t[gx_cie_cache_size * 2],
+ &Range3_default.ranges[0]));
+}
+private frac
+render_table_t_3_from_data(byte v, const gs_cie_render * pcrd)
+{
+ const encode_data_t *data = pcrd->client_data;
+
+ return float2frac(encode_from_data(v / 255.0,
+ &data->t[gx_cie_cache_size * 3],
+ &Range3_default.ranges[0]));
+}
+private const gs_cie_render_proc3 EncodeLMN_from_data = {
+ {encode_lmn_0_from_data, encode_lmn_1_from_data, encode_lmn_2_from_data}
+};
+private const gs_cie_render_proc3 EncodeABC_from_data = {
+ {encode_abc_0_from_data, encode_abc_1_from_data, encode_abc_2_from_data}
+};
+private const gs_cie_render_table_procs RenderTableT_from_data = {
+ {render_table_t_0_from_data, render_table_t_1_from_data,
+ render_table_t_2_from_data, render_table_t_3_from_data
+ }
+};
+
+/* Read a CRD directly from a parameter list. */
+int
+param_get_cie_render1(gs_cie_render * pcrd, gs_param_list * plist,
+ gx_device * dev)
+{
+ encode_data_t data;
+ gs_param_int_array rt_size;
+ int crd_type;
+ int code, code_lmn, code_abc, code_rt, code_t;
+ gs_param_string pname, pdata;
+
+ if ((code = param_read_int(plist, "ColorRenderingType", &crd_type)) < 0 ||
+ crd_type != CRD_TYPE ||
+ (code = read_vector3(plist, "WhitePoint", &pcrd->points.WhitePoint,
+ NULL)) < 0 ||
+ (code = read_vector3(plist, "BlackPoint", &pcrd->points.BlackPoint,
+ &BlackPoint_default)) < 0 ||
+ (code = read_matrix3(plist, "MatrixPQR", &pcrd->MatrixPQR)) < 0 ||
+ (code = read_range3(plist, "RangePQR", &pcrd->RangePQR)) < 0 ||
+ /* TransformPQR is handled specially below. */
+ (code = read_matrix3(plist, "MatrixLMN", &pcrd->MatrixLMN)) < 0 ||
+ (code_lmn = code =
+ read_proc3(plist, "EncodeLMNValues", data.lmn)) < 0 ||
+ (code = read_range3(plist, "RangeLMN", &pcrd->RangeLMN)) < 0 ||
+ (code = read_matrix3(plist, "MatrixABC", &pcrd->MatrixABC)) < 0 ||
+ (code_abc = code =
+ read_proc3(plist, "EncodeABCValues", data.abc)) < 0 ||
+ (code = read_range3(plist, "RangeABC", &pcrd->RangeABC)) < 0
+ )
+ return code;
+ /* Handle the sampled functions. */
+ switch (code = param_read_string(plist, "TransformPQRName", &pname)) {
+ default: /* error */
+ return code;
+ case 1: /* missing */
+ pcrd->TransformPQR = TransformPQR_default;
+ break;
+ case 0: /* specified */
+ /* The procedure name must be null-terminated: */
+ /* see param_put_cie_render1 above. */
+ if (pname.size < 1 || pname.data[pname.size - 1] != 0)
+ return_error(gs_error_rangecheck);
+ pcrd->TransformPQR.proc = TransformPQR_lookup_proc_name;
+ pcrd->TransformPQR.proc_name = (char *)pname.data;
+ switch (code = param_read_string(plist, "TransformPQRData", &pdata)) {
+ default: /* error */
+ return code;
+ case 1: /* missing */
+ pcrd->TransformPQR.proc_data.data = 0;
+ pcrd->TransformPQR.proc_data.size = 0;
+ break;
+ case 0:
+ pcrd->TransformPQR.proc_data.data = pdata.data;
+ pcrd->TransformPQR.proc_data.size = pdata.size;
+ }
+ pcrd->TransformPQR.driver_name = gs_devicename(dev);
+ break;
+ }
+ pcrd->client_data = &data;
+ if (code_lmn > 0)
+ pcrd->EncodeLMN = Encode_default;
+ else
+ pcrd->EncodeLMN = EncodeLMN_from_data;
+ if (code_abc > 0)
+ pcrd->EncodeABC = Encode_default;
+ else
+ pcrd->EncodeABC = EncodeABC_from_data;
+ code_rt = code = param_read_int_array(plist, "RenderTableSize", &rt_size);
+ if (code == 1) {
+ if (pcrd->RenderTable.lookup.table) {
+ gs_free_object(pcrd->rc.memory,
+ (void *)pcrd->RenderTable.lookup.table, /* break const */
+ "param_get_cie_render1(RenderTable)");
+ pcrd->RenderTable.lookup.table = 0;
+ }
+ pcrd->RenderTable.T = RenderTableT_default;
+ code_t = 1;
+ } else if (code < 0)
+ return code;
+ else if (rt_size.size != 4)
+ return_error(gs_error_rangecheck);
+ else {
+ gs_param_string_array rt_values;
+ gs_const_string *table;
+ int n, m, j;
+
+ code = param_read_string_array(plist, "RenderTableTable", &rt_values);
+ if (code < 0)
+ return code;
+ else if (code > 0 ||
+ rt_values.size != rt_size.data[3] *
+ rt_size.data[1] * rt_size.data[2])
+ return_error(gs_error_rangecheck);
+ pcrd->RenderTable.lookup.n = n = rt_size.size - 1;
+ pcrd->RenderTable.lookup.m = m = rt_size.data[n];
+ if (n > 4 || m > 4)
+ return_error(gs_error_rangecheck);
+ memcpy(pcrd->RenderTable.lookup.dims, rt_size.data, n * sizeof(int));
+ /****** ALLOCATE table = RenderTable.lookup.table ******/
+ for (j = 0; j < pcrd->RenderTable.lookup.dims[0]; ++j) {
+ table[j].data = rt_values.data[j].data;
+ table[j].size = rt_values.data[j].size;
+ }
+ pcrd->RenderTable.lookup.table = table;
+ pcrd->RenderTable.T = RenderTableT_from_data;
+ code_t = code = read_floats(plist, "RenderTableTValues", data.t,
+ gx_cie_cache_size * m);
+ if (code > 0)
+ pcrd->RenderTable.T = RenderTableT_default;
+ else if (code == 0)
+ pcrd->RenderTable.T = RenderTableT_from_data;
+ }
+ if ((code = gs_cie_render_init(pcrd)) >= 0 &&
+ (code = gs_cie_render_sample(pcrd)) >= 0
+ )
+ code = gs_cie_render_complete(pcrd);
+ /* Clean up before exiting. */
+ pcrd->client_data = 0;
+ if (code_lmn == 0)
+ pcrd->EncodeLMN = EncodeLMN_from_cache;
+ if (code_abc == 0)
+ pcrd->EncodeABC = EncodeABC_from_cache;
+ if (code_t == 0)
+ pcrd->RenderTable.T = RenderTableT_from_cache;
+ return code;
+}
diff --git a/pstoraster/gscrdp.h b/pstoraster/gscrdp.h
new file mode 100644
index 000000000..35f2a1d96
--- /dev/null
+++ b/pstoraster/gscrdp.h
@@ -0,0 +1,104 @@
+/* Copyright (C) 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Interface for device-specified CRDs */
+
+#ifndef gscrdp_INCLUDED
+# define gscrdp_INCLUDED
+
+#include "gscie.h"
+#include "gsparam.h"
+
+/*
+ * A driver can provide any number of its own CRDs through (read-only)
+ * device parameters whose values are slightly modified PostScript-style
+ * dictionaries. The driver doesn't need to concern itself with how the
+ * parameters are encoded: it simply constructs a CRD and calls
+ * param_write_cie_render1.
+ */
+int param_write_cie_render1(P4(gs_param_list * plist, gs_param_name key,
+ const gs_cie_render * pcrd,
+ gs_memory_t * mem));
+
+/*
+ * For internal use, we also provide an API that writes the CRD directly
+ * into a parameter list, rather than as a named parameter in a larger
+ * list.
+ */
+int param_put_cie_render1(P3(gs_param_list * plist, const gs_cie_render * pcrd,
+ gs_memory_t * mem));
+
+/*
+ * Client code that wants to initialize a CRD from a device parameter
+ * uses the following complementary procedure. The customary way to
+ * use this is:
+
+ gs_c_param_list list;
+ ...
+ gs_c_param_list_write(&list, mem);
+ gs_c_param_request(&list, "ParamName");
+ code = gs_getdeviceparams(dev, &list);
+ << error if code < 0 >>
+ gs_c_param_list_read(&list);
+ code = gs_cie_render1_param_initialize(pcrd, &list, "ParamName", dev);
+ gs_c_param_list_release(&list);
+ << error if code < 0 >>
+
+ * where "ParamName" is the parameter name, e.g., "CRDDefault".
+ */
+int gs_cie_render1_param_initialize(P4(gs_cie_render * pcrd,
+ gs_param_list * plist,
+ gs_param_name key,
+ gx_device * dev));
+
+/*
+ * Again, we provide an internal procedure that doesn't involve a
+ * parameter name.
+ */
+int param_get_cie_render1(P3(gs_cie_render * pcrd,
+ gs_param_list * plist,
+ gx_device * dev));
+
+/*
+ * The actual representation of the CRD is a slightly modified PostScript
+ * ColorRenderingType 1 dictionary. THE FOLLOWING IS SUBJECT TO CHANGE
+ * WITHOUT NOTICE. Specifically, the following keys are different:
+ * ColorRenderingType = 101
+ * (Instead of TransformPQR = [T1 T2 T3]:)
+ * TransformPQRName = procedure name (a name)
+ * TransformPQRData = procedure data (a string)
+ * (Instead of EncodeLMN/ABC = [E1 E2 E3]:)
+ * EncodeLMN/ABCValues = [V1,1 V1,2 ... V3,N], where Vi,j is the
+ * j'th sampled value of the i'th encoding array, mapped linearly
+ * to the corresponding domain (see gscie.h)
+ * (Instead of RenderTable = [NA NB NC table m T1 ... Tm]:)
+ * RenderTableSize = [NA NB NC m]
+ * RenderTableTable = table (an array of strings)
+ * RenderTableTValues = [V1,1 V1,2 ... Vm,N] (see above)
+ * The PostScript setcolorrendering operator selects the correct operator
+ * according to the ColorRenderingType key.
+ */
+
+#endif /* gscrdp_INCLUDED */
diff --git a/pstoraster/gscrypt1.h b/pstoraster/gscrypt1.h
new file mode 100644
index 000000000..80e1c6660
--- /dev/null
+++ b/pstoraster/gscrypt1.h
@@ -0,0 +1,56 @@
+/* Copyright (C) 1990, 1992, 1997 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Interface to Adobe Type 1 encryption/decryption. */
+
+#ifndef gscrypt1_INCLUDED
+# define gscrypt1_INCLUDED
+
+/* 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)
+/* c1 * c1' == 1 mod 2^16. */
+#define crypt_c1_inverse ((ushort)27493)
+#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)
+#define decrypt_skip_previous(ch, state)\
+ (state = (state - crypt_c2) * crypt_c1_inverse - (ch))
+
+#endif /* gscrypt1_INCLUDED */
diff --git a/pstoraster/gscscie.c b/pstoraster/gscscie.c
new file mode 100644
index 000000000..2b2dec89e
--- /dev/null
+++ b/pstoraster/gscscie.c
@@ -0,0 +1,374 @@
+/* Copyright (C) 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* CIE color space management */
+#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"
+
+/* ---------------- Color space definition ---------------- */
+
+/* GC descriptors */
+private_st_cie_common();
+private_st_cie_common_elements();
+private_st_cie_a();
+private_st_cie_abc();
+private_st_cie_def();
+private_st_cie_defg();
+
+/* 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. */
+extern cs_proc_init_color(gx_init_CIE);
+private cs_proc_concrete_space(gx_concrete_space_CIE);
+private cs_proc_install_cspace(gx_install_CIE);
+
+/* CIEBasedDEFG */
+gs_private_st_ptrs1(st_color_space_CIEDEFG, gs_base_color_space,
+ "gs_color_space(CIEDEFG)", cs_CIEDEFG_enum_ptrs, cs_CIEDEFG_reloc_ptrs,
+ params.defg);
+extern cs_proc_restrict_color(gx_restrict_CIEDEFG);
+extern cs_proc_concretize_color(gx_concretize_CIEDEFG);
+extern cs_proc_install_cspace(gx_install_CIEDEFG);
+private cs_proc_adjust_cspace_count(gx_adjust_cspace_CIEDEFG);
+const gs_color_space_type gs_color_space_type_CIEDEFG = {
+ gs_color_space_index_CIEDEFG, true, true,
+ &st_color_space_CIEDEFG, gx_num_components_4,
+ gx_no_base_space,
+ gx_init_CIE, gx_restrict_CIEDEFG,
+ gx_concrete_space_CIE,
+ gx_concretize_CIEDEFG, NULL,
+ gx_default_remap_color, gx_install_CIE,
+ gx_adjust_cspace_CIEDEFG, gx_no_adjust_color_count
+};
+
+/* CIEBasedDEF */
+gs_private_st_ptrs1(st_color_space_CIEDEF, gs_base_color_space,
+ "gs_color_space(CIEDEF)", cs_CIEDEF_enum_ptrs, cs_CIEDEF_reloc_ptrs,
+ params.def);
+extern cs_proc_restrict_color(gx_restrict_CIEDEF);
+extern cs_proc_concretize_color(gx_concretize_CIEDEF);
+extern cs_proc_install_cspace(gx_install_CIEDEF);
+private cs_proc_adjust_cspace_count(gx_adjust_cspace_CIEDEF);
+const gs_color_space_type gs_color_space_type_CIEDEF = {
+ gs_color_space_index_CIEDEF, true, true,
+ &st_color_space_CIEDEF, gx_num_components_3,
+ gx_no_base_space,
+ gx_init_CIE, gx_restrict_CIEDEF,
+ gx_concrete_space_CIE,
+ gx_concretize_CIEDEF, NULL,
+ gx_default_remap_color, gx_install_CIE,
+ gx_adjust_cspace_CIEDEF, gx_no_adjust_color_count
+};
+
+/* CIEBasedABC */
+gs_private_st_ptrs1(st_color_space_CIEABC, gs_base_color_space,
+ "gs_color_space(CIEABC)", cs_CIEABC_enum_ptrs, cs_CIEABC_reloc_ptrs,
+ params.abc);
+cs_proc_restrict_color(gx_restrict_CIEABC);
+cs_proc_concretize_color(gx_concretize_CIEABC);
+cs_proc_install_cspace(gx_install_CIEABC);
+private cs_proc_adjust_cspace_count(gx_adjust_cspace_CIEABC);
+extern cs_proc_remap_color(gx_remap_CIEABC);
+const gs_color_space_type gs_color_space_type_CIEABC = {
+ gs_color_space_index_CIEABC, true, true,
+ &st_color_space_CIEABC, gx_num_components_3,
+ gx_no_base_space,
+ gx_init_CIE, gx_restrict_CIEABC,
+ gx_concrete_space_CIE,
+ gx_concretize_CIEABC, NULL,
+ gx_remap_CIEABC, gx_install_CIE,
+ gx_adjust_cspace_CIEABC, gx_no_adjust_color_count
+};
+
+/* CIEBasedA */
+gs_private_st_ptrs1(st_color_space_CIEA, gs_base_color_space,
+ "gs_color_space(CIEA)", cs_CIEA_enum_ptrs, cs_CIEA_reloc_ptrs,
+ params.a);
+cs_proc_restrict_color(gx_restrict_CIEA);
+cs_proc_concretize_color(gx_concretize_CIEA);
+cs_proc_install_cspace(gx_install_CIEA);
+private cs_proc_adjust_cspace_count(gx_adjust_cspace_CIEA);
+const gs_color_space_type gs_color_space_type_CIEA = {
+ gs_color_space_index_CIEA, true, true,
+ &st_color_space_CIEA, gx_num_components_1,
+ gx_no_base_space,
+ gx_init_CIE, gx_restrict_CIEA,
+ gx_concrete_space_CIE,
+ gx_concretize_CIEA, NULL,
+ gx_default_remap_color, gx_install_CIE,
+ gx_adjust_cspace_CIEA, gx_no_adjust_color_count
+};
+
+/* Determine the concrete space underlying a CIEBased space. */
+private const gs_color_space *
+gx_concrete_space_CIE(const gs_color_space * pcs, const gs_imager_state * pis)
+{
+ const gs_cie_render *pcie = pis->cie_render;
+
+ if (pcie == 0 || pcie->RenderTable.lookup.table == 0 ||
+ pcie->RenderTable.lookup.m == 3
+ )
+ return gs_cspace_DeviceRGB(pis);
+ else /* pcie->RenderTable.lookup.m == 4 */
+ return gs_cspace_DeviceCMYK(pis);
+}
+
+/* Install a CIE space in the graphics state. */
+/* We go through an extra level of procedure so that */
+/* interpreters can substitute their own installer. */
+private int
+gx_install_CIE(gs_color_space * pcs, gs_state * pgs)
+{
+ return (*pcs->params.a->common.install_cspace) (pcs, pgs);
+}
+
+/* Adjust reference counts for a CIE color space */
+private void
+gx_adjust_cspace_CIEDEFG(const gs_color_space * pcs, int delta)
+{
+ rc_adjust_const(pcs->params.defg, delta, "gx_adjust_cspace_CIEDEFG");
+}
+
+private void
+gx_adjust_cspace_CIEDEF(const gs_color_space * pcs, int delta)
+{
+ rc_adjust_const(pcs->params.def, delta, "gx_adjust_cspace_CIEDEF");
+}
+
+private void
+gx_adjust_cspace_CIEABC(const gs_color_space * pcs, int delta)
+{
+ rc_adjust_const(pcs->params.abc, delta, "gx_adjust_cspace_CIEABC");
+}
+
+private void
+gx_adjust_cspace_CIEA(const gs_color_space * pcs, int delta)
+{
+ rc_adjust_const(pcs->params.a, delta, "gx_adjust_cspace_CIEA");
+}
+
+/* ---------------- Procedures ---------------- */
+
+/* ------ Internal initializers ------ */
+
+/*
+ * Set up the default values for the CIE parameters that are common to
+ * all CIE color spaces.
+ *
+ * There is no default for the white point, so it is set equal to the
+ * black point. If anyone actually uses the color space in that form,
+ * the results are likely to be unsatisfactory.
+ */
+private void
+set_common_cie_defaults(gs_cie_common * pcommon, void *client_data)
+{
+ pcommon->RangeLMN = Range3_default;
+ pcommon->DecodeLMN = DecodeLMN_default;
+ pcommon->MatrixLMN = Matrix3_default;
+ pcommon->points.WhitePoint = BlackPoint_default;
+ pcommon->points.BlackPoint = BlackPoint_default;
+ pcommon->client_data = client_data;
+}
+
+/*
+ * Set defaults for a CIEBasedABC color space. This is also used for
+ * CIEBasedDEF and CIEBasedDEFG color spaces.
+ */
+private void
+set_cie_abc_defaults(gs_cie_abc * pabc, void *client_data)
+{
+ set_common_cie_defaults(&pabc->common, client_data);
+ pabc->RangeABC = Range3_default;
+ pabc->DecodeABC = DecodeABC_default;
+ pabc->MatrixABC = Matrix3_default;
+}
+
+/*
+ * Set up a default color lookup table for a CIEBasedDEF[G] space. There is
+ * no specified default for this structure, so the values used here (aside
+ * from the input and output component numbers) are intended only to make
+ * the system fail in a predictable manner.
+ */
+private void
+set_ctbl_defaults(gx_color_lookup_table * plktblp, int num_comps)
+{
+ int i;
+
+ plktblp->n = num_comps;
+ plktblp->m = 3; /* always output CIE ABC */
+ for (i = 0; i < countof(plktblp->dims); i++)
+ plktblp->dims[i] = 0;
+ plktblp->table = 0;
+}
+
+/*
+ * Allocate a color space and its parameter structure.
+ * Return 0 if VMerror, otherwise the parameter structure.
+ */
+private void *
+build_cie_space(gs_color_space ** ppcspace, const gs_color_space_type * pcstype,
+ gs_memory_type_ptr_t stype, gs_memory_t * pmem)
+{
+ gs_color_space *pcspace =
+ gs_alloc_struct(pmem, gs_color_space, &st_color_space,
+ "build_cie_space");
+ gs_cie_common_elements_t *pdata;
+
+ if (pcspace == 0)
+ return 0;
+ rc_alloc_struct_1(pdata, gs_cie_common_elements_t, stype, pmem,
+ {
+ gs_free_object(pmem, pcspace, "build_cie_space");
+ return 0;
+ }
+ ,
+ "build_cie_space(data)");
+ pcspace->pmem = pmem;
+ pcspace->type = pcstype;
+ *ppcspace = pcspace;
+ return (void *)pdata;
+}
+
+/* ------ Constructors ------ */
+
+int
+gs_cspace_build_CIEA(gs_color_space ** ppcspace, void *client_data,
+ gs_memory_t * pmem)
+{
+ gs_cie_a *pciea =
+ build_cie_space(ppcspace, &gs_color_space_type_CIEA, &st_cie_a, pmem);
+
+ if (pciea == 0)
+ return_error(gs_error_VMerror);
+
+ set_common_cie_defaults(&pciea->common, client_data);
+ pciea->common.install_cspace = gx_install_CIEA;
+ pciea->RangeA = RangeA_default;
+ pciea->DecodeA = DecodeA_default;
+ pciea->MatrixA = MatrixA_default;
+
+ (*ppcspace)->params.a = pciea;
+ return 0;
+}
+
+int
+gs_cspace_build_CIEABC(gs_color_space ** ppcspace, void *client_data,
+ gs_memory_t * pmem)
+{
+ gs_cie_abc *pabc =
+ build_cie_space(ppcspace, &gs_color_space_type_CIEABC, &st_cie_abc,
+ pmem);
+
+ if (pabc == 0)
+ return_error(gs_error_VMerror);
+
+ set_cie_abc_defaults(pabc, client_data);
+ pabc->common.install_cspace = gx_install_CIEABC;
+
+ (*ppcspace)->params.abc = pabc;
+ return 0;
+}
+
+int
+gs_cspace_build_CIEDEF(gs_color_space ** ppcspace, void *client_data,
+ gs_memory_t * pmem)
+{
+ gs_cie_def *pdef =
+ build_cie_space(ppcspace, &gs_color_space_type_CIEDEF, &st_cie_def,
+ pmem);
+
+ if (pdef == 0)
+ return_error(gs_error_VMerror);
+
+ set_cie_abc_defaults((gs_cie_abc *) pdef, client_data);
+ pdef->common.install_cspace = gx_install_CIEDEF;
+ pdef->RangeDEF = Range3_default;
+ pdef->DecodeDEF = DecodeDEF_default;
+ pdef->RangeHIJ = Range3_default;
+ set_ctbl_defaults(&pdef->Table, 3);
+
+ (*ppcspace)->params.def = pdef;
+ return 0;
+}
+
+int
+gs_cspace_build_CIEDEFG(gs_color_space ** ppcspace, void *client_data,
+ gs_memory_t * pmem)
+{
+ gs_cie_defg *pdefg =
+ build_cie_space(ppcspace, &gs_color_space_type_CIEDEFG, &st_cie_defg,
+ pmem);
+
+ if (pdefg == 0)
+ return_error(gs_error_VMerror);
+
+ set_cie_abc_defaults((gs_cie_abc *) pdefg, client_data);
+ pdefg->common.install_cspace = gx_install_CIEDEFG;
+ pdefg->RangeDEFG = Range4_default;
+ pdefg->DecodeDEFG = DecodeDEFG_default;
+ pdefg->RangeHIJK = Range4_default;
+ set_ctbl_defaults(&pdefg->Table, 4);
+
+ (*ppcspace)->params.defg = pdefg;
+ return 0;
+}
+
+/* ------ Accessors ------ */
+
+int
+gs_cie_defx_set_lookup_table(gs_color_space * pcspace, int *pdims,
+ const gs_const_string * ptable)
+{
+ gx_color_lookup_table *plktblp;
+
+ switch (gs_color_space_get_index(pcspace)) {
+ case gs_color_space_index_CIEDEF:
+ plktblp = &pcspace->params.def->Table;
+ break;
+ case gs_color_space_index_CIEDEFG:
+ plktblp = &pcspace->params.defg->Table;
+ plktblp->dims[3] = pdims[3];
+ break;
+ default:
+ return_error(gs_error_rangecheck);
+ }
+
+ plktblp->dims[0] = pdims[0];
+ plktblp->dims[1] = pdims[1];
+ plktblp->dims[2] = pdims[2];
+ plktblp->table = ptable;
+ return 0;
+}
diff --git a/pstoraster/gscsel.h b/pstoraster/gscsel.h
new file mode 100644
index 000000000..7609b84bb
--- /dev/null
+++ b/pstoraster/gscsel.h
@@ -0,0 +1,44 @@
+/* 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 supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Definitions for color operand selection */
+
+#ifndef gscsel_INCLUDED
+# define gscsel_INCLUDED
+
+/*
+ * Define whether we are mapping a "source" or a "texture" color for
+ * RasterOp. Right the source and texture only have separate halftone
+ * phases in the graphics state, but someday they might have more.
+ */
+typedef enum {
+ gs_color_select_all = -1, /* for setting only, not for reading */
+ gs_color_select_texture = 0, /* 0 is the one is used for currenthtphase */
+ gs_color_select_source = 1
+} gs_color_select_t;
+
+#define gs_color_select_count 2
+
+#endif /* gscsel_INCLUDED */
diff --git a/pstoraster/gscsepnm.h b/pstoraster/gscsepnm.h
new file mode 100644
index 000000000..7d59e2097
--- /dev/null
+++ b/pstoraster/gscsepnm.h
@@ -0,0 +1,54 @@
+/* Copyright (C) 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Standard color space separation names */
+
+#ifndef gscsepnm_INCLUDED
+# define gscsepnm_INCLUDED
+
+/*
+ * Define enumeration indices for the standard separation names, and the
+ * corresponding name strings. These are only used internally: in all
+ * externally accessible APIs, separations are defined either by a string
+ * name or by an opaque identifier.
+ *
+ * NB: the enumeration and the list of strings must be synchronized. */
+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"
+
+#endif /* gscsepnm_INCLUDED */
diff --git a/pstoraster/gscsepr.c b/pstoraster/gscsepr.c
new file mode 100644
index 000000000..c03aae04c
--- /dev/null
+++ b/pstoraster/gscsepr.c
@@ -0,0 +1,352 @@
+/* Copyright (C) 1994, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Separation color space and operation definition */
+#include "gx.h"
+#include "gserrors.h"
+#include "gsrefct.h"
+#include "gsmatrix.h" /* for gscolor2.h */
+#include "gscsepr.h"
+#include "gxcspace.h"
+#include "gxfixed.h" /* for gxcolor2.h */
+#include "gxcolor2.h" /* for gs_indexed_map */
+#include "gzstate.h" /* for pgs->overprint */
+
+/* ---------------- Color space ---------------- */
+
+gs_private_st_composite(st_color_space_Separation, gs_paint_color_space,
+ "gs_color_space_Separation",
+ cs_Separation_enum_ptrs, cs_Separation_reloc_ptrs);
+
+/* Define the Separation color space type. */
+private cs_proc_base_space(gx_alt_space_Separation);
+private cs_proc_init_color(gx_init_Separation);
+private cs_proc_concrete_space(gx_concrete_space_Separation);
+private cs_proc_concretize_color(gx_concretize_Separation);
+private cs_proc_remap_concrete_color(gx_remap_concrete_Separation);
+private cs_proc_install_cspace(gx_install_Separation);
+private cs_proc_adjust_cspace_count(gx_adjust_cspace_Separation);
+const gs_color_space_type gs_color_space_type_Separation = {
+ gs_color_space_index_Separation, true, false,
+ &st_color_space_Separation, gx_num_components_1,
+ gx_alt_space_Separation,
+ gx_init_Separation, gx_restrict01_paint_1,
+ 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
+};
+
+/* GC procedures */
+
+#define pcs ((gs_color_space *)vptr)
+
+private
+ENUM_PTRS_BEGIN(cs_Separation_enum_ptrs)
+{
+ return ENUM_USING(*pcs->params.separation.alt_space.type->stype,
+ &pcs->params.separation.alt_space,
+ sizeof(pcs->params.separation.alt_space), index - 1);
+}
+ENUM_PTR(0, gs_color_space, params.separation.map);
+ENUM_PTRS_END
+private RELOC_PTRS_BEGIN(cs_Separation_reloc_ptrs)
+{
+ RELOC_PTR(gs_color_space, params.separation.map);
+ RELOC_USING(*pcs->params.separation.alt_space.type->stype,
+ &pcs->params.separation.alt_space,
+ sizeof(gs_base_color_space));
+}
+RELOC_PTRS_END
+
+#undef pcs
+
+/* Get the alternate space for a Separation space. */
+private const gs_color_space *
+gx_alt_space_Separation(const gs_color_space * pcs)
+{
+ return (const gs_color_space *)&(pcs->params.separation.alt_space);
+}
+
+/* Get the concrete space for a Separation space. */
+/* (We don't support concrete Separation spaces yet.) */
+private const gs_color_space *
+gx_concrete_space_Separation(const gs_color_space * pcs,
+ const gs_imager_state * pis)
+{
+ const gs_color_space *pacs =
+ (const gs_color_space *)&pcs->params.separation.alt_space;
+
+ return cs_concrete_space(pacs, pis);
+}
+
+/* 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, int delta)
+{
+ rc_adjust_const(pcs->params.separation.map, delta,
+ "gx_adjust_Separation");
+ (*pcs->params.separation.alt_space.type->adjust_cspace_count)
+ ((const gs_color_space *)&pcs->params.separation.alt_space, delta);
+}
+
+/* ------ Constructors/accessors ------ */
+
+/*
+ * The default separation tint transformation function. This will just return
+ * the information in the cache or, if the cache is of zero size, set all
+ * components in the alternative color space to 0.
+ *
+ * No special cases are provided for this routine, as the use of separations
+ * (particular in this form) is sufficiently rare to not have a significant
+ * performance impact.
+ */
+private int
+map_tint_value(const gs_separation_params * pcssepr, floatp in_val,
+ float *out_vals)
+{
+ int ncomps =
+ cs_num_components((const gs_color_space *)&pcssepr->alt_space);
+ int nentries = pcssepr->map->num_values / ncomps;
+ int indx;
+ const float *pv = pcssepr->map->values;
+ int i;
+
+ if (nentries == 0) {
+ for (i = 0; i < ncomps; i++)
+ out_vals[i] = 0.0;
+ return 0;
+ }
+ if (in_val > 1)
+ indx = nentries - 1;
+ else if (in_val <= 0)
+ indx = 0;
+ else
+ indx = (int)(in_val * nentries + 0.5);
+ pv += indx * ncomps;
+
+ for (i = 0; i < ncomps; i++)
+ out_vals[i] = pv[i];
+ return 0;
+}
+
+/*
+ * Allocate the indexed map required by a separation color space.
+ */
+private gs_indexed_map *
+alloc_separation_map(const gs_color_space * palt_cspace, int cache_size,
+ gs_memory_t * pmem)
+{
+ gs_indexed_map *pimap;
+
+ rc_alloc_struct_1(pimap, gs_indexed_map, &st_indexed_map, pmem,
+ return 0,
+ "gs_cspace_build_Separation"
+ );
+ pimap->rc.free = free_indexed_map;
+ pimap->proc.tint_transform = map_tint_value;
+
+ if (cache_size != 0) {
+ int num_comps = gs_color_space_num_components(palt_cspace);
+
+ cache_size *= num_comps;
+ pimap->num_values = cache_size;
+ pimap->values =
+ (float *)gs_alloc_byte_array(pmem, cache_size, sizeof(float),
+ "gs_cspace_build_Separation"
+ );
+
+ if (pimap->values == 0)
+ rc_decrement(pimap, "gs_cspace_build_Separation"); /* sets pimap = 0 */
+
+ } else {
+ pimap->num_values = 0;
+ pimap->values = 0;
+ }
+ return pimap;
+}
+
+/*
+ * Build a separation color space.
+ *
+ * The values array provided with separation color spaces is actually cached
+ * information, but filled in by the client. The alternative space is the
+ * color space in which the tint procedure will provide alternative colors.
+ */
+int
+gs_cspace_build_Separation(
+ gs_color_space ** ppcspace,
+ gs_separation_name sname,
+ const gs_color_space * palt_cspace,
+ int cache_size,
+ gs_memory_t * pmem
+)
+{
+ gs_color_space *pcspace = 0;
+ gs_separation_params *pcssepr = 0;
+ int code;
+
+ if (palt_cspace == 0 || !palt_cspace->type->can_be_alt_space)
+ return_error(gs_error_rangecheck);
+
+ code = gs_cspace_alloc(&pcspace, &gs_color_space_type_Separation, pmem);
+ if (code < 0)
+ return code;
+ pcssepr = &pcspace->params.separation;
+ pcssepr->map = alloc_separation_map(palt_cspace, cache_size, pmem);
+ if (pcssepr->map == 0) {
+ gs_free_object(pmem, pcspace, "gs_cspace_build_Separation");
+ return_error(gs_error_VMerror);
+ }
+ pcssepr->sname = sname;
+ gs_cspace_init_from((gs_color_space *) & pcssepr->alt_space, palt_cspace);
+ *ppcspace = pcspace;
+ return 0;
+}
+
+/*
+ * Get the cached value array for a separation color space. This will return
+ * a null pointer if the color space is not a separation color space, or if
+ * the separation color space has a cache size of 0.
+ */
+float *
+gs_cspace_get_separation_value_array(const gs_color_space * pcspace)
+{
+ if (gs_color_space_get_index(pcspace) != gs_color_space_index_Separation)
+ return 0;
+ return pcspace->params.separation.map->values;
+}
+
+/*
+ * Set the tint transformation procedure used by a Separation color space.
+ */
+int
+gs_cspace_set_tint_transform_proc(gs_color_space * pcspace,
+ int (*proc) (P3(const gs_separation_params *, floatp, float *)))
+{
+ if (gs_color_space_get_index(pcspace) != gs_color_space_index_Separation)
+ return_error(gs_error_rangecheck);
+ pcspace->params.separation.map->proc.tint_transform = proc;
+ return 0;
+}
+
+/* ---------------- Graphics state ---------------- */
+
+/* setoverprint */
+void
+gs_setoverprint(gs_state * pgs, bool ovp)
+{
+ pgs->overprint = ovp;
+}
+
+/* currentoverprint */
+bool
+gs_currentoverprint(const gs_state * pgs)
+{
+ return pgs->overprint;
+}
+
+/* ------ Internal procedures ------ */
+
+/* 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 int
+gx_concretize_Separation(const gs_client_color * pc, const gs_color_space * pcs,
+ frac * pconc, const gs_imager_state * pis)
+{
+ 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, pis);
+}
+
+private int
+gx_remap_concrete_Separation(const frac * pconc,
+ gx_device_color * pdc, const gs_imager_state * pis, gx_device * dev,
+ gs_color_select_t select)
+{ /* We don't support concrete Separation colors yet. */
+ return_error(gs_error_rangecheck);
+}
+
+/* ---------------- 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/gscsepr.h b/pstoraster/gscsepr.h
new file mode 100644
index 000000000..b719c60ca
--- /dev/null
+++ b/pstoraster/gscsepr.h
@@ -0,0 +1,74 @@
+/* Copyright (C) 1992, 1993, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Client interface to Separation color */
+
+#ifndef gscsepr_INCLUDED
+# define gscsepr_INCLUDED
+
+#include "gscspace.h"
+
+/* Graphics state */
+bool gs_currentoverprint(P1(const gs_state *));
+void gs_setoverprint(P2(gs_state *, bool));
+
+/*
+ * Separation color spaces.
+ *
+ * The API for creating Separation color space objects exposes the fact that
+ * they normally cache the results of sampling the tint_transform procedure,
+ * and use the cache to convert colors when necessary. When a language
+ * interpreter sets up a Separation space, it may either provide a
+ * tint_tranform procedure that will be called each time (specifying the
+ * cache size as 0), or it may fill in the cache directly and provide a
+ * dummy procedure.
+ *
+ * By default, the tint transformation procedure will simple return the
+ * entries in the cache. If this function is called when the cache size is
+ * 0, all color components in the alternative color space will be set to 0.
+ */
+extern int gs_cspace_build_Separation(
+ gs_color_space ** ppcspace,
+ gs_separation_name sname,
+ const gs_color_space * palt_cspace,
+ int cache_size,
+ gs_memory_t * pmem
+);
+
+/* Get the cached value array for a Separation color space. */
+extern float *gs_separation_value_array(P1(
+ const gs_color_space * pcspace
+ ));
+
+/* Set the tint transformation procedure for a separation color space. */
+extern int gs_cspace_set_tint_transform_proc(P2(
+ gs_color_space * pcspace,
+ int (*proc) (P3(const gs_separation_params *,
+ floatp,
+ float *
+ ))
+ ));
+
+#endif /* gscsepr_INCLUDED */
diff --git a/pstoraster/gscspace.c b/pstoraster/gscspace.c
new file mode 100644
index 000000000..16f52bf92
--- /dev/null
+++ b/pstoraster/gscspace.c
@@ -0,0 +1,237 @@
+/* Copyright (C) 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Color space operators and support */
+#include "memory_.h"
+#include "gx.h"
+#include "gserrors.h"
+#include "gsstruct.h"
+#include "gsccolor.h"
+#include "gxcspace.h"
+#include "gxistate.h"
+
+/* 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, true, true,
+ &st_base_color_space, gx_num_components_1,
+ gx_no_base_space,
+ gx_init_paint_1, gx_restrict01_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
+};
+const gs_color_space_type gs_color_space_type_DeviceRGB = {
+ gs_color_space_index_DeviceRGB, true, true,
+ &st_base_color_space, gx_num_components_3,
+ gx_no_base_space,
+ gx_init_paint_3, gx_restrict01_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
+};
+
+/* Structure descriptors */
+public_st_color_space();
+public_st_base_color_space();
+
+/* Return the shared instances of the color spaces. */
+const gs_color_space *
+gs_cspace_DeviceGray(const gs_imager_state * pis)
+{
+ return gs_imager_state_shared(pis, cs_DeviceGray);
+}
+const gs_color_space *
+gs_cspace_DeviceRGB(const gs_imager_state * pis)
+{
+ return gs_imager_state_shared(pis, cs_DeviceRGB);
+}
+const gs_color_space *
+gs_cspace_DeviceCMYK(const gs_imager_state * pis)
+{
+ return gs_imager_state_shared(pis, cs_DeviceCMYK);
+}
+
+/* ------ Create/copy/destroy ------ */
+
+int
+gs_cspace_alloc(gs_color_space ** ppcspace,
+ const gs_color_space_type * pcstype,
+ gs_memory_t * mem)
+{
+ gs_color_space *pcspace =
+ gs_alloc_struct(mem, gs_color_space, &st_color_space,
+ "gs_cspace_alloc");
+
+ if (pcspace == 0)
+ return_error(gs_error_VMerror);
+ pcspace->pmem = mem;
+ pcspace->type = pcstype;
+ *ppcspace = pcspace;
+ return 0;
+}
+
+int
+gs_cspace_build_DeviceGray(gs_color_space ** ppcspace, gs_memory_t * pmem)
+{
+ return gs_cspace_alloc(ppcspace, &gs_color_space_type_DeviceGray, pmem);
+}
+int
+gs_cspace_build_DeviceRGB(gs_color_space ** ppcspace, gs_memory_t * pmem)
+{
+ return gs_cspace_alloc(ppcspace, &gs_color_space_type_DeviceRGB, pmem);
+}
+int
+gs_cspace_build_DeviceCMYK(gs_color_space ** ppcspace, gs_memory_t * pmem)
+{
+ return gs_cspace_alloc(ppcspace, &gs_color_space_type_DeviceCMYK, pmem);
+}
+
+/*
+ * Copy just enough of a color space object. This will do the right thing
+ * for copying color spaces into the base or alternate color space of a
+ * compound color space when legal, but it can't check that the operation is
+ * actually legal.
+ */
+inline private void
+cs_copy(gs_color_space *pcsto, const gs_color_space *pcsfrom)
+{
+ memcpy(pcsto, pcsfrom, pcsfrom->type->stype->ssize);
+}
+
+/* Copy a color space into one newly allocated by the caller. */
+void
+gs_cspace_init_from(gs_color_space * pcsto, const gs_color_space * pcsfrom)
+{
+ cs_copy(pcsto, pcsfrom);
+ (*pcsto->type->adjust_cspace_count)(pcsto, 1);
+}
+
+/* Assign a color space into a previously initialized one. */
+void
+gs_cspace_assign(gs_color_space * pdest, const gs_color_space * psrc)
+{
+ /* check for a = a */
+ if (pdest == psrc)
+ return;
+ (*psrc->type->adjust_cspace_count)(psrc, 1);
+ (*pdest->type->adjust_cspace_count)(pdest, -1);
+ cs_copy(pdest, psrc);
+}
+
+
+/* Prepare to free a color space. */
+void
+gs_cspace_release(gs_color_space * pcs)
+{
+ (*pcs->type->adjust_cspace_count)(pcs, -1);
+}
+
+/* ------ Accessors ------ */
+
+/* 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 cs_num_components(pcs);
+}
+
+int
+gx_num_components_1(const gs_color_space * pcs)
+{
+ return 1;
+}
+int
+gx_num_components_3(const gs_color_space * pcs)
+{
+ return 3;
+}
+int
+gx_num_components_4(const gs_color_space * pcs)
+{
+ return 4;
+}
+
+/*
+ * For color spaces that have a base or alternative color space, return that
+ * color space. Otherwise return null.
+ */
+const gs_color_space *
+gs_cspace_base_space(const gs_color_space * pcspace)
+{
+ return cs_base_space(pcspace);
+}
+
+const gs_color_space *
+gx_no_base_space(const gs_color_space * pcspace)
+{
+ return NULL;
+}
+
+/* ------ Other implementation procedures ------ */
+
+/* Null color space installation procedure. */
+int
+gx_no_install_cspace(gs_color_space * pcs, gs_state * pgs)
+{
+ return 0;
+}
+
+/* Null reference count adjustment procedure. */
+void
+gx_no_adjust_cspace_count(const gs_color_space * pcs, int delta)
+{
+}
+
+/* GC procedures */
+
+#define pcs ((gs_color_space *)vptr)
+private
+ENUM_PTRS_BEGIN_PROC(color_space_enum_ptrs)
+{
+ return ENUM_USING(*pcs->type->stype, vptr, size, index);
+ ENUM_PTRS_END_PROC
+}
+private
+RELOC_PTRS_BEGIN(color_space_reloc_ptrs)
+{
+ RELOC_USING(*pcs->type->stype, vptr, size);
+}
+RELOC_PTRS_END
+#undef pcs
diff --git a/pstoraster/gscspace.h b/pstoraster/gscspace.h
new file mode 100644
index 000000000..ce66513a0
--- /dev/null
+++ b/pstoraster/gscspace.h
@@ -0,0 +1,400 @@
+/* Copyright (C) 1991, 1995, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Client interface to color spaces */
+
+#ifndef gscspace_INCLUDED
+# define gscspace_INCLUDED
+
+#include "gsmemory.h"
+
+/*
+ * The handling of color spaces in the graphic library is somewhat
+ * awkward because of historical artifacts.
+ *
+ * The PostScript Level 1 (DeviceGray/RGB) color spaces, and the "Level
+ * 1 1/2" DeviceCMYK space, have no associated parameters. Therefore,
+ * they can be represented as simple objects (just a pointer to a type
+ * vector containing procedures and parameters). This was the original
+ * design.
+ *
+ * PostScript Level 2 and LanguageLevel 3 add two new kinds of color spaces:
+ * color spaces with parameters (CIEBased and DevicePixel spaces), and
+ * compound color spaces (Indexed, Separation, Pattern, and DeviceN spaces),
+ * which parameters that include specifying an alternate or underlying color
+ * space. To handle these spaces, we extended the original design to store
+ * scalar parameters (i.e., parameters other than the complex color
+ * transformation data for CIEBased spaces, the lookup table for Indexed
+ * spaces, and the list of component names for DeviceN spaces) in-line in
+ * the color space object. For compound spaces, this requires storing a
+ * color space in-line inside another color space, which is clearly
+ * impossible. Therefore, we defined a generality hierarchy for color
+ * spaces:
+ *
+ * - Base spaces (DeviceGray/RGB/CMYK/Pixel and CIEBased),
+ * whose parameters (if any) don't include other color spaces.
+ *
+ * - Direct spaces (base spaces + Separation and DeviceN), which
+ * may have a base space as an alternative space.
+ *
+ * - Paint spaces (direct spaces + Indexed), which may have a
+ * direct space as the underlying space.
+ *
+ * - General spaces (paint spaces + Pattern), which may have a
+ * paint space as the underlying space.
+ *
+ * With this approach, a general space can include a paint space stored
+ * in-line; a paint space (either in its own right or as the underlying
+ * space of a Pattern space) can include a direct space in-line; and a
+ * direct space can include a base space in-line.
+ *
+ * Note that because general, paint, direct, and base spaces are
+ * (necessarily) of different sizes, assigning (copying the top object of)
+ * color spaces must take into account the actual size of the color space
+ * being assigned. In principle, this also requires checking that the
+ * source object will actually fit into the destination. Currently we rely
+ * on the caller to ensure that this is the case; in fact, the current API
+ * (gs_cspace_init and gs_cspace_assign) doesn't even provide enough
+ * information to make the check.
+ *
+ * In retrospect, we might have gotten a simpler design without significant
+ * performance loss by always referencing underlying and alternate spaces
+ * through a pointer; however, at this point, the cost and risk of changing
+ * to such a design are too high.
+ *
+ * There are two aspects to memory management for color spaces: managing
+ * the color space objects themselves, and managing the non-scalar
+ * parameters that they reference (if any).
+ *
+ * - Color space objects per se have no special management properties:
+ * they can be allocated on the stack or on the heap, and freed by
+ * scope exit, explicit deallocation, or garbage collection.
+ *
+ * - Separately allocated (non-scalar) color space parameters are
+ * managed by reference counting. Currently we do this for the
+ * CIEBased spaces, and for the first-level parameters of Indexed and
+ * Separation spaces: clients must deal with deallocating the other
+ * parameter structures mentioned above, including the Indexed lookup
+ * table if any. This is clearly not a good situation, but we don't
+ * envision fixing it any time soon.
+ *
+ * Here is the information associated with the various color space
+ * structures. Note that DevicePixel, DeviceN, and the ability to use
+ * Separation or DeviceN spaces as the base space of an Indexed space
+ * are LanguageLevel 3 additions. Unfortunately, the terminology for the
+ * different levels of generality is confusing and inconsistent for
+ * historical reasons.
+ *
+ * For base spaces:
+ *
+ * Space Space parameters Color parameters
+ * ----- ---------------- ----------------
+ * DeviceGray (none) 1 real [0-1]
+ * DeviceRGB (none) 3 reals [0-1]
+ * DeviceCMYK (none) 4 reals [0-1]
+ * DevicePixel depth 1 int [up to depth bits]
+ * CIEBasedDEFG dictionary 4 reals
+ * CIEBasedDEF dictionary 3 reals
+ * CIEBasedABC dictionary 3 reals
+ * CIEBasedA dictionary 1 real
+ *
+ * For non-base direct spaces:
+ *
+ * Space Space parameters Color parameters
+ * ----- ---------------- ----------------
+ *
+ * Separation name, alt_space, tint_xform 1 real [0-1]
+ * DeviceN names, alt_space, tint_xform N reals
+ *
+ * For non-direct paint spaces:
+ *
+ * Space Space parameters Color parameters
+ * ----- ---------------- ----------------
+ * Indexed base_space, hival, lookup 1 int [0-hival]
+ *
+ * For non-paint spaces:
+ *
+ * Space Space parameters Color parameters
+ * ----- ---------------- ----------------
+ * Pattern colored: (none) dictionary
+ * uncolored: base_space dictionary + base space params */
+
+/* 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 and above */
+ gs_color_space_index_DeviceCMYK,
+
+ /* Supported in LanguageLevel 3 only */
+ gs_color_space_index_DevicePixel,
+ gs_color_space_index_DeviceN,
+
+ /* Supported in Level 2 and above 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 an abstract type for color space types (method structures). */
+typedef struct gs_color_space_type_s gs_color_space_type;
+
+/*
+ * The common part of all color spaces. This structure now includes a memory
+ * structure pointer, so that it may be released without providing this
+ * information separately. (type is a pointer to the structure of methods.)
+ *
+ * Note that all color space structures consist of the basic information and
+ * a union containing some additional information. The macro operand is that
+ * union.
+ */
+#define gs_cspace_common(param_union) \
+ const gs_color_space_type * type; \
+ gs_memory_t * pmem; \
+ union { \
+ param_union; \
+ } params
+
+/*
+ * Parameters for base color spaces. Of the base color spaces, only
+ * DevicePixel and CIE spaces have parameters: see gscie.h for the structure
+ * definitions for CIE space parameters.
+ */
+typedef struct gs_device_pixel_params_s {
+ int depth;
+} gs_device_pixel_params;
+typedef struct gs_cie_a_s gs_cie_a;
+typedef struct gs_cie_abc_s gs_cie_abc;
+typedef struct gs_cie_def_s gs_cie_def;
+typedef struct gs_cie_defg_s gs_cie_defg;
+
+#define gs_base_cspace_params \
+ gs_device_pixel_params pixel; \
+ gs_cie_defg * defg; \
+ gs_cie_def * def; \
+ gs_cie_abc * abc; \
+ gs_cie_a * a
+
+typedef struct gs_base_color_space_s {
+ gs_cspace_common(gs_base_cspace_params);
+} gs_base_color_space;
+
+#define gs_base_color_space_size sizeof(gs_base_color_space)
+
+/*
+ * Non-base direct color spaces: Separation and DeviceN spaces.
+ * These include a base alternative color space.
+ */
+typedef ulong gs_separation_name; /* BOGUS */
+typedef struct gs_indexed_map_s gs_indexed_map;
+
+typedef struct gs_separation_params_s {
+ gs_separation_name sname;
+ gs_base_color_space alt_space;
+ gs_indexed_map *map;
+} gs_separation_params;
+
+typedef struct gs_device_n_params_s gs_device_n_params;
+struct gs_device_n_params_s {
+ gs_separation_name *names;
+ uint num_components;
+ gs_base_color_space alt_space;
+ int (*tint_transform)
+ (P4(const gs_device_n_params * params, const float *in, float *out,
+ void *data));
+ void *tint_transform_data;
+};
+
+#define gs_direct_cspace_params \
+ gs_base_cspace_params; \
+ gs_separation_params separation; \
+ gs_device_n_params device_n
+
+typedef struct gs_direct_color_space_s {
+ gs_cspace_common(gs_direct_cspace_params);
+} gs_direct_color_space;
+
+#define gs_direct_color_space_size sizeof(gs_direct_color_space)
+
+/*
+ * Non-direct paint space: Indexed space.
+ *
+ * Note that for indexed color spaces, hival is the highest support index,
+ * which is one less than the number of entries in the palette (as defined
+ * in PostScript).
+ */
+
+typedef struct gs_indexed_params_s {
+ gs_direct_color_space base_space;
+ int hival; /* num_entries - 1 */
+ union {
+ gs_const_string table; /* size is implicit */
+ gs_indexed_map *map;
+ } lookup;
+ bool use_proc; /* 0 = use table, 1 = use proc & map */
+} gs_indexed_params;
+
+#define gs_paint_cspace_params \
+ gs_direct_cspace_params; \
+ gs_indexed_params indexed
+
+typedef struct gs_paint_color_space_s {
+ gs_cspace_common(gs_paint_cspace_params);
+} gs_paint_color_space;
+
+#define gs_paint_color_space_size sizeof(gs_paint_color_space)
+
+/*
+ * Pattern parameter set. This may contain an instances of a paintable
+ * color space. The boolean indicates if this is the case.
+ */
+typedef struct gs_pattern_params_s {
+ bool has_base_space;
+ gs_paint_color_space base_space;
+} gs_pattern_params;
+
+/*
+ * Fully general color spaces.
+ */
+struct gs_color_space_s {
+ gs_cspace_common(
+ gs_paint_cspace_params;
+ gs_pattern_params pattern
+ );
+};
+
+#define gs_pattern_color_space_size sizeof(gs_color_space)
+
+/*
+ * 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
+
+ /*extern_st(st_color_space); *//* in gxcspace.h */
+#define public_st_color_space() /* in gscspace.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 ---------------- */
+
+/* ------ Create/copy/destroy ------ */
+
+/*
+ * Note that many of the constructors take no parameters, and the
+ * remainder take only a few (CIE color spaces constructures take a
+ * client data pointer as an operand, the composite color space (Separation,
+ * Indexed, and Pattern) constructurs take the base space as an operand,
+ * and the Indexed color space constructors have a few additiona operands).
+ * This is done to conserve memory. If initialization values for all the
+ * color space parameters were provided to the constructors, these values
+ * would need to have some fairly generic format. Different clients gather
+ * this data in different forms, so they would need to allocate memory to
+ * convert it to the generic form, only to immediately release this memory
+ * once the construction is complete.
+ *
+ * The alternative approach employed here is to provide a number of access
+ * methods (macros) that return pointers to individual fields of the
+ * various color space structures. This requires exporting only fairly simple
+ * data structures, and so does not violate modularity too severely.
+ *
+ * NB: Of necessity, the macros provide access to modifiable structures. If
+ * these structures are modified after the color space object is first
+ * initialized, unpredictable and, most likely, undesirable results will
+ * occur.
+ *
+ * The constructors will return an integer, which is 0 on success and an
+ * error code on failure (gs_error_VMerror or gs_error_rangecheck).
+ */
+
+extern int
+ gs_cspace_build_DeviceGray(P2(gs_color_space ** ppcspace,
+ gs_memory_t * pmem)),
+ gs_cspace_build_DeviceRGB(P2(gs_color_space ** ppcspace,
+ gs_memory_t * pmem)),
+ gs_cspace_build_DeviceCMYK(P2(gs_color_space ** ppcspace,
+ gs_memory_t * pmem));
+
+/*
+ * We preallocate instances of the 3 device color spaces, and provide
+ * procedures that return them. Note that gs_cspace_DeviceCMYK() is
+ * defined even if CMYK color support is not included in this configuration.
+ */
+#ifndef gs_imager_state_DEFINED
+# define gs_imager_state_DEFINED
+typedef struct gs_imager_state_s gs_imager_state;
+#endif
+
+const gs_color_space * gs_cspace_DeviceGray(P1(const gs_imager_state * pis));
+const gs_color_space * gs_cspace_DeviceRGB(P1(const gs_imager_state * pis));
+const gs_color_space * gs_cspace_DeviceCMYK(P1(const gs_imager_state * pis));
+
+/* Copy a color space into one newly allocated by the caller. */
+void gs_cspace_init_from(P2(gs_color_space * pcsto,
+ const gs_color_space * pcsfrom));
+
+/* Assign a color space into a previously initialized one. */
+void cs_cspace_assign(P2(gs_color_space * pdest, const gs_color_space * psrc));
+
+/* Prepare to free a color space. */
+void gs_cspace_release(P1(gs_color_space * pcs));
+
+/* ------ Accessors ------ */
+
+/* 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 or uncolored Pattern color space, or the
+ * alternate space of a Separation or DeviceN space. Return NULL if the
+ * color space does not have a base/alternative color space.
+ */
+const gs_color_space *gs_cspace_base_space(P1(const gs_color_space * pcspace));
+
+/* backwards compatibility */
+#define gs_color_space_indexed_base_space(pcspace)\
+ gs_cspace_base_space(pcspace)
+
+#endif /* gscspace_INCLUDED */
diff --git a/pstoraster/gsdcolor.h b/pstoraster/gsdcolor.h
new file mode 100644
index 000000000..3e0fae30d
--- /dev/null
+++ b/pstoraster/gsdcolor.h
@@ -0,0 +1,332 @@
+/* Copyright (C) 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Device color representation for drivers */
+
+#ifndef gsdcolor_INCLUDED
+# define gsdcolor_INCLUDED
+
+#include "gsccolor.h"
+#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])
+
+/* Accessing a colored halftone. */
+#define gx_dc_is_colored_halftone(pdc)\
+ ((pdc)->type == gx_dc_type_ht_colored)
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * 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 gx_dc_is_null(pdc)\
+ ((pdc)->type == gx_dc_type_null)
+#define color_is_null(pdc) gx_dc_is_null(pdc)
+#define color_set_null(pdc)\
+ ((pdc)->type = gx_dc_type_null)
+
+#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) gx_dc_is_colored_halftone(pdc)
+#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)
+/* Some special clients set the individual components separately. */
+#define color_finish_set_cmyk_halftone(pdc, ht)\
+ ((pdc)->colors.colored.c_ht = (ht),\
+ (pdc)->colors.colored.alpha = max_ushort,\
+ (pdc)->type = gx_dc_type_ht_colored)
+#define color_set_cmyk_halftone(pdc, ht, bc, lc, bm, lm, by, ly, bk, lk)\
+ (_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),\
+ color_finish_set_cmyk_halftone(pdc, ht))
+
+/* Note that color_set_null_pattern doesn't set mask.ccolor. */
+#define color_set_null_pattern(pdc)\
+ ((pdc)->mask.id = gx_no_bitmap_id,\
+ (pdc)->mask.m_tile = 0,\
+ (pdc)->colors.pattern.p_tile = 0,\
+ (pdc)->type = gx_dc_type_pattern)
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * 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
+
+#ifndef gx_color_tile_DEFINED
+# define gx_color_tile_DEFINED
+typedef struct gx_color_tile_s gx_color_tile;
+
+#endif
+
+/*
+ * 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):
+ * (mask is 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
+ * size.
+ *
+ * The mask elements of a device color are only used for patterns:
+ * Non-pattern:
+ * mask is unused.
+ * Pattern:
+ * mask.ccolor gives the original Pattern color (needed for
+ * reloading the pattern cache);
+ * mask.id gives the ID of the pattern (and its mask);
+ * mask.m_phase holds the negative of the graphics state
+ * halftone phase;
+ * @ mask.m_tile 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.m_tile 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.
+ */
+
+/* Define the (opaque) type for device color types. */
+/* The name is an unfortunate anachronism. */
+typedef struct gx_device_color_type_s gx_device_color_type_t;
+typedef const gx_device_color_type_t *gx_device_color_type;
+
+struct gx_device_color_s {
+ /*
+ * Since some compilers don't allow static initialization of a
+ * union, we put the type first.
+ */
+ gx_device_color_type type;
+ /*
+ * 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;
+ struct _mask {
+ gs_client_color ccolor; /* needed for remapping pattern */
+ struct mp_ {
+ short x, y;
+ } m_phase;
+ gx_bitmap_id id;
+ gx_color_tile *m_tile;
+ } mask;
+};
+
+/*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_type_t *const gx_dc_type_none; /* gxdcolor.c */
+
+#endif
+#ifndef gx_dc_type_null
+extern const gx_device_color_type_t *const gx_dc_type_null; /* gxdcolor.c */
+
+#endif
+#ifndef gx_dc_type_pure
+extern const gx_device_color_type_t *const 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_type_t * const gx_dc_type_pattern; *//* gspcolor.c */
+#endif
+#ifndef gx_dc_type_ht_binary
+extern const gx_device_color_type_t *const gx_dc_type_ht_binary; /* gxht.c */
+
+#endif
+#ifndef gx_dc_type_ht_colored
+extern const gx_device_color_type_t *const 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..b32653f37
--- /dev/null
+++ b/pstoraster/gsdevice.c
@@ -0,0 +1,570 @@
+/*
+ Copyright 1993-2000 by Easy Software Products.
+ Copyright 1989, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Device operators for Ghostscript library */
+#include "ctype_.h"
+#include "memory_.h" /* for memcpy */
+#include "string_.h"
+#include "gx.h"
+#include "gscdefs.h" /* for gs_lib_device_list */
+#include "gserrors.h"
+#include "gp.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:
+ENUM_RETURN(gx_device_enum_ptr(fdev->target));
+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, gs_no_struct_enum_ptrs, gs_no_struct_reloc_ptrs, 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 RELOC_OBJ(dev); /* gcst implicit */
+}
+
+/* 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->procs = *dev->static_procs;
+ dev->static_procs = 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;
+ 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 *const *list;
+ int count = gs_lib_device_list(&list, NULL);
+
+ if (index < 0 || index >= count)
+ return 0; /* index out of range */
+ return list[index];
+}
+
+/* Fill in the GC structure descriptor for a device. */
+/* This is only called during initialization. */
+void
+gx_device_make_struct_type(gs_memory_struct_type_t *st,
+ const gx_device *dev)
+{
+ const gx_device_procs *procs = dev->static_procs;
+ bool forward = false;
+
+ /*
+ * Try to figure out whether 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
+ * the best we can come up with.
+ */
+ if (procs == 0)
+ procs = &dev->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;
+}
+
+/* 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.
+ */
+ const gx_device *const *list;
+ gs_memory_struct_type_t *st;
+ int count = gs_lib_device_list(&list, &st);
+ int i;
+
+ 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, std,
+ "gs_copydevice(unknown)");
+ goto out;
+ }
+ std = st + i;
+ }
+ new_dev = gs_alloc_struct_immovable(mem, gx_device, std,
+ "gs_copydevice");
+out:
+ if (new_dev == 0)
+ return_error(gs_error_VMerror);
+ gx_device_init(new_dev, dev, mem, false);
+ 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;
+ rc_assign(((gx_device_memory *)dev)->target, odev,
+ "set memory device(target)");
+ }
+ code = (*dev_proc(dev, open_device)) (dev);
+ if (code < 0)
+ return_error(code);
+ dev->is_open = true;
+ }
+ gs_setdevice_no_init(pgs, dev);
+ pgs->ctm_default_set = false;
+ if ((code = gs_initmatrix(pgs)) < 0 ||
+ (code = gs_initclip(pgs)) < 0
+ )
+ return code;
+ /* If we were in a charpath or a setcachedevice, */
+ /* we aren't any longer. */
+ pgs->in_cachedevice = 0;
+ pgs->in_charpath = (gs_char_path_mode) 0;
+ return (was_open ? 0 : 1);
+}
+int
+gs_setdevice_no_init(gs_state * pgs, gx_device * dev)
+{
+ /*
+ * Just set the device, possibly changing color space but no other
+ * device parameters.
+ */
+ rc_assign(pgs->device, dev, "gs_setdevice_no_init");
+ gx_set_cmap_procs((gs_imager_state *) pgs, dev);
+ gx_unset_dev_color(pgs);
+ return 0;
+}
+
+/* Initialize a just-allocated device. */
+void
+gx_device_init(gx_device * dev, const gx_device * proto, gs_memory_t * mem,
+ bool internal)
+{
+ memcpy(dev, proto, proto->params_size);
+ dev->memory = mem;
+ rc_init(dev, mem, (internal ? 0 : 1));
+}
+
+/* Make a null device. */
+void
+gs_make_null_device(gx_device_null * dev, gs_memory_t * mem)
+{
+ gx_device_init((gx_device *) dev, (const gx_device *)&gs_null_device,
+ mem, true);
+}
+
+/* Select a null device. */
+int
+gs_nulldevice(gs_state * pgs)
+{
+ if (pgs->device == 0 || !gx_device_is_null(pgs->device)) {
+ gx_device *ndev;
+ int code = gs_copydevice(&ndev, (gx_device *) & gs_null_device,
+ pgs->memory);
+
+ if (code < 0)
+ return code;
+ /*
+ * Internal devices have a reference count of 0, not 1,
+ * aside from references from graphics states.
+ */
+ rc_init(ndev, pgs->memory, 0);
+ return gs_setdevice_no_erase(pgs, ndev);
+ }
+ return 0;
+}
+
+/* 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;
+}
+
+/*
+ * Just set the device without any reinitializing.
+ * (For internal use only.)
+ */
+void
+gx_set_device_only(gs_state * pgs, gx_device * dev)
+{
+ rc_assign(pgs->device, dev, "gx_set_device_only");
+}
+
+/* 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;
+}
+
+/*
+ * Copy device parameters back from a target. This copies all standard
+ * parameters related to page size and resolution, plus color_info.
+ */
+void
+gx_device_copy_params(gx_device *to, const gx_device *from)
+{
+#define COPY_PARAM(p) to->p = from->p
+#define COPY_ARRAY_PARAM(p) memcpy(to->p, from->p, sizeof(to->p))
+ COPY_PARAM(width);
+ COPY_PARAM(height);
+ COPY_ARRAY_PARAM(MediaSize);
+ COPY_ARRAY_PARAM(ImagingBBox);
+ COPY_PARAM(ImagingBBox_set);
+ COPY_ARRAY_PARAM(HWResolution);
+ COPY_ARRAY_PARAM(MarginsHWResolution);
+ COPY_ARRAY_PARAM(Margins);
+ COPY_ARRAY_PARAM(HWMargins);
+ COPY_PARAM(color_info);
+#undef COPY_PARAM
+#undef COPY_ARRAY_PARAM
+}
+
+/* Open the output file for a device. */
+int
+gx_device_open_output_file(const gx_device * dev, const char *fname,
+ bool binary, bool positionable, FILE ** pfile)
+{
+ char pfname[gp_file_name_sizeof];
+ char pfmt[10];
+ const char *fsrc = fname;
+ char *fdest = pfname;
+ long count1 = dev->PageCount + 1;
+
+ if (!strcmp(fname, "-")) {
+ *pfile = stdout;
+ /* Force stdout to binary. */
+ return gp_setmode_binary(*pfile, true);
+ }
+/****** SHOULD RETURN rangecheck IF FILE NAME TOO LONG ******/
+ for (; *fsrc; ++fsrc) {
+ if (*fsrc != '%') {
+ *fdest++ = *fsrc;
+ continue;
+ }
+ if (fsrc[1] == '%') {
+ *fdest++ = '%';
+ ++fsrc;
+ continue;
+ } {
+ char *ffmt = pfmt;
+ bool use_long;
+
+/****** SHOULD CHECK FOR FORMAT TOO LONG ******/
+ while (!isalpha(*fsrc))
+ *ffmt++ = *fsrc++;
+ if ((use_long = (*fsrc == 'l')))
+ *ffmt++ = *fsrc++;
+ *ffmt = *fsrc;
+ ffmt[1] = 0;
+/****** SHOULD CHECK FOR LEGAL FORMATS ******/
+ sprintf(fdest, pfmt, (use_long ? count1 : (int)count1));
+ fdest += strlen(fdest);
+ }
+ }
+ *fdest = 0;
+ if (positionable && pfname[0] != '|') {
+ char fmode[4];
+
+ strcpy(fmode, gp_fmode_wb);
+ strcat(fmode, "+");
+ *pfile = gp_fopen(pfname, fmode);
+ if (*pfile)
+ return 0;
+ }
+ *pfile = gp_open_printer(pfname, binary);
+ if (*pfile)
+ return 0;
+ return_error(gs_error_invalidfileaccess);
+}
diff --git a/pstoraster/gsdevice.h b/pstoraster/gsdevice.h
new file mode 100644
index 000000000..6c8fd830d
--- /dev/null
+++ b/pstoraster/gsdevice.h
@@ -0,0 +1,103 @@
+/* Copyright (C) 1994, 1995, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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_matrix_DEFINED
+# define gs_matrix_DEFINED
+typedef struct gs_matrix_s gs_matrix;
+#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));
+
+int 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 */
+int gs_setdevice_no_init(P2(gs_state *, gx_device *));
+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_get_device_or_hardware_params(P3(gx_device *, gs_param_list *, bool));
+#define gs_getdeviceparams(dev, plist)\
+ gs_get_device_or_hardware_params(dev, plist, false)
+#define gs_gethardwareparams(dev, plist)\
+ gs_get_device_or_hardware_params(dev, plist, true)
+
+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..efb83e1bc
--- /dev/null
+++ b/pstoraster/gsdevmem.c
@@ -0,0 +1,236 @@
+/* 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 supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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/gsdll.h b/pstoraster/gsdll.h
new file mode 100644
index 000000000..709aeb836
--- /dev/null
+++ b/pstoraster/gsdll.h
@@ -0,0 +1,145 @@
+/* Copyright (C) 1994-1996, Russell Lang. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+
+/*$Id$ */
+
+#ifndef gsdll_INCLUDED
+# define gsdll_INCLUDED
+
+#ifndef _GSDLL_H
+#define _GSDLL_H
+
+#ifndef GSDLLEXPORT
+#define GSDLLEXPORT
+#endif
+
+#ifdef __WINDOWS__
+#define _Windows
+#endif
+
+/* type of exported functions */
+#ifdef _Windows
+#ifdef _WATCOM_
+#define GSDLLAPI GSDLLEXPORT
+#else
+#define GSDLLAPI CALLBACK GSDLLEXPORT
+#endif
+#else
+#ifdef __IBMC__
+#define GSDLLAPI _System
+#else
+#define GSDLLAPI
+#endif
+#endif
+
+#ifdef _Windows
+#define GSDLLCALLLINK
+#define GSFAR FAR
+#else
+#ifdef __IBMC__
+#define GSDLLCALLLINK _System
+#else
+#define GSDLLCALLLINK
+#endif
+#define GSFAR
+#endif
+
+/* global pointer to callback */
+typedef int (GSFAR * GSDLLCALLLINK GSDLL_CALLBACK) (int, char GSFAR *, unsigned long);
+extern GSDLL_CALLBACK pgsdll_callback;
+
+/* message values for callback */
+#define GSDLL_STDIN 1 /* get count characters to str from stdin */
+ /* return number of characters read */
+#define GSDLL_STDOUT 2 /* put count characters from str to stdout */
+ /* return number of characters written */
+#define GSDLL_DEVICE 3 /* device = str has been opened if count=1 */
+ /* or closed if count=0 */
+#define GSDLL_SYNC 4 /* sync_output for device str */
+#define GSDLL_PAGE 5 /* output_page for device str */
+#define GSDLL_SIZE 6 /* resize for device str */
+ /* LOWORD(count) is new xsize */
+ /* HIWORD(count) is new ysize */
+#define GSDLL_POLL 7 /* Called from gp_check_interrupt */
+ /* Can be used by caller to poll the message queue */
+ /* Normally returns 0 */
+ /* To abort gsdll_execute_cont(), return a */
+ /* non zero error code until gsdll_execute_cont() */
+ /* returns */
+
+/* return values from gsdll_init() */
+#define GSDLL_INIT_IN_USE 100 /* DLL is in use */
+#define GSDLL_INIT_QUIT 101 /* quit or EOF during init */
+ /* This is not an error. */
+ /* gsdll_exit() must not be called */
+
+
+/* DLL exported functions */
+/* for load time dynamic linking */
+int GSDLLAPI gsdll_revision(char GSFAR * GSFAR * product, char GSFAR * GSFAR * copyright, long GSFAR * gs_revision, long GSFAR * gs_revisiondate);
+int GSDLLAPI gsdll_init(GSDLL_CALLBACK callback, HWND hwnd, int argc, char GSFAR * GSFAR * argv);
+int GSDLLAPI gsdll_execute_begin(void);
+int GSDLLAPI gsdll_execute_cont(const char GSFAR * str, int len);
+int GSDLLAPI gsdll_execute_end(void);
+int GSDLLAPI gsdll_exit(void);
+int GSDLLAPI gsdll_lock_device(unsigned char *device, int flag);
+
+#ifdef _Windows
+HGLOBAL GSDLLAPI gsdll_copy_dib(unsigned char GSFAR * device);
+HPALETTE GSDLLAPI gsdll_copy_palette(unsigned char GSFAR * device);
+void GSDLLAPI gsdll_draw(unsigned char GSFAR * device, HDC hdc, LPRECT dest, LPRECT src);
+int GSDLLAPI gsdll_get_bitmap_row(unsigned char *device, LPBITMAPINFOHEADER pbmih,
+ LPRGBQUAD prgbquad, LPBYTE * ppbyte, unsigned int row);
+
+#else
+unsigned long gsdll_get_bitmap(unsigned char *device, unsigned char **pbitmap);
+
+#endif
+
+/* Function pointer typedefs */
+/* for run time dynamic linking */
+typedef int (GSDLLAPI * PFN_gsdll_revision) (char GSFAR * GSFAR *, char GSFAR * GSFAR *, long GSFAR *, long GSFAR *);
+typedef int (GSDLLAPI * PFN_gsdll_init) (GSDLL_CALLBACK, HWND, int argc, char GSFAR * GSFAR * argv);
+typedef int (GSDLLAPI * PFN_gsdll_execute_begin) (void);
+typedef int (GSDLLAPI * PFN_gsdll_execute_cont) (const char GSFAR * str, int len);
+typedef int (GSDLLAPI * PFN_gsdll_execute_end) (void);
+typedef int (GSDLLAPI * PFN_gsdll_exit) (void);
+typedef int (GSDLLAPI * PFN_gsdll_lock_device) (unsigned char GSFAR *, int);
+
+#ifdef _Windows
+typedef HGLOBAL(GSDLLAPI * PFN_gsdll_copy_dib) (unsigned char GSFAR *);
+typedef HPALETTE(GSDLLAPI * PFN_gsdll_copy_palette) (unsigned char GSFAR *);
+typedef void (GSDLLAPI * PFN_gsdll_draw) (unsigned char GSFAR *, HDC, LPRECT, LPRECT);
+typedef int (GSDLLAPI * PFN_gsdll_get_bitmap_row) (unsigned char *device, LPBITMAPINFOHEADER pbmih,
+ LPRGBQUAD prgbquad, LPBYTE * ppbyte, unsigned int row);
+
+#else
+typedef long (*GSDLLAPI PFN_gsdll_get_bitmap) (unsigned char *, unsigned char **);
+
+#endif
+
+#endif
+
+#endif /* gsdll_INCLUDED */
diff --git a/pstoraster/gsdparam.c b/pstoraster/gsdparam.c
new file mode 100644
index 000000000..3b104ab6a
--- /dev/null
+++ b/pstoraster/gsdparam.c
@@ -0,0 +1,807 @@
+/*
+ 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 supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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_get_device_or_hardware_params(gx_device * dev, gs_param_list * plist,
+ bool is_hardware)
+{
+ 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 (is_hardware ?
+ (*dev_proc(dev, get_hardware_params)) (dev, plist) :
+ (*dev_proc(dev, get_params)) (dev, plist));
+}
+
+/* Standard ProcessColorModel values. */
+static const char *const 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 ||
+ (code = (dev->NumCopies_set < 0 ||
+ (*dev_proc(dev, get_page_device))(dev) == 0 ? 0:
+ dev->NumCopies_set ?
+ param_write_int(plist, "NumCopies", &dev->NumCopies) :
+ param_write_null(plist, "NumCopies"))) < 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;
+}
+
+/* Get hardware-detected parameters. Default action is no hardware params. */
+int
+gx_default_get_hardware_params(gx_device * dev, gs_param_list * plist)
+{
+ return 0;
+}
+
+/* ---------------- Input and output media ---------------- */
+
+/* Finish defining input or output 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;
+}
+
+/* Define input media. */
+
+const gdev_input_media_t gdev_input_media_default =
+{
+ gdev_input_media_default_values
+};
+
+int
+gdev_begin_input_media(gs_param_list * mlist, gs_param_dict * pdict,
+ int count)
+{
+ pdict->size = count;
+ return param_begin_write_dict(mlist, "InputAttributes", pdict, true);
+}
+
+int
+gdev_write_input_media(int index, gs_param_dict * pdict,
+ const gdev_input_media_t * 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) ||
+ (pim->PageSize[2] != 0 && pim->PageSize[3] != 0)
+ ) {
+ gs_param_float_array psa;
+
+ psa.data = pim->PageSize;
+ psa.size =
+ (pim->PageSize[0] == pim->PageSize[2] &&
+ pim->PageSize[1] == pim->PageSize[3] ? 2 : 4);
+ 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_write_input_page_size(int index, gs_param_dict * pdict,
+ floatp width_points, floatp height_points)
+{
+ gdev_input_media_t media;
+
+ media.PageSize[0] = media.PageSize[2] = width_points;
+ media.PageSize[1] = media.PageSize[3] = height_points;
+ media.MediaColor = 0;
+ media.MediaWeight = 0;
+ media.MediaType = 0;
+ return gdev_write_input_media(index, pdict, &media);
+}
+
+int
+gdev_end_input_media(gs_param_list * mlist, gs_param_dict * pdict)
+{
+ return param_end_write_dict(mlist, "InputAttributes", pdict);
+}
+
+/* Define output media. */
+
+const gdev_output_media_t gdev_output_media_default =
+{
+ gdev_output_media_default_values
+};
+
+int
+gdev_begin_output_media(gs_param_list * mlist, gs_param_dict * pdict,
+ int count)
+{
+ pdict->size = count;
+ return param_begin_write_dict(mlist, "OutputAttributes", pdict, true);
+}
+
+int
+gdev_write_output_media(int index, gs_param_dict * pdict,
+ const gdev_output_media_t * 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);
+}
+
+int
+gdev_end_output_media(gs_param_list * mlist, gs_param_dict * pdict)
+{
+ return param_end_write_dict(mlist, "OutputAttributes", pdict);
+}
+
+/* ================ 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;
+ int nci = dev->NumCopies;
+ int ncset = dev->NumCopies_set;
+
+ 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;
+ }
+ if (dev->NumCopies_set >= 0 &&
+ (*dev_proc(dev, get_page_device))(dev) != 0
+ ) {
+ switch (code = param_read_int(plist, (param_name = "NumCopies"), &nci)) {
+ case 0:
+ if (nci < 0)
+ ecode = gs_error_rangecheck;
+ else {
+ ncset = 1;
+ break;
+ }
+ goto nce;
+ default:
+ if ((code = param_read_null(plist, param_name)) == 0) {
+ ncset = 0;
+ break;
+ }
+ ecode = code; /* can't be 1 */
+nce:
+ param_signal_error(plist, param_name, ecode);
+ 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->NumCopies = nci;
+ dev->NumCopies_set = ncset;
+ 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/gsdpnext.h b/pstoraster/gsdpnext.h
new file mode 100644
index 000000000..7a82ffe69
--- /dev/null
+++ b/pstoraster/gsdpnext.h
@@ -0,0 +1,34 @@
+/* Copyright (C) 1997 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* API for NeXT DPS facilities */
+
+#ifndef gsdpnext_INCLUDED
+# define gsdpnext_INCLUDED
+
+#include "gsalpha.h"
+#include "gsalphac.h"
+
+#endif /* gsdpnext_INCLUDED */
diff --git a/pstoraster/gsdps.h b/pstoraster/gsdps.h
new file mode 100644
index 000000000..e31a33bba
--- /dev/null
+++ b/pstoraster/gsdps.h
@@ -0,0 +1,40 @@
+/* Copyright (C) 1997 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Client interface to Display PostScript facilities. */
+
+#ifndef gsdps_INCLUDED
+# define gsdps_INCLUDED
+
+/* Device-source images */
+#include "gsiparm2.h"
+
+/* View clipping */
+int gs_initviewclip(P1(gs_state *));
+int gs_eoviewclip(P1(gs_state *));
+int gs_viewclip(P1(gs_state *));
+int gs_viewclippath(P1(gs_state *));
+
+#endif /* gsdps_INCLUDED */
diff --git a/pstoraster/gsdps1.c b/pstoraster/gsdps1.c
new file mode 100644
index 000000000..e4707d003
--- /dev/null
+++ b/pstoraster/gsdps1.c
@@ -0,0 +1,242 @@
+/* Copyright (C) 1991, 1992, 1994, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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 "gxdevice.h"
+#include "gxfixed.h"
+#include "gxmatrix.h"
+#include "gspath.h"
+#include "gspath2.h" /* defines interface */
+#include "gzpath.h"
+#include "gzcpath.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. */
+ if (dbox.p.x < fixed2float(min_fixed + box_rounding_slop_fixed) ||
+ dbox.p.y < fixed2float(min_fixed + box_rounding_slop_fixed) ||
+ dbox.q.x >= fixed2float(max_fixed - box_rounding_slop_fixed + fixed_epsilon) ||
+ dbox.q.y >= fixed2float(max_fixed - box_rounding_slop_fixed + fixed_epsilon)
+ )
+ return_error(gs_error_limitcheck);
+ 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 = bbox;
+ }
+ 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 save;
+
+ gx_path_init_local(&save, pgs->memory);
+ gx_path_assign_preserve(&save, pgs->path);
+ gs_newpath(pgs);
+ if ((code = gs_rectappend(pgs, pr, count)) < 0 ||
+ (code = gs_clip(pgs)) < 0
+ ) {
+ gx_path_assign_free(pgs->path, &save);
+ return code;
+ }
+ gx_path_free(&save, "gs_rectclip");
+ gs_newpath(pgs);
+ return 0;
+}
+
+/* Fill a list of rectangles. */
+/* We take the trouble to do this efficiently in the simple cases. */
+int
+gs_rectfill(gs_state * pgs, const gs_rect * pr, uint count)
+{
+ const gs_rect *rlist = pr;
+ gx_clip_path *pcpath;
+ uint rcount = count;
+ int code;
+
+ gx_set_dev_color(pgs);
+ if ((is_fzero2(pgs->ctm.xy, pgs->ctm.yx) ||
+ is_fzero2(pgs->ctm.xx, pgs->ctm.yy)) &&
+ gx_effective_clip_path(pgs, &pcpath) >= 0 &&
+ clip_list_is_rectangle(gx_cpath_list(pcpath)) &&
+ gs_state_color_load(pgs) >= 0 &&
+ (*dev_proc(pgs->device, get_alpha_bits)) (pgs->device, go_graphics)
+ <= 1
+ ) {
+ uint i;
+ gs_fixed_rect clip_rect;
+
+ gx_cpath_inner_box(pcpath, &clip_rect);
+ for (i = 0; i < count; ++i) {
+ gs_fixed_point p, q;
+ gs_fixed_rect draw_rect;
+ int x, y, w, h;
+
+ if (gs_point_transform2fixed(&pgs->ctm, pr[i].p.x, pr[i].p.y, &p) < 0 ||
+ gs_point_transform2fixed(&pgs->ctm, pr[i].q.x, pr[i].q.y, &q) < 0
+ ) { /* Switch to the slow algorithm. */
+ goto slow;
+ }
+ draw_rect.p.x = min(p.x, q.x) - pgs->fill_adjust.x;
+ draw_rect.p.y = min(p.y, q.y) - pgs->fill_adjust.y;
+ draw_rect.q.x = max(p.x, q.x) + pgs->fill_adjust.x;
+ draw_rect.q.y = max(p.y, q.y) + pgs->fill_adjust.y;
+ rect_intersect(draw_rect, clip_rect);
+ x = fixed2int_pixround(draw_rect.p.x);
+ y = fixed2int_pixround(draw_rect.p.y);
+ w = fixed2int_pixround(draw_rect.q.x) - x;
+ h = fixed2int_pixround(draw_rect.q.y) - y;
+ if (w > 0 && h > 0) {
+ if (gx_fill_rectangle(x, y, w, h, pgs->dev_color, pgs) < 0)
+ goto slow;
+ }
+ }
+ return 0;
+ slow:rlist = pr + i;
+ rcount = count - i;
+ } {
+ bool do_save = !gx_path_is_null(pgs->path);
+
+ if (do_save) {
+ if ((code = gs_gsave(pgs)) < 0)
+ return code;
+ gs_newpath(pgs);
+ }
+ if ((code = gs_rectappend(pgs, rlist, rcount)) < 0 ||
+ (code = gs_fill(pgs)) < 0
+ )
+ DO_NOTHING;
+ if (do_save)
+ gs_grestore(pgs);
+ else if (code < 0)
+ gs_newpath(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)
+{
+ bool do_save = pmat != NULL || !gx_path_is_null(pgs->path);
+ int code;
+
+ if (do_save) {
+ if ((code = gs_gsave(pgs)) < 0)
+ return code;
+ gs_newpath(pgs);
+ }
+ if ((code = gs_rectappend(pgs, pr, count)) < 0 ||
+ (pmat != NULL && (code = gs_concat(pgs, pmat)) < 0) ||
+ (code = gs_stroke(pgs)) < 0
+ )
+ DO_NOTHING;
+ if (do_save)
+ gs_grestore(pgs);
+ else if (code < 0)
+ gs_newpath(pgs);
+ return code;
+}
diff --git a/pstoraster/gsdsrc.c b/pstoraster/gsdsrc.c
new file mode 100644
index 000000000..ea277bfc7
--- /dev/null
+++ b/pstoraster/gsdsrc.c
@@ -0,0 +1,121 @@
+/* Copyright (C) 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* DataSource procedures */
+
+#include "memory_.h"
+#include "gx.h"
+#include "gsdsrc.h"
+#include "gserrors.h"
+#include "stream.h"
+
+/* GC descriptor */
+public_st_data_source();
+#define psrc ((gs_data_source_t *)vptr)
+private
+ENUM_PTRS_BEGIN(data_source_enum_ptrs)
+{
+ if (psrc->type == data_source_type_string)
+ ENUM_RETURN_CONST_STRING_PTR(gs_data_source_t, data.str);
+ else if (psrc->type == data_source_type_stream)
+ ENUM_RETURN_PTR(gs_data_source_t, data.strm);
+ else /* bytes or floats */
+ ENUM_RETURN_PTR(gs_data_source_t, data.str.data);
+}
+ENUM_PTRS_END
+private RELOC_PTRS_BEGIN(data_source_reloc_ptrs)
+{
+ if (psrc->type == data_source_type_string)
+ RELOC_CONST_STRING_PTR(gs_data_source_t, data.str);
+ else if (psrc->type == data_source_type_stream)
+ RELOC_PTR(gs_data_source_t, data.strm);
+ else /* bytes or floats */
+ RELOC_PTR(gs_data_source_t, data.str.data);
+}
+RELOC_PTRS_END
+#undef psrc
+
+/* Access data from a string or a byte object. */
+/* Does *not* check bounds. */
+int
+data_source_access_string(const gs_data_source_t * psrc, ulong start,
+ uint length, byte * buf, const byte ** ptr)
+{
+ const byte *p = psrc->data.str.data + start;
+
+ if (ptr)
+ *ptr = p;
+ else
+ memcpy(buf, p, length);
+ return 0;
+}
+/* access_bytes is identical to access_string, but has a different */
+/* GC procedure. */
+int
+data_source_access_bytes(const gs_data_source_t * psrc, ulong start,
+ uint length, byte * buf, const byte ** ptr)
+{
+ const byte *p = psrc->data.str.data + start;
+
+ if (ptr)
+ *ptr = p;
+ else
+ memcpy(buf, p, length);
+ return 0;
+}
+
+/* Access data from a stream. */
+/* Returns gs_error_rangecheck if out of bounds. */
+int
+data_source_access_stream(const gs_data_source_t * psrc, ulong start,
+ uint length, byte * buf, const byte ** ptr)
+{
+ stream *s = psrc->data.strm;
+ const byte *p;
+
+ if (start >= s->position &&
+ (p = start - s->position + s->cbuf) + length <=
+ s->cursor.r.limit + 1
+ ) {
+ if (ptr)
+ *ptr = p;
+ else
+ memcpy(buf, p, length);
+ } else {
+ uint nread;
+ int code = sseek(s, start);
+
+ if (code < 0)
+ return_error(gs_error_rangecheck);
+ code = sgets(s, buf, length, &nread);
+ if (code < 0)
+ return_error(gs_error_rangecheck);
+ if (nread != length)
+ return_error(gs_error_rangecheck);
+ if (ptr)
+ *ptr = buf;
+ }
+ return 0;
+}
diff --git a/pstoraster/gsdsrc.h b/pstoraster/gsdsrc.h
new file mode 100644
index 000000000..bb6243941
--- /dev/null
+++ b/pstoraster/gsdsrc.h
@@ -0,0 +1,133 @@
+/* Copyright (C) 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* DataSource definitions */
+
+#ifndef gsdsrc_INCLUDED
+# define gsdsrc_INCLUDED
+
+#include "gsstruct.h"
+
+/* ---------------- Types and structures ---------------- */
+
+/*
+ * A gs_data_source_t represents the data source for various constructs. It
+ * can be a string (either a gs_string or a byte-type object), a
+ * positionable, non-procedure-based stream, or an array of floats. An
+ * ordinary positionable file stream will do, as long as the client doesn't
+ * attempt to read past the EOF.
+ *
+ * The handling of floats is anomalous, but we don't see a good alternative
+ * at the moment.
+ */
+
+#ifndef stream_DEFINED
+# define stream_DEFINED
+typedef struct stream_s stream;
+
+#endif
+
+/*
+ * Prepare to access a block of data from a source. buf must be a client-
+ * supplied buffer of at least length bytes. If ptr == 0, always copy the
+ * data into buf. If ptr != 0, either copy the data into buf and set *ptr =
+ * buf, or set *ptr to point to the data (which might be invalidated by the
+ * next call). Note that this procedure may or may not do bounds checking.
+ */
+#define data_source_proc_access(proc)\
+ int proc(P5(const gs_data_source_t *psrc, ulong start, uint length,\
+ byte *buf, const byte **ptr))
+
+typedef enum {
+ data_source_type_string,
+ data_source_type_bytes,
+ data_source_type_floats,
+ data_source_type_stream
+} gs_data_source_type_t;
+typedef struct gs_data_source_s gs_data_source_t;
+struct gs_data_source_s {
+ data_source_proc_access((*access));
+ gs_data_source_type_t type;
+ union d_ {
+ gs_const_string str; /* also used for byte objects */
+ stream *strm;
+ } data;
+};
+
+#define data_source_access_only(psrc, start, length, buf, ptr)\
+ (*(psrc)->access)(psrc, (ulong)(start), length, buf, ptr)
+#define data_source_access(psrc, start, length, buf, ptr)\
+ BEGIN\
+ int code_ = data_source_access_only(psrc, start, length, buf, ptr);\
+ if ( code_ < 0 ) return code_;\
+ END
+#define data_source_copy_only(psrc, start, length, buf)\
+ data_source_access_only(psrc, start, length, buf, (const byte **)0)
+#define data_source_copy(psrc, start, length, buf)\
+ data_source_access(psrc, start, length, buf, (const byte **)0)
+
+/*
+ * Data sources are always embedded in other structures, but they do have
+ * pointers that need to be traced and relocated, so they do have a GC
+ * structure type.
+ */
+extern_st(st_data_source);
+#define public_st_data_source() /* in gsdsrc.c */\
+ gs_public_st_composite(st_data_source, gs_data_source_t, "gs_data_source_t",\
+ data_source_enum_ptrs, data_source_reloc_ptrs)
+#define st_data_source_max_ptrs 1
+
+/* ---------------- Procedures ---------------- */
+
+/* Initialize a data source of the various known types. */
+data_source_proc_access(data_source_access_string);
+#define data_source_init_string(psrc, strg)\
+ ((psrc)->type = data_source_type_string,\
+ (psrc)->data.str = strg, (psrc)->access = data_source_access_string)
+#define data_source_init_string2(psrc, bytes, len)\
+ ((psrc)->type = data_source_type_string,\
+ (psrc)->data.str.data = bytes, (psrc)->data.str.size = len,\
+ (psrc)->access = data_source_access_string)
+data_source_proc_access(data_source_access_bytes);
+#define data_source_init_bytes(psrc, bytes, len)\
+ ((psrc)->type = data_source_type_bytes,\
+ (psrc)->data.str.data = bytes, (psrc)->data.str.size = len,\
+ (psrc)->access = data_source_access_bytes)
+#define data_source_init_floats(psrc, floats, count)\
+ ((psrc)->type = data_source_type_floats,\
+ (psrc)->data.str.data = (byte *)floats,\
+ (psrc)->data.str.size = (count) * sizeof(float),\
+ (psrc)->access = data_source_access_bytes)
+data_source_proc_access(data_source_access_stream);
+#define data_source_init_stream(psrc, s)\
+ ((psrc)->type = data_source_type_stream,\
+ (psrc)->data.strm = s, (psrc)->access = data_source_access_stream)
+
+#define data_source_is_stream(dsource)\
+ ((dsource).type == data_source_type_stream)
+#define data_source_is_array(dsource)\
+ ((dsource).type == data_source_type_floats)
+
+#endif /* gsdsrc_INCLUDED */
diff --git a/pstoraster/gserror.h b/pstoraster/gserror.h
new file mode 100644
index 000000000..d90bcb279
--- /dev/null
+++ b/pstoraster/gserror.h
@@ -0,0 +1,42 @@
+/* Copyright (C) 1994, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Error return macros */
+
+#ifndef gserror_INCLUDED
+# define gserror_INCLUDED
+
+#ifdef DEBUG
+int gs_log_error(P3(int, const char *, 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)
+#define return_if_error(expr)\
+ BEGIN int code_ = (expr); if ( code_ < 0 ) return code_; END
+
+#endif /* gserror_INCLUDED */
diff --git a/pstoraster/gserrors.h b/pstoraster/gserrors.h
new file mode 100644
index 000000000..22bbeb32e
--- /dev/null
+++ b/pstoraster/gserrors.h
@@ -0,0 +1,56 @@
+/* Copyright (C) 1989, 1993, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Error code definitions */
+
+#ifndef gserrors_INCLUDED
+# define gserrors_INCLUDED
+
+/* 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_hit_detected (-99)
+
+#define gs_error_Fatal (-100)
+
+#endif /* gserrors_INCLUDED */
diff --git a/pstoraster/gsexit.h b/pstoraster/gsexit.h
new file mode 100644
index 000000000..57829a7d7
--- /dev/null
+++ b/pstoraster/gsexit.h
@@ -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 supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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/gsfcmap.c b/pstoraster/gsfcmap.c
new file mode 100644
index 000000000..b418a4aeb
--- /dev/null
+++ b/pstoraster/gsfcmap.c
@@ -0,0 +1,167 @@
+/* Copyright (C) 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* CMap character decoding */
+#include "gx.h"
+#include "gserrors.h"
+#include "gsstruct.h"
+#include "gxfcmap.h"
+
+/* CMap structure descriptors */
+public_st_cmap();
+public_st_code_map();
+public_st_code_map_element();
+
+#define pcmap ((gx_code_map *)vptr)
+/* Because code maps can be elements of arrays, */
+/* their enum_ptrs procedure must never return 0 prematurely. */
+private
+ENUM_PTRS_BEGIN(code_map_enum_ptrs) return 0;
+ENUM_PTR(0, gx_code_map, cmap);
+case 1:
+switch (pcmap->type)
+{
+ case cmap_glyph:
+ (*pcmap->cmap->mark_glyph)(pcmap->data.glyph,
+ pcmap->cmap->mark_glyph_data);
+ default:
+ ENUM_RETURN(0);
+ case cmap_subtree:
+ ENUM_RETURN_PTR(gx_code_map, data.subtree);
+}
+ENUM_PTRS_END
+private RELOC_PTRS_BEGIN(code_map_reloc_ptrs);
+switch (pcmap->type) {
+ case cmap_subtree:
+ RELOC_PTR(gx_code_map, data.subtree);
+ break;
+ default:
+ ;
+}
+RELOC_PTR(gx_code_map, cmap);
+RELOC_PTRS_END
+#undef pcmap
+
+/* CIDSystemInfo structure */
+public_st_cid_system_info();
+
+/* ---------------- Procedures ---------------- */
+
+/*
+ * Decode a character from a string using a code map, updating the index.
+ * Return 0 for a CID or name, N > 0 for a character code where N is the
+ * number of bytes in the code, or an error. For undefined characters,
+ * we set *pglyph = gs_no_glyph and return 0.
+ */
+private int
+code_map_decode_next(const gx_code_map * pcmap, const gs_const_string * str,
+ uint * pindex, uint * pfidx,
+ gs_char * pchr, gs_glyph * pglyph)
+{
+ const gx_code_map *map = pcmap;
+ uint chr = 0;
+
+ for (;;) {
+ int result;
+
+ if_debug1('J', "[J]cmap char = 0x%x: ", chr);
+ switch ((gx_code_map_type) map->type) {
+ case cmap_char_code:
+ if_debug0('J', "char code");
+ *pglyph = (gs_glyph)map->data.ccode;
+ result = map->num_bytes1 + 1;
+leaf: if (chr > map->last)
+ goto undef;
+ if (map->add_offset)
+ *pglyph += chr - map->first;
+ *pfidx = map->byte_data.font_index;
+ *pchr = chr & 0xff;
+ if_debug3('J', " 0x%lx, fidx %u, result %d\n",
+ *pglyph, *pfidx, result);
+ return result;
+ case cmap_glyph:
+ if_debug0('J', "glyph");
+ *pglyph = map->data.glyph;
+ result = 0;
+ goto leaf;
+ case cmap_subtree:
+ if_debug0('J', "subtree\n");
+ if (*pindex >= str->size)
+ return_error(gs_error_rangecheck);
+ chr = str->data[(*pindex)++];
+ if (chr >= map->data.subtree[0].first) {
+ /* Invariant: map[lo].first <= chr < map[hi].first. */
+ uint lo = 0, hi = map->byte_data.count1 + 1;
+
+ map = map->data.subtree;
+ while (lo + 1 < hi) {
+ uint mid = (lo + hi) >> 1;
+
+ if (chr >= map[mid].first)
+ lo = mid;
+ else
+ hi = mid;
+ }
+ map = &map[lo];
+ continue;
+ }
+undef: if_debug0('J', " undef\n");
+ *pchr = 0;
+ *pglyph = gs_no_glyph;
+ return 0;
+ default: /* (can't happen) */
+ if_debug0('J', "error!\n");
+ return_error(gs_error_invalidfont);
+ }
+ }
+}
+
+/*
+ * Decode a character from a string using a CMap.
+ * Return like code_map_decode_next.
+ */
+int
+gs_cmap_decode_next(const gs_cmap * pcmap, const gs_const_string * str,
+ uint * pindex, uint * pfidx,
+ gs_char * pchr, gs_glyph * pglyph)
+{
+ uint save_index = *pindex;
+ int code =
+ code_map_decode_next(&pcmap->def, str, pindex, pfidx, pchr, pglyph);
+
+ if (code != 0 || *pglyph != gs_no_glyph)
+ return code;
+ /* This is an undefined character. Use the notdef map. */
+ {
+ uint next_index = *pindex;
+
+ *pindex = save_index;
+ code =
+ code_map_decode_next(&pcmap->notdef, str, pindex, pfidx,
+ pchr, pglyph);
+ *pindex = next_index;
+ }
+ return code;
+}
diff --git a/pstoraster/gsfcmap.h b/pstoraster/gsfcmap.h
new file mode 100644
index 000000000..628a111a7
--- /dev/null
+++ b/pstoraster/gsfcmap.h
@@ -0,0 +1,69 @@
+/* Copyright (C) 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* CMap data definition */
+/* Requires gsstruct.h */
+
+#ifndef gsfcmap_INCLUDED
+# define gsfcmap_INCLUDED
+
+#include "gsccode.h"
+
+/* Define the abstract type for a CMap. */
+#ifndef gs_cmap_DEFINED
+# define gs_cmap_DEFINED
+typedef struct gs_cmap_s gs_cmap;
+
+#endif
+
+/* We only need the structure descriptor for testing. */
+extern_st(st_cmap);
+
+/* Define the structure for CIDSystemInfo. */
+typedef struct gs_cid_system_info_s {
+ gs_const_string Registry;
+ gs_const_string Ordering;
+ int Supplement;
+} gs_cid_system_info;
+/* We only need the structure descriptor for embedding. */
+extern_st(st_cid_system_info);
+#define public_st_cid_system_info() /* in gsfcmap.c */\
+ gs_public_st_const_strings2(st_cid_system_info, gs_cid_system_info,\
+ "gs_cid_system_info", cid_si_enum_ptrs, cid_si_reloc_ptrs,\
+ Registry, Ordering)
+
+/* ---------------- Procedural interface ---------------- */
+
+/*
+ * Decode a character from a string using a CMap, updating the index.
+ * Return 0 for a CID or name, N > 0 for a character code where N is the
+ * number of bytes in the code, or an error. For undefined characters,
+ * we return CID 0.
+ */
+int gs_cmap_decode_next(P6(const gs_cmap * pcmap, const gs_const_string * str,
+ uint * pindex, uint * pfidx,
+ gs_char * pchr, gs_glyph * pglyph));
+
+#endif /* gsfcmap_INCLUDED */
diff --git a/pstoraster/gsflip.h b/pstoraster/gsflip.h
new file mode 100644
index 000000000..e1e19ab30
--- /dev/null
+++ b/pstoraster/gsflip.h
@@ -0,0 +1,46 @@
+/* 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 supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Interface to routines for "flipping" image data */
+
+#ifndef gsflip_INCLUDED
+# define gsflip_INCLUDED
+
+/*
+ * Convert line-based (MultipleDataSource) input to the chunky format
+ * used everywhere else.
+ *
+ * We store the output at buffer.
+ * Each row of input must consist of an integral number of pixels.
+ * In particular, for 12-bit input, nbytes must be 0 mod 3.
+ * offset is the amount to be added to each plane pointer.
+ * num_planes must be 3 or 4; bits_per_sample must be 1, 2, 4, 8, or 12.
+ * Returns -1 if num_planes or bits_per_sample is invalid, otherwise 0.
+ */
+extern int image_flip_planes(P6(byte * buffer, const byte ** planes,
+ uint offset, uint nbytes,
+ int num_planes, int bits_per_sample));
+
+#endif /* gsflip_INCLUDED */
diff --git a/pstoraster/gsfont.c b/pstoraster/gsfont.c
new file mode 100644
index 000000000..efa4204b3
--- /dev/null
+++ b/pstoraster/gsfont.c
@@ -0,0 +1,558 @@
+/* Copyright (C) 1989, 1995, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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. */
+ uint cci = index - st_font_dir_max_ptrs;
+ uint offset, count;
+ uint tmask = dir->ccache.table_mask;
+
+ if (cci == 0)
+ offset = 0, count = 1;
+ else if (cci == dir->enum_index + 1)
+ offset = dir->enum_offset + 1, count = 1;
+ else
+ offset = 0, count = cci;
+ for (; offset <= tmask; ++offset) {
+ cached_char *cc = dir->ccache.table[offset];
+
+ if (cc != 0 && !--count) {
+ (*dir->ccache.mark_glyph)
+ (cc->code, dir->ccache.mark_glyph_data);
+ dir->enum_index = cci;
+ dir->enum_offset = offset;
+ 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 *)
+ RELOC_OBJ(cc_pair(cc) - cc->pair_index) +
+ 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;
+ * when we finalize a scaled font, we unlink it from scaled_fonts.
+ * See above for more information.
+ */
+ void
+ gs_font_finalize(void *vptr)
+{
+ gs_font **ppfirst;
+ gs_font *next = pfont->next;
+ gs_font *prev = pfont->prev;
+
+ if_debug4('u', "[u]unlinking font 0x%lx, base=0x%lx, prev=0x%lx, next=0x%lx\n",
+ (ulong) pfont, (ulong) pfont->base, (ulong) prev, (ulong) next);
+ if (pfont->dir == 0)
+ ppfirst = 0;
+ else if (pfont->base == pfont)
+ ppfirst = &pfont->dir->orig_fonts;
+ else { /*
+ * Track the number of cached scaled fonts. Only decrement the
+ * count if we didn't do this already in gs_makefont.
+ */
+ if (next || prev || pfont->dir->scaled_fonts == pfont)
+ pfont->dir->ssize--;
+ ppfirst = &pfont->dir->scaled_fonts;
+ }
+ /*
+ * 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 (ppfirst != 0 && *ppfirst == pfont)
+ *ppfirst = 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, void *ignore_data)
+{
+ return false;
+}
+gs_font_dir *
+gs_font_dir_alloc2(gs_memory_t * struct_mem, gs_memory_t * bits_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_alloc2_limits(struct_mem, bits_mem,
+ smax_LARGE, bmax_LARGE, mmax_LARGE,
+ cmax_LARGE, blimit_LARGE);
+ }
+ if (pdir == 0)
+#endif
+ pdir = gs_font_dir_alloc2_limits(struct_mem, bits_mem,
+ smax_SMALL, bmax_SMALL, mmax_SMALL,
+ cmax_SMALL, blimit_SMALL);
+ if (pdir == 0)
+ return 0;
+ pdir->ccache.mark_glyph = cc_no_mark_glyph;
+ pdir->ccache.mark_glyph_data = 0;
+ return pdir;
+}
+gs_font_dir *
+gs_font_dir_alloc2_limits(gs_memory_t * struct_mem, gs_memory_t * bits_mem,
+ uint smax, uint bmax, uint mmax, uint cmax, uint upper)
+{
+ register gs_font_dir *pdir =
+ gs_alloc_struct(struct_mem, gs_font_dir, &st_font_dir,
+ "font_dir_alloc(dir)");
+ int code;
+
+ if (pdir == 0)
+ return 0;
+ code = gx_char_cache_alloc(struct_mem, bits_mem, pdir,
+ bmax, mmax, cmax, upper);
+ if (code < 0) {
+ gs_free_object(struct_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)
+ dlprintf("[m]composite");
+ else if (uid_is_UniqueID(&pbfont->UID))
+ dlprintf1("[m]UniqueID=%ld", pbfont->UID.id);
+ else if (uid_is_XUID(&pbfont->UID))
+ dlprintf1("[m]XUID(%u)", (uint) (-pbfont->UID.id));
+ else
+ dlprintf("[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 && prev != 0) { /*
+ * We must discard a cached scaled font.
+ * prev points to the last (oldest) font.
+ * (We can't free it, because there might be
+ * other references to it.)
+ */
+ if_debug1('m', "[m]discarding font 0x%lx\n",
+ (ulong) prev);
+ if (prev->prev != 0)
+ prev->prev->next = 0;
+ else
+ pdir->scaled_fonts = 0;
+ pdir->ssize--;
+ prev->prev = 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);
+ }
+ }
+ pdir->ssize++;
+ link_first(pdir->scaled_fonts, pf_out);
+ } else { /* Prevent garbage pointers. */
+ pf_out->next = pf_out->prev = 0;
+ }
+ 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;
+}
+
+/* Set the current font. This is provided only for the benefit of cshow, */
+/* which must reset the current font without disturbing the root font. */
+void
+gs_set_currentfont(gs_state * pgs, gs_font * pfont)
+{
+ pgs->font = pfont;
+ pgs->char_tm_valid = false;
+}
+
+/* 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);
+ }
+
+ /* 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..8674d82e2
--- /dev/null
+++ b/pstoraster/gsfont.h
@@ -0,0 +1,87 @@
+/* Copyright (C) 1989, 1993, 1996, 1997 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Requires gsmatrix.h */
+
+#ifndef gsfont_INCLUDED
+# define gsfont_INCLUDED
+
+/* 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_alloc2(P2(gs_memory_t * struct_mem,
+ gs_memory_t * bits_mem));
+gs_font_dir *gs_font_dir_alloc2_limits(P7(gs_memory_t * struct_mem,
+ gs_memory_t * bits_mem,
+ uint smax, uint bmax, uint mmax,
+ uint cmax, uint upper));
+
+/* Backward compatibility */
+#define gs_font_dir_alloc(mem) gs_font_dir_alloc2(mem, mem)
+#define gs_font_dir_alloc_limits(mem, smax, bmax, mmax, cmax, upper)\
+ gs_font_dir_alloc2_limits(mem, mem, smax, bmax, mmax, cmax, 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_set_currentfont(P2(gs_state *, gs_font *));
+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));
+
+#endif /* gsfont_INCLUDED */
diff --git a/pstoraster/gsfont0.c b/pstoraster/gsfont0.c
new file mode 100644
index 000000000..95ca444b7
--- /dev/null
+++ b/pstoraster/gsfont0.c
@@ -0,0 +1,136 @@
+/* Copyright (C) 1994, 1996, 1997 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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:
+switch (pfont->data.FMapType)
+{
+ case fmap_SubsVector:
+ENUM_RETURN_CONST_STRING_PTR(gs_font_type0,
+ data.SubsVector);
+ case fmap_CMap:
+ENUM_RETURN_PTR(gs_font_type0, data.CMap);
+ default:
+ENUM_RETURN(0);
+}
+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);
+switch (pfont->data.FMapType)
+{
+ case fmap_SubsVector:
+RELOC_CONST_STRING_PTR(gs_font_type0, data.SubsVector);
+break;
+ case fmap_CMap:
+RELOC_PTR(gs_font_type0, data.CMap);
+break;
+ default:
+;
+}
+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/gsfunc.c b/pstoraster/gsfunc.c
new file mode 100644
index 000000000..f0c438c9e
--- /dev/null
+++ b/pstoraster/gsfunc.c
@@ -0,0 +1,78 @@
+/* Copyright (C) 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Generic Function support */
+#include "gx.h"
+#include "gserrors.h"
+#include "gxfunc.h"
+
+/* GC descriptor */
+public_st_function();
+
+/* Generic free_params implementation. */
+void
+fn_common_free_params(gs_function_params_t * params, gs_memory_t * mem)
+{
+ gs_free_object(mem, (void *)params->Range, "Range"); /* break const */
+ gs_free_object(mem, (void *)params->Domain, "Domain"); /* break const */
+}
+
+/* Generic free implementation. */
+void
+fn_common_free(gs_function_t * pfn, bool free_params, gs_memory_t * mem)
+{
+ if (free_params)
+ gs_function_free_params(pfn, mem);
+ gs_free_object(mem, pfn, "fn_xxx_free");
+}
+
+/* Free an array of subsidiary Functions. */
+void
+fn_free_functions(gs_function_t ** Functions, int count, gs_memory_t * mem)
+{
+ int i;
+
+ for (i = count; --i >= 0;)
+ gs_function_free(Functions[i], true, mem);
+ gs_free_object(mem, Functions, "Functions");
+}
+
+/* Check the values of m, n, Domain, and (if supplied) Range. */
+int
+fn_check_mnDR(const gs_function_params_t * params, int m, int n)
+{
+ int i;
+
+ if (m <= 0 || n <= 0)
+ return_error(gs_error_rangecheck);
+ for (i = 0; i < m; ++i)
+ if (params->Domain[2 * i] > params->Domain[2 * i + 1])
+ return_error(gs_error_rangecheck);
+ if (params->Range != 0)
+ for (i = 0; i < n; ++i)
+ if (params->Range[2 * i] > params->Range[2 * i + 1])
+ return_error(gs_error_rangecheck);
+ return 0;
+}
diff --git a/pstoraster/gsfunc.h b/pstoraster/gsfunc.h
new file mode 100644
index 000000000..e621918e3
--- /dev/null
+++ b/pstoraster/gsfunc.h
@@ -0,0 +1,136 @@
+/* Copyright (C) 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Generic definitions for Functions */
+
+#ifndef gsfunc_INCLUDED
+# define gsfunc_INCLUDED
+
+/* ---------------- Types and structures ---------------- */
+
+/*
+ * gs_function_type_t is defined as equivalent to int, rather than as an
+ * enum type, because we can't enumerate all its possible values here in the
+ * generic definitions.
+ */
+typedef int gs_function_type_t;
+
+/*
+ * Define information common to all Function types.
+ * We separate the private part from the parameters so that
+ * clients can create statically initialized parameter structures.
+ */
+#define gs_function_params_common\
+ int m; /* # of inputs */\
+ const float *Domain; /* 2 x m */\
+ int n; /* # of outputs */\
+ const float *Range /* 2 x n, optional except for type 0 */
+
+/* Define a generic function, for use as the target type of pointers. */
+typedef struct gs_function_params_s {
+ gs_function_params_common;
+} gs_function_params_t;
+typedef struct gs_function_s gs_function_t;
+typedef int (*fn_evaluate_proc_t)(P3(const gs_function_t * pfn,
+ const float *in, float *out));
+typedef int (*fn_is_monotonic_proc_t)(P4(const gs_function_t * pfn,
+ const float *lower,
+ const float *upper,
+ bool must_know));
+typedef void (*fn_free_params_proc_t)(P2(gs_function_params_t * params,
+ gs_memory_t * mem));
+typedef void (*fn_free_proc_t)(P3(gs_function_t * pfn,
+ bool free_params, gs_memory_t * mem));
+typedef struct gs_function_head_s {
+ gs_function_type_t type;
+ fn_evaluate_proc_t evaluate;
+ fn_is_monotonic_proc_t is_monotonic;
+ fn_free_params_proc_t free_params;
+ fn_free_proc_t free;
+} gs_function_head_t;
+struct gs_function_s {
+ gs_function_head_t head;
+ gs_function_params_t params;
+};
+
+#define FunctionType(pfn) ((pfn)->head.type)
+
+/*
+ * Each specific function type has a definition in its own header file
+ * for its parameter record. In order to keep names from overflowing
+ * various compilers' limits, we take the name of the function type and
+ * reduce it to the first and last letter of each word, e.g., for
+ * Sampled functions, XxYy is Sd.
+
+typedef struct gs_function_XxYy_params_s {
+ gs_function_params_common;
+ << P additional members >>
+} gs_function_XxYy_params_t;
+#define private_st_function_XxYy()\
+ gs_private_st_suffix_addP(st_function_XxYy, gs_function_XxYy_t,\
+ "gs_function_XxYy_t", function_XxYy_enum_ptrs, function_XxYy_reloc_ptrs,\
+ st_function, <<params.additional_members>>)
+
+ */
+
+/* ---------------- Procedures ---------------- */
+
+/*
+ * Each specific function type has a pair of procedures in its own
+ * header file, one to allocate and initialize an instance of that type,
+ * and one to free the parameters of that type.
+
+int gs_function_XxYy_init(P3(gs_function_t **ppfn,
+ const gs_function_XxYy_params_t *params,
+ gs_memory_t *mem));
+
+void gs_function_XxYy_free_params(P2(gs_function_XxYy_params_t *params,
+ gs_memory_t *mem));
+
+ */
+
+/* Evaluate a function. */
+#define gs_function_evaluate(pfn, in, out)\
+ (*(pfn)->head.evaluate)(pfn, in, out)
+
+/*
+ * Test whether a function is monotonic on a given (closed) interval. If
+ * must_know is true, returns 0 for false, 1 for true, gs_error_rangecheck
+ * if any part of the interval is outside the function's domain; if
+ * must_know is false, may also return gs_error_undefined to mean "can't
+ * determine quickly". If lower[i] > upper[i], the result is not defined.
+ */
+#define gs_function_is_monotonic(pfn, lower, upper, must_know)\
+ (*(pfn)->head.is_monotonic)(pfn, lower, upper, must_know)
+
+/* Free function parameters. */
+#define gs_function_free_params(pfn, mem)\
+ (*(pfn)->head.free_params)(&(pfn)->params, mem)
+
+/* Free a function's implementation, optionally including its parameters. */
+#define gs_function_free(pfn, free_params, mem)\
+ (*(pfn)->head.free)(pfn, free_params, mem)
+
+#endif /* gsfunc_INCLUDED */
diff --git a/pstoraster/gsfunc0.c b/pstoraster/gsfunc0.c
new file mode 100644
index 000000000..645426606
--- /dev/null
+++ b/pstoraster/gsfunc0.c
@@ -0,0 +1,347 @@
+/* Copyright (C) 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Implementation of FunctionType 0 (Sampled) Functions */
+#include "math_.h"
+#include "gx.h"
+#include "gserrors.h"
+#include "gsfunc0.h"
+#include "gxfarith.h"
+#include "gxfunc.h"
+
+typedef struct gs_function_Sd_s {
+ gs_function_head_t head;
+ gs_function_Sd_params_t params;
+} gs_function_Sd_t;
+
+private_st_function_Sd();
+
+/* Define the maximum plausible number of inputs and outputs */
+/* for a Sampled function. */
+#define max_Sd_m 16
+#define max_Sd_n 16
+
+/* Get one set of sample values. */
+#define SETUP_SAMPLES(bps, nbytes)\
+ int n = pfn->params.n;\
+ byte buf[max_Sd_n * ((bps + 7) >> 3)];\
+ const byte *p;\
+ int i;\
+\
+ data_source_access(&pfn->params.DataSource, offset >> 3,\
+ nbytes, buf, &p)
+
+private int
+fn_gets_1(const gs_function_Sd_t * pfn, ulong offset, uint * samples)
+{
+ SETUP_SAMPLES(1, ((offset & 7) + n + 7) >> 3);
+ for (i = 0; i < n; ++i) {
+ samples[i] = (*p >> (~offset & 7)) & 1;
+ if (!(++offset & 7))
+ p++;
+ }
+ return 0;
+}
+private int
+fn_gets_2(const gs_function_Sd_t * pfn, ulong offset, uint * samples)
+{
+ SETUP_SAMPLES(2, (((offset & 7) >> 1) + n + 3) >> 2);
+ for (i = 0; i < n; ++i) {
+ samples[i] = (*p >> (6 - (offset & 7))) & 3;
+ if (!((offset += 2) & 7))
+ p++;
+ }
+ return 0;
+}
+private int
+fn_gets_4(const gs_function_Sd_t * pfn, ulong offset, uint * samples)
+{
+ SETUP_SAMPLES(4, (((offset & 7) >> 2) + n + 1) >> 1);
+ for (i = 0; i < n; ++i) {
+ samples[i] = (offset & 4 ? *p++ & 0xf : *p >> 4);
+ }
+ return 0;
+}
+private int
+fn_gets_8(const gs_function_Sd_t * pfn, ulong offset, uint * samples)
+{
+ SETUP_SAMPLES(8, n);
+ for (i = 0; i < n; ++i) {
+ samples[i] = *p++;
+ }
+ return 0;
+}
+private int
+fn_gets_12(const gs_function_Sd_t * pfn, ulong offset, uint * samples)
+{
+ SETUP_SAMPLES(12, (((offset & 7) >> 2) + 3 * n + 1) >> 1);
+ for (i = 0; i < n; ++i) {
+ if (offset & 4)
+ samples[i] = ((*p & 0xf) << 8) + p[1], p += 2;
+ else
+ samples[i] = (*p << 4) + (p[1] >> 4), p++;
+ offset ^= 4;
+ }
+ return 0;
+}
+private int
+fn_gets_16(const gs_function_Sd_t * pfn, ulong offset, uint * samples)
+{
+ SETUP_SAMPLES(16, n * 2);
+ for (i = 0; i < n; ++i) {
+ samples[i] = (*p << 8) + p[1];
+ p += 2;
+ }
+ return 0;
+}
+private int
+fn_gets_24(const gs_function_Sd_t * pfn, ulong offset, uint * samples)
+{
+ SETUP_SAMPLES(24, n * 3);
+ for (i = 0; i < n; ++i) {
+ samples[i] = (*p << 16) + (p[1] << 8) + p[2];
+ p += 3;
+ }
+ return 0;
+}
+private int
+fn_gets_32(const gs_function_Sd_t * pfn, ulong offset, uint * samples)
+{
+ SETUP_SAMPLES(32, n * 4);
+ for (i = 0; i < n; ++i) {
+ samples[i] = (*p << 24) + (p[1] << 16) + (p[2] << 8) + p[3];
+ p += 4;
+ }
+ return 0;
+}
+
+private int (*const fn_get_samples[]) (P3(const gs_function_Sd_t * pfn,
+ ulong offset, uint * samples)) =
+{
+ 0, fn_gets_1, fn_gets_2, 0, fn_gets_4, 0, 0, 0,
+ fn_gets_8, 0, 0, 0, fn_gets_12, 0, 0, 0,
+ fn_gets_16, 0, 0, 0, 0, 0, 0, 0,
+ fn_gets_24, 0, 0, 0, 0, 0, 0, 0,
+ fn_gets_32
+};
+
+/* Calculate a result by multilinear interpolation. */
+private void
+fn_interpolate_linear(const gs_function_Sd_t *pfn, const float *fparts,
+ const ulong *factors, float *samples, ulong offset, int m)
+{
+ int j;
+
+top:
+ if (m == 0) {
+ uint sdata[max_Sd_n];
+
+ (*fn_get_samples[pfn->params.BitsPerSample])(pfn, offset, sdata);
+ for (j = pfn->params.n - 1; j >= 0; --j)
+ samples[j] = sdata[j];
+ } else {
+ float fpart = *fparts++;
+ float samples1[max_Sd_n];
+
+ if (is_fzero(fpart)) {
+ ++factors;
+ --m;
+ goto top;
+ }
+ fn_interpolate_linear(pfn, fparts, factors + 1, samples,
+ offset, m - 1);
+ fn_interpolate_linear(pfn, fparts, factors + 1, samples1,
+ offset + *factors, m - 1);
+ for (j = pfn->params.n - 1; j >= 0; --j)
+ samples[j] += (samples1[j] - samples[j]) * fpart;
+ }
+}
+
+/* Evaluate a Sampled function. */
+private int
+fn_Sd_evaluate(const gs_function_t * pfn_common, const float *in, float *out)
+{
+ const gs_function_Sd_t *pfn = (const gs_function_Sd_t *)pfn_common;
+ int bps = pfn->params.BitsPerSample;
+ ulong offset = 0;
+ int i;
+ float encoded[max_Sd_m];
+ ulong factors[max_Sd_m];
+ float samples[max_Sd_n];
+
+ /* Encode the input values. */
+
+ for (i = 0; i < pfn->params.m; ++i) {
+ float d0 = pfn->params.Domain[2 * i],
+ d1 = pfn->params.Domain[2 * i + 1];
+ float arg = in[i], enc;
+
+ if (arg < d0)
+ arg = d0;
+ else if (arg > d1)
+ arg = d1;
+ if (pfn->params.Encode) {
+ float e0 = pfn->params.Encode[2 * i];
+ float e1 = pfn->params.Encode[2 * i + 1];
+
+ enc = (arg - d0) * (e1 - e0) / (d1 - d0) + e0;
+ if (enc < 0)
+ encoded[i] = 0;
+ else if (enc >= pfn->params.Size[i] - 1)
+ encoded[i] = pfn->params.Size[i] - 1;
+ else
+ encoded[i] = enc;
+ } else {
+ /* arg is guaranteed to be in bounds, ergo so is enc */
+ encoded[i] = (arg - d0) * (pfn->params.Size[i] - 1) / (d1 - d0);
+ }
+ }
+
+ /* Look up and interpolate the output values. */
+
+ {
+ ulong factor = bps * pfn->params.n;
+
+ for (i = 0; i < pfn->params.m; factor *= pfn->params.Size[i++]) {
+ int ipart = (int)encoded[i];
+
+ offset += (factors[i] = factor) * ipart;
+ encoded[i] -= ipart;
+ }
+ }
+ /****** LINEAR INTERPOLATION ONLY ******/
+ fn_interpolate_linear(pfn, encoded, factors, samples, offset,
+ pfn->params.m);
+
+ /* Encode the output values. */
+
+ for (i = 0; i < pfn->params.n; offset += bps, ++i) {
+ float d0, d1, r0, r1, value;
+
+ if (pfn->params.Range)
+ r0 = pfn->params.Range[2 * i], r1 = pfn->params.Range[2 * i + 1];
+ else
+ r0 = 0, r1 = (1 << bps) - 1;
+ if (pfn->params.Decode)
+ d0 = pfn->params.Decode[2 * i], d1 = pfn->params.Decode[2 * i + 1];
+ else
+ d0 = r0, d1 = r1;
+
+ value = samples[i] * (d1 - d0) / ((1 << bps) - 1) + d0;
+ if (value < r0)
+ out[i] = r0;
+ else if (value > r1)
+ out[i] = r1;
+ else
+ out[i] = value;
+ }
+
+ return 0;
+}
+
+/* Test whether a Sampled function is monotonic. */
+/* Since this can be very time-consuming, we only do it if necessary. */
+private int
+fn_Sd_is_monotonic(const gs_function_t * pfn_common,
+ const float *lower, const float *upper, bool must_know)
+{
+ if (!must_know)
+ return gs_error_undefined; /* don't know */
+/****** NYI ******/
+ return gs_error_undefined;
+}
+
+/* Free the parameters of a Sampled function. */
+void
+gs_function_Sd_free_params(gs_function_Sd_params_t * params, gs_memory_t * mem)
+{
+ gs_free_object(mem, (void *)params->Size, "Size"); /* break const */
+ gs_free_object(mem, (void *)params->Decode, "Decode"); /* break const */
+ gs_free_object(mem, (void *)params->Encode, "Encode"); /* break const */
+ fn_common_free_params((gs_function_params_t *) params, mem);
+}
+
+/* Allocate and initialize a Sampled function. */
+int
+gs_function_Sd_init(gs_function_t ** ppfn,
+ const gs_function_Sd_params_t * params, gs_memory_t * mem)
+{
+ static const gs_function_head_t function_Sd_head =
+ {
+ function_type_Sampled,
+ (fn_evaluate_proc_t) fn_Sd_evaluate,
+ (fn_is_monotonic_proc_t) fn_Sd_is_monotonic,
+ (fn_free_params_proc_t) gs_function_Sd_free_params,
+ fn_common_free
+ };
+ int code;
+ int i;
+
+ *ppfn = 0; /* in case of error */
+ code = fn_check_mnDR((const gs_function_params_t *)params,
+ params->m, params->n);
+ if (code < 0)
+ return code;
+ if (params->m > max_Sd_m)
+ return_error(gs_error_limitcheck);
+ switch (params->Order) {
+ case 0: /* use default */
+ case 1:
+ case 3:
+ break;
+ default:
+ return_error(gs_error_rangecheck);
+ }
+ switch (params->BitsPerSample) {
+ case 1:
+ case 2:
+ case 4:
+ case 8:
+ case 12:
+ case 16:
+ case 24:
+ case 32:
+ break;
+ default:
+ return_error(gs_error_rangecheck);
+ }
+ for (i = 0; i < params->m; ++i)
+ if (params->Size[i] <= 0)
+ return_error(gs_error_rangecheck);
+ {
+ gs_function_Sd_t *pfn =
+ gs_alloc_struct(mem, gs_function_Sd_t, &st_function_Sd,
+ "gs_function_Sd_init");
+
+ if (pfn == 0)
+ return_error(gs_error_VMerror);
+ pfn->params = *params;
+ if (params->Order == 0)
+ pfn->params.Order = 1; /* default */
+ pfn->head = function_Sd_head;
+ *ppfn = (gs_function_t *) pfn;
+ }
+ return 0;
+}
diff --git a/pstoraster/gsfunc0.h b/pstoraster/gsfunc0.h
new file mode 100644
index 000000000..b423e0fa2
--- /dev/null
+++ b/pstoraster/gsfunc0.h
@@ -0,0 +1,66 @@
+/* Copyright (C) 1997 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Definitions for FunctionType 0 (Sampled) Functions */
+
+#ifndef gsfunc0_INCLUDED
+# define gsfunc0_INCLUDED
+
+#include "gsfunc.h"
+#include "gsdsrc.h"
+
+/* ---------------- Types and structures ---------------- */
+
+/* Define the Function type. */
+#define function_type_Sampled 0
+
+/* Define Sampled functions. */
+typedef struct gs_function_Sd_params_s {
+ gs_function_params_common;
+ int Order; /* 1 or 3, optional */
+ gs_data_source_t DataSource;
+ int BitsPerSample; /* 1, 2, 4, 8, 12, 16, 24, 32 */
+ const float *Encode; /* 2 x m, optional */
+ const float *Decode; /* 2 x n, optional */
+ const int *Size; /* m */
+} gs_function_Sd_params_t;
+
+#define private_st_function_Sd() /* in gsfunc.c */\
+ gs_private_st_suffix_add3(st_function_Sd, gs_function_Sd_t,\
+ "gs_function_Sd_t", function_Sd_enum_ptrs, function_Sd_reloc_ptrs,\
+ st_function, params.Encode, params.Decode, params.Size)
+
+/* ---------------- Procedures ---------------- */
+
+/* Allocate and initialize a Sampled function. */
+int gs_function_Sd_init(P3(gs_function_t ** ppfn,
+ const gs_function_Sd_params_t * params,
+ gs_memory_t * mem));
+
+/* Free the parameters of a Sampled function. */
+void gs_function_Sd_free_params(P2(gs_function_Sd_params_t * params,
+ gs_memory_t * mem));
+
+#endif /* gsfunc0_INCLUDED */
diff --git a/pstoraster/gsfunc3.c b/pstoraster/gsfunc3.c
new file mode 100644
index 000000000..6e7e1d978
--- /dev/null
+++ b/pstoraster/gsfunc3.c
@@ -0,0 +1,361 @@
+/* Copyright (C) 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Implementation of LL3 Functions */
+#include "math_.h"
+#include "gx.h"
+#include "gserrors.h"
+#include "gsfunc3.h"
+#include "gxfunc.h"
+
+/* ---------------- Exponential Interpolation functions ---------------- */
+
+typedef struct gs_function_ElIn_s {
+ gs_function_head_t head;
+ gs_function_ElIn_params_t params;
+} gs_function_ElIn_t;
+
+private_st_function_ElIn();
+
+/* Evaluate an Exponential Interpolation function. */
+private int
+fn_ElIn_evaluate(const gs_function_t * pfn_common, const float *in, float *out)
+{
+ const gs_function_ElIn_t *const pfn =
+ (const gs_function_ElIn_t *)pfn_common;
+ double arg = in[0], raised;
+ int i;
+
+ if (arg < pfn->params.Domain[0])
+ arg = pfn->params.Domain[0];
+ else if (arg > pfn->params.Domain[1])
+ arg = pfn->params.Domain[1];
+ raised = pow(arg, pfn->params.N);
+ for (i = 0; i < pfn->params.n; ++i) {
+ float v0 = (pfn->params.C0 == 0 ? 0.0 : pfn->params.C0[i]);
+ float v1 = (pfn->params.C1 == 0 ? 1.0 : pfn->params.C1[i]);
+ double value = v0 + raised * (v1 - v0);
+
+ if (pfn->params.Range) {
+ float r0 = pfn->params.Range[2 * i],
+ r1 = pfn->params.Range[2 * i + 1];
+
+ if (value < r0)
+ value = r0;
+ else if (value > r1)
+ value = r1;
+ }
+ out[i] = value;
+ }
+ return 0;
+}
+
+/* Test whether an Exponential function is monotonic. (They always are.) */
+private int
+fn_ElIn_is_monotonic(const gs_function_t * pfn_common,
+ const float *lower, const float *upper, bool must_know)
+{
+ const gs_function_ElIn_t *const pfn =
+ (const gs_function_ElIn_t *)pfn_common;
+
+ if (lower[0] > pfn->params.Domain[1] ||
+ upper[0] < pfn->params.Domain[0]
+ )
+ return_error(gs_error_rangecheck);
+ return 1;
+}
+
+/* Free the parameters of an Exponential Interpolation function. */
+void
+gs_function_ElIn_free_params(gs_function_ElIn_params_t * params,
+ gs_memory_t * mem)
+{
+ gs_free_object(mem, (void *)params->C1, "C1"); /* break const */
+ gs_free_object(mem, (void *)params->C0, "C0"); /* break const */
+ fn_common_free_params((gs_function_params_t *) params, mem);
+}
+
+/* Allocate and initialize an Exponential Interpolation function. */
+int
+gs_function_ElIn_init(gs_function_t ** ppfn,
+ const gs_function_ElIn_params_t * params,
+ gs_memory_t * mem)
+{
+ static const gs_function_head_t function_ElIn_head =
+ {
+ function_type_ExponentialInterpolation,
+ (fn_evaluate_proc_t) fn_ElIn_evaluate,
+ (fn_is_monotonic_proc_t) fn_ElIn_is_monotonic,
+ (fn_free_params_proc_t) gs_function_ElIn_free_params,
+ fn_common_free
+ };
+ int code;
+
+ *ppfn = 0; /* in case of error */
+ code = fn_check_mnDR((const gs_function_params_t *)params, 1, params->n);
+ if (code < 0)
+ return code;
+ if ((params->C0 == 0 || params->C1 == 0) && params->n != 1)
+ return_error(gs_error_rangecheck);
+ if (params->N != floor(params->N)) {
+ /* Non-integral exponent, all inputs must be non-negative. */
+ if (params->Domain[0] < 0)
+ return_error(gs_error_rangecheck);
+ }
+ if (params->N < 0) {
+ /* Negative exponent, input must not be zero. */
+ if (params->Domain[0] <= 0 && params->Domain[1] >= 0)
+ return_error(gs_error_rangecheck);
+ } {
+ gs_function_ElIn_t *pfn =
+ gs_alloc_struct(mem, gs_function_ElIn_t, &st_function_ElIn,
+ "gs_function_ElIn_init");
+
+ if (pfn == 0)
+ return_error(gs_error_VMerror);
+ pfn->params = *params;
+ pfn->params.m = 1;
+ pfn->head = function_ElIn_head;
+ *ppfn = (gs_function_t *) pfn;
+ }
+ return 0;
+}
+
+/* ---------------- 1-Input Stitching functions ---------------- */
+
+typedef struct gs_function_1ItSg_s {
+ gs_function_head_t head;
+ gs_function_1ItSg_params_t params;
+} gs_function_1ItSg_t;
+
+private_st_function_1ItSg();
+
+/* Evaluate a 1-Input Stitching function. */
+private int
+fn_1ItSg_evaluate(const gs_function_t * pfn_common, const float *in, float *out)
+{
+ const gs_function_1ItSg_t *const pfn =
+ (const gs_function_1ItSg_t *)pfn_common;
+ float arg = in[0], b0, b1, e0, encoded;
+ int k = pfn->params.k;
+ int i;
+
+ if (arg < pfn->params.Domain[0]) {
+ arg = pfn->params.Domain[0];
+ i = 0;
+ } else if (arg > pfn->params.Domain[1]) {
+ arg = pfn->params.Domain[1];
+ i = k - 1;
+ } else {
+ for (i = 0; i < k - 1; ++i)
+ if (arg <= pfn->params.Bounds[i])
+ break;
+ }
+ b0 = (i == 0 ? pfn->params.Domain[0] : pfn->params.Bounds[i - 1]);
+ b1 = (i == k - 1 ? pfn->params.Domain[1] : pfn->params.Bounds[i]);
+ e0 = pfn->params.Encode[2 * i];
+ encoded =
+ (arg - b0) * (pfn->params.Encode[2 * i + 1] - e0) / (b1 - b0) + e0;
+ return gs_function_evaluate(pfn->params.Functions[i], &encoded, out);
+}
+
+/* Test whether a 1-Input Stitching function is monotonic. */
+private int
+fn_1ItSg_is_monotonic(const gs_function_t * pfn_common,
+ const float *lower, const float *upper, bool must_know)
+{
+ const gs_function_1ItSg_t *const pfn =
+ (const gs_function_1ItSg_t *)pfn_common;
+
+ if (lower[0] > pfn->params.Domain[1] ||
+ upper[0] < pfn->params.Domain[0]
+ )
+ return_error(gs_error_rangecheck);
+/****** NYI ******/
+ return gs_error_undefined;
+}
+
+/* Free the parameters of a 1-Input Stitching function. */
+void
+gs_function_1ItSg_free_params(gs_function_1ItSg_params_t * params,
+ gs_memory_t * mem)
+{
+ gs_free_object(mem, (void *)params->Encode, "Encode"); /* break const */
+ gs_free_object(mem, (void *)params->Bounds, "Bounds"); /* break const */
+ fn_free_functions((gs_function_t **) params->Functions, /* break const */
+ params->k, mem);
+ fn_common_free_params((gs_function_params_t *) params, mem);
+}
+
+/* Allocate and initialize a 1-Input Stitching function. */
+int
+gs_function_1ItSg_init(gs_function_t ** ppfn,
+ const gs_function_1ItSg_params_t * params, gs_memory_t * mem)
+{
+ static const gs_function_head_t function_1ItSg_head =
+ {
+ function_type_1InputStitching,
+ (fn_evaluate_proc_t) fn_1ItSg_evaluate,
+ (fn_is_monotonic_proc_t) fn_1ItSg_is_monotonic,
+ (fn_free_params_proc_t) gs_function_1ItSg_free_params,
+ fn_common_free
+ };
+ int n = (params->Range == 0 ? 0 : params->n);
+ float prev = params->Domain[0];
+ int i;
+
+ *ppfn = 0; /* in case of error */
+ for (i = 0; i < params->k; ++i) {
+ const gs_function_t *psubfn = params->Functions[i];
+
+ if (psubfn->params.m != 1)
+ return_error(gs_error_rangecheck);
+ if (n == 0)
+ n = psubfn->params.n;
+ else if (psubfn->params.n != n)
+ return_error(gs_error_rangecheck);
+ /* There are only k - 1 Bounds, not k. */
+ if (i < params->k - 1) {
+ if (params->Bounds[i] <= prev)
+ return_error(gs_error_rangecheck);
+ prev = params->Bounds[i];
+ }
+ }
+ if (params->Domain[1] < prev)
+ return_error(gs_error_rangecheck);
+ fn_check_mnDR((const gs_function_params_t *)params, 1, n);
+ {
+ gs_function_1ItSg_t *pfn =
+ gs_alloc_struct(mem, gs_function_1ItSg_t, &st_function_1ItSg,
+ "gs_function_1ItSg_init");
+
+ if (pfn == 0)
+ return_error(gs_error_VMerror);
+ pfn->params = *params;
+ pfn->params.m = 1;
+ pfn->params.n = n;
+ pfn->head = function_1ItSg_head;
+ *ppfn = (gs_function_t *) pfn;
+ }
+ return 0;
+}
+
+/* ---------------- Arrayed Output functions ---------------- */
+
+typedef struct gs_function_AdOt_s {
+ gs_function_head_t head;
+ gs_function_AdOt_params_t params;
+} gs_function_AdOt_t;
+
+private_st_function_AdOt();
+
+/* Evaluate an Arrayed Output function. */
+private int
+fn_AdOt_evaluate(const gs_function_t * pfn_common, const float *in, float *out)
+{
+ const gs_function_AdOt_t *const pfn =
+ (const gs_function_AdOt_t *)pfn_common;
+ int i;
+
+ for (i = 0; i < pfn->params.n; ++i) {
+ int code =
+ gs_function_evaluate(pfn->params.Functions[i], in, out + i);
+
+ if (code < 0)
+ return code;
+ }
+ return 0;
+}
+
+/* Test whether an Arrayed Output function is monotonic. */
+private int
+fn_AdOt_is_monotonic(const gs_function_t * pfn_common,
+ const float *lower, const float *upper, bool must_know)
+{
+ const gs_function_AdOt_t *const pfn =
+ (const gs_function_AdOt_t *)pfn_common;
+ int i;
+
+ for (i = 0; i < pfn->params.n; ++i) {
+ int code =
+ gs_function_is_monotonic(pfn->params.Functions[i], lower, upper,
+ must_know);
+
+ if (code <= 0)
+ return code;
+ }
+ return 1;
+}
+
+/* Free the parameters of an Arrayed Output function. */
+void
+gs_function_AdOt_free_params(gs_function_AdOt_params_t * params,
+ gs_memory_t * mem)
+{
+ fn_free_functions((gs_function_t **) params->Functions, /* break const */
+ params->n, mem);
+ fn_common_free_params((gs_function_params_t *) params, mem);
+}
+
+/* Allocate and initialize an Arrayed Output function. */
+int
+gs_function_AdOt_init(gs_function_t ** ppfn,
+ const gs_function_AdOt_params_t * params, gs_memory_t * mem)
+{
+ static const gs_function_head_t function_AdOt_head =
+ {
+ function_type_ArrayedOutput,
+ (fn_evaluate_proc_t) fn_AdOt_evaluate,
+ (fn_is_monotonic_proc_t) fn_AdOt_is_monotonic,
+ (fn_free_params_proc_t) gs_function_AdOt_free_params,
+ fn_common_free
+ };
+ int m = params->m, n = params->n;
+ int i;
+
+ *ppfn = 0; /* in case of error */
+ if (m <= 0 || n <= 0)
+ return_error(gs_error_rangecheck);
+ for (i = 0; i < n; ++i) {
+ const gs_function_t *psubfn = params->Functions[i];
+
+ if (psubfn->params.m != m || psubfn->params.n != 1)
+ return_error(gs_error_rangecheck);
+ }
+ {
+ gs_function_AdOt_t *pfn =
+ gs_alloc_struct(mem, gs_function_AdOt_t, &st_function_AdOt,
+ "gs_function_AdOt_init");
+
+ if (pfn == 0)
+ return_error(gs_error_VMerror);
+ pfn->params = *params;
+ pfn->params.Domain = 0;
+ pfn->params.Range = 0;
+ pfn->head = function_AdOt_head;
+ *ppfn = (gs_function_t *) pfn;
+ }
+ return 0;
+}
diff --git a/pstoraster/gsfunc3.h b/pstoraster/gsfunc3.h
new file mode 100644
index 000000000..1a35a6564
--- /dev/null
+++ b/pstoraster/gsfunc3.h
@@ -0,0 +1,114 @@
+/* Copyright (C) 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Definitions for LL3 Functions */
+
+#ifndef gsfunc3_INCLUDED
+# define gsfunc3_INCLUDED
+
+#include "gsfunc.h"
+#include "gsdsrc.h"
+
+/* ---------------- Types and structures ---------------- */
+
+/*
+ * Define the Function types.
+ * See gsfunc.h for why gs_function_type_t can't be an enum type.
+ */
+enum {
+ function_type_ExponentialInterpolation = 2,
+ function_type_1InputStitching = 3,
+ /* For internal use only */
+ function_type_ArrayedOutput = -1
+};
+
+/* Define Exponential Interpolation functions. */
+typedef struct gs_function_ElIn_params_s {
+ gs_function_params_common;
+ const float *C0; /* n, optional */
+ const float *C1; /* n, optional */
+ float N;
+} gs_function_ElIn_params_t;
+
+#define private_st_function_ElIn() /* in gsfunc.c */\
+ gs_private_st_suffix_add2(st_function_ElIn, gs_function_ElIn_t,\
+ "gs_function_ElIn_t", function_ElIn_enum_ptrs, function_ElIn_reloc_ptrs,\
+ st_function, params.C0, params.C1)
+
+/* Define 1-Input Stitching functions. */
+typedef struct gs_function_1ItSg_params_s {
+ gs_function_params_common;
+ int k;
+ const gs_function_t *const *Functions; /* k */
+ const float *Bounds; /* k - 1 */
+ const float *Encode; /* 2 x k */
+} gs_function_1ItSg_params_t;
+
+#define private_st_function_1ItSg() /* in gsfunc.c */\
+ gs_private_st_suffix_add3(st_function_1ItSg, gs_function_1ItSg_t,\
+ "gs_function_1ItSg_t", function_1ItSg_enum_ptrs, function_1ItSg_reloc_ptrs,\
+ st_function, params.Functions, params.Bounds, params.Encode)
+
+/*
+ * Define Arrayed Output functions. These consist of n m x 1 functions
+ * whose outputs are assembled into the output of the arrayed function.
+ * We use them to handle certain PostScript constructs that can accept
+ * either a single n-output function or n 1-output functions.
+ *
+ * Note that for this type, and only this type, both Domain and Range
+ * are ignored (0).
+ */
+typedef struct gs_function_AdOt_params_s {
+ gs_function_params_common;
+ const gs_function_t *const *Functions; /* n */
+} gs_function_AdOt_params_t;
+
+#define private_st_function_AdOt() /* in gsfunc.c */\
+ gs_private_st_suffix_add1(st_function_AdOt, gs_function_AdOt_t,\
+ "gs_function_AdOt_t", function_AdOt_enum_ptrs, function_AdOt_reloc_ptrs,\
+ st_function, params.Functions)
+
+/* ---------------- Procedures ---------------- */
+
+/* Allocate and initialize functions of specific types. */
+int gs_function_ElIn_init(P3(gs_function_t ** ppfn,
+ const gs_function_ElIn_params_t * params,
+ gs_memory_t * mem));
+int gs_function_1ItSg_init(P3(gs_function_t ** ppfn,
+ const gs_function_1ItSg_params_t * params,
+ gs_memory_t * mem));
+int gs_function_AdOt_init(P3(gs_function_t ** ppfn,
+ const gs_function_AdOt_params_t * params,
+ gs_memory_t * mem));
+
+/* Free parameters of specific types. */
+void gs_function_ElIn_free_params(P2(gs_function_ElIn_params_t * params,
+ gs_memory_t * mem));
+void gs_function_1ItSg_free_params(P2(gs_function_1ItSg_params_t * params,
+ gs_memory_t * mem));
+void gs_function_AdOt_free_params(P2(gs_function_AdOt_params_t * params,
+ gs_memory_t * mem));
+
+#endif /* gsfunc3_INCLUDED */
diff --git a/pstoraster/gsgc.h b/pstoraster/gsgc.h
new file mode 100644
index 000000000..56dc1482b
--- /dev/null
+++ b/pstoraster/gsgc.h
@@ -0,0 +1,91 @@
+/* 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 supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Library-level interface to garbage collector */
+
+/*
+ * This API is not strictly at the library level, since it references
+ * gs_ref_memory_t and the 4 PostScript memory spaces; however, the former
+ * concept already leaks into the library's standard allocator, and the
+ * latter is relatively small and harmless.
+ */
+
+#ifndef gsgc_INCLUDED
+# define gsgc_INCLUDED
+
+/*
+ * Define the VM space numbers, in increasing order of dynamism. Pointers
+ * from a higher-numbered space to the same or a lower-numbered space are
+ * always allowed, but not vice versa. Foreign space (the most static) is
+ * internal, the rest are visible to the programmer; the index of foreign
+ * space must be 0, so that we don't have to set any space bits in scalar
+ * refs (PostScript objects).
+ */
+typedef enum {
+ i_vm_foreign = 0, /* must be 0 */
+ i_vm_system,
+ i_vm_global,
+ i_vm_local,
+ i_vm_max = i_vm_local
+} i_vm_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
+/*
+ * r_space_bits is only defined in PostScript interpreters, but if it is
+ * defined, we want to make sure it's 2.
+ */
+#ifdef r_space_bits
+# if r_space_bits != 2
+Error_r_space_bits_is_not_2;
+# endif
+#endif
+typedef union vm_spaces_s {
+ gs_ref_memory_t *indexed[4 /*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 structures, and local variables */
+/* of type vm_spaces, are 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
+
+/*
+ * Define the top-level entry to the garbage collector.
+ */
+void gs_reclaim(P2(vm_spaces * pspaces, bool global));
+
+#endif /* gsgc_INCLUDED */
diff --git a/pstoraster/gshsb.c b/pstoraster/gshsb.c
new file mode 100644
index 000000000..6bd5479de
--- /dev/null
+++ b/pstoraster/gshsb.c
@@ -0,0 +1,171 @@
+/* Copyright (C) 1994, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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, diff;
+ long 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')) {
+ dlprintf7("[c]hsb(%g,%g,%g)->VSFI(%ld,%d,%ld,%d)->\n",
+ hue, saturation, brightness, V, S, F, I);
+ dlprintf6(" 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..c218711a6
--- /dev/null
+++ b/pstoraster/gshsb.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 supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Client interface to HSB color routines */
+
+#ifndef gshsb_INCLUDED
+# define gshsb_INCLUDED
+
+int gs_sethsbcolor(P4(gs_state *, floatp, floatp, floatp)), gs_currenthsbcolor(P2(const gs_state *, float[3]));
+
+#endif /* gshsb_INCLUDED */
diff --git a/pstoraster/gsht.c b/pstoraster/gsht.c
new file mode 100644
index 000000000..426dfe463
--- /dev/null
+++ b/pstoraster/gsht.c
@@ -0,0 +1,651 @@
+/* Copyright (C) 1989, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* setscreen operator for Ghostscript library */
+#include "memory_.h"
+#include <stdlib.h> /* for qsort */
+#include "gx.h"
+#include "gserrors.h"
+#include "gsstruct.h"
+#include "gsutil.h" /* for gs_next_ids */
+#include "gxarith.h" /* for igcd */
+#include "gzstate.h"
+#include "gxdevice.h" /* for gzht.h */
+#include "gzht.h"
+
+/* Forward declarations */
+void gx_set_effective_transfer(P1(gs_state *));
+
+/* Structure types */
+public_st_ht_order();
+private_st_ht_order_component();
+public_st_ht_order_comp_element();
+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_spot:
+ENUM_RETURN((hptr->params.spot.transfer == 0 ?
+ hptr->params.spot.transfer_closure.data :
+ 0));
+ case ht_type_threshold:
+ENUM_RETURN_CONST_STRING_PTR(gs_halftone, params.threshold.thresholds);
+ case ht_type_client_order:
+ENUM_RETURN(hptr->params.client_order.client_data);
+ case ht_type_multiple:
+ case ht_type_multiple_colorscreen:
+ENUM_RETURN(hptr->params.multiple.components);
+ case ht_type_none:
+ case ht_type_screen:
+ case ht_type_colorscreen:
+return 0;
+}
+case 1:
+switch (hptr->type) {
+ case ht_type_threshold:
+ ENUM_RETURN((hptr->params.threshold.transfer == 0 ?
+ hptr->params.threshold.transfer_closure.data :
+ 0));
+ case ht_type_client_order:
+ ENUM_RETURN(hptr->params.threshold.transfer_closure.data);
+ default:
+ return 0;
+}
+ENUM_PTRS_END
+
+private RELOC_PTRS_BEGIN(halftone_reloc_ptrs)
+{
+ switch (hptr->type) {
+ case ht_type_spot:
+ if (hptr->params.spot.transfer == 0)
+ RELOC_PTR(gs_halftone, params.spot.transfer_closure.data);
+ break;
+ case ht_type_threshold:
+ RELOC_CONST_STRING_PTR(gs_halftone, params.threshold.thresholds);
+ if (hptr->params.threshold.transfer == 0)
+ RELOC_PTR(gs_halftone, params.threshold.transfer_closure.data);
+ break;
+ case ht_type_client_order:
+ RELOC_PTR(gs_halftone, params.client_order.client_data);
+ RELOC_PTR(gs_halftone, params.client_order.transfer_closure.data);
+ 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:
+ 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;
+}
+
+/* .setscreenphase */
+int
+gx_imager_setscreenphase(gs_imager_state * pis, int x, int y,
+ gs_color_select_t select)
+{
+ if (select == gs_color_select_all) {
+ int i;
+
+ for (i = 0; i < gs_color_select_count; ++i)
+ gx_imager_setscreenphase(pis, x, y, (gs_color_select_t) i);
+ return 0;
+ } else if (select < 0 || select >= gs_color_select_count)
+ return_error(gs_error_rangecheck);
+ pis->screen_phase[select].x = x;
+ pis->screen_phase[select].y = y;
+ return 0;
+}
+int
+gs_setscreenphase(gs_state * pgs, int x, int y, gs_color_select_t select)
+{
+ int code = gx_imager_setscreenphase((gs_imager_state *) pgs, x, y,
+ select);
+
+ /*
+ * If we're only setting the source phase, we don't need to do
+ * unset_dev_color, because the source phase doesn't affect painting
+ * with the current color.
+ */
+ if (code >= 0 && (select == gs_color_select_texture ||
+ select == gs_color_select_all)
+ )
+ gx_unset_dev_color(pgs);
+ return code;
+}
+
+/* .currentscreenphase */
+int
+gs_currentscreenphase(const gs_state * pgs, gs_int_point * pphase,
+ gs_color_select_t select)
+{
+ if (select < 0 || select >= gs_color_select_count)
+ return_error(gs_error_rangecheck);
+ *pphase = pgs->screen_phase[select];
+ 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_memory(gs_screen_enum * penum, gs_state * pgs,
+ gs_screen_halftone * phsp, bool accurate, gs_memory_t * mem)
+{
+ gs_point pt;
+ int code = gs_screen_init_memory(penum, pgs, phsp, accurate, mem);
+
+ 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;
+}
+
+/* Internal procedure to allocate and initialize either an internally */
+/* generated or a client-defined halftone order. */
+private int
+gx_ht_alloc_ht_order(gx_ht_order * porder, uint width, uint height,
+ uint num_levels, uint num_bits, uint strip_shift, gs_memory_t * mem)
+{
+ gx_compute_cell_values(&porder->params);
+ porder->width = width;
+ porder->height = height;
+ porder->raster = bitmap_raster(width);
+ porder->shift = strip_shift;
+ porder->orig_height = porder->height;
+ porder->orig_shift = porder->shift;
+ porder->full_height = ht_order_full_height(porder);
+ porder->num_levels = num_levels;
+ porder->num_bits = num_bits;
+ porder->levels =
+ (uint *) gs_alloc_byte_array(mem, num_levels, sizeof(uint),
+ "ht order(levels)");
+ porder->bits =
+ (gx_ht_bit *) gs_alloc_byte_array(mem, num_bits, sizeof(gx_ht_bit),
+ "ht order(bits)");
+ if (porder->levels == 0 || porder->bits == 0) {
+ gs_free_object(mem, porder->bits, "ht order(bits)");
+ gs_free_object(mem, porder->levels, "ht order(levels)");
+ return_error(gs_error_VMerror);
+ }
+ porder->cache = 0;
+ porder->transfer = 0;
+ return 0;
+}
+
+/* Allocate and initialize the contents of a halftone order. */
+/* The client must have set the defining values in porder->params. */
+int
+gx_ht_alloc_order(gx_ht_order * porder, uint width, uint height,
+ uint strip_shift, uint num_levels, gs_memory_t * mem)
+{
+ gx_ht_order order;
+ int code;
+
+ order = *porder;
+ gx_compute_cell_values(&order.params);
+ code = gx_ht_alloc_ht_order(&order, width, height, num_levels,
+ width * height, strip_shift, mem);
+ if (code < 0)
+ return code;
+ *porder = order;
+ return 0;
+}
+
+/* Allocate and initialize the contents of a client-defined halftone order. */
+int
+gx_ht_alloc_client_order(gx_ht_order * porder, uint width, uint height,
+ uint num_levels, uint num_bits, gs_memory_t * mem)
+{
+ gx_ht_order order;
+ int code;
+
+ order = *porder;
+ order.params.M = width, order.params.N = 0;
+ order.params.R = 1;
+ order.params.M1 = height, order.params.N1 = 0;
+ order.params.R1 = 1;
+ gx_compute_cell_values(&order.params);
+ code = gx_ht_alloc_ht_order(&order, width, height, num_levels,
+ num_bits, 0, mem);
+ if (code < 0)
+ return code;
+ *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;
+
+ dlputs("[H]Sorted samples:\n");
+ for (i = 0; i < N; i++)
+ dlprintf3("%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 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, porder->orig_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 a single offset/mask. */
+void
+gx_ht_construct_bit(gx_ht_bit * bit, int width, int bit_num)
+{
+ uint padding = bitmap_raster(width) * 8 - width;
+ int pix = bit_num;
+ ht_mask_t mask;
+ byte *pb;
+
+ pix += pix / width * padding;
+ bit->offset = (pix >> 3) & -size_of(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. */
+ bit->mask = 0;
+ for (pb = (byte *) & bit->mask + (sizeof(mask) - 1);
+ mask != 0;
+ mask >>= 8, pb--
+ )
+ *pb = (byte) mask;
+}
+
+/* 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 i;
+ gx_ht_bit *phb;
+
+ for (i = 0, phb = porder->bits; i < porder->num_bits; i++, phb++)
+ gx_ht_construct_bit(phb, porder->width, phb->offset);
+#ifdef DEBUG
+ if (gs_debug_c('H')) {
+ dlprintf1("[H]Halftone order bits 0x%lx:\n", (ulong) porder->bits);
+ for (i = 0, phb = porder->bits; i < porder->num_bits; i++, phb++)
+ dlprintf3("%4d: %u:0x%lx\n", i, phb->offset,
+ (ulong) phb->mask);
+ }
+#endif
+}
+
+/* Release a gx_device_halftone by freeing its components. */
+/* (Don't free the gx_device_halftone itself.) */
+void
+gx_ht_order_release(gx_ht_order * porder, gs_memory_t * mem, bool free_cache)
+{
+ if (free_cache && porder->cache)
+ gx_ht_free_cache(mem, porder->cache);
+ gs_free_object(mem, porder->transfer, "gx_ht_order_release(transfer)");
+ gs_free_object(mem, porder->bits, "gx_ht_order_release(bits)");
+ gs_free_object(mem, porder->levels, "gx_ht_order_release(levels)");
+}
+void
+gx_device_halftone_release(gx_device_halftone * pdht, gs_memory_t * mem)
+{
+ if (pdht->components) {
+ int i;
+
+ /* One of the components might be the same as the default */
+ /* order, so check that we don't free it twice. */
+ for (i = 0; i < pdht->num_comp; ++i)
+ if (pdht->components[i].corder.bits !=
+ pdht->order.bits
+ ) { /* Currently, all orders except the default one */
+ /* own their caches. */
+ gx_ht_order_release(&pdht->components[i].corder, mem, true);
+ }
+ gs_free_object(mem, pdht->components,
+ "gx_dev_ht_release(components)");
+ pdht->components = 0;
+ pdht->num_comp = 0;
+ }
+ gx_ht_order_release(&pdht->order, mem, false);
+}
+
+/* Install a device halftone in an imager state. */
+/* Note that this does not read or update the client halftone. */
+int
+gx_imager_dev_ht_install(gs_imager_state * pis,
+ const gx_device_halftone * pdht, gs_halftone_type type, const gx_device * dev)
+{
+ gx_device_halftone *pgdht = pis->dev_ht;
+
+ if ((ulong) pdht->order.raster * (pdht->order.num_bits /
+ pdht->order.width) > pis->ht_cache->bits_size
+ )
+ return_error(gs_error_limitcheck);
+ if (pgdht != 0 && pgdht->rc.ref_count == 1 &&
+ pgdht->rc.memory == pdht->rc.memory
+ ) { /* The current device halftone isn't shared. */
+ /* Just release its components. */
+ gx_device_halftone_release(pgdht, pgdht->rc.memory);
+ } else { /* The device halftone is shared or not yet allocated. */
+ rc_unshare_struct(pis->dev_ht, gx_device_halftone,
+ &st_device_halftone, pdht->rc.memory,
+ return_error(gs_error_VMerror),
+ "gx_imager_dev_ht_install");
+ pgdht = pis->dev_ht;
+ }
+ {
+ rc_header rc;
+
+ rc = pgdht->rc;
+ *pgdht = *pdht;
+ pgdht->rc = rc;
+ }
+ pgdht->id = gs_next_ids(1);
+ pgdht->type = type;
+ /* 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(pis->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 = dev->color_info.num_components;
+ const gs_ht_separation_name *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 &&
+ (type == ht_type_colorscreen ||
+ 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_imager_set_effective_xfer(pis);
+ return 0;
+}
+
+/*
+ * Install a new halftone in the graphics state. Note that we copy the top
+ * level of the gs_halftone and the gx_device_halftone, and take ownership
+ * of any substructures.
+ */
+int
+gx_ht_install(gs_state * pgs, const gs_halftone * pht,
+ const gx_device_halftone * pdht)
+{
+ gs_memory_t *mem = pht->rc.memory;
+ gs_halftone *old_ht = pgs->halftone;
+ gs_halftone *new_ht;
+ int code;
+
+ if (old_ht != 0 && old_ht->rc.memory == mem &&
+ old_ht->rc.ref_count == 1
+ )
+ new_ht = old_ht;
+ else
+ rc_alloc_struct_1(new_ht, gs_halftone, &st_halftone,
+ mem, return_error(gs_error_VMerror),
+ "gx_ht_install(new halftone)");
+ code = gx_imager_dev_ht_install((gs_imager_state *) pgs,
+ pdht, pht->type, gs_currentdevice_inline(pgs));
+ if (code < 0) {
+ if (new_ht != old_ht)
+ gs_free_object(mem, new_ht, "gx_ht_install(new halftone)");
+ return code;
+ }
+ if (new_ht != old_ht)
+ rc_decrement(old_ht, "gx_ht_install(old halftone)");
+ {
+ rc_header rc;
+
+ rc = new_ht->rc;
+ *new_ht = *pht;
+ new_ht->rc = rc;
+ }
+ pgs->halftone = new_ht;
+ gx_unset_dev_color(pgs);
+ return 0;
+}
+
+/* Reestablish the effective transfer functions, taking into account */
+/* any overrides from halftone dictionaries. */
+void
+gx_imager_set_effective_xfer(gs_imager_state * pis)
+{
+ const gx_device_halftone *pdht = pis->dev_ht;
+
+ pis->effective_transfer = pis->set_transfer; /* default */
+ if (pdht == 0)
+ return; /* not initialized yet */
+ if (pdht->components == 0) { /* Check for transfer function override in single halftone */
+ gx_transfer_map *pmap = pdht->order.transfer;
+
+ if (pmap != 0)
+ pis->effective_transfer.indexed[0] =
+ pis->effective_transfer.indexed[1] =
+ pis->effective_transfer.indexed[2] =
+ pis->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)
+ pis->effective_transfer.indexed[i] = pmap;
+ }
+ }
+}
+void
+gx_set_effective_transfer(gs_state * pgs)
+{
+ gx_imager_set_effective_xfer((gs_imager_state *) pgs);
+}
diff --git a/pstoraster/gsht.h b/pstoraster/gsht.h
new file mode 100644
index 000000000..fd1682d19
--- /dev/null
+++ b/pstoraster/gsht.h
@@ -0,0 +1,78 @@
+/* Copyright (C) 1993, 1994, 1997 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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
+
+/* 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..f79afa730
--- /dev/null
+++ b/pstoraster/gsht1.c
@@ -0,0 +1,449 @@
+/* Copyright (C) 1994, 1995, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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_debug_c('.') ? max_tile_bytes_SMALL : max_tile_bytes_LARGE)
+#endif
+
+/* Imports from gscolor.c */
+void load_transfer_map(P3(gs_state *, gx_transfer_map *, floatp));
+
+/* Forward declarations */
+private int process_spot(P4(gx_ht_order *, gs_state *,
+ gs_spot_halftone *, gs_memory_t *));
+private int process_threshold(P4(gx_ht_order *, gs_state *,
+ gs_threshold_halftone *, gs_memory_t *));
+private int process_client_order(P4(gx_ht_order *, gs_state *,
+ gs_client_order_halftone *, gs_memory_t *));
+
+/* Structure types */
+public_st_halftone_component();
+public_st_ht_component_element();
+
+/* GC procedures */
+
+#define hptr ((gs_halftone_component *)vptr)
+
+private
+ENUM_PTRS_BEGIN(halftone_component_enum_ptrs) return 0;
+
+case 0:
+switch (hptr->type)
+{
+ case ht_type_spot:
+ENUM_RETURN((hptr->params.spot.transfer == 0 ?
+ hptr->params.spot.transfer_closure.data :
+ 0));
+ case ht_type_threshold:
+ENUM_RETURN_CONST_STRING_PTR(gs_halftone_component,
+ params.threshold.thresholds);
+ case ht_type_client_order:
+ENUM_RETURN(hptr->params.client_order.client_data);
+ default: /* not possible */
+return 0;
+}
+case 1:
+switch (hptr->type) {
+ case ht_type_threshold:
+ ENUM_RETURN((hptr->params.threshold.transfer == 0 ?
+ hptr->params.threshold.transfer_closure.data :
+ 0));
+ case ht_type_client_order:
+ ENUM_RETURN(hptr->params.threshold.transfer_closure.data);
+ default:
+ return 0;
+}
+ENUM_PTRS_END
+
+private RELOC_PTRS_BEGIN(halftone_component_reloc_ptrs)
+{
+ switch (hptr->type) {
+ case ht_type_spot:
+ if (hptr->params.spot.transfer == 0)
+ RELOC_VAR(hptr->params.spot.transfer_closure.data);
+ break;
+ case ht_type_threshold:
+ RELOC_CONST_STRING_VAR(hptr->params.threshold.thresholds);
+ if (hptr->params.threshold.transfer == 0)
+ RELOC_VAR(hptr->params.threshold.transfer_closure.data);
+ break;
+ case ht_type_client_order:
+ RELOC_VAR(hptr->params.client_order.client_data);
+ RELOC_VAR(hptr->params.client_order.transfer_closure.data);
+ break;
+ default:
+ break;
+ }
+}
+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)
+{
+ gs_halftone ht;
+
+ ht = *pht;
+ ht.rc.memory = pgs->memory;
+ return gs_sethalftone_allocated(pgs, &ht);
+}
+int
+gs_sethalftone_allocated(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;
+ dev_ht.rc.memory = pht->rc.memory;
+ 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 = pht->rc.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_memory(&senum, pgs,
+ &phc[ci], gs_currentaccuratescreens(), mem);
+ 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, mem);
+ if (code < 0)
+ return code;
+ pdht->components = 0;
+ break;
+ case ht_type_threshold:
+ code = process_threshold(&pdht->order, pgs,
+ &pht->params.threshold, mem);
+ if (code < 0)
+ return code;
+ pdht->components = 0;
+ break;
+ case ht_type_client_order:
+ code = process_client_order(&pdht->order, pgs,
+ &pht->params.client_order, mem);
+ 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, mem);
+ break;
+ case ht_type_threshold:
+ code = process_threshold(&poc->corder, pgs,
+ &phc->params.threshold, mem);
+ break;
+ case ht_type_client_order:
+ code = process_client_order(&poc->corder, pgs,
+ &phc->params.client_order, mem);
+ 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 proc, gs_mapping_closure_t * pmc,
+ gs_memory_t * mem)
+{
+ gx_transfer_map *pmap;
+
+ if (proc == 0 && pmc->proc == 0)
+ return 0;
+ pmap = gs_alloc_struct(mem, gx_transfer_map, &st_transfer_map,
+ "process_transfer");
+ if (pmap == 0)
+ return_error(gs_error_VMerror);
+ pmap->proc = proc; /* 0 => use closure */
+ pmap->closure = *pmc;
+ 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_memory_t * mem)
+{
+ gs_screen_enum senum;
+
+ int code = gx_ht_process_screen_memory(&senum, pgs, &phsp->screen,
+ phsp->accurate_screens, mem);
+
+ if (code < 0)
+ return code;
+ *porder = senum.order;
+ return process_transfer(porder, pgs, phsp->transfer,
+ &phsp->transfer_closure, mem);
+}
+
+/* Process a threshold plane. */
+private int
+process_threshold(gx_ht_order * porder, gs_state * pgs,
+ gs_threshold_halftone * phtp, gs_memory_t * mem)
+{
+ int code;
+
+ porder->params.M = phtp->width, porder->params.N = 0;
+ porder->params.R = 1;
+ porder->params.M1 = phtp->height, porder->params.N1 = 0;
+ porder->params.R1 = 1;
+ code = gx_ht_alloc_order(porder, phtp->width, phtp->height,
+ 0, 256, mem);
+ if (code < 0)
+ return code;
+ gx_ht_construct_threshold_order(porder, phtp->thresholds.data);
+ return process_transfer(porder, pgs, phtp->transfer,
+ &phtp->transfer_closure, mem);
+}
+
+/* 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);
+}
+
+/* Process a client-order plane. */
+private int
+process_client_order(gx_ht_order * porder, gs_state * pgs,
+ gs_client_order_halftone * phcop, gs_memory_t * mem)
+{
+ int code = (*phcop->procs->create_order) (porder, pgs, phcop, mem);
+
+ if (code < 0)
+ return code;
+ return process_transfer(porder, pgs, NULL,
+ &phcop->transfer_closure, mem);
+}
diff --git a/pstoraster/gsht1.h b/pstoraster/gsht1.h
new file mode 100644
index 000000000..05d220191
--- /dev/null
+++ b/pstoraster/gsht1.h
@@ -0,0 +1,60 @@
+/* Copyright (C) 1994, 1997 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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.
+ */
+#ifndef gs_halftone_DEFINED
+# define gs_halftone_DEFINED
+typedef struct gs_halftone_s gs_halftone;
+
+#endif
+/*
+ * gs_halftone structures may have complex substructures. We provide two
+ * procedures for setting them. gs_halftone assumes that the gs_halftone
+ * structure and all its substructures was allocated with the same allocator
+ * as the gs_state; gs_halftone_allocated looks in the structure itself (the
+ * rc.memory member) to find the allocator that was used. Both procedures
+ * copy the top-level structure (using the appropriate allocator), but take
+ * ownership of the substructures.
+ */
+int gs_sethalftone(P2(gs_state *, gs_halftone *));
+int gs_sethalftone_allocated(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..dccb414d5
--- /dev/null
+++ b/pstoraster/gshtscr.c
@@ -0,0 +1,574 @@
+/* Copyright (C) 1993, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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"
+
+/* Define whether to force all halftones to be strip halftones, */
+/* for debugging. */
+#define FORCE_STRIP_HALFTONES 0
+
+/* 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 < 1 + st_ht_order_max_ptrs) {
+ gs_ptr_type_t ret =
+ ENUM_USING(st_ht_order, &eptr->order, sizeof(eptr->order),
+ index - 1);
+
+ if (ret == 0) /* don't stop early */
+ ENUM_RETURN(0);
+ return ret;
+ }
+ return ENUM_USING(st_halftone, &eptr->halftone, sizeof(eptr->halftone),
+ index - (1 + st_ht_order_max_ptrs));
+}
+ENUM_PTR(0, gs_screen_enum, pgs);
+ENUM_PTRS_END
+
+private RELOC_PTRS_BEGIN(screen_enum_reloc_ptrs)
+{
+ RELOC_PTR(gs_screen_enum, pgs);
+ RELOC_USING(st_halftone, &eptr->halftone, sizeof(gs_halftone));
+ RELOC_USING(st_ht_order, &eptr->order, sizeof(gx_ht_order));
+}
+RELOC_PTRS_END
+
+#undef eptr
+
+/* Define the default value of AccurateScreens that affects */
+/* setscreen and setcolorscreen. */
+private bool screen_accurate_screens;
+
+/* Default AccurateScreens control */
+void
+gs_setaccuratescreens(bool accurate)
+{
+ screen_accurate_screens = accurate;
+}
+bool
+gs_currentaccuratescreens(void)
+{
+ return screen_accurate_screens;
+}
+
+/* Define the MinScreenLevels user parameter similarly. */
+private uint screen_min_screen_levels;
+
+void
+gs_setminscreenlevels(uint levels)
+{
+ screen_min_screen_levels = levels;
+}
+uint
+gs_currentminscreenlevels(void)
+{
+ return screen_min_screen_levels;
+}
+
+/* Initialize the screen control statics at startup. */
+void
+gs_gshtscr_init(gs_memory_t *mem)
+{
+ gs_setaccuratescreens(false);
+ gs_setminscreenlevels(1);
+}
+
+/*
+ * The following implementation notes complement the general discussion of
+ * halftone tiles found in gxdht.h.
+ *
+ * Currently we allow R(') > 1 (i.e., multiple basic cells per multi-cell)
+ * only if AccurateScreens is true or if B (the number of pixels in a basic
+ * cell) < MinScreenLevels; if AccurateScreens is false and B >=
+ * MinScreenLevels, multi-cells and basic cells are the same.
+ *
+ * To find the smallest super-cell for a given multi-cell size, i.e., the
+ * smallest (absolute value) coordinates where the corners of multi-cells
+ * lie on the coordinate axes, we 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'.
+ */
+
+/* Compute the derived values of a halftone tile. */
+void
+gx_compute_cell_values(gx_ht_cell_params_t * phcp)
+{
+ const int M = phcp->M, N = phcp->N, M1 = phcp->M1, N1 = phcp->N1;
+ const uint m = any_abs(M), n = any_abs(N);
+ const uint m1 = any_abs(M1), n1 = any_abs(N1);
+ const ulong C = phcp->C = (ulong) m * m1 + (ulong) n * n1;
+ const int D = igcd(m1, n);
+ const int D1 = igcd(m, n1);
+
+ phcp->D = D, phcp->D1 = D1;
+ phcp->W = C / D, phcp->W1 = C / D1;
+ /* Compute the shift value. */
+ /* If M1 or N is zero, the shift is zero. */
+ if (M1 && N) {
+ int h = 0, k = 0, dy = 0;
+ int shift;
+
+ /*
+ * There may be a faster way to do this: see Knuth vol. 2,
+ * section 4.5.2, Algorithm X (p. 302) and exercise 15
+ * (p. 315, solution p. 523).
+ */
+ while (dy != D)
+ if (dy > D) {
+ if (M1 > 0)
+ ++k;
+ else
+ --k;
+ dy -= m1;
+ } else {
+ if (N > 0)
+ ++h;
+ else
+ --h;
+ dy += n;
+ }
+ shift = h * M + k * N1;
+ /* We just computed what amounts to a right shift; */
+ /* what we want is a left shift. */
+ phcp->S = imod(-shift, phcp->W);
+ } else
+ phcp->S = 0;
+ if_debug12('h', "[h]MNR=(%d,%d)/%d, M'N'R'=(%d,%d)/%d => C=%lu, D=%d, D'=%d, W=%u, W'=%u, S=%d\n",
+ M, N, phcp->R, M1, N1, phcp->R1,
+ C, D, D1, phcp->W, phcp->W1, phcp->S);
+}
+
+/* Forward references */
+private int pick_cell_size(P6(gs_screen_halftone * ph,
+ const gs_matrix * pmat, ulong max_size, uint min_levels, bool accurate,
+ gx_ht_cell_params_t * phcp));
+
+/* 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_memory(gs_screen_enum * penum, gs_state * pgs,
+ gs_screen_halftone * phsp, bool accurate, gs_memory_t * mem)
+{
+ int code =
+ gs_screen_order_init_memory(&penum->order, pgs, phsp, accurate, mem);
+
+ if (code < 0)
+ return code;
+ return
+ gs_screen_enum_init_memory(penum, &penum->order, pgs, phsp, mem);
+}
+
+/* Allocate and initialize a spot screen. */
+/* This is the first half of gs_screen_init_accurate. */
+int
+gs_screen_order_init_memory(gx_ht_order * porder, const gs_state * pgs,
+ gs_screen_halftone * phsp, bool accurate, gs_memory_t * mem)
+{
+ gs_matrix imat;
+ ulong max_size = pgs->ht_cache->bits_size;
+ uint num_levels;
+ int code;
+
+ if (phsp->frequency < 0.1)
+ return_error(gs_error_rangecheck);
+ gs_deviceinitialmatrix(gs_currentdevice(pgs), &imat);
+ code = pick_cell_size(phsp, &imat, max_size,
+ screen_min_screen_levels, accurate,
+ &porder->params);
+ if (code < 0)
+ return code;
+ gx_compute_cell_values(&porder->params);
+ num_levels = porder->params.W * porder->params.D;
+#if !FORCE_STRIP_HALFTONES
+ if (((ulong)porder->params.W1 * bitmap_raster(porder->params.W) +
+ num_levels * sizeof(*porder->levels) +
+ porder->params.W * porder->params.W1 * sizeof(*porder->bits)) <=
+ max_size) {
+ /*
+ * 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, porder->params.W,
+ porder->params.W1, 0,
+ num_levels, mem);
+ porder->height = porder->orig_height = porder->params.D;
+ porder->shift = porder->orig_shift = porder->params.S;
+ } else
+#endif
+ { /* Just allocate the order for a single strip. */
+ code = gx_ht_alloc_order(porder, porder->params.W,
+ porder->params.D, porder->params.S,
+ num_levels, mem);
+ }
+ if (code < 0)
+ return code;
+ return 0;
+}
+
+/*
+ * Given a desired frequency, angle, and minimum number of levels, a maximum
+ * cell size, and an AccurateScreens flag, pick values for M('), N('), and
+ * R('). We want to get a good fit to the requested frequency and angle,
+ * provide at least the requested minimum number of levels, and keep
+ * rendering as fast as possible; trading these criteria off against each
+ * other is what makes the code complicated.
+ *
+ * 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 closest match to the requested
+ * F and A values and doesn't require more than max_size storage for a
+ * single tile. 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 int
+pick_cell_size(gs_screen_halftone * ph, const gs_matrix * pmat, ulong max_size,
+ uint min_levels, bool accurate, gx_ht_cell_params_t * phcp)
+{
+ const bool landscape = (pmat->xy != 0.0 || pmat->yx != 0.0);
+
+ /* Account for a possibly reflected coordinate system. */
+ /* See gxstroke.c for the algorithm. */
+ const bool reflected = pmat->xy * pmat->yx > pmat->xx * pmat->yy;
+ const int reflection = (reflected ? -1 : 1);
+ const int rotation =
+ (landscape ? (pmat->yx < 0 ? 90 : -90) : pmat->xx < 0 ? 180 : 0);
+ const double f0 = ph->frequency, a0 = ph->angle;
+ const double T =
+ fabs((landscape ? pmat->yx / pmat->xy : pmat->xx / pmat->yy));
+ gs_point uv0;
+
+#define u0 uv0.x
+#define v0 uv0.y
+ int rt = 1;
+ double f = 0, a = 0;
+ double e_best = 1000;
+ 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_matrix rmat;
+
+ gs_make_rotation(a0 * reflection + rotation, &rmat);
+ gs_distance_transform(72.0 / f0, 0.0, &rmat, &uv0);
+ gs_distance_transform(u0, v0, pmat, &uv0);
+ if_debug10('h', "[h]Requested: f=%g a=%g mat=[%g %g %g %g] max_size=%lu min_levels=%u =>\n u=%g v=%g\n",
+ ph->frequency, ph->angle,
+ pmat->xx, pmat->xy, pmat->yx, pmat->yy,
+ max_size, min_levels, u0, v0);
+ }
+
+ /* Adjust u and v to reasonable values. */
+
+ if (u0 == 0 && v0 == 0)
+ return_error(gs_error_rangecheck);
+ while ((fabs(u0) + fabs(v0)) * rt < 4)
+ ++rt;
+ try_size:
+ better = false;
+ {
+ int m0 = (int)floor(u0 * rt + 0.0001);
+ int n0 = (int)floor(v0 * rt + 0.0001);
+ gx_ht_cell_params_t p;
+
+ p.R = p.R1 = rt;
+ for (p.M = m0 + 1; p.M >= m0; p.M--)
+ for (p.N = n0 + 1; p.N >= n0; p.N--) {
+ long raster, wt, wt_size;
+ double fr, ar, ft, at, f_diff, a_diff, f_err, a_err;
+
+ p.M1 = (int)floor(p.M / T + 0.5);
+ p.N1 = (int)floor(p.N * T + 0.5);
+ gx_compute_cell_values(&p);
+ if_debug3('h', "[h]trying m=%d, n=%d, r=%d\n", p.M, p.N, rt);
+ wt = p.W;
+ 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 / p.D || raster > max_long / wt)
+ continue;
+ wt_size = raster * wt;
+
+ /* Compute the corresponding values of F and A. */
+
+ if (landscape)
+ ar = atan2(p.M * pmat->xy, p.N * pmat->yx),
+ fr = 72.0 * (p.M == 0 ? pmat->xy / p.N * cos(ar) :
+ pmat->yx / p.M * sin(ar));
+ else
+ ar = atan2(p.N * pmat->xx, p.M * pmat->yy),
+ fr = 72.0 * (p.M == 0 ? pmat->yy / p.N * sin(ar) :
+ pmat->xx / p.M * cos(ar));
+ ft = fabs(fr) * rt;
+ /* 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);
+ /*
+ * We used to compute the percentage difference here:
+ * a_err = (a0 == 0 ? a_diff : a_diff / fabs(a0));
+ * but using the angle difference makes more sense:
+ */
+ a_err = a_diff;
+
+ if_debug5('h', " ==> d=%d, wt=%ld, wt_size=%ld, f=%g, a=%g\n",
+ p.D, wt, bitmap_raster(wt) * wt, ft, at);
+
+ /*
+ * Minimize angle and frequency error within the
+ * permitted maximum super-cell size.
+ */
+
+ {
+ double err = f_err * a_err;
+
+ if (err > e_best)
+ continue;
+ e_best = err;
+ }
+ *phcp = p;
+ f = ft, a = at;
+ better = true;
+ if_debug3('h', "*** best wt_size=%ld, f_diff=%g, a_diff=%g\n",
+ wt_size, f_diff, a_diff);
+ if (f_err <= 0.01 && a_err <= 0.01)
+ goto done;
+ }
+ }
+ if (phcp->C < min_levels) { /* We don't have enough levels yet. Keep going. */
+ ++rt;
+ goto try_size;
+ }
+ 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,
+ * take what we've got; if R = 1, give up.
+ */
+ if (rt == 1)
+ return_error(gs_error_rangecheck);
+ }
+
+ /* Deliver the results. */
+ done:
+ if_debug5('h', "[h]Chosen: f=%g a=%g M=%d N=%d R=%d\n",
+ f, a, phcp->M, phcp->N, phcp->R);
+ ph->actual_frequency = f;
+ ph->actual_angle = a;
+ return 0;
+#undef u0
+#undef v0
+}
+
+/* Prepare to sample a spot screen. */
+/* This is the second half of gs_screen_init_accurate. */
+int
+gs_screen_enum_init_memory(gs_screen_enum * penum, const gx_ht_order * porder,
+ gs_state * pgs, gs_screen_halftone * phsp, gs_memory_t * mem)
+{
+ penum->pgs = pgs; /* ensure clean for GC */
+ penum->order = *porder;
+ penum->halftone.rc.memory = mem;
+ penum->halftone.type = ht_type_screen;
+ penum->halftone.params.screen = *phsp;
+ penum->x = penum->y = 0;
+ penum->strip = porder->num_levels / porder->width;
+ penum->shift = porder->shift;
+ /*
+ * We want a transformation matrix that maps the parallelogram
+ * (0,0), (U,V), (U-V',V+U'), (-V',U') to the square (+/-1, +/-1).
+ * If the coefficients are [a b c d e f] and we let
+ * u = U = M/R, v = V = N/R,
+ * r = -V' = -N'/R', s = U' = M'/R',
+ * then we just need to solve the equations:
+ * a*0 + c*0 + e = -1 b*0 + d*0 + f = -1
+ * a*u + c*v + e = 1 b*u + d*v + f = 1
+ * a*r + c*s + e = -1 b*r + d*s + f = 1
+ * This has the following solution:
+ * Q = 2 / (M*M' + N*N')
+ * a = Q * R * M'
+ * b = -Q * R' * N
+ * c = Q * R * N'
+ * d = Q * R' * M
+ * e = -1
+ * f = -1
+ */
+ {
+ const int M = porder->params.M, N = porder->params.N, R = porder->params.R;
+ const int M1 = porder->params.M1, N1 = porder->params.N1, R1 = porder->params.R1;
+ double Q = 2.0 / ((long)M * M1 + (long)N * N1);
+
+ penum->mat.xx = Q * (R * M1);
+ penum->mat.xy = Q * (-R1 * N);
+ penum->mat.yx = Q * (R * N1);
+ penum->mat.yy = Q * (R1 * M);
+ 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->params.R,
+ 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);
+ dlprintf6("[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.rc.memory = penum->halftone.rc.memory;
+ dev_ht.order = penum->order;
+ dev_ht.components = 0;
+ return gx_ht_install(penum->pgs, &penum->halftone, &dev_ht);
+}
diff --git a/pstoraster/gshtx.h b/pstoraster/gshtx.h
new file mode 100644
index 000000000..6e59644ba
--- /dev/null
+++ b/pstoraster/gshtx.h
@@ -0,0 +1,158 @@
+/* Copyright (C) 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* High-level interface to stand-alone halftone/transfer objects */
+
+#ifndef gshtx_INCLUDED
+# define gshtx_INCLUDED
+
+#include "gsmemory.h"
+#include "gscsepnm.h"
+#include "gsht1.h"
+#include "gxtmap.h"
+
+/*
+ * The stand-alone halftone structures are opaque, and are placed in an opaque
+ * graphic state.
+ */
+
+/* Alias type names */
+#define gs_ht gs_halftone
+#define gs_spot_ht gs_spot_halftone
+#define gs_threshold_ht gs_threshold_halftone
+#define gs_ht_component gs_halftone_component
+#define gs_multiple_ht gs_multiple_halftone
+/* Alias GC descriptors */
+#define st_gs_ht st_halftone
+#define st_ht_comp_element st_ht_component_element
+/* Alias member names */
+#define ht_spot spot
+#define ht_threshold threshold
+#define ht_multiple multiple
+
+#ifndef gs_state_DEFINED
+# define gs_state_DEFINED
+typedef struct gs_state_s gs_state;
+
+#endif
+
+/*
+ * A "closure" form of gs_mapping_proc. This allows the procedure to access
+ * client data for the purpose of filling in the transfer information.
+ *
+ * As with PostScript transfer functions, the operand will be in the range
+ * [0, 1], and the result should be in the same range.
+ */
+typedef gs_mapping_closure_proc_t gs_ht_transfer_proc; /* see gxtmap.h */
+
+/*
+ * Constructor, destructor, assign, and copy routines for a gs_ht
+ * structure, and to install them in the graphic state.
+ *
+ * Notes:
+ *
+ * Construction of a gs_ht halftone requires two steps: creating the
+ * overall halftone, and creating each of the components. Client data
+ * must be provided for each of the latter steps.
+ *
+ * The type field of gs_ht halftones will always be ht_type_multiple;
+ * if only one component is required, this halftone will always be given
+ * the component name "Default".
+ *
+ * The type fields of the gs_ht_component structures pointed to by the
+ * gs_multiple_ht structure will have the value ht_type_spot or
+ * ht_type_threshold; the constructor routines will not build any
+ * other types.
+ *
+ * Individual component halftones of a gs_ht structure must always be
+ * provided with transfer functions.
+ *
+ * Releasing the gs_ht structure will NOT release the client data
+ * (the client must do that directly).
+ */
+
+extern int gs_ht_build(P3(gs_ht ** ppht, uint num_comps, gs_memory_t * pmem));
+
+extern int gs_ht_set_spot_comp(P9(
+ gs_ht * pht,
+ int component_index,
+ gs_ht_separation_name sepr_name,
+ float freq,
+ float angle,
+ float (*spot_func) (P2(floatp, floatp)),
+ bool accurate,
+ gs_ht_transfer_proc transfer,
+ const void *client_data
+ ));
+
+extern int gs_ht_set_threshold_comp(P8(
+ gs_ht * pht,
+ int component_index,
+ gs_ht_separation_name sepr_name,
+ int width,
+ int height,
+ const gs_const_string * thresholds,
+ gs_ht_transfer_proc transfer,
+ const void *client_data
+ ));
+
+/*
+ * This procedure specifies a (possibly non-monotonic) halftone of size
+ * width x height with num_levels different levels (including white, always
+ * all 0s, but excluding black, always all 1s). Each mask is in the form of
+ * a gs_bitmap, except that there is no row padding -- the 'raster' is
+ * ceil(width / 8).
+ *
+ * Note that the client is responsible for releasing the mask data.
+ */
+extern int gs_ht_set_mask_comp(P9(
+ gs_ht * pht,
+ int component_index,
+ gs_ht_separation_name sepr_name,
+ int width,
+ int height,
+ int num_levels,
+ const byte * masks, /* width x height x num_levels */
+ gs_ht_transfer_proc transfer,
+ const void *client_data
+ ));
+
+extern void gs_ht_reference(P1(gs_ht * pht));
+extern void gs_ht_release(P1(gs_ht * pht));
+
+#define gs_ht_assign(pto, pfrom) \
+ BEGIN \
+ gs_ht_reference(pfrom); \
+ if (pto != 0) \
+ gs_ht_release(pto); \
+ pto = pfrom; \
+ END
+
+#define gs_ht_init_ptr(pto, pfrom) \
+ BEGIN gs_ht_reference(pfrom); pto = pfrom; END
+
+extern int gs_ht_install(P2(gs_state * pgs, gs_ht * pht));
+
+#endif /* gshtx_INCLUDED */
diff --git a/pstoraster/gsimage.c b/pstoraster/gsimage.c
new file mode 100644
index 000000000..127c9ab20
--- /dev/null
+++ b/pstoraster/gsimage.c
@@ -0,0 +1,326 @@
+/* Copyright (C) 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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 "gxiparam.h"
+#include "gxpath.h" /* for gx_effective_clip_path */
+#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; /* if 0, just skip over the data */
+ gx_image_enum_common_t *info; /* driver bookkeeping structure */
+ int num_planes;
+ int width, height;
+ uint raster; /* bytes per row (per plane), no padding */
+ /* The following are updated dynamically. */
+ int plane_index; /* index of next plane of data */
+ int y;
+ uint pos; /* byte position within the scan line */
+ gs_const_string sources[gs_image_max_components]; /* source data */
+ gs_string rows[gs_image_max_components]; /* row buffers */
+ 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)
+ ENUM_RETURN_STRING_PTR(gs_image_enum, sources[index]);
+ index -= eptr->plane_index;
+ if (index < eptr->num_planes)
+ ENUM_RETURN_STRING_PTR(gs_image_enum, rows[index]);
+ 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, sources[i]);
+ for (i = 0; i < eptr->num_planes; i++)
+ RELOC_STRING_PTR(gs_image_enum, rows[i]);
+}
+RELOC_PTRS_END
+#undef eptr
+
+/* Create an image enumerator given image parameters and a graphics state. */
+int
+gs_image_begin_typed(const gs_image_common_t * pic, gs_state * pgs,
+ bool uses_color, gx_image_enum_common_t ** ppie)
+{
+ gx_device *dev = gs_currentdevice(pgs);
+ gx_clip_path *pcpath;
+ int code = gx_effective_clip_path(pgs, &pcpath);
+
+ if (code < 0)
+ return code;
+ if (uses_color)
+ gx_set_dev_color(pgs);
+ return gx_device_begin_typed_image(dev, (const gs_imager_state *)pgs,
+ NULL, pic, NULL, pgs->dev_color, pcpath, pgs->memory, ppie);
+}
+
+/* Allocate an image enumerator. */
+private void
+image_enum_init(gs_image_enum * penum)
+{ /* Clean pointers for GC. */
+ int i;
+
+ penum->info = 0;
+ penum->dev = 0;
+ for (i = 0; i < countof(penum->sources); ++i) {
+ penum->sources[i].data = 0, penum->sources[i].size = 0;
+ penum->rows[i].data = 0, penum->rows[i].size = 0;
+ }
+}
+gs_image_enum *
+gs_image_enum_alloc(gs_memory_t * mem, client_name_t cname)
+{
+ gs_image_enum *penum =
+ gs_alloc_struct(mem, gs_image_enum, &st_gs_image_enum, cname);
+
+ if (penum != 0) {
+ penum->memory = mem;
+ image_enum_init(penum);
+ }
+ return penum;
+}
+
+/* Start processing an ImageType 1 image. */
+int
+gs_image_init(gs_image_enum * penum, const gs_image_t * pim, bool multi,
+ gs_state * pgs)
+{
+ gs_image_t image;
+ gx_image_enum_common_t *pie;
+ int code;
+
+ image = *pim;
+ if (image.ImageMask) {
+ image.ColorSpace = NULL;
+ if (pgs->in_cachedevice <= 1)
+ image.adjust = false;
+ } else {
+ if (pgs->in_cachedevice)
+ return_error(gs_error_undefined);
+ if (image.ColorSpace == NULL)
+ image.ColorSpace =
+ gs_cspace_DeviceGray((const gs_imager_state *)pgs);
+ }
+ code = gs_image_begin_typed((const gs_image_common_t *)&image, pgs,
+ image.ImageMask | image.CombineWithColor,
+ &pie);
+ if (code < 0)
+ return code;
+ return gs_image_common_init(penum, pie,
+ (const gs_data_image_t *)&image,
+ pgs->memory,
+ (pgs->in_charpath ? NULL :
+ gs_currentdevice_inline(pgs)));
+}
+/* Start processing a general image. */
+int
+gs_image_common_init(gs_image_enum * penum, gx_image_enum_common_t * pie,
+ const gs_data_image_t * pim, gs_memory_t * mem, gx_device * dev)
+{
+ if (pim->Width == 0 || pim->Height == 0) {
+ gx_image_end(pie, false);
+ return 1;
+ }
+ image_enum_init(penum);
+ penum->memory = mem;
+ penum->dev = dev;
+ penum->info = pie;
+ penum->num_planes = pie->num_planes;
+ penum->width = pim->Width;
+ penum->height = pim->Height;
+/****** ALL PLANES MUST HAVE SAME DEPTH FOR NOW ******/
+ penum->raster = (pim->Width * pie->plane_depths[0] + 7) >> 3;
+ /* Initialize the dynamic part of the state. */
+ penum->plane_index = 0;
+ penum->y = 0;
+ penum->pos = 0;
+ penum->error = false;
+ return 0;
+}
+
+/*
+ * Return the number of bytes of data per row per plane.
+ */
+uint
+gs_image_bytes_per_plane_row(const gs_image_enum * penum, int plane)
+{
+/****** IGNORE PLANE FOR NOW ******/
+ return penum->raster;
+}
+
+/* Process the next piece of an image. */
+private int
+copy_planes(gx_device * dev, gs_image_enum * penum, const byte ** planes,
+ int h)
+{
+ int code =
+ (penum->dev == 0 ? (penum->y + h < penum->height ? 0 : 1) :
+ gx_image_data(penum->info, planes, 0, penum->raster, h));
+
+ if (code < 0)
+ penum->error = true;
+ return code;
+}
+int
+gs_image_next(gs_image_enum * penum, const byte * dbytes, uint dsize,
+ uint * pused)
+{
+ gx_device *dev;
+ uint left;
+ int num_planes;
+ uint raster;
+ uint pos;
+ 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 entire rows;
+ * gs_image_next allows arbitrary amounts of data.
+ */
+ if (penum->plane_index != 0)
+ if (dsize != penum->sources[0].size)
+ return_error(gs_error_rangecheck);
+ penum->sources[penum->plane_index].data = dbytes;
+ penum->sources[penum->plane_index].size = dsize;
+ if (++(penum->plane_index) != penum->num_planes)
+ return 0;
+ /* We have a full set of planes. */
+ dev = penum->dev;
+ left = dsize;
+ num_planes = penum->num_planes;
+ raster = penum->raster;
+ pos = penum->pos;
+ code = 0;
+ while (left && penum->y < penum->height) {
+ const byte *planes[gs_image_max_components];
+ int i;
+
+ for (i = 0; i < num_planes; ++i)
+ planes[i] = penum->sources[i].data + dsize - left;
+ if (pos == 0 && left >= raster) { /* Pass (a) row(s) directly from the source. */
+ int h = left / raster;
+
+ if (h > penum->height - penum->y)
+ h = penum->height - penum->y;
+ code = copy_planes(dev, penum, planes, h);
+ if (code < 0)
+ break;
+ left -= raster * h;
+ penum->y += h;
+ } else { /* Buffer a partial row. */
+ uint count = min(left, raster - pos);
+
+ if (penum->rows[0].data == 0) { /* Allocate the row buffers. */
+ for (i = 0; i < num_planes; ++i) {
+ byte *row = gs_alloc_string(penum->memory, raster,
+ "gs_image_next(row)");
+
+ if (row == 0) {
+ code = gs_note_error(gs_error_VMerror);
+ while (--i >= 0) {
+ gs_free_string(penum->memory, penum->rows[i].data,
+ raster, "gs_image_next(row)");
+ penum->rows[i].data = 0;
+ penum->rows[i].size = 0;
+ }
+ break;
+ }
+ penum->rows[i].data = row;
+ penum->rows[i].size = raster;
+ }
+ if (code < 0)
+ break;
+ }
+ for (i = 0; i < num_planes; ++i)
+ memcpy(penum->rows[i].data + pos, planes[i], count);
+ pos += count;
+ left -= count;
+ if (pos == raster) {
+ for (i = 0; i < num_planes; ++i)
+ planes[i] = penum->rows[i].data;
+ code = copy_planes(dev, penum, planes, 1);
+ if (code < 0)
+ break;
+ pos = 0;
+ penum->y++;
+ }
+ }
+ }
+ penum->pos = pos;
+ penum->plane_index = 0;
+ *pused = dsize - left;
+ return code;
+}
+
+/* Clean up after processing an image. */
+void
+gs_image_cleanup(gs_image_enum * penum)
+{
+ int i;
+
+ for (i = 0; i < penum->num_planes; ++i)
+ gs_free_string(penum->memory, penum->rows[i].data,
+ penum->rows[i].size, "gs_image_cleanup(row)");
+ if (penum->dev != 0)
+ gx_image_end(penum->info, !penum->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..4f6e6b11e
--- /dev/null
+++ b/pstoraster/gsimage.h
@@ -0,0 +1,84 @@
+/* Copyright (C) 1992, 1995, 1996, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Requires gsstate.h */
+
+#ifndef gsimage_INCLUDED
+# define gsimage_INCLUDED
+
+#include "gsiparam.h"
+
+/*
+ * Create an image enumerator given image parameters and a graphics state.
+ * This calls the device's begin_typed_image procedure with appropriate
+ * parameters. Note that this is an enumerator that requires entire
+ * rows of data, not the buffered enumerator used by the procedures below:
+ * for this reason, we may move the prototype elsewhere in the future.
+ */
+#ifndef gx_image_enum_common_t_DEFINED
+# define gx_image_enum_common_t_DEFINED
+typedef struct gx_image_enum_common_s gx_image_enum_common_t;
+
+#endif
+int gs_image_begin_typed(P4(const gs_image_common_t * pic, gs_state * pgs,
+ bool uses_color, gx_image_enum_common_t ** ppie));
+
+/*
+ * 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.
+ */
+#ifndef gx_device_DEFINED
+# define gx_device_DEFINED
+typedef struct gx_device_s gx_device;
+
+#endif
+int gs_image_common_init(P5(gs_image_enum * penum, gx_image_enum_common_t * pie,
+ const gs_data_image_t * pim,
+ gs_memory_t * mem, gx_device * dev));
+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_plane_row(P2(const gs_image_enum * penum, int plane));
+
+#define gs_image_bytes_per_row(penum)\
+ gs_image_bytes_per_plane_row(penum, 0)
+/* Clean up after processing an image. */
+void gs_image_cleanup(P1(gs_image_enum * penum));
+
+#endif /* gsimage_INCLUDED */
diff --git a/pstoraster/gsimpath.c b/pstoraster/gsimpath.c
new file mode 100644
index 000000000..503398b1f
--- /dev/null
+++ b/pstoraster/gsimpath.c
@@ -0,0 +1,191 @@
+/* Copyright (C) 1989, 1992, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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 get_pixel(P3(const status *, int, int));
+private int trace_from(P4(status *, int, int, int));
+private int 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
+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
+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
+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..eddb5aa1c
--- /dev/null
+++ b/pstoraster/gsinit.c
@@ -0,0 +1,79 @@
+/* Copyright (C) 1989, 1995, 1996, 1997 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Initialization for the imager */
+#include "stdio_.h"
+#include "memory_.h"
+#include "gdebug.h"
+#include "gscdefs.h"
+#include "gsmemory.h"
+#include "gsmalloc.h"
+#include "gp.h"
+#include "gslib.h" /* interface definition */
+
+/* Imported from gsmisc.c */
+extern FILE *gs_debug_out;
+
+/* 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_init1(gs_lib_init0(debug_out));
+}
+gs_memory_t *
+gs_lib_init0(FILE * debug_out)
+{
+ gs_memory_t *mem;
+
+ gs_debug_out = debug_out;
+ mem = (gs_memory_t *) gs_malloc_init();
+ /* Reset debugging flags */
+ memset(gs_debug, 0, 128);
+ gs_log_errors = 0;
+ return mem;
+}
+void
+gs_lib_init1(gs_memory_t * mem)
+{ /* Run configuration-specific initialization procedures. */
+ {
+ void (*const *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..a5791961f
--- /dev/null
+++ b/pstoraster/gsio.h
@@ -0,0 +1,66 @@
+/* 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 supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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..a140c365f
--- /dev/null
+++ b/pstoraster/gsiodev.c
@@ -0,0 +1,318 @@
+/* Copyright (C) 1993, 1994, 1996, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* IODevice implementation for Ghostscript */
+#include "errno_.h"
+#include "string_.h"
+#include "gx.h"
+#include "gserrors.h"
+#include "gp.h"
+#include "gscdefs.h"
+#include "gsparam.h"
+#include "gsstruct.h"
+#include "gxiodev.h"
+
+/* Import the IODevice table from gconf.c. */
+extern_gx_io_device_table();
+
+/* Define a table of local copies of the IODevices, */
+/* allocated at startup. This just postpones the day of reckoning.... */
+private gx_io_device **io_device_table;
+
+private_st_io_device();
+gs_private_st_ptr(st_io_device_ptr, gx_io_device *, "gx_io_device *",
+ iodev_ptr_enum_ptrs, iodev_ptr_reloc_ptrs);
+gs_private_st_element(st_io_device_ptr_element, gx_io_device *,
+ "gx_io_device *[]", iodev_ptr_elt_enum_ptrs, iodev_ptr_elt_reloc_ptrs,
+ st_io_device_ptr);
+
+/* 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);
+const 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)
+{ /* Make writable copies of all IODevices. */
+ gx_io_device **table =
+ gs_alloc_struct_array(mem, gx_io_device_table_count,
+ gx_io_device *, &st_io_device_ptr_element,
+ "gsiodev_init(table)");
+ uint i;
+
+ for (i = 0; i < gx_io_device_table_count; ++i) {
+ table[i] = gs_alloc_struct(mem, gx_io_device, &st_io_device,
+ "gsiodev_init");
+ memcpy(table[i], gx_io_device_table[i], sizeof(gx_io_device));
+ }
+ io_device_table = table;
+ gs_register_struct_root(mem, NULL, (void **)&io_device_table,
+ "io_device_table");
+ /* Run the one-time initialization of each IODevice. */
+ for (i = 0; i < gx_io_device_table_count; ++i)
+ (table[i]->procs.init) (table[i], 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 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 = 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..650295b78
--- /dev/null
+++ b/pstoraster/gsiparam.h
@@ -0,0 +1,289 @@
+/* Copyright (C) 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Image parameter definition */
+
+#ifndef gsiparam_INCLUDED
+# define gsiparam_INCLUDED
+
+#include "gsmatrix.h"
+
+/* ---------------- Image parameters ---------------- */
+
+/*
+ * Unfortunately, we defined the gs_image_t type as designating an ImageType
+ * 1 image or mask before we realized that there were going to be other
+ * ImageTypes. We could redefine this type to include a type field without
+ * perturbing clients, but it would break implementations of driver
+ * begin_image procedures, since they are currently only prepared to handle
+ * ImageType 1 images and would have to be modified to check the ImageType.
+ * Therefore, we use gs_image_common_t for an abstract image type, and
+ * gs_image<n>_t for the various ImageTypes.
+ */
+
+/*
+ * Define the data common to all image types. The type structure is
+ * opaque here, defined in gxiparam.h.
+ */
+#ifndef gx_image_type_DEFINED
+# define gx_image_type_DEFINED
+typedef struct gx_image_type_s gx_image_type_t;
+
+#endif
+#define gs_image_common\
+ const gx_image_type_t *type;\
+ /*\
+ * Define the transformation from user space to image space.\
+ */\
+ gs_matrix ImageMatrix
+typedef struct gs_image_common_s {
+ gs_image_common;
+} gs_image_common_t;
+
+#define public_st_gs_image_common() /* in gxiinit.c */\
+ gs_public_st_simple(st_gs_image_common, gs_image_common_t,\
+ "gs_image_common_t")
+
+/*
+ * Define the maximum number of components in image data. When we
+ * support DeviceN color spaces, we will have to rethink this.
+ * 5 is either CMYK + alpha or mask + CMYK.
+ */
+#define gs_image_max_components 5
+
+/*
+ * Define the structure for defining data common to ImageType 1 images,
+ * ImageType 3 DataDicts and MaskDicts, and ImageType 4 images -- i.e.,
+ * all the image types that use explicitly supplied data. It follows
+ * closely the discussion on pp. 219-223 of the PostScript Language
+ * Reference Manual, Second Edition, with the following exceptions:
+ *
+ * DataSource and MultipleDataSources are not members of this
+ * structure, since the structure doesn't take a position on
+ * how the data are actually supplied.
+ */
+#define gs_data_image_common\
+ gs_image_common;\
+ /*\
+ * Define the width of source image in pixels.\
+ */\
+ int Width;\
+ /*\
+ * Define the height of source image in pixels.\
+ */\
+ int Height;\
+ /*\
+ * Define B, the number of bits per pixel component.\
+ * Currently this must be 1 for masks.\
+ */\
+ int BitsPerComponent;\
+ /*\
+ * 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[gs_image_max_components * 2];\
+ /*\
+ * Define whether to smooth the image.\
+ */\
+ bool Interpolate
+typedef struct gs_data_image_s {
+ gs_data_image_common;
+} gs_data_image_t;
+
+#define public_st_gs_data_image() /* in gxiinit.c */\
+ gs_public_st_simple(st_gs_data_image, gs_data_image_t,\
+ "gs_data_image_t")
+
+/*
+ * Define the data common to ImageType 1 images, ImageType 3 DataDicts,
+ * and ImageType 4 images -- i.e., all the image types that provide pixel
+ * (as opposed to mask) data. The following are added to the PostScript
+ * image parameters:
+ *
+ * format is not PostScript or PDF standard: it is normally derived
+ * from MultipleDataSources.
+ *
+ * ColorSpace is added from PDF.
+ *
+ * CombineWithColor is 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;
+
+/* 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 gs_pixel_image_common\
+ gs_data_image_common;\
+ /*\
+ * Define how the pixels are divided up into planes.\
+ */\
+ gs_image_format_t format;\
+ /*\
+ * Define the source color space (must be NULL for masks).\
+ */\
+ const gs_color_space *ColorSpace;\
+ /*\
+ * 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
+typedef struct gs_pixel_image_s {
+ gs_pixel_image_common;
+} gs_pixel_image_t;
+
+#define public_st_gs_pixel_image() /* in gxiinit.c */\
+ gs_public_st_ptrs1(st_gs_pixel_image, gs_pixel_image_t,\
+ "gs_data_image_t", pixel_image_enum_ptrs, pixel_image_reloc_ptrs,\
+ ColorSpace)
+
+/*
+ * Define an ImageType 1 image. ImageMask is an added member from PDF.
+ * adjust and Alpha are not PostScript or PDF standard.
+ */
+typedef enum {
+ /* No alpha. This must be 0 for true-false tests. */
+ gs_image_alpha_none = 0,
+ /* Alpha precedes color components. */
+ gs_image_alpha_first,
+ /* Alpha follows color components. */
+ gs_image_alpha_last
+} gs_image_alpha_t;
+
+typedef struct gs_image1_s {
+ gs_pixel_image_common;
+ /*
+ * Define whether this is a mask or a solid image.
+ * For masks, Alpha must be 'none'.
+ */
+ bool ImageMask;
+ /*
+ * Define whether to expand each destination pixel, to make
+ * masked characters look better. Only used for masks.
+ */
+ bool adjust;
+ /*
+ * Define whether there is an additional component providing
+ * alpha information for each pixel, in addition to the
+ * components implied by the color space.
+ */
+ gs_image_alpha_t Alpha;
+} gs_image1_t;
+
+/*
+ * In standard PostScript Level 1 and 2, this is the only defined ImageType.
+ */
+typedef gs_image1_t gs_image_t;
+
+/*
+ * Define procedures for initializing the standard forms of image structures
+ * to default values. Note that because these structures may add more
+ * members in the future, all clients constructing gs_*image*_t values
+ * *must* start by initializing the value by calling one of the following
+ * procedures. Note also that these procedures do not set the image type.
+ */
+void
+ /*
+ * Sets ImageMatrix to the identity matrix.
+ */
+ gs_image_common_t_init(P1(gs_image_common_t * pic)), /*
+ * Also sets Width = Height = 0, BitsPerComponent = 1,
+ * format = chunky, Interpolate = false.
+ * If num_components = N > 0, sets the first N elements of Decode to (0, 1);
+ * if num_components = N < 0, sets the first -N elements of Decode to (1, 0);
+ * if num_components = 0, doesn't set Decode.
+ */
+ gs_data_image_t_init(P2(gs_data_image_t * pim, int num_components)),
+ /*
+ * Also sets CombineWithColor = false, ColorSpace = color_space, Alpha =
+ * none. num_components is obtained from ColorSpace; if ColorSpace =
+ * NULL or ColorSpace is a Pattern space, num_components is taken as 0
+ * (Decode is not initialized).
+ */
+ gs_pixel_image_t_init(P2(gs_pixel_image_t * pim,
+ const gs_color_space * color_space));
+
+/*
+ * Initialize an ImageType 1 image (or imagemask). Also sets ImageMask,
+ * adjust, and Alpha, and the image type. 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(P2(gs_image_t * pim, const gs_color_space * pcs));
+void gs_image_t_init_mask(P2(gs_image_t * pim, bool write_1s));
+
+/* init_gray and init_color require a (const) imager state. */
+#define gs_image_t_init_gray(pim, pis)\
+ gs_image_t_init(pim, gs_cspace_DeviceGray(pis))
+#define gs_image_t_init_rgb(pim, pis)\
+ gs_image_t_init(pim, gs_cspace_DeviceRGB(pis))
+#define gs_image_t_init_cmyk(pim, pis)\
+ gs_image_t_init(pim, gs_cspace_DeviceCMYK(pis))
+
+/****** REMAINDER OF FILE UNDER CONSTRUCTION. PROCEED AT YOUR OWN RISK. ******/
+
+#if 0
+
+/* ---------------- 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/gsiparm2.h b/pstoraster/gsiparm2.h
new file mode 100644
index 000000000..f69875f30
--- /dev/null
+++ b/pstoraster/gsiparm2.h
@@ -0,0 +1,63 @@
+/* Copyright (C) 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* ImageType 2 image parameter definition */
+
+#ifndef gsiparm2_INCLUDED
+# define gsiparm2_INCLUDED
+
+#include "gsiparam.h"
+
+/* Opaque type for a path */
+#ifndef gx_path_DEFINED
+# define gx_path_DEFINED
+typedef struct gx_path_s gx_path;
+#endif
+
+/*
+ * See Section 7.1 of the Adobe PostScript Version 3010 Supplement
+ * for a definition of ImageType 2 images.
+ */
+
+typedef struct gs_image2_s {
+ gs_image_common;
+ gs_state *DataSource;
+ float XOrigin, YOrigin;
+ float Width, Height;
+ /*
+ * If UnpaintedPath is not 0, any unpainted path will be appended to it.
+ */
+ gx_path *UnpaintedPath;
+ bool PixelCopy;
+} gs_image2_t;
+
+/*
+ * Initialize an ImageType 2 image. Defaults:
+ * UnpaintedPath = 0
+ * PixelCopy = false
+ */
+void gs_image2_t_init(P1(gs_image2_t * pim));
+
+#endif /* gsiparm2_INCLUDED */
diff --git a/pstoraster/gsiparm3.h b/pstoraster/gsiparm3.h
new file mode 100644
index 000000000..0f5272518
--- /dev/null
+++ b/pstoraster/gsiparm3.h
@@ -0,0 +1,67 @@
+/* Copyright (C) 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* LanguageLevel 3 (ImageType 3 & 4) image parameter definition */
+
+#ifndef gsiparm3_INCLUDED
+# define gsiparm3_INCLUDED
+
+#include "gsiparam.h"
+
+/*
+ * See Section 4.3 of the Adobe PostScript Version 3010 Supplement
+ * for a definition of ImageType 3 and 4 images.
+ */
+
+/*
+ * If InterleaveType is 3, the data source for the mask is provided as an
+ * additional data source *before* the data sources for the pixel data. For
+ * both InterleaveType 2 and 3, the client is responsible for always
+ * providing mask data before the pixel data that it masks. (The
+ * implementation does not currently check this, but it should.)
+ */
+typedef enum {
+ interleave_chunky = 1,
+ interleave_scan_lines = 2,
+ interleave_separate_source = 3
+} gs_image3_interleave_type_t;
+typedef struct gs_image3_s {
+ gs_pixel_image_common; /* DataDict */
+ int InterleaveType;
+ gs_data_image_t MaskDict;
+} gs_image3_t;
+
+/* We export the GC descriptor because ImageType 4 subclasses it. */
+#define public_st_gs_image3() /* in gximage3.c */\
+ gs_public_st_suffix_add0(st_gs_image3, gs_image3_t, "gs_image3_t",\
+ image3_enum_ptrs, image3_reloc_ptrs, st_gs_pixel_image)
+
+/*
+ * Initialize an ImageType 3 image.
+ */
+void gs_image3_t_init(P3(gs_image3_t * pim, const gs_color_space * color_space,
+ gs_image3_interleave_type_t interleave_type));
+
+#endif /* gsiparm3_INCLUDED */
diff --git a/pstoraster/gsiparm4.h b/pstoraster/gsiparm4.h
new file mode 100644
index 000000000..6b9103cce
--- /dev/null
+++ b/pstoraster/gsiparm4.h
@@ -0,0 +1,59 @@
+/* Copyright (C) 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* ImageType 4 image parameter definition */
+
+#ifndef gsiparm4_INCLUDED
+# define gsiparm4_INCLUDED
+
+#include "gsiparam.h"
+
+/*
+ * See Section 4.3 of the Adobe PostScript Version 3010 Supplement
+ * for a definition of ImageType 4 images.
+ */
+
+typedef struct gs_image4_s {
+ gs_pixel_image_common;
+ /*
+ * If MaskColor_is_range is false, the first N elements of
+ * MaskColor are sample values; if MaskColor_is_range is true,
+ * the first 2*N elements are ranges of sample values.
+ *
+ * Currently, the largest sample values supported by the library are 12
+ * bits, but eventually we want to support DevicePixel images with
+ * samples up to 32 bits as well.
+ */
+ bool MaskColor_is_range;
+ uint MaskColor[gs_image_max_components * 2];
+} gs_image4_t;
+
+/*
+ * Initialize an ImageType 4 image. Defaults:
+ * MaskColor_is_range = false
+ */
+void gs_image4_t_init(P2(gs_image4_t * pim, const gs_color_space * color_space));
+
+#endif /* gsiparm4_INCLUDED */
diff --git a/pstoraster/gsjconf.h b/pstoraster/gsjconf.h
new file mode 100644
index 000000000..758aceed6
--- /dev/null
+++ b/pstoraster/gsjconf.h
@@ -0,0 +1,81 @@
+/* Copyright (C) 1994, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* jconfig.h file for Independent JPEG Group code */
+
+#ifndef gsjconf_INCLUDED
+# define gsjconf_INCLUDED
+
+/*
+ * We should have the following here:
+
+ #include "stdpre.h"
+
+ * But because of the directory structure used to build the IJG library, we
+ * actually concatenate stdpre.h on the front of this file instead to
+ * construct the jconfig.h file used for the compilation.
+ */
+
+#include "arch.h"
+
+/* See IJG's jconfig.doc for the contents of this file. */
+
+#ifdef __PROTOTYPES__
+# define HAVE_PROTOTYPES
+#endif
+
+#define HAVE_UNSIGNED_CHAR
+#define HAVE_UNSIGNED_SHORT
+#undef CHAR_IS_UNSIGNED
+
+#ifdef __STDC__ /* is this right? */
+# define HAVE_STDDEF_H
+# define HAVE_STDLIB_H
+#endif
+
+#undef NEED_BSD_STRINGS /* WRONG */
+#undef NEED_SYS_TYPES_H /* WRONG */
+#undef NEED_FAR_POINTERS
+#undef NEED_SHORT_EXTERNAL_NAMES
+
+#undef INCOMPLETE_TYPES_BROKEN
+
+/* The following is documented in jmemsys.h, not jconfig.doc. */
+#if arch_ints_are_short
+# undef MAX_ALLOC_CHUNK
+# define MAX_ALLOC_CHUNK 0xfff0
+#endif
+
+#ifdef JPEG_INTERNALS
+
+#if arch_arith_rshift == 0
+# define RIGHT_SHIFT_IS_UNSIGNED
+#else
+# undef RIGHT_SHIFT_IS_UNSIGNED
+#endif
+
+#endif /* JPEG_INTERNALS */
+
+#endif /* gsjconf_INCLUDED */
diff --git a/pstoraster/gsjmorec.h b/pstoraster/gsjmorec.h
new file mode 100644
index 000000000..8ddac547a
--- /dev/null
+++ b/pstoraster/gsjmorec.h
@@ -0,0 +1,59 @@
+/* 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 supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* "Wrapper" for Independent JPEG Group code jmorecfg.h */
+
+#ifndef gsjmorec_INCLUDED
+# define gsjmorec_INCLUDED
+
+#include "jmcorig.h"
+
+/* Remove unwanted / unneeded features. */
+#undef DCT_IFAST_SUPPORTED
+#if FPU_TYPE <= 0
+# undef DCT_FLOAT_SUPPORTED
+#endif
+#undef C_MULTISCAN_FILES_SUPPORTED
+#undef C_PROGRESSIVE_SUPPORTED
+#undef ENTROPY_OPT_SUPPORTED
+#undef INPUT_SMOOTHING_SUPPORTED
+
+/****** Comment out the next two lines to add progressive decoding. ******/
+#undef D_MULTISCAN_FILES_SUPPORTED
+#undef D_PROGRESSIVE_SUPPORTED
+
+#undef BLOCK_SMOOTHING_SUPPORTED
+#undef IDCT_SCALING_SUPPORTED
+#undef UPSAMPLE_SCALING_SUPPORTED
+#undef UPSAMPLE_MERGING_SUPPORTED
+#undef QUANT_1PASS_SUPPORTED
+#undef QUANT_2PASS_SUPPORTED
+/*
+ * Read "JPEG" files with up to 64 blocks/MCU for Adobe compatibility.
+ * Note that this #define will have no effect in pre-v6 IJG versions.
+ */
+#define D_MAX_BLOCKS_IN_MCU 64
+
+#endif /* gsjmorec_INCLUDED */
diff --git a/pstoraster/gslib.h b/pstoraster/gslib.h
new file mode 100644
index 000000000..bb33e9fd7
--- /dev/null
+++ b/pstoraster/gslib.h
@@ -0,0 +1,44 @@
+/* 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 supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Requires stdio.h, gsmemory.h */
+
+#ifndef gslib_INCLUDED
+# define gslib_INCLUDED
+
+/*
+ * 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));
+gs_memory_t *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));
+
+#endif /* gslib_INCLUDED */
diff --git a/pstoraster/gsline.c b/pstoraster/gsline.c
new file mode 100644
index 000000000..e6bfdf372
--- /dev/null
+++ b/pstoraster/gsline.c
@@ -0,0 +1,340 @@
+/* Copyright (C) 1989, 1995, 1996, 1997 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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)
+{
+ if ((uint) cap > gs_line_cap_max)
+ return_error(gs_error_rangecheck);
+ 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)
+{
+ if ((uint) join > gs_line_join_max)
+ return_error(gs_error_rangecheck);
+ 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) {
+ gs_free_object(mem, ppat, "gx_set_dash(old pattern)");
+ ppat = 0;
+ }
+ } else {
+ uint size = length * sizeof(float);
+
+ 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) {
+ if (ppat == 0)
+ ppat = (float *)gs_alloc_bytes(mem, size,
+ "gx_set_dash(pattern)");
+ else if (length != dash->pattern_size)
+ ppat = gs_resize_object(mem, ppat, size,
+ "gx_set_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->pattern_length = pattern_length;
+ 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;
+}
+
+/* ------ Extensions ------ */
+
+/* Device-independent */
+
+/* setdashadapt */
+void
+gs_setdashadapt(gs_state * pgs, bool adapt)
+{
+ pgs_lp->dash.adapt = adapt;
+}
+
+/* currentdashadapt */
+bool
+gs_imager_currentdashadapt(const gs_imager_state * pis)
+{
+ return gs_currentlineparams_inline(pis)->dash.adapt;
+}
+bool
+gs_currentdashadapt(const gs_state * pgs)
+{
+ return gs_imager_currentdashadapt((const gs_imager_state *)pgs);
+}
+
+/* Device-dependent */
+
+/* setaccuratecurves */
+void
+gs_setaccuratecurves(gs_state * pgs, bool accurate)
+{
+ pgs->accurate_curves = accurate;
+}
+
+/* currentaccuratecurves */
+bool
+gs_imager_currentaccuratecurves(const gs_imager_state * pis)
+{
+ return pis->accurate_curves;
+}
+bool
+gs_currentaccuratecurves(const gs_state * pgs)
+{
+ return gs_imager_currentaccuratecurves((const gs_imager_state *)pgs);
+}
+
+/* setdotlength */
+int
+gx_set_dot_length(gx_line_params * plp, floatp length, bool absolute)
+{
+ if (length < 0)
+ return_error(gs_error_rangecheck);
+ plp->dot_length = length;
+ plp->dot_length_absolute = absolute;
+ return 0;
+}
+int
+gs_setdotlength(gs_state * pgs, floatp length, bool absolute)
+{
+ return gx_set_dot_length(pgs_lp, length, absolute);
+}
+
+/* currentdotlength */
+float
+gs_currentdotlength(const gs_state * pgs)
+{
+ return pgs_lp->dot_length;
+}
+bool
+gs_currentdotlength_absolute(const gs_state * pgs)
+{
+ return pgs_lp->dot_length_absolute;
+}
diff --git a/pstoraster/gsline.h b/pstoraster/gsline.h
new file mode 100644
index 000000000..00e06bd81
--- /dev/null
+++ b/pstoraster/gsline.h
@@ -0,0 +1,75 @@
+/* Copyright (C) 1994, 1995, 1996, 1997 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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 *));
+
+/* Extensions */
+void gs_setaccuratecurves(P2(gs_state *, bool));
+bool gs_currentaccuratecurves(P1(const gs_state *));
+void gs_setdashadapt(P2(gs_state *, bool));
+bool gs_currentdashadapt(P1(const gs_state *));
+int gs_setdotlength(P3(gs_state *, floatp, bool));
+float gs_currentdotlength(P1(const gs_state *));
+bool gs_currentdotlength_absolute(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));
+bool gs_imager_currentdashadapt(P1(const gs_imager_state *));
+bool gs_imager_currentaccuratecurves(P1(const gs_imager_state *));
+
+#endif /* gsline_INCLUDED */
diff --git a/pstoraster/gslparam.h b/pstoraster/gslparam.h
new file mode 100644
index 000000000..17ae76488
--- /dev/null
+++ b/pstoraster/gslparam.h
@@ -0,0 +1,52 @@
+/* Copyright (C) 1995, 1997 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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;
+
+#define gs_line_cap_max 3
+
+/* 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;
+
+#define gs_line_join_max 4
+
+#endif /* gslparam_INCLUDED */
diff --git a/pstoraster/gsmalloc.c b/pstoraster/gsmalloc.c
new file mode 100644
index 000000000..9549fa1e7
--- /dev/null
+++ b/pstoraster/gsmalloc.c
@@ -0,0 +1,398 @@
+/* Copyright (C) 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* C heap allocator */
+#include "malloc_.h"
+#include "gdebug.h"
+#include "gstypes.h"
+#include "gsmemory.h"
+#include "gsmdebug.h"
+#include "gsstruct.h" /* for st_bytes */
+#include "gsmalloc.h"
+
+/* ------ 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.
+ */
+/* Raw memory procedures */
+private gs_memory_proc_alloc_bytes(gs_heap_alloc_bytes);
+private gs_memory_proc_resize_object(gs_heap_resize_object);
+private gs_memory_proc_free_object(gs_heap_free_object);
+private gs_memory_proc_status(gs_heap_status);
+private gs_memory_proc_free_all(gs_heap_free_all);
+
+/* Object memory procedures */
+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_object_size(gs_heap_object_size);
+private gs_memory_proc_object_type(gs_heap_object_type);
+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_enable_free(gs_heap_enable_free);
+private const gs_memory_procs_t gs_malloc_memory_procs =
+{
+ /* Raw memory procedures */
+ gs_heap_alloc_bytes,
+ gs_heap_resize_object,
+ gs_heap_free_object,
+ gs_heap_status,
+ gs_heap_free_all,
+ gs_ignore_consolidate_free,
+ /* Object memory procedures */
+ 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_object_size,
+ gs_heap_object_type,
+ 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_enable_free
+};
+
+/* We must make sure that malloc_blocks leave the block aligned. */
+/*typedef struct gs_malloc_block_s gs_malloc_block_t; */
+#define malloc_block_data\
+ gs_malloc_block_t *next;\
+ gs_malloc_block_t *prev;\
+ uint size;\
+ gs_memory_type_ptr_t type;\
+ client_name_t cname
+struct malloc_block_data_s {
+ malloc_block_data;
+};
+struct gs_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 (-size_of(struct malloc_block_data_s) & 7)
+ byte _pad[(_npad == 0 ? 8 : _npad)]; /* pad to double */
+#undef _npad
+};
+
+/* Define the default allocator. */
+gs_malloc_memory_t *gs_malloc_memory_default;
+
+/* Initialize a malloc allocator. */
+private long heap_available(P0());
+gs_malloc_memory_t *
+gs_malloc_memory_init(void)
+{
+ gs_malloc_memory_t *mem = malloc(sizeof(gs_malloc_memory_t));
+
+ mem->procs = gs_malloc_memory_procs;
+ mem->allocated = 0;
+ mem->limit = max_long;
+ mem->used = 0;
+ mem->max_used = 0;
+ return mem;
+}
+/*
+ * 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)
+{
+ gs_malloc_memory_t *mmem = (gs_malloc_memory_t *) mem;
+ byte *ptr = 0;
+
+#ifdef DEBUG
+ const char *msg;
+ static const char *const ok_msg = "OK";
+
+# define set_msg(str) (msg = (str))
+#else
+# define set_msg(str) DO_NOTHING
+#endif
+
+ if (size > mmem->limit - sizeof(gs_malloc_block_t)) {
+ /* Definitely too large to allocate; also avoids overflow. */
+ set_msg("exceeded limit");
+ } else {
+ uint added = size + sizeof(gs_malloc_block_t);
+
+ if (mmem->limit - added < mmem->used)
+ set_msg("exceeded limit");
+ else if ((ptr = (byte *) malloc(added)) == 0)
+ set_msg("failed");
+ else {
+ gs_malloc_block_t *bp = (gs_malloc_block_t *) ptr;
+
+ if (mmem->allocated)
+ mmem->allocated->prev = bp;
+ bp->next = mmem->allocated;
+ bp->prev = 0;
+ bp->size = size;
+ bp->type = &st_bytes;
+ bp->cname = cname;
+ mmem->allocated = bp;
+ set_msg(ok_msg);
+ ptr = (byte *) (bp + 1);
+ gs_alloc_fill(ptr, gs_alloc_fill_alloc, size);
+ mmem->used += size + sizeof(gs_malloc_block_t);
+ if (mmem->used > mmem->max_used)
+ mmem->max_used = mmem->used;
+ }
+ }
+#ifdef DEBUG
+ if (gs_debug_c('a') || msg != ok_msg)
+ dlprintf4("[a+]gs_malloc(%s)(%u) = 0x%lx: %s\n",
+ client_name_string(cname), size, (ulong) ptr, msg);
+#endif
+ return ptr;
+#undef set_msg
+}
+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;
+ ((gs_malloc_block_t *) 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;
+ ((gs_malloc_block_t *) 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)
+{
+ gs_malloc_memory_t *mmem = (gs_malloc_memory_t *) mem;
+ gs_malloc_block_t *ptr = (gs_malloc_block_t *) obj - 1;
+ gs_memory_type_ptr_t pstype = ptr->type;
+ uint old_size = gs_object_size(mem, obj) + sizeof(gs_malloc_block_t);
+ uint new_size =
+ gs_struct_type_size(pstype) * new_num_elements +
+ sizeof(gs_malloc_block_t);
+ gs_malloc_block_t *new_ptr =
+ (gs_malloc_block_t *) gs_realloc(ptr, old_size, new_size);
+
+ if (new_ptr == 0)
+ return 0;
+ if (new_ptr->prev)
+ new_ptr->prev->next = new_ptr;
+ else
+ mmem->allocated = new_ptr;
+ if (new_ptr->next)
+ new_ptr->next->prev = new_ptr;
+ new_ptr->size = new_size - sizeof(gs_malloc_block_t);
+ mmem->used -= old_size;
+ mmem->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 gs_malloc_block_t *)ptr)[-1].size;
+}
+private gs_memory_type_ptr_t
+gs_heap_object_type(gs_memory_t * mem, const void *ptr)
+{
+ return ((const gs_malloc_block_t *)ptr)[-1].type;
+}
+private void
+gs_heap_free_object(gs_memory_t * mem, void *ptr, client_name_t cname)
+{
+ gs_malloc_memory_t *mmem = (gs_malloc_memory_t *) mem;
+ gs_malloc_block_t *bp = mmem->allocated;
+
+ if_debug3('a', "[a-]gs_free(%s) 0x%lx(%u)\n",
+ client_name_string(cname), (ulong) ptr,
+ (ptr == 0 ? 0 : ((gs_malloc_block_t *) ptr)[-1].size));
+ if (ptr == 0)
+ return;
+ if (ptr == bp + 1) {
+ mmem->allocated = bp->next;
+ mmem->used -= bp->size + sizeof(gs_malloc_block_t);
+
+ if (mmem->allocated)
+ mmem->allocated->prev = 0;
+ gs_alloc_fill(bp, gs_alloc_fill_free,
+ bp->size + sizeof(gs_malloc_block_t));
+ free(bp);
+ } else {
+ gs_malloc_block_t *np;
+
+ /*
+ * bp == 0 at this point is an error, but we'd rather have an
+ * error message than an invalid access.
+ */
+ if (bp) {
+ for (; (np = bp->next) != 0; bp = np) {
+ if (ptr == np + 1) {
+ bp->next = np->next;
+ if (np->next)
+ np->next->prev = bp;
+ mmem->used -= np->size + sizeof(gs_malloc_block_t);
+ gs_alloc_fill(np, gs_alloc_fill_free,
+ np->size + sizeof(gs_malloc_block_t));
+ free(np);
+ return;
+ }
+ }
+ }
+ lprintf2("%s: free 0x%lx not found!\n",
+ client_name_string(cname), (ulong) ptr);
+ free((char *)((gs_malloc_block_t *) 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 int
+gs_heap_register_root(gs_memory_t * mem, gs_gc_root_t * rp,
+ gs_ptr_type_t ptype, void **up, client_name_t cname)
+{
+ return 0;
+}
+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)
+{
+ gs_malloc_memory_t *mmem = (gs_malloc_memory_t *) mem;
+
+ pstat->allocated = mmem->used + heap_available();
+ pstat->used = mmem->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 memory acquired by this allocator. */
+private void
+gs_heap_free_all(gs_memory_t * mem, uint free_mask, client_name_t cname)
+{
+ gs_malloc_memory_t *const mmem = (gs_malloc_memory_t *) mem;
+
+ if (free_mask & FREE_ALL_DATA) {
+ gs_malloc_block_t *bp = mmem->allocated;
+ gs_malloc_block_t *np;
+
+ for (; bp != 0; bp = np) {
+ np = bp->next;
+ if_debug3('a', "[a]gs_heap_free_all(%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);
+ }
+ }
+ if (free_mask & FREE_ALL_ALLOCATOR)
+ free(mem);
+}
diff --git a/pstoraster/gsmalloc.h b/pstoraster/gsmalloc.h
new file mode 100644
index 000000000..076580640
--- /dev/null
+++ b/pstoraster/gsmalloc.h
@@ -0,0 +1,78 @@
+/* Copyright (C) 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Client interface to default (C heap) allocator */
+/* Requires gsmemory.h */
+
+#ifndef gsmalloc_INCLUDED
+# define gsmalloc_INCLUDED
+
+/* Define a memory manager that allocates directly from the C heap. */
+typedef struct gs_malloc_block_s gs_malloc_block_t;
+typedef struct gs_malloc_memory_s {
+ gs_memory_common;
+ gs_malloc_block_t *allocated;
+ long limit;
+ long used;
+ long max_used;
+} gs_malloc_memory_t;
+
+/* Allocate and initialize a malloc memory manager. */
+gs_malloc_memory_t *gs_malloc_memory_init(P0());
+
+/* Release all the allocated blocks, and free the memory manager. */
+/* The cast is unfortunate, but unavoidable. */
+#define gs_malloc_memory_release(mem)\
+ gs_memory_free_all((gs_memory_t *)mem, FREE_ALL_EVERYTHING,\
+ "gs_malloc_memory_release")
+
+/*
+ * Define a default allocator that allocates from the C heap.
+ * (We would really like to get rid of this.)
+ */
+extern gs_malloc_memory_t *gs_malloc_memory_default;
+#define gs_memory_default (*(gs_memory_t *)gs_malloc_memory_default)
+
+/*
+ * The following procedures are historical artifacts that we hope to
+ * get rid of someday.
+ */
+#define gs_malloc_init()\
+ (gs_malloc_memory_default = gs_malloc_memory_init())
+#define gs_malloc_release()\
+ (gs_malloc_memory_release(gs_malloc_memory_default),\
+ gs_malloc_memory_default = 0)
+#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)
+
+/* Define an accessor for the limit on the total allocated heap space. */
+#define gs_malloc_limit (gs_malloc_memory_default->limit)
+
+/* Define an accessor for the maximum amount ever allocated from the heap. */
+#define gs_malloc_max (gs_malloc_memory_default->max_used)
+
+#endif /* gsmalloc_INCLUDED */
diff --git a/pstoraster/gsmatrix.c b/pstoraster/gsmatrix.c
new file mode 100644
index 000000000..c521558b8
--- /dev/null
+++ b/pstoraster/gsmatrix.c
@@ -0,0 +1,455 @@
+/* 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 supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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 const 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_xxyy(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_xxyy(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_xxyy(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 if (is_xyyx(pmat)) {
+ if (is_fzero(pmat->xy) || is_fzero(pmat->yx))
+ return_error(gs_error_undefinedresult);
+ ppt->x = (y - pmat->ty) / pmat->xy;
+ ppt->y = (x - pmat->tx) / pmat->yx;
+ 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_xxyy(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 if (is_xyyx(pmat)) {
+ if (is_fzero(pmat->xy) || is_fzero(pmat->yx))
+ return_error(gs_error_undefinedresult);
+ pdpt->x = dy / pmat->xy;
+ pdpt->y = dx / pmat->yx;
+ } 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..263c5e05f
--- /dev/null
+++ b/pstoraster/gsmatrix.h
@@ -0,0 +1,80 @@
+/* Copyright (C) 1989, 1995, 1997 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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)
+
+/* Macros for testing whether matrix coefficients are zero, */
+/* for shortcuts when the matrix is simple. */
+#define is_xxyy(pmat) is_fzero2((pmat)->xy, (pmat)->yx)
+#define is_xyyx(pmat) is_fzero2((pmat)->xx, (pmat)->yy)
+
+/* 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..467f13136
--- /dev/null
+++ b/pstoraster/gsmdebug.h
@@ -0,0 +1,54 @@
+/* Copyright (C) 1995, 1996, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Requires gdebug.h (for gs_debug) */
+
+#ifndef gsmdebug_INCLUDED
+# define gsmdebug_INCLUDED
+
+/* 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
+# define gs_alloc_fill(ptr, fill, len)\
+ BEGIN if ( gs_alloc_debug ) gs_alloc_memset(ptr, fill, (ulong)(len)); END
+#else
+# define gs_alloc_fill(ptr, fill, len)\
+ DO_NOTHING
+#endif
+
+#endif /* gsmdebug_INCLUDED */
diff --git a/pstoraster/gsmemlok.h b/pstoraster/gsmemlok.h
new file mode 100644
index 000000000..f49e0d848
--- /dev/null
+++ b/pstoraster/gsmemlok.h
@@ -0,0 +1,64 @@
+/* Copyright (C) 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Interface to monitor-locked heap memory allocator */
+
+/* Initial version 2/1/98 by John Desrosiers (soho@crl.com) */
+
+#if !defined(gsmemlok_INCLUDED)
+ #define gsmemlok_INCLUDED
+
+#include "gsmemory.h"
+#include "gxsync.h"
+
+/*
+ * This allocator encapsulates another allocator with a mutex.
+ * Note that it does not keep track of memory that it acquires:
+ * thus free_all with FREE_ALL_DATA is a no-op.
+ */
+
+typedef struct gs_memory_locked_s {
+ gs_memory_common; /* interface outside world sees */
+ gs_memory_t *target; /* allocator to front */
+ gx_monitor_t *monitor; /* monitor to serialize access to functions */
+} gs_memory_locked_t;
+
+/* ---------- Public constructors/destructors ---------- */
+
+/* Initialize a locked memory manager. */
+int gs_memory_locked_init(P2(
+ gs_memory_locked_t * lmem, /* allocator to init */
+ gs_memory_t * target /* allocator to monitor lock */
+ ));
+
+/* Release a locked memory manager. */
+/* Note that this has no effect on the target. */
+#define gs_memory_locked_release(lmem)\
+ gs_memory_free_all(lmem, FREE_STRUCTURES, "gs_memory_locked_release")
+
+/* Get the target of a locked memory manager. */
+gs_memory_t * gs_memory_locked_target(P1(const gs_memory_locked_t *lmem));
+
+#endif /*!defined(gsmemlok_INCLUDED) */
diff --git a/pstoraster/gsmemory.c b/pstoraster/gsmemory.c
new file mode 100644
index 000000000..9dac55dc8
--- /dev/null
+++ b/pstoraster/gsmemory.c
@@ -0,0 +1,196 @@
+/*
+ Copyright 1993-1999 by Easy Software Products.
+ Copyright 1993, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Generic allocator support */
+#include "memory_.h"
+#include "gstypes.h"
+#include "gsmemory.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");
+
+/* The structure type descriptor for GC roots. */
+public_st_gc_root_t();
+
+/* The descriptors for elements and arrays of const strings. */
+private_st_const_string();
+public_st_const_string_element();
+
+/* 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);
+ }
+}
+
+/* Allocate a structure using a "raw memory" allocator. */
+void *
+gs_raw_alloc_struct_immovable(gs_raw_memory_t * rmem,
+ gs_memory_type_ptr_t pstype,
+ client_name_t cname)
+{
+ return gs_alloc_bytes_immovable(rmem, gs_struct_type_size(pstype), cname);
+}
+
+/* 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 consolidation procedure */
+void
+gs_ignore_consolidate_free(gs_memory_t *mem)
+{
+}
+
+/* No-op pointer enumeration procedure */
+ENUM_PTRS_BEGIN_PROC(gs_no_struct_enum_ptrs)
+{
+ return 0;
+ ENUM_PTRS_END_PROC
+}
+
+/* No-op pointer relocation procedure */
+RELOC_PTRS_BEGIN(gs_no_struct_reloc_ptrs)
+{
+}
+RELOC_PTRS_END
+
+/* 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;
+}
+
+/* Register a structure root. */
+int
+gs_register_struct_root(gs_memory_t *mem, gs_gc_root_t *root,
+ void **pp, client_name_t cname)
+{
+ return gs_register_root(mem, root, ptr_struct_type, pp, cname);
+}
+
+/* Normal freeing routine for reference-counted structures. */
+void
+rc_free_struct_only(gs_memory_t * mem, void *data, client_name_t cname)
+{
+ if (mem != 0)
+ gs_free_object(mem, data, cname);
+}
+
+/* ---------------- Basic-structure GC procedures ---------------- */
+
+/* Enumerate pointers */
+ENUM_PTRS_BEGIN_PROC(basic_enum_ptrs)
+{
+ const gc_struct_data_t *psd = pstype->proc_data;
+
+ if (index < psd->num_ptrs) {
+ const gc_ptr_element_t *ppe = &psd->ptrs[index];
+ char *pptr = (char *)vptr + ppe->offset;
+
+ switch ((gc_ptr_type_index_t)ppe->type) {
+ case GC_ELT_OBJ:
+ return ENUM_OBJ(*(void **)pptr);
+ case GC_ELT_STRING:
+ return ENUM_STRING((gs_string *) pptr);
+ case GC_ELT_CONST_STRING:
+ return ENUM_CONST_STRING((gs_string *) pptr);
+ /****** WHAT ABOUT REFS? ******/
+ }
+ }
+ if (!psd->super_type)
+ return 0;
+ return ENUM_USING(*(psd->super_type),
+ (void *)((char *)vptr + psd->super_offset),
+ pstype->ssize, index - psd->num_ptrs);
+}
+ENUM_PTRS_END_PROC
+
+/* Relocate pointers */
+RELOC_PTRS_BEGIN(basic_reloc_ptrs)
+{
+ const gc_struct_data_t *psd = pstype->proc_data;
+ uint i;
+
+ for (i = 0; i < psd->num_ptrs; ++i) {
+ const gc_ptr_element_t *ppe = &psd->ptrs[i];
+ char *pptr = (char *)vptr + ppe->offset;
+
+ switch ((gc_ptr_type_index_t) ppe->type) {
+ case GC_ELT_OBJ:
+ RELOC_OBJ_VAR(*(void **)pptr);
+ break;
+ case GC_ELT_STRING:
+ RELOC_STRING_VAR(*(gs_string *)pptr);
+ break;
+ case GC_ELT_CONST_STRING:
+ RELOC_CONST_STRING_VAR(*(gs_const_string *)pptr);
+ break;
+ /****** WHAT ABOUT REFS? ******/
+ }
+ }
+ if (psd->super_type)
+ RELOC_USING(*(psd->super_type),
+ (void *)((char *)vptr + psd->super_offset),
+ pstype->ssize);
+} RELOC_PTRS_END
diff --git a/pstoraster/gsmemory.h b/pstoraster/gsmemory.h
new file mode 100644
index 000000000..2b8e4c655
--- /dev/null
+++ b/pstoraster/gsmemory.h
@@ -0,0 +1,277 @@
+/* Copyright (C) 1993, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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 is designed to interface to a garbage collector,
+ * although it does not include or call one. The allocator API recognizes
+ * that the garbage collector may move objects, relocating pointers to them;
+ * the API provides for allocating both movable (the default) and immovable
+ * objects. Clients must not attempt to resize immovable objects, and must
+ * not create references to substrings of immovable strings.
+ */
+
+#ifndef gsmemory_INCLUDED
+# define gsmemory_INCLUDED
+
+#include "gsmemraw.h"
+
+/* 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;
+
+/* Define the opaque type for an allocator. */
+/* (The actual structure is defined later in this file.) */
+typedef struct gs_memory_s gs_memory_t;
+
+/* Define the opaque type for a pointer type. */
+typedef struct gs_ptr_procs_s gs_ptr_procs_t;
+typedef const gs_ptr_procs_t *gs_ptr_type_t;
+
+/* Define the opaque type for a GC root. */
+typedef struct gs_gc_root_s gs_gc_root_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))
+
+/*
+ * Define the memory manager procedural interface.
+ */
+typedef struct gs_memory_procs_s {
+
+ gs_raw_memory_procs(gs_memory_t); /* defined in gsmemraw.h */
+
+ /* Redefine inherited procedures with the new allocator type. */
+
+#define gs_memory_proc_alloc_bytes(proc)\
+ gs_memory_t_proc_alloc_bytes(proc, gs_memory_t)
+#define gs_memory_proc_resize_object(proc)\
+ gs_memory_t_proc_resize_object(proc, gs_memory_t)
+#define gs_memory_proc_free_object(proc)\
+ gs_memory_t_proc_free_object(proc, gs_memory_t)
+#define gs_memory_proc_status(proc)\
+ gs_memory_t_proc_status(proc, gs_memory_t)
+#define gs_memory_proc_free_all(proc)\
+ gs_memory_t_proc_free_all(proc, gs_memory_t)
+#define gs_memory_proc_consolidate_free(proc)\
+ gs_memory_t_proc_consolidate_free(proc, gs_memory_t)
+
+ /*
+ * Allocate possibly movable bytes. (We inherit allocating immovable
+ * bytes from the raw memory allocator.)
+ */
+
+#define gs_alloc_bytes(mem, nbytes, cname)\
+ (*(mem)->procs.alloc_bytes)(mem, nbytes, cname)
+ gs_memory_proc_alloc_bytes((*alloc_bytes));
+
+ /*
+ * 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));
+
+ /*
+ * 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));
+
+ /*
+ * 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. root = NULL
+ * asks the memory manager to allocate the root object
+ * itself (immovable, in the manager's parent): this is the usual
+ * way to call this procedure.
+ */
+
+#define gs_memory_proc_register_root(proc)\
+ int 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. The root object itself will be freed iff
+ * it was allocated by gs_register_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));
+
+ /*
+ * 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;
+
+/* Register a structure root. This just calls gs_register_root. */
+int gs_register_struct_root(P4(gs_memory_t *mem, gs_gc_root_t *root,
+ void **pp, client_name_t cname));
+
+/* 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);
+
+/* Define a no-op consolidation procedure. */
+gs_memory_proc_consolidate_free(gs_ignore_consolidate_free);
+
+/*
+ * Allocate a structure using a "raw memory" allocator. Note that this does
+ * not retain the identity of the structure. Note also that it returns a
+ * void *, and does not take the type of the returned pointer as a
+ * parameter.
+ */
+void *gs_raw_alloc_struct_immovable(P3(gs_raw_memory_t * rmem,
+ gs_memory_type_ptr_t pstype,
+ client_name_t cname));
+
+/*
+ * Define an abstract allocator instance.
+ * Subclasses may have state as well.
+ */
+#define gs_memory_common\
+ gs_memory_procs_t procs
+struct gs_memory_s {
+ gs_memory_common;
+};
+
+#endif /* gsmemory_INCLUDED */
diff --git a/pstoraster/gsmemraw.h b/pstoraster/gsmemraw.h
new file mode 100644
index 000000000..019dd4ff2
--- /dev/null
+++ b/pstoraster/gsmemraw.h
@@ -0,0 +1,181 @@
+/* Copyright (C) 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Client interface for "raw memory" allocator */
+
+/* Initial version 02/03/1998 by John Desrosiers (soho@crl.com) */
+/* Completely rewritten 6/26/1998 by L. Peter Deutsch <ghost@aladdin.com> */
+
+#ifndef gsmemraw_INCLUDED
+# define gsmemraw_INCLUDED
+
+/*
+ * This interface provides minimal memory allocation and freeing capability.
+ * It is meant to be used for "wholesale" allocation of blocks -- typically,
+ * but not only, via malloc -- which are then divided up into "retail"
+ * objects. However, since it is a subset (superclass) of the "retail"
+ * interface defined in gsmemory.h, retail allocators implement it as
+ * well, and in fact the malloc interface defined in gsmalloc.h is used for
+ * both wholesale and retail allocation.
+ */
+
+/*
+ * Define the structure for reporting 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 abstract type for the memory manager. */
+typedef struct gs_raw_memory_s gs_raw_memory_t;
+
+/* Define the procedures for raw memory management. Memory managers have no
+ * standard constructor: each implementation defines its own, and is
+ * responsible for calling its superclass' initialization code first.
+ * Similarly, each implementation's destructor (release) must first take
+ * care of its own cleanup and then call the superclass' release.
+ */
+
+ /*
+ * Allocate bytes. The bytes are always aligned maximally
+ * if the processor requires alignment.
+ *
+ * Note that the object memory level can allocate bytes as
+ * either movable or immovable: raw memory blocks are
+ * always immovable.
+ */
+
+#define gs_memory_t_proc_alloc_bytes(proc, mem_t)\
+ byte *proc(P3(mem_t *mem, uint nbytes, client_name_t cname))
+
+#define gs_alloc_bytes_immovable(mem, nbytes, cname)\
+ ((mem)->procs.alloc_bytes_immovable(mem, nbytes, cname))
+
+ /*
+ * Resize an object to a new number of elements. At the raw
+ * memory level, the "element" is a byte; for object memory
+ * (gsmemory.h), the object may be an an array of either
+ * bytes or structures. The new size may be either larger
+ * or smaller than the old.
+ */
+
+#define gs_memory_t_proc_resize_object(proc, mem_t)\
+ void *proc(P4(mem_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))
+
+ /*
+ * Free an object (at the object memory level, this includes
+ * everything except strings). Note: data == 0 must be
+ * allowed, and must be a no-op.
+ */
+
+#define gs_memory_t_proc_free_object(proc, mem_t)\
+ void proc(P3(mem_t *mem, void *data, client_name_t cname))
+
+#define gs_free_object(mem, data, cname)\
+ ((mem)->procs.free_object(mem, data, cname))
+
+ /*
+ * Report status (assigned, used).
+ */
+
+#define gs_memory_t_proc_status(proc, mem_t)\
+ void proc(P2(mem_t *mem, gs_memory_status_t *status))
+
+#define gs_memory_status(mem, pst)\
+ ((mem)->procs.status(mem, pst))
+
+ /*
+ * Free one or more of: data memory acquired by the allocator
+ * (FREE_ALL_DATA), overhead structures other than the
+ * allocator itself (FREE_ALL_STRUCTURES), and the allocator
+ * itself (FREE_ALL_ALLOCATOR). Note that this requires
+ * allocators to keep track of all the memory they have ever
+ * acquired, and where they acquired it.
+ */
+
+#define FREE_ALL_DATA 1
+#define FREE_ALL_STRUCTURES 2
+#define FREE_ALL_ALLOCATOR 4
+#define FREE_ALL_EVERYTHING\
+ (FREE_ALL_DATA | FREE_ALL_STRUCTURES | FREE_ALL_ALLOCATOR)
+
+#define gs_memory_t_proc_free_all(proc, mem_t)\
+ void proc(P3(mem_t *mem, uint free_mask, client_name_t cname))
+
+#define gs_memory_free_all(mem, free_mask, cname)\
+ ((mem)->procs.free_all(mem, free_mask, cname))
+/* Backward compatibility */
+#define gs_free_all(mem)\
+ gs_memory_free_all(mem, FREE_ALL_DATA, "(free_all)")
+
+ /*
+ * Consolidate free space. This may be used as part of (or
+ * as an alternative to) garbage collection, or before
+ * giving up on an attempt to allocate.
+ */
+
+#define gs_memory_t_proc_consolidate_free(proc, mem_t)\
+ void proc(P1(mem_t *mem))
+
+#define gs_consolidate_free(mem)\
+ ((mem)->procs.consolidate_free(mem))
+
+/* Define the members of the procedure structure. */
+#define gs_raw_memory_procs(mem_t)\
+ gs_memory_t_proc_alloc_bytes((*alloc_bytes_immovable), mem_t);\
+ gs_memory_t_proc_resize_object((*resize_object), mem_t);\
+ gs_memory_t_proc_free_object((*free_object), mem_t);\
+ gs_memory_t_proc_status((*status), mem_t);\
+ gs_memory_t_proc_free_all((*free_all), mem_t);\
+ gs_memory_t_proc_consolidate_free((*consolidate_free), mem_t)
+
+/* Define the procedure vector for a raw memory allocator. */
+typedef struct gs_raw_memory_procs_s {
+ gs_raw_memory_procs(gs_raw_memory_t);
+} gs_raw_memory_procs_t;
+
+/*
+ * Define an abstract raw-memory allocator instance.
+ * Subclasses may have state as well.
+ */
+struct gs_raw_memory_s {
+ gs_raw_memory_procs_t procs;
+};
+
+#endif /* gsmemraw_INCLUDED */
diff --git a/pstoraster/gsmisc.c b/pstoraster/gsmisc.c
new file mode 100644
index 000000000..1865d3abb
--- /dev/null
+++ b/pstoraster/gsmisc.c
@@ -0,0 +1,942 @@
+/*
+ Copyright 1993-2000 by Easy Software Products.
+ Copyright 1989, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Miscellaneous utilities for Ghostscript library */
+#include "ctype_.h"
+#include "malloc_.h"
+#include "math_.h"
+#include "memory_.h"
+#include "string_.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;
+
+/* Test whether a given debugging option is selected. */
+/* Upper-case letters automatically include their lower-case counterpart. */
+bool
+gs_debug_c(int c)
+{
+ return
+ (c >= 'a' && c <= 'z' ? gs_debug[c] | gs_debug[c ^ 32] : gs_debug[c]);
+}
+
+/* Define the formats for debugging printout. */
+const char *const dprintf_file_and_line_format = "%10s(%4d): ";
+const char *const dprintf_file_only_format = "%10s(unkn): ";
+
+/*
+ * Define the trace printout procedures. We always include these, in case
+ * other modules were compiled with DEBUG set.
+ */
+private const char *
+dprintf_file_tail(const char *file)
+{
+ const char *tail = file + strlen(file);
+
+ while (tail > file &&
+ (isalnum(tail[-1]) || tail[-1] == '.' || tail[-1] == '_')
+ )
+ --tail;
+ return tail;
+}
+void
+dprintf_file_and_line(FILE * f, const char *file, int line)
+{
+ if (gs_debug['/'])
+ fprintf(f, dprintf_file_and_line_format,
+ dprintf_file_tail(file), line);
+}
+void
+dprintf_file(FILE * f, const char *file)
+{
+ if (gs_debug['/'])
+ fprintf(f, dprintf_file_only_format, dprintf_file_tail(file));
+}
+void
+eprintf_program_name(FILE * f, const char *program_name)
+{
+ if (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);
+}
+void
+lprintf_file_only(FILE * f, const char *file)
+{
+ fprintf(f, "%s(?): ", file);
+}
+
+/* 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 *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 ? code :
+ 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 (size_of(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
+
+#if USE_FPU_FIXED
+
+/*
+ * Convert from floating point to fixed point with scaling.
+ * These are efficient algorithms for FPU-less machines.
+ */
+#define mbits_float 23
+#define mbits_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;
+}
+/*
+ * Given a fixed value x with fbits bits of fraction, set v to the mantissa
+ * (left-justified in 32 bits) and f to the exponent word of the
+ * corresponding floating-point value with mbits bits of mantissa in the
+ * first word. (The exponent part of f is biased by -1, because we add the
+ * top 1-bit of the mantissa to it.)
+ */
+static const byte f2f_shifts[] =
+{4, 3, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0};
+
+#define f2f_declare(v, f)\
+ ulong v;\
+ long f
+#define f2f(x, v, f, mbits, fbits)\
+ if ( x < 0 )\
+ f = 0xc0000000 + (29 << mbits) - ((long)fbits << mbits), v = -x;\
+ else\
+ f = 0x40000000 + (29 << mbits) - ((long)fbits << mbits), v = x;\
+ if ( v < 0x8000 )\
+ v <<= 15, f -= 15 << mbits;\
+ if ( v < 0x800000 )\
+ v <<= 8, f -= 8 << mbits;\
+ if ( v < 0x8000000 )\
+ v <<= 4, f -= 4 << mbits;\
+ { int shift = f2f_shifts[v >> 28];\
+ v <<= shift, f -= shift << mbits;\
+ }
+long
+fixed2float_(fixed x, int frac_bits)
+{
+ f2f_declare(v, f);
+
+ if (x == 0)
+ return 0;
+ f2f(x, v, f, mbits_float, frac_bits);
+ return f + (((v >> 7) + 1) >> 1);
+}
+void
+set_fixed2double_(double *pd, fixed x, int frac_bits)
+{
+ f2f_declare(v, f);
+
+ if (x == 0) {
+ ((long *)pd)[1 - arch_is_big_endian] = 0;
+ ((ulong *) pd)[arch_is_big_endian] = 0;
+ } else {
+ f2f(x, v, f, mbits_double, frac_bits);
+ ((long *)pd)[1 - arch_is_big_endian] = f + (v >> 11);
+ ((ulong *) pd)[arch_is_big_endian] = v << 21;
+ }
+}
+
+/*
+ * Compute A * B / C when 0 <= B < C and A * B exceeds (or might exceed)
+ * the capacity of a long.
+ */
+#ifdef DEBUG
+struct {
+ long mnanb, mnab, manb, mab, mnc, mdq, mde, mds, mqh, mql;
+} fmq_stat;
+
+# define mincr(x) ++fmq_stat.x
+#else
+# define mincr(x) DO_NOTHING
+#endif
+fixed
+fixed_mult_quo(fixed signed_A, fixed B, fixed C)
+{ /* First compute A * B in double-fixed precision. */
+ ulong A = (signed_A < 0 ? -signed_A : signed_A);
+ long msw;
+ ulong lsw;
+ ulong p1;
+
+#define num_bits (sizeof(fixed) * 8)
+#define half_bits (num_bits / 2)
+#define half_mask ((1L << half_bits) - 1)
+ if (B <= half_mask) {
+ if (A <= half_mask) {
+ fixed Q = (ulong) (A * B) / (ulong) C;
+
+ mincr(mnanb);
+ return (signed_A < 0 ? -Q : Q);
+ }
+ /*
+ * We might still have C <= half_mask, which we can
+ * handle with a simpler algorithm.
+ */
+ lsw = (A & half_mask) * B;
+ p1 = (A >> half_bits) * B;
+ if (C <= half_mask) {
+ ulong q0 = (p1 += lsw >> half_bits) / C;
+ ulong rem = ((p1 - C * q0) << half_bits) + (lsw & half_mask);
+ ulong Q = (q0 << half_bits) + rem / C;
+
+ mincr(mnc);
+ return (signed_A < 0 ? -Q : Q);
+ }
+ msw = p1 >> half_bits;
+ mincr(manb);
+ } else if (A <= half_mask) {
+ p1 = A * (B >> half_bits);
+ msw = p1 >> half_bits;
+ lsw = A * (B & half_mask);
+ mincr(mnab);
+ } else { /* We have to compute all 4 products. :-( */
+ ulong lo_A = A & half_mask;
+ ulong hi_A = A >> half_bits;
+ ulong lo_B = B & half_mask;
+ ulong hi_B = B >> half_bits;
+ ulong p1x = hi_A * lo_B;
+
+ msw = hi_A * hi_B;
+ lsw = lo_A * lo_B;
+ p1 = lo_A * hi_B;
+ if (p1 > max_ulong - p1x)
+ msw += 1L << half_bits;
+ p1 += p1x;
+ msw += p1 >> half_bits;
+ mincr(mab);
+ }
+ /* Finish up by adding the low half of p1 to the high half of lsw. */
+#if max_fixed < max_long
+ p1 &= half_mask;
+#endif
+ p1 <<= half_bits;
+ if (p1 > max_ulong - lsw)
+ msw++;
+ lsw += p1;
+ /*
+ * Now divide the double-length product by C. Note that we know msw
+ * < C (otherwise the quotient would overflow). Start by shifting
+ * (msw,lsw) and C left until C >= 1 << (num_bits - 1).
+ */
+ {
+ ulong denom = C;
+ int shift = 0;
+
+#define bits_4th (num_bits / 4)
+ if (denom < 1L << (num_bits - bits_4th)) {
+ mincr(mdq);
+ denom <<= bits_4th, shift += bits_4th;
+ }
+#undef bits_4th
+#define bits_8th (num_bits / 8)
+ if (denom < 1L << (num_bits - bits_8th)) {
+ mincr(mde);
+ denom <<= bits_8th, shift += bits_8th;
+ }
+#undef bits_8th
+ while (!(denom & (1L << (num_bits - 1)))) {
+ mincr(mds);
+ denom <<= 1, ++shift;
+ }
+ msw = (msw << shift) + (lsw >> (num_bits - shift));
+ lsw <<= shift;
+#if max_fixed < max_long
+ lsw &= (1L << (sizeof(fixed) * 8)) - 1;
+#endif
+ /* Compute a trial upper-half quotient. */
+ {
+ ulong hi_D = denom >> half_bits;
+ ulong lo_D = denom & half_mask;
+ ulong hi_Q = (ulong) msw / hi_D;
+
+ /* hi_Q might be too high by 1 or 2, but it isn't too low. */
+ ulong p0 = hi_Q * hi_D;
+ ulong p1 = hi_Q * lo_D;
+ ulong hi_P;
+
+ while ((hi_P = p0 + (p1 >> half_bits)) > msw ||
+ (hi_P == msw && ((p1 & half_mask) << half_bits) > lsw)
+ ) { /* hi_Q was too high by 1. */
+ --hi_Q;
+ p0 -= hi_D;
+ p1 -= lo_D;
+ mincr(mqh);
+ }
+ p1 = (p1 & half_mask) << half_bits;
+ if (p1 > lsw)
+ msw--;
+ lsw -= p1;
+ msw -= hi_P;
+ /* Now repeat to get the lower-half quotient. */
+ msw = (msw << half_bits) + (lsw >> half_bits);
+#if max_fixed < max_long
+ lsw &= half_mask;
+#endif
+ lsw <<= half_bits;
+ {
+ ulong lo_Q = (ulong) msw / hi_D;
+ long Q;
+
+ p1 = lo_Q * lo_D;
+ p0 = lo_Q * hi_D;
+ while ((hi_P = p0 + (p1 >> half_bits)) > msw ||
+ (hi_P == msw && ((p1 & half_mask) << half_bits) > lsw)
+ ) { /* lo_Q was too high by 1. */
+ --lo_Q;
+ p0 -= hi_D;
+ p1 -= lo_D;
+ mincr(mql);
+ }
+ Q = (hi_Q << half_bits) + lo_Q;
+ return (signed_A < 0 ? -Q : Q);
+ }
+ }
+ }
+#undef half_bits
+#undef half_mask
+}
+
+#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);
+ psincos->orthogonal =
+ (is_fzero(psincos->sin) || is_fzero(psincos->cos));
+}
+
+#else /* we have floating point */
+
+static const int isincos[5] =
+{0, 1, 0, -1, 0};
+
+double
+gs_sin_degrees(double ang)
+{
+ double quot = ang / 90;
+
+ if (floor(quot) == quot) {
+ /*
+ * We need 4.0, rather than 4, here because of non-ANSI compilers.
+ * The & 3 is because quot might be negative.
+ */
+ return isincos[(int)fmod(quot, 4.0) & 3];
+ }
+ return sin(ang * (M_PI / 180));
+}
+
+double
+gs_cos_degrees(double ang)
+{
+ double quot = ang / 90;
+
+ if (floor(quot) == quot) {
+ /* See above re the following line. */
+ return isincos[((int)fmod(quot, 4.0) & 3) + 1];
+ }
+ return cos(ang * (M_PI / 180));
+}
+
+void
+gs_sincos_degrees(double ang, gs_sincos_t * psincos)
+{
+ double quot = ang / 90;
+
+ if (floor(quot) == quot) {
+ /* See above re the following line. */
+ int quads = (int)fmod(quot, 4.0) & 3;
+
+ psincos->sin = isincos[quads];
+ psincos->cos = isincos[quads + 1];
+ psincos->orthogonal = true;
+ } else {
+ double arad = ang * (M_PI / 180);
+
+ psincos->sin = sin(arad);
+ psincos->cos = cos(arad);
+ psincos->orthogonal = false;
+ }
+}
+
+#endif /* USE_FPU */
diff --git a/pstoraster/gsnorop.c b/pstoraster/gsnorop.c
new file mode 100644
index 000000000..7b891142d
--- /dev/null
+++ b/pstoraster/gsnorop.c
@@ -0,0 +1,119 @@
+/* Copyright (C) 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Stubs for unimplemented RasterOp */
+#include "gx.h"
+#include "gserrors.h"
+#include "gsrop.h"
+#include "gxdevcli.h"
+#include "gdevmrop.h"
+
+/* Stub accessors to logical operation in graphics state. */
+
+gs_logical_operation_t
+gs_current_logical_op(const gs_state * pgs)
+{
+ return lop_default;
+}
+
+int
+gs_set_logical_op(gs_state * pgs, gs_logical_operation_t lop)
+{
+ return (lop == lop_default ? 0 : gs_note_error(gs_error_rangecheck));
+}
+
+/* Stub RasterOp implementations for memory devices. */
+
+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)
+{
+ return_error(gs_error_rangecheck);
+}
+
+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)
+{
+ return_error(gs_error_rangecheck);
+}
+
+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)
+{
+ return_error(gs_error_rangecheck);
+}
+
+/* Stub default implementations of device procedures. */
+
+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_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_error(gs_error_unknownerror); /* not implemented */
+}
+
+/* Stub RasterOp source devices. */
+
+int
+gx_alloc_rop_texture_device(gx_device_rop_texture ** prsdev, gs_memory_t * mem,
+ client_name_t cname)
+{
+ return_error(gs_error_rangecheck);
+}
+
+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)
+{ /* Never called. */
+}
diff --git a/pstoraster/gspaint.c b/pstoraster/gspaint.c
new file mode 100644
index 000000000..af12ea2c5
--- /dev/null
+++ b/pstoraster/gspaint.c
@@ -0,0 +1,356 @@
+/* Copyright (C) 1989, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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_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
+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
+scale_paths(gs_state * pgs, int log2_scale_x, int log2_scale_y, bool do_path)
+{
+ if (do_path)
+ 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);
+ if (pgs->view_clip != 0)
+ gx_cpath_scale_exp2(pgs->view_clip, log2_scale_x, log2_scale_y);
+ if (pgs->effective_clip_path != pgs->clip_path &&
+ pgs->effective_clip_path != pgs->view_clip
+ )
+ gx_cpath_scale_exp2(pgs->effective_clip_path,
+ log2_scale_x, log2_scale_y);
+ return 0;
+}
+private void
+scale_dash_pattern(gs_state * pgs, floatp scale)
+{
+ int i;
+
+ for (i = 0; i < pgs->line_params.dash.pattern_size; ++i)
+ pgs->line_params.dash.pattern[i] *= scale;
+ pgs->line_params.dash.offset *= scale;
+ pgs->line_params.dash.pattern_length *= scale;
+ pgs->line_params.dash.init_dist_left *= scale;
+ if (pgs->line_params.dot_length_absolute)
+ pgs->line_params.dot_length *= scale;
+}
+private int
+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);
+ scale_paths(pgs, log2_scale.x, log2_scale.y, true);
+ return 1;
+}
+
+/* Release an alpha buffer. */
+private void
+alpha_buffer_release(gs_state * pgs, bool newpath)
+{
+ gx_device_memory *mdev =
+ (gx_device_memory *) gs_currentdevice_inline(pgs);
+
+ (*dev_proc(mdev, close_device)) ((gx_device *) mdev);
+ scale_paths(pgs, -mdev->log2_scale.x, -mdev->log2_scale.y,
+ !(newpath && !gx_path_is_shared(pgs->path)));
+ /* Reference counting will free mdev. */
+ gx_set_device_only(pgs, mdev->target);
+}
+
+/* Fill the current path using a specified rule. */
+private int
+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,
+ pgs->in_charpath);
+ else {
+ int abits, acode;
+
+ gx_set_dev_color(pgs);
+ code = gs_state_color_load(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) {
+ if (pgs->in_charpath == cpm_true_charpath) {
+ /*
+ * A stroke inside a true charpath should do the
+ * equivalent of strokepath.
+ */
+ code = gs_strokepath(pgs);
+ if (code < 0)
+ return code;
+ }
+ code = gx_path_add_char_path(pgs->show_gstate->path, pgs->path,
+ pgs->in_charpath);
+ } else {
+ int abits, acode;
+ float orig_width;
+
+ gx_set_dev_color(pgs);
+ code = gs_state_color_load(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 scale = 1 << (abits / 2);
+ float new_width =
+ (orig_width = gs_currentlinewidth(pgs)) * scale;
+ fixed extra_adjust =
+ float2fixed(max(xxyy, xyyx) * new_width / 2);
+ gx_path spath;
+
+ /* Scale up the line width and dash pattern. */
+ 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);
+ scale_dash_pattern(pgs, scale);
+ /*
+ * The alpha-buffer device requires that we fill the
+ * entire path as a single unit.
+ */
+ gx_path_init_local(&spath, pgs->memory);
+ code = gx_stroke_add(pgs->path, &spath, pgs);
+ gs_setlinewidth(pgs, orig_width);
+ scale_dash_pattern(pgs, 1.0 / scale);
+ if (code >= 0)
+ code = gx_fill_path(&spath, pgs->dev_color, pgs,
+ gx_rule_winding_number,
+ pgs->fill_adjust.x,
+ pgs->fill_adjust.y);
+ gx_path_free(&spath, "gs_stroke");
+ if (acode > 0)
+ alpha_buffer_release(pgs, code >= 0);
+ } else
+ code = gx_stroke_fill(pgs->path, pgs);
+ if (code >= 0)
+ gs_newpath(pgs);
+ }
+ return code;
+}
+
+/* Compute the stroked outline of the current path */
+int
+gs_strokepath(gs_state * pgs)
+{
+ gx_path spath;
+ int code;
+
+ gx_path_init_local(&spath, pgs->memory);
+ code = gx_stroke_add(pgs->path, &spath, pgs);
+ if (code < 0) {
+ gx_path_free(&spath, "gs_strokepath");
+ return code;
+ }
+ return gx_path_assign_free(pgs->path, &spath);
+}
diff --git a/pstoraster/gspaint.h b/pstoraster/gspaint.h
new file mode 100644
index 000000000..e7c6df2c1
--- /dev/null
+++ b/pstoraster/gspaint.h
@@ -0,0 +1,38 @@
+/* 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 supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Requires gsstate.h */
+
+#ifndef gspaint_INCLUDED
+# define gspaint_INCLUDED
+
+/* 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 *));
+
+#endif /* gspaint_INCLUDED */
diff --git a/pstoraster/gsparam.c b/pstoraster/gsparam.c
new file mode 100644
index 000000000..6631f9651
--- /dev/null
+++ b/pstoraster/gsparam.c
@@ -0,0 +1,382 @@
+/* Copyright (C) 1995, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Support for parameter lists */
+#include "memory_.h"
+#include "string_.h"
+#include "gx.h"
+#include "gserrors.h"
+#include "gsparam.h"
+#include "gsstruct.h"
+
+/* Reset a gs_param_key_t enumerator to its initial state */
+void
+param_init_enumerator(gs_param_enumerator_t * enumerator)
+{
+ memset(enumerator, 0, sizeof(*enumerator));
+}
+
+/* Transfer a collection of parameters. */
+private const byte xfer_item_sizes[] = {
+ GS_PARAM_TYPE_SIZES(0)
+};
+int
+gs_param_read_items(gs_param_list * plist, void *obj,
+ const gs_param_item_t * items)
+{
+ const gs_param_item_t *pi;
+ int ecode = 0;
+
+ for (pi = items; pi->key != 0; ++pi) {
+ const char *key = pi->key;
+ void *pvalue = (void *)((char *)obj + pi->offset);
+ gs_param_typed_value typed;
+ int code;
+
+ typed.type = pi->type;
+ code = param_read_requested_typed(plist, key, &typed);
+ switch (code) {
+ default: /* < 0 */
+ ecode = code;
+ case 1:
+ break;
+ case 0:
+ if (typed.type != pi->type) /* shouldn't happen! */
+ ecode = gs_note_error(gs_error_typecheck);
+ else
+ memcpy(pvalue, &typed.value, xfer_item_sizes[pi->type]);
+ }
+ }
+ return ecode;
+}
+int
+gs_param_write_items(gs_param_list * plist, const void *obj,
+ const void *default_obj, const gs_param_item_t * items)
+{
+ const gs_param_item_t *pi;
+ int ecode = 0;
+
+ for (pi = items; pi->key != 0; ++pi) {
+ const char *key = pi->key;
+ const void *pvalue = (const void *)((const char *)obj + pi->offset);
+ int size = xfer_item_sizes[pi->type];
+ gs_param_typed_value typed;
+ int code;
+
+ if (default_obj != 0 &&
+ !memcmp((const void *)((const char *)default_obj + pi->offset),
+ pvalue, size)
+ )
+ continue;
+ memcpy(&typed.value, pvalue, size);
+ typed.type = pi->type;
+ code = (*plist->procs->xmit_typed) (plist, key, &typed);
+ if (code < 0)
+ ecode = code;
+ }
+ return ecode;
+}
+
+/* Read a value, with coercion if requested, needed, and possible. */
+/* If mem != 0, we can coerce int arrays to float arrays. */
+int
+param_coerce_typed(gs_param_typed_value * pvalue, gs_param_type req_type,
+ gs_memory_t * mem)
+{
+ if (req_type == gs_param_type_any || pvalue->type == req_type)
+ return 0;
+ /*
+ * Look for coercion opportunities. It would be wonderful if we
+ * could convert int/float arrays and name/string arrays, but
+ * right now we can't. However, a 0-length heterogenous array
+ * will satisfy a request for any specific type.
+ */
+ switch (pvalue->type /* actual type */ ) {
+ case gs_param_type_int:
+ switch (req_type) {
+ case gs_param_type_long:
+ pvalue->value.l = pvalue->value.i;
+ goto ok;
+ case gs_param_type_float:
+ pvalue->value.f = (float)pvalue->value.l;
+ goto ok;
+ default:
+ break;
+ }
+ break;
+ case gs_param_type_long:
+ switch (req_type) {
+ case gs_param_type_int:
+#if arch_sizeof_int < arch_sizeof_long
+ if (pvalue->value.l != (int)pvalue->value.l)
+ return_error(gs_error_rangecheck);
+#endif
+ pvalue->value.i = (int)pvalue->value.l;
+ goto ok;
+ case gs_param_type_float:
+ pvalue->value.f = (float)pvalue->value.l;
+ goto ok;
+ default:
+ break;
+ }
+ break;
+ case gs_param_type_string:
+ if (req_type == gs_param_type_name)
+ goto ok;
+ break;
+ case gs_param_type_name:
+ if (req_type == gs_param_type_string)
+ goto ok;
+ break;
+ case gs_param_type_int_array:
+ switch (req_type) {
+ case gs_param_type_float_array:{
+ uint size = pvalue->value.ia.size;
+ float *fv;
+ uint i;
+
+ if (mem == 0)
+ break;
+ fv = (float *)gs_alloc_byte_array(mem, size, sizeof(float),
+ "int array => float array");
+
+ if (fv == 0)
+ return_error(gs_error_VMerror);
+ for (i = 0; i < size; ++i)
+ fv[i] = pvalue->value.ia.data[i];
+ pvalue->value.fa.data = fv;
+ pvalue->value.fa.persistent = false;
+ goto ok;
+ }
+ default:
+ break;
+ }
+ break;
+ case gs_param_type_string_array:
+ if (req_type == gs_param_type_name_array)
+ goto ok;
+ break;
+ case gs_param_type_name_array:
+ if (req_type == gs_param_type_string_array)
+ goto ok;
+ break;
+ case gs_param_type_array:
+ if (pvalue->value.d.size == 0 &&
+ (req_type == gs_param_type_int_array ||
+ req_type == gs_param_type_float_array ||
+ req_type == gs_param_type_string_array ||
+ req_type == gs_param_type_name_array)
+ )
+ goto ok;
+ break;
+ default:
+ break;
+ }
+ return_error(gs_error_typecheck);
+ ok:pvalue->type = req_type;
+ return 0;
+}
+int
+param_read_requested_typed(gs_param_list * plist, gs_param_name pkey,
+ gs_param_typed_value * pvalue)
+{
+ gs_param_type req_type = pvalue->type;
+ int code = (*plist->procs->xmit_typed) (plist, pkey, pvalue);
+
+ if (code != 0)
+ return code;
+ return param_coerce_typed(pvalue, req_type, plist->memory);
+}
+
+
+/* ---------------- Fixed-type reading procedures ---------------- */
+
+#define RETURN_READ_TYPED(alt, ptype)\
+ gs_param_typed_value typed;\
+ int code;\
+\
+ typed.type = ptype;\
+ code = param_read_requested_typed(plist, pkey, &typed);\
+ if ( code == 0 )\
+ *pvalue = typed.value.alt;\
+ return code
+
+int
+param_read_null(gs_param_list * plist, gs_param_name pkey)
+{
+ gs_param_typed_value typed;
+
+ typed.type = gs_param_type_null;
+ return param_read_requested_typed(plist, pkey, &typed);
+}
+int
+param_read_bool(gs_param_list * plist, gs_param_name pkey, bool * pvalue)
+{
+ RETURN_READ_TYPED(b, gs_param_type_bool);
+}
+int
+param_read_int(gs_param_list * plist, gs_param_name pkey, int *pvalue)
+{
+ RETURN_READ_TYPED(i, gs_param_type_int);
+}
+int
+param_read_long(gs_param_list * plist, gs_param_name pkey, long *pvalue)
+{
+ RETURN_READ_TYPED(l, gs_param_type_long);
+}
+int
+param_read_float(gs_param_list * plist, gs_param_name pkey, float *pvalue)
+{
+ RETURN_READ_TYPED(f, gs_param_type_float);
+}
+int
+param_read_string(gs_param_list * plist, gs_param_name pkey,
+ gs_param_string * pvalue)
+{
+ RETURN_READ_TYPED(s, gs_param_type_string);
+}
+int
+param_read_name(gs_param_list * plist, gs_param_name pkey,
+ gs_param_string * pvalue)
+{
+ RETURN_READ_TYPED(n, gs_param_type_string);
+}
+int
+param_read_int_array(gs_param_list * plist, gs_param_name pkey,
+ gs_param_int_array * pvalue)
+{
+ RETURN_READ_TYPED(ia, gs_param_type_int_array);
+}
+int
+param_read_float_array(gs_param_list * plist, gs_param_name pkey,
+ gs_param_float_array * pvalue)
+{
+ RETURN_READ_TYPED(fa, gs_param_type_float_array);
+}
+int
+param_read_string_array(gs_param_list * plist, gs_param_name pkey,
+ gs_param_string_array * pvalue)
+{
+ RETURN_READ_TYPED(sa, gs_param_type_string_array);
+}
+int
+param_read_name_array(gs_param_list * plist, gs_param_name pkey,
+ gs_param_string_array * pvalue)
+{
+ RETURN_READ_TYPED(na, gs_param_type_name_array);
+}
+
+#undef RETURN_READ_TYPED
+
+/* ---------------- Default writing procedures ---------------- */
+
+#define RETURN_WRITE_TYPED(alt, ptype)\
+ gs_param_typed_value typed;\
+\
+ typed.value.alt = *pvalue;\
+ typed.type = ptype;\
+ return param_write_typed(plist, pkey, &typed)
+
+int
+param_write_null(gs_param_list * plist, gs_param_name pkey)
+{
+ gs_param_typed_value typed;
+
+ typed.type = gs_param_type_null;
+ return param_write_typed(plist, pkey, &typed);
+}
+int
+param_write_bool(gs_param_list * plist, gs_param_name pkey, const bool * pvalue)
+{
+ RETURN_WRITE_TYPED(b, gs_param_type_bool);
+}
+int
+param_write_int(gs_param_list * plist, gs_param_name pkey, const int *pvalue)
+{
+ RETURN_WRITE_TYPED(i, gs_param_type_int);
+}
+int
+param_write_long(gs_param_list * plist, gs_param_name pkey, const long *pvalue)
+{
+ RETURN_WRITE_TYPED(l, gs_param_type_long);
+}
+int
+param_write_float(gs_param_list * plist, gs_param_name pkey,
+ const float *pvalue)
+{
+ RETURN_WRITE_TYPED(f, gs_param_type_float);
+}
+int
+param_write_string(gs_param_list * plist, gs_param_name pkey,
+ const gs_param_string * pvalue)
+{
+ RETURN_WRITE_TYPED(s, gs_param_type_string);
+}
+int
+param_write_name(gs_param_list * plist, gs_param_name pkey,
+ const gs_param_string * pvalue)
+{
+ RETURN_WRITE_TYPED(n, gs_param_type_string);
+}
+int
+param_write_int_array(gs_param_list * plist, gs_param_name pkey,
+ const gs_param_int_array * pvalue)
+{
+ RETURN_WRITE_TYPED(ia, gs_param_type_int_array);
+}
+int
+param_write_float_array(gs_param_list * plist, gs_param_name pkey,
+ const gs_param_float_array * pvalue)
+{
+ RETURN_WRITE_TYPED(fa, gs_param_type_float_array);
+}
+int
+param_write_string_array(gs_param_list * plist, gs_param_name pkey,
+ const gs_param_string_array * pvalue)
+{
+ RETURN_WRITE_TYPED(sa, gs_param_type_string_array);
+}
+int
+param_write_name_array(gs_param_list * plist, gs_param_name pkey,
+ const gs_param_string_array * pvalue)
+{
+ RETURN_WRITE_TYPED(na, gs_param_type_name_array);
+}
+
+#undef RETURN_WRITE_TYPED
+
+/* ---------------- Default request implementation ---------------- */
+
+int
+gs_param_request_default(gs_param_list * plist, gs_param_name pkey)
+{
+ return 0;
+}
+
+int
+gs_param_requested_default(const gs_param_list * plist, gs_param_name pkey)
+{
+ return -1; /* requested by default */
+}
diff --git a/pstoraster/gsparam.h b/pstoraster/gsparam.h
new file mode 100644
index 000000000..6b58e7f8a
--- /dev/null
+++ b/pstoraster/gsparam.h
@@ -0,0 +1,505 @@
+/* Copyright (C) 1993, 1995, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Client interface to parameter dictionaries */
+
+#ifndef gsparam_INCLUDED
+# define gsparam_INCLUDED
+
+/*
+ * Several interfaces use parameter dictionaries to communicate sets of
+ * (key, value) pairs between a client and 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.
+ */
+
+/* ---------------- Generic interfaces ---------------- */
+
+/* Define the abstract type for a parameter list. */
+#ifndef gs_param_list_DEFINED
+# define gs_param_list_DEFINED
+typedef struct gs_param_list_s gs_param_list;
+
+#endif
+
+/* Define the type for a parameter key name. */
+typedef const char *gs_param_name;
+
+/*
+ * Parameter values fall into three categories:
+ * - Scalar (null, Boolean, int, long, float);
+ * - Homogenous collection (string/name, int array, float array,
+ * string/name array);
+ * - Heterogenous collection (dictionary, int-keyed dictionary, array).
+ * Each category has its own representation and memory management issues.
+ */
+typedef enum {
+ /* Scalar */
+ gs_param_type_null, gs_param_type_bool, gs_param_type_int,
+ gs_param_type_long, gs_param_type_float,
+ /* Homogenous collection */
+ gs_param_type_string, gs_param_type_name,
+ gs_param_type_int_array, gs_param_type_float_array,
+ gs_param_type_string_array, gs_param_type_name_array,
+ /* Heterogenous collection */
+ gs_param_type_dict, gs_param_type_dict_int_keys, gs_param_type_array
+} gs_param_type;
+
+/* Define a "don't care" type for reading typed values. */
+#define gs_param_type_any ((gs_param_type)-1)
+
+/*
+ * Define the structures for homogenous collection values
+ * (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 structure for heterogenous collection values (dictionaries
+ * and heterogenous arrays).
+ */
+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 sizes of the various parameter value types, indexed by type.
+ */
+#define GS_PARAM_TYPE_SIZES(dict_size)\
+ 0, sizeof(bool), sizeof(int), sizeof(long), sizeof(float),\
+ sizeof(gs_param_string), sizeof(gs_param_string),\
+ sizeof(gs_param_int_array), sizeof(gs_param_float_array),\
+ sizeof(gs_param_string_array), sizeof(gs_param_string_array),\
+ (dict_size), (dict_size), (dict_size)
+/*
+ * Define the sizes of the underlying data types contained in or pointed
+ * to by the various value types.
+ */
+#define GS_PARAM_TYPE_BASE_SIZES(dict_elt_size)\
+ 0, sizeof(bool), sizeof(int), sizeof(long), sizeof(float),\
+ 1, 1, sizeof(int), sizeof(float),\
+ sizeof(gs_param_string), sizeof(gs_param_string),\
+ (dict_elt_size), (dict_elt_size), (dict_elt_size)
+
+/* Define tables with 0 for the sizes of the heterogenous collections. */
+extern const byte gs_param_type_sizes[];
+extern const byte gs_param_type_base_sizes[];
+
+/* Define a union capable of holding any parameter value. */
+#define GS_PARAM_VALUE_UNION(dict_type)\
+ bool b;\
+ int i;\
+ long l;\
+ float f;\
+ gs_param_string s;\
+ gs_param_string n;\
+ gs_param_int_array ia;\
+ gs_param_float_array fa;\
+ gs_param_string_array sa;\
+ gs_param_string_array na;\
+ dict_type d
+typedef union gs_param_value_s {
+ GS_PARAM_VALUE_UNION(gs_param_collection);
+} gs_param_value;
+
+/*
+ * Define a structure containing a dynamically typed value (a value along
+ * with its type). Since parameter lists are transient, we don't bother
+ * to create a GC descriptor for this.
+ */
+typedef struct gs_param_typed_value_s {
+ gs_param_value value;
+ gs_param_type type;
+} gs_param_typed_value;
+
+/*
+ * Define the representation alternatives for heterogenous collections.
+ * _any must be 0, for Boolean testing.
+ */
+typedef enum {
+
+ /* Create or accept a general dictionary. */
+
+ gs_param_collection_dict_any = 0,
+
+ /* Create a dictionary with integer string keys ("0", "1", ...); */
+ /* accept a dictionary with integer string keys, or a heterogenous */
+ /* array. */
+
+ gs_param_collection_dict_int_keys = 1,
+
+ /* Create an array if possible, otherwise a dictionary with integer */
+ /* string keys; accept the same types as dict_int_keys. */
+
+ gs_param_collection_array = 2
+
+} gs_param_collection_type_t;
+
+/*
+ * 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 an enumerator used to iterate through the keys in a list.
+ *
+ * All the members of the union must be used such that memset(0) entire
+ * union means 'beginning of enumeration'.
+ */
+typedef union gs_param_enumerator_s {
+ int intval;
+ long longval;
+ void *pvoid;
+ char *pchar;
+} gs_param_enumerator_t;
+typedef gs_const_string gs_param_key_t;
+
+/*
+ * 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.
+ *
+ * A lazy implementation can use the default procedures for scalar and
+ * homogenous collection types: these just called xmit_typed.
+ */
+
+/*
+ * 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 typed value. */
+ /*
+ * Note that read/write_typed do a begin_read/write_collection
+ * if the type is one of the heterogenous collection types.
+ * Note also that even for reading, the caller must set pvalue->type
+ * to the desired type or to gs_param_type_any.
+ */
+
+#define param_proc_xmit_typed(proc)\
+ int proc(P3(gs_param_list *, gs_param_name, gs_param_typed_value *))
+ param_proc_xmit_typed((*xmit_typed));
+ /* See below for param_read_[requested_]typed */
+#define param_write_typed(plist, pkey, pvalue)\
+ (*(plist)->procs->xmit_typed)(plist, pkey, pvalue)
+
+ /* Start transmitting a dictionary or heterogenous value. */
+
+#define param_proc_begin_xmit_collection(proc)\
+ int proc(P4(gs_param_list *, gs_param_name, gs_param_dict *,\
+ gs_param_collection_type_t))
+ param_proc_begin_xmit_collection((*begin_xmit_collection));
+#define param_begin_read_collection(plist, pkey, pvalue, coll_type)\
+ (*(plist)->procs->begin_xmit_collection)(plist, pkey, pvalue, coll_type)
+#define param_begin_read_dict(l, k, v, int_keys)\
+ param_begin_read_collection(l, k, v,\
+ (int_keys ? gs_param_collection_dict_int_keys :\
+ gs_param_collection_dict_any))
+#define param_begin_write_collection(plist, pkey, pvalue, coll_type)\
+ (*(plist)->procs->begin_xmit_collection)(plist, pkey, pvalue, coll_type)
+#define param_begin_write_dict(l, k, v, int_keys)\
+ param_begin_write_collection(l, k, v,\
+ (int_keys ? gs_param_collection_dict_int_keys :\
+ gs_param_collection_dict_any))
+
+ /* Finish transmitting a collection value. */
+
+#define param_proc_end_xmit_collection(proc)\
+ int proc(P3(gs_param_list *, gs_param_name, gs_param_dict *))
+ param_proc_end_xmit_collection((*end_xmit_collection));
+#define param_end_read_collection(plist, pkey, pvalue)\
+ (*(plist)->procs->end_xmit_collection)(plist, pkey, pvalue)
+#define param_end_read_dict(l, k, v) param_end_read_collection(l, k, v)
+#define param_end_write_collection(plist, pkey, pvalue)\
+ (*(plist)->procs->end_xmit_collection)(plist, pkey, pvalue)
+#define param_end_write_dict(l, k, v) param_end_write_collection(l, k, v)
+
+ /*
+ * Get the next key in sequence.
+ * (Only used when reading.)
+ * Use param_init_enumerator(...) to reset to first key.
+ */
+
+#define param_proc_next_key(proc)\
+ int proc(P3(gs_param_list *, gs_param_enumerator_t *, gs_param_key_t *))
+ param_proc_next_key((*next_key));
+#define param_get_next_key(plist, penum, pkey)\
+ (*(plist)->procs->next_key)(plist, penum, pkey)
+
+ /*
+ * Request a specific parameter. (Only used when writing, before
+ * writing any values.) If no specific parameters are requested,
+ * param_requested always returns -1; if specific parameters
+ * are requested, param_requested will return 1 for those,
+ * and may return either 0 or 1 for others.
+ */
+
+#define param_proc_request(proc)\
+ int proc(P2(gs_param_list *, gs_param_name))
+ param_proc_request((*request));
+
+#define param_request(plist, pkey)\
+ ((plist)->procs->request(plist, pkey))
+
+ /*
+ * Determine whether a given key has been requested. (Only used
+ * when writing.) A return value of -1 means that no specific
+ * parameters have been requested; 0 means specific parameters have
+ * been requested, but not this one; 1 means this parameter has
+ * been requested specifically.
+ */
+
+#define param_proc_requested(proc)\
+ int 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;
+
+/* Transmit typed parameters. */
+int param_read_requested_typed(P3(gs_param_list *, gs_param_name,
+ gs_param_typed_value *));
+
+#define param_read_typed(plist, pkey, pvalue)\
+ ((pvalue)->type = gs_param_type_any,\
+ param_read_requested_typed(plist, pkey, pvalue))
+
+/* Transmit parameters of specific types. */
+int param_read_null(P2(gs_param_list *, gs_param_name));
+int param_write_null(P2(gs_param_list *, gs_param_name));
+int param_read_bool(P3(gs_param_list *, gs_param_name, bool *));
+int param_write_bool(P3(gs_param_list *, gs_param_name, const bool *));
+int param_read_int(P3(gs_param_list *, gs_param_name, int *));
+int param_write_int(P3(gs_param_list *, gs_param_name, const int *));
+int param_read_long(P3(gs_param_list *, gs_param_name, long *));
+int param_write_long(P3(gs_param_list *, gs_param_name, const long *));
+int param_read_float(P3(gs_param_list *, gs_param_name, float *));
+int param_write_float(P3(gs_param_list *, gs_param_name, const float *));
+int param_read_string(P3(gs_param_list *, gs_param_name, gs_param_string *));
+int param_write_string(P3(gs_param_list *, gs_param_name,
+ const gs_param_string *));
+int param_read_name(P3(gs_param_list *, gs_param_name, gs_param_string *));
+int param_write_name(P3(gs_param_list *, gs_param_name,
+ const gs_param_string *));
+int param_read_int_array(P3(gs_param_list *, gs_param_name,
+ gs_param_int_array *));
+int param_write_int_array(P3(gs_param_list *, gs_param_name,
+ const gs_param_int_array *));
+int param_read_float_array(P3(gs_param_list *, gs_param_name,
+ gs_param_float_array *));
+int param_write_float_array(P3(gs_param_list *, gs_param_name,
+ const gs_param_float_array *));
+int param_read_string_array(P3(gs_param_list *, gs_param_name,
+ gs_param_string_array *));
+int param_write_string_array(P3(gs_param_list *, gs_param_name,
+ const gs_param_string_array *));
+int param_read_name_array(P3(gs_param_list *, gs_param_name,
+ gs_param_string_array *));
+int param_write_name_array(P3(gs_param_list *, gs_param_name,
+ const gs_param_string_array *));
+
+/* Define an abstract parameter dictionary. Implementations are */
+/* concrete subclasses. */
+#define gs_param_list_common\
+ const gs_param_list_procs *procs;\
+ gs_memory_t *memory /* for allocating coerced arrays */
+struct gs_param_list_s {
+ gs_param_list_common;
+};
+
+/* Initialize a parameter list key enumerator. */
+void param_init_enumerator(P1(gs_param_enumerator_t * penum));
+
+/*
+ * The following interface provides a convenient way to read and set
+ * collections of parameters of any type other than dictionaries.
+ */
+
+typedef struct gs_param_item_s {
+ const char *key;
+ byte /*gs_param_type */ type;
+ short offset; /* offset of value in structure */
+} gs_param_item_t;
+#define gs_param_item_end { 0 } /* list terminator */
+/*
+ * Transfer a collection of parameters.
+ * For param_write_items, if a parameter value is equal to the value in
+ * the optional default_obj, the item isn't transferred.
+ */
+int gs_param_read_items(P3(gs_param_list * plist, void *obj,
+ const gs_param_item_t * items));
+int gs_param_write_items(P4(gs_param_list * plist, const void *obj,
+ const void *default_obj,
+ const gs_param_item_t * items));
+
+/* ---------------- Default implementation ---------------- */
+
+/*
+ * Provide default generic implementations of param_request and
+ * param_requested.
+ */
+param_proc_request(gs_param_request_default); /* does nothing */
+param_proc_requested(gs_param_requested_default); /* always returns true */
+
+/*
+ * 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_c_param *head;
+ uint count;
+ bool any_requested;
+ gs_param_collection_type_t coll_type;
+} 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 *));
+
+/*
+ * Internal procedure to read a value, with coercion if requested, needed,
+ * and possible. If mem != 0, we can coerce int arrays to float arrays, and
+ * possibly do other coercions later.
+ */
+int param_coerce_typed(P3(gs_param_typed_value * pvalue,
+ gs_param_type req_type, gs_memory_t * mem));
+
+#endif /* gsparam_INCLUDED */
diff --git a/pstoraster/gsparams.c b/pstoraster/gsparams.c
new file mode 100644
index 000000000..1c58c5a92
--- /dev/null
+++ b/pstoraster/gsparams.c
@@ -0,0 +1,417 @@
+/* Copyright (C) 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Generic parameter list serializer & expander */
+
+/* Initial version 2/1/98 by John Desrosiers (soho@crl.com) */
+
+#include "gx.h"
+#include "memory_.h"
+#include "gserrors.h"
+#include "gsparams.h"
+
+/* ----------- Local Type Decl's ------------ */
+typedef struct {
+ byte *buf; /* current buffer ptr */
+ byte *buf_end; /* end of buffer */
+ unsigned total_sizeof; /* current # bytes in buf */
+} WriteBuffer;
+
+/* ---------- Forward refs ----------- */
+private void
+ align_to(P2(
+ const byte ** src, /* pointer to align */
+ unsigned alignment /* alignment, must be power of 2 */
+ ));
+private void
+ put_word(P2(
+ unsigned source, /* number to put to buffer */
+ WriteBuffer * dest /* destination descriptor */
+ ));
+private void
+ put_bytes(P3(
+ const byte * source, /* bytes to put to buffer */
+ unsigned source_sizeof, /* # bytes to put */
+ WriteBuffer * dest /* destination descriptor */
+ ));
+private void
+ put_alignment(P2(
+ unsigned alignment, /* alignment to match, must be power 2 */
+ WriteBuffer * dest /* destination descriptor */
+ ));
+
+/* Get word compressed with put_word */
+private unsigned /* decompressed word */
+ get_word(P1(
+ const byte ** src /* UPDATES: ptr to src buf ptr */
+ ));
+
+
+/* ------------ Serializer ------------ */
+/* Serialize the contents of a gs_param_list (including sub-dicts) */
+int /* ret -ve err, else # bytes needed to represent param list, whether */
+
+/* or not it actually fit into buffer. List was successully */
+
+/* serialized only if if this # is <= supplied buf size. */
+gs_param_list_serialize(
+ gs_param_list * list, /* root of list to serialize */
+ /* list MUST BE IN READ MODE */
+ byte * buf, /* destination buffer (can be 0) */
+ int buf_sizeof /* # bytes available in buf (can be 0) */
+)
+{
+ int code = 0;
+ int temp_code;
+ gs_param_enumerator_t key_enum;
+ gs_param_key_t key;
+ WriteBuffer write_buf;
+
+ write_buf.buf = buf;
+ write_buf.buf_end = buf + (buf ? buf_sizeof : 0);
+ write_buf.total_sizeof = 0;
+ param_init_enumerator(&key_enum);
+
+ /* Each item is serialized as ("word" means compressed word):
+ * word: key sizeof + 1, or 0 if end of list/dict
+ * word: data type(gs_param_type_xxx)
+ * byte[]: key, including trailing \0
+ * (if simple type)
+ * byte[]: unpacked representation of data
+ * (if simple array or string)
+ * byte[]: unpacked mem image of gs_param_xxx_array structure
+ * pad: to array alignment
+ * byte[]: data associated with array contents
+ * (if string/name array)
+ * byte[]: unpacked mem image of gs_param_string_array structure
+ * pad: to void *
+ * { gs_param_string structure mem image;
+ * data associated with string;
+ * } for each string in array
+ * (if dict/dict_int_keys)
+ * word: # of entries in dict,
+ * pad: to void *
+ * dict entries follow immediately until end-of-dict
+ *
+ * NB that this format is designed to allow using an input buffer
+ * as the direct source of data when expanding a gs_c_param_list
+ */
+ /* Enumerate all the keys; use keys to get their typed values */
+ while ((code = param_get_next_key(list, &key_enum, &key)) == 0) {
+ int value_top_sizeof;
+ int value_base_sizeof;
+
+ /* Get next datum & put its type & key to buffer */
+ gs_param_typed_value value;
+ char string_key[256];
+
+ if (sizeof(string_key) < key.size + 1) {
+ code = gs_note_error(gs_error_rangecheck);
+ break;
+ }
+ memcpy(string_key, key.data, key.size);
+ string_key[key.size] = 0;
+ if ((code = param_read_typed(list, string_key, &value)) != 0) {
+ code = code > 0 ? gs_note_error(gs_error_unknownerror) : code;
+ break;
+ }
+ put_word((unsigned)key.size + 1, &write_buf);
+ put_word((unsigned)value.type, &write_buf);
+ put_bytes((byte *) string_key, key.size + 1, &write_buf);
+
+ /* Put value & its size to buffer */
+ value_top_sizeof = gs_param_type_sizes[value.type];
+ value_base_sizeof = gs_param_type_base_sizes[value.type];
+ switch (value.type) {
+ case gs_param_type_null:
+ case gs_param_type_bool:
+ case gs_param_type_int:
+ case gs_param_type_long:
+ case gs_param_type_float:
+ put_bytes((byte *) & value.value, value_top_sizeof, &write_buf);
+ break;
+
+ case gs_param_type_string:
+ case gs_param_type_name:
+ case gs_param_type_int_array:
+ case gs_param_type_float_array:
+ put_bytes((byte *) & value.value, value_top_sizeof, &write_buf);
+ put_alignment(value_base_sizeof, &write_buf);
+ value_base_sizeof *= value.value.s.size;
+ put_bytes(value.value.s.data, value_base_sizeof, &write_buf);
+ break;
+
+ case gs_param_type_string_array:
+ case gs_param_type_name_array:
+ value_base_sizeof *= value.value.sa.size;
+ put_bytes((const byte *)&value.value, value_top_sizeof, &write_buf);
+ put_alignment(sizeof(void *), &write_buf);
+
+ put_bytes((const byte *)value.value.sa.data, value_base_sizeof,
+ &write_buf);
+ {
+ int str_count;
+ const gs_param_string *sa;
+
+ for (str_count = value.value.sa.size,
+ sa = value.value.sa.data; str_count-- > 0; ++sa)
+ put_bytes(sa->data, sa->size, &write_buf);
+ }
+ break;
+
+ case gs_param_type_dict:
+ case gs_param_type_dict_int_keys:
+ put_word(value.value.d.size, &write_buf);
+ put_alignment(sizeof(void *), &write_buf);
+
+ {
+ int bytes_written =
+ gs_param_list_serialize(value.value.d.list,
+ write_buf.buf,
+ write_buf.buf ? write_buf.buf_end - write_buf.buf : 0);
+
+ temp_code = param_end_read_dict(list,
+ (const char *)key.data,
+ &value.value.d);
+ if (bytes_written < 0)
+ code = bytes_written;
+ else {
+ code = temp_code;
+ if (bytes_written)
+ put_bytes(write_buf.buf, bytes_written, &write_buf);
+ }
+ }
+ break;
+
+ default:
+ code = gs_note_error(gs_error_unknownerror);
+ break;
+ }
+ if (code < 0)
+ break;
+ }
+
+ /* Write end marker, which is an (illegal) 0 key length */
+ if (code >= 0) {
+ put_word(0, &write_buf);
+ code = write_buf.total_sizeof;
+ }
+ return code;
+}
+
+
+/* ------------ Expander --------------- */
+/* Expand a buffer into a gs_param_list (including sub-dicts) */
+int /* ret -ve err, +ve # of chars read from buffer */
+gs_param_list_unserialize(
+ gs_param_list * list, /* root of list to expand to */
+ /* list MUST BE IN WRITE MODE */
+ const byte * buf /* source buffer */
+)
+{
+ int code = 0;
+ const byte *orig_buf = buf;
+
+ do {
+ gs_param_typed_value typed;
+ gs_param_name key;
+ unsigned key_sizeof;
+ int value_top_sizeof;
+ int value_base_sizeof;
+ int temp_code;
+ gs_param_type type;
+
+ /* key length, 0 indicates end of data */
+ key_sizeof = get_word(&buf);
+ if (key_sizeof == 0) /* end of data */
+ break;
+
+ /* data type */
+ type = (gs_param_type) get_word(&buf);
+
+ /* key */
+ key = (gs_param_name) buf;
+ buf += key_sizeof;
+
+ /* Data values */
+ value_top_sizeof = gs_param_type_sizes[type];
+ value_base_sizeof = gs_param_type_base_sizes[type];
+ typed.type = type;
+ if (type != gs_param_type_dict && type != gs_param_type_dict_int_keys) {
+ memcpy(&typed.value, buf, value_top_sizeof);
+ buf += value_top_sizeof;
+ }
+ switch (type) {
+ case gs_param_type_null:
+ case gs_param_type_bool:
+ case gs_param_type_int:
+ case gs_param_type_long:
+ case gs_param_type_float:
+ break;
+
+ case gs_param_type_string:
+ case gs_param_type_name:
+ case gs_param_type_int_array:
+ case gs_param_type_float_array:
+ align_to(&buf, value_base_sizeof);
+ typed.value.s.data = buf;
+ typed.value.s.persistent = false;
+ buf += typed.value.s.size * value_base_sizeof;
+ break;
+
+ case gs_param_type_string_array:
+ case gs_param_type_name_array:
+ align_to(&buf, sizeof(void *));
+
+ typed.value.sa.data = (const gs_param_string *)buf;
+ typed.value.sa.persistent = false;
+ buf += typed.value.s.size * value_base_sizeof;
+ {
+ int str_count;
+ gs_param_string *sa;
+
+ for (str_count = typed.value.sa.size,
+ sa = (gs_param_string *) typed.value.sa.data;
+ str_count-- > 0; ++sa) {
+ sa->data = buf;
+ sa->persistent = false;
+ buf += sa->size;
+ }
+ }
+ break;
+
+ case gs_param_type_dict:
+ case gs_param_type_dict_int_keys:
+ typed.value.d.size = get_word(&buf);
+ code = param_begin_write_dict
+ (list, key, &typed.value.d, type == gs_param_type_dict_int_keys);
+ if (code < 0)
+ break;
+ align_to(&buf, sizeof(void *));
+
+ code = gs_param_list_unserialize(typed.value.d.list, buf);
+ temp_code = param_end_write_dict(list, key, &typed.value.d);
+ if (code >= 0) {
+ buf += code;
+ code = temp_code;
+ }
+ break;
+
+ default:
+ code = gs_note_error(gs_error_unknownerror);
+ break;
+ }
+ if (code < 0)
+ break;
+ if (typed.type != gs_param_type_dict && typed.type != gs_param_type_dict_int_keys)
+ code = param_write_typed(list, key, &typed);
+ }
+ while (code >= 0);
+
+ return code >= 0 ? buf - orig_buf : code;
+}
+
+
+/* ---------- Utility functions -------- */
+
+/* Align a byte pointer on the next Nth byte */
+private void
+align_to(
+ const byte ** src, /* pointer to align */
+ unsigned alignment /* alignment, must be power of 2 */
+)
+{
+ *src += -(int)alignment_mod(*src, alignment) & (alignment - 1);
+}
+
+/* Put compressed word repr to a buffer */
+private void
+put_word(
+ unsigned source, /* number to put to buffer */
+ WriteBuffer * dest /* destination descriptor */
+)
+{
+ do {
+ byte chunk = source & 0x7f;
+
+ if (source >= 0x80)
+ chunk |= 0x80;
+ source >>= 7;
+ ++dest->total_sizeof;
+ if (dest->buf && dest->buf < dest->buf_end)
+ *dest->buf++ = chunk;
+ }
+ while (source != 0);
+}
+
+/* Put array of bytes to buffer */
+private void
+put_bytes(
+ const byte * source, /* bytes to put to buffer */
+ unsigned source_sizeof, /* # bytes to put */
+ WriteBuffer * dest /* destination descriptor */
+)
+{
+ dest->total_sizeof += source_sizeof;
+ if (dest->buf && dest->buf + source_sizeof <= dest->buf_end) {
+ if (dest->buf != source)
+ memcpy(dest->buf, source, source_sizeof);
+ dest->buf += source_sizeof;
+ }
+}
+
+/* Pad destination out to req'd alignment w/zeros */
+private void
+put_alignment(
+ unsigned alignment, /* alignment to match, must be power 2 */
+ WriteBuffer * dest /* destination descriptor */
+)
+{
+ static const byte zero =
+ {0};
+
+ while ((dest->total_sizeof & (alignment - 1)) != 0)
+ put_bytes(&zero, 1, dest);
+}
+
+/* Get word compressed with put_word */
+private unsigned /* decompressed word */
+get_word(
+ const byte ** src /* UPDATES: ptr to src buf ptr */
+)
+{
+ unsigned dest = 0;
+ byte chunk;
+ unsigned shift = 0;
+
+ do {
+ chunk = *(*src)++;
+ dest |= (chunk & 0x7f) << shift;
+ shift += 7;
+ }
+ while (chunk & 0x80);
+
+ return dest;
+}
diff --git a/pstoraster/gsparams.h b/pstoraster/gsparams.h
new file mode 100644
index 000000000..f655364e1
--- /dev/null
+++ b/pstoraster/gsparams.h
@@ -0,0 +1,77 @@
+/* Copyright (C) 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Serializer/expander for gs_parm_list's */
+
+#ifndef gsparams_INCLUDED
+# define gsparams_INCLUDED
+
+/* Initial version 2/1/98 by John Desrosiers (soho@crl.com) */
+/* 8/8/98 L. Peter Deutsch (ghost@aladdin.com) Completely redesigned
+ to use stream rather than buffer API (but retained former API for
+ compatibility as well). */
+
+#include "stream.h"
+#include "gsparam.h"
+
+#if 0 /****************/
+
+/* ------ Future interface, implemented in gsparam2.c ------ */
+
+/*
+ * Serialize the contents of a gs_param_list, including sub-collections,
+ * onto a stream. The list must be in READ mode.
+ */
+int gs_param_list_puts(P2(stream *dest, gs_param_list *list));
+
+/*
+ * Unserialize a parameter list, including sub-collections, from a stream.
+ * The list must be in WRITE mode.
+ */
+int gs_param_list_gets(P3(stream *src, gs_param_list *list, gs_memory_t *mem));
+
+#else /****************/
+
+/* ------ Present interface, implemented in gsparams.c ------ */
+
+/*
+ * Serialize a parameter list into a buffer. Return the actual number
+ * of bytes required to store the list, or a negative error code.
+ * The list was stored successfully iff the return value is positive and
+ * less than or equal to the buffer size. Note that the buffer may be
+ * NULL, in which case nothing is stored (but the size is still returned).
+ */
+int gs_param_list_serialize(P3(gs_param_list *list, byte *buf, int buf_size));
+
+/*
+ * Unserialize a parameter list from a buffer. Return the actual number
+ * of bytes occupied by the list, or a negative error code. The buffer
+ * must be void * aligned.
+ */
+int gs_param_list_unserialize(P2(gs_param_list *list, const byte *buf));
+
+#endif /****************/
+
+#endif /* gsparams_INCLUDED */
diff --git a/pstoraster/gspath.c b/pstoraster/gspath.c
new file mode 100644
index 000000000..bc69bb02e
--- /dev/null
+++ b/pstoraster/gspath.c
@@ -0,0 +1,518 @@
+/* Copyright (C) 1989, 1995, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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 "gxdevmem.h" /* for gs_device_is_memory */
+#include "gzcpath.h"
+
+/* ------ Miscellaneous ------ */
+
+int
+gs_newpath(gs_state * pgs)
+{
+ return gx_path_new(pgs->path);
+}
+
+int
+gs_closepath(gs_state * pgs)
+{
+ gx_path *ppath = pgs->path;
+ int code = gx_path_close_subpath(ppath);
+
+ if (code < 0)
+ return code;
+ if (path_start_outside_range(ppath))
+ path_set_outside_position(ppath, ppath->outside_start.x,
+ ppath->outside_start.y);
+ return code;
+}
+
+int
+gs_upmergepath(gs_state * pgs)
+{
+ return gx_path_add_path(pgs->saved->path, pgs->path);
+}
+
+/* Get the current path (for internal use only). */
+gx_path *
+gx_current_path(const gs_state * pgs)
+{
+ return pgs->path;
+}
+
+/* ------ Points and lines ------ */
+
+/*
+ * Define clamped values for out-of-range coordinates.
+ * Currently the path drawing routines can't handle values
+ * close to the edge of the representable space.
+ */
+#define max_coord_fixed (max_fixed - int2fixed(1000)) /* arbitrary */
+#define min_coord_fixed (-max_coord_fixed)
+private void
+clamp_point(gs_fixed_point * ppt, floatp x, floatp y)
+{
+#define clamp_coord(xy)\
+ ppt->xy = (xy > fixed2float(max_coord_fixed) ? max_coord_fixed :\
+ xy < fixed2float(min_coord_fixed) ? min_coord_fixed :\
+ float2fixed(xy))
+ clamp_coord(x);
+ clamp_coord(y);
+#undef clamp_coord
+}
+
+int
+gs_currentpoint(const gs_state * pgs, gs_point * ppt)
+{
+ gx_path *ppath = pgs->path;
+ int code;
+ gs_fixed_point pt;
+
+ if (path_outside_range(ppath))
+ return gs_itransform((gs_state *) pgs,
+ ppath->outside_position.x,
+ ppath->outside_position.y, ppt);
+ 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)
+{
+ gx_path *ppath = pgs->path;
+ gs_fixed_point pt;
+ int code;
+
+ if ((code = gs_point_transform2fixed(&pgs->ctm, x, y, &pt)) < 0) {
+ if (pgs->clamp_coordinates) { /* Handle out-of-range coordinates. */
+ gs_point opt;
+
+ if (code != gs_error_limitcheck ||
+ (code = gs_transform(pgs, x, y, &opt)) < 0
+ )
+ return code;
+ clamp_point(&pt, opt.x, opt.y);
+ code = gx_path_add_point(ppath, pt.x, pt.y);
+ if (code < 0)
+ return code;
+ path_set_outside_position(ppath, opt.x, opt.y);
+ ppath->outside_start = ppath->outside_position;
+ ppath->start_flags = ppath->state_flags;
+ }
+ return code;
+ }
+ return gx_path_add_point(ppath, pt.x, pt.y);
+}
+
+int
+gs_rmoveto(gs_state * pgs, floatp x, floatp y)
+{
+ gs_fixed_point dpt;
+ int code;
+
+ if ((code = gs_distance_transform2fixed(&pgs->ctm, x, y, &dpt)) < 0 ||
+ (code = gx_path_add_relative_point(pgs->path, dpt.x, dpt.y)) < 0
+ ) { /* Handle all exceptional conditions here. */
+ gs_point upt;
+
+ if ((code = gs_currentpoint(pgs, &upt)) < 0)
+ return code;
+ return gs_moveto(pgs, upt.x + x, upt.y + y);
+ }
+ return code;
+}
+
+int
+gs_lineto(gs_state * pgs, floatp x, floatp y)
+{
+ gx_path *ppath = pgs->path;
+ int code;
+ gs_fixed_point pt;
+
+ if ((code = gs_point_transform2fixed(&pgs->ctm, x, y, &pt)) < 0) {
+ if (pgs->clamp_coordinates) { /* Handle out-of-range coordinates. */
+ gs_point opt;
+
+ if (code != gs_error_limitcheck ||
+ (code = gs_transform(pgs, x, y, &opt)) < 0
+ )
+ return code;
+ clamp_point(&pt, opt.x, opt.y);
+ code = gx_path_add_line(ppath, pt.x, pt.y);
+ if (code < 0)
+ return code;
+ path_set_outside_position(ppath, opt.x, opt.y);
+ }
+ return code;
+ }
+ return gx_path_add_line(pgs->path, pt.x, pt.y);
+}
+
+int
+gs_rlineto(gs_state * pgs, floatp x, floatp y)
+{
+ gx_path *ppath = pgs->path;
+ gs_fixed_point dpt;
+ fixed nx, ny;
+ int code;
+
+ if (!path_position_in_range(ppath) ||
+ (code = gs_distance_transform2fixed(&pgs->ctm, x, y, &dpt)) < 0 ||
+ /* Check for overflow in addition. */
+ (((nx = ppath->position.x + dpt.x) ^ dpt.x) < 0 &&
+ (ppath->position.x ^ dpt.x) >= 0) ||
+ (((ny = ppath->position.y + dpt.y) ^ dpt.y) < 0 &&
+ (ppath->position.y ^ dpt.y) >= 0) ||
+ (code = gx_path_add_line(ppath, nx, ny)) < 0
+ ) { /* Handle all exceptional conditions here. */
+ gs_point upt;
+
+ if ((code = gs_currentpoint(pgs, &upt)) < 0)
+ return code;
+ return gs_lineto(pgs, upt.x + x, upt.y + 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 code1 = gs_point_transform2fixed(&pgs->ctm, x1, y1, &p1);
+ int code2 = gs_point_transform2fixed(&pgs->ctm, x2, y2, &p2);
+ int code3 = gs_point_transform2fixed(&pgs->ctm, x3, y3, &p3);
+ gx_path *ppath = pgs->path;
+
+ if ((code1 | code2 | code3) < 0) {
+ if (pgs->clamp_coordinates) { /* Handle out-of-range coordinates. */
+ gs_point opt1, opt2, opt3;
+ int code;
+
+ if ((code1 < 0 && code1 != gs_error_limitcheck) ||
+ (code1 = gs_transform(pgs, x1, y1, &opt1)) < 0
+ )
+ return code1;
+ if ((code2 < 0 && code2 != gs_error_limitcheck) ||
+ (code2 = gs_transform(pgs, x2, y2, &opt2)) < 0
+ )
+ return code2;
+ if ((code3 < 0 && code3 != gs_error_limitcheck) ||
+ (code3 = gs_transform(pgs, x3, y3, &opt3)) < 0
+ )
+ return code3;
+ clamp_point(&p1, opt1.x, opt1.y);
+ clamp_point(&p2, opt2.x, opt2.y);
+ clamp_point(&p3, opt3.x, opt3.y);
+ code = gx_path_add_curve(ppath,
+ p1.x, p1.y, p2.x, p2.y, p3.x, p3.y);
+ if (code < 0)
+ return code;
+ path_set_outside_position(ppath, opt3.x, opt3.y);
+ return code;
+ } else
+ return (code1 < 0 ? code1 : code2 < 0 ? code2 : code3);
+ }
+ return gx_path_add_curve(ppath,
+ 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)
+{
+ gx_path *ppath = pgs->path;
+ gs_fixed_point p1, p2, p3;
+ fixed ptx, pty;
+ int code;
+
+/****** SHOULD CHECK FOR OVERFLOW IN ADDITION ******/
+ if (!path_position_in_range(ppath) ||
+ (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 ||
+ (ptx = ppath->position.x, pty = ppath->position.y,
+ code = gx_path_add_curve(ppath, ptx + p1.x, pty + p1.y,
+ ptx + p2.x, pty + p2.y,
+ ptx + p3.x, pty + p3.y)) < 0
+ ) { /* Handle all exceptional conditions here. */
+ gs_point upt;
+
+ if ((code = gs_currentpoint(pgs, &upt)) < 0)
+ return code;
+ return gs_curveto(pgs, upt.x + dx1, upt.y + dy1,
+ upt.x + dx2, upt.y + dy2,
+ upt.x + dx3, upt.y + dy3);
+ }
+ return code;
+}
+
+/* ------ Clipping ------ */
+
+/* Forward references */
+private int common_clip(P2(gs_state *, int));
+
+/*
+ * Return the effective clipping path of a graphics state. Sometimes this
+ * is the intersection of the clip path and the view clip path; sometimes it
+ * is just the clip path. We aren't sure what the correct algorithm is for
+ * this: for now, we use view clipping unless the current device is a memory
+ * device. This takes care of the most important case, where the current
+ * device is a cache device.
+ */
+int
+gx_effective_clip_path(gs_state * pgs, gx_clip_path ** ppcpath)
+{
+ gs_id view_clip_id =
+ (pgs->view_clip == 0 || pgs->view_clip->rule == 0 ? gs_no_id :
+ pgs->view_clip->id);
+
+ if (gs_device_is_memory(pgs->device)) {
+ *ppcpath = pgs->clip_path;
+ return 0;
+ }
+ if (pgs->effective_clip_id == pgs->clip_path->id &&
+ pgs->effective_view_clip_id == view_clip_id
+ ) {
+ *ppcpath = pgs->effective_clip_path;
+ return 0;
+ }
+ /* Update the cache. */
+ if (view_clip_id == gs_no_id) {
+ if (!pgs->effective_clip_shared)
+ gx_cpath_free(pgs->effective_clip_path, "gx_effective_clip_path");
+ pgs->effective_clip_path = pgs->clip_path;
+ pgs->effective_clip_shared = true;
+ } else {
+ gs_fixed_rect cbox, vcbox;
+
+ gx_cpath_inner_box(pgs->clip_path, &cbox);
+ gx_cpath_outer_box(pgs->view_clip, &vcbox);
+ if (rect_within(vcbox, cbox)) {
+ if (!pgs->effective_clip_shared)
+ gx_cpath_free(pgs->effective_clip_path,
+ "gx_effective_clip_path");
+ pgs->effective_clip_path = pgs->view_clip;
+ pgs->effective_clip_shared = true;
+ } else {
+ /* Construct the intersection of the two clip paths. */
+ int code;
+ gx_clip_path ipath;
+ gx_path vpath;
+ gx_clip_path *npath = pgs->effective_clip_path;
+
+ if (pgs->effective_clip_shared) {
+ npath = gx_cpath_alloc(pgs->memory, "gx_effective_clip_path");
+ if (npath == 0)
+ return_error(gs_error_VMerror);
+ }
+ gx_cpath_init_local(&ipath, pgs->memory);
+ code = gx_cpath_assign_preserve(&ipath, pgs->clip_path);
+ if (code < 0)
+ return code;
+ gx_path_init_local(&vpath, pgs->memory);
+ code = gx_cpath_to_path(pgs->view_clip, &vpath);
+ if (code < 0 ||
+ (code = gx_cpath_clip(pgs, &ipath, &vpath,
+ gx_rule_winding_number)) < 0 ||
+ (code = gx_cpath_assign_free(npath, &ipath)) < 0
+ )
+ DO_NOTHING;
+ gx_path_free(&vpath, "gx_effective_clip_path");
+ gx_cpath_free(&ipath, "gx_effective_clip_path");
+ if (code < 0)
+ return code;
+ pgs->effective_clip_path = npath;
+ pgs->effective_clip_shared = false;
+ }
+ }
+ pgs->effective_clip_id = pgs->clip_path->id;
+ pgs->effective_view_clip_id = view_clip_id;
+ *ppcpath = pgs->effective_clip_path;
+ return 0;
+}
+
+#ifdef DEBUG
+/* Note that we just set the clipping path (internal). */
+private void
+note_set_clip_path(const gs_state * pgs)
+{
+ if (gs_debug_c('P')) {
+ extern void gx_cpath_print(P1(const gx_clip_path *));
+
+ dlprintf("[P]Clipping path:\n");
+ gx_cpath_print(pgs->clip_path);
+ }
+}
+#else
+# define note_set_clip_path(pgs) DO_NOTHING
+#endif
+
+int
+gs_clippath(gs_state * pgs)
+{
+ gx_path cpath;
+ int code;
+
+ gx_path_init_local(&cpath, pgs->memory);
+ code = gx_cpath_to_path(pgs->clip_path, &cpath);
+ if (code >= 0)
+ code = gx_path_assign_free(pgs->path, &cpath);
+ if (code < 0)
+ gx_path_free(&cpath, "gs_clippath");
+ return code;
+}
+
+int
+gs_initclip(gs_state * pgs)
+{
+ gs_fixed_rect box;
+ int code = gx_default_clip_box(pgs, &box);
+
+ if (code < 0)
+ return code;
+ 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)
+{
+ int code = gx_cpath_clip(pgs, pgs->clip_path, pgs->path, rule);
+ if (code < 0)
+ return code;
+ pgs->clip_path->rule = rule;
+ note_set_clip_path(pgs);
+ return 0;
+}
+
+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)
+{
+ int code = gx_cpath_from_rectangle(pgs->clip_path, pbox);
+
+ if (code < 0)
+ return code;
+ pgs->clip_path->rule = gx_rule_winding_number;
+ note_set_clip_path(pgs);
+ return 0;
+}
+
+/* Set the clipping path to the current path, without intersecting. */
+/* This is very inefficient right now. */
+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 ||
+ (code = gs_clip(pgs)) < 0
+ )
+ return code;
+ note_set_clip_path(pgs);
+ return 0;
+}
+
+/* Get the default clipping box. */
+int
+gx_default_clip_box(const gs_state * pgs, gs_fixed_rect * pbox)
+{
+ register gx_device *dev = gs_currentdevice(pgs);
+ gs_rect bbox;
+ gs_matrix imat;
+ int code;
+
+ 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];
+ }
+ code = gs_bbox_transform(&bbox, &imat, &bbox);
+ if (code < 0)
+ return code;
+ /* Round the clipping box so that it doesn't get ceilinged. */
+ pbox->p.x = fixed_rounded(float2fixed(bbox.p.x));
+ pbox->p.y = fixed_rounded(float2fixed(bbox.p.y));
+ pbox->q.x = fixed_rounded(float2fixed(bbox.q.x));
+ pbox->q.y = fixed_rounded(float2fixed(bbox.q.y));
+ return 0;
+}
diff --git a/pstoraster/gspath.h b/pstoraster/gspath.h
new file mode 100644
index 000000000..d736470c8
--- /dev/null
+++ b/pstoraster/gspath.h
@@ -0,0 +1,98 @@
+/* Copyright (C) 1989, 1995, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Requires gsstate.h */
+
+#ifndef gspath_INCLUDED
+# define gspath_INCLUDED
+
+#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 *));
+
+/* Imager-level procedures */
+#ifndef gs_imager_state_DEFINED
+# define gs_imager_state_DEFINED
+typedef struct gs_imager_state_s gs_imager_state;
+
+#endif
+#ifndef gx_path_DEFINED
+# define gx_path_DEFINED
+typedef struct gx_path_s gx_path;
+
+#endif
+int gs_imager_arc_add(P9(gx_path * ppath, gs_imager_state * pis,
+ bool clockwise, floatp axc, floatp ayc,
+ floatp arad, floatp aang1, floatp aang2,
+ bool add_line));
+
+#define gs_arc_add_inline(pgs, cw, axc, ayc, arad, aa1, aa2, add)\
+ gs_imager_arc_add((pgs)->path, (gs_imager_state *)(pgs),\
+ cw, axc, ayc, arad, aa1, aa2, add)
+
+/* 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 conditionally makes a copy of the path. */
+gs_path_enum *
+ gs_path_enum_alloc(P2(gs_memory_t *, client_name_t));
+int gs_path_enum_copy_init(P3(gs_path_enum *, const gs_state *, bool));
+
+#define gs_path_enum_init(penum, pgs)\
+ gs_path_enum_copy_init(penum, pgs, true)
+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 *));
+
+#endif /* gspath_INCLUDED */
diff --git a/pstoraster/gspath1.c b/pstoraster/gspath1.c
new file mode 100644
index 000000000..893a4c1f9
--- /dev/null
+++ b/pstoraster/gspath1.c
@@ -0,0 +1,480 @@
+/* Copyright (C) 1989, 1995, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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)
+
+typedef enum {
+ arc_nothing,
+ arc_moveto,
+ arc_lineto
+} arc_action;
+
+typedef struct arc_curve_params_s {
+ /* The following are set once. */
+ gx_path *ppath;
+ gs_imager_state *pis;
+ gs_point center; /* (not used by arc_add) */
+ double radius;
+ /* The following may be updated dynamically. */
+ arc_action action;
+ segment_notes notes;
+ gs_point p0, p3, pt;
+ gs_sincos_t sincos; /* (not used by arc_add) */
+ fixed angle; /* (not used by arc_add) */
+} arc_curve_params_t;
+
+/* Forward declarations */
+private int arc_add(P1(const arc_curve_params_t *));
+
+int
+gs_arc(gs_state * pgs,
+ floatp xc, floatp yc, floatp r, floatp ang1, floatp ang2)
+{
+ return gs_arc_add_inline(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_inline(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)
+{
+ return gs_arc_add_inline(pgs, clockwise, axc, ayc, arad,
+ aang1, aang2, add_line);
+}
+
+/* Compute the next curve as part of an arc. */
+private int
+next_arc_curve(arc_curve_params_t * arc, fixed anext)
+{
+ bool ortho = arc->sincos.orthogonal;
+ double sin0 = arc->sincos.sin, cos0 = arc->sincos.cos;
+ double x0 = arc->p0.x = arc->p3.x;
+ double y0 = arc->p0.y = arc->p3.y;
+ double x3, y3;
+
+ gs_sincos_degrees(fixed2float(anext), &arc->sincos);
+ arc->p3.x = x3 =
+ arc->center.x + arc->radius * arc->sincos.cos;
+ arc->p3.y = y3 =
+ arc->center.y + arc->radius * arc->sincos.sin;
+ if (ortho && arc->sincos.orthogonal) {
+ /* The common tangent point is easy to compute. */
+ if (x0 == arc->center.x)
+ arc->pt.x = x3, arc->pt.y = y0;
+ else
+ arc->pt.x = x0, arc->pt.y = y3;
+ } else {
+ /* Do it the hard way. */
+ double trad = arc->radius *
+ tan(fixed2float(anext - arc->angle) *
+ (degrees_to_radians / 2));
+
+ arc->pt.x = x0 - trad * sin0;
+ arc->pt.y = y0 + trad * cos0;
+ }
+ arc->angle = anext;
+ return arc_add(arc);
+}
+
+int
+gs_imager_arc_add(gx_path * ppath, gs_imager_state * pis, bool clockwise,
+ floatp axc, floatp ayc, floatp arad, floatp aang1, floatp aang2,
+ bool add_line)
+{
+ double ar = arad;
+ fixed ang1 = float2fixed(aang1), ang2 = float2fixed(aang2), anext;
+ double ang1r; /* reduced angle */
+ arc_curve_params_t arc;
+ int code;
+
+ arc.ppath = ppath;
+ arc.pis = pis;
+ arc.center.x = axc;
+ arc.center.y = ayc;
+#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;
+ }
+ arc.radius = ar;
+ arc.action = (add_line ? arc_lineto : arc_moveto);
+ arc.notes = sn_none;
+ ang1r = fixed2float(ang1 % fixed_360);
+ gs_sincos_degrees(ang1r, &arc.sincos);
+ arc.p3.x = axc + ar * arc.sincos.cos;
+ arc.p3.y = ayc + ar * arc.sincos.sin;
+ if (clockwise) {
+ while (ang1 < ang2)
+ ang2 -= fixed_360;
+ if (ang2 < 0) {
+ fixed adjust = round_up(-ang2, fixed_360);
+
+ ang1 += adjust, ang2 += adjust;
+ }
+ arc.angle = ang1;
+ /*
+ * Cut at multiples of 90 degrees. Invariant: ang1 >= ang2 >= 0.
+ */
+ while ((anext = round_down(arc.angle - fixed_epsilon, fixed_90)) > ang2) {
+ code = next_arc_curve(&arc, anext);
+ if (code < 0)
+ return code;
+ arc.action = arc_nothing;
+ arc.notes = sn_not_first;
+ }
+ } else {
+ while (ang2 < ang1)
+ ang2 += fixed_360;
+ if (ang1 < 0) {
+ fixed adjust = round_up(-ang1, fixed_360);
+
+ ang1 += adjust, ang2 += adjust;
+ }
+ arc.angle = ang1;
+ /*
+ * Cut at multiples of 90 degrees. Invariant: 0 <= ang1 <= ang2.
+ * We can't use round_up because of the inchoate definition of
+ * % and / for negative numbers.
+ */
+ while ((anext = round_up(arc.angle + fixed_epsilon, fixed_90)) < ang2) {
+ code = next_arc_curve(&arc, anext);
+ if (code < 0)
+ return code;
+ arc.action = arc_nothing;
+ arc.notes = sn_not_first;
+ }
+ }
+ /*
+ * Do the last curve of the arc.
+ */
+ return next_arc_curve(&arc, ang2);
+}
+
+int
+gs_arcto(gs_state * pgs,
+floatp ax1, floatp ay1, floatp ax2, floatp ay2, floatp arad, float retxy[4])
+{
+ double xt0, yt0, xt2, yt2;
+ gs_point up0;
+
+#define ax0 up0.x
+#define ay0 up0.y
+ /* Transform the current point back into user coordinates. */
+ int code = gs_currentpoint(pgs, &up0);
+
+ if (code < 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. */
+ double dx0 = ax0 - ax1, dy0 = ay0 - ay1;
+ double 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 messy part. */
+ double num = dy0 * dx2 - dy2 * dx0;
+ double denom = sqrt(sql0 * sql2) - (dx0 * dx2 + dy0 * dy2);
+
+ /* Check for collinear points. */
+ if (denom == 0) {
+ code = gs_lineto(pgs, ax1, ay1);
+ xt0 = xt2 = ax1;
+ yt0 = yt2 = ay1;
+ } else { /* not collinear */
+ double dist = fabs(arad * num / denom);
+ double l0 = dist / sqrt(sql0), l2 = dist / sqrt(sql2);
+ arc_curve_params_t arc;
+
+ arc.ppath = pgs->path;
+ arc.pis = (gs_imager_state *) pgs;
+ arc.radius = arad;
+ arc.action = arc_lineto;
+ arc.notes = sn_none;
+ if (arad < 0)
+ l0 = -l0, l2 = -l2;
+ arc.p0.x = xt0 = ax1 + dx0 * l0;
+ arc.p0.y = yt0 = ay1 + dy0 * l0;
+ arc.p3.x = xt2 = ax1 + dx2 * l2;
+ arc.p3.y = yt2 = ay1 + dy2 * l2;
+ arc.pt.x = ax1;
+ arc.pt.y = ay1;
+ code = arc_add(&arc);
+ }
+ }
+ 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(const arc_curve_params_t * arc)
+{
+ gx_path *path = arc->ppath;
+ gs_imager_state *pis = arc->pis;
+ double r = arc->radius;
+ double x0 = arc->p0.x, y0 = arc->p0.y;
+ double x3 = arc->p3.x, y3 = arc->p3.y;
+ double xt = arc->pt.x, yt = arc->pt.y;
+ 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)arc->action);
+ if ((code = gs_point_transform2fixed(&pis->ctm, x0, y0, &p0)) < 0 ||
+ (code = gs_point_transform2fixed(&pis->ctm, x3, y3, &p3)) < 0 ||
+ (code = gs_point_transform2fixed(&pis->ctm, xt, yt, &pt)) < 0 ||
+ (code =
+ (arc->action == arc_nothing ? 0 :
+ arc->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_notes(path, p3.x, p3.y, pt.x, pt.y,
+ fraction, arc->notes);
+}
+
+/* ------ Path transformers ------ */
+
+int
+gs_dashpath(gs_state * pgs)
+{
+ gx_path fpath;
+ int code;
+
+ if (gs_currentdash_length(pgs) == 0)
+ return 0; /* no dash pattern */
+ code = gs_flattenpath(pgs);
+ if (code < 0)
+ return code;
+ gx_path_init_local(&fpath, pgs->memory);
+ code = gx_path_add_dash_expansion(pgs->path, &fpath,
+ (gs_imager_state *) pgs);
+ if (code < 0) {
+ gx_path_free(&fpath, "gs_dashpath");
+ return code;
+ }
+ gx_path_assign_free(pgs->path, &fpath);
+ return 0;
+}
+
+int
+gs_flattenpath(gs_state * pgs)
+{
+ gx_path *ppath = pgs->path;
+ gx_path fpath;
+ int code;
+
+ if (!gx_path_has_curves(ppath))
+ return 0; /* nothing to do */
+ gx_path_init_local(&fpath, ppath->memory);
+ code = gx_path_add_flattened_accurate(ppath, &fpath, pgs->flatness,
+ pgs->accurate_curves);
+ if (code < 0) {
+ gx_path_free(&fpath, "gs_flattenpath");
+ return code;
+ }
+ gx_path_assign_free(ppath, &fpath);
+ return 0;
+}
+
+int
+gs_reversepath(gs_state * pgs)
+{
+ gx_path *ppath = pgs->path;
+ gx_path rpath;
+ int code;
+
+ gx_path_init_local(&rpath, ppath->memory);
+ code = gx_path_copy_reversed(ppath, &rpath);
+ if (code < 0) {
+ gx_path_free(&rpath, "gs_reversepath");
+ return code;
+ }
+ gx_path_assign_free(ppath, &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 (path_last_is_moveto(pgs->path) && 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_copy_init(gs_path_enum * penum, const gs_state * pgs, bool copy)
+{
+ gs_memory_t *mem = pgs->memory;
+
+ if (copy) {
+ gx_path *copied_path =
+ gx_path_alloc(mem, "gs_path_enum_init");
+ int code;
+
+ if (copied_path == 0)
+ return_error(gs_error_VMerror);
+ code = gx_path_copy(pgs->path, copied_path);
+ if (code < 0) {
+ gx_path_free(copied_path, "gs_path_enum_init");
+ return code;
+ }
+ gx_path_enum_init(penum, copied_path);
+ penum->copied_path = copied_path;
+ } else {
+ gx_path_enum_init(penum, pgs->path);
+ }
+ penum->memory = mem;
+ gs_currentmatrix(pgs, &penum->mat);
+ 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];
+ 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_point_transform_inverse(
+ fixed2float(fpts[1].x),
+ fixed2float(fpts[1].y),
+ &penum->mat, &ppts[1])) < 0 ||
+ (code = gs_point_transform_inverse(
+ fixed2float(fpts[2].x),
+ fixed2float(fpts[2].y),
+ &penum->mat, &ppts[2])) < 0)
+ return code;
+ /* falls through */
+ case gs_pe_moveto:
+ case gs_pe_lineto:
+ if ((code = gs_point_transform_inverse(
+ fixed2float(fpts[0].x),
+ fixed2float(fpts[0].y),
+ &penum->mat, &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) {
+ gx_path_free(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..c24074c86
--- /dev/null
+++ b/pstoraster/gspath2.h
@@ -0,0 +1,40 @@
+/* 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 supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Requires gsmatrix.h */
+
+#ifndef gspath2_INCLUDED
+# define gspath2_INCLUDED
+
+/* 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 *));
+
+#endif /* gspath2_INCLUDED */
diff --git a/pstoraster/gspcolor.c b/pstoraster/gspcolor.c
new file mode 100644
index 000000000..f5740b4ce
--- /dev/null
+++ b/pstoraster/gspcolor.c
@@ -0,0 +1,958 @@
+/* Copyright (C) 1993, 1995, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Pattern color operators and procedures for Ghostscript library */
+#include "math_.h"
+#include "gx.h"
+#include "gserrors.h"
+#include "gsrop.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 "gxp1fill.h"
+#include "gxpcolor.h"
+#include "gzstate.h"
+#include "gsimage.h"
+#include "gsiparm4.h"
+
+/* GC descriptors */
+private_st_pattern1_template();
+public_st_pattern_instance();
+
+/* Import the Pattern reloading procedure from gxpcmap.c. */
+int gx_pattern_load(P4(gx_device_color *, const gs_imager_state *,
+ gx_device *, gs_color_select_t));
+
+/* Define the Pattern color space. */
+gs_private_st_composite(st_color_space_Pattern, gs_paint_color_space,
+ "gs_color_space_Pattern", cs_Pattern_enum_ptrs, cs_Pattern_reloc_ptrs);
+private cs_proc_num_components(gx_num_components_Pattern);
+private cs_proc_base_space(gx_base_space_Pattern);
+extern cs_proc_remap_color(gx_remap_Pattern);
+private cs_proc_init_color(gx_init_Pattern);
+private cs_proc_restrict_color(gx_restrict_Pattern);
+private cs_proc_install_cspace(gx_install_Pattern);
+private cs_proc_adjust_cspace_count(gx_adjust_cspace_Pattern);
+private cs_proc_adjust_color_count(gx_adjust_color_Pattern);
+const gs_color_space_type gs_color_space_type_Pattern = {
+ gs_color_space_index_Pattern, false, false,
+ &st_color_space_Pattern, gx_num_components_Pattern,
+ gx_base_space_Pattern,
+ gx_init_Pattern, gx_restrict_Pattern,
+ gx_no_concrete_space,
+ gx_no_concretize_color, NULL,
+ gx_remap_Pattern, gx_install_Pattern,
+ gx_adjust_cspace_Pattern, gx_adjust_color_Pattern
+};
+
+/*
+ * Build a PatternType 1 Pattern color space.
+ */
+int
+gs_cspace_build_Pattern1(gs_color_space ** ppcspace,
+ const gs_color_space * pbase_cspace, gs_memory_t * pmem)
+{
+ gs_color_space *pcspace = 0;
+ int code;
+
+ if (pbase_cspace != 0) {
+ if (gs_color_space_num_components(pcspace) < 0) /* Pattern space */
+ return_error(gs_error_rangecheck);
+ }
+ code = gs_cspace_alloc(&pcspace, &gs_color_space_type_Pattern, pmem);
+ if (code < 0)
+ return code;
+ if (pbase_cspace != 0) {
+ pcspace->params.pattern.has_base_space = true;
+ gs_cspace_init_from((gs_color_space *) & (pcspace->params.pattern.base_space),
+ pbase_cspace
+ );
+ } else
+ pcspace->params.pattern.has_base_space = false;
+ *ppcspace = pcspace;
+ return 0;
+}
+
+/* Initialize a PatternType 1 pattern template. */
+void
+gs_pattern1_init(gs_pattern1_template_t * ppat)
+{
+ uid_set_invalid(&ppat->uid);
+ ppat->PaintType = 0; /* mark as PatternType 1 but invalid */
+ ppat->client_data = 0; /* for GC */
+}
+
+/* makepattern */
+private int compute_inst_matrix(P3(gs_pattern_instance * pinst,
+ const gs_state * saved,
+ gs_rect * pbbox));
+private rc_free_proc(rc_free_pattern_instance);
+int
+gs_makepattern(gs_client_color * pcc, const gs_client_pattern * pcp,
+ const gs_matrix * pmat, gs_state * pgs, gs_memory_t * mem)
+{
+ gs_pattern_instance inst;
+ gs_pattern_instance *pinst;
+ gs_state *saved;
+ gs_rect bbox;
+ gs_fixed_rect cbox;
+ int code;
+
+ if (mem == 0)
+ mem = gs_state_memory(pgs);
+ rc_alloc_struct_1(pinst, gs_pattern_instance, &st_pattern_instance,
+ mem, return_error(gs_error_VMerror),
+ "gs_makepattern");
+ pinst->rc.free = rc_free_pattern_instance;
+ inst.rc = pinst->rc;
+ saved = gs_state_copy(pgs, mem);
+ if (saved == 0) {
+ code = gs_note_error(gs_error_VMerror);
+ goto finst;
+ }
+ gs_concat(saved, pmat);
+ gs_newpath(saved);
+ switch (pcp->PaintType) {
+ case 1: /* colored */
+ gs_set_logical_op(saved, lop_default);
+ break;
+ case 2: /* uncolored */
+ gx_set_device_color_1(saved);
+ break;
+ default:
+ code = gs_note_error(gs_error_rangecheck);
+ goto fsaved;
+ }
+ inst.template = *pcp;
+ inst.saved = saved;
+ code = compute_inst_matrix(&inst, saved, &bbox);
+ if (code < 0)
+ goto fsaved;
+#define mat inst.step_matrix
+ if_debug6('t', "[t]step_matrix=[%g %g %g %g %g %g]\n",
+ mat.xx, mat.xy, mat.yx, mat.yy, mat.tx, mat.ty);
+ /* Check for singular stepping matrix. */
+ if (fabs(mat.xx * mat.yy - mat.xy * mat.yx) < 1.0e-6) {
+ code = gs_note_error(gs_error_rangecheck);
+ goto fsaved;
+ }
+ if_debug4('t', "[t]bbox=(%g,%g),(%g,%g)\n",
+ bbox.p.x, bbox.p.y, bbox.q.x, 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)
+ goto fsaved;
+ if_debug2('t',
+ "[t]adjusted XStep & YStep to size=(%d,%d)\n",
+ inst.size.x, inst.size.y);
+ if_debug4('t', "[t]bbox=(%g,%g),(%g,%g)\n",
+ bbox.p.x, bbox.p.y, bbox.q.x, bbox.q.y);
+ }
+ }
+ if ((code = gs_bbox_transform_inverse(&bbox, &mat, &inst.bbox)) < 0)
+ goto fsaved;
+ if_debug4('t', "[t]ibbox=(%g,%g),(%g,%g)\n",
+ inst.bbox.p.x, inst.bbox.p.y, inst.bbox.q.x, inst.bbox.q.y);
+ inst.is_simple = (fabs(mat.xx) == inst.size.x && mat.xy == 0 &&
+ mat.yx == 0 && fabs(mat.yy) == inst.size.y);
+ if_debug6('t',
+ "[t]is_simple? xstep=(%g,%g) ystep=(%g,%g) size=(%d,%d)\n",
+ inst.step_matrix.xx, inst.step_matrix.xy,
+ inst.step_matrix.yx, inst.step_matrix.yy,
+ inst.size.x, inst.size.y);
+ /* Absent other information, instances always require a mask. */
+ inst.uses_mask = true;
+ gx_translate_to_fixed(saved, float2fixed(mat.tx - bbox.p.x),
+ float2fixed(mat.ty - bbox.p.y));
+ mat.tx = bbox.p.x;
+ mat.ty = bbox.p.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)
+ goto fsaved;
+ inst.id = gs_next_ids(1);
+ *pinst = inst;
+ pcc->pattern = pinst;
+ return 0;
+#undef mat
+ fsaved:gs_state_free(saved);
+ finst:gs_free_object(mem, pinst, "gs_makepattern");
+ return code;
+}
+/* Compute the stepping matrix and device space instance 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)
+{
+ double xx = pinst->template.XStep * saved->ctm.xx;
+ double xy = pinst->template.XStep * saved->ctm.xy;
+ double yx = pinst->template.YStep * saved->ctm.yx;
+ double yy = pinst->template.YStep * saved->ctm.yy;
+
+ /* Adjust the stepping matrix so all coefficients are >= 0. */
+ if (xx == 0 || yy == 0) { /* We know that both xy and yx are non-zero. */
+ double temp;
+
+ temp = xx, xx = yx, yx = temp;
+ temp = xy, xy = yy, yy = temp;
+ }
+ if (xx < 0)
+ xx = -xx, xy = -xy;
+ if (yy < 0)
+ yx = -yx, yy = -yy;
+ /* Now xx > 0, yy > 0. */
+ pinst->step_matrix.xx = xx;
+ pinst->step_matrix.xy = xy;
+ pinst->step_matrix.yx = yx;
+ pinst->step_matrix.yy = yy;
+ pinst->step_matrix.tx = saved->ctm.tx;
+ pinst->step_matrix.ty = saved->ctm.ty;
+ return gs_bbox_transform(&pinst->template.BBox, &ctm_only(saved),
+ pbbox);
+}
+/* Free the saved gstate when freeing a Pattern instance. */
+private void
+rc_free_pattern_instance(gs_memory_t * mem, void *pinst_void,
+ client_name_t cname)
+{
+ gs_pattern_instance *pinst = pinst_void;
+
+ gs_state_free(pinst->saved);
+ rc_free_struct_only(mem, pinst_void, cname);
+}
+
+/* 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;
+}
+
+/*
+ * Adjust the reference count of a pattern. This is intended to support
+ * applications (such as PCL) which maintain client colors outside of the
+ * graphic state. Since the pattern instance structure is opaque to these
+ * applications, they need some way to release or retain the instances as
+ * needed.
+ */
+void
+gs_pattern_reference(gs_client_color * pcc, int delta)
+{
+ if (pcc->pattern != 0)
+ rc_adjust(pcc->pattern, delta, "gs_pattern_reference");
+}
+
+/* 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;
+}
+
+/*
+ * Code for generating patterns from bitmaps and pixmaps.
+ */
+
+/*
+ * The following structures are realized here only because this is the
+ * first location in which they were needed. Otherwise, there is nothing
+ * about them that is specific to patterns.
+ */
+public_st_gs_bitmap();
+public_st_gs_tile_bitmap();
+public_st_gs_depth_bitmap();
+public_st_gs_tile_depth_bitmap();
+public_st_gx_strip_bitmap();
+
+/*
+ * Structure for holding a gs_depth_bitmap and the corresponding depth and
+ * colorspace information.
+ *
+ * The free_proc pointer is needed to hold the original value of the pattern
+ * instance free structure. This pointer in the pattern instance will be
+ * overwritten with free_pixmap_pattern, which will free the pixmap info
+ * structure when it is freed.
+ */
+typedef struct pixmap_info_s {
+ gs_depth_bitmap bitmap; /* must be first */
+ const gs_color_space *pcspace;
+ uint white_index;
+ void (*free_proc)( gs_memory_t *, void *, client_name_t );
+} pixmap_info;
+
+gs_private_st_suffix_add1(st_pixmap_info,
+ pixmap_info,
+ "pixmap info. struct",
+ pixmap_enum_ptr,
+ pixmap_reloc_ptr,
+ st_gs_depth_bitmap,
+ pcspace
+);
+
+#define st_pixmap_info_max_ptrs (1 + st_tile_bitmap_max_ptrs)
+
+/*
+ * Free routine for pattern instances created from pixmaps. This overwrites
+ * the free procedure originally stored in the pattern instance, and stores
+ * the pointer to that procedure in the pixmap_info structure. This procedure
+ * will call the original procedure, then free the pixmap_info structure.
+ *
+ * Note that this routine does NOT release the data in the original pixmap;
+ * that remains the responsibility of the client.
+ */
+ void
+free_pixmap_pattern(
+ gs_memory_t * pmem,
+ void * pvpinst,
+ client_name_t cname
+)
+{
+ gs_pattern_instance * pinst = (gs_pattern_instance *)pvpinst;
+ const pixmap_info * ppmap = pinst->template.client_data;
+
+ ppmap->free_proc(pmem, pvpinst, cname);
+ gs_free_object(pmem, (void *)ppmap, cname);
+}
+
+/*
+ * PaintProcs for bitmap and pixmap patterns.
+ */
+private int bitmap_paint(P4(gs_image_enum * pen, gs_data_image_t * pim,
+ const gs_depth_bitmap * pbitmap, gs_state * pgs));
+private int
+mask_PaintProc(const gs_client_color * pcolor, gs_state * pgs)
+{
+ const pixmap_info *ppmap = gs_getpattern(pcolor)->client_data;
+ const gs_depth_bitmap *pbitmap = &(ppmap->bitmap);
+ gs_image_enum *pen =
+ gs_image_enum_alloc(gs_state_memory(pgs), "mask_PaintProc");
+ gs_image1_t mask;
+
+ if (pen == 0)
+ return_error(gs_error_VMerror);
+ gs_image_t_init_mask(&mask, true);
+ mask.Width = pbitmap->size.x;
+ mask.Height = pbitmap->size.y;
+ gs_image_init(pen, &mask, false, pgs);
+ return bitmap_paint(pen, (gs_data_image_t *) & mask, pbitmap, pgs);
+}
+private int
+image_PaintProc(const gs_client_color * pcolor, gs_state * pgs)
+{
+ const pixmap_info *ppmap = gs_getpattern(pcolor)->client_data;
+ const gs_depth_bitmap *pbitmap = &(ppmap->bitmap);
+ gs_image_enum *pen =
+ gs_image_enum_alloc(gs_state_memory(pgs), "image_PaintProc");
+ const gs_color_space *pcspace =
+ (ppmap->pcspace == 0 ?
+ gs_cspace_DeviceGray((const gs_imager_state *)pgs) :
+ ppmap->pcspace);
+ gx_image_enum_common_t *pie;
+ gs_image4_t image;
+ int code;
+
+ if (pen == 0)
+ return_error(gs_error_VMerror);
+ gs_image4_t_init(&image, pcspace);
+ image.Width = pbitmap->size.x;
+ image.Height = pbitmap->size.y;
+ image.MaskColor_is_range = false;
+ image.MaskColor[0] = ppmap->white_index;
+ image.Decode[0] = 0;
+ image.Decode[1] = (1 << pbitmap->pix_depth) - 1;
+ image.BitsPerComponent = pbitmap->pix_depth;
+ /* backwards compatibility */
+ if (ppmap->pcspace == 0) {
+ image.Decode[0] = 1.0;
+ image.Decode[1] = 0.0;
+ }
+ code = gs_image_begin_typed((const gs_image_common_t *)&image, pgs,
+ false, &pie);
+ if (code < 0)
+ return code;
+ code = gs_image_common_init(pen, pie, (gs_data_image_t *) & image,
+ gs_state_memory(pgs),
+ (pgs->in_charpath ? NULL :
+ gs_currentdevice_inline(pgs)));
+ if (code < 0)
+ return code;
+ return bitmap_paint(pen, (gs_data_image_t *) & image, pbitmap, pgs);
+}
+/* Finish painting any kind of bitmap pattern. */
+private int
+bitmap_paint(gs_image_enum * pen, gs_data_image_t * pim,
+ const gs_depth_bitmap * pbitmap, gs_state * pgs)
+{
+ uint raster = pbitmap->raster;
+ uint nbytes = (pim->Width * pbitmap->pix_depth + 7) >> 3;
+ uint used;
+ const byte *dp = pbitmap->data;
+ int n;
+ int code = 0;
+
+ if (nbytes == raster)
+ code = gs_image_next(pen, dp, nbytes * pim->Height, &used);
+ else
+ for (n = pim->Height; n > 0 && code >= 0; dp += raster, --n)
+ code = gs_image_next(pen, dp, nbytes, &used);
+ gs_image_cleanup(pen);
+ gs_free_object(gs_state_memory(pgs), pen, "bitmap_paint");
+ return code;
+}
+
+/*
+ * Make a pattern from a bitmap or pixmap. The pattern may be colored or
+ * uncolored, as determined by the mask operand. This code is intended
+ * primarily for use by PCL.
+ *
+ * See the comment prior to the declaration of this function in gscolor2.h
+ * for further information.
+ */
+int
+gs_makepixmappattern(
+ gs_client_color * pcc,
+ const gs_depth_bitmap * pbitmap,
+ bool mask,
+ const gs_matrix * pmat,
+ long id,
+ const gs_color_space * pcspace,
+ uint white_index,
+ gs_state * pgs,
+ gs_memory_t * mem
+)
+{
+
+ gs_client_pattern pat;
+ pixmap_info *ppmap;
+ gs_matrix mat, smat;
+ int code;
+
+ /* check that the data is legitimate */
+ if ((mask) || (pcspace == 0)) {
+ if (pbitmap->pix_depth != 1)
+ return_error(gs_error_rangecheck);
+ pcspace = 0;
+ } else if (gs_color_space_get_index(pcspace) != gs_color_space_index_Indexed)
+ return_error(gs_error_rangecheck);
+ if (pbitmap->num_comps != 1)
+ return_error(gs_error_rangecheck);
+
+ /* allocate and initialize a pixmap_info structure for the paint proc */
+ if (mem == 0)
+ mem = gs_state_memory(pgs);
+ ppmap = gs_alloc_struct(mem,
+ pixmap_info,
+ &st_pixmap_info,
+ "makepximappattern"
+ );
+ if (ppmap == 0)
+ return_error(gs_error_VMerror);
+ ppmap->bitmap = *pbitmap;
+ ppmap->pcspace = pcspace;
+ ppmap->white_index = white_index;
+
+ /* set up the client pattern structure */
+ uid_set_UniqueID(&pat.uid, (id == no_UniqueID) ? gs_next_ids(1) : id);
+ pat.PaintType = (mask ? 2 : 1);
+ pat.TilingType = 1;
+ pat.BBox.p.x = 0;
+ pat.BBox.p.y = 0;
+ pat.BBox.q.x = pbitmap->size.x;
+ pat.BBox.q.y = pbitmap->size.y;
+ pat.XStep = pbitmap->size.x;
+ pat.YStep = pbitmap->size.y;
+ pat.PaintProc = (mask ? mask_PaintProc : image_PaintProc);
+ pat.client_data = ppmap;
+
+ /* set the ctm to be the identity */
+ gs_currentmatrix(pgs, &smat);
+ gs_make_identity(&mat);
+ gs_setmatrix(pgs, &mat);
+
+ /* build the pattern, restore the previous matrix */
+ if (pmat == NULL)
+ pmat = &mat;
+ if ((code = gs_makepattern(pcc, &pat, pmat, pgs, mem)) != 0)
+ gs_free_object(mem, ppmap, "makebitmappattern_xform");
+ else {
+ /*
+ * If this is not a masked pattern and if the white pixel index
+ * is outside of the representable range, we don't need to go to
+ * the trouble of accumulating a mask that will just be all 1s.
+ */
+ if (!mask && (white_index >= (1 << pbitmap->pix_depth)))
+ pcc->pattern->uses_mask = false;
+
+ /* overwrite the free procedure for the pattern instance */
+ ppmap->free_proc = pcc->pattern->rc.free;
+ pcc->pattern->rc.free = free_pixmap_pattern;
+ }
+ gs_setmatrix(pgs, &smat);
+ return code;
+}
+
+/*
+ * Backwards compatibility.
+ */
+int
+gs_makebitmappattern_xform(
+ gs_client_color * pcc,
+ const gx_tile_bitmap * ptile,
+ bool mask,
+ const gs_matrix * pmat,
+ long id,
+ gs_state * pgs,
+ gs_memory_t * mem
+)
+{
+ gs_depth_bitmap bitmap;
+
+ /* build the bitmap the size of one repetition */
+ bitmap.data = ptile->data;
+ bitmap.raster = ptile->raster;
+ bitmap.size.x = ptile->rep_width;
+ bitmap.size.y = ptile->rep_height;
+ bitmap.id = ptile->id; /* shouldn't matter */
+ bitmap.pix_depth = 1;
+ bitmap.num_comps = 1;
+
+ return gs_makepixmappattern(pcc, &bitmap, mask, pmat, id, 0, 0, pgs, mem);
+}
+
+
+/* ------ Color space implementation ------ */
+
+/*
+ * Defined the 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);
+/*dev_color_proc_fill_rectangle(gx_dc_pattern_fill_rectangle); *//*gxp1fill.h */
+private dev_color_proc_equal(gx_dc_pattern_equal);
+private dev_color_proc_load(gx_dc_pure_masked_load);
+
+/*dev_color_proc_fill_rectangle(gx_dc_pure_masked_fill_rect); *//*gxp1fill.h */
+private dev_color_proc_equal(gx_dc_pure_masked_equal);
+private dev_color_proc_load(gx_dc_binary_masked_load);
+
+/*dev_color_proc_fill_rectangle(gx_dc_binary_masked_fill_rect); *//*gxp1fill.h */
+private dev_color_proc_equal(gx_dc_binary_masked_equal);
+private dev_color_proc_load(gx_dc_colored_masked_load);
+
+/*dev_color_proc_fill_rectangle(gx_dc_colored_masked_fill_rect); *//*gxp1fill.h */
+private dev_color_proc_equal(gx_dc_colored_masked_equal);
+
+/* The device color types are exported for gxpcmap.c. */
+gs_private_st_composite(st_dc_pattern, gx_device_color, "dc_pattern",
+ dc_pattern_enum_ptrs, dc_pattern_reloc_ptrs);
+const gx_device_color_type_t gx_dc_pattern = {
+ &st_dc_pattern,
+ gx_dc_pattern_load, gx_dc_pattern_fill_rectangle,
+ gx_dc_default_fill_masked, gx_dc_pattern_equal
+};
+
+extern_st(st_dc_ht_binary);
+gs_private_st_composite(st_dc_pure_masked, gx_device_color, "dc_pure_masked",
+ dc_masked_enum_ptrs, dc_masked_reloc_ptrs);
+const gx_device_color_type_t gx_dc_pure_masked = {
+ &st_dc_pure_masked,
+ gx_dc_pure_masked_load, gx_dc_pure_masked_fill_rect,
+ gx_dc_default_fill_masked, gx_dc_pure_masked_equal
+};
+
+gs_private_st_composite(st_dc_binary_masked, gx_device_color,
+ "dc_binary_masked", dc_binary_masked_enum_ptrs,
+ dc_binary_masked_reloc_ptrs);
+const gx_device_color_type_t gx_dc_binary_masked = {
+ &st_dc_binary_masked,
+ gx_dc_binary_masked_load, gx_dc_binary_masked_fill_rect,
+ gx_dc_default_fill_masked, gx_dc_binary_masked_equal
+};
+
+gs_private_st_composite_only(st_dc_colored_masked, gx_device_color,
+ "dc_colored_masked",
+ dc_masked_enum_ptrs, dc_masked_reloc_ptrs);
+const gx_device_color_type_t gx_dc_colored_masked = {
+ &st_dc_colored_masked,
+ gx_dc_colored_masked_load, gx_dc_colored_masked_fill_rect,
+ gx_dc_default_fill_masked, gx_dc_colored_masked_equal
+};
+
+#undef gx_dc_type_pattern
+const gx_device_color_type_t *const 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 ENUM_USING(st_dc_pure_masked, vptr, size, index - 1);
+}
+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);
+ }
+ RELOC_USING(st_dc_pure_masked, vptr, size);
+}
+RELOC_PTRS_END
+private ENUM_PTRS_BEGIN(dc_masked_enum_ptrs) ENUM_SUPER(gx_device_color, st_client_color, mask.ccolor, 1);
+case 0:
+{
+ gx_color_tile *mask = cptr->mask.m_tile;
+
+ 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.m_tile;
+
+ RELOC_SUPER(gx_device_color, st_client_color, mask.ccolor);
+ if (mask != 0) {
+ uint index = mask->index;
+
+ RELOC_TYPED_OFFSET_PTR(gx_device_color, mask.m_tile, index);
+ }
+}
+RELOC_PTRS_END
+private ENUM_PTRS_BEGIN(dc_binary_masked_enum_ptrs)
+{
+ return ENUM_USING(st_dc_ht_binary, vptr, size, index - 2);
+}
+case 0:
+case 1:
+return ENUM_USING(st_dc_pure_masked, vptr, size, index);
+ENUM_PTRS_END
+private RELOC_PTRS_BEGIN(dc_binary_masked_reloc_ptrs)
+{
+ RELOC_USING(st_dc_pure_masked, vptr, size);
+ RELOC_USING(st_dc_ht_binary, vptr, size);
+}
+RELOC_PTRS_END
+#undef cptr
+
+/* Macros for pattern loading */
+#define FINISH_PATTERN_LOAD\
+ while ( !gx_pattern_cache_lookup(pdevc, pis, dev, select) )\
+ { code = gx_pattern_load(pdevc, pis, dev, select);\
+ 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_imager_state * pis,
+ gx_device * dev, gs_color_select_t select)
+{
+ 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_imager_state * pis,
+ gx_device * dev, gs_color_select_t select)
+{
+ int code = (*gx_dc_type_data_pure.load) (pdevc, pis, dev, select);
+
+ if (code < 0)
+ return code;
+ FINISH_PATTERN_LOAD
+}
+private int
+gx_dc_binary_masked_load(gx_device_color * pdevc, const gs_imager_state * pis,
+ gx_device * dev, gs_color_select_t select)
+{
+ int code = (*gx_dc_type_data_ht_binary.load) (pdevc, pis, dev, select);
+
+ if (code < 0)
+ return code;
+ FINISH_PATTERN_LOAD
+}
+private int
+gx_dc_colored_masked_load(gx_device_color * pdevc, const gs_imager_state * pis,
+ gx_device * dev, gs_color_select_t select)
+{
+ int code = (*gx_dc_type_data_ht_colored.load) (pdevc, pis, dev, select);
+
+ 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_imager_state * pis,
+ gx_device * dev, gs_color_select_t select)
+{
+ gx_pattern_cache *pcache = pis->pattern_cache;
+ gx_bitmap_id id = pdevc->mask.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 == dev->color_info.depth)
+ ) {
+ int px = pis->screen_phase[select].x;
+ int py = pis->screen_phase[select].y;
+
+ if (pdevc->type == &gx_dc_pattern) { /* colored */
+ pdevc->colors.pattern.p_tile = ctile;
+ color_set_phase_mod(pdevc, px, py,
+ ctile->tbits.rep_width,
+ ctile->tbits.rep_height);
+ }
+ pdevc->mask.m_tile =
+ (ctile->tmask.data == 0 ? (gx_color_tile *) 0 :
+ ctile);
+ pdevc->mask.m_phase.x = -px;
+ pdevc->mask.m_phase.y = -py;
+ return true;
+ }
+ }
+ return false;
+}
+
+#undef FINISH_PATTERN_LOAD
+
+/* Compare two Pattern colors for equality. */
+private bool
+gx_dc_pattern_equal(const gx_device_color * pdevc1,
+ const gx_device_color * pdevc2)
+{
+ return pdevc2->type == pdevc1->type &&
+ pdevc1->phase.x == pdevc2->phase.x &&
+ pdevc1->phase.y == pdevc2->phase.y &&
+ pdevc1->mask.id == pdevc2->mask.id;
+}
+private bool
+gx_dc_pure_masked_equal(const gx_device_color * pdevc1,
+ const gx_device_color * pdevc2)
+{
+ return (*gx_dc_type_pure->equal) (pdevc1, pdevc2) &&
+ pdevc1->mask.id == pdevc2->mask.id;
+}
+private bool
+gx_dc_binary_masked_equal(const gx_device_color * pdevc1,
+ const gx_device_color * pdevc2)
+{
+ return (*gx_dc_type_ht_binary->equal) (pdevc1, pdevc2) &&
+ pdevc1->mask.id == pdevc2->mask.id;
+}
+private bool
+gx_dc_colored_masked_equal(const gx_device_color * pdevc1,
+ const gx_device_color * pdevc2)
+{
+ return (*gx_dc_type_ht_colored->equal) (pdevc1, pdevc2) &&
+ pdevc1->mask.id == pdevc2->mask.id;
+}
+
+/*
+ * Get the number of components in a Pattern color.
+ * For backward compatibility, and to distinguish Pattern color spaces
+ * from all others, we negate the result.
+ */
+private int
+gx_num_components_Pattern(const gs_color_space * pcs)
+{
+ return
+ (pcs->params.pattern.has_base_space ?
+ -1 - cs_num_components((const gs_color_space *)
+ &(pcs->params.pattern.base_space)) :
+ -1 /* Pattern dictionary only */ );
+}
+
+/* Get the base space of a Pattern color space. */
+private const gs_color_space *
+gx_base_space_Pattern(const gs_color_space * pcs)
+{
+ return
+ (pcs->params.pattern.has_base_space ?
+ (const gs_color_space *)&(pcs->params.pattern.base_space) :
+ NULL);
+}
+
+/* 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 */
+}
+
+/* Force a Pattern color into legal range. */
+/* Note that if the pattern is uncolored (PaintType = 2), */
+/* the color space must have a base space: we check this here only */
+/* to prevent accessing uninitialized data, but if there is no base space, */
+/* it is an error that we count on being detected elsewhere. */
+private void
+gx_restrict_Pattern(gs_client_color * pcc, const gs_color_space * pcs)
+{
+ if (pcc->pattern->template.PaintType == 2 &&
+ pcs->params.pattern.has_base_space
+ ) {
+ const gs_color_space *pbcs =
+ (const gs_color_space *)&pcs->params.pattern.base_space;
+
+ (*pbcs->type->restrict_color) (pcc, pbcs);
+ }
+}
+
+/* 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, 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, delta);
+}
+
+private void
+gx_adjust_color_Pattern(const gs_client_color * pcc,
+ const gs_color_space * pcs, int delta)
+{
+ gs_pattern_instance *pinst = pcc->pattern;
+
+ rc_adjust_only(pinst, delta, "gx_adjust_color_Pattern");
+ if (pcs && 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,
+ delta);
+}
+
+/* GC procedures */
+
+#define pcs ((gs_color_space *)vptr)
+
+private
+ENUM_PTRS_BEGIN_PROC(cs_Pattern_enum_ptrs)
+{
+ if (!pcs->params.pattern.has_base_space)
+ return 0;
+ return ENUM_USING(*pcs->params.pattern.base_space.type->stype,
+ &pcs->params.pattern.base_space,
+ sizeof(pcs->params.pattern.base_space), index);
+}
+ENUM_PTRS_END_PROC
+private RELOC_PTRS_BEGIN(cs_Pattern_reloc_ptrs)
+{
+ if (!pcs->params.pattern.has_base_space)
+ return;
+ RELOC_USING(*pcs->params.pattern.base_space.type->stype,
+ &pcs->params.pattern.base_space,
+ sizeof(gs_paint_color_space));
+}
+RELOC_PTRS_END
+
+#undef pcs
diff --git a/pstoraster/gspcolor.h b/pstoraster/gspcolor.h
new file mode 100644
index 000000000..fb6ad3f38
--- /dev/null
+++ b/pstoraster/gspcolor.h
@@ -0,0 +1,87 @@
+/* Copyright (C) 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Client interface to Pattern color */
+
+#ifndef gspcolor_INCLUDED
+# define gspcolor_INCLUDED
+
+#include "gsccolor.h"
+#include "gsuid.h"
+
+/* ---------------- Types and structures ---------------- */
+
+/*
+ * Unfortunately, we defined the gs_client_pattern structure before we
+ * realized that we would have to accommodate multiple PatternTypes.
+ * Consequently, we distinguish the different PatternTypes with a hack.
+ * We know that PatternType 1 patterns always have a positive PaintType.
+ * Therefore, we overlay the PaintType field of PatternType 1 patterns
+ * with the negative of the PatternType for generalized patterns.
+ * This allows us to distinguish PatternType 1 patterns from all others.
+ * This is a really bad hack, but doing anything else would require
+ * a non-backward-compatible change for clients, since we didn't
+ * require clients to use a procedure to initialize Patterns (another
+ * mistake, in retrospect, which we've now also fixed).
+ */
+
+/* General pattern template (called "prototype pattern" in Red Book) */
+typedef struct gs_pattern_type_s gs_pattern_type_t;
+
+#define gs_pattern_template_common\
+ gs_uid uid; /* must be first in case we ever subclass properly */\
+ int negPatternType; /* overlays PaintType, see above */\
+ const gs_pattern_type_t *type
+#define PatternType(ppt)\
+ ((ppt)->negPatternType < 0 ? -(ppt)->negPatternType : 1)
+typedef struct gs_pattern_template_s {
+ gs_pattern_template_common;
+} gs_pattern_template_t;
+
+/* ---------------- Procedures ---------------- */
+
+/* Set a Pattern color or a Pattern color space. */
+int gs_setpattern(P2(gs_state *, const gs_client_color *));
+int gs_setpatternspace(P1(gs_state *));
+
+/*
+ * The gs_memory_t argument for gs_make_pattern may be NULL, meaning use the
+ * same allocator as for the gs_state argument. Note that gs_make_pattern
+ * uses rc_alloc_struct_1 to allocate pattern instances.
+ */
+int gs_make_pattern(P5(gs_client_color *, const gs_pattern_template_t *,
+ const gs_matrix *, gs_state *, gs_memory_t *));
+const gs_pattern_template_t *gs_get_pattern(P1(const gs_client_color *));
+
+/*
+ * Adjust the reference count of a pattern. This is intended to support
+ * applications (such as PCL) which maintain client colors outside of the
+ * graphic state. Since the pattern instance structure is opaque to these
+ * applications, they need some way to release or retain the instances as
+ * needed.
+ */
+void gs_pattern_reference(P2(gs_client_color * pcc, int delta));
+
+#endif /* gspcolor_INCLUDED */
diff --git a/pstoraster/gspenum.h b/pstoraster/gspenum.h
new file mode 100644
index 000000000..935a25160
--- /dev/null
+++ b/pstoraster/gspenum.h
@@ -0,0 +1,40 @@
+/* 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 supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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/gspmdrv.h b/pstoraster/gspmdrv.h
new file mode 100644
index 000000000..3e050cebf
--- /dev/null
+++ b/pstoraster/gspmdrv.h
@@ -0,0 +1,40 @@
+/* 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 supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Definitions common to gspmdrv.c and gspmdrv.rc */
+
+#ifndef gspmdrv_INCLUDED
+# define gspmdrv_INCLUDED
+
+#define GSPMDRV_VERSION "1994-02-09"
+
+#define IDM_ABOUT 5
+#define IDM_COPY 6
+
+#define IDD_ABOUT IDM_ABOUT
+
+#define ID_GSPMDRV 1000
+
+#endif /* gspmdrv_INCLUDED */
diff --git a/pstoraster/gsptype1.h b/pstoraster/gsptype1.h
new file mode 100644
index 000000000..38e81b3ba
--- /dev/null
+++ b/pstoraster/gsptype1.h
@@ -0,0 +1,143 @@
+/* Copyright (C) 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Client interface to PatternType 1 Patterns */
+
+#ifndef gsptype1_INCLUDED
+# define gsptype1_INCLUDED
+
+#include "gspcolor.h"
+#include "gxbitmap.h"
+
+/* ---------------- Types and structures ---------------- */
+
+/* PatternType 1 template */
+typedef struct gs_pattern1_template_s {
+ gs_uid uid; /* must be first, see gspcolor.h */
+ int PaintType; /* must be second, ditto */
+ 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_pattern1_template_t;
+
+#define private_st_pattern1_template() /* in gspcolor.c */\
+ gs_private_st_ptrs2(st_pattern1_template, gs_pattern1_template_t,\
+ "PatternType 1 template", pattern_template_enum_ptrs,\
+ pattern1_template_reloc_ptrs, uid.xvalues, client_data)
+/* Backward compatibility */
+typedef gs_pattern1_template_t gs_client_pattern;
+
+/* ---------------- Procedures ---------------- */
+
+/*
+ * Construct a PatternType 1 Pattern color space. If the base space is
+ * NULL, the color space can only be used with colored patterns.
+ */
+extern int gs_cspace_build_Pattern1(
+ gs_color_space ** ppcspace,
+ const gs_color_space * pbase_cspace,
+ gs_memory_t * pmem
+);
+
+/* Initialize a PatternType 1 pattern. */
+void gs_pattern1_init(P1(gs_pattern1_template_t *));
+
+#define gs_client_pattern_init(ppat) gs_pattern1_init(ppat)
+
+/*
+ * Define versions of make_pattern and get_pattern specifically for
+ * PatternType 1 patterns.
+ *
+ * The gs_memory_t argument for gs_makepattern may be NULL, meaning use the
+ * same allocator as for the gs_state argument. Note that gs_makepattern
+ * uses rc_alloc_struct_1 to allocate pattern instances.
+ */
+int gs_makepattern(P5(gs_client_color *, const gs_client_pattern *,
+ const gs_matrix *, gs_state *, gs_memory_t *));
+const gs_client_pattern *gs_getpattern(P1(const gs_client_color *));
+
+/*
+ * Make a pattern from a bitmap or pixmap. The pattern may be colored or
+ * uncolored, as determined by the mask operand. This code is intended
+ * primarily for use by PCL.
+ *
+ * By convention, if pmat is null the identity matrix will be used, and if
+ * id is no_UniqueID the code will assign a unique id. Thes conventions allow
+ * gs_makebitmappattern to be implemented as a macro. Also, if mem is a
+ * null pointer, the memory allocator for the graphic state is used.
+ *
+ * For mask patterns, pix_depth must be 1, while pcspace and white_index are
+ * ignored; the polarity of the mask considers ones part of the mask, while
+ * zeros are not. For colored patterns pspace must point to an indexed color
+ * space and the image must used the canoncial Decode array for this color
+ * space. For both cases no interpolation or adjustment is provided.
+ *
+ * For backwards compatibility, if mask is false, pcspace is null, and
+ * pix_depth is 1, the pattern will be rendered with a color space that maps
+ * 0 to white and 1 to black.
+ *
+ * The image must be described by a gx_tile_bitmap structure (this is actually
+ * somewhat awkward, but the only option available at the moment), and the
+ * pattern step will exactly match the image size. The client need not maintain
+ * the gx_tile_bitmap structure after the completion of this call, but the
+ * raw image data itself must be kept until the pattern is no longer needed.
+ *
+ * NB: For proper handling of transparency in PCL, there must be only a single
+ * white value accessed by the pattern image. If the palette contains
+ * multiple white values, the PCL component must remap the image data to
+ * ensure that all white indices are mapped to the single, given white
+ * index.
+ */
+extern int gs_makepixmappattern(gs_client_color * pcc,
+ const gs_depth_bitmap * pbitmap,
+ bool mask,
+ const gs_matrix * pmat,
+ long id,
+ const gs_color_space * pcspace,
+ uint white_index,
+ gs_state * pgs,
+ gs_memory_t * mem
+);
+
+/*
+ * Backwards compatibility feature, to allow the existing
+ * gs_makebitmappattern operation to still function.
+ */
+extern int gs_makebitmappattern_xform(gs_client_color * pcc,
+ const gx_tile_bitmap * ptile,
+ bool mask,
+ const gs_matrix * pmat,
+ long id,
+ gs_state * pgs,
+ gs_memory_t * mem
+);
+
+#define gs_makebitmappattern(pcc, tile, mask, pgs, mem) \
+ gs_makebitmappattern_xform(pcc, tile, mask, 0, no_UniqueID, pgs, mem)
+
+#endif /* gsptype1_INCLUDED */
diff --git a/pstoraster/gsptype2.h b/pstoraster/gsptype2.h
new file mode 100644
index 000000000..4dc1d5929
--- /dev/null
+++ b/pstoraster/gsptype2.h
@@ -0,0 +1,52 @@
+/* Copyright (C) 1997 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Client interface to PatternType 2 Patterns */
+
+#ifndef gsptype2_INCLUDED
+# define gsptype2_INCLUDED
+
+#include "gspcolor.h"
+
+/* ---------------- Types and structures ---------------- */
+
+#ifndef gs_shading_t_DEFINED
+# define gs_shading_t_DEFINED
+typedef struct gs_shading_s gs_shading_t;
+
+#endif
+
+/* PatternType 2 template */
+typedef struct gs_pattern2_template_s {
+ gs_pattern_template_common;
+ const gs_shading_t *Shading;
+} gs_pattern2_template_t;
+
+/* ---------------- Procedures ---------------- */
+
+/* Initialize a PatternType 2 pattern. */
+void gs_pattern2_init(P1(gs_pattern2_template_t *));
+
+#endif /* gsptype2_INCLUDED */
diff --git a/pstoraster/gsrect.h b/pstoraster/gsrect.h
new file mode 100644
index 000000000..6fad11ad5
--- /dev/null
+++ b/pstoraster/gsrect.h
@@ -0,0 +1,61 @@
+/* Copyright (C) 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Rectangle utilities */
+
+#ifndef gsrect_INCLUDED
+# define gsrect_INCLUDED
+
+/* Check whether one rectangle is included entirely within another. */
+#define rect_within(inner, outer)\
+ (inner.q.y <= outer.q.y && inner.q.x <= outer.q.x &&\
+ inner.p.y >= outer.p.y && inner.p.x >= outer.p.x)
+
+/*
+ * Intersect two rectangles, replacing the first. The result may be
+ * anomalous (q < p) if the intersection is empty.
+ */
+#define rect_intersect(to, from)\
+ BEGIN\
+ if ( from.p.x > to.p.x ) to.p.x = from.p.x;\
+ if ( from.q.x < to.q.x ) to.q.x = from.q.x;\
+ if ( from.p.y > to.p.y ) to.p.y = from.p.y;\
+ if ( from.q.y < to.q.y ) to.q.y = from.q.y;\
+ END
+
+/*
+ * Calculate the difference of two rectangles, a list of up to 4 rectangles.
+ * Return the number of rectangles in the list, and set the first rectangle
+ * to the intersection. The resulting first rectangle is guaranteed not to
+ * be anomalous (q < p) iff it was not anomalous originally.
+ *
+ * Note that unlike the macros above, we need different versions of this
+ * depending on the data type of the individual values: we'll only implement
+ * the variations that we need.
+ */
+int int_rect_difference(P3(gs_int_rect * outer, const gs_int_rect * inner,
+ gs_int_rect * diffs /*[4] */ ));
+
+#endif /* gsrect_INCLUDED */
diff --git a/pstoraster/gsrefct.h b/pstoraster/gsrefct.h
new file mode 100644
index 000000000..49e536eaf
--- /dev/null
+++ b/pstoraster/gsrefct.h
@@ -0,0 +1,148 @@
+/* Copyright (C) 1993, 1994, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Reference counting definitions */
+
+#ifndef gsrefct_INCLUDED
+# define gsrefct_INCLUDED
+
+/*
+ * 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;
+ gs_memory_t *memory;
+#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);
+/* rc_init[_free] is only used to initialize stack-allocated structures. */
+#define rc_init_free(vp, mem, rcinit, proc)\
+ ((vp)->rc.ref_count = rcinit,\
+ (vp)->rc.memory = mem,\
+ (vp)->rc.free = proc)
+#define rc_init(vp, mem, rcinit)\
+ rc_init_free(vp, mem, rcinit, rc_free_struct_only)
+
+#define rc_alloc_struct_n(vp, typ, pstyp, mem, errstat, cname, rcinit)\
+ BEGIN\
+ if ( ((vp) = gs_alloc_struct(mem, typ, pstyp, cname)) == 0 ) {\
+ errstat;\
+ } else {\
+ rc_init(vp, mem, rcinit);\
+ }\
+ END
+#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, cname)\
+ (*(vp)->rc.free)((vp)->rc.memory, (void *)(vp), cname)
+
+/* ------ Reference counting ------ */
+
+/* Increment a reference count. */
+#define rc_increment(vp)\
+ BEGIN if ( (vp) != 0 ) (vp)->rc.ref_count++; END
+
+/* Increment a reference count, allocating the structure if necessary. */
+#define rc_allocate_struct(vp, typ, pstype, mem, errstat, cname)\
+ BEGIN\
+ if ( (vp) != 0 )\
+ (vp)->rc.ref_count++;\
+ else\
+ rc_alloc_struct_1(vp, typ, pstype, mem, errstat, cname);\
+ END
+
+/* Guarantee that a structure is allocated and is not shared. */
+#define rc_unshare_struct(vp, typ, pstype, mem, errstat, cname)\
+ BEGIN\
+ if ( (vp) == 0 || (vp)->rc.ref_count > 1 || (vp)->rc.memory != (mem) ) {\
+ typ *new;\
+ rc_alloc_struct_1(new, typ, pstype, mem, errstat, cname);\
+ if ( vp ) (vp)->rc.ref_count--;\
+ (vp) = new;\
+ }\
+ END
+
+/* Adjust a reference count either up or down. */
+#ifdef DEBUG
+# define rc_check_(vp)\
+ BEGIN\
+ if ( gs_debug_c('?') && (vp) != 0 && (vp)->rc.ref_count < 0 )\
+ lprintf2("0x%lx has ref_count of %ld!\n", (ulong)(vp),\
+ (vp)->rc.ref_count);\
+ END
+#else
+# define rc_check_(vp) DO_NOTHING
+#endif
+#define rc_adjust_(vp, delta, cname, body)\
+ BEGIN\
+ if ( (vp) != 0 && !((vp)->rc.ref_count += delta) ) {\
+ rc_free_struct(vp, cname);\
+ body;\
+ } else\
+ rc_check_(vp);\
+ END
+#define rc_adjust(vp, delta, cname)\
+ rc_adjust_(vp, delta, cname, (vp) = 0)
+#define rc_adjust_only(vp, delta, cname)\
+ rc_adjust_(vp, delta, cname, DO_NOTHING)
+#define rc_adjust_const(vp, delta, cname)\
+ rc_adjust_only(vp, delta, cname)
+#define rc_decrement(vp, cname)\
+ rc_adjust(vp, -1, cname)
+#define rc_decrement_only(vp, cname)\
+ rc_adjust_only(vp, -1, cname)
+
+/* Assign a pointer, adjusting reference counts. */
+#define rc_assign(vpto, vpfrom, cname)\
+ BEGIN\
+ if ( (vpto) != (vpfrom) ) {\
+ rc_decrement_only(vpto, cname);\
+ (vpto) = (vpfrom);\
+ rc_increment(vpto);\
+ }\
+ END
+/* 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, cname)\
+ BEGIN\
+ if ( (vpto) != (vpfrom) ) {\
+ rc_decrement_only(vpto, cname);\
+ rc_increment(vpfrom);\
+ }\
+ END
+
+#endif /* gsrefct_INCLUDED */
diff --git a/pstoraster/gsrop.h b/pstoraster/gsrop.h
new file mode 100644
index 000000000..7f524ebf4
--- /dev/null
+++ b/pstoraster/gsrop.h
@@ -0,0 +1,46 @@
+/* Copyright (C) 1995, 1996, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* RasterOp / transparency procedure interface */
+
+#ifndef gsrop_INCLUDED
+# define gsrop_INCLUDED
+
+#include "gsropt.h"
+
+/* Procedural interface */
+
+int gs_setrasterop(P2(gs_state *, gs_rop3_t));
+gs_rop3_t gs_currentrasterop(P1(const gs_state *));
+int gs_setsourcetransparent(P2(gs_state *, bool));
+bool gs_currentsourcetransparent(P1(const gs_state *));
+int gs_settexturetransparent(P2(gs_state *, bool));
+bool gs_currenttexturetransparent(P1(const gs_state *));
+
+/* Save/restore the combined logical operation. */
+gs_logical_operation_t gs_current_logical_op(P1(const gs_state *));
+int gs_set_logical_op(P2(gs_state *, gs_logical_operation_t));
+
+#endif /* gsrop_INCLUDED */
diff --git a/pstoraster/gsropc.h b/pstoraster/gsropc.h
new file mode 100644
index 000000000..c67fa89a9
--- /dev/null
+++ b/pstoraster/gsropc.h
@@ -0,0 +1,60 @@
+/* Copyright (C) 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* RasterOp-compositing interface */
+
+#ifndef gsropc_INCLUDED
+# define gsropc_INCLUDED
+
+#include "gscompt.h"
+#include "gsropt.h"
+
+/*
+ * Define parameters for RasterOp-compositing.
+ * There are two kinds of RasterOp compositing operations.
+ * If texture == 0, the input data are the texture, and the source is
+ * implicitly all 0 (black). If texture != 0, it defines the texture,
+ * and the input data are the source. Note that in the latter case,
+ * the client (the caller of gs_create_composite_rop) promises that
+ * *texture will not change.
+ */
+
+#ifndef gx_device_color_DEFINED
+# define gx_device_color_DEFINED
+typedef struct gx_device_color_s gx_device_color;
+
+#endif
+
+typedef struct gs_composite_rop_params_s {
+ gs_logical_operation_t log_op;
+ const gx_device_color *texture;
+} gs_composite_rop_params_t;
+
+/* Create a RasterOp-compositing object. */
+int gs_create_composite_rop(P3(gs_composite_t ** ppcte,
+ const gs_composite_rop_params_t * params,
+ gs_memory_t * mem));
+
+#endif /* gsropc_INCLUDED */
diff --git a/pstoraster/gsropt.h b/pstoraster/gsropt.h
new file mode 100644
index 000000000..124a6b27f
--- /dev/null
+++ b/pstoraster/gsropt.h
@@ -0,0 +1,195 @@
+/* Copyright (C) 1995, 1996, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* RasterOp / transparency 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_D_0(op) rop3_know_0_(op, rop3_D, rop3_D_shift)
+#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_D_1(op) rop3_know_1_(op, rop3_D, rop3_D_shift)
+#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
+
+/*
+ * 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(lop) ((gs_rop3_t)((lop) & 0xff)) /* 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))
+
+ /* Test whether a logical operation uses S or T. */
+#define lop_uses_S(lop)\
+ (rop3_uses_S(lop) || ((lop) & lop_S_transparent))
+#define lop_uses_T(lop)\
+ (rop3_uses_T(lop) || ((lop) & lop_T_transparent))
+/* 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));
+
+/* Define the table of operand usage by the 256 RasterOp operations. */
+typedef enum {
+ rop_usage_none = 0,
+ rop_usage_D = 1,
+ rop_usage_S = 2,
+ rop_usage_DS = 3,
+ rop_usage_T = 4,
+ rop_usage_DT = 5,
+ rop_usage_ST = 6,
+ rop_usage_DST = 7
+} rop_usage_t;
+
+#endif /* gsropt_INCLUDED */
diff --git a/pstoraster/gsshade.c b/pstoraster/gsshade.c
new file mode 100644
index 000000000..00d2a95ff
--- /dev/null
+++ b/pstoraster/gsshade.c
@@ -0,0 +1,451 @@
+/* Copyright (C) 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Constructors for shadings */
+#include "gx.h"
+#include "gscspace.h"
+#include "gserrors.h"
+#include "gsstruct.h" /* for extern_st */
+#include "gxdevcli.h"
+#include "gxcpath.h"
+#include "gxistate.h"
+#include "gxpath.h"
+#include "gxshade.h"
+
+/* ================ Initialize shadings ================ */
+
+/* ---------------- Generic services ---------------- */
+
+/* GC descriptors */
+private_st_shading();
+private_st_shading_mesh();
+
+/* Check ColorSpace, BBox, and Function (if present). */
+/* Free variables: params. */
+private int
+check_CBFD(const gs_shading_params_t * params,
+ const gs_function_t * function, const float *domain, int m)
+{
+ int ncomp = gs_color_space_num_components(params->ColorSpace);
+
+ if (ncomp < 0 ||
+ (params->have_BBox &&
+ (params->BBox.p.x > params->BBox.q.x ||
+ params->BBox.p.y > params->BBox.q.y))
+ )
+ return_error(gs_error_rangecheck);
+ if (function != 0) {
+ if (function->params.m != m || function->params.n != ncomp)
+ return_error(gs_error_rangecheck);
+ /*
+ * The Adobe documentation says that the function's domain must
+ * be a superset of the domain defined in the shading dictionary.
+ * However, Adobe implementations apparently don't necessarily
+ * check this ahead of time; therefore, we do the same.
+ */
+#if 0 /*************** */
+ {
+ int i;
+
+ for (i = 0; i < m; ++i)
+ if (function->params.Domain[2 * i] > domain[2 * i] ||
+ function->params.Domain[2 * i + 1] < domain[2 * i + 1]
+ )
+ return_error(gs_error_rangecheck);
+ }
+#endif /*************** */
+ }
+ return 0;
+}
+
+/* Check parameters for a mesh shading. */
+private int
+check_mesh(const gs_shading_mesh_params_t * params)
+{
+ if (!data_source_is_array(params->DataSource)) {
+ int code = check_CBFD((const gs_shading_params_t *)params,
+ params->Function, params->Decode, 1);
+
+ if (code < 0)
+ return code;
+ switch (params->BitsPerCoordinate) {
+ case 1: case 2: case 4: case 8:
+ case 12: case 16: case 24: case 32:
+ break;
+ default:
+ return_error(gs_error_rangecheck);
+ }
+ switch (params->BitsPerComponent) {
+ case 1: case 2: case 4: case 8:
+ case 12: case 16:
+ break;
+ default:
+ return_error(gs_error_rangecheck);
+ }
+ }
+ return 0;
+}
+
+/* Check the BitsPerFlag value. Return the value or an error code. */
+private int
+check_BPF(const gs_data_source_t *pds, int bpf)
+{
+ if (data_source_is_array(*pds))
+ return 2;
+ switch (bpf) {
+ case 2: case 4: case 8:
+ return bpf;
+ default:
+ return_error(gs_error_rangecheck);
+ }
+}
+
+/* Initialize common shading parameters. */
+private void
+shading_params_init(gs_shading_params_t *params)
+{
+ params->ColorSpace = 0; /* must be set by client */
+ params->Background = 0;
+ params->have_BBox = false;
+ params->AntiAlias = false;
+}
+
+/* Initialize common mesh shading parameters. */
+private void
+mesh_shading_params_init(gs_shading_mesh_params_t *params)
+{
+ shading_params_init((gs_shading_params_t *)params);
+ data_source_init_floats(&params->DataSource, NULL, 0);/* client must set */
+ /* Client must set BitsPerCoordinate and BitsPerComponent */
+ /* if DataSource is not an array. */
+ params->Decode = 0;
+ params->Function = 0;
+}
+
+/* Allocate and initialize a shading. */
+/* Free variables: mem, params, ppsh, psh. */
+#define ALLOC_SHADING(sttype, stype, sfrproc, cname)\
+ BEGIN\
+ psh = gs_alloc_struct(mem, void, sttype, cname);\
+ if ( psh == 0 )\
+ return_error(gs_error_VMerror);\
+ psh->head.type = stype;\
+ psh->head.fill_rectangle = sfrproc;\
+ psh->params = *params;\
+ *ppsh = (gs_shading_t *)psh;\
+ END
+
+/* ---------------- Function-based shading ---------------- */
+
+private_st_shading_Fb();
+
+/* Initialize parameters for a Function-based shading. */
+void
+gs_shading_Fb_params_init(gs_shading_Fb_params_t * params)
+{
+ shading_params_init((gs_shading_params_t *)params);
+ params->Domain[0] = params->Domain[2] = 0;
+ params->Domain[1] = params->Domain[3] = 1;
+ gs_make_identity(&params->Matrix);
+ params->Function = 0; /* must be set by client */
+}
+
+/* Allocate and initialize a Function-based shading. */
+int
+gs_shading_Fb_init(gs_shading_t ** ppsh,
+ const gs_shading_Fb_params_t * params, gs_memory_t * mem)
+{
+ gs_shading_Fb_t *psh;
+ gs_matrix imat;
+ int code = check_CBFD((const gs_shading_params_t *)params,
+ params->Function, params->Domain, 2);
+
+ if (code < 0 ||
+ (code = gs_matrix_invert(&params->Matrix, &imat)) < 0
+ )
+ return code;
+ ALLOC_SHADING(&st_shading_Fb, shading_type_Function_based,
+ gs_shading_Fb_fill_rectangle, "gs_shading_Fb_init");
+ return 0;
+}
+
+/* ---------------- Axial shading ---------------- */
+
+private_st_shading_A();
+
+/* Initialize parameters for an Axial shading. */
+void
+gs_shading_A_params_init(gs_shading_A_params_t * params)
+{
+ shading_params_init((gs_shading_params_t *)params);
+ /* Coords must be set by client */
+ params->Domain[0] = 0;
+ params->Domain[1] = 1;
+ params->Function = 0; /* must be set by client */
+ params->Extend[0] = params->Extend[1] = false;
+}
+
+/* Allocate and initialize an Axial shading. */
+int
+gs_shading_A_init(gs_shading_t ** ppsh,
+ const gs_shading_A_params_t * params, gs_memory_t * mem)
+{
+ gs_shading_A_t *psh;
+ int code = check_CBFD((const gs_shading_params_t *)params,
+ params->Function, params->Domain, 1);
+
+ if (code < 0)
+ return code;
+ ALLOC_SHADING(&st_shading_A, shading_type_Axial,
+ gs_shading_A_fill_rectangle, "gs_shading_A_init");
+ return 0;
+}
+
+/* ---------------- Radial shading ---------------- */
+
+private_st_shading_R();
+
+/* Initialize parameters for a Radial shading. */
+void
+gs_shading_R_params_init(gs_shading_R_params_t * params)
+{
+ shading_params_init((gs_shading_params_t *)params);
+ /* Coords must be set by client */
+ params->Domain[0] = 0;
+ params->Domain[1] = 1;
+ params->Function = 0; /* must be set by client */
+ params->Extend[0] = params->Extend[1] = false;
+}
+
+/* Allocate and initialize a Radial shading. */
+int
+gs_shading_R_init(gs_shading_t ** ppsh,
+ const gs_shading_R_params_t * params, gs_memory_t * mem)
+{
+ gs_shading_R_t *psh;
+ int code = check_CBFD((const gs_shading_params_t *)params,
+ params->Function, params->Domain, 1);
+
+ if (code < 0)
+ return code;
+ if ((params->Domain != 0 && params->Domain[0] == params->Domain[1]) ||
+ params->Coords[2] < 0 || params->Coords[5] < 0
+ )
+ return_error(gs_error_rangecheck);
+ ALLOC_SHADING(&st_shading_R, shading_type_Radial,
+ gs_shading_R_fill_rectangle, "gs_shading_R_init");
+ return 0;
+}
+
+/* ---------------- Free-form Gouraud triangle mesh shading ---------------- */
+
+private_st_shading_FfGt();
+
+/* Initialize parameters for a Free-form Gouraud triangle mesh shading. */
+void
+gs_shading_FfGt_params_init(gs_shading_FfGt_params_t * params)
+{
+ mesh_shading_params_init((gs_shading_mesh_params_t *)params);
+ /* Client must set BitsPerFlag if DataSource is not an array. */
+}
+
+/* Allocate and initialize a Free-form Gouraud triangle mesh shading. */
+int
+gs_shading_FfGt_init(gs_shading_t ** ppsh,
+ const gs_shading_FfGt_params_t * params,
+ gs_memory_t * mem)
+{
+ gs_shading_FfGt_t *psh;
+ int code = check_mesh((const gs_shading_mesh_params_t *)params);
+ int bpf = check_BPF(&params->DataSource, params->BitsPerFlag);
+
+ if (code < 0)
+ return code;
+ if (bpf < 0)
+ return bpf;
+ if (params->Decode != 0 && params->Decode[0] == params->Decode[1])
+ return_error(gs_error_rangecheck);
+ ALLOC_SHADING(&st_shading_FfGt, shading_type_Free_form_Gouraud_triangle,
+ gs_shading_FfGt_fill_rectangle, "gs_shading_FfGt_init");
+ psh->params.BitsPerFlag = bpf;
+ return 0;
+}
+
+/* -------------- Lattice-form Gouraud triangle mesh shading -------------- */
+
+private_st_shading_LfGt();
+
+/* Initialize parameters for a Lattice-form Gouraud triangle mesh shading. */
+void
+gs_shading_LfGt_params_init(gs_shading_LfGt_params_t * params)
+{
+ mesh_shading_params_init((gs_shading_mesh_params_t *)params);
+ /* Client must set VerticesPerRow. */
+}
+
+/* Allocate and initialize a Lattice-form Gouraud triangle mesh shading. */
+int
+gs_shading_LfGt_init(gs_shading_t ** ppsh,
+ const gs_shading_LfGt_params_t * params, gs_memory_t * mem)
+{
+ gs_shading_LfGt_t *psh;
+ int code = check_mesh((const gs_shading_mesh_params_t *)params);
+
+ if (code < 0)
+ return code;
+ if (params->VerticesPerRow < 2)
+ return_error(gs_error_rangecheck);
+ ALLOC_SHADING(&st_shading_LfGt, shading_type_Lattice_form_Gouraud_triangle,
+ gs_shading_LfGt_fill_rectangle, "gs_shading_LfGt_init");
+ return 0;
+}
+
+/* ---------------- Coons patch mesh shading ---------------- */
+
+private_st_shading_Cp();
+
+/* Initialize parameters for a Coons patch mesh shading. */
+void
+gs_shading_Cp_params_init(gs_shading_Cp_params_t * params)
+{
+ mesh_shading_params_init((gs_shading_mesh_params_t *)params);
+ /* Client must set BitsPerFlag if DataSource is not an array. */
+}
+
+/* Allocate and initialize a Coons patch mesh shading. */
+int
+gs_shading_Cp_init(gs_shading_t ** ppsh,
+ const gs_shading_Cp_params_t * params, gs_memory_t * mem)
+{
+ gs_shading_Cp_t *psh;
+ int code = check_mesh((const gs_shading_mesh_params_t *)params);
+ int bpf = check_BPF(&params->DataSource, params->BitsPerFlag);
+
+ if (code < 0)
+ return code;
+ if (bpf < 0)
+ return bpf;
+ ALLOC_SHADING(&st_shading_Cp, shading_type_Coons_patch,
+ gs_shading_Cp_fill_rectangle, "gs_shading_Cp_init");
+ psh->params.BitsPerFlag = bpf;
+ return 0;
+}
+
+/* ---------------- Tensor product patch mesh shading ---------------- */
+
+private_st_shading_Tpp();
+
+/* Initialize parameters for a Tensor product patch mesh shading. */
+void
+gs_shading_Tpp_params_init(gs_shading_Tpp_params_t * params)
+{
+ mesh_shading_params_init((gs_shading_mesh_params_t *)params);
+ /* Client must set BitsPerFlag if DataSource is not an array. */
+}
+
+/* Allocate and initialize a Tensor product patch mesh shading. */
+int
+gs_shading_Tpp_init(gs_shading_t ** ppsh,
+ const gs_shading_Tpp_params_t * params, gs_memory_t * mem)
+{
+ gs_shading_Tpp_t *psh;
+ int code = check_mesh((const gs_shading_mesh_params_t *)params);
+ int bpf = check_BPF(&params->DataSource, params->BitsPerFlag);
+
+ if (code < 0)
+ return code;
+ if (bpf < 0)
+ return bpf;
+ ALLOC_SHADING(&st_shading_Tpp, shading_type_Tensor_product_patch,
+ gs_shading_Tpp_fill_rectangle, "gs_shading_Tpp_init");
+ psh->params.BitsPerFlag = bpf;
+ return 0;
+}
+
+/* ================ Shading rendering ================ */
+
+/* Fill a path with a shading. */
+int
+gs_shading_fill_path(const gs_shading_t *psh, const gx_path *ppath,
+ gx_device *orig_dev, gs_imager_state *pis)
+{
+ gs_memory_t *mem = pis->memory;
+ gx_device *dev = orig_dev;
+ gs_fixed_rect path_box;
+ gs_rect rect;
+ gx_clip_path *box_clip = 0;
+ gx_clip_path *path_clip = 0;
+ gx_device_clip box_dev, path_dev;
+ int code;
+
+#if 0 /****** NOT IMPLEMENTED YET *****/
+ if (psh->params.have_BBox) {
+ box_clip = gx_cpath_alloc(mem, "shading_fill_path(box_clip)");
+ if (box_clip == 0)
+ return_error(gs_error_VMerror);
+ /****** APPEND TRANSFORMED BOX ******/
+ gx_make_clip_device(&box_dev, &box_dev, box_clip->list);
+ box_dev.target = dev;
+ dev = &box_dev;
+ dev_proc(dev, open_device)(dev);
+ }
+#endif
+ dev_proc(dev, get_clipping_box)(dev, &path_box);
+#if 0 /****** NOT IMPLEMENTED YET *****/
+ if (ppath) {
+ if (psh->params.Background) {
+ /****** FILL BOX WITH BACKGROUND ******/
+ }
+ path_clip = gx_cpath_alloc(mem, "shading_fill_path(path_clip)");
+ if (path_clip == 0) {
+ code = gs_note_error(gs_error_VMerror);
+ goto out;
+ }
+ /****** SET CLIP PATH ******/
+ gx_make_clip_device(&path_dev, &path_dev, path_clip->list);
+ path_dev.target = dev;
+ dev = &path_dev;
+ dev_proc(dev, open_device)(dev);
+ dev_proc(dev, get_clipping_box)(dev, &path_box);
+ }
+#endif
+ {
+ gs_rect path_rect;
+ const gs_matrix *pmat = &ctm_only(pis);
+
+ path_rect.p.x = fixed2float(path_box.p.x);
+ path_rect.p.y = fixed2float(path_box.p.y);
+ path_rect.q.x = fixed2float(path_box.q.x);
+ path_rect.q.y = fixed2float(path_box.q.y);
+ gs_bbox_transform_inverse(&path_rect, pmat, &rect);
+ }
+ code = psh->head.fill_rectangle(psh, &rect, dev, pis);
+out:
+ if (path_clip)
+ gx_cpath_free(path_clip, "shading_fill_path(path_clip)");
+ if (box_clip)
+ gx_cpath_free(box_clip, "shading_fill_path(box_clip)");
+ return code;
+}
diff --git a/pstoraster/gsshade.h b/pstoraster/gsshade.h
new file mode 100644
index 000000000..5d3a78fa2
--- /dev/null
+++ b/pstoraster/gsshade.h
@@ -0,0 +1,255 @@
+/* Copyright (C) 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Definitions for shading */
+
+#ifndef gsshade_INCLUDED
+# define gsshade_INCLUDED
+
+#include "gsccolor.h"
+#include "gscspace.h"
+#include "gsdsrc.h"
+#include "gsfunc.h"
+#include "gsmatrix.h"
+
+/* ---------------- Types and structures ---------------- */
+
+/* Define the shading types. */
+typedef enum {
+ shading_type_Function_based = 1,
+ shading_type_Axial = 2,
+ shading_type_Radial = 3,
+ shading_type_Free_form_Gouraud_triangle = 4,
+ shading_type_Lattice_form_Gouraud_triangle = 5,
+ shading_type_Coons_patch = 6,
+ shading_type_Tensor_product_patch = 7
+} gs_shading_type_t;
+
+/*
+ * Define information common to all shading types. We separate the private
+ * part from the parameters so that clients can create parameter structures
+ * without having to know the structure of the implementation.
+ */
+#define gs_shading_params_common\
+ gs_color_space *ColorSpace;\
+ gs_client_color *Background;\
+ bool have_BBox;\
+ gs_rect BBox;\
+ bool AntiAlias
+
+typedef struct gs_shading_params_s {
+ gs_shading_params_common;
+} gs_shading_params_t;
+
+/* Define the type-specific procedures for shadings. */
+#ifndef gs_shading_t_DEFINED
+# define gs_shading_t_DEFINED
+typedef struct gs_shading_s gs_shading_t;
+#endif
+#ifndef gx_device_DEFINED
+# define gx_device_DEFINED
+typedef struct gx_device_s gx_device;
+#endif
+/*
+ * Fill a user space rectangle. This will paint every pixel that is in the
+ * intersection of the rectangle and the shading's geometry, but it may
+ * leave some pixels in the rectangle unpainted, and it may also paint
+ * outside the rectangle: the caller is responsible for setting up a
+ * clipping device if necessary.
+ */
+#define shading_fill_rectangle_proc(proc)\
+ int proc(P4(const gs_shading_t *psh, const gs_rect *rect, gx_device *dev,\
+ gs_imager_state *pis))
+typedef shading_fill_rectangle_proc((*shading_fill_rectangle_proc_t));
+typedef struct gs_shading_head_s {
+ gs_shading_type_t type;
+ shading_fill_rectangle_proc_t fill_rectangle;
+} gs_shading_head_t;
+
+/* Define a generic shading, for use as the target type of pointers. */
+struct gs_shading_s {
+ gs_shading_head_t head;
+ gs_shading_params_t params;
+};
+#define ShadingType(psh) ((psh)->head.type)
+#define private_st_shading() /* in gsshade.c */\
+ gs_private_st_ptrs2(st_shading, gs_shading_t, "gs_shading_t",\
+ shading_enum_ptrs, shading_reloc_ptrs,\
+ params.ColorSpace, params.Background)
+
+/* Define Function-based shading. */
+typedef struct gs_shading_Fb_params_s {
+ gs_shading_params_common;
+ float Domain[4];
+ gs_matrix Matrix;
+ gs_function_t *Function;
+} gs_shading_Fb_params_t;
+
+#define private_st_shading_Fb() /* in gsshade.c */\
+ gs_private_st_suffix_add1(st_shading_Fb, gs_shading_Fb_t,\
+ "gs_shading_Fb_t", shading_Fb_enum_ptrs, shading_Fb_reloc_ptrs,\
+ st_shading, params.Function)
+
+/* Define Axial shading. */
+typedef struct gs_shading_A_params_s {
+ gs_shading_params_common;
+ float Coords[4];
+ float Domain[2];
+ gs_function_t *Function;
+ bool Extend[2];
+} gs_shading_A_params_t;
+
+#define private_st_shading_A() /* in gsshade.c */\
+ gs_private_st_suffix_add1(st_shading_A, gs_shading_A_t,\
+ "gs_shading_A_t", shading_A_enum_ptrs, shading_A_reloc_ptrs,\
+ st_shading, params.Function)
+
+/* Define Radial shading. */
+typedef struct gs_shading_R_params_s {
+ gs_shading_params_common;
+ float Coords[6];
+ float Domain[2];
+ gs_function_t *Function;
+ bool Extend[2];
+} gs_shading_R_params_t;
+
+#define private_st_shading_R() /* in gsshade.c */\
+ gs_private_st_suffix_add1(st_shading_R, gs_shading_R_t,\
+ "gs_shading_R_t", shading_R_enum_ptrs, shading_R_reloc_ptrs,\
+ st_shading, params.Function)
+
+/* Define common parameters for mesh shading. */
+#define gs_shading_mesh_params_common\
+ gs_shading_params_common;\
+ gs_data_source_t DataSource;\
+ int BitsPerCoordinate;\
+ int BitsPerComponent;\
+ float *Decode;\
+ gs_function_t *Function
+/* The following are for internal use only. */
+typedef struct gs_shading_mesh_params_s {
+ gs_shading_mesh_params_common;
+} gs_shading_mesh_params_t;
+typedef struct gs_shading_mesh_s {
+ gs_shading_head_t head;
+ gs_shading_mesh_params_t params;
+} gs_shading_mesh_t;
+
+#define private_st_shading_mesh() /* in gsshade.c */\
+ gs_private_st_suffix_add2(st_shading_mesh, gs_shading_mesh_t,\
+ "gs_shading_mesh_t", shading_mesh_enum_ptrs, shading_mesh_reloc_ptrs,\
+ st_shading, params.Decode, params.Function)
+
+/* Define Free-form Gouraud triangle mesh shading. */
+typedef struct gs_shading_FfGt_params_s {
+ gs_shading_mesh_params_common;
+ int BitsPerFlag;
+} gs_shading_FfGt_params_t;
+
+#define private_st_shading_FfGt() /* in gsshade.c */\
+ gs_private_st_suffix_add0_local(st_shading_FfGt, gs_shading_FfGt_t,\
+ "gs_shading_FfGt_t", shading_mesh_enum_ptrs, shading_mesh_reloc_ptrs,\
+ st_shading_mesh)
+
+/* Define Lattice-form Gouraud triangle mesh shading. */
+typedef struct gs_shading_LfGt_params_s {
+ gs_shading_mesh_params_common;
+ int VerticesPerRow;
+} gs_shading_LfGt_params_t;
+
+#define private_st_shading_LfGt() /* in gsshade.c */\
+ gs_private_st_suffix_add0_local(st_shading_LfGt, gs_shading_LfGt_t,\
+ "gs_shading_LfGt_t", shading_mesh_enum_ptrs, shading_mesh_reloc_ptrs,\
+ st_shading_mesh)
+
+/* Define Coons patch mesh shading. */
+typedef struct gs_shading_Cp_params_s {
+ gs_shading_mesh_params_common;
+ int BitsPerFlag;
+} gs_shading_Cp_params_t;
+
+#define private_st_shading_Cp() /* in gsshade.c */\
+ gs_private_st_suffix_add0_local(st_shading_Cp, gs_shading_Cp_t,\
+ "gs_shading_Cp_t", shading_mesh_enum_ptrs, shading_mesh_reloc_ptrs,\
+ st_shading_mesh)
+
+/* Define Tensor product patch mesh shading. */
+typedef struct gs_shading_Tpp_params_s {
+ gs_shading_mesh_params_common;
+ int BitsPerFlag;
+} gs_shading_Tpp_params_t;
+
+#define private_st_shading_Tpp() /* in gsshade.c */\
+ gs_private_st_suffix_add0_local(st_shading_Tpp, gs_shading_Tpp_t,\
+ "gs_shading_Tpp_t", shading_mesh_enum_ptrs, shading_mesh_reloc_ptrs,\
+ st_shading_mesh)
+
+/* ---------------- Procedures ---------------- */
+
+/* Initialize shading parameters of specific types. */
+void gs_shading_Fb_params_init(P1(gs_shading_Fb_params_t * params));
+void gs_shading_A_params_init(P1(gs_shading_A_params_t * params));
+void gs_shading_R_params_init(P1(gs_shading_R_params_t * params));
+void gs_shading_FfGt_params_init(P1(gs_shading_FfGt_params_t * params));
+void gs_shading_LfGt_params_init(P1(gs_shading_LfGt_params_t * params));
+void gs_shading_Cp_params_init(P1(gs_shading_Cp_params_t * params));
+void gs_shading_Tpp_params_init(P1(gs_shading_Tpp_params_t * params));
+
+/* Create (initialize) shadings of specific types. */
+int gs_shading_Fb_init(P3(gs_shading_t ** ppsh,
+ const gs_shading_Fb_params_t * params,
+ gs_memory_t * mem));
+int gs_shading_A_init(P3(gs_shading_t ** ppsh,
+ const gs_shading_A_params_t * params,
+ gs_memory_t * mem));
+int gs_shading_R_init(P3(gs_shading_t ** ppsh,
+ const gs_shading_R_params_t * params,
+ gs_memory_t * mem));
+int gs_shading_FfGt_init(P3(gs_shading_t ** ppsh,
+ const gs_shading_FfGt_params_t * params,
+ gs_memory_t * mem));
+int gs_shading_LfGt_init(P3(gs_shading_t ** ppsh,
+ const gs_shading_LfGt_params_t * params,
+ gs_memory_t * mem));
+int gs_shading_Cp_init(P3(gs_shading_t ** ppsh,
+ const gs_shading_Cp_params_t * params,
+ gs_memory_t * mem));
+int gs_shading_Tpp_init(P3(gs_shading_t ** ppsh,
+ const gs_shading_Tpp_params_t * params,
+ gs_memory_t * mem));
+
+/*
+ * Fill a path with a shading. This is the only externally accessible
+ * procedure for rendering a shading. A NULL path means fill the
+ * shading's geometry (shfill).
+ */
+#ifndef gx_path_DEFINED
+# define gx_path_DEFINED
+typedef struct gx_path_s gx_path;
+#endif
+int gs_shading_fill_path(P4(const gs_shading_t *psh, const gx_path *ppath,
+ gx_device *dev, gs_imager_state *pis));
+
+#endif /* gsshade_INCLUDED */
diff --git a/pstoraster/gsstate.c b/pstoraster/gsstate.c
new file mode 100644
index 000000000..7a7a4157e
--- /dev/null
+++ b/pstoraster/gsstate.c
@@ -0,0 +1,1025 @@
+/* Copyright (C) 1989, 1995, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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 "gsalpha.h"
+#include "gscolor2.h"
+#include "gscoord.h" /* for gs_initmatrix */
+#include "gscie.h"
+#include "gxcmap.h"
+#include "gxdevice.h"
+#include "gxpcache.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 *const cmap_procs_default;
+
+/* Forward references */
+private gs_state *gstate_alloc(P3(gs_memory_t *, client_name_t,
+ const gs_state *));
+private gs_state *gstate_clone(P4(gs_state *, gs_memory_t *, client_name_t,
+ gs_state_copy_reason_t));
+private void gstate_free_contents(P1(gs_state *));
+private int gstate_copy(P4(gs_state *, const gs_state *,
+ gs_state_copy_reason_t, 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 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.
+ * These fall into two groups:
+ *
+ * (3a) Objects that are logically connected to individual gstates.
+ * We use reference counting to manage these. Currently these are:
+ * halftone, dev_ht, 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.
+ * Similarly, dev_color may point to the dev_ht object. For
+ * simplicity, we initialize all of these pointers to 0 and then
+ * allocate the object itself when needed.
+ *
+ * (3b) Objects whose lifetimes are associated with something else.
+ * Currently these are:
+ * ht_cache, which is associated with the entire gstate
+ * stack, is allocated with the very first graphics state,
+ * and currently is never freed;
+ * pattern_cache, which is associated with the entire
+ * stack, is allocated when first needed, and currently
+ * is never freed;
+ * view_clip, which is associated with the current
+ * save level (effectively, with the gstate sub-stack
+ * back to the save) and is managed specially.
+ *
+ * (4) Objects that are referenced directly by exactly one gstate and that
+ * are not referenced (except transiently) from any other object.
+ * These fall into two groups:
+ *
+ * (4b) Objects allocated individually, for the given reason:
+ * line_params.dash.pattern (variable-length),
+ * color_space, path, clip_path, effective_clip.path,
+ * ccolor, dev_color
+ * (may be referenced from image enumerators or elsewhere)
+ *
+ * (4b) The "client data" for a gstate. For the interpreter, this is
+ * the refs associated with the gstate, such as the screen procedures.
+ * Client-supplied procedures manage client data.
+ *
+ * (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, clip_path, and (if different from both clip_path
+ * and view_clip) effective_clip.path require
+ * gx_path_assign/free, which uses a reference count;
+ * color_space and ccolor require cs_adjust_color/cspace_count
+ * or cs_adjust_counts, which use a reference count;
+ * dev_color has no references to storage that it owns.
+ * We count on garbage collection or restore to deallocate
+ * sub-objects of halftone.
+ *
+ * Note that when after a gsave, the existing gstate references the related
+ * objects that we allocate at the same time, and the newly allocated gstate
+ * references the old related objects. Similarly, during a grestore, we
+ * free the related objects referenced by the current gstate, but after the
+ * grestore, we free the saved gstate, not the current one. However, when
+ * we allocate gstates off-stack, the newly allocated gstate does reference
+ * the newly allocated component objects. Note also that setgstate /
+ * currentgstate may produce gstates in which different allocators own
+ * different sub-objects; this is OK, because restore guarantees that there
+ * won't be any dangling pointers (as long as we don't allow pointers from
+ * global gstates to local objects).
+ */
+
+/*
+ * Enumerate the pointers in a graphics state, other than the ones in the
+ * imager state, and device, which must be handled specially.
+ */
+#define gs_state_do_ptrs(m)\
+ m(0,saved) m(1,path) m(2,clip_path) m(3,view_clip) m(4,effective_clip_path)\
+ m(5,color_space) m(6,ccolor) m(7,dev_color)\
+ m(8,font) m(9,root_font) m(10,show_gstate) /*m(---,device)*/\
+ m(11,client_data)
+#define gs_state_num_ptrs 12
+
+/*
+ * Define these elements of the graphics state that are allocated
+ * individually for each state, except for line_params.dash.pattern.
+ * Note that effective_clip_shared is not on the list.
+ */
+typedef struct gs_state_parts_s {
+ gx_path *path;
+ gx_clip_path *clip_path;
+ gx_clip_path *effective_clip_path;
+ gs_color_space *color_space;
+ gs_client_color *ccolor;
+ gx_device_color *dev_color;
+} gs_state_parts;
+
+#define GSTATE_ASSIGN_PARTS(pto, pfrom)\
+ ((pto)->path = (pfrom)->path, (pto)->clip_path = (pfrom)->clip_path,\
+ (pto)->effective_clip_path = (pfrom)->effective_clip_path,\
+ (pto)->color_space = (pfrom)->color_space,\
+ (pto)->ccolor = (pfrom)->ccolor, (pto)->dev_color = (pfrom)->dev_color)
+
+/* GC descriptors */
+private_st_line_params();
+private_st_imager_state();
+private_st_imager_state_shared();
+private_st_gs_state();
+
+/* GC procedures for gs_imager_state */
+#define pis ((gs_imager_state *)vptr)
+private
+ENUM_PTRS_BEGIN(imager_state_enum_ptrs) ENUM_SUPER(gs_imager_state, st_line_params, line_params, st_imager_state_num_ptrs - st_line_params_num_ptrs);
+
+ENUM_PTR(0, gs_imager_state, shared);
+#define e1(i,elt) ENUM_PTR(i+1,gs_imager_state,elt);
+gs_cr_state_do_ptrs(e1)
+#undef e1
+ENUM_PTRS_END
+private RELOC_PTRS_BEGIN(imager_state_reloc_ptrs)
+{
+ RELOC_SUPER(gs_imager_state, st_line_params, line_params);
+ RELOC_PTR(gs_imager_state, shared);
+#define r1(i,elt) RELOC_PTR(gs_imager_state,elt);
+ gs_cr_state_do_ptrs(r1)
+#undef r1
+} RELOC_PTRS_END
+#undef pis
+
+/* 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 */
+ENUM_RETURN(gx_device_enum_ptr(gsvptr->device));
+#undef e1
+ENUM_PTRS_END
+private RELOC_PTRS_BEGIN(gs_state_reloc_ptrs)
+{
+ RELOC_PREFIX(st_imager_state);
+ {
+#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);
+ }
+}
+RELOC_PTRS_END
+#undef gsvptr
+
+/* Copy client data, using the copy_for procedure if available, */
+/* the copy procedure otherwise. */
+private int
+gstate_copy_client_data(gs_state * pgs, void *dto, void *dfrom,
+ gs_state_copy_reason_t reason)
+{
+ return (pgs->client_procs.copy_for != 0 ?
+ (*pgs->client_procs.copy_for) (dto, dfrom, reason) :
+ (*pgs->client_procs.copy) (dto, dfrom));
+}
+
+/* ------ Operations on the entire graphics state ------ */
+
+/* Initialize an imager state, other than the parts covered by */
+/* gs_imager_state_initial. */
+/* The halftone, dev_ht, and ht_cache elements are not set or used. */
+private float
+null_transfer(floatp gray, const gx_transfer_map * pmap)
+{
+ return gray;
+}
+private void
+rc_free_imager_shared(gs_memory_t * mem, void *data, client_name_t cname)
+{
+ gs_imager_state_shared_t * const shared =
+ (gs_imager_state_shared_t *)data;
+
+ if (shared->cs_DeviceCMYK) {
+ gs_cspace_release(shared->cs_DeviceCMYK);
+ gs_free_object(mem, shared->cs_DeviceCMYK, "shared DeviceCMYK");
+ }
+ if (shared->cs_DeviceRGB) {
+ gs_cspace_release(shared->cs_DeviceRGB);
+ gs_free_object(mem, shared->cs_DeviceRGB, "shared DeviceRGB");
+ }
+ if (shared->cs_DeviceGray) {
+ gs_cspace_release(shared->cs_DeviceGray);
+ gs_free_object(mem, shared->cs_DeviceGray, "shared DeviceGray");
+ }
+ rc_free_struct_only(mem, data, cname);
+}
+
+int
+gs_imager_state_initialize(gs_imager_state * pis, gs_memory_t * mem)
+{
+ pis->memory = mem;
+ /* Preallocate color spaces. */
+ {
+ int code;
+ gs_imager_state_shared_t *shared;
+
+ rc_alloc_struct_1(shared, gs_imager_state_shared_t,
+ &st_imager_state_shared, mem,
+ return_error(gs_error_VMerror),
+ "gs_imager_state_init(shared)");
+ shared->cs_DeviceGray = shared->cs_DeviceRGB =
+ shared->cs_DeviceCMYK = 0; /* in case we bail out */
+ shared->rc.free = rc_free_imager_shared;
+ if ((code = gs_cspace_build_DeviceGray(&shared->cs_DeviceGray, mem)) < 0 ||
+ (code = gs_cspace_build_DeviceRGB(&shared->cs_DeviceRGB, mem)) < 0 ||
+ (code = gs_cspace_build_DeviceCMYK(&shared->cs_DeviceCMYK, mem)) < 0
+ ) {
+ rc_free_imager_shared(mem, shared, "gs_imager_state_init(shared)");
+ return code;
+ }
+ pis->shared = shared;
+ }
+ /* Skip halftone */
+ {
+ int i;
+
+ for (i = 0; i < gs_color_select_count; ++i)
+ pis->screen_phase[i].x = pis->screen_phase[i].y = 0;
+ }
+ /* Skip dev_ht */
+ /* Skip ht_cache */
+ pis->cie_render = 0;
+ pis->black_generation = 0;
+ pis->undercolor_removal = 0;
+ /* Allocate an initial transfer map. */
+ rc_alloc_struct_n(pis->set_transfer.colored.gray,
+ gx_transfer_map, &st_transfer_map,
+ mem, return_error(gs_error_VMerror),
+ "gs_imager_state_init(transfer)", 4);
+ pis->set_transfer.colored.gray->proc = null_transfer;
+ pis->set_transfer.colored.gray->id = gs_next_ids(1);
+ pis->set_transfer.colored.gray->values[0] = frac_0;
+ pis->set_transfer.colored.red =
+ pis->set_transfer.colored.green =
+ pis->set_transfer.colored.blue =
+ pis->set_transfer.colored.gray;
+ pis->effective_transfer = pis->set_transfer;
+ pis->cie_joint_caches = 0;
+ pis->cmap_procs = cmap_procs_default;
+ pis->pattern_cache = 0;
+ return 0;
+}
+
+/* Release an imager state. */
+void
+gs_imager_state_release(gs_imager_state * pis)
+{
+ const char *const cname = "gs_imager_state_release";
+
+#define RCDECR(element)\
+ rc_decrement(pis->element, cname)
+
+ 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);
+ RCDECR(shared);
+#undef RCDECR
+}
+
+/* Allocate and initialize a graphics state. */
+gs_state *
+gs_state_alloc(gs_memory_t * mem)
+{
+ gs_state *pgs = gstate_alloc(mem, "gs_state_alloc", NULL);
+
+ if (pgs == 0)
+ return 0;
+ {
+ static const gs_imager_state gstate_initial =
+ {
+ gs_imager_state_initial(1.0)
+ };
+
+ *(gs_imager_state *) pgs = gstate_initial;
+ }
+ /*
+ * Just enough of the state is initialized at this point
+ * that it's OK to call gs_state_free if an allocation fails.
+ */
+ rc_alloc_struct_1(pgs->halftone, gs_halftone, &st_halftone, mem,
+ goto fail, "gs_state_alloc(halftone)");
+ pgs->saved = 0;
+
+ /* Initialize the color rendering state. */
+
+ pgs->halftone->type = ht_type_none;
+ pgs->dev_ht = 0;
+ pgs->ht_cache = gx_ht_alloc_cache(mem,
+ gx_ht_cache_default_tiles(),
+ gx_ht_cache_default_bits());
+ gs_imager_state_initialize((gs_imager_state *) pgs, mem);
+ pgs->client_data = 0;
+
+ /* Initialize other things not covered by initgraphics */
+
+ pgs->path = gx_path_alloc(mem, "gs_state_alloc(path)");
+ pgs->clip_path = gx_cpath_alloc(mem, "gs_state_alloc(clip_path)");
+ pgs->view_clip = gx_cpath_alloc(mem, "gs_state_alloc(view_clip)");
+ pgs->view_clip->rule = 0; /* no clipping */
+ pgs->effective_clip_id = pgs->clip_path->id;
+ pgs->effective_view_clip_id = gs_no_id;
+ pgs->effective_clip_path = pgs->clip_path;
+ pgs->effective_clip_shared = true;
+ /* 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->device = 0; /* setting device adjusts refcts */
+ 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_setlimitclamp(pgs, false);
+ 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 = (gs_char_path_mode) 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;
+ fail:
+ gs_state_free(pgs);
+ return 0;
+}
+
+/* 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, pgs->memory, "gs_gsave",
+ copy_for_gsave);
+
+ if (pnew == 0)
+ return_error(gs_error_VMerror);
+ 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;
+}
+
+/* Save the graphics state for a 'save'. */
+/* We cut the stack below the new gstate, and return the old one. */
+/* In addition to an ordinary gsave, we create a new view clip path. */
+int
+gs_gsave_for_save(gs_state * pgs, gs_state ** psaved)
+{
+ int code;
+ gx_clip_path *old_cpath = pgs->view_clip;
+ gx_clip_path *new_cpath;
+
+ if (old_cpath) {
+ new_cpath =
+ gx_cpath_alloc_shared(old_cpath, pgs->memory,
+ "gs_gsave_for_save(view_clip)");
+ if (new_cpath == 0)
+ return_error(gs_error_VMerror);
+ } else {
+ new_cpath = 0;
+ }
+ code = gs_gsave(pgs);
+ if (code < 0) {
+ if (new_cpath)
+ gx_cpath_free(new_cpath, "gs_gsave_for_save(view_clip)");
+ return code;
+ }
+ if (pgs->effective_clip_path == pgs->view_clip)
+ pgs->effective_clip_path = new_cpath;
+ pgs->view_clip = new_cpath;
+ /* Cut the stack so we can't grestore past here. */
+ *psaved = pgs->saved;
+ pgs->saved = 0;
+ return code;
+}
+
+/* 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)
+ gstate_copy_client_data(pgs, pdata, sdata, copy_for_grestore);
+ 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 the graphics state for a 'restore', splicing the old stack */
+/* back on. Note that we actually do a grestoreall + 2 grestores. */
+int
+gs_grestoreall_for_restore(gs_state * pgs, gs_state * saved)
+{
+ int code;
+
+ while (pgs->saved->saved) {
+ code = gs_grestore(pgs);
+ if (code < 0)
+ return code;
+ }
+ /* Make sure we don't leave dangling pointers in the caches. */
+ gx_ht_clear_cache(pgs->ht_cache);
+ if (pgs->pattern_cache)
+ (*pgs->pattern_cache->free_all) (pgs->pattern_cache);
+ pgs->saved->saved = saved;
+ code = gs_grestore(pgs);
+ if (code < 0)
+ return code;
+ if (pgs->view_clip) {
+ gx_cpath_free(pgs->view_clip, "gs_grestoreall_for_restore");
+ pgs->view_clip = 0;
+ }
+ return gs_grestore(pgs);
+}
+
+
+/* Restore to the bottommost graphics state (at this save level). */
+int
+gs_grestoreall(gs_state * pgs)
+{
+ int code;
+
+ if (!pgs->saved) /* shouldn't happen */
+ return gs_gsave(pgs);
+ while (pgs->saved->saved) {
+ code = gs_grestore(pgs);
+ if (code < 0)
+ return code;
+ }
+ code = gs_grestore(pgs);
+ if (code < 0)
+ return code;
+ return code;
+}
+
+/* Allocate and return a new graphics state. */
+gs_state *
+gs_gstate(gs_state * pgs)
+{
+ return gs_state_copy(pgs, pgs->memory);
+}
+gs_state *
+gs_state_copy(gs_state * pgs, gs_memory_t * mem)
+{
+ gs_state *pnew;
+
+ /* Prevent 'capturing' the view clip path. */
+ gx_clip_path *view_clip = pgs->view_clip;
+
+ pgs->view_clip = 0;
+ pnew = gstate_clone(pgs, mem, "gs_gstate", copy_for_gstate);
+ pgs->view_clip = view_clip;
+ 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)
+{
+ return gstate_copy(pto, pfrom, copy_for_copygstate, "gs_copygstate");
+}
+
+/* Copy the current graphics state to a previously allocated one. */
+int
+gs_currentgstate(gs_state * pto, const gs_state * pgs)
+{
+ int code =
+ gstate_copy(pto, pgs, copy_for_currentgstate, "gs_currentgstate");
+
+ if (code >= 0)
+ pto->view_clip = 0;
+ return code;
+}
+
+/* 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,
+ * the view clip, and possibly the show_gstate.
+ */
+ gs_state *saved_show = pgs->show_gstate;
+ int level = pgs->level;
+ gx_clip_path *view_clip = pgs->view_clip;
+ int code;
+
+ pgs->view_clip = 0; /* prevent refcount decrementing */
+ code = gstate_copy(pgs, pfrom, copy_for_setgstate, "gs_setgstate");
+ if (code < 0)
+ return code;
+ pgs->level = level;
+ pgs->view_clip = view_clip;
+ 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(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 ||
+ (gs_setdashadapt(pgs, false),
+ (code = gs_setdotlength(pgs, 0.0, false))) < 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 CLAMP_TO_HALF(v)\
+ ((v) <= 0 ? fixed_0 : (v) >= 0.5 ? fixed_half : float2fixed(v));
+
+ pgs->fill_adjust.x = CLAMP_TO_HALF(adjust_x);
+ pgs->fill_adjust.y = CLAMP_TO_HALF(adjust_y);
+ return 0;
+#undef CLAMP_TO_HALF
+}
+
+/* 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;
+}
+
+/* setlimitclamp */
+void
+gs_setlimitclamp(gs_state * pgs, bool clamp)
+{
+ pgs->clamp_coordinates = clamp;
+}
+
+/* currentlimitclamp */
+bool
+gs_currentlimitclamp(const gs_state * pgs)
+{
+ return pgs->clamp_coordinates;
+}
+
+/* ------ Internal routines ------ */
+
+/* Free the privately allocated parts of a gstate. */
+private void
+gstate_free_parts(const gs_state * parts, gs_memory_t * mem, client_name_t cname)
+{
+ gs_free_object(mem, parts->dev_color, cname);
+ gs_free_object(mem, parts->ccolor, cname);
+ gs_free_object(mem, parts->color_space, cname);
+ if (!parts->effective_clip_shared)
+ gx_cpath_free(parts->effective_clip_path, cname);
+ gx_cpath_free(parts->clip_path, cname);
+ gx_path_free(parts->path, cname);
+}
+
+/* Allocate the privately allocated parts of a gstate. */
+private int
+gstate_alloc_parts(gs_state * parts, const gs_state * shared,
+ gs_memory_t * mem, client_name_t cname)
+{
+ parts->path =
+ (shared ?
+ gx_path_alloc_shared(shared->path, mem,
+ "gstate_alloc_parts(path)") :
+ gx_path_alloc(mem, "gstate_alloc_parts(path)"));
+ parts->clip_path =
+ (shared ?
+ gx_cpath_alloc_shared(shared->clip_path, mem,
+ "gstate_alloc_parts(clip_path)") :
+ gx_cpath_alloc(mem, "gstate_alloc_parts(clip_path)"));
+ if (!shared || shared->effective_clip_shared) {
+ parts->effective_clip_path = parts->clip_path;
+ parts->effective_clip_shared = true;
+ } else {
+ parts->effective_clip_path =
+ gx_cpath_alloc_shared(shared->effective_clip_path, mem,
+ "gstate_alloc_parts(effective_clip_path)");
+ parts->effective_clip_shared = false;
+ }
+ parts->color_space =
+ gs_alloc_struct(mem, gs_color_space, &st_color_space, cname);
+ parts->ccolor =
+ gs_alloc_struct(mem, gs_client_color, &st_client_color, cname);
+ parts->dev_color =
+ gs_alloc_struct(mem, gx_device_color, &st_device_color, cname);
+ if (parts->path == 0 || parts->clip_path == 0 ||
+ parts->effective_clip_path == 0 ||
+ parts->color_space == 0 || parts->ccolor == 0 ||
+ parts->dev_color == 0
+ ) {
+ gstate_free_parts(parts, mem, cname);
+ return_error(gs_error_VMerror);
+ }
+ return 0;
+}
+
+/*
+ * Allocate a gstate and its contents.
+ * If pfrom is not NULL, the path, clip_path, and (if distinct from both
+ * clip_path and view_clip) effective_clip_path share the segments of
+ * pfrom's corresponding path(s).
+ */
+private gs_state *
+gstate_alloc(gs_memory_t * mem, client_name_t cname, const gs_state * pfrom)
+{
+ gs_state *pgs =
+ gs_alloc_struct(mem, gs_state, &st_gs_state, cname);
+
+ if (pgs == 0)
+ return 0;
+ if (gstate_alloc_parts(pgs, pfrom, mem, cname) < 0) {
+ gs_free_object(mem, pgs, cname);
+ return 0;
+ }
+ pgs->memory = mem;
+ return pgs;
+}
+
+/* Copy the dash pattern from one gstate to another. */
+private int
+gstate_copy_dash(gs_state * pto, const gs_state * pfrom)
+{
+ return gs_setdash(pto, pfrom->line_params.dash.pattern,
+ pfrom->line_params.dash.pattern_size,
+ pfrom->line_params.dash.offset);
+}
+
+/* Clone an existing graphics state. */
+/* Return 0 if the allocation fails. */
+/* If reason is for_gsave, the clone refers to the old contents, */
+/* and we switch the old state to refer to the new contents. */
+private gs_state *
+gstate_clone(gs_state * pfrom, gs_memory_t * mem, client_name_t cname,
+ gs_state_copy_reason_t reason)
+{
+ gs_state *pgs = gstate_alloc(mem, cname, pfrom);
+ gs_state_parts parts;
+
+ if (pgs == 0)
+ return 0;
+ GSTATE_ASSIGN_PARTS(&parts, pgs);
+ *pgs = *pfrom;
+ /* Copy the dash pattern if necessary. */
+ if (pgs->line_params.dash.pattern) {
+ int code;
+
+ pgs->line_params.dash.pattern = 0; /* force allocation */
+ code = gstate_copy_dash(pgs, pfrom);
+ if (code < 0)
+ goto fail;
+ }
+ if (pgs->client_data != 0) {
+ void *pdata = pgs->client_data = (*pgs->client_procs.alloc) (mem);
+
+ if (pdata == 0 ||
+ gstate_copy_client_data(pgs, pdata, pfrom->client_data, reason) < 0
+ )
+ goto fail;
+ }
+ 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->halftone);
+ rc_increment(pgs->dev_ht);
+ rc_increment(pgs->cie_render);
+ rc_increment(pgs->black_generation);
+ rc_increment(pgs->undercolor_removal);
+ rc_increment(pgs->cie_joint_caches);
+ rc_increment(pgs->device);
+ *parts.color_space = *pfrom->color_space;
+ *parts.ccolor = *pfrom->ccolor;
+ *parts.dev_color = *pfrom->dev_color;
+ if (reason == copy_for_gsave) {
+ float *dfrom = pfrom->line_params.dash.pattern;
+ float *dto = pgs->line_params.dash.pattern;
+
+ GSTATE_ASSIGN_PARTS(pfrom, &parts);
+ pgs->line_params.dash.pattern = dfrom;
+ pfrom->line_params.dash.pattern = dto;
+ } else {
+ GSTATE_ASSIGN_PARTS(pgs, &parts);
+ }
+ cs_adjust_counts(pgs, 1);
+ return pgs;
+ fail:
+ gs_free_object(mem, pgs->line_params.dash.pattern, cname);
+ GSTATE_ASSIGN_PARTS(pgs, &parts);
+ gstate_free_parts(pgs, mem, cname);
+ gs_free_object(mem, pgs, cname);
+ return 0;
+}
+
+/* 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;
+ gx_device_halftone *pdht = pgs->dev_ht;
+ const char *const cname = "gstate_free_contents";
+
+#define RCDECR(element)\
+ rc_decrement(pgs->element, cname)
+
+ RCDECR(device);
+ 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);
+ if (pdht != 0 && pdht->rc.ref_count == 1) {
+ /* Make sure we don't leave dangling pointers in the cache. */
+ gx_ht_cache *pcache = pgs->ht_cache;
+
+ if (pcache->order.bits == pdht->order.bits ||
+ pcache->order.levels == pdht->order.levels
+ )
+ gx_ht_clear_cache(pcache);
+ gx_device_halftone_release(pdht, pdht->rc.memory);
+ }
+ RCDECR(dev_ht);
+ RCDECR(halftone);
+ cs_adjust_counts(pgs, -1);
+ if (pgs->client_data != 0)
+ (*pgs->client_procs.free) (pgs->client_data, mem);
+ gs_free_object(mem, pgs->line_params.dash.pattern, cname);
+ gstate_free_parts(pgs, mem, cname);
+#undef RCDECR
+}
+
+/* Copy one gstate to another. */
+private int
+gstate_copy(gs_state * pto, const gs_state * pfrom,
+ gs_state_copy_reason_t reason, client_name_t cname)
+{
+ gs_state_parts parts;
+
+ GSTATE_ASSIGN_PARTS(&parts, pto);
+ /* Copy the dash pattern if necessary. */
+ if (pfrom->line_params.dash.pattern || pto->line_params.dash.pattern) {
+ int code = gstate_copy_dash(pto, pfrom);
+
+ if (code < 0)
+ return code;
+ }
+ /*
+ * 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_assign_preserve(pto->path, pfrom->path);
+ gx_cpath_assign_preserve(pto->clip_path, pfrom->clip_path);
+ /*
+ * effective_clip_shared will be copied, but we need to do the
+ * right thing with effective_clip_path.
+ */
+ if (pfrom->effective_clip_shared) {
+ /*
+ * pfrom->effective_clip_path is either pfrom->view_clip or
+ * pfrom->clip_path.
+ */
+ parts.effective_clip_path =
+ (pfrom->effective_clip_path == pfrom->view_clip ?
+ pto->view_clip : parts.clip_path);
+ } else
+ gx_cpath_assign_preserve(pto->effective_clip_path,
+ pfrom->effective_clip_path);
+ *parts.color_space = *pfrom->color_space;
+ *parts.ccolor = *pfrom->ccolor;
+ *parts.dev_color = *pfrom->dev_color;
+ cs_adjust_counts(pto, 1);
+ /* Handle references from gstate object. */
+#define RCCOPY(element)\
+ rc_pre_assign(pto->element, pfrom->element, cname)
+ RCCOPY(device);
+ 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);
+ RCCOPY(dev_ht);
+ RCCOPY(halftone);
+ {
+ struct gx_pattern_cache_s *pcache = pto->pattern_cache;
+ void *pdata = pto->client_data;
+ gs_memory_t *mem = pto->memory;
+ gs_state *saved = pto->saved;
+ float *pattern = pto->line_params.dash.pattern;
+
+ *pto = *pfrom;
+ pto->client_data = pdata;
+ pto->memory = mem;
+ pto->saved = saved;
+ pto->line_params.dash.pattern = pattern;
+ if (pto->pattern_cache == 0)
+ pto->pattern_cache = pcache;
+ if (pfrom->client_data != 0) {
+ /* We need to break 'const' here. */
+ gstate_copy_client_data((gs_state *) pfrom, pdata,
+ pfrom->client_data, reason);
+ }
+ }
+ GSTATE_ASSIGN_PARTS(pto, &parts);
+#undef RCCOPY
+ pto->show_gstate =
+ (pfrom->show_gstate == pfrom ? pto : 0);
+ return 0;
+}
diff --git a/pstoraster/gsstate.h b/pstoraster/gsstate.h
new file mode 100644
index 000000000..65641a57f
--- /dev/null
+++ b/pstoraster/gsstate.h
@@ -0,0 +1,81 @@
+/* Copyright (C) 1989, 1995, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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 *));
+int gs_gsave_for_save(P2(gs_state *, gs_state **)), gs_grestoreall_for_restore(P2(gs_state *, gs_state *));
+gs_state *gs_gstate(P1(gs_state *));
+gs_state *gs_state_copy(P2(gs_state *, gs_memory_t *));
+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"
+#include "gscsel.h"
+int gs_setscreenphase(P4(gs_state *, int, int, gs_color_select_t));
+int gs_currentscreenphase(P3(const gs_state *, gs_int_point *,
+ gs_color_select_t));
+
+#define gs_sethalftonephase(pgs, px, py)\
+ gs_setscreenphase(pgs, px, py, gs_color_select_all)
+#define gs_currenthalftonephase(pgs, ppt)\
+ gs_currentscreenphase(pgs, ppt, 0)
+int gx_imager_setscreenphase(P4(gs_imager_state *, int, int,
+ gs_color_select_t));
+
+/* Miscellaneous */
+int gs_setfilladjust(P3(gs_state *, floatp, floatp));
+int gs_currentfilladjust(P2(const gs_state *, gs_point *));
+void gs_setlimitclamp(P2(gs_state *, bool));
+bool gs_currentlimitclamp(P1(const gs_state *));
+
+#endif /* gsstate_INCLUDED */
diff --git a/pstoraster/gsstruct.h b/pstoraster/gsstruct.h
new file mode 100644
index 000000000..f1d0ba825
--- /dev/null
+++ b/pstoraster/gsstruct.h
@@ -0,0 +1,970 @@
+/* Copyright (C) 1993, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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
+
+/* Define an opaque type for the garbage collector state. */
+typedef struct gc_state_s gc_state_t;
+
+/*
+ * Define pointer types, which define how to mark the referent of the
+ * pointer.
+ */
+/*typedef struct gs_ptr_procs_s gs_ptr_procs_t;*/ /* in gsmemory.h */
+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);
+
+};
+/*typedef const gs_ptr_procs_t *gs_ptr_type_t;*/ /* in gsmemory.h */
+
+/* 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)
+
+/*
+ * Define the type for a GC root.
+ */
+/*typedef struct gs_gc_root_s gs_gc_root_t;*/ /* in gsmemory.h */
+struct gs_gc_root_s {
+ gs_gc_root_t *next;
+ gs_ptr_type_t ptype;
+ void **p;
+ bool free_on_unregister;
+};
+
+#define public_st_gc_root_t() /* in gsmemory.c */\
+ gs_public_st_ptrs1(st_gc_root_t, gs_gc_root_t, "gs_gc_root_t",\
+ gc_root_enum_ptrs, gc_root_reloc_ptrs, next)
+
+/* 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)
+
+/*
+ * We don't want to tie the allocator to using a single garbage collector,
+ * so we pass all the relevant GC procedures in to the structure pointer
+ * enumeration and relocation procedures. The GC state must begin with
+ * a pointer to the following procedure vector.
+ *
+ * By default, this is all the procedures we know about, but there are
+ * additional procedures defined in the interpreter for dealing with
+ * 'ref' objects.
+ */
+#define string_proc_reloc(proc)\
+ void proc(P2(gs_string *, gc_state_t *))
+#define const_string_proc_reloc(proc)\
+ void proc(P2(gs_const_string *, gc_state_t *))
+#define gc_procs_common\
+ /* Relocate a pointer to an object. */\
+ ptr_proc_reloc((*reloc_struct_ptr), void /*obj_header_t*/);\
+ /* Relocate a pointer to a string. */\
+ string_proc_reloc((*reloc_string));\
+ /* Relocate a pointer to a const string. */\
+ const_string_proc_reloc((*reloc_const_string))
+typedef struct gc_procs_common_s {
+ gc_procs_common;
+} gc_procs_common_t;
+
+#define gc_proc(gcst, proc) ((*(const gc_procs_common_t **)(gcst))->proc)
+
+/*
+ * The first argument of enum_ptrs procedures is logically const *.
+ * Unfortunately, actually declaring it as such would produce many compiler
+ * warnings from places that cast this argument to a non-const non-void
+ * pointer type. For the moment, we define EV_CONST as empty, with the
+ * intention of changing it to const at some future time.
+ */
+/*#define EV_CONST const */
+#define EV_CONST /* */
+
+/* Define the procedures for structure types. */
+
+ /* Clear the marks of a structure. */
+
+#define struct_proc_clear_marks(proc)\
+ void proc(P3(void /*obj_header_t*/ *pre, uint size,\
+ const gs_memory_struct_type_t *pstype))
+
+ /* Enumerate the pointers in a structure. */
+
+#define struct_proc_enum_ptrs(proc)\
+ gs_ptr_type_t proc(P6(EV_CONST void /*obj_header_t*/ *ptr, uint size,\
+ int index, const void **pep, const gs_memory_struct_type_t *pstype,\
+ gc_state_t *gcst))
+
+ /* Relocate all the pointers in this structure. */
+
+#define struct_proc_reloc_ptrs(proc)\
+ void proc(P4(void /*obj_header_t*/ *ptr, uint size,\
+ const gs_memory_struct_type_t *pstype, gc_state_t *gcst))
+
+ /*
+ * 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))
+
+/*
+ * 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 *shared;
+
+ /* ------ Procedures specific to this structure type. ------ */
+
+ struct_proc_clear_marks((*clear_marks));
+ struct_proc_enum_ptrs((*enum_ptrs));
+ struct_proc_reloc_ptrs((*reloc_ptrs));
+ struct_proc_finalize((*finalize));
+
+ /* A pointer to additional data for the above procedures. */
+
+ const void *proc_data;
+
+};
+
+#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);
+
+/* Define 'type' descriptors for some standard objects. */
+
+ /* Free blocks */
+
+extern_st(st_free);
+
+ /* Byte objects */
+
+extern_st(st_bytes);
+
+ /* GC roots */
+
+extern_st(st_gc_root_t);
+
+ /* Elements and arrays of const strings. */
+
+#define private_st_const_string()\
+ private const gc_ptr_element_t const_string_elts[] = {\
+ { GC_ELT_CONST_STRING, 0 }\
+ };\
+ gs__st_basic_with_final(private_st, st_const_string, gs_const_string,\
+ "gs_const_string", 1, const_string_elts, const_string_sdata, 0, 0, 0)
+
+extern_st(st_const_string_element);
+#define public_st_const_string_element()\
+ 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)
+
+/* ================ 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
+
+/*
+ * As an alternative to defining different enum_ptrs and reloc_ptrs
+ * procedures for basic structure types that only have a fixed number of
+ * pointers and possibly a single supertype, we can define the type's GC
+ * information using stock procedures and a table. Each entry in the table
+ * defines one element of the structure.
+ */
+
+/* Define the pointer types of individual elements. */
+
+typedef enum {
+ GC_ELT_OBJ, /* obj * or const obj * */
+ GC_ELT_STRING, /* gs_string */
+ GC_ELT_CONST_STRING, /* gs_const_string */
+ GC_ELT_REF
+} gc_ptr_type_index_t;
+
+typedef struct gc_ptr_element_s {
+ ushort /*gc_ptr_type_index_t */ type;
+ ushort offset;
+} gc_ptr_element_t;
+
+#define GC_OBJ_ELT(typ, elt)\
+ { GC_ELT_OBJ, offset_of(typ, elt) }
+#define GC_OBJ_ELT2(typ, e1, e2)\
+ GC_OBJ_ELT(typ, e1), GC_OBJ_ELT(typ, e2)
+#define GC_OBJ_ELT3(typ, e1, e2, e3)\
+ GC_OBJ_ELT(typ, e1), GC_OBJ_ELT(typ, e2), GC_OBJ_ELT(typ, e3)
+#define GC_STRING_ELT(typ, elt)\
+ { GC_ELT_STRING, offset_of(typ, elt) }
+#define GC_CONST_STRING_ELT(typ, elt)\
+ { GC_ELT_CONST_STRING, offset_of(typ, elt) }
+#define GC_REF_ELT(typ, elt)\
+ { GC_ELT_REF, offset_of(typ, elt) }
+
+/* Define the complete table of descriptor data. */
+
+typedef struct gc_struct_data_s {
+ ushort num_ptrs;
+ ushort super_offset;
+ const gs_memory_struct_type_t *super_type; /* 0 if none */
+ const gc_ptr_element_t *ptrs;
+} gc_struct_data_t;
+
+/*
+ * Define the enum_ptrs and reloc_ptrs procedures, and the declaration
+ * macros, for table-specified structures. For such structures, the
+ * proc_data points to a gc_struct_data_t. The standard defining form
+ * is:
+
+ private const gc_ptr_element_t XXX[] = {
+ ... elements ...
+ };
+ gs_(private|public)_st_basic(stname, stype, sname, XXX, YYY, supst, supoff);
+
+ */
+struct_proc_enum_ptrs(basic_enum_ptrs);
+struct_proc_reloc_ptrs(basic_reloc_ptrs);
+
+#define gs__st_basic_with_final(scope_st, stname, stype, sname, nelts, elts, sdata, supst, supoff, pfinal)\
+ private const gc_struct_data_t sdata = {\
+ nelts, supoff, supst, elts\
+ };\
+ scope_st stname = {\
+ sizeof(stype), sname, 0, 0, basic_enum_ptrs, basic_reloc_ptrs,\
+ pfinal, &sdata\
+ }
+#define gs__st_basic_final(scope_st, stname, stype, sname, elts, sdata, supst, supoff, pfinal)\
+ gs__st_basic_with_final(scope_st, stname, stype, sname, countof(elts), elts, sdata, supst, supoff, pfinal)
+#define gs_public_st_basic_final(stname, stype, sname, elts, sdata, supst, supoff, pfinal)\
+ gs__st_basic_final(public_st, stname, stype, sname, elts, sdata, supst, supoff, pfinal)
+#define gs_private_st_basic_final(stname, stype, sname, elts, sdata, supst, supoff, pfinal)\
+ gs__st_basic_final(private_st, stname, stype, sname, elts, sdata, supst, supoff, pfinal)
+#define gs__st_basic(scope_st, stname, stype, sname, elts, sdata, supst, supoff)\
+ gs__st_basic_with_final(scope_st, stname, stype, sname, countof(elts), elts, sdata, supst, supoff, 0)
+#define gs_public_st_basic(stname, stype, sname, elts, sdata, supst, supoff)\
+ gs__st_basic(public_st, stname, stype, sname, elts, sdata, supst, supoff)
+#define gs_private_st_basic(stname, stype, sname, elts, sdata, supst, supoff)\
+ gs__st_basic(private_st, stname, stype, sname, elts, sdata, supst, supoff)
+
+/*
+ * 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.
+ */
+
+ /* Begin enumeration */
+
+#ifdef __PROTOTYPES__
+# define ENUM_PTRS_BEGIN_PROC(proc)\
+ gs_ptr_type_t proc(EV_CONST void *vptr, uint size, int index, const void **pep, const gs_memory_struct_type_t *pstype, gc_state_t *gcst)
+#else
+# define ENUM_PTRS_BEGIN_PROC(proc)\
+ gs_ptr_type_t proc(vptr, size, index, pep, pstype, gcst) EV_CONST void *vptr; uint size; int index; const void **pep; const gs_memory_struct_type_t *pstype; gc_state_t *gcst;
+#endif
+#define ENUM_PTRS_BEGIN(proc)\
+ ENUM_PTRS_BEGIN_PROC(proc) { switch ( index ) { default:
+
+ /* Enumerate elements */
+
+#define ENUM_OBJ(ptr) /* pointer to object */\
+ (*pep = (const void *)(ptr), ptr_struct_type)
+#define ENUM_STRING(ptr) /* pointer to gs_string */\
+ (*pep = (const void *)(ptr), ptr_string_type)
+#define ENUM_CONST_STRING(ptr) /* pointer to gs_const_string */\
+ (*pep = (const void *)(ptr), ptr_const_string_type)
+
+#define ENUM_OBJ_ELT(typ, elt)\
+ ENUM_OBJ(((const typ *)vptr)->elt)
+#define ENUM_STRING_ELT(typ, elt)\
+ ENUM_STRING(&((const typ *)vptr)->elt)
+#define ENUM_CONST_STRING_ELT(typ, elt)\
+ ENUM_CONST_STRING(&((const typ *)vptr)->elt)
+
+#define ENUM_PTR(i, typ, elt)\
+ case i: return ENUM_OBJ_ELT(typ, elt)
+#define ENUM_PTR3(i, typ, e1, e2, e3) /* just an abbreviation */\
+ ENUM_PTR(i, typ, e1); ENUM_PTR((i)+1, typ, e2); ENUM_PTR((i)+2, typ, e3)
+#define ENUM_STRING_PTR(i, typ, elt)\
+ case i: return ENUM_STRING_ELT(typ, elt)
+#define ENUM_CONST_STRING_PTR(i, typ, elt)\
+ case i: return ENUM_CONST_STRING_ELT(typ, elt)
+
+ /* End enumeration */
+
+#define ENUM_PTRS_END\
+ } /* mustn't fall through! */ ENUM_PTRS_END_PROC }
+#define ENUM_PTRS_END_PROC /* */
+
+ /* Begin relocation */
+
+#ifdef __PROTOTYPES__
+# define RELOC_PTRS_BEGIN(proc)\
+ void proc(void *vptr, uint size, const gs_memory_struct_type_t *pstype, gc_state_t *gcst) {
+#else
+# define RELOC_PTRS_BEGIN(proc)\
+ void proc(vptr, size, pstype, gcst) void *vptr; uint size; const gs_memory_struct_type_t *pstype; gc_state_t *gcst; {
+#endif
+
+ /* Relocate elements */
+
+#define RELOC_OBJ(ptr)\
+ (gc_proc(gcst, reloc_struct_ptr)((const void *)(ptr), gcst))
+#define RELOC_OBJ_VAR(ptrvar)\
+ (ptrvar = RELOC_OBJ(ptrvar))
+#define RELOC_STRING_VAR(ptrvar)\
+ (gc_proc(gcst, reloc_string)(&(ptrvar), gcst))
+#define RELOC_CONST_STRING_VAR(ptrvar)\
+ (gc_proc(gcst, reloc_const_string)(&(ptrvar), gcst))
+
+#define RELOC_OBJ_ELT(typ, elt)\
+ RELOC_VAR(((typ *)vptr)->elt)
+#define RELOC_STRING_ELT(typ, elt)\
+ RELOC_STRING_VAR(((typ *)vptr)->elt)
+#define RELOC_CONST_STRING_ELT(typ, elt)\
+ RELOC_CONST_STRING_VAR(((typ *)vptr)->elt)
+
+/* 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_ELT(typ, elt, offset)\
+ ((typ *)vptr)->elt = (void *)\
+ ((char *)RELOC_OBJ((char *)((typ *)vptr)->elt - (offset)) +\
+ (offset))
+#define RELOC_TYPED_OFFSET_ELT(typ, elt, offset)\
+ (((typ *)vptr)->elt = (void *)RELOC_OBJ(((typ *)vptr)->elt - (offset)),\
+ ((typ *)vptr)->elt += (offset))
+
+ /* Backward compatibility */
+
+#define RELOC_VAR(ptrvar)\
+ RELOC_OBJ_VAR(ptrvar)
+#define RELOC_PTR(typ, elt)\
+ RELOC_OBJ_ELT(typ, elt)
+#define RELOC_PTR3(typ, e1, e2, e3) /* just an abbreviation */\
+ RELOC_PTR(typ,e1); RELOC_PTR(typ,e2); RELOC_PTR(typ,e3)
+#define RELOC_OFFSET_PTR(typ, elt, offset)\
+ RELOC_OFFSET_ELT(typ, elt, offset)
+#define RELOC_TYPED_OFFSET_PTR(typ, elt, offset)\
+ RELOC_TYPED_OFFSET_ELT(typ, elt, offset)
+#define RELOC_STRING_PTR(typ, elt)\
+ RELOC_STRING_ELT(typ, elt)
+#define RELOC_CONST_STRING_PTR(typ, elt)\
+ RELOC_CONST_STRING_ELT(typ, elt)
+
+ /* End relocation */
+
+#define RELOC_PTRS_END\
+ }
+
+ /* Subclass support */
+
+#define ENUM_USING(supst, ptr, size, index)\
+ (*(supst).enum_ptrs)(ptr, size, index, pep, &(supst), gcst)
+
+#define RELOC_USING(supst, ptr, size)\
+ (*(supst).reloc_ptrs)(ptr, size, &(supst), gcst)
+
+ /*
+ * Support for suffix subclasses. Special subclasses constructed
+ * 'by hand' may use this also.
+ */
+
+#define ENUM_PREFIX(supst, n)\
+ return ENUM_USING(supst, vptr, size, index-(n))
+
+#define RELOC_PREFIX(supst)\
+ RELOC_USING(supst, vptr, size)
+
+ /*
+ * Support for general subclasses.
+ */
+
+#define ENUM_SUPER_ELT(stype, supst, member, n)\
+ ENUM_USING(supst, &((EV_CONST stype *)vptr)->member, sizeof(((EV_CONST stype *)vptr)->member), index-(n))
+#define ENUM_SUPER(stype, supst, member, n)\
+ return ENUM_SUPER_ELT(stype, supst, member, n)
+
+#define RELOC_SUPER_ELT(stype, supst, member)\
+ RELOC_USING(supst, &((stype *)vptr)->member, sizeof(((stype *)vptr)->member))
+#define RELOC_SUPER(stype, supst, member)\
+ RELOC_SUPER_ELT(stype, supst, member)
+
+ /* Backward compatibility. */
+
+#define ENUM_RETURN(ptr) return ENUM_OBJ(ptr)
+#define ENUM_RETURN_PTR(typ, elt) return ENUM_OBJ_ELT(typ, elt)
+#define ENUM_RETURN_STRING_PTR(typ, elt) return ENUM_STRING_ELT(typ, elt)
+#define ENUM_RETURN_CONST_STRING(ptr) return ENUM_CONST_STRING(ptr)
+#define ENUM_RETURN_CONST_STRING_PTR(typ, elt) return ENUM_CONST_STRING_ELT(typ, elt)
+
+/* -------------- Simple structures (no internal pointers). -------------- */
+
+#define gs__st_simple(scope_st, stname, stype, sname)\
+ scope_st stname = { sizeof(stype), sname, 0, 0, gs_no_struct_enum_ptrs, gs_no_struct_reloc_ptrs, 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. ---------------- */
+
+/*
+ * Boilerplate for clear_marks procedures.
+ */
+#ifdef __PROTOTYPES__
+# define CLEAR_MARKS_PROC(proc)\
+ void proc(void *vptr, uint size, const gs_memory_struct_type_t *pstype)
+#else
+# define CLEAR_MARKS_PROC(proc)\
+ void proc(vptr, size, pstype) void *vptr; uint size; const gs_memory_struct_type_t *pstype;
+#endif
+
+ /* 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 ENUM_USING(basest, (EV_CONST char *)vptr + (index % count) * sizeof(stype),\
+ sizeof(stype), index / count);\
+ } ENUM_PTRS_END_PROC\
+ private RELOC_PTRS_BEGIN(preloc) {\
+ uint count = size / (uint)sizeof(stype);\
+ for ( ; count; count--, vptr = (char *)vptr + sizeof(stype) )\
+ RELOC_USING(basest, vptr, sizeof(stype));\
+ } 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. */
+ /* Fortunately, C's bizarre 'const' syntax does what we want here. */
+
+#define gs__st_ptr(scope_st, stname, stype, sname, penum, preloc)\
+ private ENUM_PTRS_BEGIN(penum) return 0;\
+ case 0: return ENUM_OBJ(*(stype const *)vptr);\
+ ENUM_PTRS_END\
+ private RELOC_PTRS_BEGIN(preloc) ;\
+ RELOC_VAR(*(stype *)vptr);\
+ 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 with a fixed set of pointers ----------- */
+/* Note that we "cannibalize" the penum and preloc names for elts and sdata. */
+
+ /* Structures with 1 pointer. */
+
+#define gs__st_ptrs1(scope_st, stname, stype, sname, penum, preloc, e1)\
+ private const gc_ptr_element_t penum[] = {\
+ GC_OBJ_ELT(stype, e1)\
+ };\
+ gs__st_basic(scope_st, stname, stype, sname, penum, preloc, 0, 0)
+#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 1 string. */
+
+#define gs__st_strings1(scope_st, stname, stype, sname, penum, preloc, e1)\
+ private const gc_ptr_element_t penum[] = {\
+ GC_STRING_ELT(stype, e1)\
+ };\
+ gs__st_basic(scope_st, stname, stype, sname, penum, preloc, 0, 0)
+#define gs_public_st_strings1(stname, stype, sname, penum, preloc, e1)\
+ gs__st_strings1(public_st, stname, stype, sname, penum, preloc, e1)
+#define gs_private_st_strings1(stname, stype, sname, penum, preloc, e1)\
+ gs__st_strings1(private_st, stname, stype, sname, penum, preloc, e1)
+
+ /* Structures with 1 const string. */
+
+#define gs__st_const_strings1(scope_st, stname, stype, sname, penum, preloc, e1)\
+ private const gc_ptr_element_t penum[] = {\
+ GC_CONST_STRING_ELT(stype, e1)\
+ };\
+ gs__st_basic(scope_st, stname, stype, sname, penum, preloc, 0, 0)
+#define gs_public_st_const_strings1(stname, stype, sname, penum, preloc, e1)\
+ gs__st_const_strings1(public_st, stname, stype, sname, penum, preloc, e1)
+#define gs_private_st_const_strings1(stname, stype, sname, penum, preloc, e1)\
+ gs__st_const_strings1(private_st, stname, stype, sname, penum, preloc, e1)
+
+ /* Structures with 1 pointer and 1 string. */
+
+#define gs__st_ptrs1_strings1(scope_st, stname, stype, sname, penum, preloc, e1, e2)\
+ private const gc_ptr_element_t penum[] = {\
+ GC_OBJ_ELT(stype, e1), GC_STRING_ELT(stype, e2)\
+ };\
+ gs__st_basic(scope_st, stname, stype, sname, penum, preloc, 0, 0)
+#define gs_public_st_ptrs1_strings1(stname, stype, sname, penum, preloc, e1, e2)\
+ gs__st_ptrs1_strings1(public_st, stname, stype, sname, penum, preloc, e1, e2)
+#define gs_private_st_ptrs1_strings1(stname, stype, sname, penum, preloc, e1, e2)\
+ gs__st_ptrs1_strings1(private_st, stname, stype, sname, penum, preloc, e1, e2)
+
+ /* Structures with 1 pointer and 2 strings. */
+
+#define gs__st_ptrs1_strings2(scope_st, stname, stype, sname, penum, preloc, e1, e2, e3)\
+ private const gc_ptr_element_t penum[] = {\
+ GC_OBJ_ELT(stype, e1), GC_STRING_ELT(stype, e2), GC_STRING_ELT(stype, e3)\
+ };\
+ gs__st_basic(scope_st, stname, stype, sname, penum, preloc, 0, 0)
+#define gs_public_st_ptrs1_strings2(stname, stype, sname, penum, preloc, e1, e2, e3)\
+ gs__st_ptrs1_strings2(public_st, stname, stype, sname, penum, preloc, e1, e2, e3)
+#define gs_private_st_ptrs1_strings2(stname, stype, sname, penum, preloc, e1, e2, e3)\
+ gs__st_ptrs1_strings2(private_st, stname, stype, sname, penum, preloc, e1, e2, e3)
+
+ /* Structures with 2 const strings. */
+
+#define gs__st_const_strings2(scope_st, stname, stype, sname, penum, preloc, e1, e2)\
+ private const gc_ptr_element_t penum[] = {\
+ GC_CONST_STRING_ELT(stype, e1), GC_CONST_STRING_ELT(stype, e2)\
+ };\
+ gs__st_basic(scope_st, stname, stype, sname, penum, preloc, 0, 0)
+#define gs_public_st_const_strings2(stname, stype, sname, penum, preloc, e1, e2)\
+ gs__st_const_strings2(public_st, stname, stype, sname, penum, preloc, e1, e2)
+#define gs_private_st_const_strings2(stname, stype, sname, penum, preloc, e1, e2)\
+ gs__st_const_strings2(private_st, stname, stype, sname, penum, preloc, e1, e2)
+
+ /* Structures with 2 pointers. */
+
+#define gs__st_ptrs2(scope_st, stname, stype, sname, penum, preloc, e1, e2)\
+ private const gc_ptr_element_t penum[] = {\
+ GC_OBJ_ELT2(stype, e1, e2)\
+ };\
+ gs__st_basic(scope_st, stname, stype, sname, penum, preloc, 0, 0)
+#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 const gc_ptr_element_t penum[] = {\
+ GC_OBJ_ELT3(stype, e1, e2, e3)\
+ };\
+ gs__st_basic(scope_st, stname, stype, sname, penum, preloc, 0, 0)
+#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 const gc_ptr_element_t penum[] = {\
+ GC_OBJ_ELT3(stype, e1, e2, e3), GC_OBJ_ELT(stype, e4)\
+ };\
+ gs__st_basic(scope_st, stname, stype, sname, penum, preloc, 0, 0)
+#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 const gc_ptr_element_t penum[] = {\
+ GC_OBJ_ELT3(stype, e1, e2, e3), GC_OBJ_ELT2(stype, e4, e5)\
+ };\
+ gs__st_basic(scope_st, stname, stype, sname, penum, preloc, 0, 0)
+#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)
+
+ /* Structures with 6 pointers. */
+
+#define gs__st_ptrs6(scope_st, stname, stype, sname, penum, preloc, e1, e2, e3, e4, e5, e6)\
+ private const gc_ptr_element_t penum[] = {\
+ GC_OBJ_ELT3(stype, e1, e2, e3), GC_OBJ_ELT3(stype, e4, e5, e6)\
+ };\
+ gs__st_basic(scope_st, stname, stype, sname, penum, preloc, 0, 0)
+#define gs_public_st_ptrs6(stname, stype, sname, penum, preloc, e1, e2, e3, e4, e5, e6)\
+ gs__st_ptrs6(public_st, stname, stype, sname, penum, preloc, e1, e2, e3, e4, e5, e6)
+#define gs_private_st_ptrs6(stname, stype, sname, penum, preloc, e1, e2, e3, e4, e5, e6)\
+ gs__st_ptrs6(private_st, stname, stype, sname, penum, preloc, e1, e2, e3, e4, e5, e6)
+
+/* ---------------- Suffix subclasses ---------------- */
+
+ /* Suffix subclasses with no additional pointers. */
+
+#define gs__st_suffix_add0(scope_st, stname, stype, sname, penum, preloc, supstname)\
+ gs__st_basic_with_final(scope_st, stname, stype, sname, 0, 0, preloc, &supstname, 0, 0)
+#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 with the superclass defined earlier in the same file */
+ /* as a 'basic' type. */
+ /* In this case, we don't even need new procedures. */
+
+#define gs__st_suffix_add0_local(scope_st, stname, stype, sname, supenum, supreloc, supstname)\
+ scope_st stname = {\
+ sizeof(stype), sname, 0, 0, basic_enum_ptrs, basic_reloc_ptrs,\
+ 0, &supreloc\
+ }
+#define gs_public_st_suffix_add0_local(stname, stype, sname, supenum, supreloc, supstname)\
+ gs__st_suffix_add0_local(public_st, stname, stype, sname, supenum, supreloc, supstname)
+#define gs_private_st_suffix_add0_local(stname, stype, sname, supenum, supreloc, supstname)\
+ gs__st_suffix_add0_local(private_st, stname, stype, sname, supenum, supreloc, supstname)
+
+ /* Suffix subclasses with no additional pointers and finalization. */
+ /* This is a hack -- subclasses should inherit finalization, */
+ /* but that would require a superclass 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) {\
+ ENUM_PREFIX(supstname, 0);\
+ } ENUM_PTRS_END_PROC\
+ private RELOC_PTRS_BEGIN(preloc) {\
+ RELOC_PREFIX(supstname);\
+ } 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 const gc_ptr_element_t penum[] = {\
+ GC_OBJ_ELT(stype, e1)\
+ };\
+ gs__st_basic(scope_st, stname, stype, sname, penum, preloc, &supstname, 0)
+#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. */
+ /* See above regarding finalization and subclasses. */
+
+#define gs__st_suffix_add1_final(scope_st, stname, stype, sname, penum, preloc, pfinal, supstname, e1)\
+ private const gc_ptr_element_t penum[] = {\
+ GC_OBJ_ELT(stype, e1)\
+ };\
+ gs__st_basic_final(scope_st, stname, stype, sname, penum, preloc, &supstname, 0, 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 const gc_ptr_element_t penum[] = {\
+ GC_OBJ_ELT2(stype, e1, e2)\
+ };\
+ gs__st_basic(scope_st, stname, stype, sname, penum, preloc, &supstname, 0)
+#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 2 additional pointers and finalization. */
+ /* See above regarding finalization and subclasses. */
+
+#define gs__st_suffix_add2_final(scope_st, stname, stype, sname, penum, preloc, pfinal, supstname, e1, e2)\
+ private const gc_ptr_element_t penum[] = {\
+ GC_OBJ_ELT2(stype, e1, e2)\
+ };\
+ gs__st_basic_final(scope_st, stname, stype, sname, penum, preloc, &supstname, 0, pfinal)
+#define gs_public_st_suffix_add2_final(stname, stype, sname, penum, preloc, pfinal, supstname, e1, e2)\
+ gs__st_suffix_add2_final(public_st, stname, stype, sname, penum, preloc, pfinal, supstname, e1, e2)
+#define gs_private_st_suffix_add2_final(stname, stype, sname, penum, preloc, pfinal, supstname, e1, e2)\
+ gs__st_suffix_add2_final(private_st, stname, stype, sname, penum, preloc, pfinal, 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 const gc_ptr_element_t penum[] = {\
+ GC_OBJ_ELT3(stype, e1, e2, e3)\
+ };\
+ gs__st_basic(scope_st, stname, stype, sname, penum, preloc, &supstname, 0)
+#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)
+
+ /* Suffix subclasses with 3 additional pointers and finalization. */
+ /* See above regarding finalization and subclasses. */
+
+#define gs__st_suffix_add3_final(scope_st, stname, stype, sname, penum, preloc, pfinal, supstname, e1, e2, e3)\
+ private const gc_ptr_element_t penum[] = {\
+ GC_OBJ_ELT3(stype, e1, e2, e3)\
+ };\
+ gs__st_basic_final(scope_st, stname, stype, sname, penum, preloc, &supstname, 0, pfinal)
+#define gs_public_st_suffix_add3_final(stname, stype, sname, penum, preloc, pfinal, supstname, e1, e2, e3)\
+ gs__st_suffix_add3_final(public_st, stname, stype, sname, penum, preloc, pfinal, supstname, e1, e2, e3)
+#define gs_private_st_suffix_add3_final(stname, stype, sname, penum, preloc, pfinal, supstname, e1, e2, e3)\
+ gs__st_suffix_add3_final(private_st, stname, stype, sname, penum, preloc, pfinal, supstname, e1, e2, e3)
+
+ /* Suffix subclasses with 4 additional pointers. */
+
+#define gs__st_suffix_add4(scope_st, stname, stype, sname, penum, preloc, supstname, e1, e2, e3, e4)\
+ private const gc_ptr_element_t penum[] = {\
+ GC_OBJ_ELT3(stype, e1, e2, e3), GC_OBJ_ELT(stype, e4)\
+ };\
+ gs__st_basic(scope_st, stname, stype, sname, penum, preloc, &supstname, 0)
+#define gs_public_st_suffix_add4(stname, stype, sname, penum, preloc, supstname, e1, e2, e3, e4)\
+ gs__st_suffix_add4(public_st, stname, stype, sname, penum, preloc, supstname, e1, e2, e3, e4)
+#define gs_private_st_suffix_add4(stname, stype, sname, penum, preloc, supstname, e1, e2, e3, e4)\
+ gs__st_suffix_add4(private_st, stname, stype, sname, penum, preloc, supstname, e1, e2, e3, e4)
+
+/* ---------------- General subclasses ---------------- */
+
+ /* General subclasses with no additional pointers. */
+
+#define gs__st_ptrs_add0(scope_st, stname, stype, sname, penum, preloc, supstname, member)\
+ gs__st_basic_with_final(scope_st, stname, stype, sname, 0, 0, preloc, &supstname, offset_of(stype, member), 0)
+#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 const gc_ptr_element_t penum[] = {\
+ GC_OBJ_ELT(stype, e1)\
+ };\
+ gs__st_basic(scope_st, stname, stype, sname, penum, preloc, &supstname, offset_of(stype, member))
+#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 const gc_ptr_element_t penum[] = {\
+ GC_OBJ_ELT2(stype, e1, e2)\
+ };\
+ gs__st_basic(scope_st, stname, stype, sname, penum, preloc, &supstname, offset_of(stype, member))
+#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/gstext.c b/pstoraster/gstext.c
new file mode 100644
index 000000000..9c91159c2
--- /dev/null
+++ b/pstoraster/gstext.c
@@ -0,0 +1,344 @@
+/* Copyright (C) 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Driver text interface support */
+#include "std.h"
+#include "gstypes.h"
+#include "gdebug.h"
+#include "gserror.h"
+#include "gserrors.h"
+#include "gsmemory.h"
+#include "gsstruct.h"
+#include "gstypes.h"
+#include "gxdevcli.h"
+#include "gxpath.h"
+#include "gxtext.h"
+#include "gzstate.h"
+
+/* GC descriptors */
+public_st_gs_text_params();
+public_st_gs_text_enum();
+
+#define tptr ((gs_text_params_t *)vptr)
+
+private
+ENUM_PTRS_BEGIN(text_params_enum_ptrs) return 0;
+
+case 0:
+if (tptr->operation & TEXT_FROM_STRING) {
+ /*
+ * We only need the string descriptor temporarily, but we can't
+ * put it in a local variable, because that would create a dangling
+ * pointer as soon as we return.
+ */
+ tptr->gc_string.data = tptr->data.bytes;
+ tptr->gc_string.size = tptr->size;
+ return ENUM_CONST_STRING(&tptr->gc_string);
+}
+if (tptr->operation & TEXT_FROM_BYTES)
+ return ENUM_OBJ(tptr->data.bytes);
+if (tptr->operation & TEXT_FROM_CHARS)
+ return ENUM_OBJ(tptr->data.chars);
+if (tptr->operation & TEXT_FROM_GLYPHS)
+ return ENUM_OBJ(tptr->data.glyphs);
+return ENUM_OBJ(NULL);
+case 1:
+return ENUM_OBJ(tptr->operation & TEXT_REPLACE_X_WIDTHS ?
+ tptr->x_widths : NULL);
+case 2:
+return ENUM_OBJ(tptr->operation & TEXT_REPLACE_Y_WIDTHS ?
+ tptr->y_widths : NULL);
+ENUM_PTRS_END
+
+private RELOC_PTRS_BEGIN(text_params_reloc_ptrs)
+{
+ if (tptr->operation & TEXT_FROM_STRING) {
+ gs_const_string str;
+
+ str.data = tptr->data.bytes;
+ str.size = tptr->size;
+ RELOC_CONST_STRING_VAR(str);
+ tptr->data.bytes = str.data;
+ } else if (tptr->operation & TEXT_FROM_BYTES)
+ RELOC_OBJ_VAR(tptr->data.bytes);
+ else if (tptr->operation & TEXT_FROM_CHARS)
+ RELOC_OBJ_VAR(tptr->data.chars);
+ else if (tptr->operation & TEXT_FROM_GLYPHS)
+ RELOC_OBJ_VAR(tptr->data.glyphs);
+ if (tptr->operation & TEXT_REPLACE_X_WIDTHS)
+ RELOC_OBJ_VAR(tptr->x_widths);
+ if (tptr->operation & TEXT_REPLACE_Y_WIDTHS)
+ RELOC_OBJ_VAR(tptr->y_widths);
+}
+RELOC_PTRS_END
+
+#undef tptr
+
+#define eptr ((gs_text_enum_t *)vptr)
+
+private ENUM_PTRS_BEGIN(text_enum_enum_ptrs) ENUM_USING(st_gs_text_params, &eptr->text, sizeof(eptr->text), index - 1);
+case 0:
+return ENUM_OBJ(gx_device_enum_ptr(eptr->dev));
+ENUM_PTRS_END
+
+private RELOC_PTRS_BEGIN(text_enum_reloc_ptrs)
+{
+ RELOC_USING(st_gs_text_params, &eptr->text, sizeof(eptr->text));
+ gx_device_reloc_ptr(eptr->dev, gcst);
+}
+RELOC_PTRS_END
+
+#undef eptr
+
+/* Begin processing text. */
+int
+gx_device_text_begin(gx_device * dev, gs_imager_state * pis,
+ const gs_text_params_t * text, const gs_font * font,
+ gx_path * path, /* unless DO_NONE & !RETURN_WIDTH */
+ const gx_device_color * pdcolor, /* DO_DRAW */
+ const gx_clip_path * pcpath, /* DO_DRAW */
+ gs_memory_t * mem, gs_text_enum_t ** ppte)
+{
+ if (TEXT_OPERATION_IS_INVALID(text->operation))
+ return_error(gs_error_rangecheck);
+ {
+ gx_path *tpath =
+ ((text->operation & TEXT_DO_NONE) &&
+ !(text->operation & TEXT_RETURN_WIDTH) ? 0 : path);
+ int code =
+ (*dev_proc(dev, text_begin))
+ (dev, pis, text, font, tpath,
+ (text->operation & TEXT_DO_DRAW ? pdcolor : 0),
+ (text->operation & TEXT_DO_DRAW ? pcpath : 0),
+ mem, ppte);
+ gs_text_enum_t *pte = *ppte;
+
+ if (code < 0)
+ return code;
+ pte->text = *text;
+ pte->dev = dev;
+ pte->index = 0;
+ return code;
+ }
+}
+
+/* Begin processing text based on a graphics state. */
+int
+gs_text_begin(gs_state * pgs, const gs_text_params_t * text,
+ gs_memory_t * mem, gs_text_enum_t ** ppte)
+{
+ gx_clip_path *pcpath = 0;
+
+ if (text->operation & TEXT_DO_DRAW) {
+ int code = gx_effective_clip_path(pgs, &pcpath);
+
+ if (code < 0)
+ return code;
+ gx_set_dev_color(pgs);
+ }
+ return gx_device_text_begin(pgs->device, (gs_imager_state *) pgs,
+ text, pgs->font, pgs->path, pgs->dev_color,
+ pcpath, mem, ppte);
+}
+
+/* Begin PostScript-equivalent text operations. */
+int
+gs_show_begin(gs_state * pgs, const byte * str, uint size,
+ gs_memory_t * mem, gs_text_enum_t ** ppte)
+{
+ gs_text_params_t text;
+
+ text.operation = TEXT_FROM_STRING | TEXT_DO_DRAW | TEXT_RETURN_WIDTH;
+ text.data.bytes = str, text.size = size;
+ return gs_text_begin(pgs, &text, mem, ppte);
+}
+int
+gs_ashow_begin(gs_state * pgs, floatp ax, floatp ay, const byte * str, uint size,
+ gs_memory_t * mem, gs_text_enum_t ** ppte)
+{
+ gs_text_params_t text;
+
+ text.operation = TEXT_FROM_STRING | TEXT_ADD_TO_ALL_WIDTHS |
+ TEXT_DO_DRAW | TEXT_RETURN_WIDTH;
+ text.data.bytes = str, text.size = size;
+ text.delta_all.x = ax;
+ text.delta_all.y = ay;
+ return gs_text_begin(pgs, &text, mem, ppte);
+}
+int
+gs_widthshow_begin(gs_state * pgs, floatp cx, floatp cy, gs_char chr,
+ const byte * str, uint size,
+ gs_memory_t * mem, gs_text_enum_t ** ppte)
+{
+ gs_text_params_t text;
+
+ text.operation = TEXT_FROM_STRING | TEXT_ADD_TO_SPACE_WIDTH |
+ TEXT_DO_DRAW | TEXT_RETURN_WIDTH;
+ text.data.bytes = str, text.size = size;
+ text.delta_space.x = cx;
+ text.delta_space.y = cy;
+ text.space.s_char = chr;
+ return gs_text_begin(pgs, &text, mem, ppte);
+}
+int
+gs_awidthshow_begin(gs_state * pgs, floatp cx, floatp cy, gs_char chr,
+ floatp ax, floatp ay, const byte * str, uint size,
+ gs_memory_t * mem, gs_text_enum_t ** ppte)
+{
+ gs_text_params_t text;
+
+ text.operation = TEXT_FROM_STRING |
+ TEXT_ADD_TO_ALL_WIDTHS | TEXT_ADD_TO_SPACE_WIDTH |
+ TEXT_DO_DRAW | TEXT_RETURN_WIDTH;
+ text.data.bytes = str, text.size = size;
+ text.delta_space.x = cx;
+ text.delta_space.y = cy;
+ text.space.s_char = chr;
+ text.delta_all.x = ax;
+ text.delta_all.y = ay;
+ return gs_text_begin(pgs, &text, mem, ppte);
+}
+int
+gs_kshow_begin(gs_state * pgs, const byte * str, uint size,
+ gs_memory_t * mem, gs_text_enum_t ** ppte)
+{
+ gs_text_params_t text;
+
+ text.operation = TEXT_FROM_STRING | TEXT_DO_DRAW | TEXT_INTERVENE |
+ TEXT_RETURN_WIDTH;
+ text.data.bytes = str, text.size = size;
+ return gs_text_begin(pgs, &text, mem, ppte);
+}
+int
+gs_xyshow_begin(gs_state * pgs, const byte * str, uint size,
+ const float *x_widths, const float *y_widths,
+ gs_memory_t * mem, gs_text_enum_t ** ppte)
+{
+ gs_text_params_t text;
+
+ text.operation = TEXT_FROM_STRING |
+ TEXT_REPLACE_X_WIDTHS | TEXT_REPLACE_Y_WIDTHS |
+ TEXT_DO_DRAW | TEXT_INTERVENE | TEXT_RETURN_WIDTH;
+ text.data.bytes = str, text.size = size;
+ text.x_widths = x_widths;
+ text.y_widths = y_widths;
+ return gs_text_begin(pgs, &text, mem, ppte);
+}
+int
+gs_glyphshow_begin(gs_state * pgs, gs_glyph glyph,
+ gs_memory_t * mem, gs_text_enum_t ** ppte)
+{
+ gs_text_params_t text;
+
+ /****** SET glyphs ******/
+ text.size = 1;
+ text.operation = TEXT_FROM_GLYPHS | TEXT_DO_DRAW | TEXT_RETURN_WIDTH;
+ return gs_text_begin(pgs, &text, mem, ppte);
+}
+int
+gs_cshow_begin(gs_state * pgs, const byte * str, uint size,
+ gs_memory_t * mem, gs_text_enum_t ** ppte)
+{
+ gs_text_params_t text;
+
+ text.operation = TEXT_FROM_STRING | TEXT_DO_NONE;
+ text.data.bytes = str, text.size = size;
+ return gs_text_begin(pgs, &text, mem, ppte);
+}
+int
+gs_stringwidth_begin(gs_state * pgs, const byte * str, uint size,
+ gs_memory_t * mem, gs_text_enum_t ** ppte)
+{
+ gs_text_params_t text;
+
+ text.operation = TEXT_FROM_STRING | TEXT_DO_NONE | TEXT_RETURN_WIDTH;
+ text.data.bytes = str, text.size = size;
+ return gs_text_begin(pgs, &text, mem, ppte);
+}
+int
+gs_charpath_begin(gs_state * pgs, const byte * str, uint size, bool stroke_path,
+ gs_memory_t * mem, gs_text_enum_t ** ppte)
+{
+ gs_text_params_t text;
+
+ text.operation = TEXT_FROM_STRING | TEXT_RETURN_WIDTH |
+ (stroke_path ? TEXT_DO_TRUE_CHARPATH : TEXT_DO_FALSE_CHARPATH);
+ text.data.bytes = str, text.size = size;
+ return gs_text_begin(pgs, &text, mem, ppte);
+}
+int
+gs_charboxpath_begin(gs_state * pgs, const byte * str, uint size,
+ bool stroke_path, gs_memory_t * mem, gs_text_enum_t ** ppte)
+{
+ gs_text_params_t text;
+
+ text.operation = TEXT_FROM_STRING | TEXT_RETURN_WIDTH |
+ (stroke_path ? TEXT_DO_TRUE_CHARBOXPATH : TEXT_DO_FALSE_CHARBOXPATH);
+ text.data.bytes = str, text.size = size;
+ return gs_text_begin(pgs, &text, mem, ppte);
+}
+int
+gs_glyphpath_begin(gs_state * pgs, gs_glyph glyph, bool stroke_path,
+ gs_memory_t * mem, gs_text_enum_t ** ppte)
+{
+ gs_text_params_t text;
+
+ text.operation = TEXT_FROM_GLYPHS | TEXT_RETURN_WIDTH |
+ (stroke_path ? TEXT_DO_TRUE_CHARPATH : TEXT_DO_FALSE_CHARPATH);
+ /****** SET glyphs ******/
+ text.size = 1;
+ return gs_text_begin(pgs, &text, mem, ppte);
+}
+
+/* Process text after 'begin'. */
+int
+gs_text_process(gs_text_enum_t * pte)
+{
+ return pte->procs->process(pte);
+}
+
+/* Set text metrics and optionally enable caching. */
+int
+gs_text_setcharwidth(gs_text_enum_t * pte, const double wxy[2])
+{
+ return pte->procs->set_cache(pte, wxy, TEXT_SET_CHAR_WIDTH);
+}
+int
+gs_text_setcachedevice(gs_text_enum_t * pte, const double wbox[6])
+{
+ return pte->procs->set_cache(pte, wbox, TEXT_SET_CACHE_DEVICE);
+}
+int
+gs_text_setcachedevice2(gs_text_enum_t * pte, const double wbox2[10])
+{
+ return pte->procs->set_cache(pte, wbox2, TEXT_SET_CACHE_DEVICE2);
+}
+
+/* Release the text processing structures. */
+void
+gs_text_release(gs_text_enum_t * pte, client_name_t cname)
+{
+ rc_decrement_only(pte, cname);
+}
diff --git a/pstoraster/gstext.h b/pstoraster/gstext.h
new file mode 100644
index 000000000..8dcd9211c
--- /dev/null
+++ b/pstoraster/gstext.h
@@ -0,0 +1,231 @@
+/* Copyright (C) 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Driver interface for text */
+
+#ifndef gstext_INCLUDED
+# define gstext_INCLUDED
+
+#include "gsccode.h"
+#include "gsrefct.h"
+
+/* EVERYTHING IN THIS FILE IS SUBJECT TO CHANGE WITHOUT NOTICE. */
+
+/*
+ * Note that like get_params and get_hardware_params, but unlike all other
+ * driver procedures, text display must return information to the generic
+ * code:
+ * *show except [x][y]show: the string escapement a.k.a. "width").
+ * charpath, .glyphpath: the entire character description.
+ * .charboxpath: the character bounding box.
+ */
+
+/*
+ * Define the set of possible text operations. While we define this as
+ * a bit mask for convenience in testing, only certain combinations are
+ * meaningful. Specifically, the following are errors:
+ * - No FROM or DO.
+ * The following are undefined:
+ * - More than one FROM or DO.
+ * - Both ADD_TO and REPLACE.
+ */
+#define TEXT_HAS_MORE_THAN_ONE_(op, any_)\
+ ( ((op) & any_) & (((op) & any_) - 1) )
+#define TEXT_OPERATION_IS_INVALID(op)\
+ (!((op) & TEXT_FROM_ANY_) ||\
+ !((op) & TEXT_DO_ANY_) ||\
+ TEXT_HAS_MORE_THAN_ONE_(op, TEXT_FROM_ANY_) ||\
+ TEXT_HAS_MORE_THAN_ONE_(op, TEXT_DO_ANY_) ||\
+ (((op) & TEXT_ADD_ANY_) && ((op) & TEXT_REPLACE_ANY_))\
+ )
+
+ /* Define the representation of the text itself. */
+#define TEXT_FROM_STRING 0x00001
+#define TEXT_FROM_BYTES 0x00002
+#define TEXT_FROM_CHARS 0x00004
+#define TEXT_FROM_GLYPHS 0x00008
+#define TEXT_FROM_ANY_ /* internal use only, see above */\
+ (TEXT_FROM_STRING | TEXT_FROM_BYTES | TEXT_FROM_CHARS | TEXT_FROM_GLYPHS)
+ /* Define how to compute escapements. */
+#define TEXT_ADD_TO_ALL_WIDTHS 0x00010
+#define TEXT_ADD_TO_SPACE_WIDTH 0x00020
+#define TEXT_ADD_ANY_ /* internal use only, see above */\
+ (TEXT_ADD_TO_ALL_WIDTHS | TEXT_ADD_TO_SPACE_WIDTH)
+#define TEXT_REPLACE_X_WIDTHS 0x00040
+#define TEXT_REPLACE_Y_WIDTHS 0x00080
+#define TEXT_REPLACE_ANY_ /* internal use only, see above */\
+ (TEXT_REPLACE_X_WIDTHS | TEXT_REPLACE_Y_WIDTHS)
+ /* Define what result should be produced. */
+#define TEXT_DO_NONE 0x00100 /* stringwidth or cshow only */
+#define TEXT_DO_DRAW 0x00200
+#define TEXT_DO_FALSE_CHARPATH 0x00400
+#define TEXT_DO_TRUE_CHARPATH 0x00800
+#define TEXT_DO_FALSE_CHARBOXPATH 0x01000
+#define TEXT_DO_TRUE_CHARBOXPATH 0x02000
+#define TEXT_DO_ANY_CHARPATH\
+ (TEXT_DO_FALSE_CHARPATH | TEXT_DO_TRUE_CHARPATH |\
+ TEXT_DO_FALSE_CHARBOXPATH | TEXT_DO_TRUE_CHARBOXPATH)
+#define TEXT_DO_ANY_ /* internal use only, see above */\
+ (TEXT_DO_NONE | TEXT_DO_DRAW | TEXT_DO_ANY_CHARPATH)
+ /* Define whether the client intervenes between characters. */
+#define TEXT_INTERVENE 0x10000
+ /* Define whether to return the width. */
+#define TEXT_RETURN_WIDTH 0x20000
+
+/*
+ * Define the structure of parameters passed in for text display.
+ * Note that the implementation does not modify any of these; the client
+ * must not modify them after initialization.
+ */
+typedef struct gs_text_params_s {
+ /* The client must set the following in all cases. */
+ uint operation; /* TEXT_xxx mask */
+ union sd_ {
+ const byte *bytes; /* FROM_STRING, FROM_BYTES */
+ const gs_char *chars; /* FROM_CHARS */
+ const gs_glyph *glyphs; /* FROM_GLYPHS */
+ } data;
+ uint size; /* number of data elements */
+ /* The following are used only in the indicated cases. */
+ gs_point delta_all; /* ADD_TO_ALL_WIDTHS */
+ gs_point delta_space; /* ADD_TO_SPACE_WIDTH */
+ union s_ {
+ gs_char s_char; /* ADD_TO_SPACE_WIDTH & !FROM_GLYPHS */
+ gs_glyph s_glyph; /* ADD_TO_SPACE_WIDTH & FROM_GLYPHS */
+ } space;
+ /* If x_widths == y_widths, widths are taken in pairs. */
+ /* Either one may be NULL, meaning widths = 0. */
+ const float *x_widths; /* REPLACE_X_WIDTHS */
+ const float *y_widths; /* REPLACE_Y_WIDTHS */
+ /* The following are for internal use only, not by clients. */
+ gs_const_string gc_string; /* for use only during GC */
+} gs_text_params_t;
+
+#define st_gs_text_params_max_ptrs 3
+/*extern_st(st_gs_text_params); */
+#define public_st_gs_text_params() /* in gstext.c */\
+ gs_public_st_composite(st_gs_text_params, gs_text_params_t,\
+ "gs_text_params", text_params_enum_ptrs, text_params_reloc_ptrs)
+
+/* Define the abstract type for the object procedures. */
+typedef struct gs_text_enum_procs_s gs_text_enum_procs_t;
+
+/*
+ * Define the common part of the structure that tracks the state of text
+ * display. All implementations of text_begin must allocate one of these
+ * using rc_alloc_struct_1; implementations may subclass and extend it.
+ * Note that it includes a copy of the text parameters.
+ */
+#ifndef gx_device_DEFINED
+# define gx_device_DEFINED
+typedef struct gx_device_s gx_device;
+
+#endif
+#define gs_text_enum_common\
+ /* The following are set at initialization, and const thereafter. */\
+ gs_text_params_t text;\
+ const gs_text_enum_procs_t *procs;\
+ gx_device *dev;\
+ /* The following change dynamically. */\
+ rc_header rc;\
+ uint index /* index within string */
+typedef struct gs_text_enum_s {
+ gs_text_enum_common;
+} gs_text_enum_t;
+
+#define st_gs_text_enum_max_ptrs st_gs_text_params_max_ptrs
+/*extern_st(st_gs_text_enum); */
+#define public_st_gs_text_enum() /* in gstext.c */\
+ gs_public_st_composite(st_gs_text_enum, gs_text_enum_t, "gs_text_enum_t",\
+ text_enum_enum_ptrs, text_enum_reloc_ptrs)
+
+/* Begin processing text. */
+/* Note that these take a graphics state argument. */
+#ifndef gs_state_DEFINED
+# define gs_state_DEFINED
+typedef struct gs_state_s gs_state;
+
+#endif
+int gs_text_begin(P4(gs_state * pgs, const gs_text_params_t * text,
+ gs_memory_t * mem, gs_text_enum_t ** ppenum));
+
+/* Begin the PostScript-equivalent text operators. */
+int
+ gs_show_begin(P5(gs_state *, const byte *, uint,
+ gs_memory_t *, gs_text_enum_t **)), gs_ashow_begin(P7(gs_state *, floatp, floatp, const byte *, uint,
+ gs_memory_t *, gs_text_enum_t **)),
+ gs_widthshow_begin(P8(gs_state *, floatp, floatp, gs_char,
+ const byte *, uint,
+ gs_memory_t *, gs_text_enum_t **)), gs_awidthshow_begin(P10(gs_state *, floatp, floatp, gs_char,
+ floatp, floatp, const byte *, uint,
+ gs_memory_t *, gs_text_enum_t **)),
+ gs_kshow_begin(P5(gs_state *, const byte *, uint,
+ gs_memory_t *, gs_text_enum_t **)), gs_xyshow_begin(P7(gs_state *, const byte *, uint,
+ const float *, const float *,
+ gs_memory_t *, gs_text_enum_t **)),
+ gs_glyphshow_begin(P4(gs_state *, gs_glyph,
+ gs_memory_t *, gs_text_enum_t **)), gs_cshow_begin(P5(gs_state *, const byte *, uint,
+ gs_memory_t *, gs_text_enum_t **)),
+ gs_stringwidth_begin(P5(gs_state *, const byte *, uint,
+ gs_memory_t *, gs_text_enum_t **)), gs_charpath_begin(P6(gs_state *, const byte *, uint, bool,
+ gs_memory_t *, gs_text_enum_t **)),
+ gs_glyphpath_begin(P5(gs_state *, gs_glyph, bool,
+ gs_memory_t *, gs_text_enum_t **)), gs_charboxpath_begin(P6(gs_state *, const byte *, uint, bool,
+ gs_memory_t *, gs_text_enum_t **));
+
+/*
+ * Define the possible return values from gs_text_process. The client
+ * should call text_process until it returns 0 (successful completion) or a
+ * negative (error) value.
+ */
+
+ /*
+ * The client must render a character: obtain the code from
+ * gs_show_current_char, do whatever is necessary, and then
+ * call gs_text_process again.
+ */
+#define TEXT_PROCESS_RENDER 1
+
+ /*
+ * The client has asked to intervene between characters.
+ * Obtain the previous and next codes from gs_show_previous_char
+ * and gs_kshow_next_char, do whatever is necessary, and then
+ * call gs_text_process again.
+ */
+#define TEXT_PROCESS_INTERVENE 2
+
+/* Process text after 'begin'. */
+int gs_text_process(P1(gs_text_enum_t * penum));
+
+/* Set text metrics and optionally enable caching. */
+int
+ gs_text_setcharwidth(P2(gs_text_enum_t * penum, const double wxy[2])),
+ gs_text_setcachedevice(P2(gs_text_enum_t * penum, const double wbox[6])),
+ gs_text_setcachedevice2(P2(gs_text_enum_t * penum, const double wbox2[10]));
+
+/* Release the text processing structures. */
+void gs_text_release(P2(gs_text_enum_t * penum, client_name_t cname));
+
+#endif /* gstext_INCLUDED */
diff --git a/pstoraster/gstrap.c b/pstoraster/gstrap.c
new file mode 100644
index 000000000..dfb63e833
--- /dev/null
+++ b/pstoraster/gstrap.c
@@ -0,0 +1,190 @@
+/* Copyright (C) 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Setting trapping parameters and zones */
+#include "string_.h"
+#include "gx.h"
+#include "gserrors.h"
+#include "gstrap.h"
+
+/* Parameter utilities, copied from gdevpsdf.c. */
+/* These should be merged.... */
+
+/* Compare a C string and a gs_param_string. */
+private bool
+trap_key_eq(const gs_param_string * pcs, const char *str)
+{
+ return (strlen(str) == pcs->size &&
+ !strncmp(str, (const char *)pcs->data, pcs->size));
+}
+
+/* Put an enumerated value. */
+private int
+trap_put_enum_param(gs_param_list * plist, gs_param_name param_name,
+ int *pvalue, const char *const pnames[], int ecode)
+{
+ gs_param_string ens;
+ int code = param_read_name(plist, param_name, &ens);
+
+ switch (code) {
+ case 1:
+ return ecode;
+ case 0:
+ {
+ int i;
+
+ for (i = 0; pnames[i] != 0; ++i)
+ if (trap_key_eq(&ens, pnames[i])) {
+ *pvalue = i;
+ return 0;
+ }
+ }
+ code = gs_error_rangecheck;
+ default:
+ ecode = code;
+ param_signal_error(plist, param_name, code);
+ }
+ return code;
+}
+
+/* Put a Boolean, integer, or float parameter. */
+private int
+trap_put_bool_param(gs_param_list * plist, gs_param_name param_name,
+ bool * pval, int ecode)
+{
+ int code;
+
+ switch (code = param_read_bool(plist, param_name, pval)) {
+ default:
+ ecode = code;
+ param_signal_error(plist, param_name, ecode);
+ case 0:
+ case 1:
+ break;
+ }
+ return ecode;
+}
+private int
+trap_put_int_param(gs_param_list * plist, gs_param_name param_name,
+ int *pval, int ecode)
+{
+ int code;
+
+ switch (code = param_read_int(plist, param_name, pval)) {
+ default:
+ ecode = code;
+ param_signal_error(plist, param_name, ecode);
+ case 0:
+ case 1:
+ break;
+ }
+ return ecode;
+}
+private bool
+check_unit(float *pval)
+{
+ return (*pval >= 0 && *pval <= 1);
+}
+private bool
+check_positive(float *pval)
+{
+ return (*pval > 0);
+}
+private int
+trap_put_float_param(gs_param_list * plist, gs_param_name param_name,
+ float *pval, bool(*check) (P1(float *pval)), int ecode)
+{
+ int code;
+
+ switch (code = param_read_float(plist, param_name, pval)) {
+ case 0:
+ if ((*check) (pval))
+ return 0;
+ code = gs_error_rangecheck;
+ default:
+ ecode = code;
+ param_signal_error(plist, param_name, ecode);
+ break;
+ case 1:
+ break;
+ }
+ return ecode;
+}
+
+/* settrapparams */
+int
+gs_settrapparams(gs_trap_params_t * pparams, gs_param_list * plist)
+{
+ gs_trap_params_t params;
+ int ecode = 0;
+ static const char *const trap_placement_names[] =
+ {
+ gs_trap_placement_names, 0
+ };
+
+ params = *pparams;
+ ecode = trap_put_float_param(plist, "BlackColorLimit",
+ &params.BlackColorLimit,
+ check_unit, ecode);
+ ecode = trap_put_float_param(plist, "BlackDensityLimit",
+ &params.BlackDensityLimit,
+ check_positive, ecode);
+ ecode = trap_put_float_param(plist, "BlackWidth",
+ &params.BlackWidth,
+ check_positive, ecode);
+ ecode = trap_put_bool_param(plist, "Enabled",
+ &params.Enabled, ecode);
+ ecode = trap_put_bool_param(plist, "ImageInternalTrapping",
+ &params.ImageInternalTrapping, ecode);
+ ecode = trap_put_int_param(plist, "ImageResolution",
+ &params.ImageResolution, ecode);
+ if (params.ImageResolution <= 0)
+ param_signal_error(plist, "ImageResolution",
+ ecode = gs_error_rangecheck);
+ ecode = trap_put_bool_param(plist, "ImageToObjectTrapping",
+ &params.ImageToObjectTrapping, ecode);
+ {
+ int placement = params.ImageTrapPlacement;
+
+ ecode = trap_put_enum_param(plist, "ImageTrapPlacement",
+ &placement, trap_placement_names, ecode);
+ params.ImageTrapPlacement = placement;
+ }
+ ecode = trap_put_float_param(plist, "SlidingTrapLimit",
+ &params.SlidingTrapLimit,
+ check_unit, ecode);
+ ecode = trap_put_float_param(plist, "StepLimit",
+ &params.StepLimit, check_unit, ecode);
+ ecode = trap_put_float_param(plist, "TrapColorScaling",
+ &params.TrapColorScaling,
+ check_unit, ecode);
+ ecode = trap_put_float_param(plist, "TrapWidth",
+ &params.TrapWidth,
+ check_positive, ecode);
+ if (ecode < 0)
+ return ecode;
+ *pparams = params;
+ return 0;
+}
diff --git a/pstoraster/gstrap.h b/pstoraster/gstrap.h
new file mode 100644
index 000000000..9e2a303ce
--- /dev/null
+++ b/pstoraster/gstrap.h
@@ -0,0 +1,81 @@
+/* Copyright (C) 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Definitions for trapping parameters and zones */
+
+#ifndef gstrap_INCLUDED
+# define gstrap_INCLUDED
+
+#include "gsparam.h"
+
+/* ---------------- Types and structures ---------------- */
+
+/* Opaque type for a path */
+#ifndef gx_path_DEFINED
+# define gx_path_DEFINED
+typedef struct gx_path_s gx_path;
+
+#endif
+
+/* Define the placement of image traps. */
+typedef enum {
+ tp_Center,
+ tp_Choke,
+ tp_Spread,
+ tp_Normal
+} gs_trap_placement_t;
+
+#define gs_trap_placement_names\
+ "Center", "Choke", "Spread", "Normal"
+
+/* Define a trapping parameter set. */
+typedef struct gs_trap_params_s {
+ float BlackColorLimit; /* 0-1 */
+ float BlackDensityLimit; /* > 0 */
+ float BlackWidth; /* > 0 */
+ /* ColorantZoneDetails; */
+ bool Enabled;
+ /* HalftoneName; */
+ bool ImageInternalTrapping;
+ int ImageResolution;
+ bool ImageToObjectTrapping;
+ gs_trap_placement_t ImageTrapPlacement;
+ float SlidingTrapLimit; /* 0-1 */
+ float StepLimit; /* 0-1 */
+ float TrapColorScaling; /* 0-1 */
+ float TrapWidth; /* > 0 */
+} gs_trap_params_t;
+
+/* Define a trapping zone. ****** SUBJECT TO CHANGE ****** */
+typedef struct gs_trap_zone_s {
+ gs_trap_params_t params;
+ gx_path *zone;
+} gs_trap_zone_t;
+
+/* ---------------- Procedures ---------------- */
+
+int gs_settrapparams(P2(gs_trap_params_t * params, gs_param_list * list));
+
+#endif /* gstrap_INCLUDED */
diff --git a/pstoraster/gstype1.c b/pstoraster/gstype1.c
new file mode 100644
index 000000000..4a27b3177
--- /dev/null
+++ b/pstoraster/gstype1.c
@@ -0,0 +1,562 @@
+/* Copyright (C) 1990, 1995, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Adobe Type 1 charstring interpreter */
+#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 "gzpath.h"
+#include "gxfont.h"
+#include "gxfont1.h"
+#include "gxtype1.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.
+ * We changed this from 1 to 0 in release 5.02: if it causes any
+ * problems, we'll implement a more sophisticated test.
+ */
+#define ALWAYS_DO_FLEX_AS_CURVE 0
+
+/* ------ Main interpreter ------ */
+
+/* Define a pointer to the charstring interpreter stack. */
+typedef fixed *cs_ptr;
+
+/*
+ * 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 othersubr # is stored for callothersubr.
+ */
+private int
+gs_type1_charstring_interpret(gs_type1_state * pcis,
+ const gs_const_string * str, int *pindex)
+{
+ gs_font_type1 *pfont = pcis->pfont;
+ gs_type1_data *pdata = &pfont->data;
+ bool encrypted = pdata->lenIV >= 0;
+ 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;
+ init_cstack(cstack, csp, pcis);
+
+ if (str == 0)
+ goto cont;
+ ipsp->char_string = *str;
+ cip = str->data;
+ call:state = crypt_charstring_seed;
+ if (encrypted) {
+ int skip = pdata->lenIV;
+
+ /* Skip initial random bytes */
+ for (; skip > 0; ++cip, --skip)
+ decrypt_skip_next(*cip, state);
+ }
+ goto top;
+ cont:cip = ipsp->ip;
+ state = ipsp->dstate;
+ top:for (;;) {
+ uint c0 = *cip++;
+
+ charstring_next(c0, state, c, encrypted);
+ if (c >= c_num1) {
+ /* This is a number, decode it and push it on the stack. */
+
+ if (c < c_pos2_0) { /* 1-byte number */
+ decode_push_num1(csp, c);
+ } else if (c < cx_num4) { /* 2-byte number */
+ decode_push_num2(csp, c, cip, state, encrypted);
+ } else if (c == cx_num4) { /* 4-byte number */
+ long lw;
+
+ decode_num4(lw, cip, state, encrypted);
+ *++csp = int2fixed(lw);
+ if (lw != fixed2long(*csp))
+ return_error(gs_error_rangecheck);
+ } else /* not possible */
+ return_error(gs_error_invalidfont);
+ pushed:if_debug3('1', "[1]%d: (%d) %f\n",
+ (int)(csp - cstack), c, fixed2float(*csp));
+ continue;
+ }
+#ifdef DEBUG
+ if (gs_debug['1']) {
+ static const char *const c1names[] =
+ {char1_command_names};
+
+ if (c1names[c] == 0)
+ dlprintf2("[1]0x%lx: %02x??\n", (ulong) (cip - 1), c);
+ else
+ dlprintf3("[1]0x%lx: %02x %s\n", (ulong) (cip - 1), c,
+ c1names[c]);
+ }
+#endif
+ switch ((char_command) c) {
+#define cnext clear; goto top
+#define inext goto top
+
+ /* Commands with identical functions in Type 1 and Type 2, */
+ /* except for 'escape'. */
+
+ case c_undef0:
+ case c_undef2:
+ case c_undef17:
+ return_error(gs_error_invalidfont);
+ case c_callsubr:
+ c = fixed2int_var(*csp) + pdata->subroutineNumberBias;
+ code = (*pdata->procs->subr_data)
+ (pfont, c, false, &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:
+ --ipsp;
+ goto cont;
+ case c_undoc15:
+ /* See gstype1.h for information on this opcode. */
+ cnext;
+
+ /* Commands with similar but not identical functions */
+ /* in Type 1 and Type 2 charstrings. */
+
+ case cx_hstem:
+ apply_path_hints(pcis, false);
+ type1_hstem(pcis, cs0, cs1);
+ cnext;
+ case cx_vstem:
+ apply_path_hints(pcis, false);
+ type1_vstem(pcis, cs0, cs1);
+ cnext;
+ case cx_vmoveto:
+ cs1 = cs0;
+ cs0 = 0;
+ accum_y(cs1);
+ move: /* cs0 = dx, cs1 = dy for hint checking. */
+ if ((pcis->hint_next != 0 || path_is_drawing(sppath)) &&
+ pcis->flex_count == flex_max
+ )
+ apply_path_hints(pcis, true);
+ code = gx_path_add_point(sppath, ptx, pty);
+ goto cc;
+ case cx_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 cx_hlineto:
+ accum_x(cs0);
+ cs1 = 0;
+ goto line;
+ case cx_vlineto:
+ cs1 = cs0;
+ cs0 = 0;
+ accum_y(cs1);
+ goto line;
+ case cx_rrcurveto:
+ code = gs_op1_rrcurveto(&s, cs0, cs1, cs2, cs3, cs4, cs5);
+ goto cc;
+ case cx_endchar:
+ code = gs_type1_endchar(pcis);
+ if (code == 1) {
+ /* do accent of seac */
+ spt = pcis->position;
+ ipsp = &pcis->ipstack[pcis->ips_count - 1];
+ cip = ipsp->char_string.data;
+ goto call;
+ }
+ return code;
+ case cx_rmoveto:
+ accum_xy(cs0, cs1);
+ goto move;
+ case cx_hmoveto:
+ accum_x(cs0);
+ cs1 = 0;
+ goto move;
+ case cx_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 cx_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;
+
+ /* Commands only recognized in Type 1 charstrings, */
+ /* plus 'escape'. */
+
+ case c1_closepath:
+ code = gs_op1_closepath(&s);
+ apply_path_hints(pcis, true);
+ goto cc;
+ case c1_hsbw:
+ gs_type1_sbw(pcis, cs0, fixed_0, cs1, fixed_0);
+rsbw: /* 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 {
+ /* Accumulate the side bearing now, but don't do it */
+ /* a second time for the base character of a seac. */
+ if (pcis->seac_accent < 0)
+ accum_xy(pcis->lsb.x, pcis->lsb.y);
+ pcis->position.x = ptx;
+ pcis->position.y = pty;
+ }
+ return type1_result_sbw;
+ case cx_escape:
+ charstring_next(*cip, state, c, encrypted);
+ ++cip;
+#ifdef DEBUG
+ if (gs_debug['1'] && c < char1_extended_command_count) {
+ static const char *const ce1names[] =
+ {char1_extended_command_names};
+
+ if (ce1names[c] == 0)
+ dlprintf2("[1]0x%lx: %02x??\n", (ulong) (cip - 1), c);
+ else
+ dlprintf3("[1]0x%lx: %02x %s\n", (ulong) (cip - 1), c,
+ ce1names[c]);
+ }
+#endif
+ switch ((char1_extended_command) c) {
+ case ce1_dotsection:
+ pcis->dotsection_flag ^=
+ (dotsection_in ^ dotsection_out);
+ cnext;
+ case ce1_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 ce1_hstem3:
+ apply_path_hints(pcis, false);
+ type1_hstem(pcis, cs0, cs1);
+ type1_hstem(pcis, cs2, cs3);
+ type1_hstem(pcis, cs4, cs5);
+ cnext;
+ case ce1_seac:
+ code = gs_type1_seac(pcis, cstack + 1, cstack[0],
+ ipsp);
+ if (code != 0) {
+ *pindex = ics3;
+ return code;
+ }
+ clear;
+ cip = ipsp->char_string.data;
+ goto call;
+ case ce1_sbw:
+ gs_type1_sbw(pcis, cs0, cs1, cs2, cs3);
+ goto rsbw;
+ case ce1_div:
+ csp[-1] = float2fixed((float)csp[-1] / (float)*csp);
+ --csp;
+ goto pushed;
+ case ce1_undoc15:
+ /* See gstype1.h for information on this opcode. */
+ cnext;
+ case ce1_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];
+ gs_fixed_point hpt;
+
+#endif
+
+ 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->state_flags = /* <--- sleaze */
+ pcis->flex_path_state_flags;
+#if defined(DEBUG) || !ALWAYS_DO_FLEX_AS_CURVE
+ /* 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);
+#endif
+#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_state_flags = /* <--- more sleaze */
+ sppath->state_flags;
+ 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;
+ replace_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.values[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->procs->push) (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 ce1_pop:
+ /* Check whether we're ignoring the pops after */
+ /* a known othersubr. */
+ if (pcis->ignore_pops != 0) {
+ pcis->ignore_pops--;
+ inext;
+ }
+ ++csp;
+ code = (*pdata->procs->pop) (pfont, csp);
+ if (code < 0)
+ return_error(code);
+ goto pushed;
+ case ce1_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);
+ }
+ /*NOTREACHED */
+
+ /* Fill up the dispatch up to 32. */
+
+ case_c1_undefs:
+ default: /* pacify compiler */
+ return_error(gs_error_invalidfont);
+ }
+ }
+}
+
+/* Register the interpreter. */
+void
+gs_gstype1_init(gs_memory_t * mem)
+{
+ gs_charstring_interpreter[1] = gs_type1_charstring_interpret;
+}
diff --git a/pstoraster/gstype1.h b/pstoraster/gstype1.h
new file mode 100644
index 000000000..e7c2e0c9f
--- /dev/null
+++ b/pstoraster/gstype1.h
@@ -0,0 +1,265 @@
+/* Copyright (C) 1990, 1995, 1996, 1997 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Client interface to Adobe Type 1 font routines */
+
+#ifndef gstype1_INCLUDED
+# define gstype1_INCLUDED
+
+/* ------ 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_font_type1_DEFINED
+# define gs_font_type1_DEFINED
+typedef struct gs_font_type1_s gs_font_type1;
+
+#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 othersubr # is stored for callothersubr.
+ */
+#define type1_result_sbw 1 /* allow intervention after [h]sbw */
+#define type1_result_callothersubr 2
+
+int gs_type1_interpret(P3(gs_type1_state *, const gs_const_string *, int *));
+
+/* ------ CharString number representation ------ */
+
+/* Define the representation of integers used by both Type 1 and Type 2. */
+typedef enum {
+
+ /* Values from 32 to 246 represent small integers. */
+ c_num1 = 32,
+#define c_value_num1(ch) ((int)(byte)(ch) - 139)
+
+ /* 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)
+
+} char_num_command;
+
+/* ------ Type 1 & Type 2 CharString representation ------ */
+
+/*
+ * We define both the Type 1 and Type 2 operators here, because they
+ * overlap so much.
+ */
+typedef enum {
+
+ /* Commands with identical functions in Type 1 and Type 2 */
+ /* charstrings. */
+
+ c_undef0 = 0,
+ c_undef2 = 2,
+ c_callsubr = 10,
+ c_return = 11,
+ c_undoc15 = 15, /* An obsolete and undocumented */
+ /* command used in some very old */
+ /* Adobe fonts. */
+ c_undef17 = 17,
+
+ /* Commands with similar but not identical functions */
+ /* in Type 1 and Type 2 charstrings. */
+
+ cx_hstem = 1,
+ cx_vstem = 3,
+ cx_vmoveto = 4,
+ cx_rlineto = 5,
+ cx_hlineto = 6,
+ cx_vlineto = 7,
+ cx_rrcurveto = 8,
+ cx_escape = 12, /* extends the command set */
+ cx_endchar = 14,
+ cx_rmoveto = 21,
+ cx_hmoveto = 22,
+ cx_vhcurveto = 30,
+ cx_hvcurveto = 31,
+
+ cx_num4 = 255, /* 4-byte numbers */
+
+ /* Commands recognized only in Type 1 charstrings. */
+
+ c1_closepath = 9,
+ c1_hsbw = 13,
+
+ /* Commands not recognized in Type 1 charstrings. */
+
+#define case_c1_undefs\
+ case 16: case 18: case 19:\
+ case 20: case 23: case 24:\
+ case 25: case 26: case 27: case 28: case 29
+
+ /* Commands only recognized in Type 2 charstrings. */
+
+ c2_blend = 16,
+ c2_hstemhm = 18,
+ c2_hintmask = 19,
+ c2_cntrmask = 20,
+ c2_vstemhm = 23,
+ c2_rcurveline = 24,
+ c2_rlinecurve = 25,
+ c2_vvcurveto = 26,
+ c2_hhcurveto = 27,
+ c2_shortint = 28,
+ c2_callgsubr = 29
+
+ /* Commands not recognized in Type 2 charstrings. */
+
+#define case_c2_undefs\
+ case 9: case 13
+
+} char_command;
+
+#define char1_command_names\
+ 0, "hstem", 0, "vstem", "vmoveto",\
+ "rlineto", "hlineto", "vlineto", "rrcurveto", "closepath",\
+ "callsubr", "return", "(escape)", "hsbw", "endchar",\
+ "undoc15", 0, 0, 0, 0,\
+ 0, "rmoveto", "hmoveto", 0, 0,\
+ 0, 0, 0, 0, 0,\
+ "vhcurveto", "hvcurveto"
+#define char2_command_names\
+ 0, "hstem", 0, "vstem", "vmoveto",\
+ "rlineto", "hlineto", "vlineto", "rrcurveto", 0,\
+ "callsubr", "return", "(escape)", 0, "endchar",\
+ "undoc15", "blend", 0, "hstemhm", "hintmask",\
+ "cntrmask", "rmoveto", "hmoveto", "vstemhm", "rcurveline",\
+ "rlinecurve", "vvcurveto", "hhcurveto", "shortint", "callgsubr",\
+ "vhcurveto", "hvcurveto"
+
+/*
+ * Extended (escape) commands in Type 1 charstrings.
+ */
+typedef enum {
+ ce1_dotsection = 0,
+ ce1_vstem3 = 1,
+ ce1_hstem3 = 2,
+ ce1_seac = 6,
+ ce1_sbw = 7,
+ ce1_div = 12,
+ ce1_undoc15 = 15, /* An obsolete and undocumented */
+ /* command used in some very old */
+ /* Adobe fonts. */
+ ce1_callothersubr = 16,
+ ce1_pop = 17,
+ ce1_setcurrentpoint = 33
+} char1_extended_command;
+
+#define char1_extended_command_count 34
+#define char1_extended_command_names\
+ "dotsection", "vstem3", "hstem3", 0, 0,\
+ 0, "seac", "sbw", 0, 0,\
+ 0, 0, "div", 0, 0,\
+ "undoc15", "callothersubr", "pop", 0, 0,\
+ 0, 0, 0, 0, 0,\
+ 0, 0, 0, 0, 0,\
+ 0, 0, 0, "setcurrentpoint"
+
+/*
+ * Extended (escape) commands in Type 2 charstrings.
+ */
+typedef enum {
+ ce2_and = 3,
+ ce2_or = 4,
+ ce2_not = 5,
+ ce2_store = 8,
+ ce2_abs = 9,
+ ce2_add = 10,
+ ce2_sub = 11,
+ ce2_div = 12, /* same as ce1_div */
+ ce2_load = 13,
+ ce2_neg = 14,
+ ce2_eq = 15,
+ ce2_drop = 18,
+ ce2_put = 20,
+ ce2_get = 21,
+ ce2_ifelse = 22,
+ ce2_random = 23,
+ ce2_mul = 24,
+ ce2_sqrt = 26,
+ ce2_dup = 27,
+ ce2_exch = 28,
+ ce2_index = 29,
+ ce2_roll = 30,
+ ce2_hflex = 34,
+ ce2_flex = 35,
+ ce2_hflex1 = 36,
+ ce2_flex1 = 37
+} char2_extended_command;
+
+#define char2_extended_command_count 38
+#define char2_extended_command_names\
+ 0, 0, 0, "and", "or",\
+ "not", 0, 0, "store", "abs",\
+ "add", "sub", "div", "load", "neg",\
+ "eq", 0, 0, "drop", 0,\
+ "put", "get", "ifelse", "random", "mul",\
+ 0, "sqrt", "dup", "exch", "index",\
+ "roll", 0, 0, 0, "hflex",\
+ "flex", "hflex1", "flex1"
+
+#endif /* gstype1_INCLUDED */
diff --git a/pstoraster/gstype2.c b/pstoraster/gstype2.c
new file mode 100644
index 000000000..241223fc0
--- /dev/null
+++ b/pstoraster/gstype2.c
@@ -0,0 +1,772 @@
+/* Copyright (C) 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Adobe Type 2 charstring interpreter */
+#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 "gzpath.h"
+#include "gxfont.h"
+#include "gxfont1.h"
+#include "gxtype1.h"
+
+/* NOTE: The following are not yet implemented:
+ * Registry items other than 0
+ * Counter masks (but they are parsed correctly)
+ * 'random' operator
+ */
+
+/* Define a pointer to the charstring interpreter stack. */
+typedef fixed *cs_ptr;
+
+/* ------ Internal routines ------ */
+
+/*
+ * Set the character width. This is provided as an optional extra operand
+ * on the stack for the first operator. After setting the width, we remove
+ * the extra operand, and back up the interpreter pointer so we will
+ * re-execute the operator when control re-enters the interpreter.
+ */
+#define check_first_operator(explicit_width)\
+ BEGIN\
+ if ( pcis->init_done < 0 )\
+ { ipsp->ip = cip, ipsp->dstate = state;\
+ return type2_sbw(pcis, csp, cstack, ipsp, explicit_width);\
+ }\
+ END
+private int
+type2_sbw(gs_type1_state * pcis, cs_ptr csp, cs_ptr cstack, ip_state * ipsp,
+ bool explicit_width)
+{
+ fixed wx;
+
+ if (explicit_width) {
+ wx = cstack[0] + pcis->pfont->data.nominalWidthX;
+ memmove(cstack, cstack + 1, (csp - cstack) * sizeof(*cstack));
+ --csp;
+ } else
+ wx = pcis->pfont->data.defaultWidthX;
+ gs_type1_sbw(pcis, fixed_0, fixed_0, wx, fixed_0);
+ /* Back up the interpretation pointer. */
+ {
+ ip_state *ipsp = &pcis->ipstack[pcis->ips_count - 1];
+
+ ipsp->ip--;
+ decrypt_skip_previous(*ipsp->ip, ipsp->dstate);
+ }
+ /* Save the interpreter state. */
+ pcis->os_count = csp + 1 - cstack;
+ pcis->ips_count = ipsp - &pcis->ipstack[0] + 1;
+ memcpy(pcis->ostack, cstack, pcis->os_count * sizeof(cstack[0]));
+ if (pcis->init_done < 0) { /* Finish init when we return. */
+ pcis->init_done = 0;
+ }
+ return type1_result_sbw;
+}
+private int
+type2_vstem(gs_type1_state * pcis, cs_ptr csp, cs_ptr cstack)
+{
+ fixed x = 0;
+ cs_ptr ap;
+
+ apply_path_hints(pcis, false);
+ for (ap = cstack; ap + 1 <= csp; x += ap[1], ap += 2)
+ type1_vstem(pcis, x += ap[0], ap[1]);
+ pcis->num_hints += (csp + 1 - cstack) >> 1;
+ return 0;
+}
+
+/* Enable only the hints selected by a mask. */
+private void
+enable_hints(stem_hint_table * psht, const byte * mask)
+{
+ stem_hint *table = &psht->data[0];
+ stem_hint *ph = table + psht->current;
+
+ for (ph = &table[psht->count]; --ph >= table;) {
+ ph->active = (mask[ph->index >> 3] & (0x80 >> (ph->index & 7))) != 0;
+ if_debug6('1', "[1] %s %u: %g(%g),%g(%g)\n",
+ (ph->active ? "enable" : "disable"), ph->index,
+ fixed2float(ph->v0), fixed2float(ph->dv0),
+ fixed2float(ph->v1), fixed2float(ph->dv1));
+ }
+}
+
+/* ------ Main interpreter ------ */
+
+/*
+ * Continue interpreting a Type 2 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 only for compatibility with the Type 1 charstring interpreter.
+ */
+private int
+gs_type2_charstring_interpret(gs_type1_state * pcis,
+ const gs_const_string * str, int *ignore_pindex)
+{
+ gs_font_type1 *pfont = pcis->pfont;
+ gs_type1_data *pdata = &pfont->data;
+ bool encrypted = pdata->lenIV >= 0;
+ gs_op1_state s;
+ fixed cstack[ostack_size];
+ 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;
+ cs_ptr ap;
+ bool vertical;
+ int code = 0;
+
+/****** FAKE THE REGISTRY ******/
+ struct {
+ float *values;
+ uint size;
+ } Registry[1];
+
+ Registry[0].values = pcis->pfont->data.WeightVector.values;
+
+ switch (pcis->init_done) {
+ case -1:
+ break;
+ case 0:
+ gs_type1_finish_init(pcis, &s); /* sets sfc, ptx, pty, origin */
+ break;
+ default /*case 1 */ :
+ ptx = pcis->position.x;
+ pty = pcis->position.y;
+ sfc = pcis->fc;
+ }
+ sppath = pcis->path;
+ s.pcis = pcis;
+ init_cstack(cstack, csp, pcis);
+
+ if (str == 0)
+ goto cont;
+ ipsp->char_string = *str;
+ cip = str->data;
+ call:state = crypt_charstring_seed;
+ if (encrypted) {
+ int skip = pdata->lenIV;
+
+ /* Skip initial random bytes */
+ for (; skip > 0; ++cip, --skip)
+ decrypt_skip_next(*cip, state);
+ }
+ goto top;
+ cont:cip = ipsp->ip;
+ state = ipsp->dstate;
+ top:for (;;) {
+ uint c0 = *cip++;
+
+ charstring_next(c0, state, c, encrypted);
+ if (c >= c_num1) {
+ /* This is a number, decode it and push it on the stack. */
+
+ if (c < c_pos2_0) { /* 1-byte number */
+ decode_push_num1(csp, c);
+ } else if (c < cx_num4) { /* 2-byte number */
+ decode_push_num2(csp, c, cip, state, encrypted);
+ } else if (c == cx_num4) { /* 4-byte number */
+ long lw;
+
+ decode_num4(lw, cip, state, encrypted);
+ /* 32-bit numbers are 16:16. */
+ *++csp = arith_rshift(lw, 16 - _fixed_shift);
+ } else /* not possible */
+ return_error(gs_error_invalidfont);
+ pushed:if_debug3('1', "[1]%d: (%d) %f\n",
+ (int)(csp - cstack), c, fixed2float(*csp));
+ continue;
+ }
+#ifdef DEBUG
+ if (gs_debug['1']) {
+ static const char *const c2names[] =
+ {char2_command_names};
+
+ if (c2names[c] == 0)
+ dlprintf2("[1]0x%lx: %02x??\n", (ulong) (cip - 1), c);
+ else
+ dlprintf3("[1]0x%lx: %02x %s\n", (ulong) (cip - 1), c,
+ c2names[c]);
+ }
+#endif
+ switch ((char_command) c) {
+#define cnext clear; goto top
+
+ /* Commands with identical functions in Type 1 and Type 2, */
+ /* except for 'escape'. */
+
+ case c_undef0:
+ case c_undef2:
+ case c_undef17:
+ return_error(gs_error_invalidfont);
+ case c_callsubr:
+ c = fixed2int_var(*csp) + pdata->subroutineNumberBias;
+ code = (*pdata->procs->subr_data)
+ (pfont, c, false, &ipsp[1].char_string);
+ subr:if (code < 0)
+ return_error(code);
+ --csp;
+ ipsp->ip = cip, ipsp->dstate = state;
+ ++ipsp;
+ cip = ipsp->char_string.data;
+ goto call;
+ case c_return:
+ --ipsp;
+ goto cont;
+ case c_undoc15:
+ /* See gstype1.h for information on this opcode. */
+ cnext;
+
+ /* Commands with similar but not identical functions */
+ /* in Type 1 and Type 2 charstrings. */
+
+ case cx_hstem:
+ goto hstem;
+ case cx_vstem:
+ goto vstem;
+ case cx_vmoveto:
+ check_first_operator(csp > cstack);
+ accum_y(*csp);
+ move:if ((pcis->hint_next != 0 || path_is_drawing(sppath)))
+ apply_path_hints(pcis, true);
+ code = gx_path_add_point(sppath, ptx, pty);
+ cc:if (code < 0)
+ return code;
+ goto pp;
+ case cx_rlineto:
+ for (ap = cstack; ap + 1 <= csp; ap += 2) {
+ accum_xy(ap[0], ap[1]);
+ code = gx_path_add_line(sppath, ptx, pty);
+ if (code < 0)
+ return code;
+ }
+ pp:if_debug2('1', "[1]pt=(%g,%g)\n",
+ fixed2float(ptx), fixed2float(pty));
+ cnext;
+ case cx_hlineto:
+ vertical = false;
+ goto hvl;
+ case cx_vlineto:
+ vertical = true;
+ hvl:for (ap = cstack; ap <= csp; vertical = !vertical, ++ap) {
+ if (vertical)
+ accum_y(*ap);
+ else
+ accum_x(*ap);
+ code = gx_path_add_line(sppath, ptx, pty);
+ if (code < 0)
+ return code;
+ }
+ goto pp;
+ case cx_rrcurveto:
+ for (ap = cstack; ap + 5 <= csp; ap += 6) {
+ code = gs_op1_rrcurveto(&s, ap[0], ap[1], ap[2],
+ ap[3], ap[4], ap[5]);
+ if (code < 0)
+ return code;
+ }
+ goto pp;
+ case cx_endchar:
+ /*
+ * It is an undocumented (!) feature of Type 2 CharStrings
+ * that if endchar is invoked with 4 or 5 operands, it is
+ * equivalent to the Type 1 seac operator! In this case,
+ * the asb operand of seac is missing: we assume it is
+ * the same as the l.s.b. of the accented character.
+ */
+ if (csp >= cstack + 3) {
+ check_first_operator(csp > cstack + 3);
+ code = gs_type1_seac(pcis, cstack, pcis->lsb.x, ipsp);
+ if (code < 0)
+ return code;
+ clear;
+ cip = ipsp->char_string.data;
+ goto call;
+ }
+ /*
+ * This might be the only operator in the charstring.
+ * In this case, there might be a width on the stack.
+ */
+ check_first_operator(csp >= cstack);
+ code = gs_type1_endchar(pcis);
+ if (code == 1) {
+ /*
+ * Reset the total hint count so that hintmask will
+ * parse its following data correctly.
+ * (gs_type1_endchar already reset the actual hint
+ * tables.)
+ */
+ pcis->num_hints = 0;
+ /* do accent of seac */
+ spt = pcis->position;
+ ipsp = &pcis->ipstack[pcis->ips_count - 1];
+ cip = ipsp->char_string.data;
+ goto call;
+ }
+ return code;
+ case cx_rmoveto:
+ check_first_operator(csp > cstack + 1);
+ accum_xy(csp[-1], *csp);
+ goto move;
+ case cx_hmoveto:
+ check_first_operator(csp > cstack);
+ accum_x(*csp);
+ goto move;
+ case cx_vhcurveto:
+ vertical = true;
+ goto hvc;
+ case cx_hvcurveto:
+ vertical = false;
+ hvc:for (ap = cstack; ap + 3 <= csp; vertical = !vertical, ap += 4) {
+ gs_fixed_point pt1, pt2;
+ fixed ax0 = sppath->position.x - ptx;
+ fixed ay0 = sppath->position.y - pty;
+
+ if (vertical)
+ accum_y(ap[0]);
+ else
+ accum_x(ap[0]);
+ pt1.x = ptx + ax0, pt1.y = pty + ay0;
+ accum_xy(ap[1], ap[2]);
+ pt2.x = ptx, pt2.y = pty;
+ if (vertical) {
+ if (ap + 4 == csp)
+ accum_xy(ap[3], ap[4]);
+ else
+ accum_x(ap[3]);
+ } else {
+ if (ap + 4 == csp)
+ accum_xy(ap[4], ap[3]);
+ else
+ accum_y(ap[3]);
+ }
+ code = gx_path_add_curve(sppath, pt1.x, pt1.y,
+ pt2.x, pt2.y, ptx, pty);
+ if (code < 0)
+ return code;
+ }
+ goto pp;
+
+ /***********************
+ * New Type 2 commands *
+ ***********************/
+
+ case c2_blend:
+ {
+ int n = fixed2int_var(*csp);
+ int num_values = csp - cstack;
+ gs_font_type1 *pfont = pcis->pfont;
+ int k = pfont->data.WeightVector.count;
+ int i, j;
+ cs_ptr base, deltas;
+
+ base = csp - 1 - num_values;
+ deltas = base + n - 1;
+ for (j = 0; j < n; j++, base++, deltas += k - 1)
+ for (i = 1; i < k; i++)
+ *base += deltas[i] * pfont->data.WeightVector.values[i];
+ }
+ cnext;
+ case c2_hstemhm:
+ pcis->have_hintmask = true;
+ hstem:check_first_operator(!((csp - cstack) & 1));
+ apply_path_hints(pcis, false);
+ {
+ fixed x = 0;
+
+ for (ap = cstack; ap + 1 <= csp; x += ap[1], ap += 2)
+ type1_hstem(pcis, x += ap[0], ap[1]);
+ }
+ pcis->num_hints += (csp + 1 - cstack) >> 1;
+ cnext;
+ case c2_hintmask:
+ /*
+ * A hintmask at the beginning of the CharString is
+ * equivalent to vstemhm + hintmask. For simplicity, we use
+ * this interpretation everywhere.
+ */
+ pcis->have_hintmask = true;
+ check_first_operator(!((csp - cstack) & 1));
+ type2_vstem(pcis, csp, cstack);
+ clear;
+ /* (falls through) */
+ case c2_cntrmask:
+ {
+ byte mask[max_total_stem_hints / 8];
+ int i;
+
+ if_debug3('1', "[1]mask[%d:%dv,%dh]", pcis->num_hints,
+ pcis->vstem_hints.count, pcis->hstem_hints.count);
+ for (i = 0; i < pcis->num_hints; ++cip, i += 8) {
+ charstring_next(*cip, state, mask[i >> 3], encrypted);
+ if_debug1('1', " 0x%02x", mask[i >> 3]);
+ }
+ if_debug0('1', "\n");
+ ipsp->ip = cip;
+ ipsp->dstate = state;
+ if (c == c2_cntrmask) {
+/****** NYI ******/
+ } else { /* hintmask or equivalent */
+ if_debug0('1', "[1]hstem hints:\n");
+ enable_hints(&pcis->hstem_hints, mask);
+ if_debug0('1', "[1]vstem hints:\n");
+ enable_hints(&pcis->vstem_hints, mask);
+ }
+ }
+ break;
+ case c2_vstemhm:
+ pcis->have_hintmask = true;
+ vstem:check_first_operator(!((csp - cstack) & 1));
+ type2_vstem(pcis, csp, cstack);
+ cnext;
+ case c2_rcurveline:
+ for (ap = cstack; ap + 5 <= csp; ap += 6) {
+ code = gs_op1_rrcurveto(&s, ap[0], ap[1], ap[2], ap[3],
+ ap[4], ap[5]);
+ if (code < 0)
+ return code;
+ }
+ accum_xy(ap[0], ap[1]);
+ code = gx_path_add_line(sppath, ptx, pty);
+ goto cc;
+ case c2_rlinecurve:
+ for (ap = cstack; ap + 7 <= csp; ap += 2) {
+ accum_xy(ap[0], ap[1]);
+ code = gx_path_add_line(sppath, ptx, pty);
+ if (code < 0)
+ return code;
+ }
+ code = gs_op1_rrcurveto(&s, ap[0], ap[1], ap[2], ap[3],
+ ap[4], ap[5]);
+ goto cc;
+ case c2_vvcurveto:
+ ap = cstack;
+ {
+ int n = csp + 1 - cstack;
+ fixed dxa = (n & 1 ? *ap++ : 0);
+
+ for (; ap + 3 <= csp; ap += 4) {
+ code = gs_op1_rrcurveto(&s, dxa, ap[0], ap[1], ap[2],
+ fixed_0, ap[3]);
+ if (code < 0)
+ return code;
+ dxa = 0;
+ }
+ }
+ goto pp;
+ case c2_hhcurveto:
+ ap = cstack;
+ {
+ int n = csp + 1 - cstack;
+ fixed dya = (n & 1 ? *ap++ : 0);
+
+ for (; ap + 3 <= csp; ap += 4) {
+ code = gs_op1_rrcurveto(&s, ap[0], dya, ap[1], ap[2],
+ ap[3], fixed_0);
+ if (code < 0)
+ return code;
+ dya = 0;
+ }
+ }
+ goto pp;
+ case c2_shortint:
+ {
+ int c1, c2;
+
+ charstring_next(*cip, state, c1, encrypted);
+ ++cip;
+ charstring_next(*cip, state, c2, encrypted);
+ ++cip;
+ *++csp = int2fixed((((c1 ^ 0x80) - 0x80) << 8) + c2);
+ }
+ goto pushed;
+ case c2_callgsubr:
+ c = fixed2int_var(*csp) + pdata->gsubrNumberBias;
+ code = (*pdata->procs->subr_data)
+ (pfont, c, true, &ipsp[1].char_string);
+ goto subr;
+ case cx_escape:
+ charstring_next(*cip, state, c, encrypted);
+ ++cip;
+#ifdef DEBUG
+ if (gs_debug['1'] && c < char2_extended_command_count) {
+ static const char *const ce2names[] =
+ {char2_extended_command_names};
+
+ if (ce2names[c] == 0)
+ dlprintf2("[1]0x%lx: %02x??\n", (ulong) (cip - 1), c);
+ else
+ dlprintf3("[1]0x%lx: %02x %s\n", (ulong) (cip - 1), c,
+ ce2names[c]);
+ }
+#endif
+ switch ((char2_extended_command) c) {
+ case ce2_and:
+ csp[-1] = ((csp[-1] != 0) & (*csp != 0) ? fixed_1 : 0);
+ --csp;
+ break;
+ case ce2_or:
+ csp[-1] = (csp[-1] | *csp ? fixed_1 : 0);
+ --csp;
+ break;
+ case ce2_not:
+ *csp = (*csp ? 0 : fixed_1);
+ break;
+ case ce2_store:
+ {
+ int i, n = fixed2int_var(*csp);
+ float *to = Registry[fixed2int_var(csp[-3])].values +
+ fixed2int_var(csp[-2]);
+ const fixed *from =
+ pcis->transient_array + fixed2int_var(csp[-1]);
+
+ for (i = 0; i < n; ++i)
+ to[i] = fixed2float(from[i]);
+ }
+ csp -= 4;
+ break;
+ case ce2_abs:
+ if (*csp < 0)
+ *csp = -*csp;
+ break;
+ case ce2_add:
+ csp[-1] += *csp;
+ --csp;
+ break;
+ case ce2_sub:
+ csp[-1] -= *csp;
+ --csp;
+ break;
+ case ce2_div:
+ csp[-1] = float2fixed((double)csp[-1] / *csp);
+ --csp;
+ break;
+ case ce2_load:
+ /* The specification says there is no j (starting index */
+ /* in registry array) argument.... */
+ {
+ int i, n = fixed2int_var(*csp);
+ const float *from = Registry[fixed2int_var(csp[-2])].values;
+ fixed *to =
+ pcis->transient_array + fixed2int_var(csp[-1]);
+
+ for (i = 0; i < n; ++i)
+ to[i] = float2fixed(from[i]);
+ }
+ csp -= 3;
+ break;
+ case ce2_neg:
+ *csp = -*csp;
+ break;
+ case ce2_eq:
+ csp[-1] = (csp[-1] == *csp ? fixed_1 : 0);
+ --csp;
+ break;
+ case ce2_drop:
+ --csp;
+ break;
+ case ce2_put:
+ pcis->transient_array[fixed2int_var(*csp)] = csp[-1];
+ csp -= 2;
+ break;
+ case ce2_get:
+ *csp = pcis->transient_array[fixed2int_var(*csp)];
+ break;
+ case ce2_ifelse:
+ if (csp[-1] > *csp)
+ csp[-3] = csp[-2];
+ csp -= 3;
+ break;
+ case ce2_random:
+ ++csp;
+ /****** NYI ******/
+ break;
+ case ce2_mul:
+ {
+ double prod = fixed2float(csp[-1]) * *csp;
+
+ csp[-1] =
+ (prod > max_fixed ? max_fixed :
+ prod < min_fixed ? min_fixed : prod);
+ }
+ --csp;
+ break;
+ case ce2_sqrt:
+ if (*csp >= 0)
+ *csp = float2fixed(sqrt(fixed2float(*csp)));
+ break;
+ case ce2_dup:
+ csp[1] = *csp;
+ ++csp;
+ break;
+ case ce2_exch:
+ {
+ fixed top = *csp;
+
+ *csp = csp[-1], csp[-1] = top;
+ }
+ break;
+ case ce2_index:
+ *csp =
+ (*csp < 0 ? csp[-1] : csp[-1 - fixed2int_var(csp[-1])]);
+ break;
+ case ce2_roll:
+ {
+ int distance = fixed2int_var(*csp);
+ int count = fixed2int_var(csp[-1]);
+ cs_ptr bot;
+
+ csp -= 2;
+ if (count < 0 || count > csp + 1 - cstack)
+ return_error(gs_error_invalidfont);
+ if (count == 0)
+ break;
+ if (distance < 0)
+ distance = count - (-distance % count);
+ bot = csp + 1 - count;
+ while (--distance >= 0) {
+ fixed top = *csp;
+
+ memmove(bot + 1, bot,
+ (count - 1) * sizeof(fixed));
+ *bot = top;
+ }
+ }
+ break;
+ case ce2_hflex:
+ csp[6] = fixed_half; /* fd/100 */
+ csp[4] = *csp, csp[5] = 0; /* dx6, dy6 */
+ csp[2] = csp[-1], csp[3] = -csp[-5]; /* dx5, dy5 */
+ *csp = csp[-2], csp[1] = 0; /* dx4, dy4 */
+ csp[-2] = csp[-3], csp[-1] = 0; /* dx3, dy3 */
+ csp[-3] = csp[-4], csp[-4] = csp[-5]; /* dx2, dy2 */
+ csp[-5] = 0; /* dy1 */
+ csp += 6;
+ goto flex;
+ case ce2_flex:
+ *csp /= 100; /* fd/100 */
+flex: {
+ fixed x_join = csp[-12] + csp[-10] + csp[-8];
+ fixed y_join = csp[-11] + csp[-9] + csp[-7];
+ fixed x_end = x_join + csp[-6] + csp[-4] + csp[-2];
+ fixed y_end = y_join + csp[-5] + csp[-3] + csp[-1];
+ gs_point join, end;
+ double flex_depth;
+
+ if ((code =
+ gs_distance_transform(fixed2float(x_join),
+ fixed2float(y_join),
+ &ctm_only(pcis->pis),
+ &join)) < 0 ||
+ (code =
+ gs_distance_transform(fixed2float(x_end),
+ fixed2float(y_end),
+ &ctm_only(pcis->pis),
+ &end)) < 0
+ )
+ return code;
+ /*
+ * Use the X or Y distance depending on whether
+ * the curve is more horizontal or more
+ * vertical.
+ */
+ if (any_abs(end.y) > any_abs(end.x))
+ flex_depth = join.x;
+ else
+ flex_depth = join.y;
+ if (fabs(flex_depth) < fixed2float(*csp)) {
+ /* Do flex as line. */
+ accum_xy(x_end, y_end);
+ code = gx_path_add_line(sppath, ptx, pty);
+ } else {
+ /*
+ * Do flex as curve. We can't jump to rrc,
+ * because the flex operators don't clear
+ * the stack (!).
+ */
+ code = gs_op1_rrcurveto(&s,
+ csp[-12], csp[-11], csp[-10],
+ csp[-9], csp[-8], csp[-7]);
+ if (code < 0)
+ return code;
+ code = gs_op1_rrcurveto(&s,
+ csp[-6], csp[-5], csp[-4],
+ csp[-3], csp[-2], csp[-1]);
+ }
+ if (code < 0)
+ return code;
+ csp -= 13;
+ }
+ cnext;
+ case ce2_hflex1:
+ csp[4] = fixed_half; /* fd/100 */
+ csp[2] = *csp, csp[3] = 0; /* dx6, dy6 */
+ *csp = csp[-2], csp[1] = csp[-1]; /* dx5, dy5 */
+ csp[-2] = csp[-3], csp[-1] = 0; /* dx4, dy4 */
+ csp[-3] = 0; /* dy3 */
+ csp += 4;
+ goto flex;
+ case ce2_flex1:
+ {
+ fixed dx = csp[-10] + csp[-8] + csp[-6] + csp[-4] + csp[-2];
+ fixed dy = csp[-9] + csp[-7] + csp[-5] + csp[-3] + csp[-1];
+
+ if (any_abs(dx) > any_abs(dy))
+ csp[1] = -dy; /* d6 is dx6 */
+ else
+ csp[1] = *csp, *csp = -dx; /* d6 is dy6 */
+ }
+ csp[2] = fixed_half; /* fd/100 */
+ csp += 2;
+ goto flex;
+ }
+ break;
+
+ /* Fill up the dispatch up to 32. */
+
+ case_c2_undefs:
+ default: /* pacify compiler */
+ return_error(gs_error_invalidfont);
+ }
+ }
+}
+
+/* Register the interpreter. */
+void
+gs_gstype2_init(gs_memory_t * mem)
+{
+ gs_charstring_interpreter[2] = gs_type2_charstring_interpret;
+}
diff --git a/pstoraster/gstype42.c b/pstoraster/gstype42.c
new file mode 100644
index 000000000..ab8add737
--- /dev/null
+++ b/pstoraster/gstype42.c
@@ -0,0 +1,480 @@
+/* Copyright (C) 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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)\
+ BEGIN\
+ code = (*string_proc)(pfont, (ulong)(base), length, &vptr);\
+ if ( code < 0 ) return code;\
+ END
+
+/* Get 2- or 4-byte quantities from a table. */
+#define u8(p) ((uint)((p)[0]))
+#define s8(p) (int)((u8(p) ^ 0x80) - 0x80)
+#define u16(p) (((uint)((p)[0]) << 8) + (p)[1])
+#define s16(p) (int)((u16(p) ^ 0x8000) - 0x8000)
+#define u32(p) (((ulong)u16(p) << 16) + u16((p) + 2))
+#define s32(p) (long)((u32(p) ^ 0x80000000) - 0x80000000)
+
+/* Define the default implementation for getting the outline data for */
+/* a glyph, using indexToLocFormat and the loca and glyf tables. */
+/* Set pglyph->data = 0 if the glyph is empty. */
+private int
+default_get_outline(gs_font_type42 * pfont, uint glyph_index,
+ gs_const_string * 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;
+
+ /*
+ * We can't assume that consecutive loca entries are stored
+ * contiguously in memory: we have to access each entry
+ * individually.
+ */
+ if (pfont->data.indexToLocFormat) {
+ access(pfont->data.loca + glyph_index * 4, 4, ploca);
+ glyph_start = u32(ploca);
+ access(pfont->data.loca + glyph_index * 4 + 4, 4, ploca);
+ glyph_length = u32(ploca) - glyph_start;
+ } else {
+ access(pfont->data.loca + glyph_index * 2, 2, ploca);
+ glyph_start = (ulong) u16(ploca) << 1;
+ access(pfont->data.loca + glyph_index * 2 + 2, 2, ploca);
+ glyph_length = ((ulong) u16(ploca) << 1) - glyph_start;
+ }
+ pglyph->size = glyph_length;
+ if (glyph_length == 0)
+ pglyph->data = 0;
+ else
+ access(pfont->data.glyf + glyph_start, glyph_length, pglyph->data);
+ return 0;
+}
+
+/* Initialize the cached values in a Type 42 font. */
+/* Note that this initializes get_outline as well. */
+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);
+ /* Clear optional entries. */
+ pfont->data.numLongMetrics = 0;
+ 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. Since the Adobe PostScript driver sometimes
+ * outputs garbage FontBBox values, we use a "reasonableness" check
+ * here.
+ */
+ if (pfont->FontBBox.p.x >= pfont->FontBBox.q.x ||
+ pfont->FontBBox.p.y >= pfont->FontBBox.q.y ||
+ pfont->FontBBox.p.x < -0.5 || pfont->FontBBox.p.x > 0.5 ||
+ pfont->FontBBox.p.y < -0.5 || pfont->FontBBox.p.y > 0.5
+ ) {
+ 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;
+ }
+ pfont->data.get_outline = default_get_outline;
+ 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)
+{
+ float sbw[4];
+
+ gs_type42_get_metrics(pfont, glyph_index, sbw);
+ /*
+ * 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);
+}
+
+/* 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;
+ const byte *pflags;
+ uint npoints;
+ const byte *pxc, *pyc;
+ int code;
+
+ if (numContours == 0)
+ return 0;
+ /*
+ * 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 = pinstr + 2 + u16(pinstr);
+ uint xbytes = npoints = u16(pinstr - 2) + 1;
+ 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[1].xy + pt.xy) / 2)
+#define control4off(xy) ((cpoints[0].xy + 2 * cpoints[1].xy) / 3)
+#define control5off(xy) ((2 * cpoints[1].xy + cpoints[2].xy) / 3)
+#define control6off(xy) ((2 * cpoints[1].xy + pt.xy) / 3)
+#define control7off(xy) ((2 * cpoints[1].xy + start.xy) / 3)
+ 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, control4off(x),
+ control4off(y), control6off(x),
+ control6off(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: /* >= 1 */
+ control2(x) = control3off(x);
+ control2(y) = control3off(y);
+ code = gx_path_add_curve(ppath,
+ control4off(x), control4off(y),
+ control5off(x), control5off(y),
+ control2(x), control2(y));
+ cpoints[0] = cpoints[2];
+ off_curve = 1;
+ /* falls through */
+ case 0:
+ cpoints[1] = pt;
+ }
+ }
+ if (code < 0)
+ return code;
+ }
+ if (off_curve)
+ code = gx_path_add_curve(ppath, control4off(x), control4off(y),
+ control7off(x), control7off(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)
+{
+ gs_const_string glyph_string;
+
+#define glyph glyph_string.data
+ int numContours;
+ int code;
+
+ code = (*pfont->data.get_outline) (pfont, glyph_index, &glyph_string);
+ if (code < 0)
+ return code;
+ if (glyph == 0 || glyph_string.size == 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 = s8(glyph), arg2 = s8(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);
+ }
+#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;
+ } else
+ goto no_scale;
+#undef s2_14
+ scale_mat.tx = 0;
+ scale_mat.ty = 0;
+ /* 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);
+ no_scale:code = append_outline(comp_index, &mat, ppath, pfont);
+ if (code < 0)
+ return code;
+ }
+ while (flags & cg_moreComponents);
+ }
+ return 0;
+#undef glyph
+}
diff --git a/pstoraster/gstypes.h b/pstoraster/gstypes.h
new file mode 100644
index 000000000..8adcb1773
--- /dev/null
+++ b/pstoraster/gstypes.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 supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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..8c6ceb5f4
--- /dev/null
+++ b/pstoraster/gsuid.h
@@ -0,0 +1,78 @@
+/* Copyright (C) 1992, 1993, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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.
+ * Since we sometimes use gs_ids as UniqueIDs, we want to choose as large
+ * a (positive) value as possible for no_UniqueID.
+ */
+#define no_UniqueID max_long
+#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 = -(long)(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..899ab1369
--- /dev/null
+++ b/pstoraster/gsutil.c
@@ -0,0 +1,285 @@
+/* Copyright (C) 1992, 1993, 1994, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Utilities for Ghostscript library */
+#include "string_.h"
+#include "memory_.h"
+#include "gstypes.h"
+#include "gconfigv.h" /* for USE_ASM */
+#include "gsmemory.h" /* for init procedure */
+#include "gsrect.h" /* for prototypes */
+#include "gsuid.h"
+#include "gsutil.h" /* for prototypes */
+
+/* ------ Unique IDs ------ */
+
+/* Generate a block of unique IDs. */
+static ulong gs_next_id;
+
+void
+gs_gsutil_init(gs_memory_t *mem)
+{
+ gs_next_id = 1;
+}
+
+ulong
+gs_next_ids(uint count)
+{
+ ulong 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));
+}
+
+/* ------ Rectangle utilities ------ */
+
+/*
+ * Calculate the difference of two rectangles, a list of up to 4 rectangles.
+ * Return the number of rectangles in the list, and set the first rectangle
+ * to the intersection.
+ */
+int
+int_rect_difference(gs_int_rect * outer, const gs_int_rect * inner,
+ gs_int_rect * diffs /*[4] */ )
+{
+ int x0 = outer->p.x, y0 = outer->p.y;
+ int x1 = outer->q.x, y1 = outer->q.y;
+ int count = 0;
+
+ if (y0 < inner->p.y) {
+ diffs[0].p.x = x0, diffs[0].p.y = y0;
+ diffs[0].q.x = x1, diffs[0].q.y = min(y1, inner->p.y);
+ outer->p.y = y0 = diffs[0].q.y;
+ ++count;
+ }
+ if (y1 > inner->q.y) {
+ diffs[count].p.x = x0, diffs[count].p.y = max(y0, inner->q.y);
+ diffs[count].q.x = x1, diffs[count].q.y = y1;
+ outer->q.y = y1 = diffs[count].p.y;
+ ++count;
+ }
+ if (x0 < inner->p.x) {
+ diffs[0].p.x = x0, diffs[0].p.y = y0;
+ diffs[0].q.x = min(x1, inner->p.x), diffs[0].q.y = y1;
+ outer->p.x = x0 = diffs[count].q.x;
+ ++count;
+ }
+ if (x1 > inner->q.x) {
+ diffs[count].p.x = max(x0, inner->q.x), diffs[count].p.y = y0;
+ diffs[count].q.x = x1, diffs[count].q.y = y1;
+ outer->q.x = x1 = diffs[count].p.x;
+ ++count;
+ }
+ return count;
+}
diff --git a/pstoraster/gsutil.h b/pstoraster/gsutil.h
new file mode 100644
index 000000000..756be1f7f
--- /dev/null
+++ b/pstoraster/gsutil.h
@@ -0,0 +1,67 @@
+/* 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 supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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..1968a4f98
--- /dev/null
+++ b/pstoraster/gsxfont.h
@@ -0,0 +1,45 @@
+/* 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 supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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..42ca7efb9
--- /dev/null
+++ b/pstoraster/gx.h
@@ -0,0 +1,52 @@
+/* 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 supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Common internal definitions for Ghostscript library */
+
+#ifndef gx_INCLUDED
+# define gx_INCLUDED
+
+#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
+
+#endif /* gx_INCLUDED */
diff --git a/pstoraster/gxacpath.c b/pstoraster/gxacpath.c
new file mode 100644
index 000000000..9f0b55c81
--- /dev/null
+++ b/pstoraster/gxacpath.c
@@ -0,0 +1,479 @@
+/* Copyright (C) 1993, 1995, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Accumulator for clipping paths */
+#include "gx.h"
+#include "gserrors.h"
+#include "gsrop.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,
+ NULL,
+ NULL,
+ accum_close,
+ NULL,
+ NULL,
+ 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,
+ NULL,
+ NULL,
+ gx_get_largest_clipping_box,
+ gx_default_begin_typed_image,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ gx_default_text_begin
+ }
+};
+
+/* Start accumulating a clipping path. */
+void
+gx_cpath_accum_begin(gx_device_cpath_accum * padev, gs_memory_t * mem)
+{
+ gx_device_init((gx_device *) padev,
+ (const gx_device *) & gs_cpath_accum_device,
+ NULL /* allocated on stack */ , true);
+ padev->list_memory = mem;
+ (*dev_proc(padev, open_device)) ((gx_device *) padev);
+}
+
+void
+gx_cpath_accum_set_cbox(gx_device_cpath_accum * padev,
+ const gs_fixed_rect * pbox)
+{
+ padev->clip_box.p.x = fixed2int_var(pbox->p.x);
+ padev->clip_box.p.y = fixed2int_var(pbox->p.y);
+ padev->clip_box.q.x = fixed2int_var_ceiling(pbox->q.x);
+ padev->clip_box.q.y = fixed2int_var_ceiling(pbox->q.y);
+}
+
+/* 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);
+ /* Make an entire clipping path so we can use cpath_assign. */
+ gx_clip_path apath;
+
+ if (code < 0)
+ return code;
+ gx_cpath_init_local(&apath, padev->list_memory);
+ apath.rect_list->list = padev->list;
+ apath.path.bbox.p.x = int2fixed(padev->bbox.p.x);
+ apath.path.bbox.p.y = int2fixed(padev->bbox.p.y);
+ apath.path.bbox.q.x = int2fixed(padev->bbox.q.x);
+ apath.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. */
+ apath.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 apath.inner_box correctly. */
+ if (clip_list_is_rectangle(&padev->list))
+ apath.inner_box = apath.path.bbox;
+ else {
+ /* The quick check must fail. */
+ apath.inner_box.p.x = apath.inner_box.p.y = 0;
+ apath.inner_box.q.x = apath.inner_box.q.y = 0;
+ }
+ gx_cpath_set_outer_box(&apath);
+ apath.path_valid = false;
+ apath.id = gs_next_ids(1); /* path changed => change id */
+ gx_cpath_assign_free(pcpath, &apath);
+ 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 = gx_cpath_is_outside(pcpath);
+ gs_logical_operation_t save_lop = gs_current_logical_op(pgs);
+ 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 */
+ gs_set_logical_op(pgs, lop_default);
+ 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);
+ gx_cpath_set_outside(pcpath, outside);
+ gs_set_logical_op(pgs, save_lop);
+ return code;
+}
+
+/* ------ Device implementation ------ */
+
+/* Initialize the accumulation device. */
+private int
+accum_open(register gx_device * dev)
+{
+ gx_device_cpath_accum * const adev = (gx_device_cpath_accum *)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;
+ adev->clip_box.p.x = adev->clip_box.p.y = min_int;
+ adev->clip_box.q.x = adev->clip_box.q.y = max_int;
+ return 0;
+}
+
+/* Close the accumulation device. */
+private int
+accum_close(gx_device * dev)
+{
+#ifdef DEBUG
+ gx_device_cpath_accum * const adev = (gx_device_cpath_accum *)dev;
+
+ if (gs_debug_c('q')) {
+ gx_clip_rect *rp =
+ (adev->list.count <= 1 ? &adev->list.single : adev->list.head);
+
+ dlprintf4("[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_fill_rectangle(gx_device * dev, int x, int y, int w, int h,
+ gx_color_index color)
+{
+ gx_device_cpath_accum * const adev = (gx_device_cpath_accum *)dev;
+ int xe = x + w, ye = y + h;
+ gx_clip_rect *nr;
+ gx_clip_rect *ar;
+ register gx_clip_rect *rptr;
+ int ymin, ymax;
+
+ /* Clip the rectangle being added. */
+ if (y < adev->clip_box.p.y)
+ y = adev->clip_box.p.y;
+ if (ye > adev->clip_box.q.y)
+ ye = adev->clip_box.q.y;
+ if (y >= ye)
+ return 0;
+ if (x < adev->clip_box.p.x)
+ x = adev->clip_box.p.x;
+ if (xe > adev->clip_box.q.x)
+ xe = adev->clip_box.q.x;
+ if (x >= xe)
+ return 0;
+
+ /* 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;
+
+ 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 */
+
+ {
+ 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;
+ }
+ }
+ 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;
+}
diff --git a/pstoraster/gxalloc.h b/pstoraster/gxalloc.h
new file mode 100644
index 000000000..0a3e6c7c0
--- /dev/null
+++ b/pstoraster/gxalloc.h
@@ -0,0 +1,403 @@
+/* Copyright (C) 1995, 1996, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Requires gsmemory.h, gsstruct.h */
+
+#ifndef gxalloc_INCLUDED
+# define gxalloc_INCLUDED
+
+#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 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.
+ */
+
+/*
+ * 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.
+ * In order to put a reasonable upper bound on the scanning time, we
+ * limit the length of the objects that contain runs of refs.
+ */
+#define max_size_st_refs (50 * sizeof(ref))
+
+/*
+ * 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; */
+ /* csbase is an alias for chead */
+#define csbase(cp) ((byte *)(cp)->chead)
+ /* 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 */
+ /*
+ * Free lists for single bytes in blocks of 1-3 bytes,
+ * one per 256 bytes in [csbase..climit). The chain
+ * pointer is a (1-byte) self-relative offset,
+ * terminated by a 0; obviously, the chain is sorted by
+ * increasing address. The free list pointers themselves
+ * are offsets relative to csbase.
+ *
+ * Note that these lists overlay the GC relocation table.
+ */
+ ushort *sfree1;
+ /*
+ * Free list for blocks of >= 4 bytes. Each block begins
+ * with a 2-byte size and a 2-byte next block pointer,
+ * both big-endian. This too is sorted in increasing address order.
+ */
+ ushort sfree;
+ /* 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 *));
+
+/* Initialize the string freelists in a chunk. */
+void alloc_init_free_strings(P1(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;
+
+/* Stream structure, only needed for the streams member of the state. */
+#ifndef stream_DEFINED
+# define stream_DEFINED
+typedef struct stream_s stream;
+
+#endif
+
+/* 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_raw_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. */
+ bool is_controlled; /* if true, this allocator doesn't manage */
+ /* its own chunks */
+ 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 */
+ struct lost_ { /* space freed and 'lost' */
+ ulong objects;
+ ulong refs;
+ ulong strings;
+ } lost;
+ /*
+ * The following are for the interpreter's convenience: the
+ * library initializes them to 0 and then never touches them.
+ */
+ stream *streams;
+ /* 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 gsalloc.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 3 /* streams, changes, saved */
+
+/* Define the procedures for the standard allocator. */
+/* We export this for subclasses. */
+extern const gs_memory_procs_t gs_ref_memory_procs;
+
+/*
+ * 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\
+ }\
+ }
+
+#endif /* gxalloc_INCLUDED */
diff --git a/pstoraster/gxalpha.h b/pstoraster/gxalpha.h
new file mode 100644
index 000000000..c5d444e31
--- /dev/null
+++ b/pstoraster/gxalpha.h
@@ -0,0 +1,74 @@
+/* Copyright (C) 1997 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Internal machinery for alpha channel support */
+
+#ifndef gxalpha_INCLUDED
+# define gxalpha_INCLUDED
+
+/*
+ * As discussed in the classic Porter & Duff paper on compositing,
+ * supporting alpha channel properly involves premultiplying color values
+ * that are associated with non-unity alpha values. After considerable
+ * thrashing around trying to read between the lines of the spotty NeXT
+ * documentation, we've concluded that the correct approach is to
+ * premultiply towards whatever the color value 0 represents in the device's
+ * native color space: black for DeviceGray and DeviceRGB (displays and some
+ * file formats), white for DeviceCMYK (color printers), with a special hack
+ * for monochrome printers TBD. This makes things very easy internally, at
+ * the expense of some inconsistency at the boundaries.
+ *
+ * For the record, the only places apparently affected by this decision
+ * are the following:
+ * - alphaimage, if it doesn't assume premultiplication (see below)
+ * - readimage
+ * - The cmap_rgb_alpha_ procedures in gxcmap.c
+ * - [color]image, if they are supposed to use currentalpha (see below)
+ * - The compositing code in gsalphac.c
+ *
+ * The NeXT documentation also is very unclear as to how readimage,
+ * alphaimage, and [color]image are supposed to work. Our current
+ * interpretation is the following:
+ *
+ * - readimage reads pixels exactly as the device stores them
+ * (converted into DeviceGray or DeviceRGB space if the device
+ * uses a palette). Pixels with non-unity alpha come out
+ * premultiplied, however the device stores them.
+ *
+ * - alphaimage assumes the pixels are premultiplied as appropriate
+ * for the relevant color space. This makes alphaimage and
+ * readimage complementary, i.e., the output of readimage is
+ * suitable as the input of alphaimage.
+ *
+ * - [color]image disregard currentalpha, and treat all input as
+ * opaque (alpha = 1). */
+/*
+ * Just in case we ever change our minds about the direction of
+ * premultiplication, uncommenting the following preprocessor definition is
+ * supposed to produce premultiplication towards white.
+ */
+/*#define PREMULTIPLY_TOWARDS_WHITE */
+
+#endif /* gxalpha_INCLUDED */
diff --git a/pstoraster/gxarith.h b/pstoraster/gxarith.h
new file mode 100644
index 000000000..69a6eace6
--- /dev/null
+++ b/pstoraster/gxarith.h
@@ -0,0 +1,84 @@
+/* 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 supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+#ifndef gxarith_INCLUDED
+# define gxarith_INCLUDED
+
+/*$Id$ */
+/* 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/gxband.h b/pstoraster/gxband.h
new file mode 100644
index 000000000..3fc9c7322
--- /dev/null
+++ b/pstoraster/gxband.h
@@ -0,0 +1,70 @@
+/* Copyright (C) 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Band-processing parameters for Ghostscript */
+
+#ifndef gxband_INCLUDED
+# define gxband_INCLUDED
+
+#include "gxclio.h"
+
+/*
+ * Define the parameters controlling banding.
+ */
+typedef struct gx_band_params_s {
+ int BandWidth; /* (optional) band width in pixels */
+ int BandHeight; /* (optional) */
+ long BandBufferSpace; /* (optional) */
+} gx_band_params;
+
+#define band_params_initial_values 0, 0, 0
+
+/*
+ * Define the information for a saved page.
+ */
+typedef struct gx_band_page_info_s {
+ char cfname[gp_file_name_sizeof]; /* command file name */
+ clist_file_ptr cfile; /* command file, normally 0 */
+ char bfname[gp_file_name_sizeof]; /* block file name */
+ clist_file_ptr bfile; /* block file, normally 0 */
+ uint tile_cache_size; /* size of tile cache */
+ long bfile_end_pos; /* ftell at end of bfile */
+ gx_band_params band_params; /* parameters used when writing band list */
+ /* (actual values, no 0s) */
+} gx_band_page_info;
+
+/*
+ * By convention, the structure member containing the above is called
+ * page_info. Define shorthand accessors for its members.
+ */
+#define page_cfile page_info.cfile
+#define page_cfname page_info.cfname
+#define page_bfile page_info.bfile
+#define page_bfname page_info.bfname
+#define page_tile_cache_size page_info.tile_cache_size
+#define page_bfile_end_pos page_info.bfile_end_pos
+#define page_band_height page_info.band_params.BandHeight
+
+#endif /* ndef gxband_INCLUDED */
diff --git a/pstoraster/gxbcache.c b/pstoraster/gxbcache.c
new file mode 100644
index 000000000..851548348
--- /dev/null
+++ b/pstoraster/gxbcache.c
@@ -0,0 +1,153 @@
+/* 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 supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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..bbaea0c73
--- /dev/null
+++ b/pstoraster/gxbcache.h
@@ -0,0 +1,130 @@
+/* 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 supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Bitmap cache structures */
+
+#ifndef gxbcache_INCLUDED
+# define gxbcache_INCLUDED
+
+#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 *));
+
+#endif /* gxbcache_INCLUDED */
diff --git a/pstoraster/gxbitfmt.h b/pstoraster/gxbitfmt.h
new file mode 100644
index 000000000..7c6039b6e
--- /dev/null
+++ b/pstoraster/gxbitfmt.h
@@ -0,0 +1,195 @@
+/* Copyright (C) 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Definitions for bitmap storage formats */
+
+#ifndef gxbitfmt_INCLUDED
+# define gxbitfmt_INCLUDED
+
+/*
+ * Several operations, such as the get_bits_rectangle driver procedure, can
+ * take and/or produce data in a flexible variety of formats; the ability to
+ * describe how bitmap data is stored is useful in other contexts as well.
+ * We define bitmap storage formats using a bit mask: this allows a
+ * procedure to ask for, or offer to provide, data in more than one format.
+ */
+
+typedef ulong gx_bitmap_format_t;
+
+ /*
+ * Define the supported color space alternatives.
+ */
+
+#define GB_COLORS_NATIVE (1L<<0) /* native representation (DevicePixel) */
+#define GB_COLORS_GRAY (1L<<1) /* DeviceGray */
+#define GB_COLORS_RGB (1L<<2) /* DeviceRGB */
+#define GB_COLORS_CMYK (1L<<3) /* DeviceCMYK */
+
+#define GB_COLORS_STANDARD_ALL\
+ (GB_COLORS_GRAY | GB_COLORS_RGB | GB_COLORS_CMYK)
+#define GB_COLORS_ALL\
+ (GB_COLORS_NATIVE | GB_COLORS_STANDARD_ALL)
+#define gb_colors_for_device(dev)\
+ ((dev)->color_info.num_components == 4 ? GB_COLORS_CMYK :\
+ (dev)->color_info.num_components == 3 ? GB_COLORS_RGB : GB_COLORS_GRAY)
+#define GB_COLORS_NAMES\
+ "colors_native", "colors_Gray", "colors_RGB", "colors_CMYK"
+
+ /*
+ * Define whether alpha information is included. For GB_COLORS_NATIVE,
+ * all values other than GB_ALPHA_NONE are equivalent.
+ */
+
+#define GB_ALPHA_NONE (1L<<4) /* no alpha */
+#define GB_ALPHA_FIRST (1L<<5) /* include alpha as first component */
+#define GB_ALPHA_LAST (1L<<6) /* include alpha as last component */
+ /*unused*/ /*(1L<<7)*/
+
+#define GB_ALPHA_ALL\
+ (GB_ALPHA_NONE | GB_ALPHA_FIRST | GB_ALPHA_LAST)
+#define GB_ALPHA_NAMES\
+ "alpha_none", "alpha_first", "alpha_last", "?alpha_unused?"
+
+ /*
+ * Define the supported depths per component for GB_COLORS_STANDARD.
+ */
+
+#define GB_DEPTH_1 (1L<<8)
+#define GB_DEPTH_2 (1L<<9)
+#define GB_DEPTH_4 (1L<<10)
+#define GB_DEPTH_8 (1L<<11)
+#define GB_DEPTH_12 (1L<<12)
+#define GB_DEPTH_16 (1L<<13)
+ /*unused1*/ /*(1L<<14)*/
+ /*unused2*/ /*(1L<<15)*/
+
+#define GB_DEPTH_ALL\
+ (GB_DEPTH_1 | GB_DEPTH_2 | GB_DEPTH_4 | GB_DEPTH_8 |\
+ GB_DEPTH_12 | GB_DEPTH_16)
+#define GB_DEPTH_NAMES\
+ "depth_1", "depth_2", "depth_4", "depth_8",\
+ "depth_12", "depth_16", "?depth_unused1?", "?depth_unused2?"
+
+/* Find the maximum depth of an options mask. */
+#define GB_OPTIONS_MAX_DEPTH(opt)\
+"\
+\000\001\002\002\004\004\004\004\010\010\010\010\010\010\010\010\
+\014\014\014\014\014\014\014\014\014\014\014\014\014\014\014\014\
+\020\020\020\020\020\020\020\020\020\020\020\020\020\020\020\020\
+\020\020\020\020\020\020\020\020\020\020\020\020\020\020\020\020\
+"[((opt) >> 8) & 0x3f]
+/* Find the depth of an options mask with exactly 1 bit set. */
+#define GB_OPTIONS_DEPTH(opt)\
+ ((((opt) >> 8) & 0xf) |\
+ "\000\000\014\020"[((opt) >> 12) & 3])
+
+ /*
+ * Define the supported packing formats. Currently, GB_PACKING_PLANAR is
+ * only partially supported, and GB_PACKING_BIT_PLANAR is hardly supported
+ * at all.
+ */
+
+#define GB_PACKING_CHUNKY (1L<<16)
+#define GB_PACKING_PLANAR (1L<<17) /* 1 plane per component */
+#define GB_PACKING_BIT_PLANAR (1L<<18) /* 1 plane per bit */
+ /*unused*/ /*(1L<<19)*/
+
+#define GB_PACKING_ALL\
+ (GB_PACKING_CHUNKY | GB_PACKING_PLANAR | GB_PACKING_BIT_PLANAR)
+#define GB_PACKING_NAMES\
+ "packing_chunky", "packing_planar", "packing_bit_planar", "?packing_unused?"
+
+ /*
+ * Define the possible methods of returning data.
+ */
+
+#define GB_RETURN_COPY (1L<<20) /* copy to client's buffer */
+#define GB_RETURN_POINTER (1L<<21) /* return pointers to data */
+
+#define GB_RETURN_ALL\
+ (GB_RETURN_COPY | GB_RETURN_POINTER)
+#define GB_RETURN_NAMES\
+ "return_copy", "return_pointer"
+
+ /*
+ * Define the allowable alignments. This is only relevant for
+ * GB_RETURN_POINTER: for GB_RETURN_COPY, any alignment is acceptable.
+ */
+
+#define GB_ALIGN_STANDARD (1L<<22) /* require standard bitmap alignment */
+#define GB_ALIGN_ANY (1L<<23) /* any alignment is acceptable */
+
+#define GB_ALIGN_ALL\
+ (GB_ALIGN_ANY | GB_ALIGN_STANDARD)
+#define GB_ALIGN_NAMES\
+ "align_any", "align_standard"
+
+ /*
+ * Define the allowable X offsets. GB_OFFSET_ANY is only relevant for
+ * GB_RETURN_POINTER: for GB_RETURN_COPY, clients must specify the
+ * offset so they know how much space to allocate.
+ */
+
+#define GB_OFFSET_0 (1L<<24) /* no offsetting */
+#define GB_OFFSET_SPECIFIED (1L<<25) /* client-specified offset */
+#define GB_OFFSET_ANY (1L<<26) /* any offset is acceptable */
+ /* (for GB_RETURN_POINTER only) */
+ /*unused*/ /*(1L<<27)*/
+
+#define GB_OFFSET_ALL\
+ (GB_OFFSET_0 | GB_OFFSET_SPECIFIED | GB_OFFSET_ANY)
+#define GB_OFFSET_NAMES\
+ "offset_0", "offset_specified", "offset_any", "?offset_unused?"
+
+ /*
+ * Define the allowable rasters. GB_RASTER_ANY is only relevant for
+ * GB_RETURN_POINTER, for the same reason as GB_OFFSET_ANY.
+ * Note also that if GB_ALIGN_STANDARD and GB_RASTER_SPECIFIED are
+ * both chosen and more than one scan line is being transferred,
+ * the raster value must also be aligned (i.e., 0 mod align_bitmap_mod).
+ * Implementors are not required to check this.
+ */
+
+ /*
+ * Standard raster is bitmap_raster(dev->width) for return_ptr,
+ * bitmap_raster(x_offset + width) for return_copy,
+ * padding per alignment.
+ */
+#define GB_RASTER_STANDARD (1L<<28)
+#define GB_RASTER_SPECIFIED (1L<<29) /* any client-specified raster */
+#define GB_RASTER_ANY (1L<<30) /* any raster is acceptable (for */
+ /* GB_RETURN_POINTER only) */
+
+#define GB_RASTER_ALL\
+ (GB_RASTER_STANDARD | GB_RASTER_SPECIFIED | GB_RASTER_ANY)
+#define GB_RASTER_NAMES\
+ "raster_standard", "raster_specified", "raster_any"
+
+/* Define names for debugging printout. */
+#define GX_BITMAP_FORMAT_NAMES\
+ GB_COLORS_NAMES, GB_ALPHA_NAMES, GB_DEPTH_NAMES, GB_PACKING_NAMES,\
+ GB_RETURN_NAMES, GB_ALIGN_NAMES, GB_OFFSET_NAMES, GB_RASTER_NAMES
+
+#endif /* gxbitfmt_INCLUDED */
diff --git a/pstoraster/gxbitmap.h b/pstoraster/gxbitmap.h
new file mode 100644
index 000000000..20802e9b4
--- /dev/null
+++ b/pstoraster/gxbitmap.h
@@ -0,0 +1,133 @@
+/* Copyright (C) 1989, 1993, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Definitions for stored bitmaps for Ghostscript */
+
+#ifndef gxbitmap_INCLUDED
+# define gxbitmap_INCLUDED
+
+#include "gstypes.h" /* for gs_id */
+#include "gsbitmap.h"
+
+/* Define the gx version of a bitmap identifier. */
+typedef gs_bitmap_id gx_bitmap_id;
+
+/* Define the gx version of the "no identifier" value. */
+#define gx_no_bitmap_id gs_no_bitmap_id
+
+/*
+ * For gx_bitmap data, 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.
+ */
+/* 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))
+
+/*
+ * Define the gx analogue of the basic bitmap structure. Note that since
+ * all scan lines must be aligned, the requirement on raster is:
+ * If size.y > 1,
+ * raster >= bitmap_raster(size.x * depth)
+ * raster % align_bitmap_mod = 0
+ */
+#define gx_bitmap_common gs_bitmap_common
+typedef struct gx_bitmap_s {
+ gx_bitmap_common;
+} gx_bitmap;
+
+/*
+ * Define the gx analogue of the tile bitmap structure. Note that if
+ * shift != 0 (for strip bitmaps, see below), size.y and rep_height
+ * mean something slightly different: see below for details.
+ */
+#define gx_tile_bitmap_common gs_tile_bitmap_common
+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.
+ *
+ * Note that for shifted tiles, size.y is the size of the stored bitmap
+ * (1 or more strips), and NOT the height of the actual tile. The latter
+ * is not stored in the structure at all: it can be computed as H * W /
+ * gcd(S, W).
+ *
+ * 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;
+
+extern_st(st_gx_strip_bitmap);
+#define public_st_gx_strip_bitmap() /* in gspcolor.c */\
+ gs_public_st_suffix_add0_local(st_gx_strip_bitmap, gx_strip_bitmap,\
+ "gx_strip_bitmap", bitmap_enum_ptrs, bitmap_reloc_ptrs,\
+ st_gs_tile_bitmap)
+#define st_gx_strip_bitmap_max_ptrs 1
+
+#endif /* gxbitmap_INCLUDED */
diff --git a/pstoraster/gxbitops.h b/pstoraster/gxbitops.h
new file mode 100644
index 000000000..6b8b13d86
--- /dev/null
+++ b/pstoraster/gxbitops.h
@@ -0,0 +1,142 @@
+/* Copyright (C) 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Internal definitions for bitmap operations */
+
+#ifndef gxbitops_INCLUDED
+# define gxbitops_INCLUDED
+
+#include "gsbitops.h"
+
+/*
+ * 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.
+ */
+#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
+
+#endif /* gxbitops_INCLUDED */
diff --git a/pstoraster/gxccache.c b/pstoraster/gxccache.c
new file mode 100644
index 000000000..f6dd01b94
--- /dev/null
+++ b/pstoraster/gxccache.c
@@ -0,0 +1,456 @@
+/* Copyright (C) 1989, 1995, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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 || font->PaintType != 0) { /* We can't cache by UID alone. */
+ 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) ||
+ pair->FontType != pfont->FontType
+ )
+ 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");
+ dlprintf3("[K]copying 0x%lx, offset=(%g,%g)\n", (ulong) cc,
+ fixed2float(-cc->offset.x),
+ fixed2float(-cc->offset.y));
+ dlprintf6(" 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. */
+ gx_clip_path *pcpath;
+
+ 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 */
+ code = gx_effective_clip_path(pgs, &pcpath);
+ if (code < 0)
+ return code;
+ gx_make_clip_device(&cdev, &cdev, gx_cpath_list(pcpath));
+ 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 (dev_proc(orig_dev, fill_mask) != gx_default_fill_mask ||
+ !lop_no_S_is_T(pgs->log_op)
+ ) {
+ gx_clip_path *pcpath;
+
+ code = gx_effective_clip_path(pgs, &pcpath);
+ if (code >= 0) {
+ 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, pcpath);
+ 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_memory_t *mem = &gs_memory_default;
+ gs_image_enum *pie =
+ gs_image_enum_alloc(mem, "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(mem, 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_const_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..b2a42c402
--- /dev/null
+++ b/pstoraster/gxccman.c
@@ -0,0 +1,797 @@
+/* Copyright (C) 1989, 1995, 1996, 1997 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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;
+
+ENUM_PTRS_END
+private RELOC_PTRS_BEGIN(cc_ptr_reloc_ptrs)
+{
+}
+RELOC_PTRS_END
+
+/* Forward references */
+private gx_xfont * lookup_xfont_by_name(P6(gx_device *, const 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 * struct_mem, gs_memory_t * bits_mem,
+ 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(struct_mem, mmax, cached_fm_pair,
+ &st_cached_fm_pair_element,
+ "font_dir_alloc(mdata)");
+ chars = gs_alloc_struct_array(struct_mem, chsize, cached_char *,
+ &st_cached_char_ptr_element,
+ "font_dir_alloc(chars)");
+ if (mdata == 0 || chars == 0) {
+ gs_free_object(struct_mem, chars, "font_dir_alloc(chars)");
+ gs_free_object(struct_mem, mdata, "font_dir_alloc(mdata)");
+ return_error(gs_error_VMerror);
+ }
+ pdir->fmcache.mmax = mmax;
+ pdir->fmcache.mdata = mdata;
+ pdir->ccache.struct_memory = struct_mem;
+ pdir->ccache.bits_memory = bits_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_alloc_bytes_immovable(dir->ccache.bits_memory,
+ 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;
+ pair->FontType = font->FontType;
+ /* 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;
+ const 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 && (pfn1)->size != 0 &&\
+ !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, const 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. */
+ rc_header rc;
+
+ /* Preserve the reference count, if any. */
+ rc = pdev->rc;
+ gs_make_mem_mono_device(pdev, pdev->memory, pdev->target);
+ pdev->rc = rc;
+ pdev->width = iwidth;
+ pdev->height = iheight;
+ isize = gdev_mem_bitmap_size(pdev);
+ } else {
+ /* Use an alpha-buffer device to compress as we go. */
+ rc_header rc;
+
+ /* Preserve the reference counts, if any. */
+ rc = dev2->rc;
+ gs_make_mem_alpha_device(dev2, dev2->memory, NULL, depth);
+ dev2->rc = rc;
+ dev2->width = iwidth >> log2_xscale;
+ dev2->height = iheight >> log2_yscale;
+ rc = dev->rc;
+ gs_make_mem_abuf_device(dev, dev->memory, (gx_device *) dev2,
+ pscale, depth, 0);
+ dev->rc = rc;
+ 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. */
+ gs_memory_t *mem = dir->ccache.bits_memory;
+ 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_alloc_bytes_immovable(mem, sizeof(*cck),
+ "char cache chunk");
+ if (cck == 0)
+ return 0;
+ cdata =
+ gs_alloc_bytes_immovable(mem, cksize,
+ "char cache chunk(data)");
+ if (cdata == 0) {
+ gs_free_object(mem, 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)
+
+ while (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..682c42501
--- /dev/null
+++ b/pstoraster/gxchar.h
@@ -0,0 +1,190 @@
+/* Copyright (C) 1989, 1995, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Internal character definition for Ghostscript library */
+/* Requires gsmatrix.h, gxfixed.h */
+
+#ifndef gxchar_INCLUDED
+# define gxchar_INCLUDED
+
+#include "gschar.h"
+#include "gxtext.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 {
+ /* Put this first for subclassing. */
+ gs_text_enum_common; /* (procs, text, index) */
+
+#define SHOW_IS(penum, op_mask)\
+ (((penum)->text.operation & (op_mask)) != 0)
+#define SHOW_IS_ALL_OF(penum, op_mask)\
+ (((penum)->text.operation & (op_mask)) == (op_mask))
+ /*
+ * The comments next to the following macros indicate the
+ * corresponding test in pre-5.24 filesets.
+ */
+#define SHOW_IS_ADD_TO_ALL(penum) /* add */\
+ SHOW_IS(penum, TEXT_ADD_TO_ALL_WIDTHS)
+#define SHOW_IS_ADD_TO_SPACE(penum) /* wchr != no_char */\
+ SHOW_IS(penum, TEXT_ADD_TO_SPACE_WIDTH)
+#define SHOW_IS_DO_KERN(penum) /* do_kern */\
+ SHOW_IS(penum, TEXT_INTERVENE)
+#define SHOW_IS_XYCSHOW(penum) /* do_kern < 0 */\
+ (SHOW_IS_DO_KERN(penum) &&\
+ SHOW_IS(penum, TEXT_REPLACE_X_WIDTHS | TEXT_REPLACE_Y_WIDTHS | TEXT_DO_NONE))
+#define SHOW_IS_SLOW(penum) /* slow_show */\
+ SHOW_IS(penum, TEXT_ADD_TO_ALL_WIDTHS | TEXT_ADD_TO_SPACE_WIDTH | TEXT_INTERVENE)
+#define SHOW_IS_DRAWING(penum) /* !stringwidth_flag */\
+ !SHOW_IS(penum, TEXT_DO_NONE)
+#define SHOW_IS_STRINGWIDTH(penum) /* stringwidth_flag > 0 */\
+ SHOW_IS_ALL_OF(penum, TEXT_DO_NONE | TEXT_RETURN_WIDTH)
+
+ /* Following are set at creation time */
+ gs_state *pgs;
+ int level; /* save the level of pgs */
+ gs_char_path_mode charpath_flag;
+ gs_state *show_gstate; /* for setting pgs->show_gstate */
+ /* at returns/callouts */
+ 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 *));
+
+#endif /* gxchar_INCLUDED */
diff --git a/pstoraster/gxcht.c b/pstoraster/gxcht.c
new file mode 100644
index 000000000..88f4a3118
--- /dev/null
+++ b/pstoraster/gxcht.c
@@ -0,0 +1,710 @@
+/* Copyright (C) 1993, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Color halftone rendering for Ghostscript imaging library */
+#include "memory_.h"
+#include "gx.h"
+#include "gserrors.h"
+#include "gsutil.h" /* for id generation */
+#include "gxarith.h"
+#include "gxfixed.h"
+#include "gxmatrix.h"
+#include "gxdevice.h"
+#include "gxcmap.h"
+#include "gxdcolor.h"
+#include "gxistate.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_debug_c('.') ? tile_longs_SMALL : tile_longs_LARGE)
+#endif
+
+/* Define the colored halftone device color type. */
+gs_private_st_ptrs1(st_dc_ht_colored, gx_device_color, "dc_ht_colored",
+ dc_ht_colored_enum_ptrs, dc_ht_colored_reloc_ptrs, colors.colored.c_ht);
+private dev_color_proc_load(gx_dc_ht_colored_load);
+private dev_color_proc_fill_rectangle(gx_dc_ht_colored_fill_rectangle);
+private dev_color_proc_equal(gx_dc_ht_colored_equal);
+const gx_device_color_type_t gx_dc_type_data_ht_colored = {
+ &st_dc_ht_colored,
+ gx_dc_ht_colored_load, gx_dc_ht_colored_fill_rectangle,
+ gx_dc_default_fill_masked, gx_dc_ht_colored_equal
+};
+#undef gx_dc_type_ht_colored
+const gx_device_color_type_t *const gx_dc_type_ht_colored =
+ &gx_dc_type_data_ht_colored;
+#define gx_dc_type_ht_colored (&gx_dc_type_data_ht_colored)
+
+/* Forward references. */
+private void set_ht_colors(P7(gx_color_index[16], gx_strip_bitmap *[4],
+ const gx_device_color *, gx_device *,
+ gx_ht_cache *[4], int, 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 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_imager_state * pis,
+ gx_device * ignore_dev, gs_color_select_t select)
+{
+ gx_device_halftone *pdht = pis->dev_ht;
+ gx_ht_order *porder = &pdht->components[0].corder;
+ gx_ht_cache *pcache = pis->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;
+ gx_rop_source_t no_source;
+ 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;
+ int plane_mask;
+ bool no_rop;
+
+ if (w <= 0 || h <= 0)
+ return 0;
+ /* Colored halftone patterns are unconditionally opaque. */
+ lop &= ~lop_T_transparent;
+ 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, &plane_mask);
+ if (!(plane_mask & (plane_mask - 1))) {
+ /*
+ * At most one plane is not solid-color: we can treat this as a
+ * binary halftone (or, anomalously, a pure color).
+ */
+ gx_device_color devc;
+
+ if (plane_mask == 0 ) {
+ color_set_pure(&devc, colors[0]);
+ } else {
+ int plane = small_exact_log2(plane_mask);
+ gx_ht_tile tile;
+
+ tile.tiles = *sbits[plane]; /* already rendered */
+ tile.level = pdevc->colors.colored.c_level[plane];
+ color_set_binary_tile(&devc, colors[0], colors[plane_mask], &tile);
+ devc.phase = pdevc->phase;
+ }
+ return gx_device_color_fill_rectangle(&devc, x, y, w, h, dev, lop,
+ source);
+ }
+
+ no_rop = source == NULL && lop_no_S_is_T(lop);
+ /*
+ * 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 ((w > lw || h > lh) &&
+ (raster = bitmap_raster(lw * depth)) <= tile_bytes / lh
+ ) {
+ /*
+ * The only reason we need to do fit_fill here is that if the
+ * device is a clipper, the caller might be counting on it to do
+ * all necessary clipping. Actually, we should clip against the
+ * device's clipping box, not the default....
+ */
+ fit_fill(dev, x, y, w, h);
+ /* Check to make sure we still have a big rectangle. */
+ if (w > lw || h > 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, plane_mask, colors,
+ (const gx_strip_bitmap **)sbits);
+ if (no_rop)
+ 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)
+ set_rop_no_source(source, no_source, dev);
+ 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,
+ 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. See above for
+ * why we do the X equivalent of fit_fill here.
+ */
+ if (x < 0)
+ w += x, x = 0;
+ if (x > dev->width - w)
+ w = dev->width - x;
+ if (w <= 0)
+ return 0;
+ size_x = w * depth;
+ raster = bitmap_raster(size_x);
+ if (raster > tile_bytes) {
+ /* We'll have to do a partial line. */
+ dw = tile_bytes * 8 / depth;
+ size_x = dw * depth;
+ raster = bitmap_raster(size_x);
+ dh = 1;
+ goto fit;
+ }
+ }
+ /* Do as many lines as will fit. */
+ dw = w;
+ dh = tile_bytes / raster;
+ if (dh > h)
+ dh = h;
+fit: /* 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 + pdevc->phase.x, cy + pdevc->phase.y,
+ dw, ch, depth, plane_mask, colors,
+ (const gx_strip_bitmap **)sbits);
+ if (no_rop) {
+ code = (*dev_proc(dev, copy_color))
+ (dev, tiles.data, 0, raster, gx_no_bitmap_id,
+ x, cy, dw, ch);
+ } else {
+ if (source == NULL)
+ set_rop_no_source(source, no_source, dev);
+ 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, cy, dw, ch, 0, 0, lop);
+ }
+ 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).
+ */
+
+private const ulong ht_no_bitmap_data[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0
+};
+private gx_strip_bitmap ht_no_bitmap;
+private const gx_strip_bitmap ht_no_bitmap_init = {
+ 0, sizeof(ulong),
+ {sizeof(ulong) * 8, countof(ht_no_bitmap_data)},
+ gx_no_bitmap_id, 1, 1, 0, 0
+};
+
+void
+gs_gxcht_init(gs_memory_t *mem)
+{
+ ht_no_bitmap = ht_no_bitmap_init;
+ ht_no_bitmap.data = (byte *)ht_no_bitmap_data; /* actually const */
+}
+
+/* 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, int *pmask)
+{
+ gx_color_value v[2][4];
+ gx_color_value max_color = dev->color_info.dither_colors - 1;
+ int plane_mask = 0;
+ /*
+ * NB: the halftone orders are all set up for an additive color space.
+ * To use these work with a cmyk color space, it is necessary to
+ * invert both the color level and the color pair. Note that if the
+ * original color was provided an additive space, this will reverse
+ * (in an approximate sense) the color conversion performed to
+ * express the color in cmyk space.
+ */
+ bool invert = dev->color_info.num_components == 4; /****** HACK ******/
+
+#define set_plane_color(i)\
+ BEGIN\
+ uint q = pdc->colors.colored.c_base[i];\
+ uint r = pdc->colors.colored.c_level[i];\
+\
+ v[0][i] = fractional_color(q, max_color);\
+ if (r == 0)\
+ v[1][i] = v[0][i], sbits[i] = &ht_no_bitmap;\
+ else if (!invert) {\
+ v[1][i] = fractional_color(q + 1, max_color);\
+ sbits[i] = &gx_render_ht(caches[i], r)->tiles;\
+ plane_mask |= 1 << (i);\
+ } else { \
+ const gx_device_halftone * pdht = pdc->colors.colored.c_ht;\
+ int nlevels = 0; \
+ \
+ nlevels = pdht->components[pdht->color_indices[i]].corder.num_levels;\
+ v[1][i] = v[0][i]; \
+ v[0][i] = fractional_color(q + 1, max_color); \
+ sbits[i] = &gx_render_ht(caches[i], nlevels - r)->tiles; \
+ plane_mask |= 1 << (i); \
+ }\
+ END
+#define map8(m) m(0), m(1), m(2), m(3), m(4), m(5), m(6), m(7)
+ set_plane_color(0);
+ set_plane_color(1);
+ set_plane_color(2);
+ if (nplanes == 3) {
+#define map_rgb(i)\
+ colors[i] = map1rgb(v[(i) & 1][0], v[((i) & 2) >> 1][1], v[(i) >> 2][2])
+ gx_color_value alpha = pdc->colors.colored.alpha;
+
+ if (alpha == gx_max_color_value) {
+#ifdef DEBUG
+# define map1rgb(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 map1rgb(r, g, b) (*map)(dev, r, g, b)
+#endif
+ map8(map_rgb);
+#undef map1rgb
+ } else {
+#ifdef DEBUG
+# define map1rgb(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 map1rgb(r, g, b) (*map)(dev, r, g, b, alpha)
+#endif
+ map8(map_rgb);
+#undef map1rgb
+ }
+ } else {
+#define map_cmyk(i)\
+ colors[i] = map1cmyk(v[(i) & 1][0], v[((i) & 2) >> 1][1],\
+ v[((i) & 4) >> 2][2], v[(i) >> 3][3])
+#ifdef DEBUG
+# define map1cmyk(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 map1cmyk(r, g, b, w) (*map)(dev, r, g, b, w)
+#endif
+ set_plane_color(3);
+ /*
+ * For CMYK output, especially if the input was RGB, it's
+ * common for one or more of the components to be zero.
+ * Each zero component can cut the cost of color mapping in
+ * half, so it's worth doing a little checking here.
+ */
+#define m(i) map_cmyk(i)
+ switch (plane_mask) {
+ case 15:
+ m(15); m(14); m(13); m(12);
+ m(11); m(10); m(9); m(8);
+ case 7:
+ m(7); m(6); m(5); m(4);
+c3: case 3:
+ m(3); m(2);
+c1: case 1:
+ m(1);
+ break;
+ case 14:
+ m(14); m(12); m(10); m(8);
+ case 6:
+ m(6); m(4);
+c2: case 2:
+ m(2);
+ break;
+ case 13:
+ m(13); m(12); m(9); m(8);
+ case 5:
+ m(5); m(4);
+ goto c1;
+ case 12:
+ m(12); m(8);
+ case 4:
+ m(4);
+ break;
+ case 11:
+ m(11); m(10); m(9); m(8);
+ goto c3;
+ case 10:
+ m(10); m(8);
+ goto c2;
+ case 9:
+ m(9); m(8);
+ goto c1;
+ case 8:
+ m(8);
+ break;
+ case 0:;
+ }
+ m(0);
+#undef m
+#undef map1cmyk
+ }
+#undef map8
+#undef set_plane_color
+ *pmask = plane_mask;
+}
+
+/* Define the bookkeeping structure for each plane of halftone rendering. */
+typedef 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;
+} tile_cursor_t;
+
+/* Initialize one plane cursor. */
+private void
+init_tile_cursor(int i, tile_cursor_t *ptc, const gx_strip_bitmap *btile,
+ int endx, int lasty)
+{
+ int tw = btile->size.x;
+ int bx = ((ptc->tile_shift = btile->shift) == 0 ? endx :
+ endx + lasty / btile->size.y * ptc->tile_shift) % tw;
+ int by = lasty % btile->size.y;
+
+ ptc->xoffset = bx >> 3;
+ ptc->xshift = 8 - (bx & 7);
+ ptc->xbytes = (tw - 1) >> 3;
+ ptc->xbits = ((tw - 1) & 7) + 1;
+ ptc->tdata = btile->data;
+ ptc->raster = btile->raster;
+ ptc->row = ptc->tdata + by * ptc->raster;
+ if_debug5('h', "[h]plane %d: size=%d,%d bx=%d by=%dn",
+ i, tw, btile->size.y, bx, by);
+}
+
+/* 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 plane_mask, /* which planes are halftoned */
+ 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;
+ tile_cursor_t cursor[4];
+ int dbytes = depth >> 3;
+ uint dest_raster = ctiles->raster;
+ byte *dest_row =
+ ctiles->data + dest_raster * (h - 1) + (w * depth) / 8;
+
+ if_debug6('h',
+ "[h]color_ht: x=%d y=%d w=%d h=%d plane_mask=%d depth=%d\n",
+ px, py, w, h, plane_mask, depth);
+
+ /* Do one-time cursor initialization. */
+ {
+ int endx = w + px;
+ int lasty = h - 1 + py;
+
+ if (plane_mask & 1)
+ init_tile_cursor(0, &cursor[0], sbits[0], endx, lasty);
+ if (plane_mask & 2)
+ init_tile_cursor(1, &cursor[1], sbits[1], endx, lasty);
+ if (plane_mask & 4)
+ init_tile_cursor(2, &cursor[2], sbits[2], endx, lasty);
+ if (plane_mask & 8)
+ init_tile_cursor(3, &cursor[3], sbits[3], endx, lasty);
+ }
+
+ /* 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)
+ if (plane_mask & 1)
+ set_row(cursor[0]);
+ if (plane_mask & 2)
+ set_row(cursor[1]);
+ if (plane_mask & 4)
+ set_row(cursor[2]);
+ if (plane_mask & 8)
+ 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 (plane_mask & 1) {
+ next_bits(cursor[0]);
+ indices = expand_8x1_to_8x4[bits & 0xff];
+ } else
+ indices = 0;
+ if (plane_mask & 2) {
+ next_bits(cursor[1]);
+ indices |= expand_8x1_to_8x4[bits & 0xff] << 1;
+ }
+ if (plane_mask & 4) {
+ next_bits(cursor[2]);
+ indices |= expand_8x1_to_8x4[bits & 0xff] << 2;
+ }
+ if (plane_mask & 8) {
+ next_bits(cursor[3]);
+ indices |= expand_8x1_to_8x4[bits & 0xff] << 3;
+ }
+#undef next_bits
+ nx = min(x, 8); /* 1 <= nx <= 8 */
+ x -= nx;
+ switch (dbytes) {
+ case 0: /* 4 */
+ i = nx;
+ if ((x + nx) & 1) {
+ /* First pixel is even nibble. */
+ *dest = (*dest & 0xf) +
+ ((byte)colors[indices & 0xf] << 4);
+ indices >>= 4;
+ --i;
+ }
+ /* Now 0 <= i <= 8. */
+ for (; (i -= 2) >= 0; indices >>= 8)
+ *--dest =
+ (byte)colors[indices & 0xf] +
+ ((byte)colors[(indices >> 4) & 0xf]
+ << 4);
+ /* Check for final odd nibble. */
+ if (i & 1)
+ *--dest = (byte)colors[indices & 0xf];
+ break;
+ case 4: /* 32 */
+ for (i = nx; --i >= 0; indices >>= 4) {
+ bits32 tcolor = (bits32)colors[indices & 0xf];
+
+ dest -= 4;
+ dest[3] = (byte)tcolor;
+ dest[2] = (byte)(tcolor >> 8);
+ tcolor >>= 16;
+ dest[1] = (byte)tcolor;
+ dest[0] = (byte)(tcolor >> 8);
+ }
+ break;
+ case 3: /* 24 */
+ for (i = nx; --i >= 0; indices >>= 4) {
+ bits32 tcolor = (bits32)colors[indices & 0xf];
+
+ dest -= 3;
+ dest[2] = (byte) tcolor;
+ dest[1] = (byte)(tcolor >> 8);
+ dest[0] = (byte)(tcolor >> 16);
+ }
+ break;
+ case 2: /* 16 */
+ for (i = nx; --i >= 0; indices >>= 4) {
+ uint tcolor =
+ (uint)colors[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[indices & 0xf];
+ break;
+ }
+ }
+ if (y == 0)
+ break;
+
+#define step_row(c, i)\
+ BEGIN\
+ 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 << 3) + 8 - (c.xshift & 7) +\
+ sbits[i]->size.x;\
+ c.xoffset = bx >> 3;\
+ c.xshift = 8 - (bx & 7);\
+ }\
+ else\
+ c.xshift &= 7;\
+ }\
+ }\
+ }\
+ END
+
+ if (plane_mask & 1)
+ step_row(cursor[0], 0);
+ if (plane_mask & 2)
+ step_row(cursor[1], 1);
+ if (plane_mask & 4)
+ step_row(cursor[2], 2);
+ if (plane_mask & 8)
+ step_row(cursor[3], 3);
+#undef step_row
+ }
+}
+
+/* Compare two colored halftones for equality. */
+private bool
+gx_dc_ht_colored_equal(const gx_device_color * pdevc1,
+ const gx_device_color * pdevc2)
+{
+ uint num_comp;
+
+ if (pdevc2->type != pdevc1->type ||
+ pdevc1->colors.colored.c_ht != pdevc2->colors.colored.c_ht ||
+ pdevc1->colors.colored.alpha != pdevc2->colors.colored.alpha ||
+ pdevc1->phase.x != pdevc2->phase.x ||
+ pdevc1->phase.y != pdevc2->phase.y
+ )
+ return false;
+ num_comp = pdevc1->colors.colored.c_ht->num_comp;
+ return
+ !memcmp(pdevc1->colors.colored.c_base,
+ pdevc2->colors.colored.c_base,
+ num_comp * sizeof(pdevc1->colors.colored.c_base[0])) &&
+ !memcmp(pdevc1->colors.colored.c_level,
+ pdevc2->colors.colored.c_level,
+ num_comp * sizeof(pdevc1->colors.colored.c_level[0]));
+}
diff --git a/pstoraster/gxcindex.h b/pstoraster/gxcindex.h
new file mode 100644
index 000000000..195c28813
--- /dev/null
+++ b/pstoraster/gxcindex.h
@@ -0,0 +1,91 @@
+/* Copyright (C) 1995, 1996, 1997 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Define the device color index type and macros */
+
+#ifndef gxcindex_INCLUDED
+# define gxcindex_INCLUDED
+
+#include "gsbitops.h" /* for sample_store macros */
+
+/*
+ * 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. Supported values of bpp are 1, 2, 4, 8, 12, 16, 24, 32.
+ *
+ * Note that declare_line_accum declares the variables l_dptr, l_dbyte, l_dbit,
+ * and l_xprev. Other code in the loop may use these variables.
+ */
+#define declare_line_accum(line, bpp, xo)\
+ sample_store_declare_setup(l_dptr, l_dbit, l_dbyte, line, 0, bpp);\
+ int l_xprev = (xo)
+#define line_accum(color, bpp)\
+ sample_store_next32(color, l_dptr, l_dbit, bpp, l_dbyte)
+#define line_accum_skip(bpp)\
+ sample_store_skip_next(l_dptr, l_dbit, bpp, l_dbyte)
+#define line_accum_store(bpp)\
+ sample_store_flush(l_dptr, l_dbit, bpp, l_dbyte)
+#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..e827fa40a
--- /dev/null
+++ b/pstoraster/gxclbits.c
@@ -0,0 +1,740 @@
+/* Copyright (C) 1995, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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"
+#include "gxfmap.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 ------ */
+
+/*
+ * Determine the (possibly unpadded) width in bytes for writing a bitmap,
+ * per the algorithm in gxcldev.h. If compression_mask has any of the
+ * cmd_mask_compress_any bits set, we assume the bitmap will be compressed.
+ * Return the total size of the bitmap.
+ */
+uint
+clist_bitmap_bytes(uint width_bits, uint height, int compression_mask,
+ uint * width_bytes, uint * raster)
+{
+ uint full_raster = *raster = bitmap_raster(width_bits);
+ uint short_raster = (width_bits + 7) >> 3;
+ uint width_bytes_last;
+
+ if (compression_mask & cmd_mask_compress_any)
+ *width_bytes = width_bytes_last = full_raster;
+ else if (short_raster <= cmd_max_short_width_bytes ||
+ height <= 1 ||
+ (compression_mask & decompress_spread) != 0
+ )
+ *width_bytes = width_bytes_last = short_raster;
+ else
+ *width_bytes = full_raster, width_bytes_last = short_raster;
+ return
+ (height == 0 ? 0 : *width_bytes * (height - 1) + width_bytes_last);
+}
+
+/*
+ * Compress a bitmap, skipping extra padding bytes at the end of each row if
+ * necessary. We require height >= 1, raster >= bitmap_raster(width_bits).
+ */
+private int
+cmd_compress_bitmap(stream_state * st, const byte * data, uint width_bits,
+ uint raster, uint height, stream_cursor_write * pw)
+{
+ uint width_bytes = bitmap_raster(width_bits);
+ int status = 0;
+ stream_cursor_read r;
+
+ r.ptr = data - 1;
+ if (raster == width_bytes) {
+ r.limit = r.ptr + raster * height;
+ status = (*st->template->process) (st, &r, pw, true);
+ } else { /* Compress row-by-row. */
+ uint y;
+
+ for (y = 1; (r.limit = r.ptr + width_bytes), y < height; ++y) {
+ status = (*st->template->process) (st, &r, pw, false);
+ if (status)
+ break;
+ if (r.ptr != r.limit) { /* We don't attempt to handle compressors that */
+ /* require >1 input byte to make progress. */
+ status = -1;
+ break;
+ }
+ r.ptr += raster - width_bytes;
+ }
+ if (status == 0)
+ status = (*st->template->process) (st, &r, pw, true);
+ }
+ if (st->template->release)
+ (*st->template->release) (st);
+ return status;
+}
+
+/*
+ * 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 short_raster, full_raster;
+ uint short_size =
+ clist_bitmap_bytes(width_bits, height,
+ compression_mask & ~cmd_mask_compress_any,
+ &short_raster, &full_raster);
+ uint uncompressed_raster;
+ uint uncompressed_size =
+ clist_bitmap_bytes(width_bits, height, compression_mask,
+ &uncompressed_raster, &full_raster);
+ uint max_size = cbuf_size - op_size;
+ gs_memory_t *mem = (cldev->memory ? cldev->memory : &gs_memory_default);
+ 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 & cmd_mask_compress_any) != 0 &&
+ (uncompressed_size <= max_size ||
+ (compression_mask & decompress_elsewhere) != 0)
+ ) {
+ union ss_ {
+ stream_state ss;
+ stream_CFE_state cf;
+ stream_RLE_state rl;
+ } sstate;
+ int code;
+
+ *psize = op_size + uncompressed_size;
+ code = (pcls != 0 ?
+ set_cmd_put_op(dp, cldev, pcls, 0, *psize) :
+ set_cmd_put_all_op(dp, cldev, 0, *psize));
+ if (code < 0)
+ return code;
+ cmd_uncount_op(0, *psize);
+ /*
+ * Note that we currently keep all the padding if we are
+ * compressing. This is ridiculous, but it's too hard to
+ * change right now.
+ */
+ if (compression_mask & (1 << cmd_compress_cfe)) {
+ /* Try CCITTFax compression. */
+ clist_cfe_init(&sstate.cf,
+ uncompressed_raster << 3 /*width_bits*/,
+ mem);
+ sstate.ss.template = &s_CFE_template;
+ compress = cmd_compress_cfe;
+ } else if (compression_mask & (1 << cmd_compress_rle)) {
+ /* Try RLE compression. */
+ clist_rle_init(&sstate.rl);
+ sstate.ss.template = &s_RLE_template;
+ compress = cmd_compress_rle;
+ }
+ if (compress) {
+ byte *wbase = dp + (op_size - 1);
+ stream_cursor_write w;
+
+ /*
+ * We can give up on compressing if we generate too much
+ * output to fit in the command reading buffer, or too
+ * much to make compression worthwhile.
+ */
+ uint wmax = min(uncompressed_size, max_size);
+ int status;
+
+ w.ptr = wbase;
+ w.limit = w.ptr + min(wmax, short_size >> 1);
+ status = cmd_compress_bitmap((stream_state *) & sstate, data,
+ uncompressed_raster << 3 /*width_bits */ ,
+ raster, height, &w);
+ if (status == 0) { /* Use compressed representation. */
+ uint wcount = w.ptr - wbase;
+
+ cmd_shorten_list_op(cldev,
+ (pcls ? &pcls->list : &cldev->band_range_list),
+ uncompressed_size - wcount);
+ *psize = op_size + wcount;
+ goto out;
+ }
+ }
+ if (uncompressed_size > max_size) {
+ cmd_shorten_list_op(cldev,
+ (pcls ? &pcls->list : &cldev->band_range_list),
+ *psize);
+ return_error(gs_error_limitcheck);
+ }
+ if (uncompressed_size != short_size) {
+ cmd_shorten_list_op(cldev,
+ (pcls ? &pcls->list : &cldev->band_range_list),
+ uncompressed_size - short_size);
+ *psize = op_size + short_size;
+ }
+ compress = 0;
+ } else if (uncompressed_size > max_size)
+ return_error(gs_error_limitcheck);
+ else {
+ int code;
+
+ *psize = op_size + short_size;
+ code = (pcls != 0 ?
+ set_cmd_put_op(dp, cldev, pcls, 0, *psize) :
+ set_cmd_put_all_op(dp, cldev, 0, *psize));
+ if (code < 0)
+ return code;
+ cmd_uncount_op(0, *psize);
+ }
+ 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 uint
+cmd_size_tile_params(const gx_strip_bitmap * tile)
+{
+ return 2 + cmd_size_w(tile->rep_width) + cmd_size_w(tile->rep_height) +
+ (tile->rep_width == tile->size.x ? 0 :
+ cmd_size_w(tile->size.x / tile->rep_width)) +
+ (tile->rep_height == tile->size.y ? 0 :
+ cmd_size_w(tile->size.y / tile->rep_height)) +
+ (tile->rep_shift == 0 ? 0 : cmd_size_w(tile->rep_shift));
+}
+private void
+cmd_store_tile_params(byte * dp, const gx_strip_bitmap * tile, int depth,
+ uint csize)
+{
+ byte *p = dp + 2;
+ byte bd = depth - 1;
+
+ *dp = cmd_count_op(cmd_opv_set_tile_size, csize);
+ p = cmd_put_w(tile->rep_width, p);
+ p = cmd_put_w(tile->rep_height, p);
+ if (tile->rep_width != tile->size.x) {
+ p = cmd_put_w(tile->size.x / tile->rep_width, p);
+ bd |= 0x20;
+ }
+ if (tile->rep_height != tile->size.y) {
+ p = cmd_put_w(tile->size.y / tile->rep_height, p);
+ bd |= 0x40;
+ }
+ if (tile->rep_shift != 0) {
+ cmd_put_w(tile->rep_shift, p);
+ bd |= 0x80;
+ }
+ dp[1] = bd;
+}
+
+/* Add a command to set the tile index. */
+/* This is a relatively high-frequency operation, so we declare it `inline'. */
+inline private int
+cmd_put_tile_index(gx_device_clist_writer *cldev, gx_clist_state *pcls,
+ uint indx)
+{
+ int idelta = indx - pcls->tile_index + 8;
+ byte *dp;
+ int code;
+
+ if (!(idelta & ~15)) {
+ code = set_cmd_put_op(dp, cldev, pcls,
+ cmd_op_delta_tile_index + idelta, 1);
+ if (code < 0)
+ return code;
+ } else {
+ code = set_cmd_put_op(dp, cldev, pcls,
+ cmd_op_set_tile_index + (indx >> 8), 2);
+ if (code < 0)
+ return code;
+ dp[1] = indx & 0xff;
+ }
+ if_debug2('L', "[L]writing index=%u, offset=%lu\n",
+ indx, cldev->tile_table[indx].offset);
+ return 0;
+}
+
+/* If necessary, write out data for a single color map. */
+int
+cmd_put_color_map(gx_device_clist_writer * cldev, cmd_map_index map_index,
+ const gx_transfer_map * map, gs_id * pid)
+{
+ byte *dp;
+ int code;
+
+ if (map == 0) {
+ if (pid && *pid == gs_no_id)
+ return 0; /* no need to write */
+ code = set_cmd_put_all_op(dp, cldev, cmd_opv_set_misc, 2);
+ if (code < 0)
+ return code;
+ dp[1] = cmd_set_misc_map + map_index;
+ if (pid)
+ *pid = gs_no_id;
+ } else {
+ if (pid && map->id == *pid)
+ return 0; /* no need to write */
+ code = set_cmd_put_all_op(dp, cldev, cmd_opv_set_misc,
+ 2 + sizeof(map->values));
+ if (code < 0)
+ return code;
+ dp[1] = cmd_set_misc_map + 0x20 + map_index;
+ memcpy(dp + 2, map->values, sizeof(map->values));
+ if (pid)
+ *pid = map->id;
+ }
+ 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. */
+/* NB that we cannot emit 'delta' style tile indices if VM error recovery */
+/* is in effect, since reader & writer's tile indices may get out of phase */
+/* as a consequence of error recovery occurring. */
+#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 = slot->rep_shift = tiles->rep_shift;
+ slot->x_reps = slot->y_reps = 1;
+ 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 the tile parameters (size and depth). */
+/* Currently we do this for all bands at once. */
+private void
+clist_new_tile_params(gx_strip_bitmap * new_tile, const gx_strip_bitmap * tiles,
+ int depth, const gx_device_clist_writer * cldev)
+{ /*
+ * 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,
+ * or more than 255 repetitions in X or Y, or make the tile so
+ * large that not all possible tiles will fit in the cache.
+ * Also, don't attempt Y replication if shifting is required.
+ */
+#define max_tile_reps_x 255
+#define max_tile_bytes_x 32
+#define max_tile_reps_y 4
+#define max_tile_bytes 256
+ uint rep_width = tiles->rep_width;
+ uint rep_height = tiles->rep_height;
+ uint rep_width_bits = rep_width * depth;
+ uint tile_overhead =
+ sizeof(tile_slot) + cldev->tile_band_mask_size;
+ uint max_bytes = cldev->chunk.size / (rep_width_bits * rep_height);
+
+ max_bytes -= min(max_bytes, tile_overhead);
+ if (max_bytes > max_tile_bytes)
+ max_bytes = max_tile_bytes;
+ *new_tile = *tiles;
+ {
+ uint max_bits_x = max_bytes * 8 / rep_height;
+ uint reps_x =
+ min(max_bits_x, max_tile_bytes_x * 8) / rep_width_bits;
+ uint reps_y;
+
+ while (reps_x > max_tile_reps_x)
+ reps_x >>= 1;
+ new_tile->size.x = max(reps_x, 1) * rep_width;
+ new_tile->raster = bitmap_raster(new_tile->size.x * depth);
+ if (tiles->shift != 0)
+ reps_y = 1;
+ else {
+ reps_y = max_bytes / (new_tile->raster * 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 * rep_height;
+ }
+#undef max_tile_reps_x
+#undef max_tile_bytes_x
+#undef max_tile_reps_y
+#undef max_tile_bytes
+}
+
+/* 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;
+
+#define tile_params_differ(cldev, tiles, depth)\
+ ((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)
+
+ top:if (clist_find_bits(cldev, tiles->id, &loc)) { /* The bitmap is in the cache. Check whether this band */
+ /* knows about it. */
+ int 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(cldev, pcls, loc.index);
+ } else {
+ uint extra = 0;
+
+ if tile_params_differ
+ (cldev, tiles, depth) { /*
+ * We have a cached tile whose parameters differ from
+ * the current ones. Because of the way tile IDs are
+ * managed, this is currently only possible when mixing
+ * Patterns and halftones, but if we didn't generate new
+ * IDs each time the main halftone cache needed to be
+ * refreshed, this could also happen simply from
+ * switching screens.
+ */
+ int band;
+
+ clist_new_tile_params(&cldev->tile_params, tiles, depth,
+ cldev);
+ cldev->tile_depth = depth;
+ /* No band knows about the new parameters. */
+ for (band = cldev->tile_known_min;
+ band <= cldev->tile_known_max;
+ ++band
+ )
+ cldev->states[band].known &= ~tile_params_known;
+ cldev->tile_known_min = cldev->nbands;
+ cldev->tile_known_max = -1;
+ }
+ if (!(pcls->known & tile_params_known)) { /* We're going to have to write the tile parameters. */
+ extra = cmd_size_tile_params(&cldev->tile_params);
+ } { /*
+ * This band doesn't know this tile yet, so 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 =
+ extra + 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;
+ if (extra) { /* Write the tile parameters before writing the bits. */
+ cmd_store_tile_params(dp, &cldev->tile_params, depth,
+ extra);
+ dp += extra;
+ /* This band now knows the parameters. */
+ pcls->known |= tile_params_known;
+ if (band_index < cldev->tile_known_min)
+ cldev->tile_known_min = band_index;
+ if (band_index > cldev->tile_known_max)
+ cldev->tile_known_max = band_index;
+ }
+ *dp = cmd_count_op(cmd_opv_set_tile_bits, csize - extra);
+ 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, add it. */
+ {
+ gx_strip_bitmap new_tile;
+ gx_strip_bitmap *ptile;
+
+ /* Ensure that the tile size is compatible. */
+ if (tile_params_differ(cldev, tiles, depth)) { /* We'll reset cldev->tile_params when we write the bits. */
+ clist_new_tile_params(&new_tile, tiles, depth, cldev);
+ ptile = &new_tile;
+ } else {
+ cldev->tile_params.id = tiles->id;
+ cldev->tile_params.data = tiles->data;
+ ptile = &cldev->tile_params;
+ }
+ code = clist_add_tile(cldev, ptile, tiles->raster, depth);
+ if (code < 0)
+ return code;
+ }
+ goto top;
+#undef tile_params_differ
+}
+
+/* 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(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..8dbd5a2ce
--- /dev/null
+++ b/pstoraster/gxcldev.h
@@ -0,0 +1,708 @@
+/* Copyright (C) 1995, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Internal definitions for Ghostscript command lists. */
+
+#ifndef gxcldev_INCLUDED
+# define gxcldev_INCLUDED
+
+#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 */
+
+/* ---------------- Commands ---------------- */
+
+/* Define the compression modes for bitmaps. */
+/*#define cmd_compress_none 0 *//* (implicit) */
+#define cmd_compress_rle 1
+#define clist_rle_init(ss)\
+ BEGIN\
+ s_RLE_set_defaults_inline(ss);\
+ s_RLE_init_inline(ss);\
+ END
+#define clist_rld_init(ss)\
+ BEGIN\
+ s_RLD_set_defaults_inline(ss);\
+ s_RLD_init_inline(ss);\
+ END
+#define cmd_compress_cfe 2
+#define clist_cf_init(ss, width, mem)\
+ BEGIN\
+ (ss)->memory = (mem);\
+ (ss)->K = -1;\
+ (ss)->Columns = (width);\
+ (ss)->EndOfBlock = false;\
+ (ss)->BlackIs1 = true;\
+ (ss)->DecodedByteAlign = align_bitmap_mod;\
+ END
+#define clist_cfe_init(ss, width, mem)\
+ BEGIN\
+ s_CFE_set_defaults_inline(ss);\
+ clist_cf_init(ss, width, mem);\
+ (*s_CFE_template.init)((stream_state *)(ss));\
+ END
+#define clist_cfd_init(ss, width, height, mem)\
+ BEGIN\
+ (*s_CFD_template.set_defaults)((stream_state *)ss);\
+ clist_cf_init(ss, width, mem);\
+ (ss)->Rows = (height);\
+ (*s_CFD_template.init)((stream_state *)(ss));\
+ END
+#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, /* rs?(1)nry?(1)nrx?(1)depth-1(5), */
+ /* rep_width#, rep_height#, */
+ /* [, nreps_x#][, nreps_y #] */
+ /* [, 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_misc = 0x06,
+#define cmd_set_misc_lop (0 << 6) /* 00: lop_lsb(6), lop_msb# */
+#define cmd_set_misc_data_x (1 << 6) /* 01: more(1)dx_lsb(5)[, dx_msb#] */
+#define cmd_set_misc_map (2 << 6) /* 10: non-0(1)map_index(5) */
+ /* [, n x frac] */
+#define cmd_set_misc_halftone (3 << 6) /* 11: type(6), num_comp# */
+ cmd_opv_enable_lop = 0x07, /* (nothing) */
+ cmd_opv_disable_lop = 0x08, /* (nothing) */
+ cmd_opv_set_ht_order = 0x09, /* component+1#[, cname#], */
+ /* width#, height#, raster#, */
+ /* shift#, num_levels#, num_bits# */
+ cmd_opv_set_ht_data = 0x0a, /* n, n x (uint|gx_ht_bit) */
+ cmd_opv_end_page = 0x0b, /* (nothing) */
+ 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+data_x)#, */
+ /* h#, <bits> | */
+#define cmd_copy_ht_color 4
+ /* +4+compress, x#, y#, (w+data_x)#, */
+ /* 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", "(misc2)", "(segment)", "(path)"
+
+#define cmd_misc_op_name_strings\
+ "end_run", "set_tile_size", "set_tile_phase", "set_tile_bits",\
+ "set_bits", "set_tile_color", "set_misc", "enable_lop",\
+ "disable_lop", "set_ht_order", "set_ht_data", "end_page",\
+ "delta2_color0", "delta2_color1", "set_copy_color", "set_copy_alpha",
+
+#ifdef DEBUG
+extern const char *const cmd_op_names[16];
+extern const char *const *const 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 bitmap is compressed, we don't remove any padding;
+ * - 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 going to be replicated horizontally (see the
+ * definition of decompress_spread below), we remove the padding;
+ * - Otherwise, we remove the padding only from the last scan line.
+ */
+#define cmd_max_short_width_bytes 6
+#define cmd_max_short_width_bits (cmd_max_short_width_bytes * 8)
+/*
+ * Determine the (possibly unpadded) width in bytes for writing a bitmap,
+ * per the algorithm just outlined. If compression_mask has any of the
+ * cmd_mask_compress_any bits set, we assume the bitmap will be compressed.
+ * Return the total size of the bitmap.
+ */
+uint clist_bitmap_bytes(P5(uint width_bits, uint height,
+ int compression_mask,
+ uint * width_bytes, uint * raster));
+
+/*
+ * 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.
+ */
+
+/* ---------------- Block file entries ---------------- */
+
+typedef struct cmd_block_s {
+ int band_min, band_max;
+#define cmd_band_end (-1) /* 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 */
+ /* We assign 'known' flags here from the high end; */
+ /* gxclpath.h assigns them from the low end. */
+#define tile_params_known (1<<15)
+#define begin_image_known (1<<14) /* gxclimag.c */
+#define initial_known 0x3fff /* exclude tile & image params */
+ /* Following are only used when writing */
+ cmd_list list; /* list of commands for band */
+ /* Following is set when writing, read when reading */
+ ulong cost; /* cost of rendering the 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, initial_known,\
+ { 0, 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, gxclimag.c, gxclpath.c, gxclrect.c).
+ * Note that none of the cmd_put_xxx procedures do VMerror recovery;
+ * they convert low-memory warnings to VMerror errors.
+ */
+
+/* ------ Exported by gxclist.c ------ */
+
+/*
+ * Error recovery procedures for writer-side VMerrors, for async rendering
+ * support. This logic assumes that the command list file and/or the
+ * renderer allocate memory from the same pool as the writer. Hence, when
+ * the writer runs out of memory, it tries to pause and let the renderer run
+ * for a while in hope that enough memory will be freed by it to allow the
+ * writer to allocate enough memory to proceed. Once a VMerror is detected,
+ * error recovery proceeds in two escalating stages:
+ *
+ * 1) The recovery logic repeatedly calls clist_VMerror_recover(), which
+ * waits until the next page has finished rendering. The recovery logic
+ * keeps calling clist_VMerror_recover() until enough memory is freed,
+ * or until clist_VMerror_recover() signals that no more pages
+ * remain to be rendered.
+ *
+ * 2) If enough memory is not free, the recovery logic calls
+ * clist_VMerror_recover_flush() once. This routine terminates and
+ * flushes out the partially-completed page that the writer is currently
+ * writing to the command file, then waits for the partial page to finish
+ * rendering. It then opens up a new command list "file" and resets the
+ * state of the command list machinery to an initial state as if a new
+ * page were beginning.
+ *
+ * If insufficient memory is available after the 2nd step, the situation
+ * is the same as if it ocurred in a non-async setup: the writer program
+ * simply used up too much memory and cannot continue.
+ *
+ * The first stage of error recovery (no flush) is performed without
+ * flushing out the current page, so failing commands can simply be
+ * restarted after such recovery. This is not true of 2nd stage recovery
+ * (flush): as part of its operation, the flush resets the state of both
+ * writer and renderer to initial values. In this event, the recovery logic
+ * which called clist_try_recover_VMerror_flush() must force any pertinent
+ * state information to be re-emitted before re-issuing the failing command.
+ *
+ * In case of a VMerror, the internal procedures that support the driver
+ * procedures simply return the error code: they do not attempt recovery.
+ * Note that all such procedures must take care that (1) they don't update
+ * any writer state to reflect information written to the band list unless
+ * the write actually succeeds, and (2) they are idempotent, since they may
+ * be re-executed after first-stage VMerror recovery.
+ *
+ * Error recovery is only performed by the driver procedures themselves
+ * (fill_rectangle, copy_mono, fill_path, etc.) and a few other procedures
+ * at the same level of control. The implementation of error recovery is
+ * packaged up in the FOR_RECTS et al macros defined below, but -- as noted
+ * above -- recovery is not fully transparent. Other routines which perform
+ * error recovery are those which open the device, begin a new page, or
+ * reopen the device (put_params).
+ */
+int clist_VMerror_recover(P2(gx_device_clist_writer *, int));
+int clist_VMerror_recover_flush(P2(gx_device_clist_writer *, int));
+
+/* Write out device parameters. */
+int cmd_put_params(P2(gx_device_clist_writer *, gs_param_list *));
+
+/* 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 update stats if no error occurs. */
+#define set_cmd_put_op(dp, cldev, pcls, op, csize)\
+ ( (dp = cmd_put_op(cldev, pcls, csize)) == 0 ?\
+ (cldev)->error_code :\
+ (*dp = cmd_count_op(op, csize), 0) )
+
+/* Add a command for all bands or a range of bands. */
+byte *cmd_put_range_op(P4(gx_device_clist_writer * cldev, int band_min,
+ int band_max, uint size));
+
+#define cmd_put_all_op(cldev, size)\
+ cmd_put_range_op(cldev, 0, (cldev)->nbands - 1, size)
+/* Call cmd_put_all/range_op and update stats if no error occurs. */
+#define set_cmd_put_range_op(dp, cldev, op, bmin, bmax, csize)\
+ ( (dp = cmd_put_range_op(cldev, bmin, bmax, csize)) == 0 ?\
+ (cldev)->error_code :\
+ (*dp = cmd_count_op(op, csize), 0) )
+#define set_cmd_put_all_op(dp, cldev, op, csize)\
+ set_cmd_put_range_op(dp, cldev, op, 0, (cldev)->nbands - 1, csize)
+
+/* 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)
+
+/* Write out the buffered commands, and reset the buffer. */
+/* Return 0 if OK, 1 if OK with low-memory warning, */
+/* or the usual negative error code. */
+int cmd_write_buffer(P2(gx_device_clist_writer * cldev, byte cmd_end));
+
+/* End a page by flushing the buffer and terminating the command list. */
+int clist_end_page(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)
+#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)\
+ ( (pcls)->lop_enabled == ((enable) ^ 1) &&\
+ cmd_put_enable_lop(cldev, pcls, enable) < 0 ?\
+ (cldev)->error_code : 0 )
+#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. */
+int cmd_put_enable_clip(P3(gx_device_clist_writer *, gx_clist_state *, int));
+
+#define cmd_do_enable_clip(cldev, pcls, enable)\
+ ( (pcls)->clip_enabled == ((enable) ^ 1) &&\
+ cmd_put_enable_clip(cldev, pcls, enable) < 0 ?\
+ (cldev)->error_code : 0 )
+#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));
+
+/* Disable (if default) or enable the logical operation, setting it if */
+/* needed. */
+int cmd_update_lop(P3(gx_device_clist_writer *, gx_clist_state *,
+ gs_logical_operation_t));
+
+/*
+ * Define macros for dividing up an operation into bands, per the
+ * template
+
+ FOR_RECTS {
+ ... process rectangle x, y, width, height in band pcls ...
+ } END_RECTS;
+
+ * Note that FOR_RECTS 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).
+ *
+ * If the processing code detects an error that may be a recoverable
+ * VMerror, the code may call ERROR_RECT(), which will attempt to fix the
+ * VMerror by flushing and closing the band and resetting the imager state,
+ * and then restart emitting the entire band. Before flushing the file, the
+ * 'on_error' clause of END_RECTS_ON_ERROR (defaults to the constant 1 if
+ * END_RECT is used) is evaluated and tested. The 'on_error' clause enables
+ * mop-up actions to be executed before flushing, and/or selectively
+ * inhibits the flush, close, reset and restart process. Similarly, the
+ * 'after_recovering' clause of END_RECTS_ON_ERROR allows an action to get
+ * performed after successfully recovering.
+ *
+ * The band processing code may wrap an operation with TRY_RECT { ... }
+ * HANDLE_RECT_UNLESS(code, unless_action) (or HANDLE_RECT(code)). This will
+ * perform local first-stage VMerror recovery, by waiting for some memory to
+ * become free and then retrying the failed operation starting at the
+ * TRY_RECT. If local recovery is unsuccessful, the local recovery code
+ * calls ERROR_RECT.
+ *
+ * In a few cases, the band processing code calls other driver procedures
+ * (e.g., clist_copy_mono calls itself recursively if it must split up the
+ * operation into smaller pieces) or other procedures that may attempt
+ * VMerror recovery. In such cases, the recursive call must not attempt
+ * second-stage VMerror recovery, since the caller would have no way of
+ * knowing that the writer state had been reset. Such recursive calls
+ * should be wrapped in NEST_RECT { ... } UNNEST_RECT, which causes
+ * ERROR_RECT simply to return the error code rather than attempting
+ * recovery. (TRY/HANDLE_RECT will still attempt local recovery, as
+ * described above, but this is harmless since it is transparent.) By
+ * convention, calls to cmd_put_xxx or cmd_set_xxx never attempt recovery
+ * and so never require NEST_RECTs.
+ *
+ * If a put_params call fails, the device will be left in a closed state,
+ * but higher-level code won't notice this fact. We flag this by setting
+ * permanent_error, which prevents writing to the command list.
+ */
+
+#define FOR_RECTS\
+ BEGIN\
+ int yend = y + height;\
+ int band_height = cdev->page_band_height;\
+ int band_code;\
+\
+ if (cdev->permanent_error < 0)\
+ return (cdev->permanent_error);\
+ 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;\
+retry_rect:\
+ ;
+#define NEST_RECT ++cdev->driver_call_nesting;
+#define UNNEST_RECT --cdev->driver_call_nesting
+#define ERROR_RECT(code_value)\
+ BEGIN\
+ band_code = (code_value);\
+ goto error_in_rect;\
+ END
+#define TRY_RECT\
+ BEGIN\
+ do
+#define HANDLE_RECT_UNLESS(codevar, unless_clause)\
+ while (codevar < 0 &&\
+ !(codevar = clist_VMerror_recover(cdev, (codevar)))\
+ );\
+ if (codevar < 0 && !(unless_clause))\
+ ERROR_RECT(codevar);\
+ END
+#define HANDLE_RECT(codevar)\
+ HANDLE_RECT_UNLESS(codevar, 0)
+#define END_RECTS_ON_ERROR(retry_cleanup, is_error, after_recovering)\
+ continue;\
+error_in_rect:\
+ if (cdev->error_is_retryable) {\
+ retry_cleanup;\
+ if ((is_error) &&\
+ cdev->driver_call_nesting == 0 &&\
+ (band_code =\
+ clist_VMerror_recover_flush(cdev, band_code)) >= 0 &&\
+ (after_recovering)\
+ )\
+ goto retry_rect;\
+ }\
+ return band_code;\
+ } while ((y += height) < yend);\
+ END
+#define END_RECTS END_RECTS_ON_ERROR(DO_NOTHING, 1, 1)
+
+/* ------ Exported by gxclrect.c ------ */
+
+/* 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));
+
+/* ------ 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));
+
+/*
+ * Put out commands for a color map (transfer function, black generation, or
+ * undercolor removal). If pid != 0, write the map only if its ID differs
+ * from the current one, and update the saved ID in the case.
+ */
+typedef enum {
+ cmd_map_transfer = 0, /* all transfer functions */
+ cmd_map_transfer_0, /* transfer[0] */
+ cmd_map_transfer_1, /* transfer[1] */
+ cmd_map_transfer_2, /* transfer[2] */
+ cmd_map_transfer_3, /* transfer[3] */
+ cmd_map_ht_transfer, /* transfer fn of most recent halftone order */
+ cmd_map_black_generation,
+ cmd_map_undercolor_removal
+} cmd_map_index;
+int cmd_put_color_map(P4(gx_device_clist_writer * cldev,
+ cmd_map_index map_index,
+ const gx_transfer_map * map, gs_id * pid));
+
+/*
+ * 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));
+
+/* ------ Exported by gxclimag.c ------ */
+
+/*
+ * Add commands to represent a full (device) halftone.
+ * (This routine should probably be in some other module.)
+ * ****** Note: the type parameter is now unnecessary, because device
+ * halftones record the type. ******
+ */
+int cmd_put_halftone(P3(gx_device_clist_writer * cldev,
+ const gx_device_halftone * pdht, gs_halftone_type type));
+
+/* ------ Exported by gxclrast.c for gxclread.c ------ */
+
+/*
+ * Define whether we are actually rendering a band, or just executing
+ * the put_params that occurs at the beginning of each page.
+ */
+typedef enum {
+ playback_action_render,
+ playback_action_setup
+} clist_playback_action;
+
+/* Play back and rasterize one band. */
+int clist_playback_band(P7(clist_playback_action action,
+ gx_device_clist_reader *cdev,
+ stream *s, gx_device *target,
+ int x0, int y0, gs_memory_t *mem));
+
+#endif /* gxcldev_INCLUDED */
diff --git a/pstoraster/gxclimag.c b/pstoraster/gxclimag.c
new file mode 100644
index 000000000..54abed38b
--- /dev/null
+++ b/pstoraster/gxclimag.c
@@ -0,0 +1,1320 @@
+/* Copyright (C) 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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"
+#include "gxfmap.h"
+#include "gxiparam.h"
+#include "gxpath.h"
+#include "strimpl.h" /* for siscale.h */
+#include "siscale.h"
+
+/* Define whether we should use high-level images. */
+/* (See below for additional restrictions.) */
+static bool USE_HL_IMAGES = true;
+
+/* Forward references */
+private int cmd_put_color_mapping(P3(gx_device_clist_writer * cldev,
+ const gs_imager_state * pis,
+ bool write_rgb_to_cmyk));
+private bool check_rect_for_trivial_clip(P5(
+ const gx_clip_path *pcpath, /* May be NULL, clip to evaluate */
+ int px, int py, int qx, int qy /* corners of box to test */
+));
+
+/* ------ Driver procedures ------ */
+
+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)
+{
+ gx_device_clist_writer * const cdev =
+ &((gx_device_clist *)dev)->writer;
+ const byte *orig_data = data; /* for writing tile */
+ int orig_data_x = data_x; /* ditto */
+ int orig_x = x; /* ditto */
+ int orig_width = width; /* ditto */
+ int orig_height = height; /* ditto */
+ int log2_depth = depth >> 1; /* works for 1,2,4 */
+ int y0;
+ int data_x_bit;
+ 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);
+ y0 = y; /* must do after fit_copy */
+
+ /* If non-trivial clipping & complex clipping disabled, default */
+ if ((cdev->disable_mask & clist_disable_complex_clip) &&
+ !check_rect_for_trivial_clip(pcpath, x, y, x + width, y + height)
+ )
+ return gx_default_fill_mask(dev, data, data_x, raster, id,
+ x, y, width, height, pdcolor, depth,
+ lop, pcpath);
+ if (cmd_check_clip_path(cdev, pcpath))
+ cmd_clear_known(cdev, clip_path_known);
+ data_x_bit = data_x << log2_depth;
+ FOR_RECTS {
+ int dx = (data_x_bit & 7) >> log2_depth;
+ const byte *row = data + (y - y0) * raster + (data_x_bit >> 3);
+ int code;
+
+ TRY_RECT {
+ code = cmd_update_lop(cdev, pcls, lop);
+ } HANDLE_RECT(code);
+ if (depth > 1 && !pcls->color_is_alpha) {
+ byte *dp;
+
+ TRY_RECT {
+ code =
+ set_cmd_put_op(dp, cdev, pcls, cmd_opv_set_copy_alpha, 1);
+ } HANDLE_RECT(code);
+ pcls->color_is_alpha = 1;
+ }
+ TRY_RECT {
+ code = cmd_do_write_unknown(cdev, pcls, clip_path_known);
+ if (code >= 0)
+ code = cmd_do_enable_clip(cdev, pcls, pcpath != NULL);
+ } HANDLE_RECT(code);
+ TRY_RECT {
+ code = cmd_put_drawing_color(cdev, pcls, pdcolor);
+ } HANDLE_RECT(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.
+ *
+ * We could handle more RasterOp cases here directly, but it
+ * doesn't seem worth the trouble right now.
+ */
+ if (id != gx_no_bitmap_id && gx_dc_is_pure(pdcolor) &&
+ lop == lop_default
+ ) { /* 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 *) orig_data; /* actually const */
+ tile.raster = raster;
+ tile.size.x = tile.rep_width = orig_width;
+ tile.size.y = tile.rep_height = orig_height;
+ tile.rep_shift = tile.shift = 0;
+ tile.id = id;
+ TRY_RECT {
+ code = clist_change_bits(cdev, pcls, &tile, depth);
+ } HANDLE_RECT_UNLESS(code,
+ (code != gs_error_VMerror || !cdev->error_is_retryable) );
+ if (code < 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 = orig_x, rect.y = y0;
+ rect.width = orig_width, rect.height = yend - y0;
+ rsize = 1 + cmd_sizexy(rect);
+ TRY_RECT {
+ code = 0;
+ if (orig_data_x) {
+ int dx_msb = orig_data_x >> 5;
+
+ code = set_cmd_put_op(dp, cdev, pcls, cmd_opv_set_misc,
+ 2 + cmd_size_w(dx_msb));
+ if (code >= 0) {
+ if (dx_msb) {
+ dp[1] = cmd_set_misc_data_x + 0x20 +
+ (orig_data_x & 0x1f);
+ cmd_put_w(dx_msb, dp + 2);
+ } else
+ dp[1] = cmd_set_misc_data_x + orig_data_x;
+ }
+ }
+ if (code >= 0)
+ code = set_cmd_put_op(dp, cdev, pcls, op, rsize);
+ } HANDLE_RECT(code);
+ dp++;
+ cmd_putxy(rect, dp);
+ pcls->rect = rect;
+ goto end;
+ }
+ }
+copy: /*
+ * The default fill_mask implementation uses strip_copy_rop;
+ * this is exactly what we want.
+ */
+ TRY_RECT {
+ NEST_RECT {
+ code = gx_default_fill_mask(dev, row, dx, raster,
+ (y == y0 && height == orig_height &&
+ dx == orig_data_x ? id :
+ gx_no_bitmap_id),
+ x, y, width, height, pdcolor,
+ depth, lop, pcpath);
+ } UNNEST_RECT;
+ } HANDLE_RECT(code);
+end:
+ ;
+ } END_RECTS;
+ return 0;
+}
+
+/* ------ Bitmap image driver procedures ------ */
+
+/* Define the structure for keeping track of progress through an image. */
+typedef struct clist_image_enum_s {
+ gx_image_enum_common;
+ /* Arguments of begin_image */
+ gs_memory_t *memory;
+ gs_image_t image;
+ gx_drawing_color dcolor;
+ gs_int_rect rect;
+ const gs_imager_state *pis;
+ const gx_clip_path *pcpath;
+ /* Set at creation time */
+ gx_image_enum_common_t *default_info;
+ gs_image_format_t format;
+ gs_int_point support; /* extra source pixels for interpolation */
+ int bits_per_plane; /* bits per pixel per plane */
+ gs_matrix matrix; /* image space -> device space */
+ bool uses_color;
+ byte color_space;
+ int ymin, ymax;
+ bool map_rgb_to_cmyk;
+ /* begin_image command prepared & ready to output */
+ byte begin_image_command[3 + 2 * cmd_sizew_max + 14 * sizeof(float) +
+ 4 * cmd_sizew_max];
+ int begin_image_command_length;
+ /* Updated dynamically */
+ int y;
+ bool color_map_is_known;
+} 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);
+
+private image_enum_proc_plane_data(clist_image_plane_data);
+private image_enum_proc_end_image(clist_image_end_image);
+private const gx_image_enum_procs_t clist_image_enum_procs =
+{
+ clist_image_plane_data, clist_image_end_image
+};
+
+/* Forward declarations */
+private bool image_band_box(P5(gx_device * dev, const clist_image_enum * pie,
+ int y, int h, gs_int_rect * pbox));
+private int begin_image_command(P5(byte *cbuf, const gs_image_t *pim,
+ gs_image_format_t format,
+ int num_components, bool indexed));
+private int cmd_image_plane_data(P7(gx_device_clist_writer * cldev,
+ gx_clist_state * pcls,
+ const gx_image_plane_t * planes,
+ const gx_image_enum_common_t * pie,
+ uint bytes_per_plane,
+ const uint * offsets, int h));
+private uint clist_image_unknowns(P2(gx_device *dev,
+ const clist_image_enum *pie));
+private int write_image_end_all(P2(gx_device *dev,
+ const clist_image_enum *pie));
+
+/*
+ * Since currently we are limited to writing a single subrectangle of the
+ * image for each band, images that are rotated by angles other than
+ * multiples of 90 degrees may wind up writing many copies of the data.
+ * Eventually we will fix this by breaking up the image into multiple
+ * subrectangles, but for now, don't use the high-level approach if it would
+ * cause the data to explode because of this.
+ */
+private bool
+image_matrix_ok_to_band(const gs_matrix * pmat)
+{
+ double t;
+
+ /* Don't band if the matrix is (nearly) singular. */
+ if (fabs(pmat->xx * pmat->yy - pmat->xy * pmat->yx) < 0.001)
+ return false;
+ if (is_xxyy(pmat) || is_xyyx(pmat))
+ return true;
+ t = (fabs(pmat->xx) + fabs(pmat->yy)) /
+ (fabs(pmat->xy) + fabs(pmat->yx));
+ return (t < 0.2 || t > 5);
+}
+
+/* Start processing an image. */
+int
+clist_begin_image(gx_device * dev,
+ const gs_imager_state * pis, const gs_image_t * pim,
+ gs_image_format_t format, const gs_int_rect * prect,
+ const gx_drawing_color * pdcolor, const gx_clip_path * pcpath,
+ gs_memory_t * mem, gx_image_enum_common_t ** pinfo)
+{
+ gx_device_clist_writer * const cdev =
+ &((gx_device_clist *)dev)->writer;
+ clist_image_enum *pie;
+ int base_index;
+ bool indexed;
+ int num_components;
+ int bits_per_pixel;
+ bool uses_color;
+ bool varying_depths = false;
+ gs_matrix mat;
+ gs_rect sbox, dbox;
+ bool use_default_image;
+ 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 = (gx_image_enum_common_t *) pie;
+ /* num_planes and plane_depths[] are set later, */
+ /* by gx_image_enum_common_init. */
+ if (pim->ImageMask) {
+ base_index = gs_color_space_index_DeviceGray; /* arbitrary */
+ indexed = false;
+ num_components = 1;
+ uses_color = true;
+ } 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);
+ }
+ uses_color = pim->CombineWithColor && rop3_uses_T(pis->log_op);
+ }
+ code = gx_image_enum_common_init((gx_image_enum_common_t *) pie,
+ (const gs_image_common_t *) pim,
+ &clist_image_enum_procs, dev,
+ pim->BitsPerComponent,
+ num_components, format);
+ {
+ int i;
+
+ for (i = 1; i < pie->num_planes; ++i)
+ varying_depths |= pie->plane_depths[i] != pie->plane_depths[0];
+ }
+ use_default_image =
+ (code < 0 ||
+ !USE_HL_IMAGES || /* Always use the default. */
+ (cdev->disable_mask & clist_disable_hl_image) ||
+ cdev->image_enum_id != gs_no_id || /* Can't handle nested images */
+ /****** CAN'T HANDLE CIE COLOR YET ******/
+ base_index > gs_color_space_index_DeviceCMYK ||
+ /****** CAN'T HANDLE INDEXED COLOR (READING MAP) ******/
+ indexed ||
+ /****** CAN'T HANDLE NON-PURE COLORS YET ******/
+ (uses_color && !gx_dc_is_pure(pdcolor)) ||
+ /****** CAN'T HANDLE IMAGES WITH ALPHA YET ******/
+ pim->Alpha ||
+ /****** CAN'T HANDLE IMAGES WITH IRREGULAR DEPTHS ******/
+ varying_depths ||
+ (code = gs_matrix_invert(&pim->ImageMatrix, &mat)) < 0 ||
+ (code = gs_matrix_multiply(&mat, &ctm_only(pis), &mat)) < 0 ||
+ !(cdev->disable_mask & clist_disable_nonrect_hl_image ?
+ (is_xxyy(&mat) || is_xyyx(&mat)) :
+ image_matrix_ok_to_band(&mat))
+ );
+ if (!use_default_image) {
+ int bytes_per_plane, bytes_per_row;
+
+ bits_per_pixel = pim->BitsPerComponent * num_components;
+ pie->default_info = 0;
+ pie->image = *pim;
+ pie->dcolor = *pdcolor;
+ if (prect)
+ pie->rect = *prect;
+ else {
+ pie->rect.p.x = 0, pie->rect.p.y = 0;
+ pie->rect.q.x = pim->Width, pie->rect.q.y = pim->Height;
+ }
+ pie->pis = pis;
+ pie->pcpath = pcpath;
+ pie->format = format;
+ pie->bits_per_plane = bits_per_pixel / pie->num_planes;
+ pie->matrix = mat;
+ pie->uses_color = uses_color;
+ pie->color_space = (base_index << 4) |
+ (indexed ? (pim->ColorSpace->params.indexed.use_proc ? 12 : 8) : 0);
+ pie->y = pie->rect.p.y;
+
+ /* Image row has to fit in cmd writer's buffer */
+ bytes_per_plane =
+ (pim->Width * pie->bits_per_plane + 7) >> 3;
+ bytes_per_row = bytes_per_plane * pie->num_planes;
+ bytes_per_row = max(bytes_per_row, 1);
+ use_default_image = cmd_largest_size + bytes_per_row >
+ cdev->cend - cdev->cbuf;
+ }
+ if (!use_default_image) {
+ sbox.p.x = pie->rect.p.x;
+ sbox.p.y = pie->rect.p.y;
+ sbox.q.x = pie->rect.q.x;
+ sbox.q.y = pie->rect.q.y;
+ gs_bbox_transform(&sbox, &mat, &dbox);
+
+ if (cdev->disable_mask & clist_disable_complex_clip)
+ use_default_image =
+ !check_rect_for_trivial_clip( pcpath,
+ (int)(dbox.p.x), (int)(dbox.p.y),
+ (int)ceil(dbox.q.x), (int)ceil(dbox.q.y) );
+ }
+ pie->map_rgb_to_cmyk = dev->color_info.num_components == 4 &&
+ base_index == gs_color_space_index_DeviceRGB;
+ pie->color_map_is_known = false;
+ if (use_default_image) {
+ int code = gx_default_begin_image(dev, pis, pim, format, prect,
+ pdcolor, pcpath, mem,
+ &pie->default_info);
+
+ if (code < 0)
+ gs_free_object(mem, pie, "clist_begin_image");
+ return code;
+ }
+
+ /* Create the begin_image command. */
+
+ pie->begin_image_command_length =
+ begin_image_command(pie->begin_image_command, pim, format,
+ num_components, indexed);
+ if (pim->Interpolate)
+ pie->support.x = pie->support.y = max_support + 1;
+ else
+ pie->support.x = pie->support.y = 0;
+ sbox.p.x = pie->rect.p.x - pie->support.x;
+ sbox.p.y = pie->rect.p.y - pie->support.y;
+ sbox.q.x = pie->rect.q.x + pie->support.x;
+ sbox.q.y = pie->rect.q.y + pie->support.y;
+ gs_bbox_transform(&sbox, &pie->matrix, &dbox);
+ {
+ int y0 = (int)floor(dbox.p.y - 0.51); /* adjust + rounding slop */
+ int y1 = (int)ceil(dbox.q.y + 0.51); /* ditto */
+
+ pie->ymin = max(y0, 0);
+ pie->ymax = min(y1, dev->height);
+ }
+
+ /*
+ * Make sure the CTM, color space, and clipping region (and, for
+ * masked images or images with CombineWithColor, the current color)
+ * are known at the time of the begin_image command.
+ */
+ cmd_clear_known(cdev, clist_image_unknowns(dev, pie) | begin_image_known);
+
+ cdev->image_enum_id = pie->id;
+ return 0;
+}
+
+/* Process the next piece of an image. */
+private int
+clist_image_plane_data(gx_device * dev,
+ gx_image_enum_common_t * info, const gx_image_plane_t * planes, int yh)
+{
+ gx_device_clist_writer * const cdev =
+ &((gx_device_clist *)dev)->writer;
+ clist_image_enum *pie = (clist_image_enum *) info;
+ gs_rect sbox, dbox;
+ int y0, y1;
+ int y, height; /* for BEGIN/END_RECT */
+ int code;
+
+ if (pie->default_info)
+ return gx_image_plane_data(pie->default_info, planes, yh);
+#ifdef DEBUG
+ if (pie->id != cdev->image_enum_id) {
+ lprintf2("end_image id = %lu != clist image id = %lu!\n",
+ (ulong) pie->id, (ulong) cdev->image_enum_id);
+ return_error(gs_error_Fatal);
+ }
+#endif
+ /****** CAN'T HANDLE VARYING data_x VALUES YET ******/
+ {
+ int i;
+
+ for (i = 1; i < info->num_planes; ++i)
+ if (planes[i].data_x != planes[0].data_x)
+ return_error(gs_error_rangecheck);
+ }
+ sbox.p.x = pie->rect.p.x - pie->support.x;
+ sbox.p.y = (y0 = pie->y) - pie->support.y;
+ sbox.q.x = pie->rect.q.x + pie->support.x;
+ sbox.q.y = (y1 = pie->y += yh) + pie->support.y;
+ gs_bbox_transform(&sbox, &pie->matrix, &dbox);
+ /*
+ * In order to keep the band list consistent, we must write out
+ * the image data in precisely those bands whose begin_image
+ * Y range includes the respective image scan lines. Because of
+ * rounding, we must expand the dbox by a little extra, and then
+ * use image_band_box to calculate the precise range for each band.
+ * This is slow, but we don't see any faster way to do it in the
+ * general case.
+ */
+ {
+ int ry0 = (int)floor(dbox.p.y) - 2;
+ int ry1 = (int)ceil(dbox.q.y) + 2;
+ int band_height = cdev->page_band_height;
+
+ /*
+ * Make sure we don't go beyond the Y range determined at
+ * begin_image time.
+ */
+ if (ry0 < pie->ymin)
+ ry0 = pie->ymin;
+ if (ry1 > pie->ymax)
+ ry1 = pie->ymax;
+ y = ry0 / band_height * band_height;
+ height = min(round_up(ry1, band_height), dev->height) - y;
+ }
+
+ FOR_RECTS {
+ /*
+ * Just transmit the subset of the data that intersects this band.
+ * Note that y and height always define a complete band.
+ */
+ gs_int_rect ibox;
+#define bx0 ibox.p.x
+#define by0 ibox.p.y
+#define bx1 ibox.q.x
+#define by1 ibox.q.y
+ int bpp = pie->bits_per_plane;
+ int num_planes = pie->num_planes;
+ uint offsets[gs_image_max_components];
+ int i, iy, ih, xskip, nrows;
+ uint bytes_per_plane, bytes_per_row, rows_per_cmd;
+
+ if (!image_band_box(dev, pie, y, height, &ibox))
+ continue;
+
+ /* Write out begin_image & its preamble for this band */
+ if (!(pcls->known & begin_image_known)) {
+ gs_logical_operation_t lop = pie->pis->log_op;
+ byte *dp;
+ gs_int_rect entire_box;
+ byte cb = pie->begin_image_command[0];
+ byte *bp = pie->begin_image_command +
+ pie->begin_image_command_length;
+ uint len;
+ uint band_ymax, band_ymin;
+
+ /* Compute intersection of entire band & entire image src rect */
+ band_ymax = min(band_end, pie->ymax);
+ band_ymin = max(band_end - band_height, pie->ymin);
+ if (!image_band_box(dev, pie, band_ymin,
+ band_ymax - band_ymin, &entire_box))
+ continue;
+
+ /* Make sure the imager state is up to date. */
+ TRY_RECT {
+ code = (pie->color_map_is_known ? 0 :
+ cmd_put_color_mapping(cdev, pie->pis,
+ pie->map_rgb_to_cmyk));
+ pie->color_map_is_known = true;
+ if (code >= 0)
+ code = cmd_do_write_unknown(cdev, pcls,
+ ctm_known | clip_path_known | color_space_known);
+ if (code >= 0)
+ code = cmd_do_enable_clip(cdev, pcls, pie->pcpath != NULL);
+ if (code >= 0)
+ code = cmd_update_lop(cdev, pcls, lop);
+ } HANDLE_RECT(code);
+ if (pie->uses_color) {
+ TRY_RECT {
+ code = cmd_put_drawing_color(cdev, pcls, &pie->dcolor);
+ } HANDLE_RECT(code);
+ }
+ if (entire_box.p.x != 0 || entire_box.p.y != 0 ||
+ entire_box.q.x != pie->image.Width ||
+ entire_box.q.y != pie->image.Height
+ ) {
+ cb |= 1 << 0;
+ cmd_put2w(entire_box.p.x, entire_box.p.y, bp);
+ cmd_put2w(pie->image.Width - entire_box.q.x,
+ pie->image.Height - entire_box.q.y, bp);
+ }
+ len = bp - pie->begin_image_command;
+ TRY_RECT {
+ code =
+ set_cmd_put_op(dp, cdev, pcls, cmd_opv_begin_image,
+ 1 + len);
+ } HANDLE_RECT(code);
+ dp[1] = cb;
+ memcpy(dp + 2, pie->begin_image_command + 1, len - 1);
+
+ /* Mark band's begin_image as known */
+ pcls->known |= begin_image_known;
+ }
+
+ if (by0 < y0)
+ by0 = y0;
+ if (by1 > y1)
+ by1 = y1;
+ /*
+ * Make sure we're skipping an integral number of pixels, by
+ * truncating the initial X coordinate to the next lower
+ * value that is an exact multiple of a byte.
+ */
+ xskip = bx0 & -(int)"\001\010\004\010\002\010\004\010"[bpp & 7];
+ for (i = 0; i < num_planes; ++i)
+ offsets[i] = (by0 - y0) * planes[i].raster + ((xskip * bpp) >> 3);
+ xskip = bx0 - xskip;
+ bytes_per_plane = ((xskip + bx1 - bx0) * bpp + 7) >> 3;
+ bytes_per_row = bytes_per_plane * pie->num_planes;
+ rows_per_cmd =
+ (cbuf_size - cmd_largest_size) / max(bytes_per_row, 1);
+
+ if (rows_per_cmd == 0) { /* The reader will have to buffer a row separately. */
+ rows_per_cmd = 1;
+ }
+ for (iy = by0, ih = by1 - by0; ih > 0; iy += nrows, ih -= nrows) {
+ nrows = min(ih, rows_per_cmd);
+ TRY_RECT {
+ code = cmd_image_plane_data(cdev, pcls, planes, info,
+ bytes_per_plane, offsets, nrows);
+ } HANDLE_RECT(code);
+ for (i = 0; i < num_planes; ++i)
+ offsets[i] += planes[i].raster * nrows;
+ }
+#undef bx0
+#undef by0
+#undef bx1
+#undef by1
+ } END_RECTS_ON_ERROR(\
+ BEGIN\
+ ++cdev->ignore_lo_mem_warnings;\
+ NEST_RECT {\
+ code = write_image_end_all(dev, pie);\
+ } UNNEST_RECT;\
+ --cdev->ignore_lo_mem_warnings;\
+ END,\
+ (code < 0 ? (band_code = code) : code) >= 0,\
+ (cmd_clear_known(cdev,\
+ clist_image_unknowns(dev, pie) | begin_image_known),\
+ pie->color_map_is_known = false, true)\
+ );
+ /* Update sub-rect in case memory exhaustion forced end_image */
+ if (!pie->image.Interpolate)
+ pie->rect.p.y += yh; /* interpolate & mem recovery currently incompat */
+ return pie->y >= pie->rect.q.y;
+}
+
+/* Clean up by releasing the buffers. */
+private int
+clist_image_end_image(gx_device * dev, gx_image_enum_common_t * info,
+ bool draw_last)
+{
+ gx_device_clist_writer * const cdev =
+ &((gx_device_clist *)dev)->writer;
+ clist_image_enum *pie = (clist_image_enum *) info;
+ int code;
+
+ if (pie->default_info)
+ code = gx_default_end_image(dev, pie->default_info, draw_last);
+ else {
+#ifdef DEBUG
+ if (pie->id != cdev->image_enum_id) {
+ lprintf2("end_image id = %lu != clist image id = %lu!\n",
+ (ulong) pie->id, (ulong) cdev->image_enum_id);
+ return_error(gs_error_Fatal);
+ }
+#endif
+ NEST_RECT {
+ do {
+ code = write_image_end_all(dev, pie);
+ } while (code < 0 && cdev->error_is_retryable &&
+ (code = clist_VMerror_recover(cdev, code)) >= 0
+ );
+ /* if couldn't write successsfully, do a hard flush */
+ if (code < 0 && cdev->error_is_retryable) {
+ int retry_code;
+ ++cdev->ignore_lo_mem_warnings;
+ retry_code = write_image_end_all(dev, pie); /* force it out */
+ --cdev->ignore_lo_mem_warnings;
+ if (retry_code >= 0 && cdev->driver_call_nesting == 0)
+ code = clist_VMerror_recover_flush(cdev, code);
+ }
+ } UNNEST_RECT;
+ cdev->image_enum_id = gs_no_id;
+ }
+ gs_free_object(pie->memory, pie, "clist_image_end_image");
+ return code;
+}
+
+/* Start processing a general image. */
+int
+clist_begin_typed_image(gx_device * dev,
+ const gs_imager_state * pis, const gs_matrix * pmat,
+ const gs_image_common_t * pim, const gs_int_rect * prect,
+ const gx_drawing_color * pdcolor, const gx_clip_path * pcpath,
+ gs_memory_t * mem, gx_image_enum_common_t ** pinfo)
+{
+ /****** NYI ******/
+ return gx_default_begin_typed_image(dev, pis, pmat, pim, prect,
+ pdcolor, pcpath, mem, pinfo);
+}
+
+/* Create a compositor device. */
+int
+clist_create_compositor(gx_device * dev,
+ gx_device ** pcdev, const gs_composite_t * pcte,
+ const gs_imager_state * pis, gs_memory_t * mem)
+{
+ /****** NYI ******/
+ return gx_no_create_compositor(dev, pcdev, pcte, pis, mem);
+}
+
+/* ------ Utilities ------ */
+
+/* Add commands to represent a halftone order. */
+private int
+cmd_put_ht_order(gx_device_clist_writer * cldev, const gx_ht_order * porder,
+ gs_ht_separation_name cname,
+ int component /* -1 = default/gray/black screen */ )
+{
+ byte command[cmd_max_intsize(sizeof(long)) * 8];
+ byte *cp;
+ uint len;
+ byte *dp;
+ uint i, n;
+ int code;
+
+ /* Put out the order parameters. */
+ cp = cmd_put_w(component + 1, command);
+ if (component >= 0)
+ cp = cmd_put_w(cname, cp);
+ 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;
+ code = set_cmd_put_all_op(dp, cldev, cmd_opv_set_ht_order, len + 1);
+ if (code < 0)
+ return code;
+ memcpy(dp + 1, command, len);
+
+ /* Put out the transfer function, if any. */
+ code = cmd_put_color_map(cldev, cmd_map_ht_transfer, porder->transfer,
+ NULL);
+ if (code < 0)
+ return code;
+
+ /* Put out the levels array. */
+#define nlevels min((cbuf_size - 2) / sizeof(*porder->levels), 255)
+ for (i = 0; i < porder->num_levels; i += n) {
+ n = porder->num_levels - i;
+ if (n > nlevels)
+ n = nlevels;
+ code = set_cmd_put_all_op(dp, cldev, cmd_opv_set_ht_data,
+ 2 + n * sizeof(*porder->levels));
+ if (code < 0)
+ return code;
+ dp[1] = n;
+ memcpy(dp + 2, porder->levels + i, n * sizeof(*porder->levels));
+ }
+#undef nlevels
+
+ /* Put out the bits array. */
+#define nbits min((cbuf_size - 2) / sizeof(*porder->bits), 255)
+ for (i = 0; i < porder->num_bits; i += n) {
+ n = porder->num_bits - i;
+ if (n > nbits)
+ n = nbits;
+ code = set_cmd_put_all_op(dp, cldev, cmd_opv_set_ht_data,
+ 2 + n * sizeof(*porder->bits));
+ if (code < 0)
+ return code;
+ dp[1] = n;
+ memcpy(dp + 2, porder->bits + i, n * sizeof(*porder->bits));
+ }
+#undef nbits
+
+ return 0;
+}
+
+/* Add commands to represent a full (device) halftone. */
+/* We put out the default/gray/black screen last so that the reading */
+/* pass can recognize the end of the halftone. */
+int
+cmd_put_halftone(gx_device_clist_writer * cldev, const gx_device_halftone * pdht,
+ gs_halftone_type type)
+{
+ uint num_comp = (pdht->components == 0 ? 0 : pdht->num_comp);
+
+ {
+ byte *dp;
+ int code = set_cmd_put_all_op(dp, cldev, cmd_opv_set_misc,
+ 2 + cmd_size_w(num_comp));
+
+ if (code < 0)
+ return code;
+ dp[1] = cmd_set_misc_halftone + type;
+ cmd_put_w(num_comp, dp + 2);
+ }
+ if (num_comp == 0)
+ return cmd_put_ht_order(cldev, &pdht->order,
+ gs_ht_separation_Default, -1);
+ {
+ int i;
+
+ for (i = num_comp; --i >= 0;) {
+ int code = cmd_put_ht_order(cldev, &pdht->components[i].corder,
+ pdht->components[i].cname, i);
+
+ if (code < 0)
+ return code;
+ }
+ }
+ return 0;
+}
+
+/* Write out any necessary color mapping data. */
+private int
+cmd_put_color_mapping(gx_device_clist_writer * cldev,
+ const gs_imager_state * pis, bool write_rgb_to_cmyk)
+{
+ int code;
+ const gx_device_halftone *pdht = pis->dev_ht;
+
+ /* Put out the halftone. */
+ if (pdht->id != cldev->device_halftone_id) {
+ code = cmd_put_halftone(cldev, pdht, pis->halftone->type);
+ if (code < 0)
+ return code;
+ cldev->device_halftone_id = pdht->id;
+ }
+ /* If we need to map RGB to CMYK, put out b.g. and u.c.r. */
+ if (write_rgb_to_cmyk) {
+ code = cmd_put_color_map(cldev, cmd_map_black_generation,
+ pis->black_generation,
+ &cldev->black_generation_id);
+ if (code < 0)
+ return code;
+ code = cmd_put_color_map(cldev, cmd_map_undercolor_removal,
+ pis->undercolor_removal,
+ &cldev->undercolor_removal_id);
+ if (code < 0)
+ return code;
+ }
+ /* Now put out the transfer functions. */
+ {
+ uint which = 0;
+ bool all_same = true;
+ int i;
+
+ for (i = 0; i < countof(cldev->transfer_ids); ++i) {
+ if (pis->effective_transfer.indexed[i]->id !=
+ cldev->transfer_ids[i]
+ )
+ which |= 1 << i;
+ if (pis->effective_transfer.indexed[i]->id !=
+ pis->effective_transfer.indexed[0]->id
+ )
+ all_same = false;
+ }
+ /* There are 3 cases for transfer functions: nothing to write, */
+ /* a single function, and multiple functions. */
+ if (which == 0)
+ return 0;
+ if (which == (1 << countof(cldev->transfer_ids)) - 1 && all_same) {
+ code = cmd_put_color_map(cldev, cmd_map_transfer,
+ pis->effective_transfer.indexed[0],
+ &cldev->transfer_ids[0]);
+ if (code < 0)
+ return code;
+ for (i = 1; i < countof(cldev->transfer_ids); ++i)
+ cldev->transfer_ids[i] = cldev->transfer_ids[0];
+ } else
+ for (i = 0; i < countof(cldev->transfer_ids); ++i) {
+ code = cmd_put_color_map(cldev,
+ (cmd_map_index) (cmd_map_transfer_0 + i),
+ pis->effective_transfer.indexed[i],
+ &cldev->transfer_ids[i]);
+ if (code < 0)
+ return code;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Compute the subrectangle of an image that intersects a band;
+ * return false if it is empty.
+ * It is OK for this to be too large; in fact, with the present
+ * algorithm, it will be quite a bit too large if the transformation isn't
+ * well-behaved ("well-behaved" meaning either xy = yx = 0 or xx = yy = 0).
+ */
+#define I_FLOOR(x) ((int)floor(x))
+#define I_CEIL(x) ((int)ceil(x))
+private void
+box_merge_point(gs_int_rect * pbox, floatp x, floatp y)
+{
+ int t;
+
+ if ((t = I_FLOOR(x)) < pbox->p.x)
+ pbox->p.x = t;
+ if ((t = I_CEIL(x)) > pbox->q.x)
+ pbox->q.x = t;
+ if ((t = I_FLOOR(y)) < pbox->p.y)
+ pbox->p.y = t;
+ if ((t = I_CEIL(y)) > pbox->q.y)
+ pbox->q.y = t;
+}
+private bool
+image_band_box(gx_device * dev, const clist_image_enum * pie, int y, int h,
+ gs_int_rect * pbox)
+{
+ fixed by0 = int2fixed(y);
+ fixed by1 = int2fixed(y + h);
+ int
+ px = pie->rect.p.x, py = pie->rect.p.y, qx = pie->rect.q.x, qy = pie->rect.q.y;
+ gs_fixed_rect cbox; /* device clipping box */
+ gs_rect bbox; /* cbox intersected with band */
+
+ /* Intersect the device clipping box and the band. */
+ (*dev_proc(dev, get_clipping_box)) (dev, &cbox);
+ /* The fixed_half here is to allow for adjustment. */
+ bbox.p.x = fixed2float(cbox.p.x - fixed_half);
+ bbox.q.x = fixed2float(cbox.q.x + fixed_half);
+ bbox.p.y = fixed2float(max(cbox.p.y, by0) - fixed_half);
+ bbox.q.y = fixed2float(min(cbox.q.y, by1) + fixed_half);
+#ifdef DEBUG
+ if (gs_debug_c('b')) {
+ dlprintf6("[b]band box for (%d,%d),(%d,%d), band (%d,%d) =>\n",
+ px, py, qx, qy, y, y + h);
+ dlprintf10(" (%g,%g),(%g,%g), matrix=[%g %g %g %g %g %g]\n",
+ bbox.p.x, bbox.p.y, bbox.q.x, bbox.q.y,
+ pie->matrix.xx, pie->matrix.xy, pie->matrix.yx,
+ pie->matrix.yy, pie->matrix.tx, pie->matrix.ty);
+ }
+#endif
+ if (is_xxyy(&pie->matrix) || is_xyyx(&pie->matrix)) {
+ /*
+ * The inverse transform of the band is a rectangle aligned with
+ * the coordinate axes, so we can just intersect it with the
+ * image subrectangle.
+ */
+ gs_rect ibox; /* bbox transformed back to image space */
+
+ if (gs_bbox_transform_inverse(&bbox, &pie->matrix, &ibox) < 0)
+ return false;
+ pbox->p.x = max(px, I_FLOOR(ibox.p.x));
+ pbox->q.x = min(qx, I_CEIL(ibox.q.x));
+ pbox->p.y = max(py, I_FLOOR(ibox.p.y));
+ pbox->q.y = min(qy, I_CEIL(ibox.q.y));
+ } else {
+ /*
+ * The inverse transform of the band is not aligned with the
+ * axes, i.e., is a general parallelogram. To compute an exact
+ * bounding box, we need to find the intersections of this
+ * parallelogram with the image subrectangle.
+ *
+ * There is probably a much more efficient way to do this
+ * computation, but we don't know what it is.
+ */
+ gs_point rect[4];
+ gs_point corners[5];
+ int i;
+
+ /* Store the corners of the image rectangle. */
+ rect[0].x = rect[3].x = px;
+ rect[1].x = rect[2].x = qx;
+ rect[0].y = rect[1].y = py;
+ rect[2].y = rect[3].y = qy;
+ /*
+ * Compute the corners of the clipped band in image space. If
+ * the matrix is singular or an overflow occurs, the result will
+ * be nonsense: in this case, there isn't anything useful we
+ * can do, so return an empty intersection.
+ */
+ if (gs_point_transform_inverse(bbox.p.x, bbox.p.y, &pie->matrix,
+ &corners[0]) < 0 ||
+ gs_point_transform_inverse(bbox.q.x, bbox.p.y, &pie->matrix,
+ &corners[1]) < 0 ||
+ gs_point_transform_inverse(bbox.q.x, bbox.q.y, &pie->matrix,
+ &corners[2]) < 0 ||
+ gs_point_transform_inverse(bbox.p.x, bbox.q.y, &pie->matrix,
+ &corners[3]) < 0
+ ) {
+ if_debug0('b', "[b]can't inverse-transform a band corner!\n");
+ return false;
+ }
+ corners[4] = corners[0];
+ pbox->p.x = qx, pbox->p.y = qy;
+ pbox->q.x = px, pbox->q.y = py;
+ /*
+ * We iterate over both the image rectangle and the band
+ * parallelogram in a single loop for convenience, even though
+ * there is no coupling between the two.
+ */
+ for (i = 0; i < 4; ++i) {
+ gs_point pa, pt;
+ double dx, dy;
+
+ /* Check the image corner for being inside the band. */
+ pa = rect[i];
+ gs_point_transform(pa.x, pa.y, &pie->matrix, &pt);
+ if (pt.x >= bbox.p.x && pt.x <= bbox.q.x &&
+ pt.y >= bbox.p.y && pt.y <= bbox.q.y
+ )
+ box_merge_point(pbox, pa.x, pa.y);
+ /* Check the band corner for being inside the image. */
+ pa = corners[i];
+ if (pa.x >= px && pa.x <= qx && pa.y >= py && pa.y <= qy)
+ box_merge_point(pbox, pa.x, pa.y);
+ /* Check for intersections of band edges with image edges. */
+ dx = corners[i + 1].x - pa.x;
+ dy = corners[i + 1].y - pa.y;
+#define in_range(t, tc, p, q)\
+ (0 <= t && t <= 1 && (t = tc) >= p && t <= q)
+ if (dx != 0) {
+ double t = (px - pa.x) / dx;
+
+ if_debug3('b', " (px) t=%g => (%d,%g)\n",
+ t, px, pa.y + t * dy);
+ if (in_range(t, pa.y + t * dy, py, qy))
+ box_merge_point(pbox, (floatp) px, t);
+ t = (qx - pa.x) / dx;
+ if_debug3('b', " (qx) t=%g => (%d,%g)\n",
+ t, qx, pa.y + t * dy);
+ if (in_range(t, pa.y + t * dy, py, qy))
+ box_merge_point(pbox, (floatp) qx, t);
+ }
+ if (dy != 0) {
+ double t = (py - pa.y) / dy;
+
+ if_debug3('b', " (py) t=%g => (%g,%d)\n",
+ t, pa.x + t * dx, py);
+ if (in_range(t, pa.x + t * dx, px, qx))
+ box_merge_point(pbox, t, (floatp) py);
+ t = (qy - pa.y) / dy;
+ if_debug3('b', " (qy) t=%g => (%g,%d)\n",
+ t, pa.x + t * dx, qy);
+ if (in_range(t, pa.x + t * dx, px, qx))
+ box_merge_point(pbox, t, (floatp) qy);
+ }
+#undef in_range
+ }
+ }
+ if_debug4('b', " => (%d,%d),(%d,%d)\n", pbox->p.x, pbox->p.y,
+ pbox->q.x, pbox->q.y);
+ /*
+ * If necessary, add pixels around the edges so we will have
+ * enough information to do interpolation.
+ */
+ if ((pbox->p.x -= pie->support.x) < pie->rect.p.x)
+ pbox->p.x = pie->rect.p.x;
+ if ((pbox->p.y -= pie->support.y) < pie->rect.p.y)
+ pbox->p.y = pie->rect.p.y;
+ if ((pbox->q.x += pie->support.x) > pie->rect.q.x)
+ pbox->q.x = pie->rect.q.x;
+ if ((pbox->q.y += pie->support.y) > pie->rect.q.y)
+ pbox->q.y = pie->rect.q.y;
+ return (pbox->p.x < pbox->q.x && pbox->p.y < pbox->q.y);
+}
+
+/* Determine which image-related properties are unknown */
+private uint /* mask of unknown properties(see pcls->known) */
+clist_image_unknowns(gx_device *dev, const clist_image_enum *pie)
+{
+ gx_device_clist_writer * const cdev =
+ &((gx_device_clist *)dev)->writer;
+ const gs_imager_state *const pis = pie->pis;
+ uint unknown = 0;
+
+ /*
+ * Determine if the CTM, color space, and clipping region (and, for
+ * masked images or images with CombineWithColor, the current color)
+ * are unknown. Set the device state in anticipation of the values
+ * becoming known.
+ */
+ if (cdev->imager_state.ctm.xx != pis->ctm.xx ||
+ cdev->imager_state.ctm.xy != pis->ctm.xy ||
+ cdev->imager_state.ctm.yx != pis->ctm.yx ||
+ cdev->imager_state.ctm.yy != pis->ctm.yy ||
+ cdev->imager_state.ctm.tx != pis->ctm.tx ||
+ cdev->imager_state.ctm.ty != pis->ctm.ty
+ ) {
+ unknown |= ctm_known;
+ cdev->imager_state.ctm = pis->ctm;
+ }
+ /****** hival CHECK IS NOT SUFFICIENT ******/
+ if (cdev->color_space != pie->color_space ||
+ ((cdev->color_space & 8) != 0 &&
+ cdev->indexed_params.hival !=
+ pie->image.ColorSpace->params.indexed.hival)
+ ) {
+ unknown |= color_space_known;
+ cdev->color_space = pie->color_space;
+ if (cdev->color_space & 8)
+ cdev->indexed_params = pie->image.ColorSpace->params.indexed;
+ }
+ if (cmd_check_clip_path(cdev, pie->pcpath))
+ unknown |= clip_path_known;
+
+ return unknown;
+}
+
+/* Construct the begin_image command. */
+private int
+begin_image_command(byte *cbuf, const gs_image_t *pim,
+ gs_image_format_t format, int num_components,
+ bool indexed)
+{
+ byte *cp;
+ byte b;
+
+ if (pim->ImageMask)
+ 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);
+ }
+ {
+ byte b2 = 0;
+
+ if (format != gs_image_format_chunky) {
+ b |= 1 << 4;
+ b2 |= format << 6;
+ }
+ if (pim->Interpolate) {
+ b |= 1 << 4;
+ b2 |= 1 << 5;
+ }
+ if (pim->Alpha) {
+ b |= 1 << 4;
+ b2 |= pim->Alpha << 3;
+ }
+ if (b & (1 << 4)) {
+ cbuf[1] = b2;
+ cp = cbuf + 2;
+ } else
+ cp = cbuf + 1;
+ }
+ cmd_put2w(pim->Width, pim->Height, cp);
+ 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 << (8 - num_decode);
+ }
+ }
+ if ((pim->ImageMask ? pim->adjust : pim->CombineWithColor))
+ b |= 1 << 1;
+ cbuf[0] = b;
+ return cp - cbuf;
+}
+
+/* Write data for a partial image. */
+private int
+cmd_image_plane_data(gx_device_clist_writer * cldev, gx_clist_state * pcls,
+ const gx_image_plane_t * planes, const gx_image_enum_common_t * pie,
+ uint bytes_per_plane, const uint * offsets, int h)
+{
+ int data_x = planes[0].data_x;
+ uint nbytes = bytes_per_plane * pie->num_planes * h;
+ uint len = 1 + cmd_size2w(h, bytes_per_plane) + nbytes;
+ byte *dp;
+ uint offset = 0;
+ int plane, i;
+ int code;
+
+ if (data_x) {
+ code = set_cmd_put_op(dp, cldev, pcls, cmd_opv_set_misc, 2);
+ if (code < 0)
+ return code;
+ dp[1] = cmd_set_misc_data_x + (data_x & 7);
+ offset = ((data_x & ~7) * cldev->color_info.depth) >> 3;
+ }
+ code = set_cmd_put_op(dp, cldev, pcls, cmd_opv_image_data, len);
+ if (code < 0)
+ return code;
+ dp++;
+ cmd_put2w(h, bytes_per_plane, dp);
+ for (plane = 0; plane < pie->num_planes; ++plane)
+ for (i = 0; i < h; ++i) {
+ memcpy(dp,
+ planes[plane].data + i * planes[plane].raster +
+ offsets[plane] + offset,
+ bytes_per_plane);
+ dp += bytes_per_plane;
+ }
+ return 0;
+}
+
+/* Write image_end commands into all bands */
+private int /* ret 0 ok, else -ve error status */
+write_image_end_all(gx_device *dev, const clist_image_enum *pie)
+{
+ gx_device_clist_writer * const cdev =
+ &((gx_device_clist *)dev)->writer;
+ int code;
+ int y = pie->ymin;
+ int height = pie->ymax - y;
+
+ FOR_RECTS {
+ byte *dp;
+
+ if (!(pcls->known & begin_image_known))
+ continue;
+ TRY_RECT {
+ if_debug1('L', "[L]image_end for band %d\n", band);
+ code = set_cmd_put_op(dp, cdev, pcls, cmd_opv_image_data, 2);
+ } HANDLE_RECT(code);
+ dp[1] = 0; /* EOD */
+ pcls->known ^= begin_image_known;
+ } END_RECTS;
+ return 0;
+}
+
+/*
+ * Compare a rectangle vs. clip path. Return true if there is no clipping
+ * path, if the rectangle is unclipped, or if the clipping path is a
+ * rectangle and intersects the given rectangle.
+ */
+private bool
+check_rect_for_trivial_clip(
+ const gx_clip_path *pcpath, /* May be NULL, clip to evaluate */
+ int px, int py, int qx, int qy /* corners of box to test */
+)
+{
+ gs_fixed_rect obox;
+ gs_fixed_rect imgbox;
+
+ if (!pcpath)
+ return true;
+
+ imgbox.p.x = int2fixed(px);
+ imgbox.p.y = int2fixed(py);
+ imgbox.q.x = int2fixed(qx);
+ imgbox.q.y = int2fixed(qy);
+ if (gx_cpath_includes_rectangle(pcpath,
+ imgbox.p.x, imgbox.p.y,
+ imgbox.q.x, imgbox.q.y))
+ return true;
+
+ return (gx_cpath_outer_box(pcpath, &obox) /* cpath is rectangle */ &&
+ obox.p.x <= imgbox.q.x && obox.q.x >= imgbox.p.x &&
+ obox.p.y <= imgbox.q.y && obox.q.y >= imgbox.p.y );
+}
diff --git a/pstoraster/gxclio.h b/pstoraster/gxclio.h
new file mode 100644
index 000000000..c695c5d95
--- /dev/null
+++ b/pstoraster/gxclio.h
@@ -0,0 +1,104 @@
+/* Copyright (C) 1995, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* I/O interface for command lists */
+
+#ifndef gxclio_INCLUDED
+# define gxclio_INCLUDED
+
+#include "gp.h" /* for gp_file_name_sizeof */
+
+/*
+ * There are 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 ---------------- */
+
+/*
+ * If *fname = 0, generate and store a new scratch file name; otherwise,
+ * open an existing file. Only modes "r" and "w+" are supported,
+ * and only binary data (but the caller must append the "b" if needed).
+ * Mode "r" with *fname = 0 is an error.
+ */
+int clist_fopen(P6(char fname[gp_file_name_sizeof], const char *fmode,
+ clist_file_ptr * pcf,
+ gs_memory_t * mem, gs_memory_t *data_mem,
+ bool ok_to_compress));
+
+/*
+ * Close a file, optionally deleting it.
+ */
+int clist_fclose(P3(clist_file_ptr cf, const char *fname, bool delete));
+
+/*
+ * Delete a file.
+ */
+int clist_unlink(P1(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 ---------------- */
+
+/*
+ * Set the low-memory warning threshold. clist_ferror_code will return 1
+ * if fewer than this many bytes of memory are left for storing band data.
+ */
+int clist_set_memory_warning(P2(clist_file_ptr cf, int bytes_left));
+
+/*
+ * clist_ferror_code returns a negative error code per gserrors.h, not a
+ * Boolean; 0 means no error, 1 means low-memory warning.
+ */
+int clist_ferror_code(P1(clist_file_ptr cf));
+
+long clist_ftell(P1(clist_file_ptr cf));
+
+/*
+ * We pass the file name to clist_rewind and clist_fseek in case the
+ * implementation has to close and reopen the file. (clist_fseek with
+ * offset = 0 and mode = SEEK_END indicates we are about to append.)
+ */
+void clist_rewind(P3(clist_file_ptr cf, bool discard_data,
+ const char *fname));
+
+int clist_fseek(P4(clist_file_ptr cf, long offset, int mode,
+ const char *fname));
+
+#endif /* gxclio_INCLUDED */
diff --git a/pstoraster/gxclip.c b/pstoraster/gxclip.c
new file mode 100644
index 000000000..d4139961c
--- /dev/null
+++ b/pstoraster/gxclip.c
@@ -0,0 +1,582 @@
+/* Copyright (C) 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Implementation of (path-based) clipping */
+#include "gx.h"
+#include "gxdevice.h"
+#include "gxclip.h"
+#include "gzpath.h"
+#include "gzcpath.h"
+
+/* Define whether to look for vertical clipping regions. */
+#define CHECK_VERTICAL_CLIPPING
+
+/* ------ 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_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);
+private dev_proc_get_clipping_box(clip_get_clipping_box);
+private dev_proc_get_bits_rectangle(clip_get_bits_rectangle);
+
+/* 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,
+ 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,
+ 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,
+ clip_get_clipping_box,
+ gx_default_begin_typed_image,
+ clip_get_bits_rectangle,
+ gx_forward_map_color_rgb_alpha,
+ gx_no_create_compositor,
+ gx_forward_get_hardware_params,
+ gx_default_text_begin
+ }
+};
+
+/* 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)
+{
+ gx_device_init((gx_device *) dev, (gx_device *) & gs_clip_device,
+ NULL, true);
+ 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, gx_cpath_list(pcpath));
+}
+
+/* Define debugging statistics for the clipping loops. */
+#ifdef DEBUG
+struct stats_clip_s {
+ long
+ loops, in, down, up, x, no_x;
+} stats_clip;
+private uint clip_interval = 10000;
+
+# define INCR(v) (++(stats_clip.v))
+# define INCR_THEN(v, e) (INCR(v), (e))
+#else
+# define INCR(v) DO_NOTHING
+# define INCR_THEN(v, e) (e)
+#endif
+
+/*
+ * Enumerate the rectangles of the x,w,y,h argument that fall within
+ * the clipping region.
+ */
+private int
+clip_enumerate(gx_device_clip * rdev,
+ int (*process) (P5(clip_callback_data_t * pccd, int xc, int yc, int xec, int yec)),
+ clip_callback_data_t * pccd)
+{
+ gx_clip_rect *rptr = rdev->current; /* const within algorithm */
+ const int x = pccd->x, y = pccd->y;
+ const int xe = x + pccd->w, ye = y + pccd->h;
+ int xc, xec, yc, yec, yep;
+ int code;
+
+#ifdef DEBUG
+ if (INCR(loops) % clip_interval == 0)
+ if_debug6('q',
+ "[q]loops=%ld in=%ld down=%ld up=%ld x=%ld no_x=%ld\n", \
+ stats_clip.loops, stats_clip.in,
+ stats_clip.down, stats_clip.up,
+ stats_clip.x, stats_clip.no_x);
+#endif
+ if (pccd->w <= 0 || pccd->h <= 0)
+ return 0;
+ /* Check for the region being entirely within the current rectangle. */
+ if (!rdev->list.outside) {
+ if (y >= rptr->ymin && ye <= rptr->ymax &&
+ x >= rptr->xmin && xe <= rptr->xmax
+ ) {
+ return INCR_THEN(in, (*process) (pccd, x, y, xe, ye));
+ }
+ }
+ /*
+ * 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 this loop, 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.
+ */
+ if (y >= rptr->ymax) {
+ if ((rptr = rptr->next) != 0)
+ while (INCR_THEN(up, y >= rptr->ymax))
+ rptr = rptr->next;
+ } else
+ while (rptr->prev != 0 && y < rptr->prev->ymax)
+ INCR_THEN(down, rptr = rptr->prev);
+ if (rptr == 0 || (yc = rptr->ymin) >= ye) {
+ if (rdev->list.count > 1)
+ rdev->current =
+ (rptr != 0 ? rptr :
+ y >= rdev->current->ymax ? rdev->list.tail :
+ rdev->list.head);
+ if (rdev->list.outside) {
+ return (*process) (pccd, x, y, xe, ye);
+ } else
+ return 0;
+ }
+ rdev->current = rptr;
+ if (yc < y)
+ yc = y;
+ if (rdev->list.outside) {
+ for (yep = y;;) {
+ const int ymax = rptr->ymax;
+
+ xc = x;
+ if (yc > yep) {
+ yec = yc, yc = yep;
+ xec = xe;
+ code = (*process) (pccd, xc, yc, xec, yec);
+ if (code < 0)
+ return code;
+ yc = yec;
+ }
+ yec = min(ymax, ye);
+ do {
+ xec = rptr->xmin;
+ if (xec > xc) {
+ if (xec > xe)
+ xec = xe;
+ code = (*process) (pccd, xc, yc, xec, yec);
+ if (code < 0)
+ return code;
+ xc = rptr->xmax;
+ if (xc >= xe)
+ xc = max_int;
+ } else {
+ xec = rptr->xmax;
+ if (xec > xc)
+ xc = xec;
+ }
+ }
+ while ((rptr = rptr->next) != 0 && rptr->ymax == ymax);
+ if (xc < xe) {
+ xec = xe;
+ code = (*process) (pccd, xc, yc, xec, yec);
+ if (code < 0)
+ return code;
+ }
+ yep = yec;
+ if (rptr == 0 || (yc = rptr->ymin) >= ye)
+ break;
+ }
+ if (yep < ye) {
+ xc = x, xec = xe, yc = yep, yec = ye;
+ code = (*process) (pccd, xc, yc, xec, yec);
+ if (code < 0)
+ return code;
+ }
+ } else /* !outside */
+ for (;;) {
+ const int ymax = rptr->ymax;
+ gx_clip_rect *nptr;
+
+ yec = min(ymax, ye);
+ 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);
+ INCR(x);
+/*
+ * Conditionally look ahead to detect unclipped vertical strips. This is
+ * really only valuable for 90 degree rotated images or (nearly-)vertical
+ * lines with convex clipping regions; if we ever change images to use
+ * source buffering and destination-oriented enumeration, we could probably
+ * take out the code here with no adverse effects.
+ */
+#ifdef CHECK_VERTICAL_CLIPPING
+ if (xec - xc == pccd->w) { /* full width */
+ /* Look ahead for a vertical swath. */
+ while ((nptr = rptr->next) != 0 &&
+ nptr->ymin == yec &&
+ nptr->ymax <= ye &&
+ nptr->xmin <= x &&
+ nptr->xmax >= xe
+ )
+ yec = nptr->ymax, rptr = nptr;
+ } else
+ nptr = rptr->next;
+#else
+ nptr = rptr->next;
+#endif
+ code = (*process) (pccd, xc, yc, xec, yec);
+ if (code < 0)
+ return code;
+ } else {
+ INCR_THEN(no_x, nptr = rptr->next);
+ }
+ }
+ while ((rptr = nptr) != 0 && rptr->ymax == ymax);
+ if (rptr == 0 || (yec = rptr->ymin) >= ye)
+ break;
+ yc = yec;
+ }
+ return 0;
+}
+
+/* Open a clipping device */
+private int
+clip_open(register gx_device * dev)
+{
+ gx_device_clip *rdev = (gx_device_clip *) 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;
+ rdev->width = tdev->width;
+ rdev->height = tdev->height;
+ return 0;
+}
+
+/* Fill a rectangle */
+int
+clip_call_fill_rectangle(clip_callback_data_t * pccd, int xc, int yc, int xec, int yec)
+{
+ return (*dev_proc(pccd->tdev, fill_rectangle))
+ (pccd->tdev, xc, yc, xec - xc, yec - yc, pccd->color[0]);
+}
+private int
+clip_fill_rectangle(gx_device * dev, int x, int y, int w, int h,
+ gx_color_index color)
+{
+ gx_device_clip *rdev = (gx_device_clip *) dev;
+ clip_callback_data_t ccdata;
+
+ x += rdev->translation.x;
+ y += rdev->translation.y;
+ ccdata.tdev = rdev->target;
+ ccdata.color[0] = color;
+ ccdata.x = x, ccdata.y = y, ccdata.w = w, ccdata.h = h;
+ return clip_enumerate(rdev, clip_call_fill_rectangle, &ccdata);
+}
+
+/* Copy a monochrome rectangle */
+int
+clip_call_copy_mono(clip_callback_data_t * pccd, int xc, int yc, int xec, int yec)
+{
+ return (*dev_proc(pccd->tdev, copy_mono))
+ (pccd->tdev, pccd->data + (yc - pccd->y) * pccd->raster,
+ pccd->sourcex + xc - pccd->x, pccd->raster, gx_no_bitmap_id,
+ xc, yc, xec - xc, yec - yc, pccd->color[0], pccd->color[1]);
+}
+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)
+{
+ gx_device_clip *rdev = (gx_device_clip *) dev;
+ clip_callback_data_t ccdata;
+
+ x += rdev->translation.x;
+ y += rdev->translation.y;
+ ccdata.tdev = rdev->target;
+ ccdata.data = data, ccdata.sourcex = sourcex, ccdata.raster = raster;
+ ccdata.color[0] = color0, ccdata.color[1] = color1;
+ ccdata.x = x, ccdata.y = y, ccdata.w = w, ccdata.h = h;
+ return clip_enumerate(rdev, clip_call_copy_mono, &ccdata);
+}
+
+/* Copy a color rectangle */
+int
+clip_call_copy_color(clip_callback_data_t * pccd, int xc, int yc, int xec, int yec)
+{
+ return (*dev_proc(pccd->tdev, copy_color))
+ (pccd->tdev, pccd->data + (yc - pccd->y) * pccd->raster,
+ pccd->sourcex + xc - pccd->x, pccd->raster, gx_no_bitmap_id,
+ xc, yc, xec - xc, yec - yc);
+}
+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)
+{
+ gx_device_clip *rdev = (gx_device_clip *) dev;
+ clip_callback_data_t ccdata;
+
+ x += rdev->translation.x;
+ y += rdev->translation.y;
+ ccdata.tdev = rdev->target;
+ ccdata.data = data, ccdata.sourcex = sourcex, ccdata.raster = raster;
+ ccdata.x = x, ccdata.y = y, ccdata.w = w, ccdata.h = h;
+ return clip_enumerate(rdev, clip_call_copy_color, &ccdata);
+}
+
+/* Copy a rectangle with alpha */
+int
+clip_call_copy_alpha(clip_callback_data_t * pccd, int xc, int yc, int xec, int yec)
+{
+ return (*dev_proc(pccd->tdev, copy_alpha))
+ (pccd->tdev, pccd->data + (yc - pccd->y) * pccd->raster,
+ pccd->sourcex + xc - pccd->x, pccd->raster, gx_no_bitmap_id,
+ xc, yc, xec - xc, yec - yc, pccd->color[0], pccd->depth);
+}
+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)
+{
+ gx_device_clip *rdev = (gx_device_clip *) dev;
+ clip_callback_data_t ccdata;
+
+ x += rdev->translation.x;
+ y += rdev->translation.y;
+ ccdata.tdev = rdev->target;
+ ccdata.data = data, ccdata.sourcex = sourcex, ccdata.raster = raster;
+ ccdata.x = x, ccdata.y = y, ccdata.w = w, ccdata.h = h;
+ ccdata.color[0] = color, ccdata.depth = depth;
+ return clip_enumerate(rdev, clip_call_copy_alpha, &ccdata);
+}
+
+/* Fill a region defined by a mask. */
+int
+clip_call_fill_mask(clip_callback_data_t * pccd, int xc, int yc, int xec, int yec)
+{
+ return (*dev_proc(pccd->tdev, fill_mask))
+ (pccd->tdev, pccd->data + (yc - pccd->y) * pccd->raster,
+ pccd->sourcex + xc - pccd->x, pccd->raster, gx_no_bitmap_id,
+ xc, yc, xec - xc, yec - yc, pccd->pdcolor, pccd->depth,
+ pccd->lop, NULL);
+}
+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)
+{
+ gx_device_clip *rdev = (gx_device_clip *) dev;
+ clip_callback_data_t ccdata;
+
+ if (pcpath != 0)
+ return gx_default_fill_mask(dev, data, sourcex, raster, id,
+ x, y, w, h, pdcolor, depth, lop,
+ pcpath);
+ x += rdev->translation.x;
+ y += rdev->translation.y;
+ ccdata.tdev = rdev->target;
+ ccdata.x = x, ccdata.y = y, ccdata.w = w, ccdata.h = h;
+ ccdata.data = data, ccdata.sourcex = sourcex, ccdata.raster = raster;
+ ccdata.pdcolor = pdcolor, ccdata.depth = depth, ccdata.lop = lop;
+ return clip_enumerate(rdev, clip_call_fill_mask, &ccdata);
+}
+
+/* Strip-tile a rectangle. */
+int
+clip_call_strip_tile_rectangle(clip_callback_data_t * pccd, int xc, int yc, int xec, int yec)
+{
+ return (*dev_proc(pccd->tdev, strip_tile_rectangle))
+ (pccd->tdev, pccd->tiles, xc, yc, xec - xc, yec - yc,
+ pccd->color[0], pccd->color[1], pccd->phase.x, pccd->phase.y);
+}
+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)
+{
+ gx_device_clip *rdev = (gx_device_clip *) dev;
+ clip_callback_data_t ccdata;
+
+ x += rdev->translation.x;
+ y += rdev->translation.y;
+ ccdata.tdev = rdev->target;
+ ccdata.x = x, ccdata.y = y, ccdata.w = w, ccdata.h = h;
+ ccdata.tiles = tiles;
+ ccdata.color[0] = color0, ccdata.color[1] = color1;
+ ccdata.phase.x = phase_x, ccdata.phase.y = phase_y;
+ return clip_enumerate(rdev, clip_call_strip_tile_rectangle, &ccdata);
+}
+
+/* Copy a rectangle with RasterOp and strip texture. */
+int
+clip_call_strip_copy_rop(clip_callback_data_t * pccd, int xc, int yc, int xec, int yec)
+{
+ return (*dev_proc(pccd->tdev, strip_copy_rop))
+ (pccd->tdev, pccd->data + (yc - pccd->y) * pccd->raster,
+ pccd->sourcex + xc - pccd->x, pccd->raster, gx_no_bitmap_id,
+ pccd->scolors, pccd->textures, pccd->tcolors,
+ xc, yc, xec - xc, yec - yc, pccd->phase.x, pccd->phase.y,
+ pccd->lop);
+}
+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)
+{
+ gx_device_clip *rdev = (gx_device_clip *) dev;
+ clip_callback_data_t ccdata;
+
+ x += rdev->translation.x;
+ y += rdev->translation.y;
+ ccdata.tdev = rdev->target;
+ ccdata.x = x, ccdata.y = y, ccdata.w = w, ccdata.h = h;
+ ccdata.data = sdata, ccdata.sourcex = sourcex, ccdata.raster = raster;
+ ccdata.scolors = scolors, ccdata.textures = textures,
+ ccdata.tcolors = tcolors;
+ ccdata.phase.x = phase_x, ccdata.phase.y = phase_y, ccdata.lop = lop;
+ return clip_enumerate(rdev, clip_call_strip_copy_rop, &ccdata);
+}
+
+/* Get the (outer) clipping box, in client coordinates. */
+private void
+clip_get_clipping_box(gx_device * dev, gs_fixed_rect * pbox)
+{
+ gx_device_clip *rdev = (gx_device_clip *) dev;
+ gx_device *tdev = rdev->target;
+ gs_fixed_rect tbox, cbox;
+ fixed tx = int2fixed(rdev->translation.x), ty = int2fixed(rdev->translation.y);
+
+ (*dev_proc(tdev, get_clipping_box)) (tdev, &tbox);
+ /*
+ * To get an accurate clipping box quickly in all cases, we should
+ * save the outer box from the clipping path. However,
+ * this is not currently (or even always guaranteed to be)
+ * available. Instead, we compromise: if there is more than one
+ * rectangle in the list, we return accurate Y values (which are
+ * easy to obtain, because the list is Y-sorted) but copy the
+ * X values from the target.
+ */
+ if (rdev->list.outside || rdev->list.count == 0) {
+ cbox = tbox;
+ } else if (rdev->list.count == 1) {
+ cbox.p.x = int2fixed(rdev->list.single.xmin);
+ cbox.p.y = int2fixed(rdev->list.single.ymin);
+ cbox.q.x = int2fixed(rdev->list.single.xmax);
+ cbox.q.y = int2fixed(rdev->list.single.ymax);
+ } else { /* The head and tail elements are dummies.... */
+ cbox.p.x = tbox.p.x;
+ cbox.p.y = int2fixed(rdev->list.head->next->ymin);
+ cbox.q.x = tbox.q.x;
+ cbox.q.y = int2fixed(rdev->list.tail->prev->ymax);
+ }
+ rect_intersect(tbox, cbox);
+ if (tbox.p.x != min_fixed)
+ tbox.p.x -= tx;
+ if (tbox.p.y != min_fixed)
+ tbox.p.y -= ty;
+ if (tbox.q.x != max_fixed)
+ tbox.q.x -= tx;
+ if (tbox.q.y != max_fixed)
+ tbox.q.y -= ty;
+ *pbox = tbox;
+}
+
+/* Get bits back from the device. */
+private int
+clip_get_bits_rectangle(gx_device * dev, const gs_int_rect * prect,
+ gs_get_bits_params_t * params, gs_int_rect ** unread)
+{
+ gx_device_clip *rdev = (gx_device_clip *) dev;
+ gx_device *tdev = rdev->target;
+ int tx = rdev->translation.x, ty = rdev->translation.y;
+ gs_int_rect rect;
+ int code;
+
+ rect.p.x = prect->p.x - tx, rect.p.y = prect->p.y - ty;
+ rect.q.x = prect->q.x - tx, rect.q.y = prect->q.y - ty;
+ code = (*dev_proc(tdev, get_bits_rectangle))
+ (tdev, &rect, params, unread);
+ if (code > 0) {
+ /* Adjust unread rectangle coordinates */
+ gs_int_rect *list = *unread;
+ int i;
+
+ for (i = 0; i < code; ++list, ++i) {
+ list->p.x += tx, list->p.y += ty;
+ list->q.x += tx, list->q.y += ty;
+ }
+ }
+ return code;
+}
diff --git a/pstoraster/gxclip.h b/pstoraster/gxclip.h
new file mode 100644
index 000000000..ef13576e1
--- /dev/null
+++ b/pstoraster/gxclip.h
@@ -0,0 +1,75 @@
+/* Copyright (C) 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Internal definitions for clipping */
+
+#ifndef gxclip_INCLUDED
+# define gxclip_INCLUDED
+
+/*
+ * Both rectangle list and mask clipping use callback procedures to process
+ * each rectangle selected by the clipping region. They share both the
+ * callback procedures themselves and the structure that provides closure
+ * data for these procedures. We define a single closure structure, rather
+ * than one per client/callback, just to reduce source code clutter. The
+ * comments below show which clients use each member.
+ */
+typedef struct clip_callback_data_s {
+ /*
+ * The original driver procedure stores the following of its arguments
+ * that the callback procedure or the clipping algorithm needs.
+ */
+ gx_device *tdev; /* target device (always set) */
+ int x, y, w, h; /* (always set) */
+ gx_color_index color[2]; /* (all but copy_color) */
+ const byte *data; /* copy_*, fill_mask */
+ int sourcex; /* ibid. */
+ uint raster; /* ibid. */
+ int depth; /* copy_alpha, fill_mask */
+ const gx_drawing_color *pdcolor; /* fill_mask */
+ gs_logical_operation_t lop; /* fill_mask, strip_copy_rop */
+ const gx_clip_path *pcpath; /* fill_mask */
+ const gx_strip_bitmap *tiles; /* strip_tile_rectangle */
+ gs_int_point phase; /* strip_* */
+ const gx_color_index *scolors; /* strip_copy_rop */
+ const gx_strip_bitmap *textures; /* ibid. */
+ const gx_color_index *tcolors; /* ibid. */
+} clip_callback_data_t;
+
+/* Declare the callback procedures. */
+int
+ clip_call_fill_rectangle(P5(clip_callback_data_t * pccd,
+ int xc, int yc, int xec, int yec)), clip_call_copy_mono(P5(clip_callback_data_t * pccd,
+ int xc, int yc, int xec, int yec)),
+ clip_call_copy_color(P5(clip_callback_data_t * pccd,
+ int xc, int yc, int xec, int yec)), clip_call_copy_alpha(P5(clip_callback_data_t * pccd,
+ int xc, int yc, int xec, int yec)),
+ clip_call_fill_mask(P5(clip_callback_data_t * pccd,
+ int xc, int yc, int xec, int yec)), clip_call_strip_tile_rectangle(P5(clip_callback_data_t * pccd,
+ int xc, int yc, int xec, int yec)),
+ clip_call_strip_copy_rop(P5(clip_callback_data_t * pccd,
+ int xc, int yc, int xec, int yec));
+
+#endif /* gxclip_INCLUDED */
diff --git a/pstoraster/gxclip2.c b/pstoraster/gxclip2.c
new file mode 100644
index 000000000..22ef30e06
--- /dev/null
+++ b/pstoraster/gxclip2.c
@@ -0,0 +1,306 @@
+/* Copyright (C) 1993, 1995, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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_forward_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,
+ gx_forward_get_clipping_box,
+ gx_default_begin_typed_image,
+ gx_forward_get_bits_rectangle,
+ gx_forward_map_color_rgb_alpha,
+ gx_no_create_compositor,
+ gx_forward_get_hardware_params,
+ gx_default_text_begin
+ }
+};
+
+/* Initialize a tile clipping device from a mask. */
+int
+tile_clip_initialize(gx_device_tile_clip * cdev, const gx_strip_bitmap * tiles,
+ gx_device * tdev, int px, int py)
+{
+ int code =
+ gx_mask_clip_initialize(cdev, &gs_tile_clip_device,
+ (const gx_bitmap *)tiles,
+ tdev, 0, 0); /* phase will be reset */
+
+ if (code >= 0) {
+ cdev->tiles = *tiles;
+ tile_clip_set_phase(cdev, px, py);
+ }
+ return code;
+}
+
+/* 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;
+}
+
+/* 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_tile_clip *cdev = (gx_device_tile_clip *) dev;
+ 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_device_tile_clip *cdev = (gx_device_tile_clip *) dev;
+ gx_color_index color, mcolor0, mcolor1;
+ int ty, ny;
+ int code;
+
+ setup_mask_copy_mono(cdev, color, mcolor0, mcolor1);
+ 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;
+}
+
+/*
+ * Define the skeleton for the other copying operations. 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. The schema
+ * is:
+ * FOR_RUNS(data_row, tx1, tx, ty) {
+ * ... process the run ([tx1,tx),ty) ...
+ * } END_FOR_RUNS();
+ * Free variables: cdev, data, sourcex, raster, x, y, w, h.
+ */
+#define t_next(tx)\
+ BEGIN {\
+ if ( ++cx == cdev->tiles.size.x )\
+ cx = 0, tp = tile_row, tbit = 0x80;\
+ else if ( (tbit >>= 1) == 0 )\
+ tp++, tbit = 0x80;\
+ tx++;\
+ } END
+#define FOR_RUNS(data_row, tx1, tx, ty)\
+ 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;\
+\
+ for ( tx = x; tx < x + w; ) {\
+ int tx1;\
+\
+ /* Skip a run of 0s. */\
+ while ( tx < x + w && (*tp & tbit) == 0 )\
+ t_next(tx);\
+ if ( tx == x + w )\
+ break;\
+ /* Scan a run of 1s. */\
+ tx1 = tx;\
+ do {\
+ t_next(tx);\
+ } while ( tx < x + w && (*tp & tbit) != 0 );\
+ if_debug3('T', "[T]run x=(%d,%d), y=%d\n", tx1, tx, ty);
+/* (body goes here) */
+#define END_FOR_RUNS()\
+ }\
+ if ( ++cy == cdev->tiles.size.y )\
+ cy = 0, tile_row = cdev->tiles.data;\
+ else\
+ tile_row += cdev->tiles.raster;\
+ }
+
+/* Copy a color rectangle. */
+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)
+{
+ gx_device_tile_clip *cdev = (gx_device_tile_clip *) dev;
+
+ FOR_RUNS(data_row, txrun, tx, ty) {
+ /* Copy the run. */
+ int code = (*dev_proc(cdev->target, copy_color))
+ (cdev->target, data_row, sourcex + txrun - x, raster,
+ gx_no_bitmap_id, txrun, ty, tx - txrun, 1);
+
+ if (code < 0)
+ return code;
+ }
+ END_FOR_RUNS();
+ 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)
+{
+ gx_device_tile_clip *cdev = (gx_device_tile_clip *) dev;
+
+ FOR_RUNS(data_row, txrun, tx, ty) {
+ /* Copy the run. */
+ int code = (*dev_proc(cdev->target, copy_alpha))
+ (cdev->target, data_row, sourcex + txrun - x, raster,
+ gx_no_bitmap_id, txrun, ty, tx - txrun, 1, color, depth);
+
+ if (code < 0)
+ return code;
+ }
+ END_FOR_RUNS();
+ 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)
+{
+ gx_device_tile_clip *cdev = (gx_device_tile_clip *) dev;
+
+ FOR_RUNS(data_row, txrun, tx, ty) {
+ /* Copy the run. */
+ int code = (*dev_proc(cdev->target, strip_copy_rop))
+ (cdev->target, data_row, sourcex + txrun - x, raster,
+ gx_no_bitmap_id, scolors, textures, tcolors,
+ txrun, ty, tx - txrun, 1, phase_x, phase_y, lop);
+
+ if (code < 0)
+ return code;
+ }
+ END_FOR_RUNS();
+ return 0;
+}
diff --git a/pstoraster/gxclip2.h b/pstoraster/gxclip2.h
new file mode 100644
index 000000000..c89def430
--- /dev/null
+++ b/pstoraster/gxclip2.h
@@ -0,0 +1,55 @@
+/* Copyright (C) 1993, 1996, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Tiled mask clipping device and interface */
+
+#ifndef gxclip2_INCLUDED
+# define gxclip2_INCLUDED
+
+#include "gxmclip.h"
+
+/* The structure for tile clipping is the same as for simple mask clipping. */
+typedef gx_device_mask_clip gx_device_tile_clip;
+
+#define private_st_device_tile_clip() /* in gxclip2.c */\
+ gs_private_st_suffix_add0(st_device_tile_clip, gx_device_tile_clip,\
+ "gx_device_tile_clip", device_tile_clip_enum_ptrs,\
+ device_tile_clip_reloc_ptrs, st_device_mask_clip)
+
+/*
+ * Initialize a tile clipping device from a mask.
+ * We supply an explicit phase.
+ */
+int tile_clip_initialize(P5(gx_device_tile_clip * cdev,
+ const gx_strip_bitmap * tiles,
+ gx_device * tdev, int px, int py));
+
+/*
+ * 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 * cdev, int px, int py));
+
+#endif /* gxclip2_INCLUDED */
diff --git a/pstoraster/gxclipm.c b/pstoraster/gxclipm.c
new file mode 100644
index 000000000..73e1802ed
--- /dev/null
+++ b/pstoraster/gxclipm.c
@@ -0,0 +1,309 @@
+/* Copyright (C) 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Mask clipping device */
+#include "memory_.h"
+#include "gx.h"
+#include "gxdevice.h"
+#include "gxdevmem.h"
+#include "gxclipm.h"
+
+/* Device procedures */
+private dev_proc_fill_rectangle(mask_clip_fill_rectangle);
+private dev_proc_copy_mono(mask_clip_copy_mono);
+private dev_proc_copy_color(mask_clip_copy_color);
+private dev_proc_copy_alpha(mask_clip_copy_alpha);
+private dev_proc_strip_copy_rop(mask_clip_strip_copy_rop);
+private dev_proc_get_clipping_box(mask_clip_get_clipping_box);
+
+/* The device descriptor. */
+const gx_device_mask_clip gs_mask_clip_device =
+{std_device_std_body_open(gx_device_mask_clip, 0, "mask 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,
+ mask_clip_fill_rectangle,
+ gx_default_tile_rectangle,
+ mask_clip_copy_mono,
+ mask_clip_copy_color,
+ gx_default_draw_line,
+ gx_forward_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,
+ mask_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,
+ mask_clip_strip_copy_rop,
+ mask_clip_get_clipping_box,
+ gx_default_begin_typed_image,
+ gx_forward_get_bits_rectangle,
+ gx_forward_map_color_rgb_alpha,
+ gx_no_create_compositor,
+ gx_forward_get_hardware_params,
+ gx_default_text_begin
+ }
+};
+
+/* Fill a rectangle by painting through the mask. */
+private int
+mask_clip_fill_rectangle(gx_device * dev, int x, int y, int w, int h,
+ gx_color_index color)
+{
+ gx_device_mask_clip *cdev = (gx_device_mask_clip *) dev;
+ gx_device *tdev = cdev->target;
+
+ /* Clip the rectangle to the region covered by the mask. */
+ int mx0 = x + cdev->phase.x, my0 = y + cdev->phase.y;
+ int mx1 = mx0 + w, my1 = my0 + h;
+
+ if (mx0 < 0)
+ mx0 = 0;
+ if (my0 < 0)
+ my0 = 0;
+ if (mx1 > cdev->tiles.size.x)
+ mx1 = cdev->tiles.size.x;
+ if (my1 > cdev->tiles.size.y)
+ my1 = cdev->tiles.size.y;
+ return (*dev_proc(tdev, copy_mono))
+ (tdev, cdev->tiles.data + my0 * cdev->tiles.raster, mx0,
+ cdev->tiles.raster, cdev->tiles.id,
+ mx0 - cdev->phase.x, my0 - cdev->phase.y,
+ mx1 - mx0, my1 - my0, gx_no_color_index, color);
+}
+
+/*
+ * Clip the rectangle for a copy operation.
+ * Sets m{x,y}{0,1} to the region in the mask coordinate system;
+ * subtract cdev->phase.{x,y} to get target coordinates.
+ * Sets sdata, sx to adjusted values of data, sourcex.
+ * References cdev, data, sourcex, raster, x, y, w, h.
+ */
+#define DECLARE_MASK_COPY\
+ const byte *sdata;\
+ int sx, mx0, my0, mx1, my1
+#define FIT_MASK_COPY(data, sourcex, raster, vx, vy, vw, vh)\
+ BEGIN\
+ sdata = data, sx = sourcex;\
+ mx0 = vx + cdev->phase.x, my0 = vy + cdev->phase.y;\
+ mx1 = mx0 + vw, my1 = my0 + vh;\
+ if ( mx0 < 0 )\
+ sx -= mx0, mx0 = 0;\
+ if ( my0 < 0 )\
+ sdata -= my0 * raster, my0 = 0;\
+ if ( mx1 > cdev->tiles.size.x )\
+ mx1 = cdev->tiles.size.x;\
+ if ( my1 > cdev->tiles.size.y )\
+ my1 = cdev->tiles.size.y;\
+ END
+
+/* Copy a monochrome bitmap by playing Boolean games. */
+private int
+mask_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_device_mask_clip *cdev = (gx_device_mask_clip *) dev;
+ gx_device *tdev = cdev->target;
+ gx_color_index color, mcolor0, mcolor1;
+
+ DECLARE_MASK_COPY;
+ int cy, ny;
+ int code;
+
+ setup_mask_copy_mono(cdev, color, mcolor0, mcolor1);
+ FIT_MASK_COPY(data, sourcex, raster, x, y, w, h);
+ for (cy = my0; cy < my1; cy += ny) {
+ int ty = cy - cdev->phase.y;
+ int cx, nx;
+
+ ny = my1 - cy;
+ if (ny > cdev->mdev.height)
+ ny = cdev->mdev.height;
+ for (cx = mx0; cx < mx1; cx += nx) {
+ int tx = cx - cdev->phase.x;
+
+ nx = mx1 - cx; /* also should be min */
+ /* 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,
+ sdata + (ty - y) * raster, sx + tx - x,
+ raster, gx_no_bitmap_id,
+ cx, 0, nx, ny, mcolor0, mcolor1);
+ /* Now copy the color through the double mask. */
+ code = (*dev_proc(tdev, 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;
+}
+
+/*
+ * Define the run enumerator for the other copying operations. We can't use
+ * the BitBlt tricks: we have to scan for runs of 1s. There are obvious
+ * ways to speed this up; we'll implement some if we need to.
+ */
+private int
+clip_runs_enumerate(gx_device_mask_clip * cdev,
+ int (*process) (P5(clip_callback_data_t * pccd, int xc, int yc, int xec, int yec)),
+ clip_callback_data_t * pccd)
+{
+ DECLARE_MASK_COPY;
+ int cy;
+ const byte *tile_row;
+
+ FIT_MASK_COPY(pccd->data, pccd->sourcex, pccd->raster,
+ pccd->x, pccd->y, pccd->w, pccd->h);
+ tile_row = cdev->tiles.data + my0 * cdev->tiles.raster + (mx0 >> 3);
+ for (cy = my0; cy < my1; cy++) {
+ int cx = mx0;
+ const byte *tp = tile_row;
+ byte tbit = 0x80 >> (cx & 7);
+
+ while (cx < mx1) {
+ int tx1, tx, ty;
+ int code;
+
+ /* Skip a run of 0s. */
+ while (cx < mx1 && (*tp & tbit) == 0) {
+ if ((tbit >>= 1) == 0)
+ tp++, tbit = 0x80;
+ ++cx;
+ }
+ if (cx == mx1)
+ break;
+ /* Scan a run of 1s. */
+ tx1 = cx - cdev->phase.x;
+ do {
+ if ((tbit >>= 1) == 0)
+ tp++, tbit = 0x80;
+ ++cx;
+ } while (cx < mx1 && (*tp & tbit) != 0);
+ tx = cx - cdev->phase.x;
+ ty = cy - cdev->phase.y;
+ code = (*process) (pccd, tx1, ty, tx, ty + 1);
+ if (code < 0)
+ return code;
+ }
+ tile_row += cdev->tiles.raster;
+ }
+ return 0;
+}
+
+/* Copy a color rectangle */
+private int
+mask_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)
+{
+ gx_device_mask_clip *cdev = (gx_device_mask_clip *) dev;
+ clip_callback_data_t ccdata;
+
+ ccdata.tdev = cdev->target;
+ ccdata.data = data, ccdata.sourcex = sourcex, ccdata.raster = raster;
+ ccdata.x = x, ccdata.y = y, ccdata.w = w, ccdata.h = h;
+ return clip_runs_enumerate(cdev, clip_call_copy_color, &ccdata);
+}
+
+/* Copy a rectangle with alpha */
+private int
+mask_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)
+{
+ gx_device_mask_clip *cdev = (gx_device_mask_clip *) dev;
+ clip_callback_data_t ccdata;
+
+ ccdata.tdev = cdev->target;
+ ccdata.data = data, ccdata.sourcex = sourcex, ccdata.raster = raster;
+ ccdata.x = x, ccdata.y = y, ccdata.w = w, ccdata.h = h;
+ ccdata.color[0] = color, ccdata.depth = depth;
+ return clip_runs_enumerate(cdev, clip_call_copy_alpha, &ccdata);
+}
+
+private int
+mask_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)
+{
+ gx_device_mask_clip *cdev = (gx_device_mask_clip *) dev;
+ clip_callback_data_t ccdata;
+
+ ccdata.tdev = cdev->target;
+ ccdata.x = x, ccdata.y = y, ccdata.w = w, ccdata.h = h;
+ ccdata.data = data, ccdata.sourcex = sourcex, ccdata.raster = raster;
+ ccdata.scolors = scolors, ccdata.textures = textures,
+ ccdata.tcolors = tcolors;
+ ccdata.phase.x = phase_x, ccdata.phase.y = phase_y, ccdata.lop = lop;
+ return clip_runs_enumerate(cdev, clip_call_strip_copy_rop, &ccdata);
+}
+
+private void
+mask_clip_get_clipping_box(gx_device * dev, gs_fixed_rect * pbox)
+{
+ gx_device_mask_clip *cdev = (gx_device_mask_clip *) dev;
+ gx_device *tdev = cdev->target;
+ gs_fixed_rect tbox;
+
+ (*dev_proc(tdev, get_clipping_box)) (tdev, &tbox);
+ pbox->p.x = tbox.p.x - cdev->phase.x;
+ pbox->p.y = tbox.p.y - cdev->phase.y;
+ pbox->q.x = tbox.q.x - cdev->phase.x;
+ pbox->q.y = tbox.q.y - cdev->phase.y;
+}
diff --git a/pstoraster/gxclipm.h b/pstoraster/gxclipm.h
new file mode 100644
index 000000000..84755640d
--- /dev/null
+++ b/pstoraster/gxclipm.h
@@ -0,0 +1,35 @@
+/* Copyright (C) 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Requires gsstruct.h, gxdevice.h, gxdevmem.h */
+
+#ifndef gxclipm_INCLUDED
+# define gxclipm_INCLUDED
+
+#include "gxmclip.h"
+
+extern const gx_device_mask_clip gs_mask_clip_device;
+
+#endif /* gxclipm_INCLUDED */
diff --git a/pstoraster/gxclist.c b/pstoraster/gxclist.c
new file mode 100644
index 000000000..cf80ace20
--- /dev/null
+++ b/pstoraster/gxclist.c
@@ -0,0 +1,728 @@
+/*
+ Copyright 1993-2000 by Easy Software Products.
+ Copyright 1991, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Command list document- and page-level code. */
+#include "memory_.h"
+#include "string_.h"
+#include "gx.h"
+#include "gp.h"
+#include "gpcheck.h"
+#include "gserrors.h"
+#include "gxdevice.h"
+#include "gxdevmem.h" /* must precede gxcldev.h */
+#include "gxcldev.h"
+#include "gxclpath.h"
+#include "gsparams.h"
+
+/* Forward declarations of driver procedures */
+private dev_proc_open_device(clist_open);
+private dev_proc_output_page(clist_output_page);
+private dev_proc_close_device(clist_close);
+private dev_proc_get_band(clist_get_band);
+
+/* In gxclrect.c */
+extern dev_proc_fill_rectangle(clist_fill_rectangle);
+extern dev_proc_copy_mono(clist_copy_mono);
+extern dev_proc_copy_color(clist_copy_color);
+extern dev_proc_copy_alpha(clist_copy_alpha);
+extern dev_proc_strip_tile_rectangle(clist_strip_tile_rectangle);
+extern dev_proc_strip_copy_rop(clist_strip_copy_rop);
+
+/* In gxclpath.c */
+extern dev_proc_fill_path(clist_fill_path);
+extern dev_proc_stroke_path(clist_stroke_path);
+
+/* In gxclimag.c */
+extern dev_proc_fill_mask(clist_fill_mask);
+extern dev_proc_begin_image(clist_begin_image);
+extern dev_proc_begin_typed_image(clist_begin_typed_image);
+extern dev_proc_create_compositor(clist_create_compositor);
+
+/* In gxclread.c */
+extern dev_proc_get_bits_rectangle(clist_get_bits_rectangle);
+
+/* Other forward declarations */
+private int clist_put_current_params(P1(gx_device_clist_writer *cldev));
+
+/* The device procedures */
+const gx_device_procs gs_clist_device_procs = {
+ clist_open,
+ gx_forward_get_initial_matrix,
+ gx_default_sync_output,
+ clist_output_page,
+ clist_close,
+ 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,
+ 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,
+ clist_copy_alpha,
+ clist_get_band,
+ gx_default_copy_rop,
+ clist_fill_path,
+ clist_stroke_path,
+ clist_fill_mask,
+ gx_default_fill_trapezoid,
+ gx_default_fill_parallelogram,
+ gx_default_fill_triangle,
+ gx_default_draw_thin_line,
+ clist_begin_image,
+ gx_default_image_data,
+ gx_default_end_image,
+ clist_strip_tile_rectangle,
+ clist_strip_copy_rop,
+ gx_forward_get_clipping_box,
+ clist_begin_typed_image,
+ clist_get_bits_rectangle,
+ gx_forward_map_color_rgb_alpha,
+ clist_create_compositor,
+ gx_forward_get_hardware_params,
+ gx_default_text_begin
+};
+
+/* ------ Define the command set and syntax ------ */
+
+/* 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)};
+
+/*
+ * 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.
+ *
+ * All the routines for allocating tables in the buffer are idempotent, so
+ * they can be used to check whether a given-size buffer is large enough.
+ */
+
+/*
+ * Calculate the desired size for the tile cache.
+ */
+private uint
+clist_tile_cache_size(const gx_device * target, uint data_size)
+{
+ uint bits_size =
+ (data_size / 5) & -align_cached_bits_mod; /* arbitrary */
+
+ if (!gx_device_must_halftone(target)) { /* 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;
+#undef min_bits_size
+ return bits_size;
+}
+
+/*
+ * Initialize the allocation for the tile cache. Sets: tile_hash_mask,
+ * tile_max_count, tile_table, chunk (structure), bits (structure).
+ */
+private int
+clist_init_tile_cache(gx_device * dev, byte * init_data, ulong data_size)
+{
+ gx_device_clist_writer * const cdev =
+ &((gx_device_clist *)dev)->writer;
+ byte *data = init_data;
+ uint bits_size = data_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;
+ uint hc = bits_size / avg_char_size;
+ uint hsize;
+
+ 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 */
+ /* Make sure the tables will fit. */
+ while (hc >= 3 && (hsize = (hc + 1) * sizeof(tile_hash)) >= bits_size)
+ hc >>= 1;
+ if (hc < 3)
+ return_error(gs_error_rangecheck);
+ cdev->tile_hash_mask = hc;
+ cdev->tile_max_count = hc - (hc >> 2);
+ cdev->tile_table = (tile_hash *) data;
+ data += hsize;
+ bits_size -= hsize;
+ gx_bits_cache_chunk_init(&cdev->chunk, data, bits_size);
+ gx_bits_cache_init(&cdev->bits, &cdev->chunk);
+ return 0;
+}
+
+/*
+ * Initialize the allocation for the bands. Requires: target. Sets:
+ * page_band_height (=page_info.band_params.BandHeight), nbands.
+ */
+private int
+clist_init_bands(gx_device * dev, uint data_size, int band_width,
+ int band_height)
+{
+ gx_device_clist_writer * const cdev =
+ &((gx_device_clist *)dev)->writer;
+ gx_device *target = cdev->target;
+ int nbands;
+
+ if (gdev_mem_data_size((gx_device_memory *) target, band_width,
+ band_height) > data_size
+ )
+ return_error(gs_error_rangecheck);
+ cdev->page_band_height = band_height;
+ nbands = (target->height + band_height - 1) / band_height;
+ cdev->nbands = nbands;
+#ifdef DEBUG
+ if (gs_debug_c('l') | gs_debug_c(':'))
+ dlprintf4("[:]width=%d, band_width=%d, band_height=%d, nbands=%d\n",
+ target->width, band_width, band_height, nbands);
+#endif
+ return 0;
+}
+
+/*
+ * Initialize the allocation for the band states, which are used only
+ * when writing. Requires: nbands. Sets: states, cbuf, cend.
+ */
+private int
+clist_init_states(gx_device * dev, byte * init_data, uint data_size)
+{
+ gx_device_clist_writer * const cdev =
+ &((gx_device_clist *)dev)->writer;
+ ulong state_size = cdev->nbands * (ulong) sizeof(gx_clist_state);
+
+ /*
+ * The +100 in the next line is bogus, but we don't know what the
+ * real check should be. We're effectively assuring that at least 100
+ * bytes will be available to buffer command operands.
+ */
+ if (state_size + sizeof(cmd_prefix) + cmd_largest_size + 100 > data_size)
+ return_error(gs_error_rangecheck);
+ cdev->states = (gx_clist_state *) init_data;
+ cdev->cbuf = init_data + state_size;
+ cdev->cend = init_data + data_size;
+ return 0;
+}
+
+/*
+ * Initialize all the data allocations. Requires: target. Sets:
+ * page_tile_cache_size, page_info.band_params.BandWidth,
+ * page_info.band_params.BandBufferSpace, + see above.
+ */
+private int
+clist_init_data(gx_device * dev, byte * init_data, uint data_size)
+{
+ gx_device_clist_writer * const cdev =
+ &((gx_device_clist *)dev)->writer;
+ gx_device *target = cdev->target;
+ const int band_width =
+ cdev->page_info.band_params.BandWidth =
+ (cdev->band_params.BandWidth ? cdev->band_params.BandWidth :
+ target->width);
+ int band_height = cdev->band_params.BandHeight;
+ const uint band_space =
+ cdev->page_info.band_params.BandBufferSpace =
+ (cdev->band_params.BandBufferSpace ?
+ cdev->band_params.BandBufferSpace : data_size);
+ byte *data = init_data;
+ uint size = band_space;
+ uint bits_size;
+ int code;
+
+ if (band_height) { /*
+ * The band height is fixed, so the band buffer requirement
+ * is completely determined.
+ */
+ uint band_data_size =
+ gdev_mem_data_size((gx_device_memory *) target,
+ band_width, band_height);
+
+ if (band_data_size >= band_space)
+ return_error(gs_error_rangecheck);
+ bits_size = min(band_space - band_data_size, data_size >> 1);
+ } else { /*
+ * Choose the largest band height that will fit in the
+ * rendering-time buffer.
+ */
+ bits_size = clist_tile_cache_size(target, band_space);
+ bits_size = min(bits_size, data_size >> 1);
+ band_height = gdev_mem_max_height((gx_device_memory *) target,
+ band_width,
+ band_space - bits_size);
+ if (band_height == 0)
+ return_error(gs_error_rangecheck);
+ }
+ code = clist_init_tile_cache(dev, data, bits_size);
+ if (code < 0)
+ return code;
+ cdev->page_tile_cache_size = bits_size;
+ data += bits_size;
+ size -= bits_size;
+ code = clist_init_bands(dev, size, band_width, band_height);
+ if (code < 0)
+ return code;
+ return clist_init_states(dev, data, data_size - bits_size);
+}
+/*
+ * Reset the device state (for writing). This routine requires only
+ * data, data_size, and target to be set, and is idempotent.
+ */
+private int
+clist_reset(gx_device * dev)
+{
+ gx_device_clist_writer * const cdev =
+ &((gx_device_clist *)dev)->writer;
+ int code = clist_init_data(dev, cdev->data, cdev->data_size);
+ int nbands;
+
+ if (code < 0)
+ return (cdev->permanent_error = code);
+ /* Now initialize the rest of the state. */
+ cdev->permanent_error = 0;
+ nbands = cdev->nbands;
+ cdev->ymin = cdev->ymax = -1; /* render_init not done yet */
+ memset(cdev->tile_table, 0, (cdev->tile_hash_mask + 1) *
+ sizeof(*cdev->tile_table));
+ cdev->cnext = cdev->cbuf;
+ cdev->ccl = 0;
+ cdev->band_range_list.head = cdev->band_range_list.tail = 0;
+ cdev->band_range_min = 0;
+ cdev->band_range_max = nbands - 1;
+ {
+ int band;
+ gx_clist_state *states = cdev->states;
+
+ for (band = 0; band < nbands; band++, states++) {
+ static const gx_clist_state cls_initial =
+ {cls_initial_values};
+
+ *states = cls_initial;
+ }
+ }
+ /*
+ * Round up the size of the per-tile 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->tile_known_min = nbands;
+ cdev->tile_known_max = -1;
+ cdev->imager_state = clist_imager_state_initial;
+ cdev->clip_path = NULL;
+ cdev->clip_path_id = gs_no_id;
+ cdev->color_space = 0;
+ {
+ int i;
+
+ for (i = 0; i < countof(cdev->transfer_ids); ++i)
+ cdev->transfer_ids[i] = gs_no_id;
+ }
+ cdev->black_generation_id = gs_no_id;
+ cdev->undercolor_removal_id = gs_no_id;
+ cdev->device_halftone_id = gs_no_id;
+ return 0;
+}
+/*
+ * Initialize the device state (for writing). This routine requires only
+ * data, data_size, and target to be set, and is idempotent.
+ */
+private int
+clist_init(gx_device * dev)
+{
+ gx_device_clist_writer * const cdev =
+ &((gx_device_clist *)dev)->writer;
+ int code = clist_reset(dev);
+
+ if (code >= 0) {
+ cdev->image_enum_id = gs_no_id;
+ cdev->error_is_retryable = 0;
+ cdev->driver_call_nesting = 0;
+ cdev->ignore_lo_mem_warnings = 0;
+ }
+ return code;
+}
+
+/* (Re)init open band files for output (set block size, etc). */
+private int /* ret 0 ok, -ve error code */
+clist_reinit_output_file(gx_device *dev)
+{ gx_device_clist_writer * const cdev =
+ &((gx_device_clist *)dev)->writer;
+ int code = 0;
+
+ /* bfile needs to guarantee cmd_blocks for: 1 band range, nbands */
+ /* & terminating entry */
+ int b_block = sizeof(cmd_block) * (cdev->nbands + 2);
+
+ /* cfile needs to guarantee one writer buffer */
+ /* + one end_clip cmd (if during image's clip path setup) */
+ /* + an end_image cmd for each band (if during image) */
+ /* + end_cmds for each band and one band range */
+ int c_block
+ = cdev->cend - cdev->cbuf + 2 + cdev->nbands * 2 + (cdev->nbands + 1);
+
+ /* All this is for partial page rendering's benefit, do only */
+ /* if partial page rendering is available */
+ if ( clist_test_VMerror_recoverable(cdev) )
+ { if (cdev->page_bfile != 0)
+ code = clist_set_memory_warning(cdev->page_bfile, b_block);
+ if (code >= 0 && cdev->page_cfile != 0)
+ code = clist_set_memory_warning(cdev->page_cfile, c_block);
+ }
+ return code;
+}
+
+/* Write out the current parameters that must be at the head of each page */
+/* if async rendering is in effect */
+private int
+clist_emit_page_header(gx_device *dev)
+{
+ gx_device_clist_writer * const cdev =
+ &((gx_device_clist *)dev)->writer;
+ int code = 0;
+
+ if ( (cdev->disable_mask & clist_disable_pass_thru_params) )
+ { do
+ if ( ( code = clist_put_current_params(cdev) ) >= 0 )
+ break;
+ while ( ( code = clist_VMerror_recover(cdev, code) ) < 0 );
+ cdev->permanent_error = (code < 0) ? code : 0;
+ if (cdev->permanent_error < 0)
+ cdev->error_is_retryable = 0;
+ }
+ return code;
+}
+
+/* Open the device's bandfiles */
+private int
+clist_open_output_file(gx_device *dev)
+{
+ gx_device_clist_writer * const cdev =
+ &((gx_device_clist *)dev)->writer;
+ char fmode[4];
+ int code;
+
+ if (cdev->do_not_open_or_close_bandfiles)
+ return 0; /* external bandfile open/close managed externally */
+ cdev->page_cfile = 0; /* in case of failure */
+ cdev->page_bfile = 0; /* ditto */
+ code = clist_init(dev);
+ if (code < 0)
+ return code;
+ strcpy(fmode, "w+");
+ strcat(fmode, gp_fmode_binary_suffix);
+ cdev->page_cfname[0] = 0; /* create a new file */
+ cdev->page_bfname[0] = 0; /* ditto */
+ cdev->page_bfile_end_pos = 0;
+ if ((code = clist_fopen(cdev->page_cfname, fmode, &cdev->page_cfile,
+ cdev->bandlist_memory, cdev->bandlist_memory,
+ true)) < 0 ||
+ (code = clist_fopen(cdev->page_bfname, fmode, &cdev->page_bfile,
+ cdev->bandlist_memory, cdev->bandlist_memory,
+ true)) < 0 ||
+ (code = clist_reinit_output_file(dev)) < 0
+ ) {
+ clist_close_output_file(dev);
+ cdev->permanent_error = code;
+ cdev->error_is_retryable = 0;
+ }
+ return code;
+}
+
+/* Close the device by freeing the temporary files. */
+/* Note that this does not deallocate the buffer. */
+int
+clist_close_output_file(gx_device *dev)
+{
+ gx_device_clist_writer * const cdev =
+ &((gx_device_clist *)dev)->writer;
+
+ if (cdev->page_cfile != NULL) {
+ clist_fclose(cdev->page_cfile, cdev->page_cfname, true);
+ cdev->page_cfile = NULL;
+ }
+ if (cdev->page_bfile != NULL) {
+ clist_fclose(cdev->page_bfile, cdev->page_bfname, true);
+ cdev->page_bfile = NULL;
+ }
+ return 0;
+}
+
+/* Open the device by initializing the device state and opening the */
+/* scratch files. */
+private int
+clist_open(gx_device *dev)
+{
+ gx_device_clist_writer * const cdev =
+ &((gx_device_clist *)dev)->writer;
+ int code;
+
+ cdev->permanent_error = 0;
+ code = clist_init(dev);
+ if (code < 0)
+ return code;
+ code = clist_open_output_file(dev);
+ if ( code >= 0)
+ code = clist_emit_page_header(dev);
+ return code;
+}
+
+private int
+clist_close(gx_device *dev)
+{
+ gx_device_clist_writer * const cdev =
+ &((gx_device_clist *)dev)->writer;
+
+ if (cdev->do_not_open_or_close_bandfiles)
+ return 0;
+ return clist_close_output_file(dev);
+}
+
+/* The output_page procedure should never be called! */
+private int
+clist_output_page(gx_device * dev, int num_copies, int flush)
+{
+ return_error(gs_error_Fatal);
+}
+
+/* Reset (or prepare to append to) the command list after printing a page. */
+int
+clist_finish_page(gx_device *dev, bool flush)
+{
+ gx_device_clist_writer * const cdev =
+ &((gx_device_clist *)dev)->writer;
+ int code;
+
+ if (flush) {
+ if (cdev->page_cfile != 0)
+ clist_rewind(cdev->page_cfile, true, cdev->page_cfname);
+ if (cdev->page_bfile != 0)
+ clist_rewind(cdev->page_bfile, true, cdev->page_bfname);
+ cdev->page_bfile_end_pos = 0;
+ } else {
+ if (cdev->page_cfile != 0)
+ clist_fseek(cdev->page_cfile, 0L, SEEK_END, cdev->page_cfname);
+ if (cdev->page_bfile != 0)
+ clist_fseek(cdev->page_bfile, 0L, SEEK_END, cdev->page_bfname);
+ }
+ code = clist_init(dev); /* reinitialize */
+ if (code >= 0)
+ code = clist_reinit_output_file(dev);
+ if (code >= 0)
+ code = clist_emit_page_header(dev);
+
+ return code;
+}
+
+/* ------ Writing ------ */
+
+/* End a page by flushing the buffer and terminating the command list. */
+int /* ret 0 all-ok, -ve error code, or +1 ok w/low-mem warning */
+clist_end_page(gx_device_clist_writer * cldev)
+{
+ int code = cmd_write_buffer(cldev, cmd_opv_end_page);
+ cmd_block cb;
+ int ecode = 0;
+
+ if (code >= 0) {
+ /*
+ * Write the terminating entry in the block file.
+ * Note that because of copypage, there may be many such entries.
+ */
+ cb.band_min = cb.band_max = cmd_band_end;
+ cb.pos = (cldev->page_cfile == 0 ? 0 : clist_ftell(cldev->page_cfile));
+ clist_fwrite_chars(&cb, sizeof(cb), cldev->page_bfile);
+ cldev->page_bfile_end_pos = clist_ftell(cldev->page_bfile);
+ }
+ if (code >= 0) {
+ ecode |= code;
+ cldev->page_bfile_end_pos = clist_ftell(cldev->page_bfile);
+ }
+ if (code < 0)
+ ecode = code;
+
+ /* Reset warning margin to 0 to release reserve memory if mem files */
+ if (cldev->page_bfile != 0)
+ clist_set_memory_warning(cldev->page_bfile, 0);
+ if (cldev->page_cfile != 0)
+ clist_set_memory_warning(cldev->page_cfile, 0);
+
+#ifdef DEBUG
+ if (gs_debug_c('l') | gs_debug_c(':'))
+ dlprintf2("[:]clist_end_page at cfile=%ld, bfile=%ld\n",
+ cb.pos, cldev->page_bfile_end_pos);
+#endif
+ return 0;
+}
+
+/* Recover recoverable VM error if possible without flushing */
+int /* ret -ve err, >= 0 if recovered w/# = cnt pages left in page queue */
+clist_VMerror_recover(gx_device_clist_writer *cldev,
+ int old_error_code)
+{
+ int code = old_error_code;
+ int pages_remain;
+
+ if (!clist_test_VMerror_recoverable(cldev) ||
+ !cldev->error_is_retryable ||
+ old_error_code != gs_error_VMerror
+ )
+ return old_error_code;
+
+ /* Do some rendering, return if enough memory is now free */
+ do {
+ pages_remain =
+ (*cldev->free_up_bandlist_memory)( (gx_device *)cldev, false );
+ if (pages_remain < 0) {
+ code = pages_remain; /* abort, error or interrupt req */
+ break;
+ }
+ if (clist_reinit_output_file( (gx_device *)cldev ) == 0) {
+ code = pages_remain; /* got enough memory to continue */
+ break;
+ }
+ } while (pages_remain);
+
+ if_debug1('L', "[L]soft flush of command list, status: %d\n", code);
+ return code;
+}
+
+/* If recoverable VM error, flush & try to recover it */
+int /* ret 0 ok, else -ve error */
+clist_VMerror_recover_flush(gx_device_clist_writer *cldev,
+ int old_error_code)
+{
+ int free_code = 0;
+ int reset_code = 0;
+ int code;
+
+ /* If the device has the ability to render partial pages, flush
+ * out the bandlist, and reset the writing state. Then, get the
+ * device to render this band. When done, see if there's now enough
+ * memory to satisfy the minimum low-memory guarantees. If not,
+ * get the device to render some more. If there's nothing left to
+ * render & still insufficient memory, declare an error condition.
+ */
+ if (!clist_test_VMerror_recoverable(cldev) ||
+ old_error_code != gs_error_VMerror
+ )
+ return old_error_code; /* sorry, don't have any means to recover this error */
+ free_code = (*cldev->free_up_bandlist_memory)( (gx_device *)cldev, true );
+
+ /* Reset the state of bands to "don't know anything" */
+ reset_code = clist_reset( (gx_device *)cldev );
+ if (reset_code >= 0)
+ reset_code = clist_open_output_file( (gx_device *)cldev );
+ if ( reset_code >= 0 &&
+ (cldev->disable_mask & clist_disable_pass_thru_params)
+ )
+ reset_code = clist_put_current_params(cldev);
+ if (reset_code < 0) {
+ cldev->permanent_error = reset_code;
+ cldev->error_is_retryable = 0;
+ }
+
+ code = (reset_code < 0 ? reset_code : free_code < 0 ? old_error_code : 0);
+ if_debug1('L', "[L]hard flush of command list, status: %d\n", code);
+ return code;
+}
+
+/* Write the target device's current parameter list */
+private int /* ret 0 all ok, -ve error */
+clist_put_current_params(gx_device_clist_writer *cldev)
+{
+ gx_device *target = cldev->target;
+ gs_c_param_list param_list;
+ int code;
+
+ /*
+ * If a put_params call fails, the device will be left in a closed
+ * state, but higher-level code won't notice this fact. We flag this by
+ * setting permanent_error, which prevents writing to the command list.
+ */
+
+ if (cldev->permanent_error)
+ return cldev->permanent_error;
+ gs_c_param_list_write(&param_list, cldev->memory);
+ code = (*dev_proc(target, get_params))
+ (target, (gs_param_list *)&param_list);
+ if (code >= 0) {
+ gs_c_param_list_read(&param_list);
+ code = cmd_put_params( cldev, (gs_param_list *)&param_list );
+ }
+ gs_c_param_list_release(&param_list);
+
+ return code;
+}
+
+/* ---------------- Driver interface ---------------- */
+
+private int
+clist_get_band(gx_device * dev, int y, int *band_start)
+{
+ gx_device_clist_writer * const cdev =
+ &((gx_device_clist *)dev)->writer;
+ int band_height = cdev->page_band_height;
+ int start;
+
+ if (y < 0)
+ y = 0;
+ else if (y >= dev->height)
+ y = dev->height;
+ *band_start = start = y - y % band_height;
+ return min(dev->height - start, band_height);
+}
diff --git a/pstoraster/gxclist.h b/pstoraster/gxclist.h
new file mode 100644
index 000000000..29f2ea820
--- /dev/null
+++ b/pstoraster/gxclist.h
@@ -0,0 +1,311 @@
+/* Copyright (C) 1991, 1995, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Command list definitions for Ghostscript. */
+/* Requires gxdevice.h and gxdevmem.h */
+
+#ifndef gxclist_INCLUDED
+# define gxclist_INCLUDED
+
+#include "gscspace.h"
+#include "gxband.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 a range of bands. In order to
+ * synchronize the two, we maintain the following invariant for buffered
+ * commands:
+ *
+ * If there are any band-range 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 band-range
+ * command in the buffer, we check to see if the buffer already has any
+ * band-range commands in it, and if so, whether they are the last commands
+ * in the buffer and are for the same range; if the answer to any of these
+ * questions is negative, we flush the buffer.
+ */
+
+/* ---------------- Public structures ---------------- */
+
+/*
+ * Define a saved page object. This consists of a snapshot of the device
+ * structure, information about the page per se, and the num_copies
+ * parameter of output_page.
+ */
+typedef struct gx_saved_page_s {
+ gx_device device;
+ char dname[8 + 1]; /* device name for checking */
+ gx_band_page_info info;
+ int num_copies;
+} gx_saved_page;
+
+/*
+ * Define a saved page placed at a particular (X,Y) offset for rendering.
+ */
+typedef struct gx_placed_page_s {
+ gx_saved_page *page;
+ gs_int_point offset;
+} gx_placed_page;
+
+/*
+ * Define a procedure to cause some bandlist memory to be freed up,
+ * probably by rendering current bandlist contents.
+ */
+#define proc_free_up_bandlist_memory(proc)\
+ int proc(P2(gx_device *dev, bool flush_current))
+
+/* ---------------- Internal structures ---------------- */
+
+/*
+ * 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;
+ /* To save space, instead of storing rep_width and rep_height, */
+ /* we store width / rep_width and height / rep_height. */
+ byte x_reps, y_reps;
+ ushort rep_shift;
+ 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 band-range 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_members\
+ gx_device_forward_common; /* (see gxdevice.h) */\
+ /* Following must be set before writing or reading. */\
+ /* See gx_device_clist_writer, below, for more that must be init'd */\
+ /* gx_device *target; */ /* device for which commands */\
+ /* are being buffered */\
+ dev_proc_make_buffer_device((*make_buffer_device));\
+ gs_memory_t *bandlist_memory; /* allocator for in-memory bandlist files */\
+ byte *data; /* buffer area */\
+ uint data_size; /* size of buffer */\
+ gx_band_params band_params; /* band buffering parameters */\
+ bool do_not_open_or_close_bandfiles; /* if true, do not open/close bandfiles */\
+ /* 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. */\
+ gx_band_page_info page_info; /* page information */\
+ int nbands /* # of bands */
+
+typedef struct gx_device_clist_common_s {
+ gx_device_clist_common_members;
+} gx_device_clist_common;
+
+#define clist_band_height(cldev) ((cldev)->page_info.band_height)
+#define clist_cfname(cldev) ((cldev)->page_info.cfname)
+#define clist_cfile(cldev) ((cldev)->page_info.cfile)
+#define clist_bfname(cldev) ((cldev)->page_info.bfname)
+#define clist_bfile(cldev) ((cldev)->page_info.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_members; /* (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 band_range_list; /* list of band-range commands */
+ int band_range_min, band_range_max; /* range for list */
+ 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 */
+ int tile_known_min, tile_known_max;
+ /* range of bands that knows the */
+ /* current tile parameters */
+ 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 */
+ gs_id clip_path_id; /* id of current clip path */
+ byte color_space; /* current color space identifier */
+ /* (only used for images) */
+ gs_indexed_params indexed_params; /* current indexed space parameters */
+ /* (ditto) */
+ gs_id transfer_ids[4]; /* ids of transfer maps */
+ gs_id black_generation_id; /* id of black generation map */
+ gs_id undercolor_removal_id; /* id of u.c.r. map */
+ gs_id device_halftone_id; /* id of device halftone */
+ gs_id image_enum_id; /* non-0 if we are inside an image */
+ /* that we are passing through */
+ int error_is_retryable; /* Extra status used to distinguish hard VMerrors */
+ /* from warnings upgraded to VMerrors. */
+ /* T if err ret'd by cmd_put_op et al can be retried */
+ int permanent_error; /* if < 0, error only cleared by clist_reset() */
+ int driver_call_nesting; /* nesting level of non-retryable driver calls */
+ int ignore_lo_mem_warnings; /* ignore warnings from clist file/mem */
+ /* Following must be set before writing */
+ proc_free_up_bandlist_memory((*free_up_bandlist_memory)); /* if nz, proc to free some bandlist memory */
+ int disable_mask; /* mask of routines to disable clist_disable_xxx */
+} gx_device_clist_writer;
+
+/* Bits for gx_device_clist_writer.disable_mask. Bit set disables behavior */
+#define clist_disable_fill_path (1 << 0)
+#define clist_disable_stroke_path (1 << 1)
+#define clist_disable_hl_image (1 << 2)
+#define clist_disable_complex_clip (1 << 3)
+#define clist_disable_nonrect_hl_image (1 << 4)
+#define clist_disable_pass_thru_params (1 << 5) /* disable EXCEPT at top of page */
+
+/* Define the state of a band list when reading. */
+/* For normal rasterizing, pages and num_pages are both 0. */
+typedef struct gx_device_clist_reader_s {
+ gx_device_clist_common_members; /* (must be first) */
+ const gx_placed_page *pages;
+ int num_pages;
+} gx_device_clist_reader;
+
+typedef union gx_device_clist_s {
+ gx_device_clist_common common;
+ gx_device_clist_reader reader;
+ gx_device_clist_writer writer;
+} gx_device_clist;
+
+/* setup before opening clist device */
+#define clist_init_params(xclist, xdata, xdata_size, xtarget, xmake_buffer, xband_params, xexternal, xmemory, xfree_bandlist, xdisable)\
+ (xclist)->common.data = (xdata);\
+ (xclist)->common.data_size = (xdata_size);\
+ (xclist)->common.target = (xtarget);\
+ (xclist)->common.make_buffer_device = (xmake_buffer);\
+ (xclist)->common.band_params = (xband_params);\
+ (xclist)->common.do_not_open_or_close_bandfiles = (xexternal);\
+ (xclist)->common.bandlist_memory = (xmemory);\
+ (xclist)->writer.free_up_bandlist_memory = (xfree_bandlist);\
+ (xclist)->writer.disable_mask = (xdisable)
+
+/* Determine whether this clist device is able to recover VMerrors */
+#define clist_test_VMerror_recoverable(cldev)\
+ ((cldev)->free_up_bandlist_memory != 0)
+
+/* The device template itself is never used, only the procedures. */
+extern const gx_device_procs gs_clist_device_procs;
+
+/* Reset (or prepare to append to) the command list after printing a page. */
+int clist_finish_page(P2(gx_device * dev, bool flush));
+
+/* Force bandfiles closed */
+int clist_close_output_file(P1(gx_device *dev));
+
+/* Define the abstract type for a printer device. */
+#ifndef gx_device_printer_DEFINED
+# define gx_device_printer_DEFINED
+typedef struct gx_device_printer_s gx_device_printer;
+#endif
+
+/* Do device setup from params passed in the command list. */
+int clist_setup_params(P1(gx_device *dev));
+
+/* Do more rendering to a client-supplied memory image, return results */
+int clist_get_overlay_bits(P4(gx_device_printer *pdev, int y, int line_count,
+ byte *data));
+
+/* Find out where the band buffer for a given line is going to fall on the */
+/* next call to get_bits. */
+int clist_locate_overlay_buffer(P3(gx_device_printer *pdev, int y,
+ byte **pdata));
+
+#endif /* gxclist_INCLUDED */
diff --git a/pstoraster/gxclmem.c b/pstoraster/gxclmem.c
new file mode 100644
index 000000000..2a0e1ce90
--- /dev/null
+++ b/pstoraster/gxclmem.c
@@ -0,0 +1,1136 @@
+/* Copyright (C) 1995, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* RAM-based command list implementation */
+#include "memory_.h"
+#include "gx.h"
+#include "gserrors.h"
+#include "gsmalloc.h" /* for gs_memory_default */
+#include "gxclmem.h"
+
+/*
+ * Based on: memfile.c Version: 1.4 3/21/95 14:59:33 by Ray Johnston.
+ * Copyright assigned to Aladdin Enterprises.
+ */
+
+/*****************************************************************************
+
+ This package is more or less optimal for use by the clist routines, with
+ a couple of the more likely to change "tuning" parameters given in the
+ two macros below -- NEED_TO_COMPRESS and GET_NUM_RAW_BUFFERS. Usually
+ the NEED_TO_COMPRESS decision will be deferred as long as possible based
+ on some total system free RAM space remaining.
+
+ The data structures are in "memfile.h", and the primary 'tuning' parameter
+ is MEMFILE_DATA_SIZE. This should not be too small to keep the overhead
+ ratio of the block structures to the clist data small. A value of 16384
+ is probably in the ballpark.
+
+ The concept is that a memory based "file" is created initially without
+ compression, with index blocks every MEMFILE_DATA_SIZE of the file. The
+ primary blocks (used by the memfile_fseek logic) for indexing into the
+ file are called 'logical' (LOG_MEMFILE_BLK) and the data in stored in a
+ different block called a 'physical' block (PHYS_MEMFILE_BLK). When the
+ file is not yet compressed, indicated by (f->phys_curr==NULL), then there
+ is one physical block for each logical block. The physical block also has
+ the 'data_limit' set to NULL if the data is not compressed. Thus when a
+ file is not compressed there is one physical block for each logical block.
+
+COMPRESSION.
+
+ When compression is triggered for a file then all of the blocks except
+ the last are compressed. Compression will result in a physical block
+ that holds data for more than one logical block. Each logical block now
+ points to the start of compressed data in a physical block with the
+ 'phys_pdata' pointer. The 'data_limit' pointer in the physical block is
+ where the compression logic stopped storing data (as stream data
+ compressors are allowed to do). The data for the logical block may span
+ to the next physical block. Once physical blocks are compressed, they are
+ chained together using the 'link' field.
+
+ The 'f->phys_curr' points to the block being filled by compression, with
+ the 'f->wt.ptr' pointing to the last byte filled in the block. These are
+ used during subsequent compression when the last logical block of the
+ file fills the physical block.
+
+DECOMPRESSION.
+
+ During reading the clist, if the logical block points to an uncompressed
+ physical block, then 'memfile_get_pdata' simply sets the 'pdata' and the
+ 'pdata_end' pointers. If the logical block was compressed, then it may
+ still be resident in a cache of decompression buffers. The number of these
+ decompression buffers is not critical -- even one is enough, but having
+ more may prevent decompressing blocks more than once (a cache_miss). The
+ number of decompression buffers, called "raw" buffers, that are attempted
+ to allocate can be changed with the GET_NUM_RAW_BUFFERS macro, but no
+ error occurs if less than that number can be allocated.
+
+ If the logical block still resides in a decompression cache buffer, then
+ the 'raw_block' will identify the block. If the data for a logical block
+ only exists in compressed form, then the "tail" of the list of decompression
+ buffers is re-used, marking the 'raw_block' of the logical block that was
+ previously associated with this data to NULL.
+
+ Whichever raw decompression buffer is accessed is moved to the head of the
+ decompression buffer list in order to keep the tail of the list as the
+ "least recently used".
+
+ There are some DEBUG global static variables used to count the number of
+ cache hits "tot_cache_hits" and the number of times a logical block is
+ decompressed "tot_cache_miss". Note that the actual number of cache miss
+ events is 'f->log_length/MEMFILE_DATA_SIZE - tot_cache_miss' since we
+ assume that every logical block must be decmpressed at least once.
+
+ Empirical results so far indicate that if one cache raw buffer for every
+ 32 logical blocks, then the hit/miss ratio exceeds 99%. Of course, the
+ number of raw buffers should be more than 1 if possible, and in many
+ implementations (single threaded), the memory usage does not increase
+ during the page output step so almost all of memory can be used for
+ these raw buffers to prevent the likelihood of a cache miss.
+
+ Of course, this is dependent on reasonably efficient clist blocking
+ during writing which is dependent on the data and on the BufferSpace
+ value which determines the number of clist band data buffers available.
+ Empirical testing shows that the overall efficiency is best if the
+ BufferSpace value is 1,000,000 (as in the original Ghostscript source).
+ [Note: I expected to be able to use smaller buffer sizes for some cases,
+ but this resulted in a high level of thrashing...RJJ]
+
+LIMITATIONS.
+
+ The most serious limitation is caused by the way 'memfile_fwrite' decides
+ to free up and re-initialize a file. If memfile_fwrite is called after
+ a seek to any location except the start of the file, then an error is
+ issued since logic is not present to properly free up on a partial file.
+ This is not a problem as used by the 'clist' logic since rewind is used
+ to position to the start of a file when re-using it after an 'erasepage'.
+
+ Since the 'clist' logic always traverses the clist using fseek's to ever
+ increasing locations, no optimizations of backward seeks was implemented.
+ This would be relatively easy with back chain links or bi-directional
+ "X-OR" pointer information to link the logical block chain. The rewind
+ function is optimal and moves directly to the start of the file.
+
+********************************************************************************/
+
+/*
+ The need to compress should be conditional on the amount of available
+ memory, but we don't have a way to communicate this to these routines.
+ Instead, we simply start compressing when we've allocated more than
+ COMPRESSION_THRESHOLD amount of data. The threshold should be at
+ least as large as the fixed overhead of the compressor plus the
+ decompressor, plus the expected compressed size of a block that size.
+ */
+private const long COMPRESSION_THRESHOLD = 300000;
+
+#define NEED_TO_COMPRESS(f)\
+ ((f)->ok_to_compress && (f)->total_space > COMPRESSION_THRESHOLD)
+
+ /* FOR NOW ALLOCATE 1 raw buffer for every 32 blocks (at least 8) */
+#define GET_NUM_RAW_BUFFERS( f ) \
+ max(f->log_length/MEMFILE_DATA_SIZE/32, 8)
+
+#define MALLOC(f, siz, cname)\
+ (void *)gs_alloc_bytes((f)->data_memory, siz, cname)
+#define FREE(f, obj, cname)\
+ (gs_free_object((f)->data_memory, obj, cname),\
+ (f)->total_space -= sizeof(*(obj)))
+
+/* Structure descriptor for GC */
+private_st_MEMFILE();
+
+ /* forward references */
+private void memfile_free_mem(P1(MEMFILE * f));
+private int memfile_init_empty(P1(MEMFILE * f));
+
+/************************************************/
+/* #define DEBUG /- force statistics -/ */
+/************************************************/
+
+#ifdef DEBUG
+long tot_compressed;
+long tot_raw;
+long tot_cache_miss;
+long tot_cache_hits;
+long tot_swap_out;
+
+/*
+ The following pointers are here only for helping with a dumb debugger
+ that can't inspect local variables!
+ */
+byte *decomp_wt_ptr0, *decomp_wt_limit0;
+const byte *decomp_rd_ptr0, *decomp_rd_limit0;
+byte *decomp_wt_ptr1, *decomp_wt_limit1;
+const byte *decomp_rd_ptr1, *decomp_rd_limit1;
+
+#endif
+
+/* ----------------------------- Memory Allocation --------------------- */
+void * /* allocated memory's address, 0 if failure */
+allocateWithReserve(
+ MEMFILE *f, /* file to allocate mem to */
+ int sizeofBlock, /* size of block to allocate */
+ int *return_code, /* RET 0 ok, -ve GS-style error, or +1 if OK but low memory */
+ const char *allocName, /* name to allocate by */
+ const char *errorMessage /* error message to print */
+)
+{
+ int code = 0; /* assume success */
+ void *block = MALLOC(f, sizeofBlock, allocName);
+
+ if (block == NULL) {
+ /* Try to recover block from reserve */
+ if (sizeofBlock == sizeof(LOG_MEMFILE_BLK)) {
+ if (f->reserveLogBlockCount > 0) {
+ block = f->reserveLogBlockChain;
+ f->reserveLogBlockChain = f->reserveLogBlockChain->link;
+ --f->reserveLogBlockCount;
+ }
+ } else if (sizeofBlock == sizeof(PHYS_MEMFILE_BLK) ||
+ sizeofBlock == sizeof(RAW_BUFFER)
+ ) {
+ if (f->reservePhysBlockCount > 0) {
+ block = f->reservePhysBlockChain;
+ f->reservePhysBlockChain = f->reservePhysBlockChain->link;
+ --f->reservePhysBlockCount;
+ }
+ }
+ if (block != NULL)
+ code = 1; /* successful, but allocated from reserve */
+ }
+ if (block != NULL)
+ f->total_space += sizeofBlock;
+ else
+ code = gs_note_error(gs_error_VMerror);
+ *return_code = code;
+ return block;
+}
+
+/* ---------------- Open/close/unlink ---------------- */
+
+int
+memfile_fopen(char fname[gp_file_name_sizeof], const char *fmode,
+ clist_file_ptr /*MEMFILE * */ * pf,
+ gs_memory_t *mem, gs_memory_t *data_mem, bool ok_to_compress)
+{
+ MEMFILE *f = 0;
+ int code = 0;
+
+ /* We don't implement reopening an existing file. */
+ if (fname[0] != 0 || fmode[0] != 'w') {
+ code = gs_note_error(gs_error_invalidfileaccess);
+ goto finish;
+ }
+
+ /* There is no need to set fname in this implementation, */
+ /* but we do it anyway. */
+ fname[0] = (ok_to_compress ? 'a' : 'b');
+ fname[1] = 0;
+
+ f = gs_alloc_struct(mem, MEMFILE, &st_MEMFILE,
+ "memfile_open_scratch(MEMFILE)");
+ if (f == NULL) {
+ eprintf1("memfile_open_scratch(%s): gs_alloc_struct failed\n", fname);
+ code = gs_note_error(gs_error_VMerror);
+ goto finish;
+ }
+ f->memory = mem;
+ f->data_memory = data_mem;
+ /* init an empty file, BEFORE allocating de/compress state */
+ f->compress_state = 0; /* make clean for GC, or alloc'n failure */
+ f->decompress_state = 0;
+ f->total_space = 0;
+ f->reservePhysBlockChain = NULL;
+ f->reservePhysBlockCount = 0;
+ f->reserveLogBlockChain = NULL;
+ f->reserveLogBlockCount = 0;
+ /* init an empty file */
+ if ((code = memfile_init_empty(f)) < 0)
+ goto finish;
+ if ((code = memfile_set_memory_warning(f, 0)) < 0)
+ goto finish;
+ /*
+ * Disregard the ok_to_compress flag, since the size threshold gives us
+ * a much better criterion for deciding when compression is appropriate.
+ */
+ f->ok_to_compress = /*ok_to_compress */ true;
+ f->compress_state = 0; /* make clean for GC */
+ f->decompress_state = 0;
+ if (f->ok_to_compress) {
+ const stream_state *compress_proto = clist_compressor_state(NULL);
+ const stream_state *decompress_proto = clist_decompressor_state(NULL);
+ const stream_template *compress_template = compress_proto->template;
+ const stream_template *decompress_template = decompress_proto->template;
+
+ f->compress_state =
+ gs_alloc_struct(mem, stream_state, compress_template->stype,
+ "memfile_open_scratch(compress_state)");
+ f->decompress_state =
+ gs_alloc_struct(mem, stream_state, decompress_template->stype,
+ "memfile_open_scratch(decompress_state)");
+ if (f->compress_state == 0 || f->decompress_state == 0) {
+ eprintf1("memfile_open_scratch(%s): gs_alloc_struct failed\n", fname);
+ code = gs_note_error(gs_error_VMerror);
+ goto finish;
+ }
+ memcpy(f->compress_state, compress_proto,
+ gs_struct_type_size(compress_template->stype));
+ f->compress_state->memory = mem;
+ memcpy(f->decompress_state, decompress_proto,
+ gs_struct_type_size(decompress_template->stype));
+ f->decompress_state->memory = mem;
+ if (compress_template->set_defaults)
+ (*compress_template->set_defaults) (f->compress_state);
+ if (decompress_template->set_defaults)
+ (*decompress_template->set_defaults) (f->decompress_state);
+ }
+ f->total_space = 0;
+
+#ifdef DEBUG
+ /* If this is the start, init some statistics. */
+ /* Hack: we know the 'a' file is opened first. */
+ if (*fname == 'a') {
+ tot_compressed = 0;
+ tot_raw = 0;
+ tot_cache_miss = 0;
+ tot_cache_hits = 0;
+ tot_swap_out = 0;
+ }
+#endif
+finish:
+ if (code < 0) {
+ /* return failure, clean up memory before leaving */
+ if (f != NULL)
+ memfile_fclose((clist_file_ptr)f, fname, true);
+ } else {
+ /* return success */
+ *pf = f;
+ }
+ return code;
+}
+
+int
+memfile_fclose(clist_file_ptr cf, const char *fname, bool delete)
+{
+ MEMFILE *const f = (MEMFILE *)cf;
+
+ /* We don't implement closing without deletion. */
+ if (!delete)
+ return_error(gs_error_invalidfileaccess);
+ memfile_free_mem(f);
+
+ /* Free reserve blocks; don't do it in memfile_free_mem because */
+ /* that routine gets called to reinit file */
+ while (f->reserveLogBlockChain != NULL) {
+ LOG_MEMFILE_BLK *block = f->reserveLogBlockChain;
+
+ f->reserveLogBlockChain = block->link;
+ FREE(f, block, "memfile_set_block_size");
+ }
+ while (f->reservePhysBlockChain != NULL) {
+ PHYS_MEMFILE_BLK *block = f->reservePhysBlockChain;
+
+ f->reservePhysBlockChain = block->link;
+ FREE(f, block, "memfile_set_block_size");
+ }
+
+ /* deallocate de/compress state */
+ gs_free_object(f->memory, f->decompress_state,
+ "memfile_close_and_unlink(decompress_state)");
+ gs_free_object(f->memory, f->compress_state,
+ "memfile_close_and_unlink(compress_state)");
+
+ /* deallocate the memfile object proper */
+ gs_free_object(f->memory, f, "memfile_close_and_unlink(MEMFILE)");
+ return 0;
+}
+
+int
+memfile_unlink(const char *fname)
+{
+ /*
+ * Since we have no way to represent a memfile other than by the
+ * pointer, we don't (can't) implement unlinking.
+ */
+ return_error(gs_error_invalidfileaccess);
+}
+
+/* ---------------- Writing ---------------- */
+
+/* Pre-alloc enough reserve mem blox to guarantee a write of N bytes will succeed */
+int /* returns 0 ok, gs_error_VMerror if insufficient */
+memfile_set_memory_warning(clist_file_ptr cf, int bytes_left)
+{
+ MEMFILE *const f = (MEMFILE *)cf;
+ int code = 0;
+ /*
+ * Determine req'd memory block count from bytes_left.
+ * Allocate enough phys & log blocks to hold bytes_left
+ * + 1 phys blk for compress_log_blk + 1 phys blk for decompress.
+ */
+ int logNeeded =
+ (bytes_left + MEMFILE_DATA_SIZE - 1) / MEMFILE_DATA_SIZE;
+ int physNeeded = logNeeded;
+
+ if (bytes_left > 0)
+ ++physNeeded;
+ if (f->raw_head == NULL)
+ ++physNeeded; /* have yet to allocate read buffers */
+
+ /* Allocate or free memory depending on need */
+ while (logNeeded > f->reserveLogBlockCount) {
+ LOG_MEMFILE_BLK *block =
+ MALLOC( f, sizeof(LOG_MEMFILE_BLK), "memfile_set_block_size" );
+
+ if (block == NULL) {
+ code = gs_note_error(gs_error_VMerror);
+ goto finish;
+ }
+ block->link = f->reserveLogBlockChain;
+ f->reserveLogBlockChain = block;
+ ++f->reserveLogBlockCount;
+ }
+ while (logNeeded < f->reserveLogBlockCount) {
+ LOG_MEMFILE_BLK *block = f->reserveLogBlockChain;
+
+ f->reserveLogBlockChain = block->link;
+ FREE(f, block, "memfile_set_block_size");
+ --f->reserveLogBlockCount;
+ }
+ while (physNeeded > f->reservePhysBlockCount) {
+ PHYS_MEMFILE_BLK *block =
+ MALLOC( f,
+ max( sizeof(PHYS_MEMFILE_BLK), sizeof(RAW_BUFFER) ),
+ "memfile_set_block_size");
+
+ if (block == NULL) {
+ code = gs_note_error(gs_error_VMerror);
+ goto finish;
+ }
+ block->link = f->reservePhysBlockChain;
+ f->reservePhysBlockChain = block;
+ ++f->reservePhysBlockCount;
+ }
+ while (physNeeded < f->reservePhysBlockCount) {
+ PHYS_MEMFILE_BLK *block = f->reservePhysBlockChain;
+
+ f->reservePhysBlockChain = block->link;
+ FREE(f, block, "memfile_set_block_size");
+ --f->reservePhysBlockCount;
+ }
+ f->error_code = 0; /* memfile_set_block_size is how user resets this */
+finish:
+ return code;
+}
+
+private int
+compress_log_blk(MEMFILE * f, LOG_MEMFILE_BLK * bp)
+{
+ int status;
+ int ecode = 0; /* accumulate low-memory warnings */
+ int code;
+ long compressed_size;
+ byte *start_ptr;
+ PHYS_MEMFILE_BLK *newphys;
+
+ /* compress this block */
+ f->rd.ptr = (const byte *)(bp->phys_blk->data) - 1;
+ f->rd.limit = f->rd.ptr + MEMFILE_DATA_SIZE;
+
+ bp->phys_blk = f->phys_curr;
+ bp->phys_pdata = (char *)(f->wt.ptr) + 1;
+ if (f->compress_state->template->reinit != 0)
+ (*f->compress_state->template->reinit)(f->compress_state);
+ compressed_size = 0;
+
+ start_ptr = f->wt.ptr;
+ status = (*f->compress_state->template->process)(f->compress_state,
+ &(f->rd), &(f->wt), true);
+ bp->phys_blk->data_limit = (char *)(f->wt.ptr);
+
+ if (status == 1) { /* More output space needed (see strimpl.h) */
+ /* allocate another physical block, then compress remainder */
+ compressed_size = f->wt.limit - start_ptr;
+ newphys =
+ allocateWithReserve(f, sizeof(*newphys), &code, "memfile newphys",
+ "compress_log_blk : MALLOC for 'newphys' failed\n");
+ if (code < 0)
+ return code;
+ ecode |= code; /* accumulate any low-memory warnings */
+ newphys->link = NULL;
+ bp->phys_blk->link = newphys;
+ f->phys_curr = newphys;
+ f->wt.ptr = (byte *) (newphys->data) - 1;
+ f->wt.limit = f->wt.ptr + MEMFILE_DATA_SIZE;
+
+ start_ptr = f->wt.ptr;
+ status =
+ (*f->compress_state->template->process)(f->compress_state,
+ &(f->rd), &(f->wt), true);
+ if (status != 0) {
+ /*
+ * You'd think the above line is a bug, but in real life 1 src
+ * block never ends up getting split across 3 dest blocks.
+ */
+ /* CHANGE memfile_set_memory_warning if this assumption changes. */
+ eprintf("Compression required more than one full block!\n");
+ return_error(gs_error_Fatal);
+ }
+ newphys->data_limit = (char *)(f->wt.ptr);
+ }
+ compressed_size += f->wt.ptr - start_ptr;
+ if (compressed_size > MEMFILE_DATA_SIZE) {
+ eprintf2("\nCompression didn't - raw=%d, compressed=%ld\n",
+ MEMFILE_DATA_SIZE, compressed_size);
+ }
+#ifdef DEBUG
+ tot_compressed += compressed_size;
+#endif
+ return (status < 0 ? gs_note_error(gs_error_ioerror) : ecode);
+} /* end "compress_log_blk()" */
+
+/* Internal (private) routine to handle end of logical block */
+private int /* ret 0 ok, -ve error, or +ve low-memory warning */
+memfile_next_blk(MEMFILE * f)
+{
+ LOG_MEMFILE_BLK *bp = f->log_curr_blk;
+ LOG_MEMFILE_BLK *newbp;
+ PHYS_MEMFILE_BLK *newphys, *oldphys;
+ int ecode = 0; /* accumulate low-memory warnings */
+ int code;
+
+ if (f->phys_curr == NULL) { /* means NOT compressing */
+ /* allocate a new block */
+ newphys =
+ allocateWithReserve(f, sizeof(*newphys), &code, "memfile newphys",
+ "memfile_next_blk: MALLOC 1 for 'newphys' failed\n");
+ if (code < 0)
+ return code;
+ ecode |= code; /* accumulate low-mem warnings */
+ newphys->link = NULL;
+ newphys->data_limit = NULL; /* raw */
+
+ newbp =
+ allocateWithReserve(f, sizeof(*newbp), &code, "memfile newbp",
+ "memfile_next_blk: MALLOC 1 for 'newbp' failed\n");
+ if (code < 0) {
+ FREE(f, newphys, "memfile newphys");
+ return code;
+ }
+ ecode |= code; /* accumulate low-mem warnings */
+ bp->link = newbp;
+ newbp->link = NULL;
+ newbp->raw_block = NULL;
+ f->log_curr_blk = newbp;
+
+ /* check if need to start compressing */
+ if (NEED_TO_COMPRESS(f)) {
+ if_debug0(':', "[:]Beginning compression\n");
+ /* compress the entire file up to this point */
+ if (!f->compressor_initialized) {
+ int code = 0;
+
+ if (f->compress_state->template->init != 0)
+ code = (*f->compress_state->template->init) (f->compress_state);
+ if (code < 0)
+ return_error(gs_error_VMerror); /****** BOGUS ******/
+ if (f->decompress_state->template->init != 0)
+ code = (*f->decompress_state->template->init)
+ (f->decompress_state);
+ if (code < 0)
+ return_error(gs_error_VMerror); /****** BOGUS ******/
+ f->compressor_initialized = true;
+ }
+ /* Write into the new physical block we just allocated, */
+ /* replace it after the loop (after some blocks are freed) */
+ f->phys_curr = newphys;
+ f->wt.ptr = (byte *) (newphys->data) - 1;
+ f->wt.limit = f->wt.ptr + MEMFILE_DATA_SIZE;
+ bp = f->log_head;
+ while (bp != newbp) { /* don't compress last block */
+ int code;
+
+ oldphys = bp->phys_blk;
+ if ((code = compress_log_blk(f, bp)) < 0)
+ return code;
+ ecode |= code;
+ FREE(f, oldphys, "memfile_next_blk(oldphys)");
+ bp = bp->link;
+ } /* end while( ) compress loop */
+ /* Allocate a physical block for this (last) logical block */
+ newphys =
+ allocateWithReserve(f, sizeof(*newphys), &code,
+ "memfile newphys",
+ "memfile_next_blk: MALLOC 2 for 'newphys' failed\n");
+ if (code < 0)
+ return code;
+ ecode |= code; /* accumulate low-mem warnings */
+ newphys->link = NULL;
+ newphys->data_limit = NULL; /* raw */
+
+ } /* end convert file to compressed */
+ newbp->phys_blk = newphys;
+ f->pdata = newphys->data;
+ f->pdata_end = newphys->data + MEMFILE_DATA_SIZE;
+ } /* end if NOT compressing */
+ /* File IS being compressed */
+ else {
+ int code;
+
+ oldphys = bp->phys_blk; /* save raw phys block ID */
+ /* compresses bp on phys list */
+ if ((code = compress_log_blk(f, bp)) < 0)
+ return code;
+ ecode |= code;
+ newbp =
+ allocateWithReserve(f, sizeof(*newbp), &code, "memfile newbp",
+ "memfile_next_blk: MALLOC 2 for 'newbp' failed\n");
+ if (code < 0)
+ return code;
+ ecode |= code;
+ bp->link = newbp;
+ newbp->link = NULL;
+ newbp->raw_block = NULL;
+ /* Re-use the raw phys block for this new logical blk */
+ newbp->phys_blk = oldphys;
+ f->pdata = oldphys->data;
+ f->pdata_end = f->pdata + MEMFILE_DATA_SIZE;
+ f->log_curr_blk = newbp;
+ } /* end else (when we are compressing) */
+
+ return (0);
+}
+
+int /* returns # of chars actually written */
+memfile_fwrite_chars(const void *data, uint len, clist_file_ptr cf)
+{
+ const char *str = (const char *)data;
+ MEMFILE *f = (MEMFILE *) cf;
+ uint count = len;
+ int ecode;
+
+ /* check if we are writing to the start of the file. If so, then */
+ /* free the file memory and re-initialize it (frees memory) */
+ if (f->log_curr_pos == 0) {
+ int code;
+
+ memfile_free_mem(f);
+ if ((code = memfile_init_empty(f)) < 0) {
+ f->error_code = code;
+ return 0;
+ }
+ }
+ if (f->log_curr_blk->link != 0) {
+ eprintf(" Write file truncate -- need to free physical blocks.\n");
+ }
+ while (count) {
+ uint move_count = f->pdata_end - f->pdata;
+
+ if (move_count == 0) {
+ if ((ecode = memfile_next_blk(f)) != 0) {
+ f->error_code = ecode;
+ if (ecode < 0)
+ return 0;
+ }
+ } else {
+ if (move_count > count)
+ move_count = count;
+ memmove(f->pdata, str, move_count);
+ f->pdata += move_count;
+ str += move_count;
+ count -= move_count;
+ }
+ }
+ f->log_curr_pos += len;
+ f->log_length = f->log_curr_pos; /* truncate length to here */
+#ifdef DEBUG
+ tot_raw += len;
+#endif
+ return (len);
+}
+
+/* */
+/* Internal routine to set the f->pdata and f->pdata_end pointers */
+/* for the current logical block f->log_curr_blk */
+/* */
+/* If data only exists in compressed form, allocate a raw buffer */
+/* and decompress it. */
+/* */
+
+private int
+memfile_get_pdata(MEMFILE * f)
+{
+ int i, num_raw_buffers, status;
+ LOG_MEMFILE_BLK *bp = f->log_curr_blk;
+
+ if (bp->phys_blk->data_limit == NULL) {
+ /* Not compressed, return this data pointer */
+ f->pdata = (bp->phys_blk)->data;
+ i = f->log_curr_pos % MEMFILE_DATA_SIZE; /* pos within block */
+ i = f->log_curr_pos - i; /* base of block */
+ if (i + MEMFILE_DATA_SIZE > f->log_length)
+ f->pdata_end = f->pdata + f->log_length - i;
+ else
+ f->pdata_end = f->pdata + MEMFILE_DATA_SIZE;
+ } else {
+ /* data was compressed */
+ if (f->raw_head == NULL) {
+ /* need to allocate the raw buffer pool */
+ num_raw_buffers = GET_NUM_RAW_BUFFERS(f);
+ if (f->reservePhysBlockCount) {
+ /* HACK: allocate reserve block that's been reserved for
+ * decompression. This buffer's block was pre-allocated to make
+ * sure we won't come up short here. Take from chain instead of
+ * allocateWithReserve() since this buf would just be wasted if
+ * allowed to remain preallocated. */
+ f->raw_head = (RAW_BUFFER *)f->reservePhysBlockChain;
+ f->reservePhysBlockChain = f->reservePhysBlockChain->link;
+ --f->reservePhysBlockCount;
+ } else {
+ int code;
+
+ f->raw_head =
+ allocateWithReserve(f, sizeof(*f->raw_head), &code,
+ "memfile raw buffer",
+ "memfile_get_pdata: MALLOC for 'raw_head' failed\n");
+ if (code < 0)
+ return code;
+ }
+ f->raw_head->back = NULL;
+ f->raw_tail = f->raw_head;
+ f->raw_tail->log_blk = NULL;
+ for (i = 0; i < num_raw_buffers; i++) {
+ f->raw_tail->fwd = (RAW_BUFFER *) MALLOC(f, sizeof(RAW_BUFFER),
+ "memfile raw buffer");
+ /* if MALLOC fails, then just stop allocating */
+ if (!f->raw_tail->fwd)
+ break;
+ f->total_space += sizeof(RAW_BUFFER);
+ f->raw_tail->fwd->back = f->raw_tail;
+ f->raw_tail = f->raw_tail->fwd;
+ f->raw_tail->log_blk = NULL;
+ }
+ f->raw_tail->fwd = NULL;
+ num_raw_buffers = i + 1; /* if MALLOC failed, then OK */
+ if_debug1(':', "[:]Number of raw buffers allocated=%d\n",
+ num_raw_buffers);
+ } /* end allocating the raw buffer pool (first time only) */
+ if (bp->raw_block == NULL) {
+#ifdef DEBUG
+ tot_cache_miss++; /* count every decompress */
+#endif
+ /* find a raw buffer and decompress */
+ if (f->raw_tail->log_blk != NULL) {
+ /* This block was in use, grab it */
+#ifdef DEBUG
+ tot_swap_out++;
+#endif
+ f->raw_tail->log_blk->raw_block = NULL; /* data no longer here */
+ f->raw_tail->log_blk = NULL;
+ }
+ /* Use the last raw block in the chain (the oldest) */
+ f->raw_tail->back->fwd = NULL; /* disconnect from tail */
+ f->raw_tail->fwd = f->raw_head; /* new head */
+ f->raw_head->back = f->raw_tail;
+ f->raw_tail = f->raw_tail->back;
+ f->raw_head = f->raw_head->back;
+ f->raw_head->back = NULL;
+ f->raw_head->log_blk = bp;
+
+ /* Decompress the data into this raw block */
+ /* Initialize the decompressor */
+ if (f->decompress_state->template->reinit != 0)
+ (*f->decompress_state->template->reinit) (f->decompress_state);
+ /* Set pointers and call the decompress routine */
+ f->wt.ptr = (byte *) (f->raw_head->data) - 1;
+ f->wt.limit = f->wt.ptr + MEMFILE_DATA_SIZE;
+ f->rd.ptr = (const byte *)(bp->phys_pdata) - 1;
+ f->rd.limit = (const byte *)bp->phys_blk->data_limit;
+#ifdef DEBUG
+ decomp_wt_ptr0 = f->wt.ptr;
+ decomp_wt_limit0 = f->wt.limit;
+ decomp_rd_ptr0 = f->rd.ptr;
+ decomp_rd_limit0 = f->rd.limit;
+#endif
+ status = (*f->decompress_state->template->process)
+ (f->decompress_state, &(f->rd), &(f->wt), true);
+ if (status == 0) { /* More input data needed */
+ /* switch to next block and continue decompress */
+ int back_up = 0; /* adjust pointer backwards */
+
+ if (f->rd.ptr != f->rd.limit) {
+ /* transfer remainder bytes from the previous block */
+ back_up = f->rd.limit - f->rd.ptr;
+ for (i = 0; i < back_up; i++)
+ *(bp->phys_blk->link->data - back_up + i) = *++f->rd.ptr;
+ }
+ f->rd.ptr = (const byte *)bp->phys_blk->link->data - back_up - 1;
+ f->rd.limit = (const byte *)bp->phys_blk->link->data_limit;
+#ifdef DEBUG
+ decomp_wt_ptr1 = f->wt.ptr;
+ decomp_wt_limit1 = f->wt.limit;
+ decomp_rd_ptr1 = f->rd.ptr;
+ decomp_rd_limit1 = f->rd.limit;
+#endif
+ status = (*f->decompress_state->template->process)
+ (f->decompress_state, &(f->rd), &(f->wt), true);
+ if (status == 0) {
+ eprintf("Decompression required more than one full block!\n");
+ return_error(gs_error_Fatal);
+ }
+ }
+ bp->raw_block = f->raw_head; /* point to raw block */
+ }
+ /* end if( raw_block == NULL ) meaning need to decompress data */
+ else {
+ /* data exists in the raw data cache, if not raw_head, move it */
+ if (bp->raw_block != f->raw_head) {
+ /* move to raw_head */
+ /* prev.fwd = this.fwd */
+ bp->raw_block->back->fwd = bp->raw_block->fwd;
+ if (bp->raw_block->fwd != NULL)
+ /* next.back = this.back */
+ bp->raw_block->fwd->back = bp->raw_block->back;
+ else
+ f->raw_tail = bp->raw_block->back; /* tail = prev */
+ f->raw_head->back = bp->raw_block; /* head.back = this */
+ bp->raw_block->fwd = f->raw_head; /* this.fwd = orig head */
+ f->raw_head = bp->raw_block; /* head = this */
+ f->raw_head->back = NULL; /* this.back = NULL */
+#ifdef DEBUG
+ tot_cache_hits++; /* counting here prevents repeats since */
+ /* won't count if already at head */
+#endif
+ }
+ }
+ f->pdata = bp->raw_block->data;
+ f->pdata_end = f->pdata + MEMFILE_DATA_SIZE;
+ /* NOTE: last block is never compressed, so a compressed block */
+ /* is always full size. */
+ } /* end else (when data was compressed) */
+
+ return (0);
+}
+
+/* ---------------- Reading ---------------- */
+
+int
+memfile_fread_chars(void *data, uint len, clist_file_ptr cf)
+{
+ char *str = (char *)data;
+ MEMFILE *f = (MEMFILE *) cf;
+ uint count = len, num_read, move_count;
+
+ num_read = f->log_length - f->log_curr_pos;
+ if (count > num_read)
+ count = num_read;
+ num_read = count;
+
+ while (count) {
+ f->log_curr_pos++; /* move into next byte */
+ if (f->pdata == f->pdata_end) {
+ f->log_curr_blk = (f->log_curr_blk)->link;
+ memfile_get_pdata(f);
+ }
+ move_count = f->pdata_end - f->pdata;
+ if (move_count > count)
+ move_count = count;
+ f->log_curr_pos += move_count - 1; /* new position */
+ memmove(str, f->pdata, move_count);
+ str += move_count;
+ f->pdata += move_count;
+ count -= move_count;
+ }
+
+ return (num_read);
+}
+
+/* ---------------- Position/status ---------------- */
+
+int
+memfile_ferror_code(clist_file_ptr cf)
+{
+ return (((MEMFILE *) cf)->error_code); /* errors stored here */
+}
+
+long
+memfile_ftell(clist_file_ptr cf)
+{
+ return (((MEMFILE *) cf)->log_curr_pos);
+}
+
+void
+memfile_rewind(clist_file_ptr cf, bool discard_data, const char *ignore_fname)
+{
+ MEMFILE *f = (MEMFILE *) cf;
+
+ if (discard_data) {
+ memfile_free_mem(f);
+ /* We have to call memfile_init_empty to preserve invariants. */
+ memfile_init_empty(f);
+ } else {
+ f->log_curr_blk = f->log_head;
+ f->log_curr_pos = 0;
+ memfile_get_pdata(f);
+ }
+}
+
+int
+memfile_fseek(clist_file_ptr cf, long offset, int mode, const char *ignore_fname)
+{
+ MEMFILE *f = (MEMFILE *) cf;
+ long i, block_num, new_pos;
+
+ switch (mode) {
+ case SEEK_SET: /* offset from the beginning of the file */
+ new_pos = offset;
+ break;
+
+ case SEEK_CUR: /* offset from the current position in the file */
+ new_pos = offset + f->log_curr_pos;
+ break;
+
+ case SEEK_END: /* offset back from the end of the file */
+ new_pos = f->log_length - offset;
+ break;
+
+ default:
+ return (-1);
+ }
+ if (new_pos < 0 || new_pos > f->log_length)
+ return -1;
+ if ((f->pdata == f->pdata_end) && (f->log_curr_blk->link != NULL)) {
+ /* log_curr_blk is actually one block behind log_curr_pos */
+ f->log_curr_blk = f->log_curr_blk->link;
+ }
+ block_num = new_pos / MEMFILE_DATA_SIZE;
+ i = f->log_curr_pos / MEMFILE_DATA_SIZE;
+ if (block_num < i) { /* if moving backwards, start at beginning */
+ f->log_curr_blk = f->log_head;
+ i = 0;
+ }
+ for (; i < block_num; i++) {
+ f->log_curr_blk = f->log_curr_blk->link;
+ }
+ f->log_curr_pos = new_pos;
+ memfile_get_pdata(f); /* pointers to start of block */
+ f->pdata += new_pos - (block_num * MEMFILE_DATA_SIZE);
+
+ return 0; /* return "normal" status */
+}
+
+/* ---------------- Internal routines ---------------- */
+
+private void
+memfile_free_mem(MEMFILE * f)
+{
+ LOG_MEMFILE_BLK *bp, *tmpbp;
+
+#ifdef DEBUG
+ /* output some diagnostics about the effectiveness */
+ if (tot_raw > 100) {
+ if_debug2(':', "[:]tot_raw=%ld, tot_compressed=%ld\n",
+ tot_raw, tot_compressed);
+ }
+ if (tot_cache_hits != 0) {
+ if_debug3(':', "[:]Cache hits=%ld, cache misses=%ld, swapouts=%ld\n",
+ tot_cache_hits,
+ tot_cache_miss - (f->log_length / MEMFILE_DATA_SIZE),
+ tot_swap_out);
+ }
+ tot_raw = 0;
+ tot_compressed = 0;
+ tot_cache_hits = 0;
+ tot_cache_miss = 0;
+ tot_swap_out = 0;
+#endif
+
+ /* Free up memory that was allocated for the memfile */
+ bp = f->log_head;
+
+/******************************************************************
+ * The following was the original algorithm here. This algorithm has a bug:
+ * the second loop references the physical blocks again after they have been
+ * freed.
+ ******************************************************************/
+
+#if 0 /**************** *************** */
+
+ if (bp != NULL) {
+ /* Free the physical blocks that make up the compressed data */
+ PHYS_MEMFILE_BLK *pphys = (f->log_head)->phys_blk;
+
+ if (pphys->data_limit != NULL) {
+ /* the data was compressed, free the chain of blocks */
+ while (pphys != NULL) {
+ PHYS_MEMFILE_BLK *tmpphys = pphys->link;
+
+ FREE(f, pphys, "memfile_free_mem(pphys)");
+ pphys = tmpphys;
+ }
+ }
+ }
+ /* free the logical blocks */
+ while (bp != NULL) {
+ /* if this logical block was not compressed, free the phys_blk */
+ if (bp->phys_blk->data_limit == NULL) {
+ FREE(f, bp->phys_blk, "memfile_free_mem(phys_blk)");
+ }
+ tmpbp = bp->link;
+ FREE(f, bp, "memfile_free_mem(log_blk)");
+ bp = tmpbp;
+ }
+
+#else /**************** *************** */
+# if 1 /**************** *************** */
+
+/****************************************************************
+ * This algorithm is correct (we think).
+ ****************************************************************/
+
+ if (bp != NULL) {
+ /* Null out phys_blk pointers to compressed data. */
+ PHYS_MEMFILE_BLK *pphys = bp->phys_blk;
+
+ {
+ for (tmpbp = bp; tmpbp != NULL; tmpbp = tmpbp->link)
+ if (tmpbp->phys_blk->data_limit != NULL)
+ tmpbp->phys_blk = 0;
+ }
+ /* Free the physical blocks that make up the compressed data */
+ if (pphys->data_limit != NULL) {
+ /* the data was compressed, free the chain of blocks */
+ while (pphys != NULL) {
+ PHYS_MEMFILE_BLK *tmpphys = pphys->link;
+
+ FREE(f, pphys, "memfile_free_mem(pphys)");
+ pphys = tmpphys;
+ }
+ }
+ }
+ /* Now free the logical blocks, and any uncompressed physical blocks. */
+ while (bp != NULL) {
+ if (bp->phys_blk != NULL) {
+ FREE(f, bp->phys_blk, "memfile_free_mem(phys_blk)");
+ }
+ tmpbp = bp->link;
+ FREE(f, bp, "memfile_free_mem(log_blk)");
+ bp = tmpbp;
+ }
+
+/***********************************************************************
+ * This algorithm appears to be both simpler and free of the bug that
+ * occasionally causes the older one to reference freed blocks; but in
+ * fact it can miss blocks, because the very last compressed logical block
+ * can have spill into a second physical block, which is not referenced by
+ * any logical block.
+ ***********************************************************************/
+
+# else /**************** *************** */
+
+ {
+ PHYS_MEMFILE_BLK *prev_phys = 0;
+
+ while (bp != NULL) {
+ PHYS_MEMFILE_BLK *phys = bp->phys_blk;
+
+ if (phys != prev_phys) {
+ FREE(f, phys, "memfile_free_mem(phys_blk)");
+ prev_phys = phys;
+ }
+ tmpbp = bp->link;
+ FREE(f, bp, "memfile_free_mem(log_blk)");
+ bp = tmpbp;
+ }
+ }
+
+# endif /**************** *************** */
+#endif /**************** *************** */
+
+ f->log_head = NULL;
+
+ /* Free any internal compressor state. */
+ if (f->compressor_initialized) {
+ if (f->decompress_state->template->release != 0)
+ (*f->decompress_state->template->release) (f->decompress_state);
+ if (f->compress_state->template->release != 0)
+ (*f->compress_state->template->release) (f->compress_state);
+ f->compressor_initialized = false;
+ }
+ /* free the raw buffers */
+ while (f->raw_head != NULL) {
+ RAW_BUFFER *tmpraw = f->raw_head->fwd;
+
+ FREE(f, f->raw_head, "memfile_free_mem(raw)");
+ f->raw_head = tmpraw;
+ }
+}
+
+private int
+memfile_init_empty(MEMFILE * f)
+{
+ PHYS_MEMFILE_BLK *pphys;
+ LOG_MEMFILE_BLK *plog;
+
+ /* Zero out key fields so that allocation failure will be unwindable */
+ f->phys_curr = NULL; /* flag as file not compressed */
+ f->log_head = NULL;
+ f->log_curr_blk = NULL;
+ f->log_curr_pos = 0;
+ f->log_length = 0;
+ f->raw_head = NULL;
+ f->compressor_initialized = false;
+ f->total_space = 0;
+
+ /* File empty - get a physical mem block (includes the buffer area) */
+ pphys = MALLOC(f, sizeof(*pphys), "memfile pphys");
+ if (!pphys) {
+ eprintf("memfile_init_empty: MALLOC for 'pphys' failed\n");
+ return_error(gs_error_VMerror);
+ }
+ f->total_space += sizeof(*pphys);
+ pphys->data_limit = NULL; /* raw data for now */
+
+ /* Get logical mem block to go with physical one */
+ plog = (LOG_MEMFILE_BLK *)MALLOC( f, sizeof(*plog), "memfile_init_empty" );
+ if (plog == NULL) {
+ FREE(f, pphys, "memfile_init_empty");
+ eprintf("memfile_init_empty: MALLOC for log_curr_blk failed\n");
+ return_error(gs_error_VMerror);
+ }
+ f->total_space += sizeof(*plog);
+ f->log_head = f->log_curr_blk = plog;
+ f->log_curr_blk->link = NULL;
+ f->log_curr_blk->phys_blk = pphys;
+ f->log_curr_blk->phys_pdata = NULL;
+ f->log_curr_blk->raw_block = NULL;
+
+ f->pdata = pphys->data;
+ f->pdata_end = f->pdata + MEMFILE_DATA_SIZE;
+
+ f->error_code = 0;
+
+ return 0;
+}
diff --git a/pstoraster/gxclmem.h b/pstoraster/gxclmem.h
new file mode 100644
index 000000000..88b125991
--- /dev/null
+++ b/pstoraster/gxclmem.h
@@ -0,0 +1,155 @@
+/* Copyright (C) 1995, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Definitions and declarations for clist implementation in memory. */
+
+#ifndef gxclmem_INCLUDED
+# define gxclmem_INCLUDED
+
+#include "gxclio.h" /* defines interface */
+#include "strimpl.h" /* stream structures */
+
+/*
+ * The best values of MEMFILE_DATA_SIZE are slightly less than a power of 2,
+ * to allow typical malloc implementations to allocate in units of a power
+ * of 2 rather than having to go slightly over.
+ */
+#define MEMFILE_DATA_SIZE (16384 - 160)
+
+ /* ============================================================ */
+ /* */
+ /* Memfile structure definitions. */
+ /* */
+ /* The PHYS structures are the elements actually allocated in */
+ /* RAM, containing the compressed data (or optionally raw data) */
+ /* */
+ /* There can be several LOG (logical) elements per physical */
+ /* element, depending on the compression. The MEMFILE pdata */
+ /* item always points into a raw block of data. */
+ /* */
+ /* ============================================================ */
+
+typedef struct RAW_BUFFER {
+ struct RAW_BUFFER *fwd, *back;
+ struct LOG_MEMFILE_BLK *log_blk;
+ char data[MEMFILE_DATA_SIZE];
+} RAW_BUFFER;
+
+typedef struct PHYS_MEMFILE_BLK {
+ struct PHYS_MEMFILE_BLK *link;
+ char *data_limit; /* end of data when compressed */
+ /* NULL if not compressed */
+ char data_spare[4]; /* used during de-compress */
+ char data[MEMFILE_DATA_SIZE];
+} PHYS_MEMFILE_BLK;
+
+typedef struct LOG_MEMFILE_BLK {
+ struct LOG_MEMFILE_BLK *link;
+ PHYS_MEMFILE_BLK *phys_blk;
+ char *phys_pdata;
+ RAW_BUFFER *raw_block; /* or NULL */
+} LOG_MEMFILE_BLK;
+
+typedef struct MEMFILE {
+ gs_memory_t *memory; /* storage allocator */
+ gs_memory_t *data_memory; /* storage allocator for data */
+ bool ok_to_compress; /* if true, OK to compress this file */
+ /*
+ * Reserve memory blocks: these are used to guarantee that a
+ * given-sized write (or sequence of writes) will always succeed.
+ * More specifically, the guarantee is that N bytes can successfully
+ * be written after a low-memory warning is first returned by
+ * fwrite. The reserve of N bytes for a given file is (re)allocated
+ * by a successful call to memfile_set_memory_warning(N). Fwrite
+ * allocates memory only from the reserve when its normal allocation
+ * attempts fail; in such cases, it allocates blocks from the
+ * reserve pool as needed and completes normally, but returns a
+ * low-memory warning status. Once a low-memory warning has been
+ * returned, fwrite will continue to attempt to allocate memory from
+ * the usual allocator on subsequent fwrites, but does *not* try to
+ * "top up" the reserve if becomes available -- only an explicit
+ * memfile_set_memory_warning does so.
+ */
+ PHYS_MEMFILE_BLK *reservePhysBlockChain; /* chain of reserve phys blks */
+ int reservePhysBlockCount; /* count of entries on reservePhysBlockChain */
+ LOG_MEMFILE_BLK *reserveLogBlockChain; /* chain of reserve log blks */
+ int reserveLogBlockCount; /* count of entries on reserveLogBlockChain */
+ /* logical file properties */
+ LOG_MEMFILE_BLK *log_head;
+ LOG_MEMFILE_BLK *log_curr_blk;
+ long log_length; /* updated during write */
+ long log_curr_pos; /* updated during seek, close, read */
+ char *pdata; /* raw data */
+ char *pdata_end;
+ /* physical file properties */
+ long total_space; /* so we know when to start compress */
+ PHYS_MEMFILE_BLK *phys_curr; /* NULL if not compressing */
+ RAW_BUFFER *raw_head, *raw_tail;
+ int error_code; /* used by CLIST_ferror */
+ stream_cursor_read rd; /* use .ptr, .limit */
+ stream_cursor_write wt; /* use .ptr, .limit */
+ bool compressor_initialized;
+ stream_state *compress_state;
+ stream_state *decompress_state;
+} MEMFILE;
+
+/*
+ * Only the MEMFILE and stream_state structures are GC-compatible, so we
+ * allocate all the other structures on the C heap.
+ */
+
+#define private_st_MEMFILE() /* in gxclmem.c */\
+ gs_private_st_ptrs2(st_MEMFILE, MEMFILE, "MEMFILE",\
+ MEMFILE_enum_ptrs, MEMFILE_reloc_ptrs, compress_state, decompress_state)
+
+/* Make the memfile_... operations aliases for the clist_... operations. */
+
+#define memfile_fopen(fname, fmode, pcf, mem, data_mem, compress)\
+ clist_fopen(fname, fmode, pcf, mem, data_mem, compress)
+#define memfile_fclose(cf, fname, delete)\
+ clist_fclose(cf, fname, delete)
+#define memfile_unlink(fname)\
+ clist_unlink(fname)
+
+#define memfile_space_available(req)\
+ clist_space_available(req)
+#define memfile_fwrite_chars(data, len, cf)\
+ clist_fwrite_chars(data, len, cf)
+
+#define memfile_fread_chars(data, len, cf)\
+ clist_fread_chars(data, len, cf)
+
+#define memfile_set_memory_warning(cf, nbytes) clist_set_memory_warning(cf, nbytes)
+#define memfile_ferror_code(cf) clist_ferror_code(cf)
+#define memfile_ftell(cf) clist_ftell(cf)
+#define memfile_rewind(cf, discard, fname) clist_rewind(cf, discard, fname)
+#define memfile_fseek(cf, offset, mode, fname) clist_fseek(cf, offset, mode, fname)
+
+/* Declare the procedures for returning the prototype filter states */
+/* for compressing and decompressing the band list. */
+const stream_state *clist_compressor_state(P1(void *));
+const stream_state *clist_decompressor_state(P1(void *));
+
+#endif /* gxclmem_INCLUDED */
diff --git a/pstoraster/gxclpage.c b/pstoraster/gxclpage.c
new file mode 100644
index 000000000..bcf242c58
--- /dev/null
+++ b/pstoraster/gxclpage.c
@@ -0,0 +1,127 @@
+/* Copyright (C) 1997 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Page object management */
+#include "gdevprn.h"
+#include "gxcldev.h"
+#include "gxclpage.h"
+
+/* Save a page. */
+int
+gdev_prn_save_page(gx_device_printer * pdev, gx_saved_page * page,
+ int num_copies)
+{
+ /* Make sure we are banding. */
+ if (!pdev->buffer_space)
+ return_error(gs_error_rangecheck);
+ if (strlen(pdev->dname) >= sizeof(page->dname))
+ return_error(gs_error_limitcheck);
+ {
+ gx_device_clist_writer * const pcldev =
+ (gx_device_clist_writer *)pdev;
+ int code;
+
+ if ((code = clist_end_page(pcldev)) < 0 ||
+ (code = clist_fclose(pcldev->page_cfile, pcldev->page_cfname, false)) < 0 ||
+ (code = clist_fclose(pcldev->page_bfile, pcldev->page_bfname, false)) < 0
+ )
+ return code;
+ /* Save the device information. */
+ memcpy(&page->device, pdev, sizeof(gx_device));
+ strcpy(page->dname, pdev->dname);
+ /* Save the page information. */
+ page->info = pcldev->page_info;
+ page->info.cfile = 0;
+ page->info.bfile = 0;
+ }
+ /* Save other information. */
+ page->num_copies = num_copies;
+ return (*gs_clist_device_procs.open_device) ((gx_device *) pdev);
+}
+
+/* Render an array of saved pages. */
+int
+gdev_prn_render_pages(gx_device_printer * pdev,
+ const gx_placed_page * ppages, int count)
+{
+ gx_device_clist_reader * const pcldev =
+ (gx_device_clist_reader *)pdev;
+
+ /* Check to make sure the pages are compatible with the device. */
+ {
+ int i;
+ gx_band_params params;
+
+ for (i = 0; i < count; ++i) {
+ const gx_saved_page *page = ppages[i].page;
+
+ /* We would like to fully check the color representation, */
+ /* but we don't have enough information to do that. */
+ if (strcmp(page->dname, pdev->dname) != 0 ||
+ memcmp(&page->device.color_info, &pdev->color_info,
+ sizeof(pdev->color_info)) != 0
+ )
+ return_error(gs_error_rangecheck);
+ /* Currently we don't allow translation in Y. */
+ if (ppages[i].offset.y != 0)
+ return_error(gs_error_rangecheck);
+ /* Make sure the band parameters are compatible. */
+ if (page->info.band_params.BandBufferSpace !=
+ pdev->buffer_space ||
+ page->info.band_params.BandWidth !=
+ pdev->width
+ )
+ return_error(gs_error_rangecheck);
+ /* Currently we require all band heights to be the same. */
+ if (i == 0)
+ params = page->info.band_params;
+ else if (page->info.band_params.BandHeight !=
+ params.BandHeight
+ )
+ return_error(gs_error_rangecheck);
+ }
+ }
+ /* Set up the page list in the device. */
+ /****** SHOULD FACTOR THIS OUT OF clist_render_init ******/
+ pcldev->ymin = pcldev->ymax = 0;
+ pcldev->pages = ppages;
+ pcldev->num_pages = count;
+ /* Render the pages. */
+ {
+ int code = (*dev_proc(pdev, output_page))
+ ((gx_device *) pdev, ppages[0].page->num_copies, true);
+
+ /* Delete the temporary files. */
+ int i;
+
+ for (i = 0; i < count; ++i) {
+ const gx_saved_page *page = ppages[i].page;
+
+ clist_unlink(page->info.cfname);
+ clist_unlink(page->info.bfname);
+ }
+ return code;
+ }
+}
diff --git a/pstoraster/gxclpage.h b/pstoraster/gxclpage.h
new file mode 100644
index 000000000..f54aaf734
--- /dev/null
+++ b/pstoraster/gxclpage.h
@@ -0,0 +1,63 @@
+/* Copyright (C) 1997 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Requires gdevprn.h, gxclist.h */
+
+#ifndef gxclpage_INCLUDED
+# define gxclpage_INCLUDED
+
+#include "gxclio.h"
+
+/* ---------------- Procedures ---------------- */
+
+/*
+ * Package up the current page in a banding device as a page object.
+ * The client must provide storage for the page object.
+ * The client may retain the object in memory, or may write it on a file
+ * for later retrieval; in the latter case, the client should free the
+ * in-memory structure.
+ */
+int gdev_prn_save_page(P3(gx_device_printer * pdev, gx_saved_page * page,
+ int num_copies));
+
+/*
+ * Render an array of saved pages by setting up a modified get_bits
+ * procedure and then calling the device's normal output_page procedure.
+ * Any current page in the device's buffers is lost.
+ * The (0,0) point of each saved page is translated to the corresponding
+ * specified offset on the combined page. (Currently the Y offset must be 0.)
+ * The client is responsible for freeing the saved and placed pages.
+ *
+ * Note that the device instance for rendering need not be, and normally is
+ * not, the same as the device from which the pages were saved, but it must
+ * be an instance of the same device. The client is responsible for
+ * ensuring that the rendering device's buffer size (BufferSpace value) is
+ * the same as the BandBufferSpace value of all the saved pages, and that
+ * the device width is the same as the BandWidth value of the saved pages.
+ */
+int gdev_prn_render_pages(P3(gx_device_printer * pdev,
+ const gx_placed_page * ppages, int count));
+
+#endif /* gxclpage_INCLUDED */
diff --git a/pstoraster/gxclpath.c b/pstoraster/gxclpath.c
new file mode 100644
index 000000000..55e3c81c0
--- /dev/null
+++ b/pstoraster/gxclpath.c
@@ -0,0 +1,1198 @@
+/* Copyright (C) 1995, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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 "gxcolor2.h"
+#include "gxpaint.h" /* for gx_fill/stroke_params */
+#include "gzpath.h"
+#include "gzcpath.h"
+
+/* Statistics */
+#ifdef DEBUG
+ulong stats_cmd_diffs[5];
+#endif
+
+/* Forward declarations */
+private int cmd_put_path(P8(gx_device_clist_writer * cldev,
+ gx_clist_state * pcls, const gx_path * ppath, fixed ymin, fixed ymax, byte op,
+ bool implicit_close, segment_notes keep_notes));
+
+/* ------ 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);
+
+ if (color1 != pcls->colors[1]) {
+ int code = cmd_set_color1(cldev, pcls, color1);
+
+ if (code < 0)
+ return code;
+ }
+ return cmd_dc_type_pure;
+ }
+ /* Any non-pure color will require the phase. */
+ {
+ 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;
+ }
+ }
+ 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);
+ /* 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_error(-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;
+ }
+ return cmd_dc_type_ht;
+ } else if (gx_dc_is_colored_halftone(pdcolor)) {
+ const gx_device_halftone *pdht = pdcolor->colors.colored.c_ht;
+ int num_comp = pdht->num_comp;
+ byte buf[4 + 4 * cmd_max_intsize(sizeof(pdcolor->colors.colored.c_level[0]))];
+ byte *bp = buf;
+ int i;
+ uint short_bases = 0;
+ ulong bases = 0;
+ byte *dp;
+ int code;
+
+ /****** HOW TO TELL IF COLOR IS ALREADY SET? ******/
+ if (pdht->id != cldev->device_halftone_id) {
+ int code = cmd_put_halftone(cldev, pdht, pdht->type);
+
+ if (code < 0)
+ return code;
+ cldev->device_halftone_id = pdht->id;
+ }
+ for (i = 0; i < num_comp; ++i) {
+ uint base = pdcolor->colors.colored.c_base[i];
+
+ if (base > 31)
+ return_error(gs_error_rangecheck);
+ bases |= base << ((3 - i) * 5);
+ short_bases |= base << (3 - i);
+ }
+ if (bases & 0xf7bde) { /* Some base value requires more than 1 bit. */
+ *bp++ = 0x10 + (byte) (bases >> 16);
+ *bp++ = (byte) (bases >> 8);
+ *bp++ = (byte) bases;
+ } else { /* The bases all fit in 1 bit each. */
+ *bp++ = 0x00 + (byte) short_bases;
+ }
+ for (i = 0; i < num_comp; ++i)
+ bp = cmd_put_w((uint) pdcolor->colors.colored.c_level[i], bp);
+ /****** IGNORE alpha ******/
+ code = set_cmd_put_op(dp, cldev, pcls, cmd_opv_set_color, bp - buf + 1);
+ if (code < 0)
+ return code;
+ memcpy(dp + 1, buf, bp - buf);
+ return cmd_dc_type_color;
+ } else
+ return_error(-1);
+}
+
+/* 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;
+ byte *dp;
+ int code;
+
+ if (unknown & flatness_known) {
+ code = set_cmd_put_op(dp, cldev, pcls, cmd_opv_set_flatness,
+ 1 + sizeof(float));
+ if (code < 0)
+ return code;
+ memcpy(dp + 1, &cldev->imager_state.flatness, sizeof(float));
+ pcls->known |= flatness_known;
+ }
+ if (unknown & fill_adjust_known) {
+ code = set_cmd_put_op(dp, cldev, pcls, cmd_opv_set_fill_adjust,
+ 1 + sizeof(fixed) * 2);
+ if (code < 0)
+ return code;
+ 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;
+
+ code = set_cmd_put_op(dp, cldev, pcls, cmd_opv_set_ctm, len + 1);
+ if (code < 0)
+ return code;
+ memcpy(dp + 1, cbuf, len);
+ pcls->known |= ctm_known;
+ }
+ if (unknown & line_width_known) {
+ float width =
+ gx_current_line_width(&cldev->imager_state.line_params);
+
+ code = set_cmd_put_op(dp, cldev, pcls, cmd_opv_set_line_width,
+ 1 + sizeof(width));
+ if (code < 0)
+ return code;
+ memcpy(dp + 1, &width, sizeof(width));
+ pcls->known |= line_width_known;
+ }
+ if (unknown & miter_limit_known) {
+ code = set_cmd_put_op(dp, cldev, pcls, cmd_opv_set_miter_limit,
+ 1 + sizeof(float));
+ if (code < 0)
+ return code;
+ memcpy(dp + 1, &cldev->imager_state.line_params.miter_limit,
+ sizeof(float));
+ pcls->known |= miter_limit_known;
+ }
+ if (unknown & misc0_known) {
+ code = set_cmd_put_op(dp, cldev, pcls, cmd_opv_set_misc2, 2);
+ if (code < 0)
+ return code;
+ dp[1] = cmd_set_misc2_cap_join +
+ (cldev->imager_state.line_params.cap << 3) +
+ cldev->imager_state.line_params.join;
+ pcls->known |= misc0_known;
+ }
+ if (unknown & misc1_known) {
+ code = set_cmd_put_op(dp, cldev, pcls, cmd_opv_set_misc2, 2);
+ if (code < 0)
+ return code;
+ dp[1] = cmd_set_misc2_ac_op_sa +
+ (cldev->imager_state.accurate_curves ? 4 : 0) +
+ (cldev->imager_state.overprint ? 2 : 0) +
+ (cldev->imager_state.stroke_adjust ? 1 : 0);
+ pcls->known |= misc1_known;
+ }
+ if (unknown & dash_known) {
+ int n = cldev->imager_state.line_params.dash.pattern_size;
+
+ code = set_cmd_put_op(dp, cldev, pcls, cmd_opv_set_dash,
+ 2 + (n + 2) * sizeof(float));
+ if (code < 0)
+ return code;
+ dp[1] = n + (cldev->imager_state.line_params.dash.adapt ? 0x80 : 0) +
+ (cldev->imager_state.line_params.dot_length_absolute ? 0x40 : 0);
+ memcpy(dp + 2, &cldev->imager_state.line_params.dot_length,
+ sizeof(float));
+ memcpy(dp + 2 + sizeof(float),
+ &cldev->imager_state.line_params.dash.offset,
+ sizeof(float));
+ if (n != 0)
+ memcpy(dp + 2 + sizeof(float) * 2,
+ cldev->imager_state.line_params.dash.pattern,
+ n * sizeof(float));
+ pcls->known |= dash_known;
+ }
+ if (unknown & alpha_known) {
+ code = set_cmd_put_op(dp, cldev, pcls, cmd_opv_set_misc2,
+ 2 + sizeof(cldev->imager_state.alpha));
+ if (code < 0)
+ return code;
+ dp[1] = cmd_set_misc2_alpha;
+ memcpy(dp + 2, &cldev->imager_state.alpha,
+ sizeof(cldev->imager_state.alpha));
+ pcls->known |= alpha_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 band_height = cldev->page_band_height;
+ int ymin = (pcls - cldev->states) * band_height;
+ int ymax = min(ymin + band_height, cldev->height);
+ gs_fixed_rect box;
+ bool punt_to_outer_box = false;
+ int code;
+
+ code = set_cmd_put_op(dp, cldev, pcls, cmd_opv_begin_clip, 1);
+ if (code < 0)
+ return code;
+ if (pcpath->path_valid) {
+ if (gx_path_is_rectangle(&pcpath->path, &box) &&
+ fixed_is_int(box.p.x | box.p.y | box.q.x | box.q.y)
+ ) {
+ /* Write the path as a rectangle. */
+ code = cmd_write_rect_cmd(cldev, pcls, cmd_op_fill_rect,
+ fixed2int_var(box.p.x),
+ fixed2int_var(box.p.y),
+ fixed2int(box.q.x - box.p.x),
+ fixed2int(box.q.y - box.p.y));
+ } else if ( !(cldev->disable_mask & clist_disable_complex_clip) ) {
+ /* Write the path. */
+ code = cmd_put_path(cldev, pcls, &pcpath->path,
+ int2fixed(ymin - 1),
+ int2fixed(ymax + 1),
+ (pcpath->rule == gx_rule_even_odd ?
+ cmd_opv_eofill : cmd_opv_fill),
+ true, sn_not_first);
+ } else {
+ /* Complex paths disabled: write outer box as clip */
+ punt_to_outer_box = true;
+ }
+ } else { /* Write out the rectangles. */
+ const gx_clip_list *list = gx_cpath_list(pcpath);
+ const gx_clip_rect *prect = list->head;
+
+ if (prect == 0)
+ prect = &list->single;
+ else if (cldev->disable_mask & clist_disable_complex_clip)
+ punt_to_outer_box = true;
+ if (!punt_to_outer_box) {
+ for (; prect != 0 && code >= 0; prect = prect->next)
+ if (prect->xmax > prect->xmin &&
+ prect->ymin < ymax && prect->ymax > ymin
+ ) {
+ code =
+ cmd_write_rect_cmd(cldev, pcls, cmd_op_fill_rect,
+ prect->xmin, prect->ymin,
+ prect->xmax - prect->xmin,
+ prect->ymax - prect->ymin);
+ }
+ }
+ }
+ if (punt_to_outer_box) {
+ /* Clip is complex, but disabled. Write out the outer box */
+ gs_fixed_rect box;
+
+ gx_cpath_outer_box(pcpath, &box);
+ box.p.x = fixed_floor(box.p.x);
+ box.p.y = fixed_floor(box.p.y);
+ code = cmd_write_rect_cmd(cldev, pcls, cmd_op_fill_rect,
+ fixed2int_var(box.p.x),
+ fixed2int_var(box.p.y),
+ fixed2int_ceiling(box.q.x - box.p.x),
+ fixed2int_ceiling(box.q.y - box.p.y));
+ }
+ {
+ int end_code =
+ set_cmd_put_op(dp, cldev, pcls, cmd_opv_end_clip, 2);
+
+ if (code >= 0)
+ code = end_code; /* take the first failure seen */
+ if (end_code < 0 && cldev->error_is_retryable) {
+ /*
+ * end_clip has to work despite lo-mem to maintain consistency.
+ * This isn't error recovery, but just to prevent dangling
+ * cmd_opv_begin_clip's.
+ */
+ ++cldev->ignore_lo_mem_warnings;
+ end_code =
+ set_cmd_put_op(dp, cldev, pcls, cmd_opv_end_clip, 2);
+ --cldev->ignore_lo_mem_warnings;
+ }
+ if (end_code >= 0)
+ dp[1] = (gx_cpath_is_outside(pcpath) ? 1 : 0);
+ }
+ if (code < 0)
+ return code;
+ pcls->clip_enabled = 1;
+ pcls->known |= clip_path_known;
+ }
+ if (unknown & color_space_known) {
+ byte *dp;
+
+ if (cldev->color_space & 8) { /* indexed */
+ uint num_values = (cldev->indexed_params.hival + 1) *
+ gs_color_space_num_components(
+ (const gs_color_space *)&cldev->indexed_params.base_space);
+ bool use_proc = cldev->color_space & 4;
+ const void *map_data;
+ uint map_size;
+
+ if (use_proc) {
+ map_data = cldev->indexed_params.lookup.map->values;
+ map_size = num_values *
+ sizeof(cldev->indexed_params.lookup.map->values[0]);
+ } else {
+ map_data = cldev->indexed_params.lookup.table.data;
+ map_size = num_values;
+ }
+ code = set_cmd_put_op(dp, cldev, pcls, cmd_opv_set_color_space,
+ 2 + cmd_sizew(cldev->indexed_params.hival) + map_size);
+ if (code < 0)
+ return code;
+ memcpy(cmd_put_w(cldev->indexed_params.hival, dp + 2),
+ map_data, map_size);
+ } else {
+ code = set_cmd_put_op(dp, cldev, pcls, cmd_opv_set_color_space, 2);
+ if (code < 0)
+ return code;
+ }
+ dp[1] = cldev->color_space;
+ pcls->known |= color_space_known;
+ }
+ return 0;
+}
+
+/* ------ Driver procedures ------ */
+
+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)
+{
+ gx_device_clist_writer * const cdev =
+ &((gx_device_clist *)dev)->writer;
+ uint unknown = 0;
+ int y, height, y0, y1;
+ gs_logical_operation_t lop = pis->log_op;
+ byte op = (byte)
+ (params->rule == gx_rule_even_odd ?
+ cmd_opv_eofill : cmd_opv_fill);
+ gs_fixed_point adjust;
+
+ if ( (cdev->disable_mask & clist_disable_fill_path) ||
+ gs_debug_c(',')
+ ) {
+ /* Disable path-based banding. */
+ return gx_default_fill_path(dev, pis, ppath, params, pdcolor,
+ pcpath);
+ }
+ adjust = params->adjust;
+ {
+ gs_fixed_rect bbox;
+
+ gx_path_bbox(ppath, &bbox);
+ y = fixed2int(bbox.p.y) - 1;
+ height = fixed2int_ceiling(bbox.q.y) - y + 1;
+ fit_fill_y(dev, y, height);
+ fit_fill_h(dev, y, height);
+ if (height <= 0)
+ return 0;
+ }
+ y0 = y;
+ y1 = y + height;
+ 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 (cdev->imager_state.alpha != pis->alpha) {
+ unknown |= alpha_known;
+ state_update(alpha);
+ }
+ if (cmd_check_clip_path(cdev, pcpath))
+ unknown |= clip_path_known;
+ if (unknown)
+ cmd_clear_known(cdev, unknown);
+ FOR_RECTS {
+ int code =
+ cmd_do_write_unknown(cdev, pcls,
+ flatness_known | fill_adjust_known |
+ alpha_known | clip_path_known);
+
+ if (code < 0)
+ return code;
+ if ((code = cmd_do_enable_clip(cdev, pcls, pcpath != NULL)) < 0 ||
+ (code = cmd_update_lop(cdev, pcls, lop)) < 0
+ )
+ return code;
+ 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(max(y - 1, y0)),
+ int2fixed(min(y + height + 1, y1)),
+ op + code, /* cmd_dc_type */
+ true, sn_none /* fill doesn't need the notes */ );
+ if (code < 0)
+ return code;
+ } END_RECTS;
+ return 0;
+}
+
+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)
+{
+ gx_device_clist_writer * const cdev =
+ &((gx_device_clist *)dev)->writer;
+ int pattern_size = pis->line_params.dash.pattern_size;
+ uint unknown = 0;
+ gs_fixed_rect bbox;
+ gs_fixed_point expansion;
+ int adjust_y;
+ int y, height, y0, y1;
+ gs_logical_operation_t lop = pis->log_op;
+
+ if ((cdev->disable_mask & clist_disable_stroke_path) ||
+ gs_debug_c(',')
+ ) {
+ /* Disable path-based banding. */
+ return gx_default_stroke_path(dev, pis, ppath, params, pdcolor,
+ pcpath);
+ }
+ gx_path_bbox(ppath, &bbox);
+ /* We must use the supplied imager state, not our saved one, */
+ /* for computing the stroke expansion. */
+ if (gx_stroke_path_expansion(pis, ppath, &expansion) < 0) {
+ /* Expansion is too large: use the entire page. */
+ adjust_y = 0;
+ y = 0;
+ height = dev->height;
+ } else {
+ adjust_y = fixed2int_ceiling(expansion.y) + 1;
+ y = fixed2int(bbox.p.y) - adjust_y;
+ height = fixed2int_ceiling(bbox.q.y) - y + adjust_y;
+ fit_fill_y(dev, y, height);
+ fit_fill_h(dev, y, height);
+ if (height <= 0)
+ return 0;
+ }
+ y0 = y;
+ y1 = y + height;
+ /* 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))) ||
+ cdev->imager_state.line_params.dash.offset !=
+ pis->line_params.dash.offset ||
+ cdev->imager_state.line_params.dash.adapt !=
+ pis->line_params.dash.adapt ||
+ cdev->imager_state.line_params.dot_length !=
+ pis->line_params.dot_length ||
+ cdev->imager_state.line_params.dot_length_absolute !=
+ pis->line_params.dot_length_absolute
+ ) { /* 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);
+ gx_set_dash_adapt(&cdev->imager_state.line_params.dash,
+ pis->line_params.dash.adapt);
+ gx_set_dot_length(&cdev->imager_state.line_params,
+ pis->line_params.dot_length,
+ pis->line_params.dot_length_absolute);
+ }
+ 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)) {
+ unknown |= misc0_known;
+ state_update(line_params.cap);
+ state_update(line_params.join);
+ }
+ if (state_neq(accurate_curves) || state_neq(overprint) ||
+ state_neq(stroke_adjust)
+ ) {
+ unknown |= misc1_known;
+ state_update(accurate_curves);
+ state_update(overprint);
+ state_update(stroke_adjust);
+ }
+ if (cdev->imager_state.alpha != pis->alpha) {
+ unknown |= alpha_known;
+ state_update(alpha);
+ }
+ if (cmd_check_clip_path(cdev, pcpath))
+ unknown |= clip_path_known;
+ if (unknown)
+ cmd_clear_known(cdev, unknown);
+ FOR_RECTS {
+ int code;
+
+ if ((code = cmd_do_write_unknown(cdev, pcls, stroke_all_known)) < 0 ||
+ (code = cmd_do_enable_clip(cdev, pcls, pcpath != NULL)) < 0 ||
+ (code = cmd_update_lop(cdev, pcls, lop)) < 0
+ )
+ return code;
+ 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(max(y - adjust_y, y0)),
+ ymax = int2fixed(min(y + height + adjust_y, y1));
+ else
+ ymin = min_fixed,
+ ymax = max_fixed;
+ code = cmd_put_path(cdev, pcls, ppath, ymin, ymax,
+ cmd_opv_stroke + code, /* cmd_dc_type */
+ false, (segment_notes) ~ 0);
+ if (code < 0)
+ return code;
+ }
+ } END_RECTS;
+ 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 */
+ segment_notes notes;
+ 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
+cmd_put_segment(cmd_segment_writer * psw, byte op,
+ const fixed * operands, segment_notes notes)
+{
+ const fixed *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;
+
+ dlprintf2("[L] %s:%d:", cmd_sub_op_names[op >> 4][op & 0xf],
+ (int)notes);
+ 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:
+ if (notes != psw->notes)
+ break;
+ op = cmd_opv_rm2lineto;
+ goto merge;
+ case cmd_opv_rm2lineto:
+ if (notes != psw->notes)
+ break;
+ 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(stats_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(stats_cmd_diffs[0]);
+ q[1] = (byte) ((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(stats_cmd_diffs[1]);
+ q[1] = (byte) (((d >> 16) & 0x3f) + 0x40);
+ q += 3;
+ } else if (is_bits(d, 30)) {
+ cmd_count_add1(stats_cmd_diffs[2]);
+ q[1] = (byte) (((d >> 24) & 0x3f) + 0x80);
+ q[2] = (byte) (d >> 16);
+ q += 4;
+ } else {
+ int b;
+
+ cmd_count_add1(stats_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;
+ }
+ if (notes != psw->notes) {
+ byte *dp;
+ int code =
+ set_cmd_put_op(dp, psw->cldev, psw->pcls, cmd_opv_set_misc2, 2);
+
+ if (code < 0)
+ return code;
+ dp[1] = cmd_set_misc2_notes + notes;
+ psw->notes = notes;
+ } {
+ int len = q + 2 - psw->cmd;
+ byte *dp;
+ int code = set_cmd_put_op(dp, psw->cldev, psw->pcls, op, len);
+
+ if (code < 0)
+ return code;
+ 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, sn_none)
+#define cmd_put_rlineto(psw, operands, notes)\
+ cmd_put_segment(psw, cmd_opv_rlineto, operands, notes)
+
+/*
+ * 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, segment_notes keep_notes)
+{
+ gs_path_enum cenum;
+ cmd_segment_writer writer;
+
+ /*
+ * initial_op is logically const, so we declare it as such,
+ * since some systems really dislike non-const statics.
+ * This entails an otherwise pointless cast in set_first_point().
+ */
+ static const 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;
+
+ /* If the last out-going segment was a lineto, */
+ /* its notes: */
+ segment_notes out_notes;
+
+ /*
+ * 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.notes = sn_none;
+#define set_first_point() (writer.dp = (byte *)&initial_op)
+#define first_point() (writer.dp == &initial_op)
+ set_first_point();
+ 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);
+ return set_cmd_put_op(dp, cldev, pcls, path_op, 1);
+ 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);
+ segment_notes notes =
+ gx_path_enum_notes(&cenum) & keep_notes;
+
+ 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;
+ out_notes = notes;
+ 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, out_notes);
+ 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, notes);
+ }
+ 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, out_notes);
+ 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, sn_none);
+ 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;
+ /*
+ * Force writing an explicit moveto if the next subpath
+ * starts with a moveto to the same point where this one
+ * ends.
+ */
+ set_first_point();
+ continue;
+ }
+ open = 0;
+ px = first.x, py = first.y;
+ code = cmd_put_segment(&writer, cmd_opv_closepath, &A, sn_none);
+ if_debug0('p', "[p]close\n");
+ break;
+ case gs_pe_curveto:
+ {
+ segment_notes notes =
+ gx_path_enum_notes(&cenum) & keep_notes;
+
+ {
+ 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;
+ out_notes = notes;
+ 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, out_notes);
+ 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 *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;
+ if ((B ^ D) >= 0) {
+ if (C == D && E == B)
+ op = cmd_opv_hqcurveto;
+ } else if (C == -D && E == -B)
+ C = D, op = cmd_opv_hqcurveto;
+ } else if (A == 0 && F == 0) {
+ optr++, op = cmd_opv_vhcurveto;
+ if ((B ^ C) >= 0) {
+ if (D == C && E == B)
+ op = cmd_opv_vqcurveto;
+ } else if (D == -C && E == -B)
+ op = cmd_opv_vqcurveto;
+ } 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, notes);
+ }
+ }
+ 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..27412f281
--- /dev/null
+++ b/pstoraster/gxclpath.h
@@ -0,0 +1,207 @@
+/* Copyright (C) 1995, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Extends (requires) gxcldev.h */
+
+#ifndef gxclpath_INCLUDED
+# define gxclpath_INCLUDED
+
+#include "gxfixed.h" /* for gzpath.h */
+
+/* Define the flags indicating whether a band knows the current values of */
+/* various miscellaneous parameters (pcls->known). */
+#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 misc0_known (1<<5)
+#define misc1_known (1<<6)
+#define dash_known (1<<7)
+#define alpha_known (1<<8)
+#define clip_path_known (1<<9)
+#define stroke_all_known ((1<<10)-1)
+#define color_space_known (1<<10)
+/*#define all_known ((1<<11)-1) */
+
+/* Define the drawing color types for distinguishing different */
+/* fill/stroke command variations. */
+typedef enum {
+ cmd_dc_type_pure = 0,
+ cmd_dc_type_ht = 1,
+ cmd_dc_type_color = 2
+} cmd_dc_type;
+
+/* 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,yx+xy)(0=0, 1=V)x(tx,ty), */
+ /* 0..5 x coeff(float) */
+ cmd_opv_set_line_width = 0xd3, /* width(float) */
+ cmd_opv_set_misc2 = 0xd4,
+#define cmd_set_misc2_cap_join (0 << 6) /* 00: cap(3)join(3) */
+#define cmd_set_misc2_ac_op_sa (1 << 6) /* 01: 0(3)acc.curves(1)overprint(1) */
+ /* stroke_adj(1) */
+#define cmd_set_misc2_notes (2 << 6) /* 10: seg.notes(6) */
+#define cmd_set_misc2_alpha (3 << 6) /* 11: -unused-, alpha */
+ cmd_opv_set_miter_limit = 0xd5, /* miter limit(float) */
+ cmd_opv_set_dash = 0xd6, /* adapt(1)abs.dot(1)n(6), dot */
+ /* length(float), 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?(2)0(3) */
+ /* [, hival#, table|map] */
+ cmd_opv_begin_image = 0xdc, /* BPCi(3)(0=mask) */
+ /* more params(1) */
+ /* Matrix?(1)Decode?(1) */
+ /* adjust/CombineWithColor(1) */
+ /* rect?(1), */
+ /* [format(2)Interpolate(1)Alpha(2) */
+ /* 0(3),] 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)], */
+ /* [, x0#, w-x1#, y0#, h-y1#] */
+ cmd_opv_image_data = 0xdd, /* height# (premature EOD if 0), */
+ /* raster#, <data> */
+ cmd_opv_set_color = 0xde, /* (0000abcd | */
+ /* 0001aaaa abbbbbcc cccddddd), */
+ /* (3|4) x level#: colored halftone */
+ /* with base colors a,b,c,d */
+ cmd_opv_put_params = 0xdf, /* (nothing) */
+ 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_vqcurveto = 0xec, /* dy1%, dx2%[,dy2=dx2 with sign */
+ /* of dy1, dx3=dy1 with sign of dx2] */
+ cmd_opv_hqcurveto = 0xed, /* dx1%, [dx2=dy2 with sign */
+ /* of dx1,]%dy2, [dy3=dx1 with sign */
+ /* of dy2] */
+ cmd_opv_closepath = 0xee, /* (nothing) */
+ cmd_op_path = 0xf0, /* (see below) */
+ /* The path drawing commands come in groups: */
+ /* each group consists of a base command plus an offset */
+ /* which is a cmd_dc_type. */
+ cmd_opv_fill = 0xf0,
+ cmd_opv_htfill = 0xf1,
+ cmd_opv_colorfill = 0xf2,
+ cmd_opv_eofill = 0xf3,
+ cmd_opv_hteofill = 0xf4,
+ cmd_opv_coloreofill = 0xf5,
+ cmd_opv_stroke = 0xf6,
+ cmd_opv_htstroke = 0xf7,
+ cmd_opv_colorstroke = 0xf8
+} gx_cmd_xop;
+
+static const byte clist_segment_op_num_operands[] =
+{2, 2, 1, 1, 6, 4, 4, 4, 4, 4, 6, 6, 2, 2, 0
+};
+
+#define cmd_misc2_op_name_strings\
+ "set_flatness", "set_fill_adjust", "set_ctm", "set_line_width",\
+ "set_misc2", "set_miter_limit", "set_dash", "enable_clip",\
+ "disable_clip", "begin_clip", "end_clip", "set_color_space",\
+ "begin_image", "image_data", "set_color", "put_params"
+
+#define cmd_segment_op_name_strings\
+ "rmoveto", "rlineto", "hlineto", "vlineto",\
+ "rrcurveto", "hvcurveto", "vhcurveto", "nrcurveto",\
+ "rncurveto", "rmlineto", "rm2lineto", "rm3lineto",\
+ "vqcurveto", "hqcurveto", "closepath", "?ef?"
+
+#define cmd_path_op_name_strings\
+ "fill", "htfill", "colorfill", "eofill",\
+ "hteofill", "coloreofill", "stroke", "htstroke",\
+ "colorstroke", "?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. */
+/* Return a cmd_dc_type. */
+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));
+
+/* Write out values of any unknown parameters. */
+#define cmd_do_write_unknown(cldev, pcls, must_know)\
+ ( ~(pcls)->known & (must_know) ?\
+ cmd_write_unknown(cldev, pcls, must_know) : 0 )
+int cmd_write_unknown(P3(gx_device_clist_writer * cldev, gx_clist_state * pcls,
+ uint must_know));
+
+/* 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));
+
+#endif /* gxclpath_INCLUDED */
diff --git a/pstoraster/gxclrast.c b/pstoraster/gxclrast.c
new file mode 100644
index 000000000..e208a48a2
--- /dev/null
+++ b/pstoraster/gxclrast.c
@@ -0,0 +1,2331 @@
+/* Copyright (C) 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Command list interpreter/rasterizer */
+#include "memory_.h"
+#include "gx.h"
+#include "gp.h" /* for gp_fmode_rb */
+#include "gpcheck.h"
+#include "gserrors.h"
+#include "gsbitops.h"
+#include "gsparams.h"
+#include "gsstate.h" /* (should only be imager state) */
+#include "gxdcolor.h"
+#include "gxdevice.h"
+#include "gscoord.h" /* requires gsmatrix.h */
+#include "gsdevice.h" /* for gs_deviceinitialmatrix */
+#include "gxdevmem.h" /* must precede gxcldev.h */
+#include "gxcldev.h"
+#include "gxclpath.h"
+#include "gxcmap.h"
+#include "gxcspace.h" /* for gs_color_space_type */
+#include "gxgetbit.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"
+
+/* We need color space types for constructing temporary color spaces. */
+extern const gs_color_space_type gs_color_space_type_Indexed;
+
+/* Print a bitmap for tracing */
+#ifdef DEBUG
+private void
+cmd_print_bits(const byte * data, int width, int height, int raster)
+{
+ int i, j;
+
+ dlprintf3("[L]width=%d, height=%d, raster=%d\n",
+ width, height, raster);
+ for (i = 0; i < height; i++) {
+ const byte *row = data + i * raster;
+
+ dlprintf("[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
+
+/* Get a variable-length integer operand. */
+#define cmd_getw(var, p)\
+ BEGIN\
+ if ( *p < 0x80 ) var = *p++;\
+ else { const byte *_cbp; var = cmd_get_w(p, &_cbp); p = _cbp; }\
+ END
+private long
+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;
+}
+
+/*
+ * Define the structure for keeping track of the command reading buffer.
+ *
+ * The ptr member is only used for passing the current pointer to, and
+ * receiving an updated pointer from, commands implemented as separate
+ * procedures: normally it is kept in a register.
+ */
+typedef struct command_buf_s {
+ byte *data; /* actual buffer, guaranteed aligned */
+ uint size;
+ const byte *ptr; /* next byte to be read (see above) */
+ const byte *limit; /* refill warning point */
+ const byte *end; /* byte just beyond valid data */
+ stream *s; /* for refilling buffer */
+ int end_status;
+} command_buf_t;
+
+/* Set the end of a command buffer. */
+private void
+set_cb_end(command_buf_t *pcb, const byte *end)
+{
+ pcb->end = end;
+ pcb->limit = pcb->data + (pcb->size - cmd_largest_size + 1);
+ if ( pcb->limit > pcb->end )
+ pcb->limit = pcb->end;
+}
+
+/* Read more data into a command buffer. */
+private const byte *
+top_up_cbuf(command_buf_t *pcb, const byte *cbp)
+{
+ uint nread;
+ byte *cb_top = pcb->data + (pcb->end - cbp);
+
+ memmove(pcb->data, cbp, pcb->end - cbp);
+ nread = pcb->end - cb_top;
+ pcb->end_status = sgets(pcb->s, cb_top, nread, &nread);
+ if ( nread == 0 ) {
+ /* No data for this band at all. */
+ *cb_top = cmd_opv_end_run;
+ nread = 1;
+ }
+ set_cb_end(pcb, cb_top + nread);
+ process_interrupts();
+ return pcb->data;
+}
+
+/* Read data from the command buffer and stream. */
+private const byte *
+cmd_read_data(command_buf_t *pcb, byte *ptr, uint rsize, const byte *cbp)
+{
+ if (pcb->end - cbp >= rsize) {
+ memcpy(ptr, cbp, rsize);
+ return cbp + rsize;
+ } else {
+ uint cleft = pcb->end - cbp;
+ uint rleft = rsize - cleft;
+
+ memcpy(ptr, cbp, cleft);
+ sgets(pcb->s, ptr + cleft, rleft, &rleft);
+ return pcb->end;
+ }
+}
+#define cmd_read(ptr, rsize, cbp)\
+ cbp = cmd_read_data(&cbuf, ptr, rsize, cbp)
+
+/* Read a fixed-size value from the command buffer. */
+inline private const byte *
+cmd_copy_value(void *pvar, int var_size, const byte *cbp)
+{
+ memcpy(pvar, cbp, var_size);
+ return cbp + var_size;
+}
+#define cmd_get_value(var, cbp)\
+ cbp = cmd_copy_value(&var, sizeof(var), cbp)
+
+/*
+ * Render one band to a specified target device. Note that if
+ * action == setup, target may be 0.
+ */
+private int read_set_tile_size(P2(command_buf_t *pcb, tile_slot *bits));
+private int read_set_bits(P8(command_buf_t *pcb, tile_slot *bits,
+ int compress, gx_clist_state *pcls,
+ gx_strip_bitmap *tile, tile_slot **pslot,
+ gx_device_clist_reader *cdev, gs_memory_t *mem));
+private int read_set_ht_order(P4(command_buf_t *pcb, gx_device_halftone *pdht,
+ gx_ht_order **pporder, gs_memory_t *mem));
+private int read_set_ht_data(P8(command_buf_t *pcb, uint *pdata_index,
+ gx_ht_order *porder, gx_device_halftone *pdht,
+ gs_halftone_type halftone_type,
+ gs_imager_state *pis,
+ gx_device_clist_reader *cdev,
+ gs_memory_t *mem));
+private int read_begin_image(P5(command_buf_t *pcb, gs_image_t *pim,
+ int *pnum_planes, gs_int_rect *prect,
+ const gs_color_space *pcs));
+private int read_put_params(P3(command_buf_t *pcb,
+ gx_device_clist_reader *cdev,
+ gs_memory_t *mem));
+
+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 const byte *cmd_read_short_bits(P6(command_buf_t *pcb, byte *data,
+ int width_bytes, int height,
+ uint raster, const byte *cbp));
+private int cmd_select_map(P7(cmd_map_index, bool, gs_imager_state *,
+ gx_ht_order *, frac **, uint *, gs_memory_t *));
+private int cmd_resize_halftone(P3(gx_device_halftone *, uint, gs_memory_t *));
+private int clist_decode_segment(P7(gx_path *, int, fixed[6],
+ gs_fixed_point *, int, int,
+ segment_notes));
+
+int
+clist_playback_band(clist_playback_action playback_action,
+ gx_device_clist_reader *cdev, stream *s,
+ gx_device *target, int x0, int y0, gs_memory_t * mem)
+{
+ /* cbuf must be maximally aligned, but still be a byte *. */
+ typedef union { void *p; double d; long l; } aligner_t;
+ aligner_t cbuf_storage[cbuf_size / sizeof(aligner_t) + 1];
+ command_buf_t cbuf;
+ /* 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;
+ int dev_depth = cdev->color_info.depth;
+ int dev_depth_bytes = (dev_depth + 7) >> 3;
+ gx_device *tdev;
+ gx_clist_state state;
+ gx_color_index *set_colors;
+ tile_slot *state_slot;
+ gx_strip_bitmap state_tile; /* parameters for reading tiles */
+ tile_slot tile_bits; /* parameters of current tile */
+ gs_int_point tile_phase;
+ gx_path path;
+ bool in_path;
+ gs_fixed_point ppos;
+ gx_clip_path clip_path;
+ bool use_clip;
+ gx_clip_path *pcpath;
+ gx_device_cpath_accum clip_accum;
+ gs_fixed_rect target_box;
+ struct _cas {
+ bool lop_enabled;
+ gs_fixed_point fill_adjust;
+ } clip_save;
+ gs_imager_state imager_state;
+ gx_device_color dev_color;
+ float dash_pattern[cmd_max_dash];
+ gx_fill_params fill_params;
+ gx_stroke_params stroke_params;
+ gx_device_halftone dev_ht;
+ gs_halftone_type halftone_type;
+ gx_ht_order *porder;
+ uint ht_data_index;
+ gs_image_t image;
+ int image_num_planes;
+ gs_int_rect image_rect;
+ gs_color_space color_space; /* only used for indexed spaces */
+ const gs_color_space *pcs;
+ gx_image_enum_common_t *image_info;
+ segment_notes notes;
+ int data_x;
+ int code = 0;
+
+ cbuf.data = (byte *)cbuf_storage;
+ cbuf.size = cbuf_size;
+ cbuf.s = s;
+ cbuf.end_status = 0;
+ set_cb_end(&cbuf, cbuf.data + cbuf.size);
+ cbp = cbuf.end;
+in: /* Initialize for a new page. */
+ tdev = target;
+ set_colors = state.colors;
+ use_clip = false;
+ pcpath = NULL;
+ notes = sn_none;
+ data_x = 0;
+ {
+ 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 = x0;
+ tile_phase.y = y0;
+ gx_path_init_local(&path, mem);
+ in_path = false;
+ /*
+ * Initialize the clipping region to the full page.
+ * (Since we also initialize use_clip to false, this is arbitrary.)
+ */
+ {
+ gs_fixed_rect cbox;
+
+ gx_cpath_init_local(&clip_path, mem);
+ 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);
+ }
+ if (target != 0)
+ (*dev_proc(target, get_clipping_box))(target, &target_box);
+ imager_state = clist_imager_state_initial;
+ imager_state.line_params.dash.pattern = dash_pattern;
+ code = gs_imager_state_initialize(&imager_state, mem);
+ if (code < 0)
+ goto out;
+ imager_state.halftone = 0; /* never referenced */
+ memset(&dev_ht, 0, sizeof(dev_ht));
+ dev_ht.order.levels = 0; /* clear pointers explicitly, just in case */
+ dev_ht.order.bits = 0;
+ dev_ht.order.transfer = 0;
+ dev_ht.components = 0;
+ imager_state.dev_ht = &dev_ht;
+ imager_state.ht_cache = 0;
+ if (tdev != 0)
+ gx_set_cmap_procs(&imager_state, tdev);
+ gx_imager_setscreenphase(&imager_state, -x0, -y0, gs_color_select_all);
+ halftone_type = ht_type_none;
+ fill_params.fill_zero_width = false;
+ pcs = gs_cspace_DeviceGray(&imager_state);
+ data_bits = gs_alloc_bytes(mem, data_bits_size,
+ "clist_playback_band(data_bits)");
+ if (data_bits == 0) {
+ code = gs_note_error(gs_error_VMerror);
+ goto out;
+ }
+ while (code >= 0) {
+ int op;
+ int compress, depth, raster;
+ byte *source;
+ gx_color_index colors[2];
+ gx_color_index *pcolor;
+ gs_logical_operation_t log_op;
+ tile_slot bits; /* parameters for reading bits */
+
+ /* Make sure the buffer contains a full command. */
+ if (cbp >= cbuf.limit) {
+ if (cbuf.end_status < 0) { /* End of file or error. */
+ if (cbp == cbuf.end) {
+ code = (cbuf.end_status == EOFC ? 0 :
+ gs_note_error(gs_error_ioerror));
+ break;
+ }
+ } else {
+ cbp = top_up_cbuf(&cbuf, cbp);
+ }
+ }
+ op = *cbp++;
+#ifdef DEBUG
+ if (gs_debug_c('L')) {
+ const char *const *sub = cmd_sub_op_names[op >> 4];
+
+ if (sub)
+ dlprintf1("[L]%s:", sub[op & 0xf]);
+ else
+ dlprintf2("[L]%s %d:", cmd_op_names[op >> 4], op & 0xf);
+ }
+#endif
+ switch (op >> 4) {
+ case cmd_op_misc >> 4:
+ switch (op) {
+ case cmd_opv_end_run:
+ if_debug0('L', "\n");
+ continue;
+ case cmd_opv_set_tile_size:
+ cbuf.ptr = cbp;
+ code = read_set_tile_size(&cbuf, &tile_bits);
+ cbp = cbuf.ptr;
+ if (code < 0)
+ goto out;
+ continue;
+ case cmd_opv_set_tile_phase:
+ cmd_getw(state.tile_phase.x, cbp);
+ cmd_getw(state.tile_phase.y, cbp);
+ if_debug2('L', " (%d,%d)\n",
+ state.tile_phase.x,
+ state.tile_phase.y);
+ goto set_phase;
+ case cmd_opv_set_tile_bits:
+ bits = tile_bits;
+ compress = 0;
+ stb:
+ cbuf.ptr = cbp;
+ code = read_set_bits(&cbuf, &bits, compress,
+ &state, &state_tile, &state_slot,
+ cdev, mem);
+ cbp = cbuf.ptr;
+ if (code < 0)
+ goto out;
+ goto stp;
+ case cmd_opv_set_bits:
+ compress = *cbp & 3;
+ bits.cb_depth = *cbp++ >> 2;
+ cmd_getw(bits.width, cbp);
+ cmd_getw(bits.height, cbp);
+ if_debug4('L', " compress=%d depth=%d size=(%d,%d)",
+ compress, bits.cb_depth,
+ bits.width, bits.height);
+ bits.cb_raster =
+ bitmap_raster(bits.width * bits.cb_depth);
+ bits.x_reps = bits.y_reps = 1;
+ bits.shift = bits.rep_shift = 0;
+ goto stb;
+ case cmd_opv_set_tile_color:
+ set_colors = state.tile_colors;
+ if_debug0('L', "\n");
+ continue;
+ case cmd_opv_set_misc:
+ {
+ uint cb = *cbp++;
+
+ switch (cb >> 6) {
+ case cmd_set_misc_lop >> 6:
+ cmd_getw(state.lop, cbp);
+ state.lop = (state.lop << 6) + (cb & 0x3f);
+ if_debug1('L', " lop=0x%x\n", state.lop);
+ if (state.lop_enabled)
+ imager_state.log_op = state.lop;
+ break;
+ case cmd_set_misc_data_x >> 6:
+ if (cb & 0x20)
+ cmd_getw(data_x, cbp);
+ else
+ data_x = 0;
+ data_x = (data_x << 5) + (cb & 0x1f);
+ if_debug1('L', " data_x=%d\n", data_x);
+ break;
+ case cmd_set_misc_map >> 6:
+ {
+ frac *mdata;
+ uint count;
+
+ code = cmd_select_map(cb & 0x1f,
+ cb & 0x20,
+ &imager_state,
+ porder, &mdata,
+ &count, mem);
+
+ if (code < 0)
+ goto out;
+ if (mdata) {
+ cmd_read((byte *) mdata, count, cbp);
+#ifdef DEBUG
+ if (gs_debug_c('L')) {
+ uint i;
+
+ for (i = 0; i < count / sizeof(*mdata); ++i)
+ dprintf1(" 0x%04x", mdata[i]);
+ dputc('\n');
+ }
+ } else {
+ if_debug0('L', " none\n");
+#endif
+ }
+ }
+ /* Recompute the effective transfer, */
+ /* in case this was a transfer map. */
+ gx_imager_set_effective_xfer(
+ &imager_state);
+ break;
+ case cmd_set_misc_halftone >> 6:
+ halftone_type = cb & 0x3f;
+ {
+ uint num_comp;
+
+ cmd_getw(num_comp, cbp);
+ if_debug2('L', " halftone type=%d num_comp=%u\n",
+ halftone_type, num_comp);
+ code = cmd_resize_halftone(&dev_ht,
+ num_comp, mem);
+ if (code < 0)
+ goto out;
+ }
+ break;
+ default:
+ goto bad_op;
+ }
+ }
+ continue;
+ case cmd_opv_enable_lop:
+ state.lop_enabled = true;
+ imager_state.log_op = state.lop;
+ if_debug0('L', "\n");
+ continue;
+ case cmd_opv_disable_lop:
+ state.lop_enabled = false;
+ imager_state.log_op = lop_default;
+ if_debug0('L', "\n");
+ continue;
+ case cmd_opv_set_ht_order:
+ cbuf.ptr = cbp;
+ code = read_set_ht_order(&cbuf, &dev_ht, &porder, mem);
+ cbp = cbuf.ptr;
+ if (code < 0)
+ goto out;
+ ht_data_index = 0;
+ /*
+ * Free the relevant cache, because its sizes
+ * are probably not correct any more.
+ */
+ {
+ gx_ht_cache *pcache = porder->cache;
+
+ if (pcache) {
+ if (pcache != imager_state.ht_cache)
+ gx_ht_free_cache(mem, pcache);
+ porder->cache = 0;
+ }
+ }
+ continue;
+ case cmd_opv_set_ht_data:
+ cbuf.ptr = cbp;
+ code = read_set_ht_data(&cbuf, &ht_data_index, porder,
+ &dev_ht, halftone_type,
+ &imager_state, cdev, mem);
+ cbp = cbuf.ptr;
+ if (code < 0)
+ goto out;
+ continue;
+ case cmd_opv_end_page:
+ if_debug0('L', "\n");
+ /*
+ * Do end-of-page cleanup, then reinitialize if
+ * there are more pages to come.
+ */
+ goto out;
+ 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;
+ }
+ if_debug1('L', " 0x%lx\n", *pcolor);
+ continue;
+ case cmd_opv_set_copy_color:
+ state.color_is_alpha = 0;
+ if_debug0('L', "\n");
+ continue;
+ case cmd_opv_set_copy_alpha:
+ state.color_is_alpha = 1;
+ if_debug0('L', "\n");
+ continue;
+ default:
+ goto bad_op;
+ }
+ /*NOTREACHED */
+ 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;
+ goto setc;
+ 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;
+ goto setc;
+ }
+ case 3:
+ {
+ gx_color_index b = *cbp++;
+
+ *pcolor +=
+ ((gx_color_index) (op & 0xf) << 16) +
+ ((b & 0xf0) << 4) + (b & 0x0f) -
+ cmd_delta1_24_bias;
+ goto setc;
+ }
+ case 2:
+ break;
+ case 1:
+ *pcolor += (gx_color_index) (op & 0xf) - 8;
+ goto setc;
+ }
+ }
+ {
+ 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;
+ }
+ setc:if_debug1('L', " 0x%lx\n", *pcolor);
+ 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 width_bits, width_bytes;
+ uint bytes;
+
+ cmd_getw(state.rect.width, cbp);
+ cmd_getw(state.rect.height, cbp);
+ width_bits = state.rect.width * depth;
+ bytes =
+ clist_bitmap_bytes(width_bits,
+ state.rect.height,
+ op & 3, &width_bytes,
+ (uint *)&raster);
+ /* 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.data),
+ (int)(cbuf.end - cbuf.data));
+ 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 = cbuf.end - cbp;
+
+ if (cleft < bytes) {
+ uint nread = cbuf_size - cleft;
+
+ memmove(cbuf.data, cbp, cleft);
+ cbuf.end_status = sgets(s, cbuf.data + cleft, nread, &nread);
+ set_cb_end(&cbuf, cbuf.data + cleft + nread);
+ cbp = cbuf.data;
+ }
+ r.ptr = cbp - 1;
+ r.limit = cbuf.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,
+ width_bytes << 3 /*state.rect.width */ ,
+ state.rect.height, mem);
+ /* 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 (state.rect.height > 1 &&
+ width_bytes != raster
+ ) {
+ source = data_bits;
+ cbp = cmd_read_short_bits(&cbuf, source, width_bytes,
+ state.rect.height,
+ raster, cbp);
+ } else {
+ cmd_read(cbuf.data, bytes, cbp);
+ source = cbuf.data;
+ }
+#ifdef DEBUG
+ if (gs_debug_c('L')) {
+ dprintf2(" 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', " index=%u offset=%lu\n",
+ state.tile_index,
+ cdev->tile_table[state.tile_index].offset);
+ state_tile.data = (byte *) (state_slot + 1);
+ stp:state_tile.size.x = state_slot->width;
+ state_tile.size.y = state_slot->height;
+ state_tile.raster = state_slot->cb_raster;
+ state_tile.rep_width = state_tile.size.x /
+ state_slot->x_reps;
+ state_tile.rep_height = state_tile.size.y /
+ state_slot->y_reps;
+ state_tile.rep_shift = state_slot->rep_shift;
+ state_tile.shift = state_slot->shift;
+ set_phase:tile_phase.x =
+ (state.tile_phase.x + x0) % state_tile.size.x;
+ /*
+ * The true tile height for shifted tiles is not
+ * size.y: see gxbitmap.h for the computation.
+ */
+ {
+ int full_height;
+
+ if (state_tile.shift == 0)
+ full_height = state_tile.size.y;
+ else
+ full_height = state_tile.rep_height *
+ (state_tile.rep_width /
+ igcd(state_tile.rep_shift,
+ state_tile.rep_width));
+ tile_phase.y =
+ (state.tile_phase.y + y0) % full_height;
+ }
+ gx_imager_setscreenphase(&imager_state,
+ -(state.tile_phase.x + x0),
+ -(state.tile_phase.y + y0),
+ gs_color_select_all);
+ continue;
+ case cmd_op_misc2 >> 4:
+ switch (op) {
+ case cmd_opv_set_flatness:
+ cmd_get_value(imager_state.flatness, cbp);
+ if_debug1('L', " %g\n", imager_state.flatness);
+ 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);
+ if_debug2('L', " (%g,%g)\n",
+ fixed2float(imager_state.fill_adjust.x),
+ fixed2float(imager_state.fill_adjust.y));
+ continue;
+ case cmd_opv_set_ctm:
+ {
+ gs_matrix mat;
+
+ cbp = cmd_read_matrix(&mat, cbp);
+ mat.tx -= x0;
+ mat.ty -= y0;
+ gs_imager_setmatrix(&imager_state, &mat);
+ if_debug6('L', " [%g %g %g %g %g %g]\n",
+ mat.xx, mat.xy, mat.yx, mat.yy,
+ mat.tx, mat.ty);
+ }
+ continue;
+ case cmd_opv_set_line_width:
+ {
+ float width;
+
+ cmd_get_value(width, cbp);
+ if_debug1('L', " %g\n", width);
+ gx_set_line_width(&imager_state.line_params, width);
+ }
+ continue;
+ case cmd_opv_set_misc2:
+ {
+ uint cb = *cbp;
+
+ switch (cb >> 6) {
+ case cmd_set_misc2_cap_join >> 6:
+ imager_state.line_params.cap =
+ (gs_line_cap) ((cb >> 3) & 7);
+ imager_state.line_params.join =
+ (gs_line_join) (cb & 7);
+ if_debug2('L', " cap=%d join=%d\n",
+ imager_state.line_params.cap,
+ imager_state.line_params.join);
+ break;
+ case cmd_set_misc2_ac_op_sa >> 6:
+ imager_state.accurate_curves =
+ (cb & 4) != 0;
+ imager_state.overprint = (cb & 2) != 0;
+ imager_state.stroke_adjust = cb & 1;
+ if_debug3('L', " AC=%d OP=%d SA=%d\n",
+ imager_state.accurate_curves,
+ imager_state.overprint,
+ imager_state.stroke_adjust);
+ break;
+ case cmd_set_misc2_notes >> 6:
+ notes = (segment_notes) (cb & 0x3f);
+ if_debug1('L', " notes=%d\n", notes);
+ break;
+ case cmd_set_misc2_alpha >> 6:
+ memcpy(&imager_state.alpha, cbp + 1,
+ sizeof(imager_state.alpha));
+ cbp += sizeof(imager_state.alpha);
+ break;
+ default:
+ goto bad_op;
+ }
+ }
+ cbp++;
+ continue;
+ case cmd_opv_set_miter_limit:
+ {
+ float limit;
+
+ cmd_get_value(limit, cbp);
+ if_debug1('L', " %g\n", limit);
+ gx_set_miter_limit(&imager_state.line_params, limit);
+ }
+ continue;
+ case cmd_opv_set_dash:
+ {
+ int nb = *cbp++;
+ int n = nb & 0x3f;
+ float dot_length, offset;
+
+ cmd_get_value(dot_length, cbp);
+ 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);
+ gx_set_dash_adapt(&imager_state.line_params.dash,
+ (nb & 0x80) != 0);
+ gx_set_dot_length(&imager_state.line_params,
+ dot_length,
+ (nb & 0x40) != 0);
+#ifdef DEBUG
+ if (gs_debug_c('L')) {
+ int i;
+
+ dprintf4(" dot=%g(mode %d) adapt=%d offset=%g [",
+ dot_length,
+ (nb & 0x40) != 0,
+ (nb & 0x80) != 0, offset);
+ for (i = 0; i < n; ++i)
+ dprintf1("%g ", dash_pattern[i]);
+ dputs("]\n");
+ }
+#endif
+ cbp += n * sizeof(float);
+ }
+ break;
+ case cmd_opv_enable_clip:
+ pcpath = (use_clip ? &clip_path : NULL);
+ if_debug0('L', "\n");
+ break;
+ case cmd_opv_disable_clip:
+ pcpath = NULL;
+ if_debug0('L', "\n");
+ break;
+ case cmd_opv_begin_clip:
+ pcpath = NULL;
+ if_debug0('L', "\n");
+ code = gx_cpath_reset(&clip_path);
+ if (code < 0)
+ goto out;
+ gx_cpath_accum_begin(&clip_accum, mem);
+ gx_cpath_accum_set_cbox(&clip_accum,
+ &target_box);
+ 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.log_op = lop_default;
+ imager_state.fill_adjust.x =
+ imager_state.fill_adjust.y = fixed_half;
+ break;
+ case cmd_opv_end_clip:
+ if_debug0('L', "\n");
+ gx_cpath_accum_end(&clip_accum, &clip_path);
+ gx_cpath_set_outside(&clip_path, *cbp++);
+ tdev = target;
+ /*
+ * If the entire band falls within the clip
+ * path, no clipping is needed.
+ */
+ {
+ gs_fixed_rect cbox;
+
+ gx_cpath_inner_box(&clip_path, &cbox);
+ use_clip =
+ !(cbox.p.x <= target_box.p.x &&
+ cbox.q.x >= target_box.q.x &&
+ cbox.p.y <= target_box.p.y &&
+ cbox.q.y >= target_box.q.y);
+ }
+ pcpath = (use_clip ? &clip_path : NULL);
+ state.lop_enabled = clip_save.lop_enabled;
+ imager_state.log_op =
+ (state.lop_enabled ? state.lop :
+ lop_default);
+ imager_state.fill_adjust =
+ clip_save.fill_adjust;
+ break;
+ case cmd_opv_set_color_space:
+ {
+ byte b = *cbp++;
+ int index = b >> 4;
+
+ if_debug2('L', " %d%s\n", index,
+ (b & 8 ? " (indexed)" : ""));
+ switch (index) {
+ case gs_color_space_index_DeviceGray:
+ pcs = gs_cspace_DeviceGray(&imager_state);
+ break;
+ case gs_color_space_index_DeviceRGB:
+ pcs = gs_cspace_DeviceRGB(&imager_state);
+ break;
+ case gs_color_space_index_DeviceCMYK:
+ pcs = gs_cspace_DeviceCMYK(&imager_state);
+ break;
+ default:
+ goto bad_op; /* others are NYI */
+ }
+ if (b & 8) {
+#if 0 /****************/
+ int num_comp =
+ gs_color_space_num_components(pcs);
+
+ /****** SET map ******/
+#endif /****************/
+ color_space.type =
+ &gs_color_space_type_Indexed;
+ color_space.params.indexed.base_space.type =
+ pcs->type;
+ cmd_getw(color_space.params.indexed.hival,
+ cbp);
+ color_space.params.indexed.use_proc =
+ (b & 4) != 0;
+ pcs = &color_space;
+ }
+ }
+ break;
+ case cmd_opv_begin_image:
+ cbuf.ptr = cbp;
+ code = read_begin_image(&cbuf, &image,
+ &image_num_planes,
+ &image_rect, pcs);
+ cbp = cbuf.ptr;
+ if (code < 0)
+ goto out;
+ {
+ gx_drawing_color devc;
+
+ color_set_pure(&devc, state.colors[1]);
+ code = (*dev_proc(tdev, begin_image))
+ (tdev, &imager_state, &image, image.format,
+ &image_rect, &devc, pcpath, mem,
+ &image_info);
+ if (code < 0)
+ goto out;
+ }
+ break;
+ case cmd_opv_image_data:
+ {
+ uint height;
+
+ cmd_getw(height, cbp);
+ if (height == 0) {
+ if_debug0('L', " done image\n");
+ code = gx_image_end(image_info, true);
+ } else {
+ uint bytes_per_plane, nbytes;
+ const byte *data;
+ byte *data_on_heap = 0;
+ const byte *planes[64];
+
+/****** DOESN'T HANDLE #PLANES YET *****/
+
+ cmd_getw(bytes_per_plane, cbp);
+ if_debug2('L', " height=%u raster=%u\n",
+ height, bytes_per_plane);
+ nbytes = bytes_per_plane *
+ image_num_planes * height;
+ if ( cbuf.end - cbp < nbytes)
+ cbp = top_up_cbuf(&cbuf, cbp);
+ if (cbuf.end - cbp >= nbytes) {
+ data = cbp;
+ cbp += nbytes;
+ } else {
+ uint cleft = cbuf.end - cbp;
+ uint rleft = nbytes - cleft;
+ byte *rdata;
+
+ if (nbytes > cbuf.end - cbuf.data) { /* Allocate a separate buffer. */
+ rdata = data_on_heap =
+ gs_alloc_bytes(mem, nbytes,
+ "clist image_data");
+ if (rdata == 0) {
+ code = gs_note_error(gs_error_VMerror);
+ goto out;
+ }
+ } else
+ rdata = cbuf.data;
+ memmove(rdata, cbp, cleft);
+ sgets(s, rdata + cleft, rleft,
+ &rleft);
+ data = rdata;
+ cbp = cbuf.end; /* force refill */
+ }
+#ifdef DEBUG
+ if (gs_debug_c('L'))
+ cmd_print_bits(data, image_rect.q.x -
+ image_rect.p.x,
+ image_num_planes * height,
+ bytes_per_plane);
+#endif
+ {
+ int plane;
+
+ for (plane = 0;
+ plane < image_num_planes;
+ ++plane
+ )
+ planes[plane] = data +
+ bytes_per_plane * height * plane;
+ }
+ code = gx_image_data(image_info, planes,
+ data_x, bytes_per_plane,
+ height);
+ if (data_on_heap)
+ gs_free_object(mem, data_on_heap,
+ "clist image_data");
+ data_x = 0;
+ }
+ }
+ if (code < 0)
+ goto out;
+ continue;
+ case cmd_opv_set_color:
+ {
+#define dcb dev_color.colors.colored.c_base
+#define dcl dev_color.colors.colored.c_level
+ byte b = *cbp++;
+ int i;
+
+ switch (b >> 4) {
+ case 0:
+ dcb[0] = (b >> 3) & 1;
+ dcb[1] = (b >> 2) & 1;
+ dcb[2] = (b >> 1) & 1;
+ dcb[3] = b & 1;
+ break;
+ case 1:
+ dcb[0] = ((b & 0xf) << 1) + (*cbp >> 7);
+ dcb[1] = (*cbp >> 2) & 0x1f;
+ dcb[2] = ((*cbp & 3) << 3) + (cbp[1] >> 5);
+ dcb[3] = cbp[1] & 0x1f;
+ cbp += 2;
+ break;
+ default:
+ goto bad_op;
+ }
+ for (i = 0; i < imager_state.dev_ht->num_comp; ++i)
+ cmd_getw(dcl[i], cbp);
+ if_debug10('L', " format %d num_comp=%d base=(%u,%u,%u,%u) level=(%u,%u,%u,%u)\n",
+ b >> 4,
+ imager_state.dev_ht->num_comp,
+ dcb[0], dcb[1], dcb[2], dcb[3],
+ dcl[0], dcl[1], dcl[2], dcl[3]);
+ color_finish_set_cmyk_halftone(&dev_color,
+ imager_state.dev_ht);
+#undef dcb
+#undef dcl
+ }
+ continue;
+ case cmd_opv_put_params:
+ cbuf.ptr = cbp;
+ code = read_put_params(&cbuf, cdev, mem);
+ cbp = cbuf.ptr;
+ if (code > 0)
+ break; /* empty list */
+ if (code < 0)
+ goto out;
+ if (playback_action == playback_action_setup)
+ goto out;
+ break;
+ default:
+ goto bad_op;
+ }
+ continue;
+ case cmd_op_segment >> 4:
+ {
+ fixed vs[6];
+ int i, code;
+
+ if (!in_path) {
+ ppos.x = int2fixed(state.rect.x);
+ ppos.y = int2fixed(state.rect.y);
+ if_debug2('L', " (%d,%d)", state.rect.x,
+ state.rect.y);
+ notes = sn_none;
+ in_path = true;
+ }
+ 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);
+ if_debug1('L', " %g", fixed2float(vs[i - 1]));
+ cbp += 2;
+ v = (int)((*cbp & 7) ^ 4) - 4;
+ break;
+ case 2:
+ case 3:
+ v = (b ^ 0x60) - 0x20;
+ break;
+ case 4:
+ case 5:
+ /*
+ * Without the following cast, C's
+ * brain-damaged coercion rules cause the
+ * result to be considered unsigned, and not
+ * sign-extended on machines where
+ * sizeof(long) > sizeof(int).
+ */
+ v = (((b ^ 0xa0) - 0x20) << 8) + (int)*++cbp;
+ break;
+ case 6:
+ v = (b ^ 0xd0) - 0x10;
+ vs[i] =
+ ((v << 8) + cbp[1]) << (_fixed_shift - 2);
+ if_debug1('L', " %g", fixed2float(vs[i]));
+ 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_debug1('L', " %g", fixed2float(vs[i]));
+ }
+ if_debug0('L', "\n");
+ code = clist_decode_segment(&path, op, vs, &ppos,
+ x0, y0, notes);
+ if (code < 0)
+ goto out;
+ }
+ continue;
+ case cmd_op_path >> 4:
+ {
+ gx_device_color devc;
+ gx_device_color *pdevc;
+ gx_ht_tile ht_tile;
+
+ if_debug0('L', "\n");
+ 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]);
+ pdevc = &devc;
+ 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);
+ pdevc = &devc;
+ pdevc->phase = tile_phase;
+ goto fill;
+ case cmd_opv_colorfill:
+ fill_params.rule = gx_rule_winding_number;
+ goto fill_color;
+ case cmd_opv_coloreofill:
+ fill_params.rule = gx_rule_even_odd;
+ fill_color:pdevc = &dev_color;
+ pdevc->phase = tile_phase;
+ code =
+ gx_color_load(pdevc, &imager_state, tdev);
+ if (code < 0)
+ break;
+ 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,
+ pdevc, pcpath);
+ break;
+ case cmd_opv_stroke:
+ color_set_pure(&devc, state.colors[1]);
+ pdevc = &devc;
+ 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);
+ pdevc = &devc;
+ pdevc->phase = tile_phase;
+ goto stroke;
+ case cmd_opv_colorstroke:
+ pdevc = &dev_color;
+ pdevc->phase = tile_phase;
+ code =
+ gx_color_load(pdevc, &imager_state, tdev);
+ if (code < 0)
+ break;
+ stroke:stroke_params.flatness = imager_state.flatness;
+ code = gx_stroke_path_only(&path,
+ (gx_path *) 0, tdev,
+ &imager_state, &stroke_params,
+ pdevc, 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_free(&path, "clist_render_band");
+ gx_path_init_local(&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.data), (int)(cbuf.end - cbuf.data));
+ {
+ const byte *pp;
+
+ for (pp = cbuf.data; pp < cbuf.end; pp += 10) {
+ dlprintf1("%4d:", (int)(pp - cbuf.data));
+ 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', " 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];
+ log_op = state.lop;
+ 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 - data_x, state.rect.height,
+ tile_phase.x, tile_phase.y, log_op);
+ 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;
+ log_op = state.lop;
+ goto do_rop;
+ }
+ if ((op & cmd_copy_use_tile) || pcpath != NULL) { /*
+ * This call of copy_mono originated as a call
+ * of fill_mask.
+ */
+ 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 - data_x, state.rect.height,
+ &dcolor, 1, imager_state.log_op, 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 - data_x, 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 - data_x, state.rect.height,
+ state.colors[1], depth);
+ } else {
+ if (state.lop_enabled) {
+ pcolor = NULL;
+ log_op = state.lop;
+ 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 - data_x, state.rect.height);
+ }
+ data_x = 0;
+ break;
+ default: /* can't happen */
+ goto bad_op;
+ }
+ }
+ /* Clean up before we exit. */
+ out:gx_cpath_free(&clip_path, "clist_render_band exit");
+ gx_path_free(&path, "clist_render_band exit");
+ if (imager_state.ht_cache)
+ gx_ht_free_cache(mem, imager_state.ht_cache);
+ gx_device_halftone_release(&dev_ht, mem);
+ gs_imager_state_release(&imager_state);
+ gs_free_object(mem, data_bits, "clist_playback_band(data_bits)");
+ if (code < 0)
+ return_error(code);
+ /* Check whether we have more pages to process. */
+ if (playback_action != playback_action_setup &&
+ (cbp < cbuf.end || !seofp(s))
+ )
+ goto in;
+ return code;
+}
+
+/* ---------------- Individual commands ---------------- */
+
+/*
+ * These single-use procedures implement a few large individual commands,
+ * primarily for readability but also to avoid overflowing compilers'
+ * optimization limits. They all take the command buffer as their first
+ * parameter (pcb), assume that the current buffer pointer is in pcb->ptr,
+ * and update it there.
+ */
+
+private int
+read_set_tile_size(command_buf_t *pcb, tile_slot *bits)
+{
+ const byte *cbp = pcb->ptr;
+ uint rep_width, rep_height;
+ byte bd = *cbp++;
+
+ bits->cb_depth = (bd & 31) + 1;
+ cmd_getw(rep_width, cbp);
+ cmd_getw(rep_height, cbp);
+ if (bd & 0x20) {
+ cmd_getw(bits->x_reps, cbp);
+ bits->width = rep_width * bits->x_reps;
+ } else {
+ bits->x_reps = 1;
+ bits->width = rep_width;
+ }
+ if (bd & 0x40) {
+ cmd_getw(bits->y_reps, cbp);
+ bits->height = rep_height * bits->y_reps;
+ } else {
+ bits->y_reps = 1;
+ bits->height = rep_height;
+ }
+ if (bd & 0x80)
+ cmd_getw(bits->rep_shift, cbp);
+ else
+ bits->rep_shift = 0;
+ if_debug6('L', " depth=%d size=(%d,%d), rep_size=(%d,%d), rep_shift=%d\n",
+ bits->cb_depth, bits->width,
+ bits->height, rep_width,
+ rep_height, bits->rep_shift);
+ bits->shift =
+ (bits->rep_shift == 0 ? 0 :
+ (bits->rep_shift * (bits->height / rep_height)) % rep_width);
+ bits->cb_raster = bitmap_raster(bits->width * bits->cb_depth);
+ pcb->ptr = cbp;
+ return 0;
+}
+
+private int
+read_set_bits(command_buf_t *pcb, tile_slot *bits, int compress,
+ gx_clist_state *pcls, gx_strip_bitmap *tile, tile_slot **pslot,
+ gx_device_clist_reader *cdev, gs_memory_t *mem)
+{
+ const byte *cbp = pcb->ptr;
+ uint rep_width = bits->width / bits->x_reps;
+ uint rep_height = bits->height / bits->y_reps;
+ uint index;
+ ulong offset;
+ uint width_bits = rep_width * bits->cb_depth;
+ uint width_bytes;
+ uint raster;
+ uint bytes =
+ clist_bitmap_bytes(width_bits, rep_height,
+ compress |
+ (rep_width < bits->width ?
+ decompress_spread : 0) |
+ decompress_elsewhere,
+ &width_bytes,
+ (uint *)&raster);
+ byte *data;
+ tile_slot *slot;
+
+ cmd_getw(index, cbp);
+ cmd_getw(offset, cbp);
+ if_debug2('L', " index=%d offset=%lu\n", pcls->tile_index, offset);
+ pcls->tile_index = index;
+ cdev->tile_table[pcls->tile_index].offset = offset;
+ slot = (tile_slot *)(cdev->chunk.data + offset);
+ *pslot = slot;
+ *slot = *bits;
+ tile->data = data = (byte *)(slot + 1);
+#ifdef DEBUG
+ slot->index = pcls->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 = pcb->end - cbp;
+
+ if (cleft < bytes) {
+ uint nread = cbuf_size - cleft;
+
+ memmove(pcb->data, cbp, cleft);
+ pcb->end_status = sgets(pcb->s, pcb->data + cleft, nread, &nread);
+ set_cb_end(pcb, pcb->data + cleft + nread);
+ cbp = pcb->data;
+ }
+ r.ptr = cbp - 1;
+ r.limit = pcb->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_bytes << 3 /*width_bits */ ,
+ rep_height, mem);
+ (*s_CFD_template.process)
+ ((stream_state *)&sstate, &r, &w, true);
+ (*s_CFD_template.release)
+ ((stream_state *)&sstate);
+ }
+ break;
+ default:
+ return_error(gs_error_unregistered);
+ }
+ cbp = r.ptr + 1;
+ } else if (rep_height > 1 && width_bytes != bits->cb_raster) {
+ cbp = cmd_read_short_bits(pcb, data,
+ width_bytes, rep_height,
+ bits->cb_raster, cbp);
+ } else {
+ cbp = cmd_read_data(pcb, 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'))
+ cmd_print_bits(data, bits->width, bits->height, bits->cb_raster);
+#endif
+ pcb->ptr = cbp;
+ return 0;
+}
+
+private int
+read_set_ht_order(command_buf_t *pcb, gx_device_halftone *pdht,
+ gx_ht_order **pporder, gs_memory_t *mem)
+{
+ const byte *cbp = pcb->ptr;
+ gx_ht_order *porder;
+ uint *levels;
+ gx_ht_bit *bits;
+ int index;
+ gx_ht_order new_order;
+
+ cmd_getw(index, cbp);
+ if (index == 0)
+ porder = &pdht->order;
+ else {
+ gx_ht_order_component *pcomp = &pdht->components[index - 1];
+
+ cmd_getw(pcomp->cname, cbp);
+ if_debug1('L', " cname=%lu", (ulong) pcomp->cname);
+ porder = &pcomp->corder;
+ }
+ *pporder = porder;
+ new_order = *porder;
+ cmd_getw(new_order.width, cbp);
+ cmd_getw(new_order.height, cbp);
+ cmd_getw(new_order.raster, cbp);
+ cmd_getw(new_order.shift, cbp);
+ cmd_getw(new_order.num_levels, cbp);
+ cmd_getw(new_order.num_bits, cbp);
+ pcb->ptr = cbp;
+ if_debug7('L', " index=%d size=(%d,%d) raster=%d shift=%d num_levels=%d num_bits=%d\n",
+ index, new_order.width, new_order.height,
+ new_order.raster, new_order.shift,
+ new_order.num_levels, new_order.num_bits);
+ levels = porder->levels;
+ bits = porder->bits;
+ /*
+ * Note that for resizing a byte array, the element size is 1 byte,
+ * not the element size given to alloc_byte_array!
+ */
+ if (new_order.num_levels > porder->num_levels) {
+ if (levels == 0)
+ levels = (uint *) gs_alloc_byte_array(mem, new_order.num_levels,
+ sizeof(*levels),
+ "ht order(levels)");
+ else
+ levels = gs_resize_object(mem, levels,
+ new_order.num_levels * sizeof(*levels),
+ "ht order(levels)");
+ if (levels == 0)
+ return_error(gs_error_VMerror);
+ /* Update porder in case we bail out. */
+ porder->levels = levels;
+ porder->num_levels = new_order.num_levels;
+ }
+ if (new_order.num_bits > porder->num_bits) {
+ if (bits == 0)
+ bits = (gx_ht_bit *) gs_alloc_byte_array(mem, new_order.num_bits,
+ sizeof(*bits),
+ "ht order(bits)");
+ else
+ bits = gs_resize_object(mem, bits,
+ new_order.num_bits * sizeof(*bits),
+ "ht order(bits)");
+ if (bits == 0)
+ return_error(gs_error_VMerror);
+ }
+ *porder = new_order;
+ porder->levels = levels;
+ porder->bits = bits;
+ porder->full_height = ht_order_full_height(porder);
+ return 0;
+}
+
+private int
+read_set_ht_data(command_buf_t *pcb, uint *pdata_index, gx_ht_order *porder,
+ gx_device_halftone *pdht, gs_halftone_type halftone_type,
+ gs_imager_state *pis, gx_device_clist_reader *cdev,
+ gs_memory_t *mem)
+{
+ const byte *cbp = pcb->ptr;
+ int n = *cbp++;
+
+ if (*pdata_index < porder->num_levels) { /* Setting levels */
+ byte *lptr = (byte *)(porder->levels + *pdata_index);
+
+ cbp = cmd_read_data(pcb, lptr, n * sizeof(*porder->levels), cbp);
+#ifdef DEBUG
+ if (gs_debug_c('L')) {
+ int i;
+
+ dprintf1(" levels[%u]", *pdata_index);
+ for (i = 0; i < n; ++i)
+ dprintf1(" %u",
+ porder->levels[*pdata_index + i]);
+ dputc('\n');
+ }
+#endif
+ } else { /* Setting bits */
+ byte *bptr = (byte *)
+ (porder->bits + (*pdata_index - porder->num_levels));
+
+ cbp = cmd_read_data(pcb, bptr, n * sizeof(*porder->bits), cbp);
+#ifdef DEBUG
+ if (gs_debug_c('L')) {
+ int i;
+
+ dprintf1(" bits[%u]", *pdata_index - porder->num_levels);
+ for (i = 0; i < n; ++i) {
+ const gx_ht_bit *pb =
+ &porder->bits[*pdata_index - porder->num_levels + i];
+
+ dprintf2(" (%u,0x%lx)",
+ pb->offset,
+ (ulong) pb->mask);
+ }
+ dputc('\n');
+ }
+#endif
+ }
+ *pdata_index += n;
+ /* If this is the end of the data, */
+ /* install the (device) halftone. */
+ if (porder ==
+ (pdht->components != 0 ?
+ &pdht->components[0].corder :
+ &pdht->order) &&
+ *pdata_index == porder->num_levels + porder->num_bits
+ ) { /* Make sure we have a halftone cache. */
+ uint i;
+
+ if (pis->ht_cache == 0) {
+ gx_ht_cache *pcache =
+ gx_ht_alloc_cache(mem,
+ porder->num_levels + 2,
+ gx_ht_cache_default_bits());
+
+ if (pcache == 0)
+ return_error(gs_error_VMerror);
+ pis->ht_cache = pcache;
+ }
+ for (i = 1; i < pdht->num_comp; ++i) {
+ gx_ht_order *pco = &pdht->components[i].corder;
+
+ if (!pco->cache) {
+ gx_ht_cache *pcache =
+ gx_ht_alloc_cache(mem, 1,
+ pco->raster * (pco->num_bits /
+ pco->width));
+
+ if (pcache == 0)
+ return_error(gs_error_VMerror);
+ pco->cache = pcache;
+ gx_ht_init_cache(pco->cache, pco);
+ }
+ }
+ if (pdht->num_comp) {
+ pdht->components[0].corder.cache = pis->ht_cache;
+ pdht->order = pdht->components[0].corder;
+ }
+ gx_imager_dev_ht_install(pis, pdht, halftone_type,
+ (const gx_device *)cdev);
+ }
+ pcb->ptr = cbp;
+ return 0;
+}
+
+private int
+read_begin_image(command_buf_t *pcb, gs_image_t *pim, int *pnum_planes,
+ gs_int_rect *prect, const gs_color_space *pcs)
+{
+ const byte *cbp = pcb->ptr;
+ byte b = *cbp++;
+ int bpci = b >> 5;
+ static const byte bpc[6] = {1, 1, 2, 4, 8, 12};
+ int num_components;
+ gs_image_format_t format;
+
+ if (bpci == 0)
+ gs_image_t_init_mask(pim, false);
+ else
+ gs_image_t_init(pim, pcs);
+ if (b & (1 << 4)) {
+ byte b2 = *cbp++;
+
+ format = b2 >> 6;
+ pim->Interpolate = (b2 & (1 << 5)) != 0;
+ pim->Alpha = (gs_image_alpha_t) ((b2 >> 3) & 3);
+ } else {
+ format = gs_image_format_chunky;
+ }
+ pim->format = format;
+ cmd_getw(pim->Width, cbp);
+ cmd_getw(pim->Height, cbp);
+ if_debug4('L', " BPCi=%d I=%d size=(%d,%d)",
+ bpci, (b & 0x10) != 0, pim->Width, pim->Height);
+ if (b & (1 << 3)) { /* Non-standard ImageMatrix */
+ cbp = cmd_read_matrix(
+ &pim->ImageMatrix, cbp);
+ if_debug6('L', " matrix=[%g %g %g %g %g %g]",
+ pim->ImageMatrix.xx, pim->ImageMatrix.xy,
+ pim->ImageMatrix.yx, pim->ImageMatrix.yy,
+ pim->ImageMatrix.tx, pim->ImageMatrix.ty);
+ } else {
+ 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;
+ }
+ pim->BitsPerComponent = bpc[bpci];
+ if (bpci == 0) {
+ num_components = 1;
+ } else {
+ pim->ColorSpace = pcs;
+ if (gs_color_space_get_index(pcs) == gs_color_space_index_Indexed) {
+ pim->Decode[0] = 0;
+ pim->Decode[1] = (1 << pim->BitsPerComponent) - 1;
+ } else {
+ static const float decode01[] = {
+ 0, 1, 0, 1, 0, 1, 0, 1, 0, 1
+ };
+
+ memcpy(pim->Decode, decode01, sizeof(pim->Decode));
+ }
+ num_components = gs_color_space_num_components(pcs);
+ }
+ switch (format) {
+ case gs_image_format_chunky:
+ *pnum_planes = 1;
+ break;
+ case gs_image_format_component_planar:
+ *pnum_planes = num_components;
+ break;
+ case gs_image_format_bit_planar:
+ *pnum_planes = num_components * pim->BitsPerComponent;
+ break;
+ default:
+ return_error(gs_error_unregistered);
+ }
+ 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 */
+ pim->Decode[i] = pim->Decode[i + 1];
+ pim->Decode[i + 1] = 0;
+ break;
+ case 3:
+ cmd_get_value(pim->Decode[i], cbp);
+ /* falls through */
+ case 2:
+ cmd_get_value(pim->Decode[i + 1], cbp);
+ }
+#ifdef DEBUG
+ if (gs_debug_c('L')) {
+ dputs(" decode=[");
+ for (i = 0; i < num_components * 2; ++i)
+ dprintf1("%g ", pim->Decode[i]);
+ dputc(']');
+ }
+#endif
+ }
+ pim->adjust = false;
+ if (b & (1 << 1)) {
+ if (pim->ImageMask)
+ pim->adjust = true;
+ else
+ pim->CombineWithColor = true;
+ if_debug1('L', " %s",
+ (pim->ImageMask ? " adjust" : " CWC"));
+ }
+ if (b & (1 << 0)) { /* Non-standard rectangle */
+ uint diff;
+
+ cmd_getw(prect->p.x, cbp);
+ cmd_getw(prect->p.y, cbp);
+ cmd_getw(diff, cbp);
+ prect->q.x = pim->Width - diff;
+ cmd_getw(diff, cbp);
+ prect->q.y = pim->Height - diff;
+ if_debug4('L', " rect=(%d,%d),(%d,%d)",
+ prect->p.x, prect->p.y,
+ prect->q.x, prect->q.y);
+ } else {
+ prect->p.x = 0;
+ prect->p.y = 0;
+ prect->q.x = pim->Width;
+ prect->q.y = pim->Height;
+ }
+ if_debug0('L', "\n");
+ pcb->ptr = cbp;
+ return 0;
+}
+
+private int
+read_put_params(command_buf_t *pcb, gx_device_clist_reader *cdev,
+ gs_memory_t *mem)
+{
+ const byte *cbp = pcb->ptr;
+ gs_c_param_list param_list;
+ uint cleft;
+ uint rleft;
+ bool alloc_data_on_heap = false;
+ byte *param_buf;
+ uint param_length;
+ int code = 0;
+
+ cmd_get_value(param_length, cbp);
+ if_debug1('L', " length=%d\n", param_length);
+ if (param_length == 0) {
+ code = 1; /* empty list */
+ goto out;
+ }
+
+ /* Make sure entire serialized param list is in cbuf */
+ /* + force void* alignment */
+ cbp = top_up_cbuf(pcb, cbp);
+ if (pcb->end - cbp >= param_length) {
+ param_buf = (byte *)cbp;
+ cbp += param_length;
+ } else {
+ /* NOTE: param_buf must be maximally aligned */
+ param_buf = gs_alloc_bytes(mem, param_length,
+ "clist put_params");
+ if (param_buf == 0) {
+ code = gs_note_error(gs_error_VMerror);
+ goto out;
+ }
+ alloc_data_on_heap = true;
+ cleft = pcb->end - cbp;
+ rleft = param_length - cleft;
+ memmove(param_buf, cbp, cleft);
+ pcb->end_status = sgets(pcb->s, param_buf + cleft, rleft, &rleft);
+ cbp = pcb->end; /* force refill */
+ }
+
+ /*
+ * Create a gs_c_param_list & expand into it.
+ * NB that gs_c_param_list doesn't copy objects into
+ * it, but rather keeps *pointers* to what's passed.
+ * That's OK because the serialized format keeps enough
+ * space to hold expanded versions of the structures,
+ * but this means we cannot deallocate source buffer
+ * until the gs_c_param_list is deleted.
+ */
+ gs_c_param_list_write(&param_list, mem);
+ code = gs_param_list_unserialize
+ ( (gs_param_list *)&param_list, param_buf );
+ if (code >= 0 && code != param_length)
+ code = gs_error_unknownerror; /* must match */
+ if (code >= 0) {
+ gs_c_param_list_read(&param_list);
+ code = (*dev_proc(cdev, put_params))
+ ((gx_device *)cdev, (gs_param_list *)&param_list);
+ }
+ gs_c_param_list_release(&param_list);
+ if (alloc_data_on_heap)
+ gs_free_object(mem, param_buf, "clist put_params");
+
+out:
+ pcb->ptr = cbp;
+ return code;
+}
+
+/* ---------------- Utilities ---------------- */
+
+/* Read and unpack a short bitmap */
+private const byte *
+cmd_read_short_bits(command_buf_t *pcb, byte *data, int width_bytes,
+ int height, uint raster, const byte *cbp)
+{
+ uint bytes = width_bytes * height;
+ const byte *pdata = data /*src*/ + bytes;
+ byte *udata = data /*dest*/ + height * raster;
+
+ cbp = cmd_read_data(pcb, data, width_bytes * height, cbp);
+ 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:; /* shouldn't happen */
+ }
+ }
+ return cbp;
+}
+
+/* Read a rectangle. */
+private const byte *
+cmd_read_rect(int op, gx_cmd_rect * prect, 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;
+}
+
+/* Select a map for loading with data. */
+/* load = false is not possible for cmd_map_transfer*. */
+private int
+cmd_select_map(cmd_map_index map_index, bool load, gs_imager_state * pis,
+ gx_ht_order * porder, frac ** pmdata, uint * pcount, gs_memory_t * mem)
+{
+ gx_transfer_map *map;
+ gx_transfer_map **pmap;
+ const char *cname;
+
+ switch (map_index) {
+ case cmd_map_transfer:
+ if_debug0('L', " transfer");
+ map = pis->set_transfer.colored.gray;
+ pis->effective_transfer.indexed[0] =
+ pis->effective_transfer.indexed[1] =
+ pis->effective_transfer.indexed[2] =
+ pis->effective_transfer.indexed[3] =
+ map;
+ break;
+ case cmd_map_transfer_0:
+ case cmd_map_transfer_1:
+ case cmd_map_transfer_2:
+ case cmd_map_transfer_3:
+ {
+ int i = map_index - cmd_map_transfer_0;
+
+ if_debug1('L', " transfer[%d]", i);
+ rc_unshare_struct(pis->set_transfer.indexed[i], gx_transfer_map,
+ &st_transfer_map, mem,
+ return_error(gs_error_VMerror),
+ "cmd_select_map(transfer)");
+ map = pis->set_transfer.indexed[i];
+ pis->effective_transfer.indexed[i] = map;
+ }
+ break;
+ case cmd_map_ht_transfer:
+ if_debug0('L', " ht transfer");
+ /* Halftone transfer maps are never shared, but */
+ /* rc_unshare_struct is a good way to get one allocated */
+ /* if it hasn't been yet. */
+ pmap = &porder->transfer;
+ cname = "cmd_select_map(ht transfer)";
+ goto alloc;
+ case cmd_map_black_generation:
+ if_debug0('L', " black generation");
+ pmap = &pis->black_generation;
+ cname = "cmd_select_map(black generation)";
+ goto alloc;
+ case cmd_map_undercolor_removal:
+ if_debug0('L', " undercolor removal");
+ pmap = &pis->undercolor_removal;
+ cname = "cmd_select_map(undercolor removal)";
+alloc: if (!load) {
+ rc_decrement(*pmap, cname);
+ *pmap = 0;
+ *pmdata = 0;
+ *pcount = 0;
+ return 0;
+ }
+ rc_unshare_struct(*pmap, gx_transfer_map, &st_transfer_map,
+ mem, return_error(gs_error_VMerror), cname);
+ map = *pmap;
+ break;
+ default:
+ *pmdata = 0;
+ return 0;
+ }
+ map->proc = gs_mapped_transfer;
+ *pmdata = map->values;
+ *pcount = sizeof(map->values);
+ return 0;
+}
+
+/* Resize the halftone components array if necessary. */
+private int
+cmd_resize_halftone(gx_device_halftone * pdht, uint num_comp,
+ gs_memory_t * mem)
+{
+ if (num_comp != pdht->num_comp) {
+ gx_ht_order_component *pcomp;
+
+ /*
+ * We must be careful not to shrink or free the components array
+ * before releasing any relevant elements.
+ */
+ if (num_comp < pdht->num_comp) {
+ uint i;
+
+ /* Don't release the default order. */
+ for (i = pdht->num_comp; i-- > num_comp;)
+ if (pdht->components[i].corder.bits != pdht->order.bits)
+ gx_ht_order_release(&pdht->components[i].corder, mem, true);
+ if (num_comp == 0) {
+ gs_free_object(mem, pdht->components, "cmd_resize_halftone");
+ pcomp = 0;
+ } else {
+ pcomp = gs_resize_object(mem, pdht->components, num_comp,
+ "cmd_resize_halftone");
+ if (pcomp == 0) {
+ pdht->num_comp = num_comp; /* attempt consistency */
+ return_error(gs_error_VMerror);
+ }
+ }
+ } else {
+ /* num_comp > pdht->num_comp */
+ if (pdht->num_comp == 0)
+ pcomp = gs_alloc_struct_array(mem, num_comp,
+ gx_ht_order_component,
+ &st_ht_order_component_element,
+ "cmd_resize_halftone");
+ else
+ pcomp = gs_resize_object(mem, pdht->components, num_comp,
+ "cmd_resize_halftone");
+ if (pcomp == 0)
+ return_error(gs_error_VMerror);
+ memset(&pcomp[pdht->num_comp], 0,
+ sizeof(*pcomp) * (num_comp - pdht->num_comp));
+ }
+ pdht->num_comp = num_comp;
+ pdht->components = pcomp;
+ }
+ return 0;
+}
+
+/* ------ 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, segment_notes notes)
+{
+ 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_notes(ppath, px += A, py += B, notes);
+ break;
+ case cmd_opv_hlineto:
+ code = gx_path_add_line_notes(ppath, px += A, py, notes);
+ break;
+ case cmd_opv_vlineto:
+ code = gx_path_add_line_notes(ppath, px, py += A, notes);
+ 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_notes(ppath, px + A, py + B,
+ px + C, py + D,
+ px + E, py + F, notes);
+ px += E, py += F;
+ break;
+ case cmd_opv_hvcurveto: /* a b c d => a 0 a+b c a+b c+d */
+hvc: 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 */
+vhc: 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_notes(ppath, px += C, py += D, notes);
+ break;
+ case cmd_opv_rm2lineto:
+ if ((code = gx_path_add_point(ppath, px += A, py += B)) < 0 ||
+ (code = gx_path_add_line_notes(ppath, px += C, py += D,
+ notes)) < 0
+ )
+ break;
+ code = gx_path_add_line_notes(ppath, px += E, py += F, notes);
+ break;
+ case cmd_opv_vqcurveto: /* a b => VH a b TS(a,b) TS(b,a) */
+ if ((A ^ B) < 0)
+ C = -B, D = -A;
+ else
+ C = B, D = A;
+ goto vhc;
+ case cmd_opv_hqcurveto: /* a b => HV a TS(a,b) b TS(b,a) */
+ if ((A ^ B) < 0)
+ D = -A, C = B, B = -B;
+ else
+ D = A, C = B;
+ goto hvc;
+ case cmd_opv_rm3lineto:
+ if ((code = gx_path_add_point(ppath, px += A, py += B)) < 0 ||
+ (code = gx_path_add_line_notes(ppath, px += C, py += D,
+ notes)) < 0 ||
+ (code = gx_path_add_line_notes(ppath, px += E, py += F,
+ notes)) < 0
+ )
+ break;
+ code = gx_path_add_line_notes(ppath, px -= C, py -= D, notes);
+ 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/gxclread.c b/pstoraster/gxclread.c
new file mode 100644
index 000000000..d6085e472
--- /dev/null
+++ b/pstoraster/gxclread.c
@@ -0,0 +1,517 @@
+/*
+ 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 supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Command list reading for Ghostscript. */
+#include "memory_.h"
+#include "gx.h"
+#include "gp.h" /* for gp_fmode_rb */
+#include "gpcheck.h"
+#include "gserrors.h"
+#include "gxdevice.h"
+#include "gscoord.h" /* requires gsmatrix.h */
+#include "gsdevice.h" /* for gs_deviceinitialmatrix */
+#include "gxdevmem.h" /* must precede gxcldev.h */
+#include "gxcldev.h"
+#include "gxgetbit.h"
+#include "gxhttile.h"
+#include "gdevht.h"
+#include "stream.h"
+#include "strimpl.h"
+
+/* ------ 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 band
+ * ranges that include the current band).
+ */
+typedef struct stream_band_read_state_s {
+ stream_state_common;
+ gx_band_page_info page_info;
+ int band;
+ uint left; /* amount of data left in this run */
+ cmd_block b_this;
+} stream_band_read_state;
+
+private int
+s_band_read_init(stream_state * st)
+{
+ stream_band_read_state *const ss = (stream_band_read_state *) st;
+
+ ss->left = 0;
+ ss->b_this.band_min = 0;
+ ss->b_this.band_max = 0;
+ ss->b_this.pos = 0;
+ clist_rewind(ss->page_bfile, false, ss->page_bfname);
+ return 0;
+}
+
+private int
+s_band_read_process(stream_state * st, stream_cursor_read * ignore_pr,
+ stream_cursor_write * pw, bool last)
+{
+ stream_band_read_state *const ss = (stream_band_read_state *) st;
+ register byte *q = pw->ptr;
+ byte *wlimit = pw->limit;
+ clist_file_ptr cfile = ss->page_cfile;
+ clist_file_ptr bfile = ss->page_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);
+ if (clist_ferror_code(cfile) < 0) {
+ status = ERRC;
+ break;
+ }
+ q += count;
+ left -= count;
+ process_interrupts();
+ continue;
+ }
+ rb: /* Scan for the next run for this band (or a band range */
+ /* that includes the current band). */
+ if (ss->b_this.band_min == cmd_band_end &&
+ clist_ftell(bfile) == ss->page_bfile_end_pos
+ ) {
+ status = EOFC;
+ break;
+ } {
+ int bmin = ss->b_this.band_min;
+ int bmax = ss->b_this.band_max;
+ long pos = ss->b_this.pos;
+
+ clist_fread_chars(&ss->b_this, sizeof(ss->b_this), bfile);
+ if (!(ss->band >= bmin && ss->band <= bmax))
+ goto rb;
+ clist_fseek(cfile, pos, SEEK_SET, ss->page_cfname);
+ left = (uint) (ss->b_this.pos - pos);
+ if_debug5('l', "[l]reading for bands (%d,%d) at bfile %ld, cfile %ld, length %u\n",
+ bmin, bmax,
+ clist_ftell(bfile) - 2 * sizeof(ss->b_this),
+ pos, left);
+ }
+ }
+ pw->ptr = q;
+ ss->left = left;
+ return status;
+}
+
+/* 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 ------ */
+
+/* Forward references */
+
+private int clist_render_init(P1(gx_device_clist *));
+private int clist_playback_file_band(P7(clist_playback_action action,
+ gx_device_clist_reader *cdev,
+ gx_band_page_info *page_info,
+ gx_device *target,
+ int band, int x0, int y0));
+private int clist_rasterize_lines(P6(gx_device *dev, int y, int lineCount,
+ byte *data_in, gx_device_memory *mdev,
+ int *pmy));
+
+/*
+ * Do device setup from params stored in command list. This is only for
+ * async rendering & assumes that the first command in every command list
+ * is a put_params command which sets all space-related parameters to the
+ * value they will have for the duration of that command list.
+ */
+int
+clist_setup_params(gx_device *dev)
+{
+ gx_device_clist_reader * const crdev =
+ &((gx_device_clist *)dev)->reader;
+ int code = clist_render_init((gx_device_clist *)dev);
+ if ( code < 0 )
+ return code;
+
+ code = clist_playback_file_band(playback_action_setup,
+ crdev, &crdev->page_info, 0, 0, 0, 0);
+
+ /* put_params may have reinitialized device into a writer */
+ clist_render_init((gx_device_clist *)dev);
+
+ return code;
+}
+
+/* Find out where the band buffer for a given line is going to fall on the */
+/* next call to get_bits. */
+/****** THIS IS WRONG: IT DUPLICATES CODE INSIDE make_buffer_device
+ ****** AND ASSUMES THAT make_buffer_device JUST SETS UP A MEMORY DEVICE.
+ ******/
+int /* rets # lines from y till end of buffer, or -ve error code */
+clist_locate_overlay_buffer(gx_device_printer *pdev, int y, byte **data)
+{
+ gx_device_clist_reader * const crdev =
+ &((gx_device_clist *)pdev)->reader;
+ gx_device * const dev = (gx_device *)pdev;
+ gx_device *target = crdev->target;
+
+ uint raster = gdev_mem_raster(target);
+ byte *mdata = crdev->data + crdev->page_tile_cache_size;
+ int band_height = crdev->page_band_height;
+ int band = y / band_height;
+ int band_begin_line = band * band_height;
+ int bytes_from_band_begin_to_line = (y - band_begin_line) * raster;
+ int band_end_line = band_begin_line + band_height;
+
+ if (band_end_line > dev->height)
+ band_end_line = dev->height;
+
+ /* Make sure device will rasterize on next get_bits or get_overlay_bits */
+ if ( crdev->ymin >= 0 )
+ crdev->ymin = crdev->ymax = 0;
+
+ *data = mdata + bytes_from_band_begin_to_line;
+ return band_end_line - y; /* # lines remaining in this band */
+}
+
+/* Do more rendering to a client-supplied memory image, return results */
+int
+clist_get_overlay_bits(gx_device_printer *pdev, int y, int line_count,
+ byte *data)
+{
+ gx_device_clist_reader * const crdev =
+ &((gx_device_clist *)pdev)->reader;
+ byte *data_orig = data;
+ gx_device * const dev = (gx_device *)pdev;
+ gx_device *target = crdev->target;
+ uint raster = gdev_mem_raster(target);
+ int lines_left = line_count;
+
+ /* May have to render more than once to cover requested line range */
+ while (lines_left > 0) {
+ gx_device_memory mdev;
+ int my;
+ byte *data_transformed;
+ int line_count_rasterized =
+ clist_rasterize_lines(dev, y, lines_left, data_orig, &mdev, &my);
+ uint byte_count_rasterized = raster * line_count_rasterized;
+
+ if (line_count_rasterized < 0)
+ return line_count_rasterized;
+ data_transformed = mdev.base + raster * my;
+ if (data_orig != data_transformed)
+ memcpy(data_orig, data_transformed, byte_count_rasterized);
+ data_orig += byte_count_rasterized;
+ lines_left -= line_count_rasterized;
+ }
+ return 0;
+}
+
+/* Copy a rasterized rectangle to the client, rasterizing if needed. */
+int
+clist_get_bits_rectangle(gx_device *dev, const gs_int_rect * prect,
+ gs_get_bits_params_t *params, gs_int_rect **unread)
+{
+ gs_get_bits_options_t options = params->options;
+ int y = prect->p.y;
+ int end_y = prect->q.y;
+ int line_count = end_y - y;
+ gs_int_rect band_rect;
+ int lines_rasterized;
+ gx_device_memory mdev;
+ int my;
+ int code;
+
+ if (prect->p.x < 0 || prect->q.x > dev->width ||
+ y < 0 || end_y > dev->height
+ )
+ return_error(gs_error_rangecheck);
+ if (line_count <= 0 || prect->p.x >= prect->q.x)
+ return 0;
+ code = clist_rasterize_lines(dev, y, line_count, NULL, &mdev, &my);
+ if (code < 0)
+ return code;
+ lines_rasterized = min(code, line_count);
+ /* Return as much of the rectangle as falls within the rasterized lines. */
+ band_rect = *prect;
+ band_rect.p.y = my;
+ band_rect.q.y = my + lines_rasterized;
+ code = (*dev_proc(&mdev, get_bits_rectangle))
+ ((gx_device *)&mdev, &band_rect, params, unread);
+ if (code < 0 || lines_rasterized == line_count)
+ return code;
+ /*
+ * We'll have to return the rectangle in pieces. Force GB_RETURN_COPY
+ * rather than GB_RETURN_POINTER, and require all subsequent pieces to
+ * use the same values as the first piece for all of the other format
+ * options. If copying isn't allowed, or if there are any unread
+ * rectangles, punt.
+ */
+ if (!(options & GB_RETURN_COPY) || code > 0)
+ return gx_default_get_bits_rectangle(dev, prect, params, unread);
+ options = params->options;
+ if (!(options & GB_RETURN_COPY)) {
+ /* Redo the first piece with copying. */
+ params->options = options =
+ (params->options & ~GB_RETURN_ALL) | GB_RETURN_COPY;
+ lines_rasterized = 0;
+ }
+ {
+ gs_get_bits_params_t band_params;
+ int num_planes =
+ (options & GB_PACKING_CHUNKY ? 1 :
+ options & GB_PACKING_PLANAR ? mdev.color_info.num_components :
+ options & GB_PACKING_BIT_PLANAR ? mdev.color_info.depth :
+ 0 /****** NOT POSSIBLE ******/);
+ uint raster = gdev_mem_raster(&mdev);
+
+ band_params = *params;
+ while ((y += lines_rasterized) < end_y) {
+ int i;
+
+ /* Increment data pointers by lines_rasterized. */
+ for (i = 0; i < num_planes; ++i)
+ band_params.data[i] += raster * lines_rasterized;
+ line_count = end_y - y;
+ code = clist_rasterize_lines(dev, y, line_count, NULL, &mdev, &my);
+ if (code < 0)
+ return code;
+ lines_rasterized = min(code, line_count);
+ band_rect.p.y = my;
+ band_rect.q.y = my + lines_rasterized;
+ code = (*dev_proc(&mdev, get_bits_rectangle))
+ ((gx_device *)&mdev, &band_rect, &band_params, unread);
+ if (code < 0)
+ return code;
+ params->options = options = band_params.options;
+ if (lines_rasterized == line_count)
+ return code;
+ }
+ }
+ return 0;
+}
+
+/* Copy scan lines to the client. This is where rendering gets done. */
+/* Processes min(requested # lines, # lines available thru end of band) */
+private int /* returns -ve error code, or # scan lines copied */
+clist_rasterize_lines(gx_device *dev, int y, int line_count, byte *data_in,
+ gx_device_memory *mdev, int *pmy)
+{
+ gx_device_clist_reader * const crdev =
+ &((gx_device_clist *)dev)->reader;
+ gx_device *target = crdev->target;
+ uint raster = gx_device_raster(target, true);
+ byte *mdata = crdev->data + crdev->page_tile_cache_size;
+ gx_device *tdev = (gx_device *)mdev;
+ int code;
+
+ /* Initialize for rendering if we haven't done so yet. */
+ if (crdev->ymin < 0) {
+ code = clist_end_page(&((gx_device_clist *)crdev)->writer);
+ if ( code < 0 )
+ return code;
+ code = clist_render_init((gx_device_clist *)dev);
+ if ( code < 0 )
+ return code;
+#if 0 /* **************** */
+ gx_device_ht hdev;
+
+ code = clist_render_init((gx_device_clist *) dev, &hdev);
+ if (code < 0)
+ return code;
+ if (code != 0) {
+ hdev.target = tdev;
+ tdev = (gx_device *) & hdev;
+ }
+#endif /* **************** */
+ }
+
+ /* Render a band if necessary, and copy it incrementally. */
+ code = (*crdev->make_buffer_device)(mdev, target, 0, true);
+ if (code < 0)
+ return code;
+ mdev->width = target->width;
+ mdev->raster = raster;
+ if (data_in || !(y >= crdev->ymin && y < crdev->ymax)) {
+ const gx_placed_page *ppages = crdev->pages;
+ int num_pages = crdev->num_pages;
+ gx_saved_page current_page;
+ gx_placed_page placed_page;
+ int band_height = crdev->page_band_height;
+ int band = y / band_height;
+ int band_begin_line = band * band_height;
+ int band_end_line = band_begin_line + band_height;
+ int i;
+
+ if (band_end_line > dev->height)
+ band_end_line = dev->height;
+ /* Clip line_count to current band */
+ if (line_count > band_end_line - y)
+ line_count = band_end_line - y;
+
+ if (y < 0 || y > dev->height)
+ return_error(gs_error_rangecheck);
+ /****** QUESTIONABLE, BUT BETTER THAN OMITTING ******/
+ mdev->color_info = dev->color_info;
+ mdev->base = mdata;
+ /*
+ * 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->height = band_height;
+ (*dev_proc(mdev, open_device))((gx_device *)mdev);
+ if_debug1('l', "[l]rendering band %d\n", band);
+ /*
+ * Unfortunately, there is currently no way to get a mem
+ * device to rasterize into a given memory space, since
+ * a mem device's memory space must also contain internal
+ * structures.
+ */
+ if (data_in)
+ memcpy(mdev->base + (y - band_begin_line) * raster,
+ data_in, line_count * raster);
+ /*
+ * If we aren't rendering saved pages, do the current one.
+ * Note that this is the only case in which we may encounter
+ * a gx_saved_page with non-zero cfile or bfile.
+ */
+ if (ppages == 0) {
+ current_page.info = crdev->page_info;
+ placed_page.page = &current_page;
+ placed_page.offset.x = placed_page.offset.y = 0;
+ ppages = &placed_page;
+ num_pages = 1;
+ }
+ for (i = 0; i < num_pages; ++i) {
+ const gx_placed_page *ppage = &ppages[i];
+
+ code = clist_playback_file_band(playback_action_render,
+ crdev, &ppage->page->info, tdev,
+ band, -ppage->offset.x,
+ band * mdev->height);
+ if ( code < 0 )
+ break;
+ }
+ /* Reset the band boundaries now, so that we don't get */
+ /* an infinite loop. */
+ crdev->ymin = band_begin_line;
+ crdev->ymax = band_end_line;
+ if (code < 0)
+ return code;
+ *pmy = y - crdev->ymin;
+ } else {
+ /* Just fill in enough of the memory device to access the
+ * already-rasterized scan lines; in particular, only set up scan
+ * line pointers for the requested Y range.
+ */
+ mdev->base = mdata + (y - crdev->ymin) * raster;
+ mdev->height = crdev->ymax - y;
+ gdev_mem_open_scan_lines(mdev, min(line_count, mdev->height));
+ *pmy = 0;
+ }
+ return (line_count > crdev->ymax - y ? crdev->ymax - y : line_count);
+}
+
+/* Initialize for reading. */
+private int
+clist_render_init(gx_device_clist *dev /******, gx_device_ht *hdev ******/)
+{
+ gx_device_clist_reader * const crdev = &dev->reader;
+
+ crdev->ymin = crdev->ymax = 0;
+ /* For normal rasterizing, pages and num_pages are zero. */
+ crdev->pages = 0;
+ crdev->num_pages = 0;
+ return 0;
+}
+
+/* Playback the band file, taking the indicated action w/ its contents. */
+private int
+clist_playback_file_band(clist_playback_action action,
+ gx_device_clist_reader *cdev,
+ gx_band_page_info *page_info, gx_device *target,
+ int band, int x0, int y0)
+{
+ int code = 0;
+ int opened_bfile = 0;
+ int opened_cfile = 0;
+
+ /* We have to pick some allocator for rendering.... */
+ gs_memory_t *mem =
+ (cdev->memory != 0 ? cdev->memory : &gs_memory_default);
+
+ /* setup stream */
+ stream_band_read_state rs;
+ rs.template = &s_band_read_template;
+ rs.memory = 0;
+ rs.band = band;
+ rs.page_info = *page_info;
+
+ /* If this is a saved page, open the files. */
+ if (rs.page_cfile == 0) {
+ code = clist_fopen(rs.page_cfname,
+ gp_fmode_rb, &rs.page_cfile, cdev->bandlist_memory,
+ cdev->bandlist_memory, true);
+ opened_cfile = (code >= 0);
+ }
+ if (rs.page_bfile == 0 && code >= 0) {
+ code = clist_fopen(rs.page_bfname,
+ gp_fmode_rb, &rs.page_bfile, cdev->bandlist_memory,
+ cdev->bandlist_memory, false);
+ opened_bfile = (code >= 0);
+ }
+ if (rs.page_cfile != 0 && rs.page_bfile != 0) {
+ stream s;
+ byte sbuf[cbuf_size];
+ static 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
+ };
+
+ 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;
+ code = clist_playback_band(action, cdev, &s, target, -x0, y0, mem);
+ }
+
+ /* Close the files if we just opened them. */
+ if (opened_bfile && rs.page_bfile != 0)
+ clist_fclose(rs.page_bfile, rs.page_bfname, false);
+ if (opened_cfile && rs.page_cfile != 0)
+ clist_fclose(rs.page_cfile, rs.page_cfname, false);
+
+ return code;
+}
diff --git a/pstoraster/gxclrect.c b/pstoraster/gxclrect.c
new file mode 100644
index 000000000..080cc41db
--- /dev/null
+++ b/pstoraster/gxclrect.c
@@ -0,0 +1,656 @@
+/* Copyright (C) 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Rectangle-oriented command writing for command list */
+#include "gx.h"
+#include "gserrors.h"
+#include "gsutil.h" /* for gs_next_ids */
+#include "gxdevice.h"
+#include "gxdevmem.h" /* must precede gxcldev.h */
+#include "gxcldev.h"
+
+/* ---------------- Writing utilities ---------------- */
+
+#define cmd_set_rect(rect)\
+ ((rect).x = x, (rect).y = y,\
+ (rect).width = width, (rect).height = height)
+
+/* Write a rectangle. */
+private 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;
+ byte *dp;
+ int code;
+
+#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;
+
+ if (dx == width - dwidth && dy == 0) {
+ code = set_cmd_put_op(dp, cldev, pcls, op_tiny + 8, 1);
+ if (code < 0)
+ return code;
+ } else {
+ code = set_cmd_put_op(dp, cldev, pcls, op_tiny, 2);
+ if (code < 0)
+ return code;
+ 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;
+
+ if ((unsigned)dh <= cmd_max_dxy_tiny - cmd_min_dxy_tiny &&
+ dh != 0 && dy == 0
+ ) {
+ op += dh;
+ code = set_cmd_put_op(dp, cldev, pcls, op + 0x10, 3);
+ if (code < 0)
+ return code;
+ if_debug3('L', " rs2:%d,%d,0,%d\n",
+ dx, dwidth, dheight);
+ } else {
+ code = set_cmd_put_op(dp, cldev, pcls, op + 0x10, 5);
+ if (code < 0)
+ return code;
+ 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
+ ) {
+ int rcsize = 1 + cmd_sizew(x) + cmd_sizew(width);
+
+ code = set_cmd_put_op(dp, cldev, pcls,
+ op + ((dy + 2) << 2) + dheight + 2, rcsize);
+ if (code < 0)
+ return code;
+ ++dp;
+ cmd_put2w(x, width, dp);
+ } else {
+ int rcsize = 1 + cmd_size_rect(&pcls->rect);
+
+ code = set_cmd_put_op(dp, cldev, pcls, op, rcsize);
+ if (code < 0)
+ return code;
+ 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;
+}
+
+/* ---------------- Driver procedures ---------------- */
+
+int
+clist_fill_rectangle(gx_device * dev, int x, int y, int width, int height,
+ gx_color_index color)
+{
+ gx_device_clist_writer * const cdev =
+ &((gx_device_clist *)dev)->writer;
+ int code;
+
+ fit_fill(dev, x, y, width, height);
+ FOR_RECTS {
+ TRY_RECT {
+ code = cmd_disable_lop(cdev, pcls);
+ if (code >= 0 && color != pcls->colors[1])
+ code = cmd_put_color(cdev, pcls, &clist_select_color1,
+ color, &pcls->colors[1]);
+ if (code >= 0)
+ code = cmd_write_rect_cmd(cdev, pcls, cmd_op_fill_rect, x, y,
+ width, height);
+ } HANDLE_RECT(code);
+ } END_RECTS;
+ return 0;
+}
+
+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)
+{
+ gx_device_clist_writer * const cdev =
+ &((gx_device_clist *)dev)->writer;
+ int depth =
+ (color1 == gx_no_color_index && color0 == gx_no_color_index ?
+ dev->color_info.depth : 1);
+ int code;
+
+ fit_fill(dev, x, y, width, height);
+ FOR_RECTS {
+ ulong offset_temp;
+
+ TRY_RECT {
+ code = cmd_disable_lop(cdev, pcls);
+ } HANDLE_RECT(code);
+ if (!cls_has_tile_id(cdev, pcls, tile->id, offset_temp)) {
+ code = 0;
+ if (tile->id != gx_no_bitmap_id) {
+ TRY_RECT {
+ code = clist_change_tile(cdev, pcls, tile, depth);
+ } HANDLE_RECT_UNLESS(code,
+ (code != gs_error_VMerror || !cdev->error_is_retryable));
+ }
+ if (code < 0) {
+ /* ok if gx_default... does retries internally: */
+ /* it's self-sufficient */
+ code = gx_default_strip_tile_rectangle(dev, tile,
+ x, y, width, height,
+ color0, color1,
+ px, py);
+ if (code < 0)
+ ERROR_RECT(code);
+ goto endr;
+ }
+ }
+ TRY_RECT {
+ code = 0;
+ if (color0 != pcls->tile_colors[0] || color1 != pcls->tile_colors[1])
+ code = cmd_set_tile_colors(cdev, pcls, color0, color1);
+ if (px != pcls->tile_phase.x || py != pcls->tile_phase.y) {
+ if (code >= 0)
+ code = cmd_set_tile_phase(cdev, pcls, px, py);
+ }
+ if (code >= 0)
+ code = cmd_write_rect_cmd(cdev, pcls, cmd_op_tile_rect, x, y,
+ width, height);
+ } HANDLE_RECT(code);
+endr:;
+ } END_RECTS;
+ return 0;
+}
+
+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)
+{
+ gx_device_clist_writer * const cdev =
+ &((gx_device_clist *)dev)->writer;
+ int y0;
+ gx_bitmap_id orig_id = id;
+
+ fit_copy(dev, data, data_x, raster, id, x, y, width, height);
+ y0 = y;
+ FOR_RECTS {
+ int dx = data_x & 7;
+ int w1 = dx + width;
+ const byte *row = data + (y - y0) * raster + (data_x >> 3);
+ int code;
+
+ TRY_RECT {
+ code = cmd_disable_lop(cdev, pcls);
+ if (code >= 0)
+ code = cmd_disable_clip(cdev, pcls);
+ if (color0 != pcls->colors[0] && code >= 0)
+ code = cmd_set_color0(cdev, pcls, color0);
+ if (color1 != pcls->colors[1] && code >= 0)
+ code = cmd_set_color1(cdev, pcls, color1);
+ } HANDLE_RECT(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;
+ uint compress;
+ int code;
+
+ rect.x = x, rect.y = y;
+ rect.width = w1, rect.height = height;
+ rsize = (dx ? 3 : 1) + cmd_size_rect(&rect);
+ TRY_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);
+ } HANDLE_RECT_UNLESS(code, code == gs_error_limitcheck);
+ compress = (uint)code;
+ if (code < 0) {
+ /* The bitmap was too large; split up the transfer. */
+ if (height > 1) {
+ /*
+ * Split the transfer by reducing the height.
+ * See the comment above FOR_RECTS in gxcldev.h.
+ */
+ height >>= 1;
+ goto copy;
+ } else {
+ /* Split a single (very long) row. */
+ int w2 = w1 >> 1;
+
+ NEST_RECT {
+ code = clist_copy_mono(dev, row, dx,
+ raster, gx_no_bitmap_id, x, y,
+ w2, 1, color0, color1);
+ if (code >= 0)
+ code = clist_copy_mono(dev, row, dx + w2,
+ raster, gx_no_bitmap_id,
+ x + w2, y,
+ w1 - w2, 1, color0, color1);
+ } UNNEST_RECT;
+ if (code < 0)
+ ERROR_RECT(code);
+ continue;
+ }
+ }
+ op += compress;
+ if (dx) {
+ *dp++ = cmd_count_op(cmd_opv_set_misc, 2);
+ *dp++ = cmd_set_misc_data_x + dx;
+ }
+ *dp++ = cmd_count_op(op, csize);
+ cmd_put2w(x, y, dp);
+ cmd_put2w(w1, height, dp);
+ pcls->rect = rect;
+ }
+ } END_RECTS;
+ return 0;
+}
+
+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)
+{
+ gx_device_clist_writer * const cdev =
+ &((gx_device_clist *)dev)->writer;
+ 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;
+ FOR_RECTS {
+ int dx = (data_x_bit & 7) / depth;
+ int w1 = dx + width;
+ const byte *row = data + (y - y0) * raster + (data_x_bit >> 3);
+ int code;
+
+ TRY_RECT {
+ code = cmd_disable_lop(cdev, pcls);
+ if (code >= 0)
+ code = cmd_disable_clip(cdev, pcls);
+ } HANDLE_RECT(code);
+ if (pcls->color_is_alpha) {
+ byte *dp;
+
+ TRY_RECT {
+ code =
+ set_cmd_put_op(dp, cdev, pcls, cmd_opv_set_copy_color, 1);
+ } HANDLE_RECT(code);
+ pcls->color_is_alpha = 0;
+ }
+copy:{
+ gx_cmd_rect rect;
+ int rsize;
+ byte op = (byte) cmd_op_copy_color_alpha;
+ byte *dp;
+ uint csize;
+ uint compress;
+
+ rect.x = x, rect.y = y;
+ rect.width = w1, rect.height = height;
+ rsize = (dx ? 3 : 1) + cmd_size_rect(&rect);
+ TRY_RECT {
+ code = cmd_put_bits(cdev, pcls, row, w1 * depth,
+ height, raster, rsize,
+ 1 << cmd_compress_rle, &dp, &csize);
+ } HANDLE_RECT_UNLESS(code, code == gs_error_limitcheck);
+ compress = (uint)code;
+ if (code < 0) {
+ /* The bitmap was too large; split up the transfer. */
+ if (height > 1) {
+ /* Split the transfer by reducing the height.
+ * See the comment above FOR_RECTS in gxcldev.h.
+ */
+ height >>= 1;
+ goto copy;
+ } else {
+ /* Split a single (very long) row. */
+ int w2 = w1 >> 1;
+
+ NEST_RECT {
+ code = clist_copy_color(dev, row, dx,
+ raster, gx_no_bitmap_id,
+ x, y, w2, 1);
+ if (code >= 0)
+ code = clist_copy_color(dev, row, dx + w2,
+ raster, gx_no_bitmap_id,
+ x + w2, y, w1 - w2, 1);
+ } UNNEST_RECT;
+ if (code < 0)
+ ERROR_RECT(code);
+ continue;
+ }
+ }
+ op += compress;
+ if (dx) {
+ *dp++ = cmd_count_op(cmd_opv_set_misc, 2);
+ *dp++ = cmd_set_misc_data_x + dx;
+ }
+ *dp++ = cmd_count_op(op, csize);
+ cmd_put2w(x, y, dp);
+ cmd_put2w(w1, height, dp);
+ pcls->rect = rect;
+ }
+ } END_RECTS;
+ return 0;
+}
+
+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)
+{
+ gx_device_clist_writer * const cdev =
+ &((gx_device_clist *)dev)->writer;
+ /* 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;
+ FOR_RECTS {
+ 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;
+
+ TRY_RECT {
+ code = cmd_disable_lop(cdev, pcls);
+ if (code >= 0)
+ code = cmd_disable_clip(cdev, pcls);
+ } HANDLE_RECT(code);
+ if (!pcls->color_is_alpha) {
+ byte *dp;
+
+ TRY_RECT {
+ code =
+ set_cmd_put_op(dp, cdev, pcls, cmd_opv_set_copy_alpha, 1);
+ } HANDLE_RECT(code);
+ pcls->color_is_alpha = 1;
+ }
+ if (color != pcls->colors[1]) {
+ TRY_RECT {
+ code = cmd_set_color1(cdev, pcls, color);
+ } HANDLE_RECT(code);
+ }
+copy:{
+ gx_cmd_rect rect;
+ int rsize;
+ byte op = (byte) cmd_op_copy_color_alpha;
+ byte *dp;
+ uint csize;
+ uint compress;
+
+ rect.x = x, rect.y = y;
+ rect.width = w1, rect.height = height;
+ rsize = (dx ? 4 : 2) + cmd_size_rect(&rect);
+ TRY_RECT {
+ code = cmd_put_bits(cdev, pcls, row, w1 << log2_depth,
+ height, raster, rsize,
+ 1 << cmd_compress_rle, &dp, &csize);
+ } HANDLE_RECT_UNLESS(code, code == gs_error_limitcheck);
+ compress = (uint)code;
+ if (code < 0) {
+ /* The bitmap was too large; split up the transfer. */
+ if (height > 1) {
+ /* Split the transfer by reducing the height.
+ * See the comment above FOR_RECTS in gxcldev.h.
+ */
+ height >>= 1;
+ goto copy;
+ } else {
+ /* Split a single (very long) row. */
+ int w2 = w1 >> 1;
+
+ NEST_RECT {
+ code = clist_copy_alpha(dev, row, dx,
+ raster, gx_no_bitmap_id, x, y,
+ w2, 1, color, depth);
+ if (code >= 0)
+ code = clist_copy_alpha(dev, row, dx + w2,
+ raster, gx_no_bitmap_id,
+ x + w2, y, w1 - w2, 1,
+ color, depth);
+ } UNNEST_RECT;
+ if (code < 0)
+ ERROR_RECT(code);
+ continue;
+ }
+ }
+ op += compress;
+ if (dx) {
+ *dp++ = cmd_count_op(cmd_opv_set_misc, 2);
+ *dp++ = cmd_set_misc_data_x + dx;
+ }
+ *dp++ = cmd_count_op(op, csize);
+ *dp++ = depth;
+ cmd_put2w(x, y, dp);
+ cmd_put2w(w1, height, dp);
+ pcls->rect = rect;
+ }
+ } END_RECTS;
+ return 0;
+}
+
+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)
+{
+ gx_device_clist_writer * const cdev =
+ &((gx_device_clist *)dev)->writer;
+ gs_rop3_t rop = lop_rop(lop);
+ gx_strip_bitmap tile_with_id;
+ const gx_strip_bitmap *tiles = textures;
+ int y0;
+
+ 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);
+ }
+ y0 = y;
+ /*
+ * We shouldn't need to put the logic below inside FOR/END_RECTS,
+ * but the lop_enabled flags are per-band.
+ */
+ FOR_RECTS {
+ const byte *row = sdata + (y - y0) * sraster;
+ int code;
+
+ 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;
+ }
+ TRY_RECT {
+ code = clist_change_tile(cdev, pcls, tiles,
+ (tcolors != 0 ? 1 :
+ dev->color_info.depth));
+ } HANDLE_RECT_UNLESS(code, code == gs_error_limitcheck);
+ if (code < 0) {
+ /*
+ * The error is a limitcheck: we have a tile that
+ * is too big to fit in the command reading buffer.
+ * For now, just divide up the transfer into scan
+ * lines. (If a single scan line won't fit, punt.)
+ * Eventually, we'll need a way to transfer the tile
+ * in pieces.
+ */
+ uint rep_height = tiles->rep_height;
+ gs_id ids;
+ gx_strip_bitmap line_tile;
+ int iy;
+
+ if (rep_height == 1 ||
+ /****** CAN'T HANDLE SHIFT YET ******/
+ tiles->rep_shift != 0
+ )
+ return code;
+ /*
+ * Allocate enough fake IDs, since the inner call on
+ * clist_strip_copy_rop will need them anyway.
+ */
+ ids = gs_next_ids(min(height, rep_height));
+ line_tile = *tiles;
+ line_tile.size.y = 1;
+ line_tile.rep_height = 1;
+ for (iy = 0; iy < height; ++iy) {
+ line_tile.data = tiles->data + line_tile.raster *
+ ((y + iy + phase_y) % rep_height);
+ line_tile.id = ids + (iy % rep_height);
+ /*
+ * Note that since we're only transferring
+ * a single scan line, phase_y is irrelevant;
+ * we may as well use the current tile phase
+ * so we don't have to write extra commands.
+ */
+ NEST_RECT {
+ code = clist_strip_copy_rop(dev,
+ (sdata == 0 ? 0 : row + iy * sraster),
+ sourcex, sraster,
+ gx_no_bitmap_id, scolors,
+ &line_tile, tcolors,
+ x, y + iy, width, 1,
+ phase_x, pcls->tile_phase.y, lop);
+ } UNNEST_RECT;
+ if (code < 0)
+ ERROR_RECT(code);
+ }
+ continue;
+ }
+ if (phase_x != pcls->tile_phase.x ||
+ phase_y != pcls->tile_phase.y
+ ) {
+ TRY_RECT {
+ code = cmd_set_tile_phase(cdev, pcls, phase_x,
+ phase_y);
+ } HANDLE_RECT(code);
+ }
+ }
+ }
+ /* Set the tile colors. */
+ TRY_RECT {
+ 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));
+ } HANDLE_RECT(code);
+ }
+ TRY_RECT {
+ code = 0;
+ if (lop != pcls->lop)
+ code = cmd_set_lop(cdev, pcls, lop);
+ if (code >= 0)
+ code = cmd_enable_lop(cdev, pcls);
+ } HANDLE_RECT(code);
+
+ /* Set lop_enabled to -1 so that fill_rectangle / copy_* */
+ /* won't attempt to set it to 0. */
+ pcls->lop_enabled = -1;
+ NEST_RECT {
+ 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, row, sourcex, sraster, id,
+ x, y, width, height,
+ scolors[0], scolors[1]);
+ } else
+ code = clist_copy_color(dev, row, sourcex, sraster, id,
+ x, y, width, height);
+ } UNNEST_RECT;
+ pcls->lop_enabled = 1;
+ if (code < 0)
+ ERROR_RECT(code);
+ } END_RECTS;
+ return 0;
+}
diff --git a/pstoraster/gxclutil.c b/pstoraster/gxclutil.c
new file mode 100644
index 000000000..5bb9d64a5
--- /dev/null
+++ b/pstoraster/gxclutil.c
@@ -0,0 +1,616 @@
+/* Copyright (C) 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Command list writing utilities. */
+
+#include "memory_.h"
+#include "string_.h"
+#include "gx.h"
+#include "gp.h"
+#include "gpcheck.h"
+#include "gserrors.h"
+#include "gxdevice.h"
+#include "gxdevmem.h" /* must precede gxcldev.h */
+#include "gxcldev.h"
+#include "gxclpath.h"
+#include "gsparams.h"
+
+/* ---------------- Statistics ---------------- */
+
+#ifdef DEBUG
+const char *const cmd_op_names[16] =
+{cmd_op_name_strings};
+private const char *const cmd_misc_op_names[16] =
+{cmd_misc_op_name_strings};
+private const char *const cmd_misc2_op_names[16] =
+{cmd_misc2_op_name_strings};
+private const char *const cmd_segment_op_names[16] =
+{cmd_segment_op_name_strings};
+private const char *const cmd_path_op_names[16] =
+{cmd_path_op_name_strings};
+const char *const *const cmd_sub_op_names[16] =
+{cmd_misc_op_names, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, cmd_misc2_op_names, cmd_segment_op_names, cmd_path_op_names
+};
+struct stats_cmd_s {
+ ulong op_counts[256];
+ ulong op_sizes[256];
+ ulong tile_reset, tile_found, tile_added;
+ ulong same_band, other_band;
+} stats_cmd;
+extern ulong stats_cmd_diffs[5]; /* in gxclpath.c */
+int
+cmd_count_op(int op, uint size)
+{
+ stats_cmd.op_counts[op]++;
+ stats_cmd.op_sizes[op] += size;
+ if (gs_debug_c('L')) {
+ const char *const *sub = cmd_sub_op_names[op >> 4];
+
+ if (sub)
+ dlprintf2(", %s(%u)\n", sub[op & 0xf], size);
+ else
+ dlprintf3(", %s %d(%u)\n", cmd_op_names[op >> 4], op & 0xf,
+ size);
+ fflush(dstderr);
+ }
+ return op;
+}
+void
+cmd_uncount_op(int op, uint size)
+{
+ stats_cmd.op_counts[op]--;
+ stats_cmd.op_sizes[op] -= size;
+}
+#endif
+
+/* Print statistics. */
+#ifdef DEBUG
+void
+cmd_print_stats(void)
+{
+ int ci, cj;
+
+ dlprintf3("[l]counts: reset = %lu, found = %lu, added = %lu\n",
+ stats_cmd.tile_reset, stats_cmd.tile_found,
+ stats_cmd.tile_added);
+ dlprintf5(" diff 2.5 = %lu, 3 = %lu, 4 = %lu, 2 = %lu, >4 = %lu\n",
+ stats_cmd_diffs[0], stats_cmd_diffs[1], stats_cmd_diffs[2],
+ stats_cmd_diffs[3], stats_cmd_diffs[4]);
+ dlprintf2(" same_band = %lu, other_band = %lu\n",
+ stats_cmd.same_band, stats_cmd.other_band);
+ for (ci = 0; ci < 0x100; ci += 0x10) {
+ const char *const *sub = cmd_sub_op_names[ci >> 4];
+
+ if (sub != 0) {
+ dlprintf1("[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],
+ stats_cmd.op_counts[cj], stats_cmd.op_sizes[cj],
+ sub[cj - ci + 1],
+ stats_cmd.op_counts[cj + 1], stats_cmd.op_sizes[cj + 1]);
+ } else {
+ ulong tcounts = 0, tsizes = 0;
+
+ for (cj = ci; cj < ci + 0x10; cj++)
+ tcounts += stats_cmd.op_counts[cj],
+ tsizes += stats_cmd.op_sizes[cj];
+ dlprintf3("[l] %s (%lu,%lu) =\n\t",
+ cmd_op_names[ci >> 4], tcounts, tsizes);
+ for (cj = ci; cj < ci + 0x10; cj++)
+ if (stats_cmd.op_counts[cj] == 0)
+ dputs(" -");
+ else
+ dprintf2(" %lu(%lu)", stats_cmd.op_counts[cj],
+ stats_cmd.op_sizes[cj]);
+ }
+ dputs("\n");
+ }
+}
+#endif /* DEBUG */
+
+/* ---------------- Writing utilities ---------------- */
+
+/* Write the commands for one band or band range. */
+private int /* ret 0 all ok, -ve error code, or +1 ok w/low-mem warning */
+cmd_write_band(gx_device_clist_writer * cldev, int band_min, int band_max,
+ cmd_list * pcl, byte cmd_end)
+{
+ const cmd_prefix *cp = pcl->head;
+ int code_b = 0;
+ int code_c = 0;
+
+ if (cp != 0 || cmd_end != cmd_opv_end_run) {
+ clist_file_ptr cfile = cldev->page_cfile;
+ clist_file_ptr bfile = cldev->page_bfile;
+ cmd_block cb;
+ byte end = cmd_count_op(cmd_end, 1);
+
+ if (cfile == 0 || bfile == 0)
+ return_error(gs_error_ioerror);
+ cb.band_min = band_min;
+ cb.band_max = band_max;
+ cb.pos = clist_ftell(cfile);
+ if_debug3('l', "[l]writing for bands (%d,%d) at %ld\n",
+ band_min, band_max, cb.pos);
+ clist_fwrite_chars(&cb, sizeof(cb), bfile);
+ if (cp != 0) {
+ 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();
+ code_b = clist_ferror_code(bfile);
+ code_c = clist_ferror_code(cfile);
+ if (code_b < 0)
+ return_error(code_b);
+ if (code_c < 0)
+ return_error(code_c);
+ }
+ return code_b | code_c;
+}
+
+/* Write out the buffered commands, and reset the buffer. */
+int /* ret 0 all-ok, -ve error code, or +1 ok w/low-mem warning */
+cmd_write_buffer(gx_device_clist_writer * cldev, byte cmd_end)
+{
+ int nbands = cldev->nbands;
+ gx_clist_state *pcls;
+ int band;
+ int code = cmd_write_band(cldev, cldev->band_range_min,
+ cldev->band_range_max,
+ &cldev->band_range_list, cmd_opv_end_run);
+ int warning = code;
+
+ for (band = 0, pcls = cldev->states;
+ code >= 0 && band < nbands; band++, pcls++
+ ) {
+ code = cmd_write_band(cldev, band, band, &pcls->list, cmd_end);
+ warning |= code;
+ }
+ /* If an error occurred, finish cleaning up the pointers. */
+ for (; band < nbands; band++, pcls++)
+ pcls->list.head = pcls->list.tail = 0;
+ cldev->cnext = cldev->cbuf;
+ cldev->ccl = 0;
+#ifdef DEBUG
+ if (gs_debug_c('l'))
+ cmd_print_stats();
+#endif
+ return_check_interrupt(code != 0 ? code : warning);
+}
+
+/*
+ * Add a command to the appropriate band list, and allocate space for its
+ * data. Return the pointer to the data area. If an error or (low-memory
+ * warning) 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) {
+ if ((cldev->error_code =
+ cmd_write_buffer(cldev, cmd_opv_end_run)) != 0) {
+ if (cldev->error_code < 0)
+ cldev->error_is_retryable = 0; /* hard error */
+ else {
+ /* upgrade lo-mem warning into an error */
+ if (!cldev->ignore_lo_mem_warnings)
+ cldev->error_code = gs_note_error(gs_error_VMerror);
+ cldev->error_is_retryable = 1;
+ }
+ return 0;
+ }
+ else
+ 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(stats_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(stats_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 a range of bands. */
+byte *
+cmd_put_range_op(gx_device_clist_writer * cldev, int band_min, int band_max,
+ uint size)
+{
+ if_debug4('L', "[L]band range(%d,%d): size=%u, left=%u",
+ band_min, band_max, size,
+ (uint)(cldev->cend - cldev->cnext));
+ if (cldev->ccl != 0 &&
+ (cldev->ccl != &cldev->band_range_list ||
+ band_min != cldev->band_range_min ||
+ band_max != cldev->band_range_max)
+ ) {
+ if ((cldev->error_code = cmd_write_buffer(cldev, cmd_opv_end_run)) != 0) {
+ if (cldev->error_code < 0)
+ cldev->error_is_retryable = 0; /* hard error */
+ else {
+ /* upgrade lo-mem warning into an error */
+ cldev->error_code = gs_error_VMerror;
+ cldev->error_is_retryable = 1;
+ }
+ return 0;
+ }
+ cldev->band_range_min = band_min;
+ cldev->band_range_max = band_max;
+ }
+ return cmd_put_list_op(cldev, &cldev->band_range_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;
+}
+
+/* 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;
+ int code;
+
+ if (diff == 0)
+ return 0;
+ if (select->tile_color) {
+ code = set_cmd_put_op(dp, cldev, pcls, cmd_opv_set_tile_color, 1);
+ if (code < 0)
+ return code;
+ }
+ 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.
+ */
+ code = set_cmd_put_op(dp, cldev, pcls, op + 15, 1);
+ if (code < 0)
+ return code;
+ } 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
+ ) {
+ code = set_cmd_put_op(dp, cldev, pcls,
+ (byte) (op + operand), 2);
+ if (code < 0)
+ return code;
+ dp[1] = (byte) (((delta >> 10) & 0300) +
+ (delta >> 5) + delta);
+ break;
+ }
+ if (!((delta = diff + cmd_delta2_32_bias) &
+ ~cmd_delta2_32_mask)
+ ) {
+ code = set_cmd_put_op(dp, cldev, pcls, op_delta2, 3);
+ if (code < 0)
+ return code;
+ dp[1] = (byte) ((delta >> 20) + (delta >> 16));
+ dp[2] = (byte) ((delta >> 4) + delta);
+ break;
+ }
+ code = set_cmd_put_op(dp, cldev, pcls, op, 5);
+ if (code < 0)
+ return code;
+ *++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
+ ) {
+ code = set_cmd_put_op(dp, cldev, pcls,
+ (byte) (op + operand), 2);
+ if (code < 0)
+ return code;
+ dp[1] = (byte) ((delta >> 4) + delta);
+ break;
+ }
+ if (!((delta = diff + cmd_delta2_24_bias) &
+ ~cmd_delta2_24_mask)
+ ) {
+ code = set_cmd_put_op(dp, cldev, pcls, op_delta2, 3);
+ if (code < 0)
+ return code;
+ dp[1] = ((byte) (delta >> 13) & 0xf8) +
+ ((byte) (delta >> 11) & 7);
+ dp[2] = (byte) (((delta >> 3) & 0xe0) + delta);
+ break;
+ }
+ code = set_cmd_put_op(dp, cldev, pcls, op, 4);
+ if (code < 0)
+ return code;
+b3: *++dp = (byte) (color >> 16);
+ goto b2;
+ case 3:
+ code = set_cmd_put_op(dp, cldev, pcls, op, 3);
+ if (code < 0)
+ return code;
+b2: *++dp = (byte) (color >> 8);
+ goto b1;
+ case 2:
+ if (diff >= -7 && diff < 7) {
+ code = set_cmd_put_op(dp, cldev, pcls,
+ op + (int)diff + 8, 1);
+ if (code < 0)
+ return code;
+ break;
+ }
+ code = set_cmd_put_op(dp, cldev, pcls, op, 2);
+ if (code < 0)
+ return code;
+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)
+{
+ int code = 0;
+
+ if (color0 != pcls->tile_colors[0]) {
+ 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])
+ code = cmd_put_color(cldev, pcls,
+ &clist_select_tile_color1,
+ color1, &pcls->tile_colors[1]);
+ return code;
+}
+
+/* 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;
+ int code;
+
+ pcsize = 1 + cmd_size2w(px, py);
+ code =
+ set_cmd_put_op(dp, cldev, pcls, (byte)cmd_opv_set_tile_phase, pcsize);
+ if (code < 0)
+ return code;
+ ++dp;
+ cmd_putxy(pcls->tile_phase, dp);
+ pcls->tile_phase.x = px;
+ pcls->tile_phase.y = py;
+ 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;
+ int code = set_cmd_put_op(dp, cldev, pcls,
+ (byte)(enable ? cmd_opv_enable_lop :
+ cmd_opv_disable_lop),
+ 1);
+
+ if (code < 0)
+ return code;
+ 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;
+ int code = set_cmd_put_op(dp, cldev, pcls,
+ (byte)(enable ? cmd_opv_enable_clip :
+ cmd_opv_disable_clip),
+ 1);
+
+ if (code < 0)
+ return code;
+ 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;
+ uint lop_msb = lop >> 6;
+ int code = set_cmd_put_op(dp, cldev, pcls,
+ cmd_opv_set_misc, 2 + cmd_size_w(lop_msb));
+
+ if (code < 0)
+ return code;
+ dp[1] = cmd_set_misc_lop + (lop & 0x3f);
+ cmd_put_w(lop_msb, dp + 2);
+ pcls->lop = lop;
+ return 0;
+}
+
+/* Disable (if default) or enable the logical operation, setting it if */
+/* needed. */
+int
+cmd_update_lop(gx_device_clist_writer *cldev, gx_clist_state *pcls,
+ gs_logical_operation_t lop)
+{
+ int code;
+
+ if (lop == lop_default)
+ return cmd_disable_lop(cldev, pcls);
+ code = cmd_set_lop(cldev, pcls, lop);
+ if (code < 0)
+ return code;
+ return cmd_enable_lop(cldev, pcls);
+}
+
+/* Write a parameter list */
+int /* ret 0 all ok, -ve error */
+cmd_put_params(gx_device_clist_writer *cldev,
+ gs_param_list *param_list) /* NB open for READ */
+{
+ byte *dp;
+ int code;
+ byte local_buf[512]; /* arbitrary */
+ int param_length;
+
+ /* Get serialized list's length + try to get it into local var if it fits. */
+ param_length = code =
+ gs_param_list_serialize(param_list, local_buf, sizeof(local_buf));
+ if (param_length > 0) {
+ /* Get cmd buffer space for serialized */
+ code = set_cmd_put_all_op(dp, cldev, cmd_opv_put_params,
+ 1 + sizeof(unsigned) + param_length);
+ if (code < 0)
+ return code;
+
+ /* write param list to cmd list: needs to all fit in cmd buffer */
+ if_debug1('l', "[l]put_params, length=%d\n", param_length);
+ ++dp;
+ memcpy(dp, &param_length, sizeof(unsigned));
+ dp += sizeof(unsigned);
+ if (param_length > sizeof(local_buf)) {
+ int old_param_length = param_length;
+
+ param_length = code =
+ gs_param_list_serialize(param_list, dp, old_param_length);
+ if (param_length >= 0)
+ code = (old_param_length != param_length ?
+ gs_note_error(gs_error_unknownerror) : 0);
+ if (code < 0) {
+ /* error serializing: back out by writing a 0-length parm list */
+ memset(dp - sizeof(unsigned), 0, sizeof(unsigned));
+ cmd_shorten_list_op(cldev, &cldev->band_range_list,
+ old_param_length);
+ }
+ } else
+ memcpy(dp, local_buf, param_length); /* did this when computing length */
+ }
+ return code;
+}
diff --git a/pstoraster/gxclzlib.c b/pstoraster/gxclzlib.c
new file mode 100644
index 000000000..885ab4e77
--- /dev/null
+++ b/pstoraster/gxclzlib.c
@@ -0,0 +1,64 @@
+/*
+ Copyright 1993-2000 by Easy Software Products
+ Copyright (C) 1997 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+#include <config.h>
+#ifdef HAVE_LIBZ
+/*$Id$ */
+/* zlib filter initialization for RAM-based band lists */
+/* Must be compiled with -I$(ZSRCDIR) */
+#include "std.h"
+#include "gstypes.h"
+#include "gsmemory.h"
+#include "gxclmem.h"
+#include "szlibx.h"
+
+private stream_zlib_state cl_zlibE_state;
+private stream_zlib_state cl_zlibD_state;
+
+/* Initialize the states to be copied. */
+void
+gs_cl_zlib_init(gs_memory_t * mem)
+{
+ s_zlib_set_defaults((stream_state *) & cl_zlibE_state);
+ cl_zlibE_state.no_wrapper = true;
+ cl_zlibE_state.template = &s_zlibE_template;
+ s_zlib_set_defaults((stream_state *) & cl_zlibD_state);
+ cl_zlibD_state.no_wrapper = true;
+ cl_zlibD_state.template = &s_zlibD_template;
+}
+
+/* Return the prototypes for compressing/decompressing the band list. */
+const stream_state *
+clist_compressor_state(void *client_data)
+{
+ return (const stream_state *)&cl_zlibE_state;
+}
+const stream_state *
+clist_decompressor_state(void *client_data)
+{
+ return (const stream_state *)&cl_zlibD_state;
+}
+#endif /* HAVE_LIBZ */
diff --git a/pstoraster/gxcmap.c b/pstoraster/gxcmap.c
new file mode 100644
index 000000000..99ec9c5ed
--- /dev/null
+++ b/pstoraster/gxcmap.c
@@ -0,0 +1,890 @@
+/* Copyright (C) 1992, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Color mapping for Ghostscript */
+#include "gx.h"
+#include "gserrors.h"
+#include "gsccolor.h"
+#include "gxalpha.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)
+{
+ return ENUM_USING(*cptr->type->stype, vptr, size, index);
+}
+ENUM_PTRS_END
+private RELOC_PTRS_BEGIN(device_color_reloc_ptrs)
+{
+ RELOC_USING(*cptr->type->stype, vptr, size);
+}
+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 cmap_proc_rgb_alpha(cmap_rgb_alpha_halftoned);
+private cmap_proc_rgb_alpha(cmap_rgb_alpha_direct);
+
+/* Procedure names are only guaranteed unique to 23 characters.... */
+private cmap_proc_rgb_alpha(cmap_rgb_alpha2gray_halftoned);
+private cmap_proc_rgb_alpha(cmap_rgb_alpha2gray_direct);
+private cmap_proc_rgb_alpha(cmap_rgb_alpha_to_cmyk);
+
+private const gx_color_map_procs
+ cmap_gray_few =
+{cmap_gray_halftoned, cmap_rgb_to_gray_halftoned, cmap_cmyk_to_gray,
+ cmap_rgb_alpha2gray_halftoned
+}, cmap_gray_many =
+{cmap_gray_direct, cmap_rgb_to_gray_direct, cmap_cmyk_to_gray,
+ cmap_rgb_alpha2gray_direct
+}, cmap_rgb_few =
+{cmap_gray_to_rgb_halftoned, cmap_rgb_halftoned, cmap_cmyk_to_rgb,
+ cmap_rgb_alpha_halftoned
+}, cmap_rgb_many =
+{cmap_gray_to_rgb_direct, cmap_rgb_direct, cmap_cmyk_to_rgb,
+ cmap_rgb_alpha_direct
+}, cmap_cmyk_few =
+{cmap_gray_to_cmyk_halftoned, cmap_rgb_to_cmyk, cmap_cmyk_halftoned,
+ cmap_rgb_alpha_to_cmyk
+}, cmap_cmyk_many =
+{cmap_gray_to_cmyk_direct, cmap_rgb_to_cmyk, cmap_cmyk_direct,
+ cmap_rgb_alpha_to_cmyk
+};
+
+const gx_color_map_procs *const cmap_procs_default = &cmap_gray_many;
+
+private const gx_color_map_procs *const cmap_few[] =
+{
+ 0, &cmap_gray_few, 0, &cmap_rgb_few, &cmap_cmyk_few
+};
+
+private const gx_color_map_procs *const 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_must_halftone(dev) ? cmap_few : cmap_many)
+ [dev->color_info.num_components];
+}
+
+/* Set the color mapping procedures in the graphics state. */
+void
+gx_set_cmap_procs(gs_imager_state * pis, const gx_device * dev)
+{
+ pis->cmap_procs = gx_device_cmap_procs(dev);
+}
+
+/* Remap the color in the graphics state. */
+int
+gx_remap_color(gs_state * pgs)
+{
+ const gs_color_space *pcs = pgs->color_space;
+
+ /* The current color in the graphics state is always used for */
+ /* the texture, never for the source. */
+ return (*pcs->type->remap_color) (pgs->ccolor, pcs, pgs->dev_color,
+ (gs_imager_state *) pgs, pgs->device,
+ gs_color_select_texture);
+}
+
+/* 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_imager_state * pis)
+{
+ return NULL;
+}
+
+/* Indicate that a color space is concrete. */
+const gs_color_space *
+gx_same_concrete_space(const gs_color_space * pcs, const gs_imager_state * pis)
+{
+ 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_imager_state * pis)
+{
+ 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_imager_state * pis, gx_device * dev,
+ gs_color_select_t select)
+{
+ frac conc[4];
+ const gs_color_space *pconcs;
+ int code = (*pcs->type->concretize_color) (pcc, pcs, conc, pis);
+
+ if (code < 0)
+ return code;
+ pconcs = cs_concrete_space(pcs, pis);
+ return (*pconcs->type->remap_concrete_color) (conc, pdc, pis, dev,
+ select);
+}
+
+/* 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_imager_state * pis)
+{
+ 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_imager_state * pis, gx_device * dev,
+ gs_color_select_t select)
+{
+ if (pis->alpha == gx_max_color_value)
+ (*pis->cmap_procs->map_gray)
+ (pconc[0], pdc, pis, dev, select);
+ else
+ (*pis->cmap_procs->map_rgb_alpha)
+ (pconc[0], pconc[0], pconc[0], cv2frac(pis->alpha),
+ pdc, pis, dev, select);
+ return 0;
+}
+int
+gx_remap_DeviceGray(const gs_client_color * pc, const gs_color_space * pcs,
+ gx_device_color * pdc, const gs_imager_state * pis, gx_device * dev,
+ gs_color_select_t select)
+{
+ float ftemp;
+ frac fgray = unit_frac(pc->paint.values[0], ftemp);
+
+ if (pis->alpha == gx_max_color_value)
+ (*pis->cmap_procs->map_gray)
+ (fgray, pdc, pis, dev, select);
+ else
+ (*pis->cmap_procs->map_rgb_alpha)
+ (fgray, fgray, fgray, cv2frac(pis->alpha), pdc, pis, dev, select);
+ return 0;
+}
+
+/* DeviceRGB */
+int
+gx_concretize_DeviceRGB(const gs_client_color * pc, const gs_color_space * pcs,
+ frac * pconc, const gs_imager_state * pis)
+{
+ 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_imager_state * pis, gx_device * dev,
+ gs_color_select_t select)
+{
+ if (pis->alpha == gx_max_color_value)
+ gx_remap_concrete_rgb(pconc[0], pconc[1], pconc[2],
+ pdc, pis, dev, select);
+ else
+ gx_remap_concrete_rgb_alpha(pconc[0], pconc[1], pconc[2],
+ cv2frac(pis->alpha),
+ pdc, pis, dev, select);
+ return 0;
+}
+int
+gx_remap_DeviceRGB(const gs_client_color * pc, const gs_color_space * pcs,
+ gx_device_color * pdc, const gs_imager_state * pis, gx_device * dev,
+ gs_color_select_t select)
+{
+ float ftemp;
+ frac fred = unit_frac(pc->paint.values[0], ftemp), fgreen = unit_frac(pc->paint.values[1], ftemp),
+ fblue = unit_frac(pc->paint.values[2], ftemp);
+
+ if (pis->alpha == gx_max_color_value)
+ gx_remap_concrete_rgb(fred, fgreen, fblue,
+ pdc, pis, dev, select);
+ else
+ gx_remap_concrete_rgb_alpha(fred, fgreen, fblue, cv2frac(pis->alpha),
+ pdc, pis, dev, select);
+ return 0;
+}
+
+/* DeviceCMYK */
+int
+gx_concretize_DeviceCMYK(const gs_client_color * pc, const gs_color_space * pcs,
+ frac * pconc, const gs_imager_state * pis)
+{
+ 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_imager_state * pis, gx_device * dev,
+ gs_color_select_t select)
+{
+/****** IGNORE alpha ******/
+ gx_remap_concrete_cmyk(pconc[0], pconc[1], pconc[2], pconc[3], pdc,
+ pis, dev, select);
+ return 0;
+}
+int
+gx_remap_DeviceCMYK(const gs_client_color * pc, const gs_color_space * pcs,
+ gx_device_color * pdc, const gs_imager_state * pis, gx_device * dev,
+ gs_color_select_t select)
+{
+/****** IGNORE alpha ******/
+ 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, pis, dev, select);
+ return 0;
+}
+
+/* ------ Render Gray color. ------ */
+
+private void
+cmap_gray_halftoned(frac gray, gx_device_color * pdc,
+ const gs_imager_state * pis, gx_device * dev, gs_color_select_t select)
+{
+ if (gx_render_gray(gx_map_color_frac(pis, gray, effective_transfer.colored.gray), pdc, pis, dev, select) == 1)
+ gx_color_load_select(pdc, pis, dev, select);
+}
+
+private void
+cmap_gray_direct(frac gray, gx_device_color * pdc, const gs_imager_state * pis,
+ gx_device * dev, gs_color_select_t select)
+{
+ frac mgray = gx_map_color_frac(pis, gray, effective_transfer.colored.gray);
+ gx_color_value cv_gray = frac2cv(mgray);
+ gx_color_index color =
+ (pis->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, pis->alpha));
+
+ if (color == gx_no_color_index) {
+ if (gx_render_gray(mgray, pdc, pis, dev, select) == 1)
+ gx_color_load_select(pdc, pis, dev, select);
+ return;
+ }
+ color_set_pure(pdc, color);
+}
+
+private void
+cmap_gray_to_rgb_halftoned(frac gray, gx_device_color * pdc,
+ const gs_imager_state * pis, gx_device * dev, gs_color_select_t select)
+{
+ cmap_rgb_halftoned(gray, gray, gray, pdc, pis, dev, select);
+}
+
+private void
+cmap_gray_to_rgb_direct(frac gray, gx_device_color * pdc,
+ const gs_imager_state * pis, gx_device * dev, gs_color_select_t select)
+{
+ cmap_rgb_direct(gray, gray, gray, pdc, pis, dev, select);
+}
+
+private void
+cmap_gray_to_cmyk_halftoned(frac gray, gx_device_color * pdc,
+ const gs_imager_state * pis, gx_device * dev, gs_color_select_t select)
+{ /*
+ * 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(pis, gray, effective_transfer.colored.gray);
+
+ if (gx_render_gray(mgray, pdc, pis, dev, select) == 1)
+ gx_color_load_select(pdc, pis, dev, select);
+}
+
+private void
+cmap_gray_to_cmyk_direct(frac gray, gx_device_color * pdc,
+ const gs_imager_state * pis, gx_device * dev, gs_color_select_t select)
+{ /*
+ * 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(pis, gray, effective_transfer.colored.gray);
+ frac mblack = frac_1 - mgray;
+ 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, pis, dev, select) == 1)
+ gx_color_load_select(pdc, pis, dev, select);
+}
+
+/* ------ 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_imager_state * pis, gx_device * dev, gs_color_select_t select)
+{
+ frac mred = gx_map_color_frac(pis, r, effective_transfer.colored.red);
+ frac mgreen = gx_map_color_frac(pis, g, effective_transfer.colored.green);
+ frac mblue = gx_map_color_frac(pis, b, effective_transfer.colored.blue);
+
+ if ((mred == mgreen && mred == mblue ? /* gray shade */
+ gx_render_gray(mred, pdc, pis, dev, select) :
+ gx_render_rgb(mred, mgreen, mblue, pdc, pis, dev, select)) == 1)
+ gx_color_load_select(pdc, pis, dev, select);
+}
+
+private void
+cmap_rgb_direct(frac r, frac g, frac b, gx_device_color * pdc,
+ const gs_imager_state * pis, gx_device * dev, gs_color_select_t select)
+{
+ frac mred = gx_map_color_frac(pis, r, effective_transfer.colored.red);
+ frac mgreen = gx_map_color_frac(pis, g, effective_transfer.colored.green);
+ frac mblue = gx_map_color_frac(pis, b, effective_transfer.colored.blue);
+ gx_color_index color =
+ (pis->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), pis->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, pis, dev, select) :
+ gx_render_rgb(mred, mgreen, mblue, pdc, pis, dev, select)) == 1)
+ gx_color_load_select(pdc, pis, dev, select);
+}
+
+private void
+cmap_rgb_to_gray_halftoned(frac r, frac g, frac b, gx_device_color * pdc,
+ const gs_imager_state * pis, gx_device * dev, gs_color_select_t select)
+{
+ cmap_gray_halftoned(color_rgb_to_gray(r, g, b, pis), pdc, pis, dev, select);
+}
+
+private void
+cmap_rgb_to_gray_direct(frac r, frac g, frac b, gx_device_color * pdc,
+ const gs_imager_state * pis, gx_device * dev, gs_color_select_t select)
+{
+ cmap_gray_direct(color_rgb_to_gray(r, g, b, pis), pdc, pis, dev, select);
+}
+
+private void
+cmap_rgb_to_cmyk(frac r, frac g, frac b, gx_device_color * pdc,
+ const gs_imager_state * pis, gx_device * dev, gs_color_select_t select)
+{
+ frac cmyk[4];
+
+ color_rgb_to_cmyk(r, g, b, pis, cmyk);
+ (*pis->cmap_procs->map_cmyk) (cmyk[0], cmyk[1], cmyk[2], cmyk[3], pdc, pis, dev, select);
+}
+
+/* ------ Render CMYK color. ------ */
+
+private void
+cmap_cmyk_to_gray(frac c, frac m, frac y, frac k, gx_device_color * pdc,
+ const gs_imager_state * pis, gx_device * dev, gs_color_select_t select)
+{
+ (*pis->cmap_procs->map_gray) (color_cmyk_to_gray(c, m, y, k, pis), pdc, pis, dev, select);
+}
+
+private void
+cmap_cmyk_direct(frac c, frac m, frac y, frac k, gx_device_color * pdc,
+ const gs_imager_state * pis, gx_device * dev, gs_color_select_t select)
+{
+ frac mcyan = frac_1 - gx_map_color_frac(pis, frac_1 - c, effective_transfer.colored.red);
+ frac mmagenta = frac_1 - gx_map_color_frac(pis, frac_1 - m, effective_transfer.colored.green);
+ frac myellow = frac_1 - gx_map_color_frac(pis, frac_1 - y, effective_transfer.colored.blue);
+ frac mblack = frac_1 - gx_map_color_frac(pis, 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 (!gx_color_device_must_halftone(dev)) {
+ 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, pis, dev, select) == 1)
+ gx_color_load_select(pdc, pis, dev, select);
+}
+
+private void
+cmap_cmyk_to_rgb(frac c, frac m, frac y, frac k, gx_device_color * pdc,
+ const gs_imager_state * pis, gx_device * dev, gs_color_select_t select)
+{
+ frac rgb[3];
+
+ color_cmyk_to_rgb(c, m, y, k, pis, rgb);
+ (*pis->cmap_procs->map_rgb) (rgb[0], rgb[1], rgb[2], pdc, pis, dev, select);
+}
+
+/* ------ Render RGB+alpha color. ------ */
+
+#ifdef PREMULTIPLY_TOWARDS_WHITE
+# define alpha_bias_value frac_1 - alpha
+# define alpha_bias(v) ((v) + alpha_bias_value
+#else
+# define alpha_bias(v) (v)
+#endif
+
+private void
+cmap_rgb_alpha2gray_halftoned(frac r, frac g, frac b, frac alpha,
+ gx_device_color * pdc, const gs_imager_state * pis, gx_device * dev,
+ gs_color_select_t select)
+{
+ frac gray = color_rgb_to_gray(r, g, b, pis);
+
+ if (alpha != frac_1) /* premultiply */
+ gray = alpha_bias((frac) ((long)gray * alpha / frac_1));
+ if (gx_render_gray_alpha(gx_map_color_frac(pis, gray, effective_transfer.colored.gray), frac2cv(alpha), pdc, pis, dev, select) == 1)
+ gx_color_load_select(pdc, pis, dev, select);
+}
+
+private void
+cmap_rgb_alpha2gray_direct(frac r, frac g, frac b, frac alpha,
+ gx_device_color * pdc, const gs_imager_state * pis, gx_device * dev,
+ gs_color_select_t select)
+{
+ frac gray = color_rgb_to_gray(r, g, b, pis);
+
+ if (alpha != frac_1) /* premultiply */
+ gray = alpha_bias((frac) ((long)gray * alpha / frac_1));
+ {
+ frac mgray =
+ gx_map_color_frac(pis, gray, effective_transfer.colored.gray);
+ gx_color_value cv_gray = frac2cv(mgray);
+ gx_color_index color =
+ (alpha == frac_1 ?
+ gx_map_rgb_color(dev, cv_gray, cv_gray, cv_gray) :
+ gx_map_rgb_alpha_color(dev, cv_gray, cv_gray, cv_gray, frac2cv(alpha)));
+
+ if (color == gx_no_color_index) {
+ if (gx_render_gray_alpha(mgray, frac2cv(alpha), pdc, pis, dev, select) == 1)
+ gx_color_load_select(pdc, pis, dev, select);
+ return;
+ }
+ color_set_pure(pdc, color);
+ }
+}
+
+private void
+cmap_rgb_alpha_halftoned(frac r, frac g, frac b, frac alpha,
+ gx_device_color * pdc, const gs_imager_state * pis, gx_device * dev,
+ gs_color_select_t select)
+{
+ frac red = r, green = g, blue = b;
+
+ if (alpha != frac_1) { /* premultiply */
+ red = alpha_bias((frac) ((long)red * alpha / frac_1));
+ green = alpha_bias((frac) ((long)green * alpha / frac_1));
+ blue = alpha_bias((frac) ((long)blue * alpha / frac_1));
+ } {
+ frac mred =
+ gx_map_color_frac(pis, red, effective_transfer.colored.red);
+ frac mgreen =
+ gx_map_color_frac(pis, green, effective_transfer.colored.green);
+ frac mblue =
+ gx_map_color_frac(pis, blue, effective_transfer.colored.blue);
+ gx_color_value cv_alpha = frac2cv(alpha);
+
+ if ((mred == mgreen && mred == mblue ? /* gray shade */
+ gx_render_gray_alpha(mred, cv_alpha, pdc, pis, dev, select) :
+ gx_render_rgb_alpha(mred, mgreen, mblue, cv_alpha, pdc, pis, dev, select)) == 1)
+ gx_color_load_select(pdc, pis, dev, select);
+ }
+}
+
+private void
+cmap_rgb_alpha_direct(frac r, frac g, frac b, frac alpha, gx_device_color * pdc,
+ const gs_imager_state * pis, gx_device * dev, gs_color_select_t select)
+{
+ frac red = r, green = g, blue = b;
+
+ if (alpha != frac_1) { /* premultiply */
+ red = alpha_bias((frac) ((long)red * alpha / frac_1));
+ green = alpha_bias((frac) ((long)green * alpha / frac_1));
+ blue = alpha_bias((frac) ((long)blue * alpha / frac_1));
+ } {
+ frac mred =
+ gx_map_color_frac(pis, red, effective_transfer.colored.red);
+ frac mgreen =
+ gx_map_color_frac(pis, green, effective_transfer.colored.green);
+ frac mblue =
+ gx_map_color_frac(pis, blue, effective_transfer.colored.blue);
+ gx_color_value cv_alpha = frac2cv(alpha);
+ gx_color_index color =
+ (cv_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), cv_alpha));
+
+ if (color != gx_no_color_index) {
+ color_set_pure(pdc, color);
+ return;
+ }
+ if ((mred == mgreen && mred == mblue ? /* gray shade */
+ gx_render_gray_alpha(mred, cv_alpha, pdc, pis, dev, select) :
+ gx_render_rgb_alpha(mred, mgreen, mblue, cv_alpha, pdc, pis, dev, select)) == 1)
+ gx_color_load_select(pdc, pis, dev, select);
+ }
+}
+
+/* Currently CMYK devices can't handle alpha. */
+/* Just multiply the values towards white. */
+private void
+cmap_rgb_alpha_to_cmyk(frac r, frac g, frac b, frac alpha,
+ gx_device_color * pdc, const gs_imager_state * pis, gx_device * dev,
+ gs_color_select_t select)
+{
+#ifdef PREMULTIPLY_TOWARDS_WHITE
+# undef alpha_bias_value
+ frac alpha_bias_value = frac_1 - alpha;
+
+#endif
+
+ cmap_rgb_to_cmyk(alpha_bias((frac) ((long)r * alpha / frac_1)),
+ alpha_bias((frac) ((long)g * alpha / frac_1)),
+ alpha_bias((frac) ((long)b * alpha / frac_1)),
+ pdc, pis, dev, select);
+}
+
+#undef alpha_bias
+#undef alpha_bias_value
+
+/* ------ Transfer function mapping ------ */
+
+/* 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);
+}
+
+#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 / 2)) / 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 between RGB+alpha and 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)
+{ /* Colors have been premultiplied: we don't need to do it here. */
+ return gx_map_rgb_color(dev, r, g, b);
+}
+
+int
+gx_default_map_color_rgb_alpha(gx_device * dev, gx_color_index color,
+ gx_color_value prgba[4])
+{
+ prgba[3] = gx_max_color_value; /* alpha = 1 */
+ return (*dev_proc(dev, map_color_rgb)) (dev, color, prgba);
+}
diff --git a/pstoraster/gxcmap.h b/pstoraster/gxcmap.h
new file mode 100644
index 000000000..89b3e09d5
--- /dev/null
+++ b/pstoraster/gxcmap.h
@@ -0,0 +1,101 @@
+/* Copyright (C) 1989, 1995, 1996, 1997 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Requires gxdcolor.h, gxdevice.h. */
+
+#ifndef gxcmap_INCLUDED
+# define gxcmap_INCLUDED
+
+#include "gscsel.h"
+#include "gxfmap.h"
+
+/* Procedures for rendering colors specified by fractions. */
+
+#define cmap_proc_gray(proc)\
+ void proc(P5(frac, gx_device_color *, const gs_imager_state *,\
+ gx_device *, gs_color_select_t))
+#define cmap_proc_rgb(proc)\
+ void proc(P7(frac, frac, frac, gx_device_color *, const gs_imager_state *,\
+ gx_device *, gs_color_select_t))
+#define cmap_proc_cmyk(proc)\
+ void proc(P8(frac, frac, frac, frac, gx_device_color *,\
+ const gs_imager_state *, gx_device *, gs_color_select_t))
+#define cmap_proc_rgb_alpha(proc)\
+ void proc(P8(frac, frac, frac, frac, gx_device_color *,\
+ const gs_imager_state *, gx_device *, gs_color_select_t))
+
+/* 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));
+ cmap_proc_rgb_alpha((*map_rgb_alpha));
+};
+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(P2(gs_imager_state *, const gx_device *));
+
+/* 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, dev, select)\
+ (*pgs->cmap_procs->map_rgb)(cr, cg, cb, pdc, pgs, dev, select)
+#define gx_remap_concrete_cmyk(cc, cm, cy, ck, pdc, pgs, dev, select)\
+ (*pgs->cmap_procs->map_cmyk)(cc, cm, cy, ck, pdc, pgs, dev, select)
+#define gx_remap_concrete_rgb_alpha(cr, cg, cb, ca, pdc, pgs, dev, select)\
+ (*pgs->cmap_procs->map_rgb_alpha)(cr, cg, cb, ca, pdc, pgs, dev, select)
+
+/* 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
+
+#endif /* gxcmap_INCLUDED */
diff --git a/pstoraster/gxcolor2.h b/pstoraster/gxcolor2.h
new file mode 100644
index 000000000..45a8ea2df
--- /dev/null
+++ b/pstoraster/gxcolor2.h
@@ -0,0 +1,98 @@
+/* Copyright (C) 1993, 1995, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Internal definitions for Level 2 color routines */
+/* Requires gsstruct.h, gxfixed.h */
+
+#ifndef gxcolor2_INCLUDED
+# define gxcolor2_INCLUDED
+
+#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] */
+};
+
+extern_st(st_indexed_map);
+#define public_st_indexed_map() /* in gscolor2.c */\
+ gs_public_st_ptrs1(st_indexed_map, gs_indexed_map, "gs_indexed_map",\
+ indexed_map_enum_ptrs, indexed_map_reloc_ptrs, values)
+
+/* Allocate an indexed map and its values. */
+int alloc_indexed_map(P4(gs_indexed_map ** ppmap, int num_values,
+ gs_memory_t * mem, client_name_t cname));
+
+/* Free an indexed map and its values when the reference count goes to 0. */
+rc_free_proc(free_indexed_map);
+
+/*
+ * 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. Note that since all we care about is that the
+ * stepping matrix (the transformation from tiling space to device space)
+ * yield the right set of coordinates for integral X and Y values, we can
+ * adjust it to make the tiling computation easier; in particular, we can
+ * arrange it so that all 4 transformation factors are non-negative.
+ */
+
+/* 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 step_matrix; /* tiling space -> device space */
+ gs_rect bbox; /* bbox of tile in tiling space */
+ bool is_simple; /* true if xstep/ystep = tile size */
+ /*
+ * uses_mask is always true for PostScript patterns, but is false
+ * for bitmap patterns that don't have explicit transparent pixels.
+ */
+ bool uses_mask; /* if true, pattern mask must be created */
+ 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_pattern1_template, template, saved)
+
+#endif /* gxcolor2_INCLUDED */
diff --git a/pstoraster/gxcomp.h b/pstoraster/gxcomp.h
new file mode 100644
index 000000000..d2927b280
--- /dev/null
+++ b/pstoraster/gxcomp.h
@@ -0,0 +1,113 @@
+/* Copyright (C) 1997 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Definitions for implementing compositing functions */
+
+#ifndef gxcomp_INCLUDED
+# define gxcomp_INCLUDED
+
+#include "gscompt.h"
+#include "gsrefct.h"
+#include "gxbitfmt.h"
+
+/*
+ * Define the abstract superclass for all compositing function types.
+ */
+ /*typedef struct gs_composite_s gs_composite_t; *//* in gscompt.h */
+
+#ifndef gs_imager_state_DEFINED
+# define gs_imager_state_DEFINED
+typedef struct gs_imager_state_s gs_imager_state;
+
+#endif
+
+#ifndef gx_device_DEFINED
+# define gx_device_DEFINED
+typedef struct gx_device_s gx_device;
+
+#endif
+
+typedef struct gs_composite_type_procs_s {
+
+ /*
+ * Create the default compositor for a compositing function.
+ */
+#define composite_create_default_compositor_proc(proc)\
+ int proc(P5(const gs_composite_t *pcte, gx_device **pcdev,\
+ gx_device *dev, const gs_imager_state *pis, gs_memory_t *mem))
+ composite_create_default_compositor_proc((*create_default_compositor));
+
+ /*
+ * Test whether this function is equal to another one.
+ */
+#define composite_equal_proc(proc)\
+ bool proc(P2(const gs_composite_t *pcte, const gs_composite_t *pcte2))
+ composite_equal_proc((*equal));
+
+ /*
+ * Convert the representation of this function to a string
+ * for writing in a command list. *psize is the amount of space
+ * available. If it is large enough, the procedure sets *psize
+ * to the amount used and returns 0; if it is not large enough,
+ * the procedure sets *psize to the amount needed and returns a
+ * rangecheck error; in the case of any other error, *psize is
+ * not changed.
+ */
+#define composite_write_proc(proc)\
+ int proc(P3(const gs_composite_t *pcte, byte *data, uint *psize))
+ composite_write_proc((*write));
+
+ /*
+ * Convert the string representation of a function back to
+ * a structure, allocating the structure.
+ */
+#define composite_read_proc(proc)\
+ int proc(P4(gs_composite_t **ppcte, const byte *data, uint size,\
+ gs_memory_t *mem))
+ composite_read_proc((*read));
+
+} gs_composite_type_procs_t;
+typedef struct gs_composite_type_s {
+ gs_composite_type_procs_t procs;
+} gs_composite_type_t;
+
+/*
+ * Compositing objects are reference-counted, because graphics states will
+ * eventually reference them. Note that the common part has no
+ * garbage-collectible pointers and is never actually instantiated, so no
+ * structure type is needed for it.
+ */
+#define gs_composite_common\
+ const gs_composite_type_t *type;\
+ gs_id id; /* see gscompt.h */\
+ rc_header rc
+struct gs_composite_s {
+ gs_composite_common;
+};
+
+/* Replace a procedure with a macro. */
+#define gs_composite_id(pcte) ((pcte)->id)
+
+#endif /* gxcomp_INCLUDED */
diff --git a/pstoraster/gxcoord.h b/pstoraster/gxcoord.h
new file mode 100644
index 000000000..b341bde89
--- /dev/null
+++ b/pstoraster/gxcoord.h
@@ -0,0 +1,44 @@
+/* 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 supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Requires gxmatrix.h and gzstate.h */
+
+#ifndef gxcoord_INCLUDED
+# define gxcoord_INCLUDED
+
+#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));
+
+#endif /* gxcoord_INCLUDED */
diff --git a/pstoraster/gxcpath.c b/pstoraster/gxcpath.c
new file mode 100644
index 000000000..e4e980bf8
--- /dev/null
+++ b/pstoraster/gxcpath.c
@@ -0,0 +1,962 @@
+/* Copyright (C) 1991, 1995, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Implementation of clipping paths, other than actual clipping */
+#include "gx.h"
+#include "gserrors.h"
+#include "gsstruct.h"
+#include "gsutil.h"
+#include "gsline.h"
+#include "gxdevice.h"
+#include "gxfixed.h"
+#include "gscoord.h" /* needs gsmatrix.h */
+#include "gxstate.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 *));
+
+/* Other structure types */
+public_st_clip_rect();
+private_st_clip_list();
+public_st_clip_path();
+private_st_clip_rect_list();
+public_st_device_clip();
+private_st_cpath_enum();
+
+/* GC procedures for gx_clip_path */
+#define cptr ((gx_clip_path *)vptr)
+private
+ENUM_PTRS_BEGIN(clip_path_enum_ptrs) return ENUM_USING(st_path, &cptr->path, sizeof(cptr->path), index - 1);
+
+case 0:
+ENUM_RETURN((cptr->rect_list == &cptr->local_list ? 0 :
+ cptr->rect_list));
+ENUM_PTRS_END
+private RELOC_PTRS_BEGIN(clip_path_reloc_ptrs)
+{
+ if (cptr->rect_list != &cptr->local_list)
+ RELOC_VAR(cptr->rect_list);
+ RELOC_USING(st_path, &cptr->path, sizeof(gx_path));
+}
+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 ENUM_USING(st_clip_list, &cptr->list,
+ sizeof(gx_clip_list), index - 1);
+ return ENUM_USING(st_device_forward, vptr,
+ sizeof(gx_device_forward),
+ index - (st_clip_list_max_ptrs + 1));
+}
+case 0:
+ENUM_RETURN((cptr->current == &cptr->list.single ? NULL :
+ (void *)cptr->current));
+ENUM_PTRS_END
+private RELOC_PTRS_BEGIN(device_clip_reloc_ptrs)
+{
+ if (cptr->current == &cptr->list.single)
+ cptr->current = &((gx_device_clip *)RELOC_OBJ(vptr))->list.single;
+ else
+ RELOC_PTR(gx_device_clip, current);
+ RELOC_USING(st_clip_list, &cptr->list, sizeof(gx_clip_list));
+ RELOC_USING(st_device_forward, vptr, sizeof(gx_device_forward));
+}
+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 memory management ------ */
+
+private rc_free_proc(rc_free_cpath_list);
+private rc_free_proc(rc_free_cpath_list_local);
+
+/* Initialize those parts of the contents of a clip path that aren't */
+/* part of the path. */
+private void
+cpath_init_rectangle(gx_clip_path * pcpath, gs_fixed_rect * pbox)
+{
+ gx_clip_list_from_rectangle(&pcpath->rect_list->list, pbox);
+ pcpath->inner_box = *pbox;
+ pcpath->path_valid = false;
+ pcpath->path.bbox = *pbox;
+ gx_cpath_set_outer_box(pcpath);
+ pcpath->id = gs_next_ids(1); /* path changed => change id */
+}
+private void
+cpath_init_own_contents(gx_clip_path * pcpath)
+{ /* We could make null_rect static, but then it couldn't be const. */
+ gs_fixed_rect null_rect;
+
+ null_rect.p.x = null_rect.p.y = null_rect.q.x = null_rect.q.y = 0;
+ cpath_init_rectangle(pcpath, &null_rect);
+}
+private void
+cpath_share_own_contents(gx_clip_path * pcpath, const gx_clip_path * shared)
+{
+ pcpath->inner_box = shared->inner_box;
+ pcpath->path_valid = shared->path_valid;
+ pcpath->outer_box = shared->outer_box;
+ pcpath->id = shared->id;
+}
+
+/* Allocate only the segments of a clipping path on the heap. */
+private int
+cpath_alloc_list(gx_clip_rect_list ** prlist, gs_memory_t * mem,
+ client_name_t cname)
+{
+ rc_alloc_struct_1(*prlist, gx_clip_rect_list, &st_clip_rect_list, mem,
+ return_error(gs_error_VMerror), cname);
+ (*prlist)->rc.free = rc_free_cpath_list;
+ return 0;
+}
+int
+gx_cpath_init_contained_shared(gx_clip_path * pcpath,
+ const gx_clip_path * shared, gs_memory_t * mem, client_name_t cname)
+{
+ if (shared) {
+ if (shared->path.segments == &shared->path.local_segments) {
+ lprintf1("Attempt to share (local) segments of clip path 0x%lx!\n",
+ (ulong) shared);
+ return_error(gs_error_Fatal);
+ }
+ *pcpath = *shared;
+ pcpath->path.memory = mem;
+ pcpath->path.allocation = path_allocated_contained;
+ rc_increment(pcpath->path.segments);
+ rc_increment(pcpath->rect_list);
+ } else {
+ int code = cpath_alloc_list(&pcpath->rect_list, mem, cname);
+
+ if (code < 0)
+ return code;
+ code = gx_path_alloc_contained(&pcpath->path, mem, cname);
+ if (code < 0) {
+ gs_free_object(mem, pcpath->rect_list, cname);
+ pcpath->rect_list = 0;
+ return code;
+ }
+ cpath_init_own_contents(pcpath);
+ }
+ return 0;
+}
+#define gx_cpath_alloc_contents(pcpath, shared, mem, cname)\
+ gx_cpath_init_contained_shared(pcpath, shared, mem, cname)
+
+/* Allocate all of a clipping path on the heap. */
+gx_clip_path *
+gx_cpath_alloc_shared(const gx_clip_path * shared, gs_memory_t * mem,
+ client_name_t cname)
+{
+ gx_clip_path *pcpath =
+ gs_alloc_struct(mem, gx_clip_path, &st_clip_path, cname);
+ int code;
+
+ if (pcpath == 0)
+ return 0;
+ code = gx_cpath_alloc_contents(pcpath, shared, mem, cname);
+ if (code < 0) {
+ gs_free_object(mem, pcpath, cname);
+ return 0;
+ }
+ pcpath->path.allocation = path_allocated_on_heap;
+ return pcpath;
+}
+
+/* Initialize a stack-allocated clipping path. */
+int
+gx_cpath_init_local_shared(gx_clip_path * pcpath, const gx_clip_path * shared,
+ gs_memory_t * mem)
+{
+ if (shared) {
+ if (shared->path.segments == &shared->path.local_segments) {
+ lprintf1("Attempt to share (local) segments of clip path 0x%lx!\n",
+ (ulong) shared);
+ return_error(gs_error_Fatal);
+ }
+ pcpath->path = shared->path;
+ pcpath->path.allocation = path_allocated_on_stack;
+ rc_increment(pcpath->path.segments);
+ pcpath->rect_list = shared->rect_list;
+ rc_increment(pcpath->rect_list);
+ cpath_share_own_contents(pcpath, shared);
+ } else {
+ gx_path_init_local(&pcpath->path, mem);
+ rc_init_free(&pcpath->local_list, mem, 1, rc_free_cpath_list_local);
+ pcpath->rect_list = &pcpath->local_list;
+ cpath_init_own_contents(pcpath);
+ }
+ return 0;
+}
+
+/* Unshare a clipping path. */
+int
+gx_cpath_unshare(gx_clip_path * pcpath)
+{
+ int code = gx_path_unshare(&pcpath->path);
+ gx_clip_rect_list *rlist = pcpath->rect_list;
+
+ if (code < 0)
+ return code;
+ if (rlist->rc.ref_count > 1) {
+ int code = cpath_alloc_list(&pcpath->rect_list, pcpath->path.memory,
+ "gx_cpath_unshare");
+
+ if (code < 0)
+ return code;
+ /* Copy the rectangle list. */
+/**************** NYI ****************/
+ rc_decrement(rlist, "gx_cpath_unshare");
+ }
+ return code;
+}
+
+/* Free a clipping path. */
+void
+gx_cpath_free(gx_clip_path * pcpath, client_name_t cname)
+{
+ rc_decrement(pcpath->rect_list, cname);
+ /* Clean up pointers for GC. */
+ pcpath->rect_list = 0;
+ {
+ gx_path_allocation_t alloc = pcpath->path.allocation;
+
+ if (alloc == path_allocated_on_heap) {
+ pcpath->path.allocation = path_allocated_contained;
+ gx_path_free(&pcpath->path, cname);
+ gs_free_object(pcpath->path.memory, pcpath, cname);
+ } else
+ gx_path_free(&pcpath->path, cname);
+ }
+}
+
+/* Assign a clipping path, preserving the source. */
+int
+gx_cpath_assign_preserve(gx_clip_path * pcpto, gx_clip_path * pcpfrom)
+{
+ int code = gx_path_assign_preserve(&pcpto->path, &pcpfrom->path);
+ gx_clip_rect_list *fromlist = pcpfrom->rect_list;
+ gx_clip_rect_list *tolist = pcpto->rect_list;
+ gx_path path;
+
+ if (code < 0)
+ return 0;
+ if (fromlist == &pcpfrom->local_list) {
+ /* We can't use pcpfrom's list object. */
+ if (tolist == &pcpto->local_list || tolist->rc.ref_count > 1) {
+ /* We can't use pcpto's list either. Allocate a new one. */
+ int code = cpath_alloc_list(&tolist, tolist->rc.memory,
+ "gx_cpath_assign");
+
+ if (code < 0)
+ return code;
+ rc_decrement(pcpto->rect_list, "gx_cpath_assign");
+ } else {
+ /* Use pcpto's list object. */
+ rc_free_cpath_list_local(tolist->rc.memory, tolist,
+ "gx_cpath_assign");
+ }
+ tolist->list = fromlist->list;
+ pcpfrom->rect_list = tolist;
+ rc_increment(tolist);
+ } else {
+ /* We can use pcpfrom's list object. */
+ rc_increment(fromlist);
+ rc_decrement(pcpto->rect_list, "gx_cpath_assign");
+ }
+ path = pcpto->path, *pcpto = *pcpfrom, pcpto->path = path;
+ return 0;
+}
+
+/* Assign a clipping path, releasing the source. */
+int
+gx_cpath_assign_free(gx_clip_path * pcpto, gx_clip_path * pcpfrom)
+{ /* For right now, just do assign + free. */
+ int code = gx_cpath_assign_preserve(pcpto, pcpfrom);
+
+ if (code < 0)
+ return 0;
+ gx_cpath_free(pcpfrom, "gx_cpath_assign_free");
+ return 0;
+}
+
+/* Free the clipping list when its reference count goes to zero. */
+private void
+rc_free_cpath_list_local(gs_memory_t * mem, void *vrlist,
+ client_name_t cname)
+{
+ gx_clip_rect_list *rlist = (gx_clip_rect_list *) vrlist;
+
+ gx_clip_list_free(&rlist->list, mem);
+}
+private void
+rc_free_cpath_list(gs_memory_t * mem, void *vrlist, client_name_t cname)
+{
+ rc_free_cpath_list_local(mem, vrlist, cname);
+ gs_free_object(mem, vrlist, cname);
+}
+
+/* ------ Clipping path accessing ------ */
+
+/* Return the path of a clipping path. */
+int
+gx_cpath_to_path(gx_clip_path * pcpath, gx_path * ppath)
+{
+ if (!pcpath->path_valid) {
+ /* Synthesize a path. */
+ gs_cpath_enum cenum;
+ gs_fixed_point pts[3];
+ gx_path rpath;
+ int code;
+
+ gx_path_init_local(&rpath, pcpath->path.memory);
+ gx_cpath_enum_init(&cenum, pcpath);
+ while ((code = gx_cpath_enum_next(&cenum, pts)) != 0) {
+ switch (code) {
+ case gs_pe_moveto:
+ code = gx_path_add_point(&rpath, pts[0].x, pts[0].y);
+ break;
+ case gs_pe_lineto:
+ code = gx_path_add_line_notes(&rpath, pts[0].x, pts[0].y,
+ gx_cpath_enum_notes(&cenum));
+ break;
+ case gs_pe_curveto:
+ code = gx_path_add_curve_notes(&rpath, pts[0].x, pts[0].y,
+ pts[1].x, pts[1].y,
+ pts[2].x, pts[2].y,
+ gx_cpath_enum_notes(&cenum));
+ break;
+ case gs_pe_closepath:
+ code = gx_path_close_subpath_notes(&rpath,
+ gx_cpath_enum_notes(&cenum));
+ break;
+ default:
+ if (code >= 0)
+ code = gs_note_error(gs_error_unregistered);
+ }
+ if (code < 0)
+ break;
+ }
+ if (code >= 0)
+ code = gx_path_assign_free(&pcpath->path, &rpath);
+ if (code < 0) {
+ gx_path_free(&rpath, "gx_cpath_to_path error");
+ return code;
+ }
+ pcpath->path_valid = true;
+ }
+ return gx_path_assign_preserve(ppath, &pcpath->path);
+}
+
+/* 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 (gx_cpath_is_outside(pcpath)) {
+ 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(gx_cpath_list(pcpath));
+ }
+}
+bool
+gx_cpath_outer_box(const gx_clip_path * pcpath, gs_fixed_rect * pbox)
+{
+ if (gx_cpath_is_outside(pcpath)) {
+ 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(gx_cpath_list(pcpath));
+ }
+}
+
+/* Test if a clipping path includes a rectangle. */
+/* The rectangle need not be oriented correctly, i.e. x0 > x1 is OK. */
+bool
+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 != gx_cpath_list(pcpath)->outside) {
+ pcpath->id = gs_next_ids(1); /* path changed => change id */
+ gx_cpath_list(pcpath)->outside = outside;
+ }
+ return 0;
+}
+
+/* Return the current outsideness of a clipping path. */
+bool
+gx_cpath_is_outside(const gx_clip_path * pcpath)
+{
+ return gx_cpath_list(pcpath)->outside;
+}
+
+/* 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 ------ */
+
+/* Create a rectangular clipping path. */
+/* The supplied rectangle may not be oriented correctly, */
+/* but it will be oriented correctly upon return. */
+private int
+cpath_set_rectangle(gx_clip_path * pcpath, gs_fixed_rect * pbox)
+{
+ gx_clip_rect_list *rlist = pcpath->rect_list;
+
+ if (rlist->rc.ref_count <= 1)
+ gx_clip_list_free(&rlist->list, rlist->rc.memory);
+ else {
+ int code = cpath_alloc_list(&pcpath->rect_list, pcpath->path.memory,
+ "gx_cpath_from_rectangle");
+
+ if (code < 0)
+ return code;
+ rc_decrement(rlist, "gx_cpath_from_rectangle");
+ rlist = pcpath->rect_list;
+ }
+ cpath_init_rectangle(pcpath, pbox);
+ return 0;
+}
+int
+gx_cpath_from_rectangle(gx_clip_path * pcpath, gs_fixed_rect * pbox)
+{
+ int code = gx_path_new(&pcpath->path);
+
+ if (code < 0)
+ return code;
+ return cpath_set_rectangle(pcpath, pbox);
+}
+int
+gx_cpath_reset(gx_clip_path * pcpath)
+{
+ gs_fixed_rect null_rect;
+
+ null_rect.p.x = null_rect.p.y = null_rect.q.x = null_rect.q.y = 0;
+ return gx_cpath_from_rectangle(pcpath, &null_rect);
+}
+
+/* Intersect a new clipping path with an old one. */
+/* Flatten the new path first (in a copy) if necessary. */
+int
+gx_cpath_clip(gs_state *pgs, gx_clip_path *pcpath, gx_path *ppath_orig,
+ int rule)
+{
+ gx_path fpath;
+ gx_path *ppath = ppath_orig;
+ gs_fixed_rect old_box, new_box;
+ int code;
+
+ /* Flatten the path if necessary. */
+ if (gx_path_has_curves_inline(ppath)) {
+ gx_path_init_local(&fpath, gs_state_memory(pgs));
+ code = gx_path_add_flattened_accurate(ppath, &fpath,
+ gs_currentflat(pgs),
+ gs_currentaccuratecurves(pgs));
+ if (code < 0)
+ return code;
+ ppath = &fpath;
+ }
+ /**************** SHOULD CHANGE THIS TO KEEP PATH ****************/
+ if (gx_cpath_inner_box(pcpath, &old_box) &&
+ ((code = gx_path_is_rectangle(ppath, &new_box)) ||
+ gx_path_is_void(ppath))
+ ) {
+ bool changed = false;
+ bool outside = gx_cpath_is_outside(pcpath);
+
+ if (!code) {
+ /* The new path is void. */
+ if (gx_path_current_point(ppath, &new_box.p) < 0) {
+ /* Use the user space origin (arbitrarily). */
+ gs_point origin;
+
+ gs_transform(pgs, 0.0, 0.0, &origin);
+ new_box.p.x = float2fixed(origin.x);
+ new_box.p.y = float2fixed(origin.y);
+ changed = true;
+ }
+ new_box.q = new_box.p;
+ } else {
+ /* 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;
+ /* Check for a degenerate rectangle. */
+ if (new_box.q.x < new_box.p.x)
+ new_box.q.x = new_box.p.x;
+ if (new_box.q.y < new_box.p.y)
+ new_box.q.y = new_box.p.y;
+ }
+ if (changed) {
+ /* Defer constructing the path. */
+ gx_path_new(&pcpath->path);
+ pcpath->path_valid = false;
+ } else {
+ gx_path_assign_preserve(&pcpath->path, ppath);
+ pcpath->path_valid = true;
+ }
+ ppath->bbox = new_box;
+ cpath_set_rectangle(pcpath, &new_box);
+ pcpath->rect_list->list.outside = outside;
+ } else {
+ /* Existing clip path is not a rectangle. Intersect the slow way. */
+ bool path_valid =
+ gx_cpath_inner_box(pcpath, &old_box) &&
+ gx_path_bbox(ppath, &new_box) >= 0 &&
+ gx_cpath_includes_rectangle(pcpath,
+ new_box.p.x, new_box.p.y,
+ new_box.q.x, new_box.q.y);
+
+ code = gx_cpath_intersect_slow(pgs, pcpath, ppath, rule);
+ if (code >= 0 && path_valid) {
+ gx_path_assign_preserve(&pcpath->path, ppath_orig);
+ pcpath->path_valid = true;
+ }
+ }
+ if (ppath != ppath_orig)
+ gx_path_free(ppath, "gx_cpath_clip");
+ 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_list *list = gx_cpath_list(pcpath);
+ 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 = list->head;
+ if (pr == 0)
+ pr = &list->single;
+ for (; pr != 0; pr = pr->next)
+ if (pr != list->head && pr != 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;
+}
+
+/* Start enumerating a clipping path. */
+int
+gx_cpath_enum_init(gs_cpath_enum * penum, gx_clip_path * pcpath)
+{
+ if ((penum->using_path = pcpath->path_valid)) {
+ gx_path_enum_init(&penum->path_enum, &pcpath->path);
+ penum->rp = penum->visit = 0;
+ } else {
+ gx_path empty_path;
+ gx_clip_list *clp = gx_cpath_list(pcpath);
+ gx_clip_rect *head = (clp->count <= 1 ? &clp->single : clp->head);
+ gx_clip_rect *rp;
+
+ /* Initialize the pointers in the path_enum properly. */
+ gx_path_init_local(&empty_path, pcpath->path.memory);
+ gx_path_enum_init(&penum->path_enum, &empty_path);
+ penum->visit = head;
+ for (rp = head; rp != 0; rp = rp->next)
+ rp->to_visit =
+ (rp->xmin < rp->xmax && rp->ymin < rp->ymax ?
+ visit_left | visit_right : 0);
+ penum->rp = 0; /* scan will initialize */
+ penum->any_rectangles = false;
+ penum->state = cpe_scan;
+ penum->have_line = false;
+ }
+ return 0;
+}
+
+/* Enumerate the next segment of a clipping path. */
+/* In general, this produces a path made up of zillions of tiny lines. */
+int
+gx_cpath_enum_next(gs_cpath_enum * penum, gs_fixed_point pts[3])
+{
+ if (penum->using_path)
+ return gx_path_enum_next(&penum->path_enum, pts);
+#define set_pt(xi, yi)\
+ (pts[0].x = int2fixed(xi), pts[0].y = int2fixed(yi))
+#define set_line(xi, yi)\
+ (penum->line_end.x = (xi), penum->line_end.y = (yi), penum->have_line = true)
+ if (penum->have_line) {
+ set_pt(penum->line_end.x, penum->line_end.y);
+ penum->have_line = false;
+ return gs_pe_lineto;
+ } {
+ gx_clip_rect *visit = penum->visit;
+ gx_clip_rect *rp = penum->rp;
+ cpe_visit_t first_visit = penum->first_visit;
+ cpe_state_t state = penum->state;
+ gx_clip_rect *look;
+ int code;
+
+ switch (state) {
+
+ case cpe_scan:
+ /* Look for the start of an edge to trace. */
+ for (; visit != 0; visit = visit->next) {
+ if (visit->to_visit & visit_left) {
+ set_pt(visit->xmin, visit->ymin);
+ first_visit = visit_left;
+ state = cpe_left;
+ } else if (visit->to_visit & visit_right) {
+ set_pt(visit->xmax, visit->ymax);
+ first_visit = visit_right;
+ state = cpe_right;
+ } else
+ continue;
+ rp = visit;
+ code = gs_pe_moveto;
+ penum->any_rectangles = true;
+ goto out;
+ }
+ /* We've enumerated all the edges. */
+ state = cpe_done;
+ if (!penum->any_rectangles) {
+ /* We didn't have any rectangles. */
+ set_pt(fixed_0, fixed_0);
+ code = gs_pe_moveto;
+ break;
+ }
+ /* falls through */
+
+ case cpe_done:
+ /* All done. */
+ code = 0;
+ break;
+
+/* We can't use the BEGIN ... END hack here: we need to be able to break. */
+#define return_line(px, py)\
+ set_pt(px, py); code = gs_pe_lineto; break
+
+ case cpe_left:
+
+ left: /* Trace upward along a left edge. */
+ /* We're at the lower left corner of rp. */
+ rp->to_visit &= ~visit_left;
+ /* Look for an adjacent rectangle above rp. */
+ for (look = rp;
+ (look = look->next) != 0 &&
+ (look->ymin == rp->ymin ||
+ (look->ymin == rp->ymax && look->xmax <= rp->xmin));
+ );
+ /* Now we know look->ymin >= rp->ymax. */
+ if (look == 0 || look->ymin > rp->ymax ||
+ look->xmin >= rp->xmax
+ ) { /* No adjacent rectangle, switch directions. */
+ state =
+ (rp == visit && first_visit == visit_right ? cpe_close :
+ (set_line(rp->xmax, rp->ymax), cpe_right));
+ return_line(rp->xmin, rp->ymax);
+ }
+ /* We found an adjacent rectangle. */
+ /* See if it also adjoins a rectangle to the left of rp. */
+ {
+ gx_clip_rect *prev = rp->prev;
+ gx_clip_rect *cur = rp;
+
+ if (prev != 0 && prev->ymax == rp->ymax &&
+ look->xmin < prev->xmax
+ ) { /* There's an adjoining rectangle as well. */
+ /* Switch directions. */
+ rp = prev;
+ state =
+ (rp == visit && first_visit == visit_right ? cpe_close :
+ (set_line(prev->xmax, prev->ymax), cpe_right));
+ return_line(cur->xmin, cur->ymax);
+ }
+ rp = look;
+ if (rp == visit && first_visit == visit_left)
+ state = cpe_close;
+ else if (rp->xmin == cur->xmin)
+ goto left;
+ else
+ set_line(rp->xmin, rp->ymin);
+ return_line(cur->xmin, cur->ymax);
+ }
+
+ case cpe_right:
+
+ right: /* Trace downward along a right edge. */
+ /* We're at the upper right corner of rp. */
+ rp->to_visit &= ~visit_right;
+ /* Look for an adjacent rectangle below rp. */
+ for (look = rp;
+ (look = look->prev) != 0 &&
+ (look->ymax == rp->ymax ||
+ (look->ymax == rp->ymin && look->xmin >= rp->xmax));
+ );
+ /* Now we know look->ymax <= rp->ymin. */
+ if (look == 0 || look->ymax < rp->ymin ||
+ look->xmax <= rp->xmin
+ ) { /* No adjacent rectangle, switch directions. */
+ state =
+ (rp == visit && first_visit == visit_left ? cpe_close :
+ (set_line(rp->xmin, rp->ymin), cpe_left));
+ return_line(rp->xmax, rp->ymin);
+ }
+ /* We found an adjacent rectangle. */
+ /* See if it also adjoins a rectangle to the right of rp. */
+ {
+ gx_clip_rect *next = rp->next;
+ gx_clip_rect *cur = rp;
+
+ if (next != 0 && next->ymin == rp->ymin &&
+ look->xmax > next->xmin
+ ) { /* There's an adjoining rectangle as well. */
+ /* Switch directions. */
+ rp = next;
+ state =
+ (rp == visit && first_visit == visit_left ? cpe_close :
+ (set_line(next->xmin, next->ymin), cpe_left));
+ return_line(cur->xmax, cur->ymin);
+ }
+ rp = look;
+ if (rp == visit && first_visit == visit_right)
+ state = cpe_close;
+ else if (rp->xmax == cur->xmax)
+ goto right;
+ else
+ set_line(rp->xmax, rp->ymax);
+ return_line(cur->xmax, cur->ymin);
+ }
+
+#undef return_line
+
+ case cpe_close:
+ /* We've gone all the way around an edge. */
+ code = gs_pe_closepath;
+ state = cpe_scan;
+ break;
+
+ default:
+ return_error(gs_error_unknownerror);
+ }
+
+ out: /* Store the state before exiting. */
+ penum->visit = visit;
+ penum->rp = rp;
+ penum->first_visit = first_visit;
+ penum->state = state;
+ return code;
+ }
+#undef set_pt
+#undef set_line
+}
+segment_notes
+gx_cpath_enum_notes(const gs_cpath_enum * penum)
+{
+ return sn_none;
+}
+
+/* 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);
+}
+
+/* ------ Debugging printout ------ */
+
+#ifdef DEBUG
+
+/* Print a clipping path */
+void
+gx_cpath_print(const gx_clip_path * pcpath)
+{
+ const gx_clip_rect *pr;
+ const gx_clip_list *list = gx_cpath_list(pcpath);
+
+ if (pcpath->path_valid)
+ gx_path_print(&pcpath->path);
+ else
+ dlputs(" (path not valid)\n");
+ dlprintf4(" 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));
+ dlprintf4(" outer_box=(%g,%g),(%g,%g)",
+ fixed2float(pcpath->outer_box.p.x),
+ fixed2float(pcpath->outer_box.p.y),
+ fixed2float(pcpath->outer_box.q.x),
+ fixed2float(pcpath->outer_box.q.y));
+ dprintf4(" rule=%d outside=%d count=%d list.refct=%ld\n",
+ pcpath->rule, list->outside, list->count,
+ pcpath->rect_list->rc.ref_count);
+ switch (list->count) {
+ case 0:
+ pr = 0;
+ break;
+ case 1:
+ pr = &list->single;
+ break;
+ default:
+ pr = list->head;
+ }
+ for (; pr != 0; pr = pr->next)
+ dlprintf4(" 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..04d358196
--- /dev/null
+++ b/pstoraster/gxcpath.h
@@ -0,0 +1,134 @@
+/* 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 supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Requires gxdevice.h */
+
+#ifndef gxcpath_INCLUDED
+# define gxcpath_INCLUDED
+
+/* 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 */
+ byte to_visit; /* bookkeeping for gs_clippath */
+};
+
+/* 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 *));
+
+#endif /* gxcpath_INCLUDED */
diff --git a/pstoraster/gxcspace.h b/pstoraster/gxcspace.h
new file mode 100644
index 000000000..8737e3a2a
--- /dev/null
+++ b/pstoraster/gxcspace.h
@@ -0,0 +1,237 @@
+/* Copyright (C) 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Implementation of color spaces */
+/* Requires gsstruct.h */
+
+#ifndef gxcspace_INCLUDED
+# define gxcspace_INCLUDED
+
+#include "gscspace.h" /* client interface */
+#include "gsccolor.h"
+#include "gscsel.h"
+#include "gxfrac.h" /* for concrete colors */
+
+/* Define opaque types. */
+
+#ifndef gx_device_color_DEFINED
+# define gx_device_color_DEFINED
+typedef struct gx_device_color_s gx_device_color;
+
+#endif
+
+#ifndef gx_device_DEFINED
+# define gx_device_DEFINED
+typedef struct gx_device_s gx_device;
+
+#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 whether the space can be the base space for an Indexed
+ * color space or the alternate space for a Separation or DeviceN
+ * color space.
+ */
+
+ bool can_be_base_space;
+ bool can_be_alt_space;
+
+ /*
+ * Define the true structure type for this variant of the color
+ * space union.
+ */
+
+ gs_memory_type_ptr_t stype;
+
+ /* ------ Procedures ------ */
+
+ /*
+ * 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 for colored Patterns,
+ * -N-1 for uncolored Patterns, where N is the number of components
+ * in the base space.
+ */
+
+#define cs_proc_num_components(proc)\
+ int proc(P1(const gs_color_space *))
+#define cs_num_components(pcs)\
+ (*(pcs)->type->num_components)(pcs)
+ cs_proc_num_components((*num_components));
+
+ /*
+ * Return the base or alternate color space underlying this one.
+ * Only defined for Indexed, Separation, DeviceN, and
+ * uncolored Pattern spaces; returns NULL for all others.
+ */
+
+#define cs_proc_base_space(proc)\
+ const gs_color_space *proc(P1(const gs_color_space *))
+#define cs_base_space(pcs)\
+ (*(pcs)->type->base_space)(pcs)
+ cs_proc_base_space((*base_space));
+
+ /* 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));
+
+ /* Force a client color into its legal range. */
+
+#define cs_proc_restrict_color(proc)\
+ void proc(P2(gs_client_color *, const gs_color_space *))
+ cs_proc_restrict_color((*restrict_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_imager_state *))
+#define cs_concrete_space(pcs, pis)\
+ (*(pcs)->type->concrete_space)(pcs, pis)
+ cs_proc_concrete_space((*concrete_space));
+
+ /*
+ * Reduce a color to a concrete color. A concrete color is one
+ * that the device can handle directly (possibly with halftoning):
+ * a DeviceGray/RGB/CMYK/Pixel color, or a Separation or DeviceN
+ * color that does not use the alternate space.
+ * (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_imager_state *))
+#define cs_concretize_color(pcc, pcs, values, pis)\
+ (*(pcs)->type->concretize_color)(pcc, pcs, values, pis)
+ 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(P5(const frac *, gx_device_color *, const gs_imager_state *,\
+ gx_device *, gs_color_select_t))
+ cs_proc_remap_concrete_color((*remap_concrete_color));
+
+ /* Map a color directly to a device color. */
+
+#define cs_proc_remap_color(proc)\
+ int proc(P6(const gs_client_color *, const gs_color_space *,\
+ gx_device_color *, const gs_imager_state *, gx_device *,\
+ gs_color_select_t))
+ 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(P2(const gs_color_space *, int))
+#define cs_adjust_cspace_count(pgs, delta)\
+ (*(pgs)->color_space->type->adjust_cspace_count)((pgs)->color_space, delta)
+ cs_proc_adjust_cspace_count((*adjust_cspace_count));
+
+ /* Adjust reference counts of indirect color components. */
+ /*
+ * Note: the color space argument may be NULL, which indicates that the
+ * caller warrants that any subsidiary colors don't have allocation
+ * issues. This is a hack for an application that needs to be able to
+ * release Pattern colors.
+ */
+
+#define cs_proc_adjust_color_count(proc)\
+ void proc(P3(const gs_client_color *, const gs_color_space *, int))
+#define cs_adjust_color_count(pgs, delta)\
+ (*(pgs)->color_space->type->adjust_color_count)\
+ ((pgs)->ccolor, (pgs)->color_space, 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))
+
+};
+
+/* Standard color space structure types */
+extern_st(st_base_color_space);
+#define public_st_base_color_space() /* in gscspace.c */\
+ gs_public_st_simple(st_base_color_space, gs_base_color_space,\
+ "gs_base_color_space")
+/*extern_st(st_paint_color_space); *//* (not needed) */
+
+/* Standard color space procedures */
+cs_proc_num_components(gx_num_components_1);
+cs_proc_num_components(gx_num_components_3);
+cs_proc_num_components(gx_num_components_4);
+cs_proc_base_space(gx_no_base_space);
+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_restrict_color(gx_restrict01_paint_1);
+cs_proc_restrict_color(gx_restrict01_paint_3);
+cs_proc_restrict_color(gx_restrict01_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);
+
+/* 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);
+
+/*
+ * Allocate a color space and initialize its type and memory fields.
+ * This is only used by color space implementations.
+ */
+
+int gs_cspace_alloc(P3(gs_color_space **ppcspace,
+ const gs_color_space_type *pcstype,
+ gs_memory_t *mem));
+
+#endif /* gxcspace_INCLUDED */
diff --git a/pstoraster/gxctable.c b/pstoraster/gxctable.c
new file mode 100644
index 000000000..f7a53b722
--- /dev/null
+++ b/pstoraster/gxctable.c
@@ -0,0 +1,146 @@
+/* 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 supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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..237f47cdd
--- /dev/null
+++ b/pstoraster/gxctable.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 supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Interface to color table lookup and interpolation */
+
+#ifndef gxctable_INCLUDED
+# define gxctable_INCLUDED
+
+#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));
+
+#endif /* gxctable_INCLUDED */
diff --git a/pstoraster/gxcvalue.h b/pstoraster/gxcvalue.h
new file mode 100644
index 000000000..f919415ed
--- /dev/null
+++ b/pstoraster/gxcvalue.h
@@ -0,0 +1,48 @@
+/* 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 supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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..97a055d86
--- /dev/null
+++ b/pstoraster/gxdcconv.c
@@ -0,0 +1,162 @@
+/* Copyright (C) 1992, 1995, 1997 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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 "gxistate.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_imager_state * pis)
+{
+ 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_imager_state * pis,
+ 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 =
+ (pis->black_generation == NULL ? frac_0 :
+ gx_map_color_frac(pis, k, black_generation));
+ signed_frac ucr =
+ (pis->undercolor_removal == NULL ? frac_0 :
+ gx_map_color_frac(pis, 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_imager_state * pis)
+{
+ frac not_gray = color_rgb_to_gray(c, m, y, pis);
+
+ 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_imager_state * pis,
+ 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..f38f8eeab
--- /dev/null
+++ b/pstoraster/gxdcconv.h
@@ -0,0 +1,43 @@
+/* Copyright (C) 1992, 1993, 1997 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Internal device color conversion interfaces */
+
+#ifndef gxdcconv_INCLUDED
+# define gxdcconv_INCLUDED
+
+#include "gxfrac.h"
+
+/* Color space conversion routines */
+frac color_rgb_to_gray(P4(frac r, frac g, frac b,
+ const gs_imager_state * pis));
+void color_rgb_to_cmyk(P5(frac r, frac g, frac b,
+ const gs_imager_state * pis, frac cmyk[4]));
+frac color_cmyk_to_gray(P5(frac c, frac m, frac y, frac k,
+ const gs_imager_state * pis));
+void color_cmyk_to_rgb(P6(frac c, frac m, frac y, frac k,
+ const gs_imager_state * pis, frac rgb[3]));
+
+#endif /* gxdcconv_INCLUDED */
diff --git a/pstoraster/gxdcolor.c b/pstoraster/gxdcolor.c
new file mode 100644
index 000000000..00da3d9af
--- /dev/null
+++ b/pstoraster/gxdcolor.c
@@ -0,0 +1,339 @@
+/* Copyright (C) 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Pure and null device color implementation */
+#include "gx.h"
+#include "gserrors.h"
+#include "gsbittab.h"
+#include "gxdcolor.h"
+#include "gxdevice.h"
+
+/* Define the standard device color types. */
+
+/* 'none' means the color is not defined. */
+private dev_color_proc_load(gx_dc_no_load);
+private dev_color_proc_fill_rectangle(gx_dc_no_fill_rectangle);
+private dev_color_proc_fill_masked(gx_dc_no_fill_masked);
+private dev_color_proc_equal(gx_dc_no_equal);
+const gx_device_color_type_t gx_dc_type_data_none = {
+ &st_bytes,
+ gx_dc_no_load, gx_dc_no_fill_rectangle, gx_dc_no_fill_masked,
+ gx_dc_no_equal
+};
+#undef gx_dc_type_none
+const gx_device_color_type_t *const gx_dc_type_none = &gx_dc_type_data_none;
+#define gx_dc_type_none (&gx_dc_type_data_none)
+
+/* 'null' means the color has no effect when used for drawing. */
+private dev_color_proc_load(gx_dc_null_load);
+private dev_color_proc_fill_rectangle(gx_dc_null_fill_rectangle);
+private dev_color_proc_fill_masked(gx_dc_null_fill_masked);
+private dev_color_proc_equal(gx_dc_null_equal);
+const gx_device_color_type_t gx_dc_type_data_null = {
+ &st_bytes,
+ gx_dc_null_load, gx_dc_null_fill_rectangle, gx_dc_null_fill_masked,
+ gx_dc_null_equal
+};
+#undef gx_dc_type_null
+const gx_device_color_type_t *const gx_dc_type_null = &gx_dc_type_data_null;
+#define gx_dc_type_null (&gx_dc_type_data_null)
+
+private dev_color_proc_load(gx_dc_pure_load);
+private dev_color_proc_fill_rectangle(gx_dc_pure_fill_rectangle);
+private dev_color_proc_fill_masked(gx_dc_pure_fill_masked);
+private dev_color_proc_equal(gx_dc_pure_equal);
+const gx_device_color_type_t gx_dc_type_data_pure = {
+ &st_bytes,
+ gx_dc_pure_load, gx_dc_pure_fill_rectangle, gx_dc_pure_fill_masked,
+ gx_dc_pure_equal
+};
+#undef gx_dc_type_pure
+const gx_device_color_type_t *const gx_dc_type_pure = &gx_dc_type_data_pure;
+#define gx_dc_type_pure (&gx_dc_type_data_pure)
+
+/*
+ * Get the black and white pixel values of a device. The documentation for
+ * the driver API says that map_rgb_color will do the right thing on CMYK
+ * devices. Unfortunately, that isn't true at present, and fixing it is too
+ * much work.
+ */
+gx_color_index
+gx_device_black(gx_device *dev)
+{
+ return
+ (dev->color_info.num_components == 4 ?
+ (*dev_proc(dev, map_cmyk_color))
+ (dev, (gx_color_index)0, (gx_color_index)0, (gx_color_index)0,
+ gx_max_color_value) :
+ (*dev_proc(dev, map_rgb_color))
+ (dev, (gx_color_index)0, (gx_color_index)0, (gx_color_index)0));
+}
+gx_color_index
+gx_device_white(gx_device *dev)
+{
+ return
+ (dev->color_info.num_components == 4 ?
+ (*dev_proc(dev, map_cmyk_color))
+ (dev, (gx_color_index)0, (gx_color_index)0, (gx_color_index)0,
+ (gx_color_index)0) :
+ (*dev_proc(dev, map_rgb_color))
+ (dev, gx_max_color_value, gx_max_color_value, gx_max_color_value));
+}
+
+/* Set a null RasterOp source. */
+private const gx_rop_source_t gx_rop_no_source_0 = {gx_rop_no_source_body(0)};
+void
+gx_set_rop_no_source(const gx_rop_source_t **psource,
+ gx_rop_source_t *pno_source, gx_device *dev)
+{
+ gx_color_index black = gx_device_black(dev);
+
+ if ( black == 0 )
+ *psource = &gx_rop_no_source_0;
+ else {
+ *pno_source = gx_rop_no_source_0;
+ gx_rop_source_set_color(pno_source, black);
+ *psource = pno_source;
+ }
+}
+
+/* ------ Undefined color ------ */
+
+private int
+gx_dc_no_load(gx_device_color *pdevc, const gs_imager_state *ignore_pis,
+ gx_device *ignore_dev, gs_color_select_t ignore_select)
+{
+ 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)
+{
+ gx_device_color filler;
+
+ if (w <= 0 || h <= 0)
+ return 0;
+ if (lop_uses_T(lop))
+ return_error(gs_error_Fatal);
+ color_set_pure(&filler, 0); /* any valid value for dev will do */
+ return gx_dc_pure_fill_rectangle(&filler, x, y, w, h, dev, lop, source);
+}
+
+private int
+gx_dc_no_fill_masked(const gx_device_color *pdevc, const byte *data,
+ int data_x, int raster, gx_bitmap_id id,
+ int x, int y, int w, int h, gx_device *dev,
+ gs_logical_operation_t lop, bool invert)
+{
+ if (w <= 0 || h <= 0)
+ return 0;
+ return_error(gs_error_Fatal);
+}
+
+private bool
+gx_dc_no_equal(const gx_device_color *pdevc1, const gx_device_color *pdevc2)
+{
+ return false;
+}
+
+/* ------ Null color ------ */
+
+private int
+gx_dc_null_load(gx_device_color *pdevc, const gs_imager_state *ignore_pis,
+ gx_device *ignore_dev, gs_color_select_t ignore_select)
+{
+ return 0;
+}
+
+private int
+gx_dc_null_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;
+}
+
+private int
+gx_dc_null_fill_masked(const gx_device_color * pdevc, const byte * data,
+ int data_x, int raster, gx_bitmap_id id,
+ int x, int y, int w, int h, gx_device * dev,
+ gs_logical_operation_t lop, bool invert)
+{
+ return 0;
+}
+
+private bool
+gx_dc_null_equal(const gx_device_color * pdevc1, const gx_device_color * pdevc2)
+{
+ return pdevc2->type == pdevc1->type;
+}
+
+/* ------ Pure color ------ */
+
+private int
+gx_dc_pure_load(gx_device_color * pdevc, const gs_imager_state * ignore_pis,
+ gx_device * ignore_dev, gs_color_select_t ignore_select)
+{
+ 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];
+ gx_rop_source_t no_source;
+
+ colors[0] = colors[1] = pdevc->colors.pure;
+ if (source == NULL)
+ set_rop_no_source(source, no_source, dev);
+ 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);
+ }
+}
+
+/* Fill a mask with a pure color. */
+/* Note that there is no source in this case: the mask is the source. */
+private int
+gx_dc_pure_fill_masked(const gx_device_color * pdevc, const byte * data,
+ int data_x, int raster, gx_bitmap_id id, int x, int y, int w, int h,
+ gx_device * dev, gs_logical_operation_t lop, bool invert)
+{
+ if (lop_no_S_is_T(lop)) {
+ gx_color_index color0, color1;
+
+ if (invert)
+ color0 = pdevc->colors.pure, color1 = gx_no_color_index;
+ else
+ color1 = pdevc->colors.pure, color0 = gx_no_color_index;
+ return (*dev_proc(dev, copy_mono))
+ (dev, data, data_x, raster, id, x, y, w, h, color0, color1);
+ } {
+ gx_color_index scolors[2];
+ gx_color_index tcolors[2];
+
+ scolors[0] = gx_device_black(dev);
+ scolors[1] = gx_device_white(dev);
+ tcolors[0] = tcolors[1] = pdevc->colors.pure;
+ return (*dev_proc(dev, strip_copy_rop))
+ (dev, data, data_x, raster, id, scolors,
+ NULL, tcolors, x, y, w, h, 0, 0,
+ (invert ? rop3_invert_S(lop) : lop) | lop_S_transparent);
+ }
+}
+
+private bool
+gx_dc_pure_equal(const gx_device_color * pdevc1, const gx_device_color * pdevc2)
+{
+ return pdevc2->type == pdevc1->type &&
+ gx_dc_pure_color(pdevc1) == gx_dc_pure_color(pdevc2);
+}
+
+/* ------ Default implementations ------ */
+
+/* Fill a mask with a color by parsing the mask into rectangles. */
+int
+gx_dc_default_fill_masked(const gx_device_color * pdevc, const byte * data,
+ int data_x, int raster, gx_bitmap_id id, int x, int y, int w, int h,
+ gx_device * dev, gs_logical_operation_t lop, bool invert)
+{
+ int lbit = data_x & 7;
+ const byte *row = data + (data_x >> 3);
+ uint one = (invert ? 0 : 0xff);
+ uint zero = one ^ 0xff;
+ int iy;
+
+ for (iy = 0; iy < h; ++iy, row += raster) {
+ const byte *p = row;
+ int bit = lbit;
+ int left = w;
+ int l0;
+
+ while (left) {
+ int run, code;
+
+ /* Skip a run of zeros. */
+ run = byte_bit_run_length[bit][*p ^ one];
+ if (run) {
+ if (run < 8) {
+ if (run >= left)
+ break; /* end of row while skipping */
+ bit += run, left -= run;
+ } else if ((run -= 8) >= left)
+ break; /* end of row while skipping */
+ else {
+ left -= run;
+ ++p;
+ while (left > 8 && *p == zero)
+ left -= 8, ++p;
+ run = byte_bit_run_length_0[*p ^ one];
+ if (run >= left) /* run < 8 unless very last byte */
+ break; /* end of row while skipping */
+ else
+ bit = run & 7, left -= run;
+ }
+ }
+ l0 = left;
+ /* Scan a run of ones, and then paint it. */
+ run = byte_bit_run_length[bit][*p ^ zero];
+ if (run < 8) {
+ if (run >= left)
+ left = 0;
+ else
+ bit += run, left -= run;
+ } else if ((run -= 8) >= left)
+ left = 0;
+ else {
+ left -= run;
+ ++p;
+ while (left > 8 && *p == one)
+ left -= 8, ++p;
+ run = byte_bit_run_length_0[*p ^ zero];
+ if (run >= left) /* run < 8 unless very last byte */
+ left = 0;
+ else
+ bit = run & 7, left -= run;
+ }
+ code = gx_device_color_fill_rectangle(pdevc,
+ x + w - l0, y + iy, l0 - left, 1, dev, lop, NULL);
+ if (code < 0)
+ return code;
+ }
+ }
+ return 0;
+}
diff --git a/pstoraster/gxdcolor.h b/pstoraster/gxdcolor.h
new file mode 100644
index 000000000..015652e24
--- /dev/null
+++ b/pstoraster/gxdcolor.h
@@ -0,0 +1,199 @@
+/* Copyright (C) 1993, 1995, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Device color representation for Ghostscript */
+
+#ifndef gxdcolor_INCLUDED
+# define gxdcolor_INCLUDED
+
+#include "gscsel.h"
+#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;
+
+/*
+ * Note that the following definition depends on the gx_color_index for
+ * black, which may not be 0. Clients must check this and construct
+ * a different null source if necessary.
+ */
+#define gx_rop_no_source_body(black_pixel)\
+ NULL, 0, 0, gx_no_bitmap_id, {black_pixel, black_pixel}, true
+#define gx_rop_source_set_color(prs, pixel)\
+ ((prs)->scolors[0] = (prs)->scolors[1] = (pixel))
+void gx_set_rop_no_source(P3(const gx_rop_source_t **psource,
+ gx_rop_source_t *pno_source, gx_device *dev));
+#define set_rop_no_source(source, no_source, dev)\
+ gx_set_rop_no_source(&(source), &(no_source), dev)
+
+/*
+ * Define the device color structure per se.
+ */
+
+/* The typedef is in gsdcolor.h. */
+/*typedef struct gx_device_color_type_s gx_device_color_type_t; */
+struct gx_device_color_type_s {
+
+ /*
+ * In order to simplify memory management, we use a union, but since
+ * different variants may have different pointer tracing procedures,
+ * we have to define a separate GC structure type for each variant.
+ */
+
+ gs_memory_type_ptr_t stype;
+
+ /*
+ * If necessary and possible, load the halftone or Pattern cache
+ * with the rendering of this color.
+ */
+
+#define dev_color_proc_load(proc)\
+ int proc(P4(gx_device_color *pdevc, const gs_imager_state *pis,\
+ gx_device *dev, gs_color_select_t select))
+ 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));
+
+ /*
+ * Fill a masked region with a color. Nearly all device colors
+ * use the default implementation, which simply parses the mask
+ * into rectangles and calls fill_rectangle. Note that in this
+ * case there is no RasterOp source: the mask is the source.
+ */
+
+#define dev_color_proc_fill_masked(proc)\
+ int proc(P12(const gx_device_color *pdevc, const byte *data, int data_x,\
+ int raster, gx_bitmap_id id, int x, int y, int w, int h,\
+ gx_device *dev, gs_logical_operation_t lop, bool invert))
+ dev_color_proc_fill_masked((*fill_masked));
+
+ /*
+ * Test whether this color is equal to another.
+ */
+
+#define dev_color_proc_equal(proc)\
+ bool proc(P2(const gx_device_color *pdevc1, const gx_device_color *pdevc2))
+ dev_color_proc_equal((*equal));
+
+};
+
+/* Define the default implementation of fill_masked. */
+dev_color_proc_fill_masked(gx_dc_default_fill_masked);
+
+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_type_t
+#define gx_dc_type_none (&gx_dc_type_data_none)
+ gx_dc_type_data_none, /* gxdcolor.c */
+#define gx_dc_type_null (&gx_dc_type_data_null)
+ gx_dc_type_data_null, /* gxdcolor.c */
+#define gx_dc_type_pure (&gx_dc_type_data_pure)
+ gx_dc_type_data_pure, /* gxdcolor.c */
+/*#define gx_dc_type_pattern (&gx_dc_type_data_pattern) */
+ /*gx_dc_type_data_pattern, *//* gspcolor.c */
+#define gx_dc_type_ht_binary (&gx_dc_type_data_ht_binary)
+ gx_dc_type_data_ht_binary, /* gxht.c */
+#define gx_dc_type_ht_colored (&gx_dc_type_data_ht_colored)
+ gx_dc_type_data_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));
+
+/* Get the black and white pixel values of a device. */
+gx_color_index gx_device_black(P1(gx_device *dev));
+gx_color_index gx_device_white(P1(gx_device *dev));
+
+/* 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_select(pdevc, pis, dev, select)\
+ (*(pdevc)->type->load)(pdevc, pis, dev, select)
+#define gx_color_load(pdevc, pis, dev)\
+ gx_color_load_select(pdevc, pis, dev, gs_color_select_texture)
+#define gs_state_color_load(pgs)\
+ gx_color_load((pgs)->dev_color, (const gs_imager_state *)(pgs),\
+ (pgs)->device)
+
+/* 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)
+
+/* Test device colors for equality. */
+#define gx_device_color_equal(pdevc1, pdevc2)\
+ (((pdevc1)->type->equal)(pdevc1, pdevc2))
+
+#endif /* gxdcolor_INCLUDED */
diff --git a/pstoraster/gxdda.h b/pstoraster/gxdda.h
new file mode 100644
index 000000000..e0fe391e1
--- /dev/null
+++ b/pstoraster/gxdda.h
@@ -0,0 +1,158 @@
+/* Copyright (C) 1994, 1995, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Requires gxfixed.h */
+
+#ifndef gxdda_INCLUDED
+# define gxdda_INCLUDED
+
+/*
+ * 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 + (N-R)/N where Q and R are integers, 0 < R <= N,
+ * with the following auxiliary values:
+ * dQ = floor(D/N)
+ * dR = D mod N (0 <= dR < N)
+ * NdR = N - dR
+ * Then at each iteration we do:
+ * Q += dQ;
+ * if ( R > dR ) R -= dR; else ++Q, R += NdR;
+ * These formulas work regardless of the sign of D, and never let R go
+ * out of range.
+ */
+/* In the following structure definitions, ntype must be an unsigned type. */
+#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;
+/*
+ * Define a pair of DDAs for iterating along an arbitrary line.
+ */
+ typedef struct gx_dda_fixed_point_s {
+ gx_dda_fixed x, y;
+ } gx_dda_fixed_point;
+/*
+ * 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.
+ * Note that since dR + NdR = N, this quantity must be the same in both
+ * fromstep and tostep.
+ */
+#define dda_step_add(tostep, fromstep)\
+ (tostep).dQ +=\
+ ((tostep).dR < (fromstep).NdR ?\
+ ((tostep).dR += (fromstep).dR, (tostep).NdR -= (fromstep).dR,\
+ (fromstep).dQ) :\
+ ((tostep).dR -= (fromstep).NdR, (tostep).NdR += (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)
+#define dda_current_fixed2int(dda)\
+ fixed2int_var(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)
+/*
+ * Advance a DDA by an arbitrary number of steps.
+ * This implementation is very inefficient; we'll improve it if needed.
+ */
+#define dda_state_advance(dstate, dstep, nsteps)\
+ BEGIN\
+ uint n_ = (nsteps);\
+ (dstate).Q += (dstep).dQ * (nsteps);\
+ while ( n_-- )\
+ if ( (dstate).R > (dstep).dR ) (dstate).R -= (dstep).dR;\
+ else (dstate).R += (dstep).NdR, (dstate).Q++;\
+ END
+#define dda_advance(dda, nsteps)\
+ dda_state_advance((dda).state, (dda).step, nsteps)
+/*
+ * Translate the position of a DDA by a given amount.
+ */
+#define dda_state_translate(dstate, delta)\
+ ((dstate).Q += (delta))
+#define dda_translate(dda, delta)\
+ dda_state_translate((dda).state, delta)
+
+#endif /* gxdda_INCLUDED */
diff --git a/pstoraster/gxdevcli.h b/pstoraster/gxdevcli.h
new file mode 100644
index 000000000..603cedb95
--- /dev/null
+++ b/pstoraster/gxdevcli.h
@@ -0,0 +1,877 @@
+/* Copyright (C) 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Definitions for device clients */
+
+#ifndef gxdevcli_INCLUDED
+# define gxdevcli_INCLUDED
+
+#include "std.h" /* for FILE */
+#include "gscompt.h"
+#include "gsdcolor.h"
+#include "gsmatrix.h"
+#include "gsiparam.h" /* requires gsmatrix.h */
+#include "gsrefct.h"
+#include "gsropt.h"
+#include "gsstruct.h"
+#include "gsxfont.h"
+#include "gxbitmap.h"
+#include "gxcindex.h"
+#include "gxcvalue.h"
+#include "gxfixed.h"
+#include "gxtext.h"
+
+/* See Drivers.htm for documentation of the driver interface. */
+
+#ifndef gx_device_DEFINED
+# define gx_device_DEFINED
+typedef struct gx_device_s gx_device;
+
+#endif
+
+/* ---------------- Auxiliary types and structures ---------------- */
+
+/* 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
+/* We need an abstract type for the image enumeration state, */
+/* for begin[_typed]_image. */
+#ifndef gx_image_enum_common_t_DEFINED
+# define gx_image_enum_common_t_DEFINED
+typedef struct gx_image_enum_common_s gx_image_enum_common_t;
+
+#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 parameters passed to get_bits_rectangle. */
+#ifndef gs_get_bits_params_DEFINED
+# define gs_get_bits_params_DEFINED
+typedef struct gs_get_bits_params_s gs_get_bits_params_t;
+#endif
+
+/* Define the structure for device color capabilities. */
+typedef struct gx_device_color_info_s {
+ int num_components; /* doesn't include alpha: */
+ /* 0 = alpha only, 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 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 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 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 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 */\
+ rc_header rc; /* reference count from gstates, */\
+ /* +1 if not internal device */\
+ 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 */\
+ int NumCopies;\
+ bool NumCopies_set;\
+ 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 procs /* object 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)->procs.p)
+#define set_dev_proc(dev, p, proc) ((dev)->procs.p = (proc))
+#define fill_dev_proc(dev, p, dproc)\
+ if ( dev_proc(dev, p) == 0 ) set_dev_proc(dev, p, dproc)
+#define assign_dev_procs(todev, fromdev)\
+ ((todev)->procs = (fromdev)->procs)
+
+/* ---------------- 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)\
+ const 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 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; */
+ /* begin_image and image_data changed in 4.30, */
+ /* begin_image changed in 5.23. */
+
+#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, const gs_int_rect *prect,\
+ const gx_drawing_color *pdcolor, const gx_clip_path *pcpath,\
+ gs_memory_t *memory, gx_image_enum_common_t **pinfo))
+#define dev_proc_begin_image(proc)\
+ dev_t_proc_begin_image(proc, gx_device)
+
+ /* OBSOLETED in release 5.23 */
+
+#define dev_t_proc_image_data(proc, dev_t)\
+ int proc(P6(dev_t *dev,\
+ gx_image_enum_common_t *info, const byte **planes, int data_x,\
+ uint raster, int height))
+#define dev_proc_image_data(proc)\
+ dev_t_proc_image_data(proc, gx_device)
+
+ /* OBSOLETED in release 5.23 */
+
+#define dev_t_proc_end_image(proc, dev_t)\
+ int proc(P3(dev_t *dev,\
+ gx_image_enum_common_t *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)
+
+ /* Added in release 4.20 */
+
+#define dev_t_proc_get_clipping_box(proc, dev_t)\
+ void proc(P2(dev_t *dev, gs_fixed_rect *pbox))
+#define dev_proc_get_clipping_box(proc)\
+ dev_t_proc_get_clipping_box(proc, gx_device)
+
+ /* Added in release 5.20, changed in 5.23 */
+
+#define dev_t_proc_begin_typed_image(proc, dev_t)\
+ int proc(P9(dev_t *dev,\
+ const gs_imager_state *pis, const gs_matrix *pmat,\
+ const gs_image_common_t *pim, const gs_int_rect *prect,\
+ const gx_drawing_color *pdcolor, const gx_clip_path *pcpath,\
+ gs_memory_t *memory, gx_image_enum_common_t **pinfo))
+#define dev_proc_begin_typed_image(proc)\
+ dev_t_proc_begin_typed_image(proc, gx_device)
+
+ /* Added in release 5.20 */
+
+#define dev_t_proc_get_bits_rectangle(proc, dev_t)\
+ int proc(P4(dev_t *dev, const gs_int_rect *prect,\
+ gs_get_bits_params_t *params, gs_int_rect **unread))
+#define dev_proc_get_bits_rectangle(proc)\
+ dev_t_proc_get_bits_rectangle(proc, gx_device)
+
+#define dev_t_proc_map_color_rgb_alpha(proc, dev_t)\
+ int proc(P3(dev_t *dev,\
+ gx_color_index color, gx_color_value rgba[4]))
+#define dev_proc_map_color_rgb_alpha(proc)\
+ dev_t_proc_map_color_rgb_alpha(proc, gx_device)
+
+#define dev_t_proc_create_compositor(proc, dev_t)\
+ int proc(P5(dev_t *dev,\
+ gx_device **pcdev, const gs_composite_t *pcte,\
+ const gs_imager_state *pis, gs_memory_t *memory))
+#define dev_proc_create_compositor(proc)\
+ dev_t_proc_create_compositor(proc, gx_device)\
+
+ /* Added in release 5.23 */
+
+#define dev_t_proc_get_hardware_params(proc, dev_t)\
+ int proc(P2(dev_t *dev, gs_param_list *plist))
+#define dev_proc_get_hardware_params(proc)\
+ dev_t_proc_get_hardware_params(proc, gx_device)
+
+ /* Added in release 5.24 */
+
+ /* ... text_begin ... see gxtext.h for definition */
+
+/* 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((*obsolete_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);\
+ dev_t_proc_get_clipping_box((*get_clipping_box), dev_t);\
+ dev_t_proc_begin_typed_image((*begin_typed_image), dev_t);\
+ dev_t_proc_get_bits_rectangle((*get_bits_rectangle), dev_t);\
+ dev_t_proc_map_color_rgb_alpha((*map_color_rgb_alpha), dev_t);\
+ dev_t_proc_create_compositor((*create_compositor), dev_t);\
+ dev_t_proc_get_hardware_params((*get_hardware_params), dev_t);\
+ dev_t_proc_text_begin((*text_begin), dev_t);\
+}
+/*
+ * Provide procedures for passing image data. image_data and end_image
+ * are the equivalents of the obsolete driver procedures. image_plane_data
+ * was originally planned as a driver procedure, but is now associated with
+ * the image enumerator, like the other two.
+ */
+
+typedef struct gx_image_plane_s {
+ const byte *data;
+ int data_x;
+ uint raster;
+} gx_image_plane_t;
+
+#define image_enum_proc_plane_data(proc)\
+ int proc(P4(gx_device *dev,\
+ gx_image_enum_common_t *info, const gx_image_plane_t *planes,\
+ int height))
+#define gx_device_begin_image(dev, pis, pim, format, prect, pdcolor, pcpath, memory, pinfo)\
+ ((*dev_proc(dev, begin_image))\
+ (dev, pis, pim, format, prect, pdcolor, pcpath, memory, pinfo))
+#define gx_device_begin_typed_image(dev, pis, pmat, pim, prect, pdcolor, pcpath, memory, pinfo)\
+ ((*dev_proc(dev, begin_typed_image))\
+ (dev, pis, pmat, pim, prect, pdcolor, pcpath, memory, pinfo))
+
+/*
+ * The driver-like procedures gx_device_{image_data, image_plane_data,
+ * end_image} are now DEPRECATED and will eventually be removed.
+ * Their replacements no longer take an ignored dev argument.
+ */
+int gx_image_data(P5(gx_image_enum_common_t *info, const byte **planes,
+ int data_x, uint raster, int height));
+int gx_image_plane_data(P3(gx_image_enum_common_t *info,
+ const gx_image_plane_t *planes, int height));
+int gx_image_end(P2(gx_image_enum_common_t *info, bool draw_last));
+
+#define gx_device_image_data(dev, info, planes, data_x, raster, height)\
+ gx_image_data(info, planes, data_x, raster, height)
+#define gx_device_image_plane_data(dev, info, planes, height)\
+ gx_image_plane_data(info, planes, height)
+#define gx_device_end_image(dev, info, draw_last)\
+ gx_image_end(info, draw_last)
+
+/* 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 */
+/* We use vacuous enum/reloc procedures, rather than 0, so that */
+/* gx_device can have subclasses. */
+#define public_st_device() /* in gsdevice.c */\
+ gs_public_st_complex_only(st_device, gx_device, "gx_device",\
+ 0, gs_no_struct_enum_ptrs, gs_no_struct_reloc_ptrs, 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 forwarding device forwards all non-display operations, and possibly
+ * some imaging operations (possibly transformed in some way), to another
+ * device called the "target". This is used for many different purposes
+ * internally, including clipping, banding, image and pattern accumulation,
+ * compositing, halftoning, and the null device.
+ */
+#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 const gx_device_null gs_null_device;
+
+#define gx_device_is_null(dev)\
+ ((dev)->dname == gs_null_device.dname)
+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
+
+/*
+ * Initialize a just-allocated device from a prototype.
+ * internal = true means initialize the reference count to 0,
+ * false means initialize to 1.
+ */
+void gx_device_init(P4(gx_device * dev, const gx_device * proto,
+ gs_memory_t * mem, bool internal));
+
+/* 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)
+
+/*
+ * Temporarily install a null device, or a special device such as
+ * a clipping or cache device.
+ */
+void gx_set_device_only(P2(gs_state *, gx_device *));
+
+/* Close a device. */
+int gs_closedevice(P1(gx_device *));
+
+/* ------ Device types (an unused concept right now) ------ */
+
+#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 /* gxdevcli_INCLUDED */
diff --git a/pstoraster/gxdevice.h b/pstoraster/gxdevice.h
new file mode 100644
index 000000000..b0fb7b38b
--- /dev/null
+++ b/pstoraster/gxdevice.h
@@ -0,0 +1,453 @@
+/* Copyright (C) 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Definitions for device implementors */
+
+#ifndef gxdevice_INCLUDED
+# define gxdevice_INCLUDED
+
+#include "stdio_.h" /* for FILE */
+#include "gxdevcli.h"
+#include "gsparam.h"
+/*
+ * Drivers still use gs_malloc and gs_free, so include the interface for
+ * these. (Eventually they should go away.)
+ */
+#include "gsmalloc.h"
+
+/* ---------------- Auxiliary types and structures ---------------- */
+
+/* Define default pages sizes. */
+/* 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
+
+/* ---------------- Device structure ---------------- */
+
+/*
+ * 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 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, { 0 } /*rc*/,\
+ open_init() /*is_open, max_fill_band */
+/* color_info goes here */
+/*
+ * The MetroWerks compiler has some bizarre bug that produces a spurious
+ * error message if the width and/or height are defined as 0 below,
+ * unless we use the +/- workaround in the next macro.
+ */
+#define std_device_part2_(width, height, x_dpi, y_dpi)\
+ width, height,\
+ { (((width) * 72.0 + 0.5) - 0.5) / (x_dpi),\
+ (((height) * 72.0 + 0.5) - 0.5) / (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, 1, 0/*false*/, 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, stype, w, h, xdpi, ydpi, 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, 0,\
+ w, h, xdpi, ydpi,\
+ open_init_closed, dci_black_and_white_, no_margins_)
+
+#define std_device_std_body_type_open(dtype, pprocs, dname, stype, w, h, xdpi, ydpi)\
+ std_device_body_with_macros_(dtype, pprocs, dname, stype,\
+ w, h, xdpi, ydpi,\
+ open_init_open, dci_black_and_white_, no_margins_)
+
+#define std_device_std_body_open(dtype, pprocs, dname, w, h, xdpi, ydpi)\
+ std_device_std_body_type_open(dtype, pprocs, dname, 0, w, h, xdpi, ydpi)
+
+#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_type_body(dtype, pprocs, dname, stype, w, h, xdpi, ydpi, ncomp, depth, mg, mc, dg, dc)\
+ std_device_part1_(dtype, pprocs, dname, stype, open_init_closed),\
+ dci_values(ncomp, depth, mg, mc, dg, dc),\
+ std_device_part2_(w, h, xdpi, ydpi),\
+ offset_margin_values(0, 0, 0, 0, 0, 0),\
+ std_device_part3_()
+
+#define std_device_dci_body(dtype, pprocs, dname, w, h, xdpi, ydpi, ncomp, depth, mg, mc, dg, dc)\
+ std_device_dci_type_body(dtype, pprocs, dname, 0,\
+ w, h, xdpi, ydpi, ncomp, depth, mg, mc, dg, dc)
+
+#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_()
+
+/* ---------------- Default implementations ---------------- */
+
+/* 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_no_get_bits); /* gives error */
+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 */
+dev_proc_copy_rop(gx_default_copy_rop);
+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 */
+dev_proc_strip_copy_rop(gx_default_strip_copy_rop);
+dev_proc_get_clipping_box(gx_default_get_clipping_box);
+dev_proc_get_clipping_box(gx_get_largest_clipping_box);
+dev_proc_begin_typed_image(gx_default_begin_typed_image);
+dev_proc_get_bits_rectangle(gx_no_get_bits_rectangle); /* gives error */
+dev_proc_get_bits_rectangle(gx_default_get_bits_rectangle);
+dev_proc_map_color_rgb_alpha(gx_default_map_color_rgb_alpha);
+dev_proc_create_compositor(gx_no_create_compositor);
+/* default is for ordinary "leaf" devices, non_imaging is for */
+/* devices that only care about coverage and not contents. */
+dev_proc_create_compositor(gx_default_create_compositor);
+dev_proc_create_compositor(gx_non_imaging_create_compositor);
+dev_proc_get_hardware_params(gx_default_get_hardware_params);
+dev_proc_text_begin(gx_default_text_begin);
+
+/* 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_fill_rectangle(gx_forward_fill_rectangle);
+dev_proc_tile_rectangle(gx_forward_tile_rectangle);
+dev_proc_copy_mono(gx_forward_copy_mono);
+dev_proc_copy_color(gx_forward_copy_color);
+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_copy_alpha(gx_forward_copy_alpha);
+dev_proc_get_band(gx_forward_get_band);
+dev_proc_copy_rop(gx_forward_copy_rop);
+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);
+#define gx_forward_image_data gx_default_image_data
+#define gx_forward_end_image gx_default_end_image
+dev_proc_strip_tile_rectangle(gx_forward_strip_tile_rectangle);
+dev_proc_strip_copy_rop(gx_forward_strip_copy_rop);
+dev_proc_get_clipping_box(gx_forward_get_clipping_box);
+dev_proc_begin_typed_image(gx_forward_begin_typed_image);
+dev_proc_get_bits_rectangle(gx_forward_get_bits_rectangle);
+dev_proc_map_color_rgb_alpha(gx_forward_map_color_rgb_alpha);
+/* There is no forward_create_compositor (see Drivers.htm). */
+dev_proc_get_hardware_params(gx_forward_get_hardware_params);
+dev_proc_text_begin(gx_forward_text_begin);
+
+/* ---------------- Implementation utilities ---------------- */
+
+/* Fill in the GC structure descriptor for a device. */
+/* This is only called during initialization. */
+void gx_device_make_struct_type(P2(gs_memory_struct_type_t *,
+ const gx_device *));
+
+/* 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 *));
+
+/*
+ * Copy device parameters back from a target. This copies all standard
+ * parameters related to page size and resolution, plus color_info.
+ */
+void gx_device_copy_params(P2(gx_device *to, const gx_device *from));
+
+/* Open the output file for a device. */
+int gx_device_open_output_file(P5(const gx_device * dev, const char *fname,
+ bool binary, bool positionable,
+ FILE ** pfile));
+
+/*
+ * Determine whether a given device needs to halftone. Eventually this
+ * should take an imager state as an additional argument.
+ */
+#define gx_device_must_halftone(dev)\
+ ((gx_device_has_color(dev) ? (dev)->color_info.max_color :\
+ (dev)->color_info.max_gray) < 31)
+#define gx_color_device_must_halftone(dev)\
+ ((dev)->color_info.max_gray < 31)
+
+/*
+ * Device procedures that draw into rectangles need to clip the coordinates
+ * to the rectangle ((0,0),(dev->width,dev->height)). The following macros
+ * do the clipping. They assume that the arguments of the procedure are
+ * named dev, x, y, w, and h, and may modify the arguments (other than dev).
+ *
+ * For procedures that fill a region, dev, x, y, w, and h are the only
+ * relevant arguments. For procedures that copy bitmaps, see below.
+ *
+ * The following group of macros for region-filling procedures clips
+ * specific edges of the supplied rectangle, as indicated by the macro name.
+ */
+#define fit_fill_xy(dev, x, y, w, h)\
+ BEGIN\
+ if ( (x | y) < 0 ) {\
+ if ( x < 0 )\
+ w += x, x = 0;\
+ if ( y < 0 )\
+ h += y, y = 0;\
+ }\
+ END
+#define fit_fill_y(dev, y, h)\
+ BEGIN\
+ if ( y < 0 )\
+ h += y, y = 0;\
+ END
+#define fit_fill_w(dev, x, w)\
+ BEGIN\
+ if ( w > dev->width - x )\
+ w = dev->width - x;\
+ END
+#define fit_fill_h(dev, y, h)\
+ BEGIN\
+ if ( h > dev->height - y )\
+ h = dev->height - y;\
+ END
+#define fit_fill_xywh(dev, x, y, w, h)\
+ BEGIN\
+ fit_fill_xy(dev, x, y, w, h);\
+ fit_fill_w(dev, x, w);\
+ fit_fill_h(dev, y, h);\
+ END
+/*
+ * Clip all edges, and return from the procedure if the result is empty.
+ */
+#define fit_fill(dev, x, y, w, h)\
+ BEGIN\
+ fit_fill_xywh(dev, x, y, w, h);\
+ if ( w <= 0 || h <= 0 )\
+ return 0;\
+ END
+
+/*
+ * For driver procedures that copy bitmaps (e.g., copy_mono, copy_color),
+ * clipping the destination region also may require adjusting the pointer to
+ * the source data. In addition to dev, x, y, w, and h, the clipping macros
+ * for these procedures reference data, data_x, raster, and id; they may
+ * modify the values of data, data_x, and id.
+ *
+ * Clip the edges indicated by the macro name.
+ */
+#define fit_copy_xyw(dev, data, data_x, raster, id, x, y, w, h)\
+ BEGIN\
+ 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 ( w > dev->width - x )\
+ w = dev->width - x;\
+ END
+/*
+ * Clip all edges, and return from the procedure if the result is empty.
+ */
+#define fit_copy(dev, data, data_x, raster, id, x, y, w, h)\
+ BEGIN\
+ fit_copy_xyw(dev, data, data_x, raster, id, x, y, w, h);\
+ if ( h > dev->height - y )\
+ h = dev->height - y;\
+ if ( w <= 0 || h <= 0 )\
+ return 0;\
+ END
+
+/* ---------------- Media parameters ---------------- */
+
+/* Define the InputAttributes and OutputAttributes of a device. */
+/* The device get_params procedure would call these. */
+
+typedef struct gdev_input_media_s {
+ float PageSize[4]; /* nota bene */
+ const char *MediaColor;
+ float MediaWeight;
+ const char *MediaType;
+} gdev_input_media_t;
+
+#define gdev_input_media_default_values { 0, 0, 0, 0 }, 0, 0, 0
+extern const gdev_input_media_t gdev_input_media_default;
+
+void gdev_input_media_init(P1(gdev_input_media_t * pim));
+
+int gdev_begin_input_media(P3(gs_param_list * mlist, gs_param_dict * pdict,
+ int count));
+
+int gdev_write_input_page_size(P4(int index, gs_param_dict * pdict,
+ floatp width_points, floatp height_points));
+
+int gdev_write_input_media(P3(int index, gs_param_dict * pdict,
+ const gdev_input_media_t * pim));
+
+int gdev_end_input_media(P2(gs_param_list * mlist, gs_param_dict * pdict));
+
+typedef struct gdev_output_media_s {
+ const char *OutputType;
+} gdev_output_media_t;
+
+#define gdev_output_media_default_values 0
+extern const gdev_output_media_t gdev_output_media_default;
+
+int gdev_begin_output_media(P3(gs_param_list * mlist, gs_param_dict * pdict,
+ int count));
+
+int gdev_write_output_media(P3(int index, gs_param_dict * pdict,
+ const gdev_output_media_t * pom));
+
+int gdev_end_output_media(P2(gs_param_list * mlist, gs_param_dict * pdict));
+
+#endif /* gxdevice_INCLUDED */
diff --git a/pstoraster/gxdevmem.h b/pstoraster/gxdevmem.h
new file mode 100644
index 000000000..c10d3d667
--- /dev/null
+++ b/pstoraster/gxdevmem.h
@@ -0,0 +1,170 @@
+/* Copyright (C) 1989, 1995, 1997 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Requires gxdevice.h */
+
+#ifndef gxdevmem_INCLUDED
+# define gxdevmem_INCLUDED
+
+/*
+ * 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.
+ */
+ulong gdev_mem_data_size(P3(const gx_device_memory *, int, int));
+
+#define gdev_mem_bitmap_size(mdev)\
+ gdev_mem_data_size(mdev, (mdev)->width, (mdev)->height)
+/*
+ * Do the inverse computation: given the device width and a buffer size,
+ * compute the maximum height.
+ */
+int gdev_mem_max_height(P3(const gx_device_memory *, int, ulong));
+
+/*
+ * Compute the raster (data 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));
+
+/*
+ * Open a memory device, only setting line pointers to a subset of its
+ * scan lines. Banding devices use this (see gxclread.c).
+ */
+int gdev_mem_open_scan_lines(P2(gx_device_memory *mdev, int setup_height));
+
+/* 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 *));
+
+#endif /* gxdevmem_INCLUDED */
diff --git a/pstoraster/gxdevrop.h b/pstoraster/gxdevrop.h
new file mode 100644
index 000000000..037fb33f6
--- /dev/null
+++ b/pstoraster/gxdevrop.h
@@ -0,0 +1,35 @@
+/* Copyright (C) 1996, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Extension of gxdevice.h for RasterOp */
+
+#ifndef gxdevrop_INCLUDED
+# define gxdevrop_INCLUDED
+
+/* Define an unaligned implementation of [strip_]copy_rop. */
+dev_proc_copy_rop(gx_copy_rop_unaligned);
+dev_proc_strip_copy_rop(gx_strip_copy_rop_unaligned);
+
+#endif /* gxdevrop_INCLUDED */
diff --git a/pstoraster/gxdht.h b/pstoraster/gxdht.h
new file mode 100644
index 000000000..cceda256d
--- /dev/null
+++ b/pstoraster/gxdht.h
@@ -0,0 +1,272 @@
+/* Copyright (C) 1995, 1996, 1997 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Definition of device halftones */
+
+#ifndef gxdht_INCLUDED
+# define gxdht_INCLUDED
+
+#include "gsrefct.h"
+#include "gscsepnm.h"
+#include "gxarith.h" /* for igcd */
+#include "gxhttype.h"
+
+/*
+ * We represent a halftone tile as a rectangular super-cell consisting of
+ * multiple copies of a multi-cell whose corners lie on integral
+ * coordinates, which in turn is a parallelogram (normally square) array of
+ * basic parallelogram (normally square) 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 U' and V') 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('). From these definitions, the basic cell has
+ * an area of B = U*U' + V*V' = (M*M' + N*N') / R*R' pixels, and the
+ * multi-cell has an area of C = B * R*R' = 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). 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'. 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 halftone setup
+ * routines 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.
+ */
+typedef struct gx_ht_cell_params_s {
+ /* Defining values. M * M1 != 0 or N * N1 != 0; R > 0, R1 > 0. */
+ /* R and D are short rather than ushort so that we don't get */
+ /* unsigned results from arithmetic. */
+ short M, N, R;
+ short M1, N1, R1;
+ /* Derived values. */
+ ulong C;
+ short D, D1;
+ uint W, W1;
+ int S;
+} gx_ht_cell_params_t;
+
+/* Compute the derived values from the defining values. */
+void gx_compute_cell_values(P1(gx_ht_cell_params_t *));
+
+/*
+ * 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;
+
+#ifndef gx_ht_order_DEFINED
+# define gx_ht_order_DEFINED
+typedef struct gx_ht_order_s gx_ht_order;
+
+#endif
+struct gx_ht_order_s {
+ gx_ht_cell_params_t params; /* parameters defining the cells */
+ ushort width;
+ ushort height;
+ ushort raster;
+ ushort shift;
+ ushort orig_height;
+ ushort orig_shift;
+ uint full_height;
+ 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 */
+};
+
+#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).
+ *
+ * Multi-component halftone orders may be required even in Level 1 systems,
+ * because they are needed for setcolorscreen.
+ *
+ * NOTE: it is assumed that all subsidiary structures of device halftones
+ * (the components array, and the bits, levels, cache, and transfer members
+ * of any gx_ht_orders, both the default order and any component orders) are
+ * allocated with the same allocator as the device halftone itself.
+ */
+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 gsht.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
+/* We only export st_ht_order_component_element for use in banding. */
+extern_st(st_ht_order_component_element);
+#define public_st_ht_order_comp_element() /* in gsht.c */\
+ gs_public_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; /* must be first, for subclassing */
+ rc_header rc;
+ gs_id id; /* the id changes whenever the data change */
+ /*
+ * We have to keep the halftone type so that we can pass it
+ * through the band list for gx_imager_dev_ht_install.
+ */
+ gs_halftone_type type;
+ gx_ht_order_component *components;
+ uint num_comp;
+ /* The following are computed from the above. */
+ uint color_indices[4];
+ 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)
+
+/* Release a gx_device_halftone by freeing its components. */
+/* (Don't free the gx_device_halftone itself.) */
+void gx_device_halftone_release(P2(gx_device_halftone * pdht,
+ gs_memory_t * mem));
+
+#endif /* gxdht_INCLUDED */
diff --git a/pstoraster/gxdither.c b/pstoraster/gxdither.c
new file mode 100644
index 000000000..b76b4fae1
--- /dev/null
+++ b/pstoraster/gxdither.c
@@ -0,0 +1,502 @@
+/* Copyright (C) 1989, 1995, 1996, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+#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 *const 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..803333f32
--- /dev/null
+++ b/pstoraster/gxdither.h
@@ -0,0 +1,76 @@
+/* Copyright (C) 1994, 1995, 1996, 1997 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Interface to gxdither.c */
+
+#ifndef gxdither_INCLUDED
+# define gxdither_INCLUDED
+
+#ifndef gx_device_halftone_DEFINED
+# define gx_device_halftone_DEFINED
+typedef struct gx_device_halftone_s gx_device_halftone;
+
+#endif
+
+/*
+ * Note that in the procedures below, the colors are specified by fracs,
+ * but the alpha value is a gx_color_value. This is a design flaw that
+ * we might be able to fix eventually.
+ */
+
+/* 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_alpha(gray, alpha, pdevc, pis, dev, select)\
+ gx_render_device_gray(gray, alpha, pdevc, dev, pis->dev_ht,\
+ &pis->screen_phase[select])
+#define gx_render_gray(gray, pdevc, pis, dev, select)\
+ gx_render_gray_alpha(gray, pis->alpha, pdevc, pis, dev, select)
+
+/* 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_alpha(r, g, b, w, a, cmyk, pdevc, pis, dev, select)\
+ gx_render_device_color(r, g, b, w, cmyk, a, pdevc, dev,\
+ pis->dev_ht, &pis->screen_phase[select])
+#define gx_render_color(r, g, b, w, cmyk, pdevc, pis, dev, select)\
+ gx_render_color_alpha(r, g, b, w, pis->alpha, cmyk, pdevc, pis, dev, select)
+#define gx_render_rgb(r, g, b, pdevc, pis, dev, select)\
+ gx_render_color(r, g, b, frac_0, false, pdevc, pis, dev, select)
+#define gx_render_cmyk(c, m, y, k, pdevc, pis, dev, select)\
+ gx_render_color(c, m, y, k, true, pdevc, pis, dev, select)
+#define gx_render_rgb_alpha(r, g, b, a, pdevc, pis, dev, select)\
+ gx_render_color_alpha(r, g, b, frac_0, a, false, pdevc, pis, dev, select)
+
+#endif /* gxdither_INCLUDED */
diff --git a/pstoraster/gxfarith.h b/pstoraster/gxfarith.h
new file mode 100644
index 000000000..002d0187a
--- /dev/null
+++ b/pstoraster/gxfarith.h
@@ -0,0 +1,144 @@
+/* Copyright (C) 1993, 1995, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Floating point arithmetic macros for Ghostscript library */
+
+#ifndef gxfarith_INCLUDED
+# define gxfarith_INCLUDED
+
+#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, hit exact values at multiples of 90 degrees, and 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;
+ bool orthogonal; /* angle is multiple of 90 degrees */
+} gs_sincos_t;
+void gs_sincos_degrees(P2(double angle, gs_sincos_t * psincos));
+
+#endif /* gxfarith_INCLUDED */
diff --git a/pstoraster/gxfcache.h b/pstoraster/gxfcache.h
new file mode 100644
index 000000000..93dbf657d
--- /dev/null
+++ b/pstoraster/gxfcache.h
@@ -0,0 +1,270 @@
+/* Copyright (C) 1992, 1995, 1997 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Requires gsfont.h */
+
+#ifndef gxfcache_INCLUDED
+# define gxfcache_INCLUDED
+
+#include "gsuid.h"
+#include "gsxfont.h"
+#include "gxbcache.h"
+#include "gxftype.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; in this case,
+ * we also need the FontType as part of the key.
+ * Note that because of the dependency on StrokeWidth, we can't cache
+ * fonts with non-zero PaintType.
+ * 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 */
+ font_type FontType; /* (part of key if UID is valid) */
+ 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 cc_const_bits(cc) ((const 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 */
+
+/* ------ 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 *struct_memory;
+ gs_memory_t *bits_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 */
+ gs_glyph_mark_proc_t mark_glyph;
+ void *mark_glyph_data; /* closure data */
+} 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;
+ /* Scanning cache for GC */
+ uint enum_index; /* index (N) */
+ uint enum_offset; /* ccache.table[offset] is N'th non-zero entry */
+};
+
+#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) m(3,ccache.mark_glyph_data)
+#define st_font_dir_max_ptrs 4
+
+/* Character cache procedures (in gxccache.c and gxccman.c) */
+int gx_char_cache_alloc(P7(gs_memory_t * struct_mem, gs_memory_t * bits_mem,
+ gs_font_dir * pdir, uint bmax, uint mmax,
+ uint cmax, uint upper));
+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));
+
+#endif /* gxfcache_INCLUDED */
diff --git a/pstoraster/gxfcmap.h b/pstoraster/gxfcmap.h
new file mode 100644
index 000000000..dc3d64a21
--- /dev/null
+++ b/pstoraster/gxfcmap.h
@@ -0,0 +1,101 @@
+/* Copyright (C) 1997 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Internal CMap data definition */
+
+/* This file should be called gxcmap.h, except that name is already used. */
+
+#ifndef gxfcmap_INCLUDED
+# define gxfcmap_INCLUDED
+
+#include "gsfcmap.h"
+#include "gsuid.h"
+
+/*
+ * The main body of data in a CMap is two code maps, one for defined
+ * characters, one for notdefs. Each code map is a multi-level tree,
+ * one level per byte decoded from the input string. Each node of
+ * the tree may be:
+ * a character code (1-4 bytes)
+ * a character name (gs_glyph)
+ * a CID (gs_glyph)
+ * a subtree
+ */
+typedef enum {
+ cmap_char_code,
+ cmap_glyph, /* character name or CID */
+ cmap_subtree
+} gx_code_map_type;
+typedef struct gx_code_map_s gx_code_map;
+struct gx_code_map_s {
+ byte first; /* first char code covered by this node */
+ byte last; /* last char code ditto */
+ uint /*gx_code_map_type */ type:2;
+ uint num_bytes1:2; /* # of bytes -1 for char_code */
+ uint /*bool */ add_offset:1; /* if set, add char - first to ccode / glyph */
+ /* We would like to combine the two unions into a union of structs, */
+ /* but no compiler seems to do the right thing about packing. */
+ union bd_ {
+ byte font_index; /* for leaf, font index */
+ /* (only non-zero if rearranged font) */
+ byte count1; /* for subtree, # of entries -1 */
+ } byte_data;
+ union d_ {
+ gs_char ccode; /* num_bytes bytes */
+ gs_glyph glyph;
+ gx_code_map *subtree; /* [count] */
+ } data;
+ gs_cmap *cmap; /* point back to CMap for GC mark proc */
+};
+
+/* The GC information for a gx_code_map is complex, because names must be */
+/* traced. */
+extern_st(st_code_map);
+extern_st(st_code_map_element);
+#define public_st_code_map() /* in gsfcmap.c */\
+ gs_public_st_composite(st_code_map, gx_code_map, "gx_code_map",\
+ code_map_enum_ptrs, code_map_reloc_ptrs)
+#define public_st_code_map_element() /* in gsfcmap.c */\
+ gs_public_st_element(st_code_map_element, gx_code_map, "gx_code_map[]",\
+ code_map_elt_enum_ptrs, code_map_elt_reloc_ptrs, st_code_map)
+
+/* A CMap proper is relatively simple. */
+struct gs_cmap_s {
+ gs_cid_system_info CIDSystemInfo; /* must be first */
+ gs_uid uid;
+ int WMode;
+ gx_code_map def; /* defined characters (cmap_subtree) */
+ gx_code_map notdef; /* notdef characters (cmap_subtree) */
+ gs_glyph_mark_proc_t mark_glyph; /* glyph marking procedure for GC */
+ void *mark_glyph_data; /* closure data */
+};
+
+/*extern_st(st_cmap); */
+#define public_st_cmap() /* in gsfcmap.c */\
+ gs_public_st_suffix_add4(st_cmap, gs_cmap, "gs_cmap",\
+ cmap_enum_ptrs, cmap_reloc_ptrs, st_cid_system_info,\
+ uid.xvalues, def.data.subtree, notdef.data.subtree, mark_glyph_data)
+
+#endif /* gxfcmap_INCLUDED */
diff --git a/pstoraster/gxfill.c b/pstoraster/gxfill.c
new file mode 100644
index 000000000..caf23647e
--- /dev/null
+++ b/pstoraster/gxfill.c
@@ -0,0 +1,1543 @@
+/* Copyright (C) 1989, 1995, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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) :\
+ (INCR_EXPR(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)
+{
+ dlprintf5("[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));
+ dlprintf5(" 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));
+ dlprintf2(" 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;
+
+ dlprintf3("[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 *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 insert_x_new(P2(active_line *, ll_ptr));
+private bool end_x_line(P1(active_line *));
+
+#define fill_loop_proc(proc)\
+int proc(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 fill_loop_proc(fill_loop_by_scan_lines);
+private fill_loop_proc(fill_loop_by_trapezoids);
+
+/* Statistics */
+#ifdef DEBUG
+struct stats_fill_s {
+ long
+ fill, fill_alloc, y_up, y_down, horiz, x_step, slow_x, iter, find_y,
+ band, band_step, band_fill, afill, slant, slant_shallow, sfill;
+} stats_fill;
+
+# define INCR(x) (++(stats_fill.x))
+# define INCR_EXPR(x) INCR(x)
+# define INCR_BY(x,n) (stats_fill.x += (n))
+#else
+# define INCR(x) DO_NOTHING
+# define INCR_EXPR(x) discard(0)
+# define INCR_BY(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, bbox;
+ 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)
+ gx_cpath_inner_box(pcpath, &bbox);
+ else
+ (*dev_proc(dev, get_clipping_box)) (dev, &bbox);
+ if (!rect_within(ibox, bbox)) { /*
+ * Intersect the path box and the clip bounding box.
+ * If the intersection is empty, this fill is a no-op.
+ */
+ if (pcpath)
+ 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.
+ *
+ * If there is a clipping path, set up a clipping device.
+ */
+ if (pcpath) {
+ dev = (gx_device *) & cdev;
+ gx_make_clip_device(&cdev, &cdev, gx_cpath_list(pcpath));
+ 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.
+ * NOTE: We changed the adjust_right/above value from 0.5+epsilon
+ * to 0.5 in release 5.01; even though this does the right thing
+ * in every case we could imagine, we aren't confident that it's
+ * correct. (The old values were definitely incorrect, since they
+ * caused 1-pixel-wide/high objects to color 2 pixels even if
+ * they fell exactly on pixel boundaries.)
+ */
+ if (adjust.x == fixed_half)
+ adjust_left = fixed_half - fixed_epsilon,
+ adjust_right = fixed_half /* + fixed_epsilon */ ; /* see above */
+ else
+ adjust_left = adjust_right = adjust.x;
+ if (adjust.y == fixed_half)
+ adjust_below = fixed_half - fixed_epsilon,
+ adjust_above = fixed_half /* + fixed_epsilon */ ; /* see above */
+ 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 || !gx_path_has_curves(ppath) ||
+ params->flatness >= 1.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.
+ */
+ gx_path_init_local(&ffpath, ppath->memory);
+ if (!gx_path_has_curves(ppath)) /* don't need to flatten */
+ pfpath = ppath;
+ else
+#ifdef FILL_CURVES
+ if (fill_by_trapezoids) {
+ gx_path_init_local(&ffpath, ppath->memory);
+ code = gx_path_add_flattened_accurate(ppath, &ffpath,
+ params->flatness,
+ pis->accurate_curves);
+ if (code < 0)
+ return code;
+ pfpath = &ffpath;
+ } else if (gx_path_is_monotonic(ppath))
+ pfpath = ppath;
+ else {
+ gx_path_init_local(&ffpath, ppath->memory);
+ code = gx_path_add_monotonized(ppath, &ffpath);
+ if (code < 0)
+ return code;
+ pfpath = &ffpath;
+ }
+#else
+ {
+ gx_path_init_local(&ffpath, ppath->memory);
+ code = gx_path_add_flattened_accurate(ppath, &ffpath,
+ params->flatness,
+ pis->accurate_curves);
+ if (code < 0)
+ return code;
+ pfpath = &ffpath;
+ }
+#endif
+ if ((code = add_y_list(pfpath, &lst, adjust_below, adjust_above, &ibox)) < 0)
+ goto nope;
+ {
+ fill_loop_proc((*fill_loop));
+
+ /* Some short-sighted compilers won't allow a conditional here.... */
+ if (fill_by_trapezoids)
+ fill_loop = fill_loop_by_trapezoids;
+ else
+ fill_loop = fill_loop_by_scan_lines;
+ code = (*fill_loop)
+ (&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_free(pfpath, "gx_default_fill_path(flattened path)");
+#ifdef DEBUG
+ if (gs_debug_c('f')) {
+ dlputs("[f] # alloc up down horiz step slowx iter find band bstep bfill\n");
+ dlprintf5(" %5ld %5ld %5ld %5ld %5ld",
+ stats_fill.fill, stats_fill.fill_alloc,
+ stats_fill.y_up, stats_fill.y_down,
+ stats_fill.horiz);
+ dlprintf4(" %5ld %5ld %5ld %5ld",
+ stats_fill.x_step, stats_fill.slow_x,
+ stats_fill.iter, stats_fill.find_y);
+ dlprintf3(" %5ld %5ld %5ld\n",
+ stats_fill.band, stats_fill.band_step,
+ stats_fill.band_fill);
+ dlputs("[f] afill slant shall sfill\n");
+ dlprintf4(" %5ld %5ld %5ld %5ld\n",
+ stats_fill.afill, stats_fill.slant,
+ stats_fill.slant_shallow, stats_fill.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;
+ INCR(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)
+ ) {
+ INCR(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;
+ INCR(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 (INCR_EXPR(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 (INCR_EXPR(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
+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 (INCR_EXPR(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
+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;
+
+ INCR(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)
+ INCR(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);
+ INCR(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 fill_slant_adjust(P12(fixed, fixed, fixed, fixed, fixed,
+ fixed, fixed, fixed, const gs_fixed_rect *,
+ const gx_device_color *, gx_device *, gs_logical_operation_t));
+private void resort_x_line(P1(active_line *));
+
+/****** PATCH ******/
+#define loop_fill_trapezoid_fixed(fx0, fw0, fy0, fx1, fw1, fh)\
+ loop_fill_trap(dev, fx0, fw0, fy0, fx1, fw1, fh, pbox, pdevc, lop)
+private int
+loop_fill_trap(gx_device * dev, fixed fx0, fixed fw0, fixed fy0,
+ fixed fx1, fixed fw1, fixed fh, const gs_fixed_rect * pbox,
+ const gx_device_color * pdevc, gs_logical_operation_t lop)
+{
+ fixed fy1 = fy0 + fh;
+ fixed ybot = max(fy0, pbox->p.y);
+ fixed ytop = min(fy1, pbox->q.y);
+ gs_fixed_edge left, right;
+
+ if (ybot >= ytop)
+ return 0;
+ 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, ybot, ytop, false, 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;
+ const 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));
+/*
+ * Define a faster test for
+ * fixed2int_pixround(y - below) != fixed2int_pixround(y + above)
+ * where we know
+ * 0 <= below <= _fixed_pixround_v,
+ * 0 <= above <= min(fixed_half, 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)
+ * Letting A = _fixed_pixround_v - below and B = _fixed_pixround_v + above,
+ * we can rewrite this as
+ * fixed2int(fixed_fraction(y) + A) != fixed2int(fixed_fraction(y) + B)
+ * Because of the range constraints given above, this is true precisely when
+ * fixed_fraction(y) + A < fixed_1 && fixed_fraction(y) + B >= fixed_1
+ * or equivalently
+ * fixed_fraction(y + B) < B - A.
+ * i.e.
+ * fixed_fraction(y + _fixed_pixround_v + above) < below + above
+ */
+ fixed y_span_delta = _fixed_pixround_v + adjust_above;
+ fixed y_span_limit = adjust_below + adjust_above;
+
+#define adjusted_y_spans_pixel(y)\
+ fixed_fraction((y) + y_span_delta) < y_span_limit
+
+ if (yll == 0)
+ return 0; /* empty list */
+ if (fill_direct)
+ cindex = pdevc->colors.pure,
+ fill_rect = dev_proc(dev, fill_rectangle);
+ 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;
+
+ INCR(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')) {
+ dlprintf2("[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;
+ INCR_EXPR(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')) {
+ dlprintf1("[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;
+
+ INCR(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);
+ INCR(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;
+ INCR(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);
+ if (adjusted_y_spans_pixel(y1)) {
+ if (code < 0)
+ return code;
+ INCR(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, pbox,
+ pdevc, dev, lop);
+ }
+ } else {
+ if (xtop <= xbot) { /* Bottom wider than top. */
+ if (adjusted_y_spans_pixel(y)) {
+ INCR(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);
+ } else { /* Slanted trapezoid. */
+ code = fill_slant_adjust(xlbot, xbot, y,
+ xltop, xtop, height, adjust_below,
+ adjust_above, pbox,
+ pdevc, dev, lop);
+ }
+ }
+ } else /* No Y adjustment. */
+ code = loop_fill_trapezoid_fixed(xlbot, xbot - xlbot,
+ y, xltop, wtop, height);
+ 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
+ * (or rectangle) plus two horizontal almost-rectangles.
+ */
+private int
+fill_slant_adjust(fixed xlbot, fixed xbot, fixed y,
+ fixed xltop, fixed xtop, fixed height, fixed adjust_below,
+ fixed adjust_above, const gs_fixed_rect * pbox,
+ const gx_device_color * pdevc, gx_device * dev,
+ gs_logical_operation_t lop)
+{
+ fixed y1 = y + height;
+
+ dev_proc_fill_trapezoid((*fill_trap)) =
+ dev_proc(dev, fill_trapezoid);
+ const fixed yb = y - adjust_below;
+ const fixed ya = y + adjust_above;
+ const fixed y1b = y1 - adjust_below;
+ const fixed y1a = y1 + adjust_above;
+ const gs_fixed_edge *plbot;
+ const gs_fixed_edge *prbot;
+ const gs_fixed_edge *pltop;
+ const gs_fixed_edge *prtop;
+ gs_fixed_edge vert_left, slant_left, vert_right, slant_right;
+ int code;
+
+ INCR(slant);
+
+ /* Set up all the edges, even though we may not need them all. */
+
+ if (xlbot < xltop) { /* && xbot < xtop */
+ vert_left.start.x = vert_left.end.x = xlbot;
+ vert_left.start.y = yb, vert_left.end.y = ya;
+ vert_right.start.x = vert_right.end.x = xtop;
+ vert_right.start.y = y1b, vert_right.end.y = y1a;
+ slant_left.start.y = ya, slant_left.end.y = y1a;
+ slant_right.start.y = yb, slant_right.end.y = y1b;
+ plbot = &vert_left, prbot = &slant_right,
+ pltop = &slant_left, prtop = &vert_right;
+ } else {
+ vert_left.start.x = vert_left.end.x = xltop;
+ vert_left.start.y = y1b, vert_left.end.y = y1a;
+ vert_right.start.x = vert_right.end.x = xbot;
+ vert_right.start.y = yb, vert_right.end.y = ya;
+ slant_left.start.y = yb, slant_left.end.y = y1b;
+ slant_right.start.y = ya, slant_right.end.y = y1a;
+ plbot = &slant_left, prbot = &vert_right,
+ pltop = &vert_left, prtop = &slant_right;
+ }
+ slant_left.start.x = xlbot, slant_left.end.x = xltop;
+ slant_right.start.x = xbot, slant_right.end.x = xtop;
+
+ if (ya >= y1b) { /*
+ * The upper and lower adjustment bands overlap.
+ * 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 don't attempt to do this.
+ */
+ int iyb = fixed2int_var_pixround(yb);
+ int iya = fixed2int_var_pixround(ya);
+ int iy1b = fixed2int_var_pixround(y1b);
+ int iy1a = fixed2int_var_pixround(y1a);
+
+ INCR(slant_shallow);
+ if (iy1b > iyb) {
+ code = (*fill_trap) (dev, plbot, prbot,
+ yb, y1b, false, pdevc, lop);
+ if (code < 0)
+ return code;
+ }
+ if (iya > iy1b) {
+ int ix = fixed2int_var_pixround(vert_left.start.x);
+ int iw = fixed2int_var_pixround(vert_right.start.x) - ix;
+
+ code = loop_fill_rectangle(ix, iy1b, iw, iya - iy1b);
+ if (code < 0)
+ return code;
+ }
+ if (iy1a > iya)
+ code = (*fill_trap) (dev, pltop, prtop,
+ ya, y1a, false, pdevc, lop);
+ else
+ code = 0;
+ } else { /*
+ * Clip the trapezoid if possible. This can save a lot
+ * of work when filling paths that cross band boundaries.
+ */
+ fixed yac;
+
+ if (pbox->p.y < ya) {
+ code = (*fill_trap) (dev, plbot, prbot,
+ yb, ya, false, pdevc, lop);
+ if (code < 0)
+ return code;
+ yac = ya;
+ } else
+ yac = pbox->p.y;
+ if (pbox->q.y > y1b) {
+ code = (*fill_trap) (dev, &slant_left, &slant_right,
+ yac, y1b, false, pdevc, lop);
+ if (code < 0)
+ return code;
+ code = (*fill_trap) (dev, pltop, prtop,
+ y1b, y1a, false, pdevc, lop);
+ } else
+ code = (*fill_trap) (dev, &slant_left, &slant_right,
+ yac, pbox->q.y, false, pdevc, lop);
+ }
+ return code;
+}
+
+/* Re-sort the x list by moving alp backward to its proper spot. */
+private void
+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..fcd334e81
--- /dev/null
+++ b/pstoraster/gxfixed.h
@@ -0,0 +1,248 @@
+/* Copyright (C) 1989, 1995, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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;
+typedef ulong ufixed; /* only used in a very few places */
+
+#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 fixed_pre_pixround(x) ((x)+_fixed_pixround_v)
+#define fixed2int_pixround(x) fixed2int(fixed_pre_pixround(x))
+#define fixed_is_int(x) !((x)&_fixed_fraction_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 the largest and smallest integer values that fit in a fixed. */
+#if arch_sizeof_int == arch_sizeof_long
+# define max_int_in_fixed fixed2int(max_fixed)
+# define min_int_in_fixed fixed2int(min_fixed)
+#else
+# define max_int_in_fixed max_int
+# define min_int_in_fixed min_int
+#endif
+
+#ifdef USE_FPU
+# define USE_FPU_FIXED (USE_FPU < 0 && arch_floats_are_IEEE && arch_sizeof_long == 4)
+#else
+# define USE_FPU_FIXED 0
+#endif
+
+/*
+ * 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....
+ */
+
+#if USE_FPU_FIXED
+fixed fixed_mult_quo(P3(fixed A, fixed B, fixed C));
+
+#else
+# define fixed_mult_quo(fixed_a, fixed_b, fixed_c)\
+ ((fixed)floor((double)(fixed_a) * (fixed_b) / (fixed_c)))
+#endif
+
+/*
+ * 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.
+ */
+
+/*
+ * 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 && arch_sizeof_short == 2
+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.
+ * set_ldexp_fixed2double(R, V, E) does the equivalent of R=ldexp((double)V,E):
+ * R is a double, V is a fixed, E is an int.
+ * R and F must be variables, not expressions; V and E may be expressions.
+ */
+#if USE_FPU_FIXED
+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_(&vf, x, fixed_fraction_bits), 0))
+#define set_ldexp_fixed2double(vd, x, exp)\
+ set_fixed2double_(&vd, x, -(exp))
+#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)
+# define set_ldexp_fixed2double(vd, x, exp)\
+ discard(vd = ldexp((double)(x), exp))
+#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..6f24952f4
--- /dev/null
+++ b/pstoraster/gxfmap.h
@@ -0,0 +1,105 @@
+/* Copyright (C) 1992, 1995, 1996, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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.
+ *
+ * NOTE: proc and closure are redundant. Eventually closure will replace
+ * proc. For now, things are in an uneasy intermediate state where
+ * proc = 0 means use closure.
+ */
+/* 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 */
+ gs_mapping_closure_t closure; /* SEE ABOVE */
+ /* The id changes whenever the map or function changes. */
+ gs_id id;
+ frac values[transfer_map_size];
+};
+
+extern_st(st_transfer_map);
+#define public_st_transfer_map() /* in gscolor.c */\
+ gs_public_st_composite(st_transfer_map, gx_transfer_map, "gx_transfer_map",\
+ transfer_map_enum_ptrs, transfer_map_reloc_ptrs)
+
+/*
+ * 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)
+
+/* Define a mapping procedure that just looks up the value in the cache. */
+/* (It is equivalent to gx_map_color_float with the arguments swapped.) */
+float gs_mapped_transfer(P2(floatp, const gx_transfer_map *));
+
+#endif /* gxfmap_INCLUDED */
diff --git a/pstoraster/gxfont.h b/pstoraster/gxfont.h
new file mode 100644
index 000000000..c8b562110
--- /dev/null
+++ b/pstoraster/gxfont.h
@@ -0,0 +1,224 @@
+/* Copyright (C) 1989, 1995, 1996, 1997 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Requires gsmatrix.h, gxdevice.h */
+
+#ifndef gxfont_INCLUDED
+# define gxfont_INCLUDED
+
+#include "gsccode.h"
+#include "gsfont.h"
+#include "gsuid.h"
+#include "gsstruct.h" /* for extern_st */
+#include "gxftype.h"
+
+/* 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.
+ *
+ * This procedure is OBSOLETE as of release 4.61, superseded by
+ * next_glyph; however, we have to continue supporting it for
+ * backward compatibility.
+ */
+
+#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));
+
+ /*
+ * Define the font's algorithm for getting the next character or
+ * glyph from a string being shown. We only use this if the
+ * next_char procedure is 0 (for backward compatibility).
+ */
+
+#define font_proc_next_glyph(proc)\
+ int proc(P3(gs_show_enum *, gs_char *, gs_glyph *))
+ font_proc_next_glyph((*next_glyph));
+
+} 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);
+font_proc_next_glyph(gs_default_next_glyph);
+
+/* 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)
+
+#endif /* gxfont_INCLUDED */
diff --git a/pstoraster/gxfont0.h b/pstoraster/gxfont0.h
new file mode 100644
index 000000000..4e56110d9
--- /dev/null
+++ b/pstoraster/gxfont0.h
@@ -0,0 +1,81 @@
+/* Copyright (C) 1994, 1996, 1997 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Type 0 (composite) font data definition */
+
+#ifndef gxfont0_INCLUDED
+# define gxfont0_INCLUDED
+
+/* 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_CMap = 9
+} fmap_type;
+
+#define fmap_type_min 2
+#define fmap_type_max 9
+#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. */
+#ifndef gs_cmap_DEFINED
+# define gs_cmap_DEFINED
+typedef struct gs_cmap_s gs_cmap;
+
+#endif
+typedef struct gs_type0_data_s {
+ fmap_type FMapType;
+ byte EscChar, ShiftIn, ShiftOut;
+ gs_const_string SubsVector; /* fmap_SubsVector only */
+ uint subs_size; /* bytes per entry */
+ uint subs_width; /* # of entries */
+ uint *Encoding;
+ uint encoding_size;
+ gs_font **FDepVector;
+ uint fdep_size;
+ const gs_cmap *CMap; /* fmap_CMap only */
+} 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)
+
+#endif /* gxfont0_INCLUDED */
diff --git a/pstoraster/gxfont1.h b/pstoraster/gxfont1.h
new file mode 100644
index 000000000..a19f76342
--- /dev/null
+++ b/pstoraster/gxfont1.h
@@ -0,0 +1,158 @@
+/* Copyright (C) 1994, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Type 1 font data definition (including Type 2 charstrings) */
+
+#ifndef gxfont1_INCLUDED
+# define gxfont1_INCLUDED
+
+
+/*
+ * This is the type-specific information for an Adobe Type 1 font.
+ * It also includes the information for Type 2 charstrings, because
+ * there isn't very much of it and it's less trouble to include here.
+ */
+
+#ifndef gs_font_type1_DEFINED
+# define gs_font_type1_DEFINED
+typedef struct gs_font_type1_s gs_font_type1;
+
+#endif
+
+/*
+ * 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 float_array(size)\
+ struct {\
+ int count;\
+ float values[size];\
+ }
+#define stem_table(size)\
+ float_array(size)
+
+typedef struct gs_type1_data_s gs_type1_data;
+
+typedef struct gs_type1_data_procs_s {
+
+ /* Get the data for any glyph. */
+
+ int (*glyph_data) (P3(gs_font_type1 * pfont, gs_glyph glyph,
+ gs_const_string * pgdata));
+
+ /* Get the data for a Subr. */
+
+ int (*subr_data) (P4(gs_font_type1 * pfont, int subr_num, bool global,
+ gs_const_string * psdata));
+
+ /* Get the data for a seac character. */
+
+ int (*seac_data) (P3(gs_font_type1 * pfont, int ccode,
+ gs_const_string * pcdata));
+
+ /*
+ * Get the next glyph. index = 0 means return the first one; a
+ * returned index of 0 means the enumeration is finished.
+ */
+
+ int (*next_glyph) (P3(gs_font_type1 * pfont, int *pindex,
+ gs_glyph * pglyph));
+
+ /* Push (a) value(s) onto the client ('PostScript') stack. */
+
+ int (*push) (P3(gs_font_type1 * pfont, const fixed * values, int count));
+
+ /* Pop a value from the client stack. */
+
+ int (*pop) (P2(gs_font_type1 * pfont, fixed * value));
+
+} gs_type1_data_procs_t;
+
+/*
+ * 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.
+ */
+struct gs_type1_data_s {
+ /*int PaintType; *//* in gs_font_common */
+ int CharstringType; /* 1 or 2 */
+ const gs_type1_data_procs_t *procs;
+ void *proc_data; /* data for procs */
+ int lenIV; /* -1 means no encryption */
+ /* (undocumented feature!) */
+ uint subroutineNumberBias; /* added to operand of callsubr */
+ /* (undocumented feature!) */
+ /* Type 2 charstring additions */
+ uint gsubrNumberBias; /* added to operand of callgsubr */
+ long initialRandomSeed;
+ fixed defaultWidthX;
+ fixed nominalWidthX;
+ /* 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_array(max_WeightVector) 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)
+
+#endif /* gxfont1_INCLUDED */
diff --git a/pstoraster/gxfont42.h b/pstoraster/gxfont42.h
new file mode 100644
index 000000000..dd9b5152d
--- /dev/null
+++ b/pstoraster/gxfont42.h
@@ -0,0 +1,72 @@
+/* Copyright (C) 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Type 42 font data definition */
+
+#ifndef gxfont42_INCLUDED
+# define gxfont42_INCLUDED
+
+/* 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 procedures */
+ /* The following are initialized by ...font_init, */
+ /* but may be reset by the client. */
+ int (*get_outline) (P3(gs_font_type42 *, uint, gs_const_string *));
+ /* 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. */
+/* Note that this initializes get_outline as well. */
+int gs_type42_font_init(P1(gs_font_type42 *));
+
+#endif /* gxfont42_INCLUDED */
diff --git a/pstoraster/gxfrac.h b/pstoraster/gxfrac.h
new file mode 100644
index 000000000..aaa4500e7
--- /dev/null
+++ b/pstoraster/gxfrac.h
@@ -0,0 +1,98 @@
+/* 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 supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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/gxftype.h b/pstoraster/gxftype.h
new file mode 100644
index 000000000..13c32c41d
--- /dev/null
+++ b/pstoraster/gxftype.h
@@ -0,0 +1,57 @@
+/* Copyright (C) 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Definition of font type and bitmap font behavior */
+
+#ifndef gxftype_INCLUDED
+# define gxftype_INCLUDED
+
+/* 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_CFF = 2,
+ ft_user_defined = 3,
+ ft_disk_based = 4,
+ ft_CID_encrypted = 9, /* CIDFontType 0 */
+ ft_CID_user_defined = 10, /* CIDFontType 1 */
+ ft_CID_TrueType = 11, /* CIDFontType 2 */
+ ft_Chameleon = 14,
+ ft_CID_bitmap = 32, /* CIDFontType 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;
+
+#endif /* gxftype_INCLUDED */
diff --git a/pstoraster/gxfunc.h b/pstoraster/gxfunc.h
new file mode 100644
index 000000000..c199bb1ef
--- /dev/null
+++ b/pstoraster/gxfunc.h
@@ -0,0 +1,56 @@
+/* Copyright (C) 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Internal definitions for Functions */
+
+#ifndef gxfunc_INCLUDED
+# define gxfunc_INCLUDED
+
+#include "gsfunc.h"
+#include "gsstruct.h"
+
+/* ---------------- Types and structures ---------------- */
+
+/* Define the generic Function structure type. This is never instantiated. */
+extern_st(st_function);
+#define public_st_function() /* in gsfunc.c */\
+ gs_public_st_ptrs2(st_function, gs_function_t, "gs_function_t",\
+ function_enum_ptrs, function_reloc_ptrs, params.Domain, params.Range)
+
+/* ---------------- Internal procedures ---------------- */
+
+/* Generic free_params implementation. */
+void fn_common_free_params(P2(gs_function_params_t * params, gs_memory_t * mem));
+
+/* Generic free implementation. */
+void fn_common_free(P3(gs_function_t * pfn, bool free_params, gs_memory_t * mem));
+
+/* Free an array of subsidiary Functions. */
+void fn_free_functions(P3(gs_function_t ** Functions, int count, gs_memory_t * mem));
+
+/* Check the values of m, n, Domain, and (if supplied) Range. */
+int fn_check_mnDR(P3(const gs_function_params_t * params, int m, int n));
+
+#endif /* gxfunc_INCLUDED */
diff --git a/pstoraster/gxgetbit.h b/pstoraster/gxgetbit.h
new file mode 100644
index 000000000..5bc8bc06b
--- /dev/null
+++ b/pstoraster/gxgetbit.h
@@ -0,0 +1,101 @@
+/* Copyright (C) 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Interface for get_bits_rectangle driver procedure */
+
+#ifndef gxgetbit_INCLUDED
+# define gxgetbit_INCLUDED
+
+#include "gxbitfmt.h"
+
+/* The parameter record typedef is also in gxdevcli.h. */
+#ifndef gs_get_bits_params_DEFINED
+# define gs_get_bits_params_DEFINED
+typedef struct gs_get_bits_params_s gs_get_bits_params_t;
+#endif
+
+/*
+ * We define the options for get_bits_rectangle here in a separate file
+ * so that the great majority of driver implementors and clients, which
+ * don't care about the details, don't need to be recompiled if the set
+ * of options changes.
+ */
+typedef gx_bitmap_format_t gs_get_bits_options_t;
+
+/*
+ * Define the parameter record passed to get_bits_rectangle.
+ * get_bits_rectangle may update members of this structure if
+ * the options allow it to choose their values, and always updates options
+ * to indicate what options were actually used (1 option per group).
+ */
+struct gs_get_bits_params_s {
+ gs_get_bits_options_t options;
+ byte *data[32];
+ int x_offset; /* in returned data */
+ uint raster;
+};
+
+/*
+ * gx_bitmap_format_t defines the options passed to get_bits_rectangle,
+ * which indicate which formats are acceptable for the returned data. If
+ * successful, get_bits_rectangle sets the options member of the parameter
+ * record to indicate what options were chosen -- 1 per group, and never the
+ * _ANY option. Note that the chosen option is not necessarily one that
+ * appeared in the original options: for example, if GB_RASTER_ANY is the
+ * only raster option originally set, the chosen option will be
+ * GB_RASTER_STANDARD or GB_RASTER_SPECIFIED.
+ *
+ * If the options mask is 0, get_bits_rectangle must set it to the
+ * complete set of supported options and return an error. This allows
+ * clients to determine what options are supported without actually doing
+ * a transfer.
+ *
+ * All devices must support at least one option in each group, and must
+ * support GB_COLORS_NATIVE.
+ *
+ * NOTE: the current default implementation supports only the following
+ * options in their respective groups (i.e., any other options must be
+ * supported directly by the device):
+ * GB_DEPTH_8
+ * GB_PACKING_CHUNKY
+ * GB_RETURN_COPY
+ * The current default implementation also requires that all devices
+ * support GB_PACKING_CHUNKY. */
+
+/* ---------------- Procedures ---------------- */
+
+/* Try to implement get_bits_rectangle by returning a pointer. */
+int gx_get_bits_return_pointer(P6(gx_device * dev, int x, int h,
+ gs_get_bits_params_t * params,
+ gs_get_bits_options_t stored,
+ byte * stored_base));
+
+/* Implement get_bits_rectangle by copying. */
+int gx_get_bits_copy(P8(gx_device * dev, int x, int w, int h,
+ gs_get_bits_params_t * params,
+ gs_get_bits_options_t stored,
+ const byte * src_base, uint dev_raster));
+
+#endif /* gxgetbit_INCLUDED */
diff --git a/pstoraster/gxhint1.c b/pstoraster/gxhint1.c
new file mode 100644
index 000000000..a5f005fb5
--- /dev/null
+++ b/pstoraster/gxhint1.c
@@ -0,0 +1,275 @@
+/* Copyright (C) 1990, 1992, 1993, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Font level hints for Type 1 fonts */
+#include "gx.h"
+#include "gserrors.h"
+#include "gxarith.h"
+#include "gxfixed.h"
+#include "gxmatrix.h"
+#include "gxfont.h"
+#include "gxfont1.h"
+#include "gxtype1.h"
+
+/* Define whether to use font hints. */
+/* Only set this to false for debugging the hint machinery. */
+static bool USE_HINTS = true;
+
+/* ------ Initialization ------ */
+
+typedef zone_table(1) a_zone_table;
+typedef stem_table(1) a_stem_table;
+private void
+ compute_snaps(P6(const gs_matrix_fixed *, const a_stem_table *,
+ stem_snap_table *, int, int, const char *));
+private alignment_zone *
+ compute_zones(P6(const gs_matrix_fixed *, const font_hints *,
+ const a_zone_table *, const a_zone_table *, alignment_zone *, int));
+private int
+ 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 = USE_HINTS;
+ else if (is_fzero(pmat->xx))
+ pfh->y_inverted = is_fneg(pmat->xy),
+ pfh->axes_swapped = true,
+ pfh->use_y_hints = USE_HINTS;
+ if (is_fzero(pmat->yx))
+ pfh->x_inverted = is_fneg(pmat->xx),
+ pfh->use_x_hints = USE_HINTS;
+ else if (is_fzero(pmat->yy))
+ pfh->x_inverted = is_fneg(pmat->yx),
+ pfh->axes_swapped = true,
+ pfh->use_x_hints = USE_HINTS;
+ 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);
+ /*
+ * Don't let the blue shift exceed half a pixel.
+ * See the discussion of BlueShift in section 5.7 of the
+ * "Adobe Type 1 Font Format" book.
+ */
+ 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
+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 *
+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
+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..f438d85fe
--- /dev/null
+++ b/pstoraster/gxhint2.c
@@ -0,0 +1,397 @@
+/* Copyright (C) 1990, 1995, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Character level hints for Type 1 fonts. */
+#include "memory_.h"
+#include "gx.h"
+#include "gserrors.h"
+#include "gxarith.h"
+#include "gxfixed.h"
+#include "gxmatrix.h"
+#include "gxfont.h"
+#include "gxfont1.h"
+#include "gxtype1.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 *type1_stem(P4(const gs_type1_state *, stem_hint_table *, fixed, fixed));
+private fixed find_snap(P3(fixed, const stem_snap_table *, const pixel_scale *));
+private alignment_zone *find_zone(P3(gs_type1_state *, fixed, fixed));
+
+/* Reset the stem hints. */
+void
+reset_stem_hints(gs_type1_state * pcis)
+{
+ pcis->hstem_hints.count = pcis->hstem_hints.replaced_count = 0;
+ pcis->vstem_hints.count = pcis->vstem_hints.replaced_count = 0;
+ update_stem_hints(pcis);
+}
+
+/* Prepare to replace the stem hints. */
+private void
+save_replaced_hints(stem_hint_table * psht)
+{
+ int rep_count = min(psht->replaced_count + psht->count, max_stems);
+
+ memmove(&psht->data[max_stems - rep_count], &psht->data[0],
+ psht->count * sizeof(psht->data[0]));
+ psht->replaced_count = rep_count;
+ psht->count = psht->current = 0;
+}
+void
+type1_replace_stem_hints(gs_type1_state * pcis)
+{
+ if_debug2('y', "[y]saving hints: %d hstem, %d vstem\n",
+ pcis->hstem_hints.count, pcis->vstem_hints.count);
+ save_replaced_hints(&pcis->hstem_hints);
+ save_replaced_hints(&pcis->vstem_hints);
+ if_debug2('y', "[y]total saved hints: %d hstem, %d vstem\n",
+ pcis->hstem_hints.replaced_count,
+ pcis->vstem_hints.replaced_count);
+}
+
+/* Update the internal stem hint pointers after moving or copying the state. */
+void
+update_stem_hints(gs_type1_state * pcis)
+{
+ pcis->hstem_hints.current = 0;
+ pcis->vstem_hints.current = 0;
+}
+
+/* ------ Add hints ------ */
+
+#undef c_fixed
+#define c_fixed(d, c) m_fixed(d, c, pcis->fc, max_coeff_bits)
+
+#define if_debug_print_add_stem(chr, msg, psht, psh, c, dc, v, dv)\
+ if_debug10(chr, "%s %d/%d: %g,%g -> %g(%g)%g ; d = %g,%g\n",\
+ msg, (int)((psh) - &(psht)->data[0]), (psht)->count,\
+ fixed2float(c), fixed2float(dc),\
+ fixed2float(v), fixed2float(dv), fixed2float((v) + (dv)),\
+ fixed2float((psh)->dv0), fixed2float((psh)->dv1))
+
+/* Compute and store the adjusted stem coordinates. */
+private void
+store_stem_deltas(const stem_hint_table * psht, stem_hint * psh,
+ const pixel_scale * psp, fixed v, fixed dv, fixed adj_dv)
+{ /*
+ * We want to align the stem so its edges fall on pixel boundaries
+ * (possibly "big pixel" boundaries if we are oversampling),
+ * but if hint replacement has occurred, we must shift edges in a
+ * consistent way. This is a real nuisance, but I don't see how
+ * to avoid it; if we don't do it, we get bizarre anomalies like
+ * disappearing stems.
+ */
+ const stem_hint *psh0 = 0;
+ const stem_hint *psh1 = 0;
+ int i;
+
+ /*
+ * If we ever had a hint with the same edge(s), align this one
+ * the same way.
+ */
+ for (i = max_stems - psht->replaced_count; i < max_stems; ++i) {
+ const stem_hint *ph = &psht->data[i];
+
+ if (ph == psh)
+ continue;
+ if (ph->v0 == psh->v0)
+ psh0 = ph;
+ if (ph->v1 == psh->v1)
+ psh1 = ph;
+ }
+ for (i = 0; i < psht->count; ++i) {
+ const stem_hint *ph = &psht->data[i];
+
+ if (ph == psh)
+ continue;
+ if (ph->v0 == psh->v0)
+ psh0 = ph;
+ if (ph->v1 == psh->v1)
+ psh1 = ph;
+ }
+ if (psh0 != 0) {
+ psh->dv0 = psh0->dv0;
+ if (psh1 != 0) { /* Both edges are determined. */
+ psh->dv1 = psh1->dv1;
+ } else { /* Only the lower edge is determined. */
+ psh->dv1 = psh->dv0 + adj_dv - dv;
+ }
+ } else if (psh1 != 0) { /* Only the upper edge is determined. */
+ psh->dv1 = psh1->dv1;
+ psh->dv0 = psh->dv1 + adj_dv - dv;
+ } else { /* Neither edge is determined. */
+ 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;
+ }
+}
+
+/* Add a horizontal stem hint. */
+void
+type1_do_hstem(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, &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. */
+ store_stem_deltas(&pcis->hstem_hints, psh, psp, v, dv, adj_dv);
+ }
+ if_debug_print_add_stem('y', "[y]hstem", &pcis->hstem_hints, psh,
+ y, dy, v, dv);
+}
+
+/* Add a vertical stem hint. */
+void
+type1_do_vstem(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;
+
+ 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, &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. */
+ store_stem_deltas(&pcis->vstem_hints, psh, psp, v, dv, adj_dv);
+ if_debug_print_add_stem('y', "[y]vstem", &pcis->vstem_hints, psh,
+ x, dx, v, dv);
+}
+
+/* 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 *
+type1_stem(const gs_type1_state * pcis, 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;
+ top->index = pcis->hstem_hints.count + pcis->vstem_hints.count;
+ top->active = true;
+ psht->count++;
+ return top;
+}
+
+/* Compute the adjusted width of a stem. */
+/* The value returned is always a multiple of scale.unit. */
+private fixed
+find_snap(fixed dv, const stem_snap_table * psst, const pixel_scale * pps)
+{ /* We aren't sure why a maximum difference of pps->half */
+ /* works better than pps->unit, but it does. */
+#define max_snap_distance (pps->half)
+ fixed best = max_snap_distance;
+ 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) < max_snap_distance ?
+ 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;
+#undef max_snap_distance
+}
+
+/* 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 *
+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..ea3e0bda1
--- /dev/null
+++ b/pstoraster/gxhint3.c
@@ -0,0 +1,560 @@
+/* Copyright (C) 1994, 1995, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Apply hints for Type 1 fonts. */
+#include "math_.h" /* for floor in fixed_mult_quo */
+#include "gx.h"
+#include "gserrors.h"
+#include "gxarith.h"
+#include "gxfixed.h"
+#include "gxmatrix.h"
+#include "gxfont.h"
+#include "gxfont1.h"
+#include "gxtype1.h"
+#include "gzpath.h"
+
+/* ------ 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.
+ *
+ * Note that "upper" and "lower" refer to device coordinates, which are
+ * what we use throughout the Type 1 code; however, "horizontal" and
+ * "vertical" refer to the character space coordinate system.
+ */
+#define HINT_VERT_LOWER 1
+#define HINT_VERT_UPPER 2 /* must be > lower */
+#define HINT_VERT (HINT_VERT_LOWER | HINT_VERT_UPPER)
+#define HINT_HORZ_LOWER 4
+#define HINT_HORZ_UPPER 8 /* must be > lower */
+#define HINT_HORZ (HINT_HORZ_LOWER | HINT_HORZ_UPPER)
+#define NEARLY_AXIAL(dmajor, dminor)\
+ ((dminor) <= (dmajor) >> 4)
+
+/*
+ * Determine which types of hints, if any, are applicable to a given
+ * line segment.
+ */
+private int
+line_hints(const gs_type1_state * pcis, const gs_fixed_point * p0,
+ const gs_fixed_point * p1)
+{
+ fixed dx = p1->x - p0->x;
+ fixed dy = p1->y - p0->y;
+ fixed adx, ady;
+ bool xi = pcis->fh.x_inverted, yi = pcis->fh.y_inverted;
+ int hints;
+
+ /*
+ * 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.)
+ */
+
+ /*
+ * Map the deltas back into character space. This is essentially an
+ * inverse-distance-transform with the combined matrix, but we don't
+ * bother to undo the scaling, since it only matters for the axiality
+ * test and we don't care about situations where X and Y scaling are
+ * radically different.
+ */
+ if (xi)
+ dx = -dx;
+ if (yi)
+ dy = -dy;
+ if (pcis->fh.axes_swapped) {
+ fixed t = dx;
+ int ti = xi;
+
+ dx = dy, xi = yi;
+ dy = t, yi = ti;
+ }
+ adx = any_abs(dx);
+ ady = any_abs(dy);
+ /*
+ * Note that since upper/lower refer to device space, we must
+ * interchange them if the corresponding axis is inverted.
+ */
+ if (dy != 0 && NEARLY_AXIAL(ady, adx)) {
+ hints = (dy > 0 ? HINT_VERT_UPPER : HINT_VERT_LOWER);
+ if (xi)
+ hints ^= (HINT_VERT_LOWER | HINT_VERT_UPPER);
+ } else if (dx != 0 && NEARLY_AXIAL(adx, ady)) {
+ hints = (dx < 0 ? HINT_HORZ_UPPER : HINT_HORZ_LOWER);
+ if (yi)
+ hints ^= (HINT_HORZ_LOWER | HINT_HORZ_UPPER);
+ } else
+ hints = 0;
+ if_debug7('y', "[y]hint from 0x%lx(%1.4f,%1.4f) to 0x%lx(%1.4f,%1.4f) = %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
+apply_hints_at(gs_type1_state * pcis, int hints, gs_fixed_point * ppt,
+ gs_fixed_point * pdiff)
+{
+ fixed px = ppt->x, py = ppt->y;
+
+ if_debug4('y', "[y]applying hints %d to 0x%lx(%1.4f,%1.4f) ...\n",
+ hints, (ulong) ppt, fixed2float(px), fixed2float(py));
+ if ((hints & HINT_VERT) != 0 &&
+ (pcis->vstem_hints.count & pcis->dotsection_flag) != 0
+ )
+ 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
+ )
+ apply_hstem_hints(pcis, (hints & HINT_HORZ_UPPER) -
+ (hints & HINT_HORZ_LOWER), ppt);
+ if (pdiff != NULL)
+ pdiff->x = ppt->x - px,
+ pdiff->y = ppt->y - py;
+ /* Here is where we would round *ppt to the nearest quarter-pixel */
+ /* if we wanted to. */
+ if_debug2('y', "[y] ... => (%1.4f,%1.4f)\n",
+ fixed2float(ppt->x), fixed2float(ppt->y));
+}
+
+/* Add a hint delta to a point. */
+#ifndef DEBUG
+inline
+#endif
+private void
+add_hint_diff(gs_fixed_point * ppt, gs_fixed_point delta)
+{
+ if_debug7('y', "[y]adding diff (%1.4f,%1.4f) to 0x%lx(%1.4f,%1.4f) => (%1.4f,%1.4f)\n",
+ fixed2float(delta.x), fixed2float(delta.y), (ulong) ppt,
+ fixed2float(ppt->x), fixed2float(ppt->y),
+ fixed2float(ppt->x + delta.x), fixed2float(ppt->y + delta.y));
+ ppt->x += delta.x;
+ ppt->y += delta.y;
+}
+
+/* Test whether a line is null. */
+inline private bool
+line_is_null(gs_fixed_point p0, gs_fixed_point p1)
+{
+ return (any_abs(p1.x - p0.x) + any_abs(p1.y - p0.y) < fixed_epsilon * 4);
+}
+
+/*
+ * Adjust the other control points of a curve proportionately when moving
+ * one end. The Boolean argument indicates whether the point being
+ * adjusted is the one nearer the point that was moved.
+ */
+private fixed
+scale_delta(fixed diff, fixed dv, fixed lv, bool nearer)
+{
+ if (dv == 0)
+ return 0;
+ /*
+ * fixed_mult_quo requires non-negative 2nd and 3rd arguments,
+ * and also 2nd argument < 3rd argument.
+ * If it weren't for that, we would just use it directly.
+ *
+ * lv = 0 is implausible, but we have to allow for it.
+ */
+ if (lv == 0)
+ return (nearer ? diff : fixed_0);
+ if (lv < 0)
+ lv = -lv, dv = -dv;
+ if (dv < 0)
+ dv = -dv, diff = -diff;
+ /*
+ * If dv > lv, there has been some kind of anomaly similar to
+ * the lv = 0 case.
+ */
+ if (dv >= lv)
+ return (nearer ? diff : fixed_0);
+ else
+ return fixed_mult_quo(diff, dv, lv);
+}
+private void
+adjust_curve_start(curve_segment * pcseg, const gs_fixed_point * pdiff)
+{
+ fixed dx = pdiff->x, dy = pdiff->y;
+ fixed end_x = pcseg->pt.x, end_y = pcseg->pt.y;
+ const segment *prev = pcseg->prev;
+ fixed lx = end_x - (prev->pt.x - dx), ly = end_y - (prev->pt.y - dy);
+ gs_fixed_point delta;
+
+ delta.x = scale_delta(end_x - pcseg->p1.x, dx, lx, true);
+ delta.y = scale_delta(end_y - pcseg->p1.y, dy, ly, true);
+ add_hint_diff(&pcseg->p1, delta);
+ delta.x = scale_delta(end_x - pcseg->p2.x, dx, lx, false);
+ delta.y = scale_delta(end_y - pcseg->p2.y, dy, ly, false);
+ add_hint_diff(&pcseg->p2, delta);
+}
+private void
+adjust_curve_end(curve_segment * pcseg, const gs_fixed_point * pdiff)
+{
+ fixed dx = pdiff->x, dy = pdiff->y;
+ const segment *prev = pcseg->prev;
+ fixed start_x = prev->pt.x, start_y = prev->pt.y;
+ fixed lx = pcseg->pt.x - dx - start_x, ly = pcseg->pt.y - dy - start_y;
+ gs_fixed_point delta;
+
+ delta.x = scale_delta(pcseg->p1.x - start_x, dx, lx, false);
+ delta.y = scale_delta(pcseg->p1.y - start_y, dy, ly, false);
+ add_hint_diff(&pcseg->p1, delta);
+ delta.x = scale_delta(pcseg->p2.x - start_x, dx, lx, true);
+ delta.y = scale_delta(pcseg->p2.y - start_y, dy, ly, true);
+ add_hint_diff(&pcseg->p2, delta);
+}
+
+/*
+ * 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;
+ }
+ }
+}
+
+/*
+ * Handle the end of the subpath wrapping around to the start. This is
+ * ugly, messy code that we should be able to improve, but I neither see how
+ * to do it nor understand how the IBM Type 1 rasterizer can produce such
+ * good results without doing anything like this.
+ *
+ * This is a separate procedure only for readability: it is only called
+ * from one place in the next procedure.
+ */
+private void
+apply_wrapped_hints(gs_type1_state * pcis, subpath * psub, segment * pseg,
+ int hints, gs_fixed_point * pdiff)
+{
+ /* Some fonts don't use closepath when they should.... */
+ fixed ctemp;
+ 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 *const pfirst = psub->next;
+ 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. However, because of
+ * hint replacement, the points might differ even if hints ==
+ * hints_first. In this case, the initial hints take priority,
+ * because the initial segment was laid down first.
+ */
+ int do_x, do_y;
+ gs_fixed_point diff2;
+
+ if_debug2('y', "[y]closing closed, hints=%d, hints_first=%d\n",
+ hints, hints_first);
+ 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;
+
+ pdiff->x =
+ (hints_start & do_x ?
+ pseg->pt.x - pcis->unmoved_end.x : 0);
+ pdiff->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, *pdiff);
+ /*
+ * Now align the initial and final points, to deal with hint
+ * replacement.
+ */
+ diff2.x = psub->pt.x - pseg->pt.x;
+ diff2.y = psub->pt.y - pseg->pt.y;
+ if (diff2.x || diff2.y) {
+ /* Force the points to coincide. */
+ pseg->pt = psub->pt;
+ apply_final_hint(pseg, &diff2);
+ }
+ } else {
+ int hints_close =
+ line_hints(pcis, &pcis->unmoved_end, &pcis->unmoved_start);
+
+ hints_close &= ~(hints | hints_first);
+ if_debug3('y', "[y]closing open, hints=%d, hints_close=%d, hints_first=%d\n",
+ hints, hints_close, hints_first);
+ if (hints_close) {
+ apply_hints_at(pcis, hints_close, &pseg->pt, pdiff);
+ apply_final_hint(pseg, pdiff);
+ apply_hints_at(pcis, hints_close, &psub->pt, pdiff);
+ } else
+ pdiff->x = pdiff->y = 0;
+ }
+ if (pfirst->type == s_curve)
+ adjust_curve_start((curve_segment *) pfirst, pdiff);
+}
+
+/*
+ * 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;
+ segment *pnext;
+ subpath *const 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;
+
+ /*
+ * Since unknown OtherSubrs call apply_path_hints before returning
+ * to the client, and since OtherSubrs may be invoked before the
+ * [h]sbw is seen, it's possible that init_done < 0, i.e., the path
+ * and hint members of the state haven't been set up yet. In this
+ * case, we know there are no relevant hints.
+ */
+ if (pcis->init_done < 0)
+ return;
+ 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:{
+ curve_segment *const pnext_curve = (curve_segment *) pnext;
+ 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;
+ if (hints_first)
+ apply_hints_at(pcis, hints_first, &pseg->pt, &dseg);
+ else
+ dseg.x = dseg.y = 0;
+ 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);
+ if (hints_next) {
+ apply_hints_at(pcis, hints_next, &pnext_curve->p2, &diff);
+ pcis->unmoved_end = pnext->pt;
+ add_hint_diff(&pnext->pt, diff);
+ } else
+ pcis->unmoved_end = pnext->pt;
+ break;
+ }
+ case s_line_close:
+ /* Undo any initial hints propagated to the end. */
+ pnext->pt = pcis->unmoved_start;
+ 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);
+ if (hints_next & ~hints)
+ apply_hints_at(pcis, hints_next & ~hints,
+ &pseg->pt, &dseg);
+ else
+ dseg.x = dseg.y = 0;
+ }
+ if (pseg == (segment *) psub)
+ pcis->hints_initial = hints_next;
+ pcis->unmoved_end = pnext->pt;
+ if (hints_next)
+ apply_hints_at(pcis, hints_next, &pnext->pt, NULL);
+ }
+ if (pseg->type == s_curve)
+ adjust_curve_end((curve_segment *) pseg, &dseg);
+ hints = hints_next;
+ }
+ if (closing) {
+ apply_wrapped_hints(pcis, psub, pseg, hints, &diff);
+ pcis->hint_next = 0;
+ pcis->hints_pending = 0;
+ } else {
+ pcis->hint_next = pseg;
+ pcis->hints_pending = hints;
+ }
+}
+
+/* ------ Individual hints ------ */
+
+private const stem_hint *search_hints(P2(stem_hint_table *, fixed));
+
+/*
+ * Adjust a point according to the relevant hints.
+ * dx or dy is > 0 for the upper edge, < 0 for the lower.
+ * 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.
+ */
+
+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);
+ const stem_hint *ph = search_hints(&pcis->vstem_hints, *pv);
+
+ if (ph != 0) {
+ if_debug3('Y', "[Y]use vstem %d: %1.4f (%s)",
+ (int)(ph - &pcis->vstem_hints.data[0]),
+ fixed2float(*pv),
+ (dy == 0 ? "?!" : dy > 0 ? "upper" : "lower"));
+#ifdef DEBUG
+ if (dy == 0) {
+ lprintf("dy == 0 in apply_vstem_hints!\n");
+ return;
+ }
+#endif
+ *pv += (dy > 0 ? ph->dv1 : ph->dv0);
+ if_debug1('Y', " -> %1.4f\n", fixed2float(*pv));
+ }
+}
+
+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);
+ const stem_hint *ph = search_hints(&pcis->hstem_hints, *pv);
+
+ if (ph != 0) {
+ if_debug3('Y', "[Y]use hstem %d: %1.4f (%s)",
+ (int)(ph - &pcis->hstem_hints.data[0]),
+ fixed2float(*pv),
+ (dx == 0 ? "?!" : dx > 0 ? "upper" : "lower"));
+#ifdef DEBUG
+ if (dx == 0) {
+ lprintf("dx == 0 in apply_vstem_hints!\n");
+ return;
+ }
+#endif
+ *pv += (dx > 0 ? ph->dv1 : ph->dv0);
+ if_debug1('Y', " -> %1.4f\n", fixed2float(*pv));
+ }
+}
+
+/* Search one hint table for an adjustment. */
+private const stem_hint *
+search_hints(stem_hint_table * psht, fixed v)
+{
+ const stem_hint *table = &psht->data[0];
+ const stem_hint *ph = table + psht->current;
+
+ if (v >= ph->v0 && v <= ph->v1 && ph->active)
+ 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 && ph->active) {
+ psht->current = ph - table;
+ return ph;
+ }
+ return 0;
+}
diff --git a/pstoraster/gxht.c b/pstoraster/gxht.c
new file mode 100644
index 000000000..7b03ddba9
--- /dev/null
+++ b/pstoraster/gxht.c
@@ -0,0 +1,532 @@
+/* Copyright (C) 1989, 1995, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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 "gxdcolor.h"
+#include "gxfixed.h"
+#include "gxdevice.h" /* for gzht.h */
+#include "gxistate.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. */
+/* The type descriptor must be public for Pattern types. */
+gs_public_st_composite(st_dc_ht_binary, gx_device_color, "dc_ht_binary",
+ dc_ht_binary_enum_ptrs, dc_ht_binary_reloc_ptrs);
+private dev_color_proc_load(gx_dc_ht_binary_load);
+private dev_color_proc_fill_rectangle(gx_dc_ht_binary_fill_rectangle);
+private dev_color_proc_equal(gx_dc_ht_binary_equal);
+const gx_device_color_type_t
+ gx_dc_type_data_ht_binary =
+{&st_dc_ht_binary,
+ gx_dc_ht_binary_load, gx_dc_ht_binary_fill_rectangle,
+ gx_dc_default_fill_masked, gx_dc_ht_binary_equal
+};
+
+#undef gx_dc_type_ht_binary
+const gx_device_color_type_t *const gx_dc_type_ht_binary =
+&gx_dc_type_data_ht_binary;
+
+#define gx_dc_type_ht_binary (&gx_dc_type_data_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;
+ RELOC_VAR(bits);
+ 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_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_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;
+}
+
+/* Free a halftone cache. */
+void
+gx_ht_free_cache(gs_memory_t * mem, gx_ht_cache * pcache)
+{
+ gs_free_object(mem, pcache->ht_tiles, "free_ht_cache(ht_tiles)");
+ gs_free_object(mem, pcache->bits, "free_ht_cache(bits)");
+ gs_free_object(mem, pcache, "free_ht_cache(struct)");
+}
+
+/* Make the cache order current, and return whether */
+/* there is room for all possible tiles in the cache. */
+bool
+gx_check_tile_cache(const gs_imager_state * pis)
+{
+ const gx_ht_order *porder = &pis->dev_ht->order;
+ gx_ht_cache *pcache = pis->ht_cache;
+
+ if (pcache == 0 || pis->dev_ht == 0)
+ return false; /* no halftone or 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(const gs_imager_state * pis, int w, int y, int h,
+ gs_color_select_t select, int *ppx)
+{
+ int tsy;
+ const gx_strip_bitmap *ptile0;
+
+ if (pis->ht_cache == 0)
+ return -1; /* no halftone cache */
+ ptile0 = &pis->ht_cache->ht_tiles[0].tiles; /* a typical tile */
+ if (h > ptile0->rep_height || w > ptile0->rep_width ||
+ ptile0->shift != 0
+ )
+ return -1;
+ tsy = (y + imod(-pis->screen_phase[select].y, ptile0->rep_height)) %
+ ptile0->rep_height;
+ if (tsy + h > ptile0->size.y)
+ return -1;
+ /* Tile fits in Y, might fit in X. */
+ *ppx = imod(-pis->screen_phase[select].x, ptile0->rep_width);
+ return tsy * ptile0->raster;
+}
+
+/* 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_imager_state * pis,
+ gx_device * dev, gs_color_select_t select)
+{
+ const gx_ht_order *porder = &pis->dev_ht->order;
+ gx_ht_cache *pcache = pis->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)
+{
+ gx_rop_source_t no_source;
+
+ /*
+ * Observation of H-P devices and documentation yields confusing
+ * evidence about whether white pixels in halftones are always
+ * opaque. It appears that for black-and-white devices, these
+ * pixels are *not* opaque.
+ */
+ if (dev->color_info.depth > 1)
+ lop &= ~lop_T_transparent;
+ 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)
+ set_rop_no_source(source, no_source, dev);
+ 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);
+}
+
+/* Compare two binary halftones for equality. */
+private bool
+gx_dc_ht_binary_equal(const gx_device_color * pdevc1,
+ const gx_device_color * pdevc2)
+{
+ return pdevc2->type == pdevc1->type &&
+ pdevc1->phase.x == pdevc2->phase.x &&
+ pdevc1->phase.y == pdevc2->phase.y &&
+ gx_dc_binary_color0(pdevc1) == gx_dc_binary_color0(pdevc2) &&
+ gx_dc_binary_color1(pdevc1) == gx_dc_binary_color1(pdevc2) &&
+ pdevc1->colors.binary.b_level == pdevc2->colors.binary.b_level;
+}
+
+/* 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;
+
+ /* Non-monotonic halftones may have more bits than size. */
+ if (porder->num_bits >= size)
+ size = porder->num_bits + 1;
+ /* 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..8a86c0928
--- /dev/null
+++ b/pstoraster/gxht.h
@@ -0,0 +1,222 @@
+/* Copyright (C) 1993, 1995, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Rest of (client) halftone definitions */
+
+#ifndef gxht_INCLUDED
+# define gxht_INCLUDED
+
+#include "gscsepnm.h"
+#include "gsht1.h"
+#include "gsrefct.h"
+#include "gxhttype.h"
+#include "gxtmap.h"
+
+/*
+ * Halftone types. Note that for this implementation there are only
+ * spot functions, thresholds, and multi-component halftones; the peculiar
+ * colored halftones supported by PostScript (HalftoneType's 2 and 4) are
+ * not supported.
+ *
+ * NB1: While this code supports relocation of the client data, it will not
+ * free that data when the halftone is released. The client must handle
+ * that task directly.
+ *
+ * NB2: The garbage collection code will deal with the user provided data as
+ * a structure pointer allocated on the heap. The client must make
+ * certain this is the case.
+ *
+ * There is, somewhat unfortunately, no identifier applied to these
+ * halftones. This reflects the origin of this graphics library as a set
+ * of routines for use by a PostScript interpreter.
+ *
+ * In PostScript, halftone objects do not exist in an identified form outside
+ * of the graphic state. Though Level 2 and PostScript 3 support halftone
+ * dictionaries, these are neither read-only structures nor tagged
+ * by a unique identifier. Hence, they are not suitable for use as cache keys.
+ * Caching of halftones for PostScript is confined to the graphic state,
+ * and this holds true for the graphic library as well.
+ *
+ * Note also that implementing a generalized halftone cache is not trivial,
+ * as the device-specific representation of spot halftones depends on the
+ * default transformation for the device, and more generally the device
+ * specific representation of halftones may depend on the sense of the device
+ * (additive or subtract). Hence, a halftone cache would need to be keyed
+ * by device. (This is not an issue when caching halftones in the graphic
+ * state as the device is also a component of the graphic state).
+ */
+
+/*
+ * Note that the transfer_closure members will replace transfer sometime
+ * in the future. For the moment, transfer_closure is only used if
+ * transfer = 0.
+ */
+
+/* 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; /* OBSOLETE */
+ gs_mapping_closure_t transfer_closure;
+} gs_spot_halftone;
+
+#define st_spot_halftone_max_ptrs st_screen_halftone_max_ptrs + 1
+
+/* Type 3 halftone. */
+typedef struct gs_threshold_halftone_s {
+ int width;
+ int height;
+ gs_const_string thresholds;
+ gs_mapping_proc transfer; /* OBSOLETE */
+ gs_mapping_closure_t transfer_closure;
+} gs_threshold_halftone;
+
+#define st_threshold_halftone_max_ptrs 2
+
+/* Client-defined halftone that generates a halftone order. */
+typedef struct gs_client_order_halftone_s gs_client_order_halftone;
+
+#ifndef gx_ht_order_DEFINED
+# define gx_ht_order_DEFINED
+typedef struct gx_ht_order_s gx_ht_order;
+
+#endif
+typedef struct gs_client_order_ht_procs_s {
+
+ /*
+ * Allocate and fill in the order. gx_ht_alloc_client_order
+ * (see gzht.h) does everything but fill in the actual data.
+ */
+
+ int (*create_order) (P4(gx_ht_order * porder,
+ gs_state * pgs,
+ const gs_client_order_halftone * phcop,
+ gs_memory_t * mem));
+
+} gs_client_order_ht_procs_t;
+struct gs_client_order_halftone_s {
+ int width;
+ int height;
+ int num_levels;
+ const gs_client_order_ht_procs_t *procs;
+ const void *client_data;
+ gs_mapping_closure_t transfer_closure;
+};
+
+#define st_client_order_halftone_max_ptrs 2
+
+/* 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 */
+ gs_client_order_halftone client_order; /* client order */
+ } 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(max(st_spot_halftone_max_ptrs, st_threshold_halftone_max_ptrs),\
+ st_client_order_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.
+ *
+ * NOTE: it is assumed that all subsidiary structures of halftones (the
+ * threshold array(s) for Type 3 halftones or halftone components, and the
+ * components array for Type 5 halftones) are allocated with the same
+ * allocator as the halftone structure itself.
+ */
+struct gs_halftone_s {
+ gs_halftone_type type;
+ rc_header rc;
+ union {
+ gs_screen_halftone screen; /* setscreen */
+ gs_colorscreen_halftone colorscreen; /* setcolorscreen */
+ gs_spot_halftone spot; /* Type 1 */
+ gs_threshold_halftone threshold; /* Type 3 */
+ gs_client_order_halftone client_order; /* client order */
+ 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),\
+ max(st_client_order_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_memory(P5(gs_screen_enum *, gs_state *,
+ gs_screen_halftone *, bool, gs_memory_t *));
+
+#define gs_screen_init_accurate(penum, pgs, phsp, accurate)\
+ gs_screen_init_memory(penum, pgs, phsp, accurate, pgs->memory)
+
+/* Procedural interface for MinScreenLevels (a Ghostscript extension) */
+
+/*
+ * Set/get the MinScreenLevels value.
+ *
+ * Note that this value is stored in a static variable.
+ */
+void gs_setminscreenlevels(P1(uint));
+uint gs_currentminscreenlevels(P0());
+
+#endif /* gxht_INCLUDED */
diff --git a/pstoraster/gxhttile.h b/pstoraster/gxhttile.h
new file mode 100644
index 000000000..f72f7ad3b
--- /dev/null
+++ b/pstoraster/gxhttile.h
@@ -0,0 +1,54 @@
+/* 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 supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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/gxhttype.h b/pstoraster/gxhttype.h
new file mode 100644
index 000000000..d39200ac4
--- /dev/null
+++ b/pstoraster/gxhttype.h
@@ -0,0 +1,45 @@
+/* Copyright (C) 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Client halftone type enumeration */
+
+#ifndef gxhttype_INCLUDED
+# define gxhttype_INCLUDED
+
+/* 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 */
+ ht_type_client_order /* client-defined, creating a gx_ht_order */
+} gs_halftone_type;
+
+#endif /* gxhttype_INCLUDED */
diff --git a/pstoraster/gxi12bit.c b/pstoraster/gxi12bit.c
new file mode 100644
index 000000000..e5f40b550
--- /dev/null
+++ b/pstoraster/gxi12bit.c
@@ -0,0 +1,300 @@
+/* Copyright (C) 1994, 1995, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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 "gxdevice.h"
+#include "gxcmap.h"
+#include "gxdcolor.h"
+#include "gxistate.h"
+#include "gzpath.h"
+#include "gxdevmem.h"
+#include "gxcpath.h"
+#include "gximage.h"
+
+/* ---------------- Unpacking procedures ---------------- */
+
+private const byte *
+sample_unpack_12(byte * bptr, int *pdata_x, const byte * data,
+ int data_x, uint dsize, const sample_lookup_t * ignore_ptab,
+ int spread)
+{
+ register frac *bufp = (frac *) bptr;
+ uint dskip = (data_x >> 1) * 3;
+ const byte *psrc = data + dskip;
+#define inc_bufp(bp, n) bp = (frac *)((byte *)(bp) + (n))
+ uint sample;
+ int left = dsize - dskip;
+
+ if ((data_x & 1) && left > 0)
+ switch (left) {
+ default:
+ sample = ((uint) (psrc[1] & 0xf) << 8) + psrc[2];
+ *bufp = bits2frac(sample, 12);
+ inc_bufp(bufp, spread);
+ psrc += 3;
+ left -= 3;
+ break;
+ case 2: /* xxxxxxxx xxxxdddd */
+ *bufp = (psrc[1] & 0xf) * (frac_1 / 15);
+ case 1: /* xxxxxxxx */
+ left = 0;
+ }
+ while (left >= 3) {
+ sample = ((uint) * psrc << 4) + (psrc[1] >> 4);
+ *bufp = bits2frac(sample, 12);
+ inc_bufp(bufp, spread);
+ sample = ((uint) (psrc[1] & 0xf) << 8) + psrc[2];
+ *bufp = bits2frac(sample, 12);
+ inc_bufp(bufp, spread);
+ psrc += 3;
+ left -= 3;
+ }
+ /* Handle trailing bytes. */
+ switch (left) {
+ case 2: /* dddddddd ddddxxxx */
+ sample = ((uint) * psrc << 4) + (psrc[1] >> 4);
+ *bufp = bits2frac(sample, 12);
+ inc_bufp(bufp, spread);
+ *bufp = (psrc[1] & 0xf) * (frac_1 / 15);
+ break;
+ case 1: /* dddddddd */
+ sample = (uint) * psrc << 4;
+ *bufp = bits2frac(sample, 12);
+ break;
+ case 0: /* Nothing more to do. */
+ ;
+ }
+ *pdata_x = 0;
+ return bptr;
+}
+
+/* ------ Strategy procedure ------ */
+
+/* Use special (slow) logic for 12-bit source values. */
+private irender_proc(image_render_frac);
+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;
+}
+
+void
+gs_gxi12bit_init(gs_memory_t * mem)
+{
+ image_strategies.fracs = image_strategy_frac;
+ sample_unpack_12_proc = sample_unpack_12;
+}
+
+/* ---------------- 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
+private int
+image_render_frac(gx_image_enum * penum, const byte * buffer, int data_x,
+ uint w, int h, gx_device * dev)
+{
+ const gs_imager_state *pis = penum->pis;
+ gs_logical_operation_t lop = penum->log_op;
+ gx_dda_fixed_point pnext;
+ image_posture posture = penum->posture;
+ fixed xl, ytf;
+ 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 *pdevc = &devc1;
+ gx_device_color *pdevc_next = &devc2;
+ int spp = penum->spp;
+ const frac *psrc = (const frac *)buffer + data_x * spp;
+ fixed xrun; /* x at start of run */
+ int irun; /* int xrun */
+ fixed yrun; /* y ditto */
+ color_fracs run; /* run value */
+ color_fracs next; /* next sample value */
+ const frac *bufend = psrc + w;
+ int code;
+
+ if (h == 0)
+ return 0;
+ pnext = penum->dda.pixel0;
+ xrun = xl = dda_current(pnext.x);
+ irun = fixed2int_var_rounded(xrun);
+ yrun = ytf = dda_current(pnext.y);
+ pdyx = dda_current(penum->dda.row.x) - penum->cur.x;
+ pdyy = dda_current(penum->dda.row.y) - penum->cur.y;
+ 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, pis, dev, gs_color_select_source);
+ run.v[0] = ~psrc[0]; /* force remap */
+
+ while (psrc < bufend) {
+ 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, pis, dev,
+ gs_color_select_source);
+ 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, pis, dev,
+ gs_color_select_source);
+ 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, pis, dev,
+ gs_color_select_source);
+ 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, pis, dev,
+ gs_color_select_source);
+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)) {
+ /* Fill the region between xrun/irun and xl */
+ gx_device_color *ptemp;
+
+ if (posture != image_portrait) { /* Parallelogram */
+ code = (*dev_proc(dev, fill_parallelogram))
+ (dev, xrun, yrun,
+ xl - xrun, ytf - yrun, pdyx, pdyy,
+ pdevc, lop);
+ } 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;
+ ptemp = pdevc;
+ pdevc = pdevc_next;
+ pdevc_next = ptemp;
+ xrun = xl;
+ yrun = ytf;
+ }
+ run = next;
+inc:
+ xl = dda_next(pnext.x);
+ ytf = dda_next(pnext.y);
+ }
+ /* Fill the final run. */
+ code = (*dev_proc(dev, fill_parallelogram))
+ (dev, xrun, yrun, xl - xrun, ytf - yrun, pdyx, pdyy, pdevc, lop);
+ return (code < 0 ? code : 1);
+}
diff --git a/pstoraster/gxicolor.c b/pstoraster/gxicolor.c
new file mode 100644
index 000000000..ac2ab49ef
--- /dev/null
+++ b/pstoraster/gxicolor.c
@@ -0,0 +1,307 @@
+/* Copyright (C) 1992, 1995, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Color image rendering */
+#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 "gxdevice.h"
+#include "gxcmap.h"
+#include "gxdcconv.h"
+#include "gxdcolor.h"
+#include "gxistate.h"
+#include "gzpath.h"
+#include "gxdevmem.h"
+#include "gxcpath.h"
+#include "gximage.h"
+
+/* ------ Strategy procedure ------ */
+
+private irender_proc(image_render_color);
+private irender_proc_t
+image_strategy_color(gx_image_enum * penum)
+{
+ return image_render_color;
+}
+
+void
+gs_gxicolor_init(gs_memory_t * mem)
+{
+ image_strategies.color = image_strategy_color;
+}
+
+/* ------ 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;
+private int
+image_render_color(gx_image_enum * penum, const byte * buffer, int data_x,
+ uint w, int h, gx_device * dev)
+{
+ const gs_imager_state *pis = penum->pis;
+ gs_logical_operation_t lop = penum->log_op;
+ gx_dda_fixed_point pnext;
+ image_posture posture = penum->posture;
+ fixed xprev, yprev;
+ 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_3)) = cmap_procs->map_rgb;
+ cmap_proc_cmyk((*map_4)) =
+ (penum->alpha ? cmap_procs->map_rgb_alpha : 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;
+ gx_image_clue clue_temp;
+ int spp = penum->spp;
+ const byte *psrc = buffer + data_x * spp;
+ fixed xrun; /* x at start of run */
+ fixed yrun; /* y ditto */
+ int irun; /* int x/rrun */
+ color_samples run; /* run value */
+ color_samples next; /* next sample value */
+ const byte *bufend = psrc + w;
+ bool use_cache = spp * penum->bps <= 12;
+ int code = 0;
+
+ if (h == 0)
+ return 0;
+ pnext = penum->dda.pixel0;
+ xrun = xprev = dda_current(pnext.x);
+ yrun = yprev = dda_current(pnext.y);
+ pdyx = dda_current(penum->dda.row.x) - penum->cur.x;
+ pdyy = dda_current(penum->dda.row.y) - penum->cur.y;
+ 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;
+ default:
+ break;
+ }
+
+ if_debug4('b', "[b]y=%d w=%d xt=%f yt=%f\n",
+ penum->y, w, fixed2float(xprev), fixed2float(yprev));
+ 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) {
+ dda_next(pnext.x);
+ dda_next(pnext.y);
+#define CLUE_HASH3(penum, next)\
+ &penum->clues[(next.v[0] + (next.v[1] << 2) + (next.v[2] << 4)) & 255];
+#define CLUE_HASH4(penum, next)\
+ &penum->clues[(next.v[0] + (next.v[1] << 2) + (next.v[2] << 4) +\
+ (next.v[3] << 6)) & 255]
+
+ if (spp == 4) { /* cmyk or rgba */
+ next.v[0] = psrc[0];
+ next.v[1] = psrc[1];
+ next.v[2] = psrc[2];
+ next.v[3] = psrc[3];
+ psrc += 4;
+map4: if (next.all == run.all)
+ goto inc;
+ if (use_cache) {
+ pic_next = CLUE_HASH4(penum, next);
+ if (pic_next->key == next.all)
+ goto f;
+ /*
+ * If we are really unlucky, pic_next == pic,
+ * so mapping this color would clobber the one
+ * we're about to use for filling the run.
+ */
+ if (pic_next == pic) {
+ clue_temp = *pic;
+ pic = &clue_temp;
+ }
+ pic_next->key = next.all;
+ }
+ if (device_color) {
+ (*map_4)(byte2frac(next.v[0]), byte2frac(next.v[1]),
+ byte2frac(next.v[2]), byte2frac(next.v[3]),
+ pdevc_next, pis, dev,
+ gs_color_select_source);
+ goto mapped;
+ }
+ decode_sample(next.v[3], cc, 3);
+ if_debug1('B', "[B]cc[3]=%g\n", cc.paint.values[3]);
+ } else if (spp == 3) { /* rgb */
+ next.v[0] = psrc[0];
+ next.v[1] = psrc[1];
+ next.v[2] = psrc[2];
+ psrc += 3;
+ if (next.all == run.all)
+ goto inc;
+ if (use_cache) {
+ pic_next = CLUE_HASH3(penum, next);
+ if (pic_next->key == next.all)
+ goto f;
+ /* See above re the following check. */
+ if (pic_next == pic) {
+ clue_temp = *pic;
+ pic = &clue_temp;
+ }
+ pic_next->key = next.all;
+ }
+ if (device_color) {
+ (*map_3)(byte2frac(next.v[0]), byte2frac(next.v[1]),
+ byte2frac(next.v[2]),
+ pdevc_next, pis, dev,
+ gs_color_select_source);
+ goto mapped;
+ }
+ } else if (spp == 2) { /* gray+alpha */
+ next.v[2] = next.v[1] = next.v[0] = psrc[0];
+ next.v[3] = psrc[1];
+ psrc += 2;
+ goto map4;
+ } else { /* spp == 5, cmyk+alpha */
+ /* Convert CMYK to RGB. */
+ frac rgb[3];
+
+ color_cmyk_to_rgb(byte2frac(psrc[0]), byte2frac(psrc[1]),
+ byte2frac(psrc[2]), byte2frac(psrc[3]),
+ pis, rgb);
+ /*
+ * It seems silly to do all this converting between
+ * fracs and bytes, but that's what the current
+ * APIs require.
+ */
+ next.v[0] = frac2byte(rgb[0]);
+ next.v[1] = frac2byte(rgb[1]);
+ next.v[2] = frac2byte(rgb[2]);
+ next.v[3] = psrc[4];
+ psrc += 5;
+ goto map4;
+ }
+ 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, pis, dev,
+ gs_color_select_source);
+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))
+ goto set;
+fill: /* Fill the region between */
+ /* xrun/irun and xprev */
+ switch (posture) {
+ case image_portrait:
+ { /* Rectangle */
+ int xi = irun;
+ int wi =
+ (irun = fixed2int_var_rounded(xprev)) - xi;
+
+ if (wi < 0)
+ xi += wi, wi = -wi;
+ if (wi > 0)
+ code = gx_fill_rectangle_device_rop(xi, vci, wi, vdi,
+ pdevc, dev, lop);
+ xrun = xprev; /* for sake of final run */
+ }
+ break;
+ case image_landscape:
+ { /* 90 degree rotated rectangle */
+ int yi = irun;
+ int hi =
+ (irun = fixed2int_var_rounded(yprev)) - yi;
+
+ if (hi < 0)
+ yi += hi, hi = -hi;
+ if (hi > 0)
+ code = gx_fill_rectangle_device_rop(vci, yi, vdi, hi,
+ pdevc, dev, lop);
+ yrun = yprev; /* for sake of final run */
+ }
+ break;
+ default:
+ { /* Parallelogram */
+ code = (*dev_proc(dev, fill_parallelogram))
+ (dev, xrun, yrun,
+ xprev - xrun, yprev - yrun, pdyx, pdyy,
+ pdevc, lop);
+ xrun = xprev;
+ yrun = yprev;
+ }
+ }
+ 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: xprev = dda_current(pnext.x);
+ yprev = dda_current(pnext.y); /* harmless if no skew */
+ }
+ /* Fill the last run. */
+ code = (*dev_proc(dev, fill_parallelogram))
+ (dev, xrun, yrun, xprev - xrun, yprev - yrun, pdyx, pdyy, pdevc, lop);
+ return (code < 0 ? code : 1);
+}
diff --git a/pstoraster/gxidata.c b/pstoraster/gxidata.c
new file mode 100644
index 000000000..58d32540e
--- /dev/null
+++ b/pstoraster/gxidata.c
@@ -0,0 +1,255 @@
+/* Copyright (C) 1995, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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 ImageType 1 image. */
+int
+gx_image1_plane_data(gx_device * dev, gx_image_enum_common_t * info,
+ const gx_image_plane_t * planes, int height)
+{
+ gx_image_enum *penum = (gx_image_enum *) info;
+ int y = penum->y;
+ int y_end = min(y + height, penum->rect.h);
+ int width_spp = penum->rect.w * penum->spp;
+ int num_planes = penum->num_planes;
+
+#define bcount(plane) /* bytes per data row */\
+ (((penum->rect.w + (plane).data_x) * penum->spp / num_planes * penum->bps\
+ + 7) >> 3)
+ fixed adjust = penum->adjust;
+ ulong offsets[gs_image_max_components];
+ int ignore_data_x;
+ int code;
+
+ if (height == 0)
+ return 0;
+
+ /* 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. */
+
+ memset(offsets, 0, num_planes * sizeof(offsets[0]));
+ for (; penum->y < y_end; penum->y++) {
+ int px;
+
+ /*
+ * Normally, we unpack the data into the buffer, but if
+ * there is only one plane and we don't need to expand the
+ * input samples, we may use the data directly.
+ */
+ int sourcex = planes[0].data_x;
+ const byte *buffer =
+ (*penum->unpack) (penum->buffer, &sourcex,
+ planes[0].data + offsets[0],
+ planes[0].data_x, bcount(planes[0]),
+ &penum->map[0].table, penum->spread);
+
+ offsets[0] += planes[0].raster;
+ for (px = 1; px < num_planes; ++px) {
+ (*penum->unpack) (penum->buffer + (px << penum->log2_xbytes),
+ &ignore_data_x,
+ planes[px].data + offsets[px],
+ planes[px].data_x, bcount(planes[px]),
+ &penum->map[px].table, penum->spread);
+ offsets[px] += planes[px].raster;
+ }
+#ifdef DEBUG
+ if (gs_debug_c('B')) {
+ int i, n = width_spp;
+
+ dlputs("[B]row:");
+ for (i = 0; i < n; i++)
+ dprintf1(" %02x", buffer[i]);
+ dputs("\n");
+ }
+#endif
+ penum->cur.x = dda_current(penum->dda.row.x);
+ dda_next(penum->dda.row.x);
+ penum->cur.y = dda_current(penum->dda.row.y);
+ dda_next(penum->dda.row.y);
+ if (!penum->interpolate)
+ switch (penum->posture) {
+ case image_portrait:
+ { /* Precompute integer y and height, */
+ /* and check for clipping. */
+ fixed yc = penum->cur.y, yn = dda_current(penum->dda.row.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->cur.x, xn = dda_current(penum->dda.row.x);
+
+ if (xn < xc) {
+ fixed temp = xn;
+
+ xn = xc;
+ xc = temp;
+ }
+ xc -= adjust;
+ 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:
+ ;
+ }
+ dda_translate(penum->dda.pixel0.x,
+ penum->cur.x - penum->prev.x);
+ dda_translate(penum->dda.pixel0.y,
+ penum->cur.y - penum->prev.y);
+ penum->prev = penum->cur;
+ code = (*penum->render) (penum, buffer, sourcex, width_spp, 1,
+ dev);
+ if (code < 0)
+ goto err;
+ mt:;
+ }
+ if (penum->y < penum->rect.h) {
+ code = 0;
+ goto out;
+ }
+ /* End of data. Render any left-over buffered data. */
+ code = gx_image1_flush(info);
+ if (code < 0) {
+ penum->y--;
+ goto err;
+ }
+ code = 1;
+ goto out;
+ err: /* Error or interrupt, restore original state. */
+ while (penum->y > y) {
+ dda_previous(penum->dda.row.x);
+ dda_previous(penum->dda.row.y);
+ --(penum->y);
+ }
+ /* Note that caller must call end_image */
+ /* for both error and normal termination. */
+ out:return code;
+}
+
+/* Flush any buffered data. */
+int
+gx_image1_flush(gx_image_enum_common_t * info)
+{
+ gx_image_enum *penum = (gx_image_enum *)info;
+ int width_spp = penum->rect.w * penum->spp;
+ fixed adjust = penum->adjust;
+
+ penum->cur.x = dda_current(penum->dda.row.x);
+ penum->cur.y = dda_current(penum->dda.row.y);
+ switch (penum->posture) {
+ case image_portrait:
+ {
+ fixed yc = penum->cur.y;
+
+ penum->yci = fixed2int_rounded(yc - adjust);
+ penum->hci = fixed2int_rounded(yc + adjust) - penum->yci;
+ }
+ break;
+ case image_landscape:
+ {
+ fixed xc = penum->cur.x;
+
+ penum->xci = fixed2int_rounded(xc - adjust);
+ penum->wci = fixed2int_rounded(xc + adjust) - penum->xci;
+ }
+ break;
+ case image_skewed: /* pacify compilers */
+ ;
+ }
+ dda_translate(penum->dda.pixel0.x, penum->cur.x - penum->prev.x);
+ dda_translate(penum->dda.pixel0.y, penum->cur.y - penum->prev.y);
+ penum->prev = penum->cur;
+ return (*penum->render)(penum, NULL, 0, width_spp, 0, penum->dev);
+}
+
+/* Clean up by releasing the buffers. */
+/* Currently we ignore draw_last. */
+int
+gx_image1_end_image(gx_device *ignore_dev, gx_image_enum_common_t * info,
+ bool draw_last)
+{
+ gx_image_enum *penum = (gx_image_enum *) info;
+ gs_memory_t *mem = penum->memory;
+ stream_IScale_state *scaler = penum->scaler;
+
+#ifdef DEBUG
+ if_debug2('b', "[b]%send_image, y=%d\n",
+ (penum->y < penum->rect.h ? "premature " : ""), penum->y);
+#endif
+ 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;
+}
diff --git a/pstoraster/gxifast.c b/pstoraster/gxifast.c
new file mode 100644
index 000000000..cb7008241
--- /dev/null
+++ b/pstoraster/gxifast.c
@@ -0,0 +1,716 @@
+/* Copyright (C) 1989, 1995, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Fast monochrome image rendering */
+#include "gx.h"
+#include "memory_.h"
+#include "gpcheck.h"
+#include "gsbittab.h"
+#include "gserrors.h"
+#include "gxfixed.h"
+#include "gxarith.h"
+#include "gxmatrix.h"
+#include "gsccolor.h"
+#include "gspaint.h"
+#include "gsutil.h"
+#include "gxdevice.h"
+#include "gxcmap.h"
+#include "gxdcolor.h"
+#include "gxistate.h"
+#include "gzpath.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
+
+/* ------ Strategy procedure ------ */
+
+/* Use special fast logic for portrait or landscape black-and-white images. */
+private irender_proc(image_render_simple);
+private irender_proc(image_render_landscape);
+private irender_proc_t
+image_strategy_simple(gx_image_enum * penum)
+{
+ irender_proc_t rproc;
+ fixed ox = dda_current(penum->dda.pixel0.x);
+ fixed oy = dda_current(penum->dda.pixel0.y);
+
+ if (penum->use_rop || penum->spp != 1 || penum->bps != 1)
+ return 0;
+ switch (penum->posture) {
+ case image_portrait:
+ { /* Use fast portrait algorithm. */
+ long dev_width =
+ fixed2long_pixround(ox + penum->x_extent.x) -
+ fixed2long_pixround(ox);
+
+ if (dev_width != penum->rect.w) {
+ /*
+ * 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,
+ (gx_image_enum_common_t *)penum,
+ false);
+ return 0;
+ }
+ }
+ if_debug2('b', "[b]render=simple, unpack=copy; rect.w=%d, dev_width=%ld\n",
+ penum->rect.w, dev_width);
+ rproc = image_render_simple;
+ break;
+ }
+ case image_landscape:
+ { /* Use fast landscape algorithm. */
+ long dev_width =
+ fixed2long_pixround(oy + penum->x_extent.y) -
+ fixed2long_pixround(oy);
+ 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->rect.w && 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,
+ (gx_image_enum_common_t *) penum,
+ false);
+ return 0;
+ }
+ penum->xi_next = penum->line_xy = fixed2int_var_rounded(ox);
+ if_debug3('b', "[b]render=landscape, unpack=copy; rect.w=%d, dev_width=%ld, line_size=%ld\n",
+ penum->rect.w, dev_width, line_size);
+ rproc = image_render_landscape;
+ /* Precompute values needed for rasterizing. */
+ penum->dxy =
+ float2fixed(penum->matrix.xy +
+ fixed2float(fixed_epsilon) / 2);
+ break;
+ }
+ default:
+ return 0;
+ }
+ /* Precompute values needed for rasterizing. */
+ penum->dxx =
+ float2fixed(penum->matrix.xx + fixed2float(fixed_epsilon) / 2);
+ /*
+ * 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 = sample_unpack_copy;
+ penum->unpack_bps = 8;
+ return rproc;
+}
+
+void
+gs_gxifast_init(gs_memory_t * mem)
+{
+ image_strategies.simple = image_strategy_simple;
+}
+
+/* ------ Rendering procedures ------ */
+
+/*
+ * 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.
+ *
+ * To be precise, the input to this routine is the w bits starting at
+ * bit data_x in buffer. These w bits expand to abs(x_extent) bits,
+ * either inverted (zero = 0xff) or not (zero = 0), starting at bit
+ * line_x in line which corresponds to coordinate
+ * fixed2int_pixround(xcur + min(x_extent, 0)). Note that the entire
+ * bytes containing the first and last output bits are affected: the
+ * other bits in those bytes are set to zero (i.e., the value of the
+ * 'zero' argument).
+ */
+#ifdef STATS
+struct stats_image_fast_s {
+ long
+ calls, all0s, all1s, runs, lbit0, byte00, byte01, byte02, byte03,
+ byte04, rbit0, lbit1, byte1, rbit1, thin, thin2, nwide, bwide,
+ nfill, bfill;
+} stats_image_fast;
+# define INCS(stat) ++stats_image_fast.stat
+# define ADDS(stat, n) stats_image_fast.stat += n
+#else
+# define INCS(stat) DO_NOTHING
+# define ADDS(stat, n) DO_NOTHING
+#endif
+inline private void
+fill_row(byte *line, int line_x, uint raster, int value)
+{
+ memset(line + (line_x >> 3), value, raster - (line_x >> 3));
+}
+private void
+image_simple_expand(byte * line, int line_x, uint raster,
+ const byte * buffer, int data_x, uint w,
+ fixed xcur, fixed x_extent, byte zero /* 0 or 0xff */ )
+{
+ int dbitx = data_x & 7;
+ byte sbit = 0x80 >> dbitx;
+ byte sbitmask = 0xff >> dbitx;
+ uint wx = dbitx + w;
+ gx_dda_fixed xl;
+ gx_dda_step_fixed dxx4, dxx8, dxx16, dxx24, dxx32;
+ register const byte *psrc = buffer + (data_x >> 3);
+
+ /*
+ * The following 3 variables define the end of the input data row.
+ * We would put them in a struct, except that no compiler that we
+ * know of will optimize individual struct members as though they
+ * were simple variables (e.g., by putting them in registers).
+ *
+ * endp points to the byte that contains the bit just beyond the
+ * end of the row. endx gives the bit number of this bit within
+ * the byte, with 0 being the *least* significant bit. endbit is
+ * a mask for this bit.
+ */
+ const byte *endp = psrc + (wx >> 3);
+ int endx = ~wx & 7;
+ byte endbit = 1 << endx;
+
+ /*
+ * The following 3 variables do the same for start of the last run
+ * of the input row (think of it as a pointer to just beyond the
+ * end of the next-to-last run).
+ */
+ const byte *stop = endp;
+ int stopx;
+ byte stopbit = endbit;
+ byte data;
+ byte one = ~zero;
+ fixed xl0;
+
+ if (w == 0)
+ return;
+ INCS(calls);
+
+ /* Scan backward for the last transition. */
+ if (stopbit == 0x80)
+ --stop, stopbit = 1;
+ else
+ stopbit <<= 1;
+ /* Now (stop, stopbit) give the last bit of the row. */
+ {
+ byte stopmask = -stopbit << 1;
+ byte last = *stop;
+
+ if (stop == psrc) /* only 1 input byte */
+ stopmask &= sbitmask;
+ if (last & stopbit) {
+ /* The last bit is a 1: look for a 0-to-1 transition. */
+ if (~last & stopmask) { /* Transition in last byte. */
+ last |= stopbit - 1;
+ } else { /* No transition in the last byte. */
+ while (stop > psrc && stop[-1] == 0xff)
+ --stop;
+ if (stop == psrc ||
+ (stop == psrc + 1 && !(~*psrc & sbitmask))
+ ) {
+ /* The input is all 1s. Clear the row and exit. */
+ INCS(all1s);
+ fill_row(line, line_x, raster, one);
+ return;
+ }
+ last = *--stop;
+ }
+ stopx = byte_bit_run_length_0[byte_reverse_bits[last]] - 1;
+ } else {
+ /* The last bit is a 0: look for a 1-to-0 transition. */
+ if (last & stopmask) { /* Transition in last byte. */
+ last &= -stopbit;
+ } else { /* No transition in the last byte. */
+ while (stop > psrc && stop[-1] == 0)
+ --stop;
+ if (stop == psrc ||
+ (stop == psrc + 1 && !(*psrc & sbitmask))
+ ) {
+ /* The input is all 0s. Clear the row and exit. */
+ INCS(all0s);
+ fill_row(line, line_x, raster, zero);
+ return;
+ }
+ last = *--stop;
+ }
+ stopx = byte_bit_run_length_0[byte_reverse_bits[last ^ 0xff]] - 1;
+ }
+ if (stopx < 0)
+ stopx = 7, ++stop;
+ stopbit = 1 << stopx;
+ }
+
+ /* Pre-clear the row. */
+ fill_row(line, line_x, raster, zero);
+
+ /* Set up the DDAs. */
+ xl0 =
+ (x_extent >= 0 ?
+ fixed_fraction(fixed_pre_pixround(xcur)) :
+ fixed_fraction(fixed_pre_pixround(xcur + x_extent)) - x_extent);
+ xl0 += int2fixed(line_x);
+ dda_init(xl, xl0, x_extent, w);
+ dxx4 = xl.step;
+ dda_step_add(dxx4, xl.step);
+ dda_step_add(dxx4, dxx4);
+ dxx8 = dxx4;
+ dda_step_add(dxx8, dxx4);
+ dxx16 = dxx8;
+ dda_step_add(dxx16, dxx8);
+ dxx24 = dxx16;
+ dda_step_add(dxx24, dxx8);
+ dxx32 = dxx24;
+ dda_step_add(dxx32, dxx8);
+
+ /*
+ * Loop invariants:
+ * data = *psrc;
+ * sbit = 1 << n, 0<=n<=7.
+ */
+ 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) {
+ dda_next(xl);
+ 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) {
+ dda_state_next(xl.state, dxx8);
+ psrc += 2;
+ INCS(byte01);
+ } else if ((data = psrc[3]) != 0) {
+ dda_state_next(xl.state, dxx16);
+ psrc += 3;
+ INCS(byte02);
+ } else if ((data = psrc[4]) != 0) {
+ dda_state_next(xl.state, dxx24);
+ psrc += 4;
+ INCS(byte03);
+ } else {
+ dda_state_next(xl.state, dxx32);
+ psrc += 4;
+ INCS(byte04);
+ goto sw;
+ }
+ if (data > 0xf)
+ sbit = 0x80;
+ else {
+ sbit = 0x08;
+ dda_state_next(xl.state, dxx4);
+ }
+ data ^= 0xff; /* invert */
+ while (data & sbit) {
+ dda_next(xl);
+ sbit >>= 1;
+ INCS(rbit0);
+ }
+ }
+ x0 = dda_current_fixed2int(xl);
+ if (psrc >= stop && sbit == stopbit) {
+ /*
+ * We've scanned the last run of 0s.
+ * Prepare to fill the final run of 1s.
+ */
+ n = fixed2int(xl0 + x_extent) - x0;
+ } else { /* Scan a run of ones. */
+ /* We know the current bit is a one. */
+ data ^= 0xff; /* un-invert */
+ do {
+ dda_next(xl);
+ sbit >>= 1;
+ INCS(lbit1);
+ }
+ while (data & sbit);
+ if (!sbit) { /* Scan a run of 0xff bytes. */
+ while ((data = *++psrc) == 0xff) {
+ dda_state_next(xl.state, dxx8);
+ INCS(byte1);
+ }
+ if (data < 0xf0)
+ sbit = 0x80;
+ else {
+ sbit = 0x08;
+ dda_state_next(xl.state, dxx4);
+ }
+ while (data & sbit) {
+ dda_next(xl);
+ sbit >>= 1;
+ INCS(rbit1);
+ }
+ }
+ n = dda_current_fixed2int(xl) - x0;
+ }
+
+ /* Fill the run in the scan line. */
+ 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];
+ }
+ if (psrc >= stop && sbit == stopbit)
+ break;
+ }
+}
+
+/* Copy one rendered scan line to the device. */
+private int
+copy_portrait(gx_image_enum * penum, const byte * data, int dx, int raster,
+ int x, int y, int w, int h, gx_device * dev)
+{
+ const gx_device_color *pdc0;
+ const gx_device_color *pdc1;
+ uint align = alignment_mod(data, align_bitmap_mod);
+
+ /*
+ * We know that the lookup table maps 1 bit to 1 bit,
+ * so it can only have 2 states: straight-through or invert.
+ */
+ if (penum->map[0].table.lookup4x1to32[0])
+ pdc0 = &penum->icolor1, pdc1 = &penum->icolor0;
+ else
+ pdc0 = &penum->icolor0, pdc1 = &penum->icolor1;
+ data -= align;
+ dx += align << 3;
+ if (gx_dc_is_pure(pdc0) && gx_dc_is_pure(pdc1)) {
+ /* Just use copy_mono. */
+ dev_proc_copy_mono((*copy_mono)) =
+ (h == 1 || (raster & (align_bitmap_mod - 1)) == 0 ?
+ dev_proc(dev, copy_mono) : gx_copy_mono_unaligned);
+ return (*copy_mono)
+ (dev, data, dx, raster, gx_no_bitmap_id,
+ x, y, w, h, pdc0->colors.pure, pdc1->colors.pure);
+ }
+ /*
+ * At least one color isn't pure: if the other one is transparent, use
+ * the opaque color's fill_masked procedure. Note that we use a
+ * slightly unusual representation for transparent here (per
+ * gx_begin_image1): a pure color with pixel value gx_no_color_index.
+ */
+ {
+ const gx_device_color *pdc;
+ bool invert;
+
+#define DC_IS_NULL(pdc)\
+ (gx_dc_is_pure(pdc) && (pdc)->colors.pure == gx_no_color_index)
+
+ if (DC_IS_NULL(pdc1)) {
+ pdc = pdc0;
+ invert = true;
+ } else {
+ if (!DC_IS_NULL(pdc0)) {
+ int code = gx_device_color_fill_rectangle
+ (pdc0, x, y, w, h, dev, lop_default, NULL);
+
+ if (code < 0)
+ return code;
+ }
+ pdc = pdc1;
+ invert = false;
+ }
+
+#undef DC_IS_NULL
+
+ return (*pdc->type->fill_masked)
+ (pdc, data, dx, raster, gx_no_bitmap_id, x, y, w, h,
+ dev, lop_default, invert);
+
+ }
+}
+
+/* Rendering procedure for a monobit image with no */
+/* skew or rotation and pure colors. */
+private int
+image_render_simple(gx_image_enum * penum, const byte * buffer, int data_x,
+ uint w, int h, gx_device * dev)
+{
+ dev_proc_copy_mono((*copy_mono)) = dev_proc(dev, copy_mono);
+ const fixed dxx = penum->dxx;
+ const byte *line;
+ uint line_width, line_size;
+ int line_x;
+ fixed xcur = dda_current(penum->dda.pixel0.x);
+ int ix = fixed2int_pixround(xcur);
+ const int iy = penum->yci, ih = penum->hci;
+ const gx_device_color * const pdc0 = &penum->icolor0;
+ const gx_device_color * const pdc1 = &penum->icolor1;
+ int dy;
+
+ if (h == 0)
+ return 0;
+ if (penum->line == 0) { /* A direct BitBlt is possible. */
+ line = buffer;
+ line_size = (w + 7) >> 3;
+ line_width = w;
+ line_x = 0;
+ } else if (copy_mono == dev_proc(&mem_mono_device, copy_mono) &&
+ dxx > 0 && gx_dc_is_pure(pdc1) && gx_dc_is_pure(pdc0) &&
+ /* We know the colors must be (0,1) or (1,0). */
+ (pdc0->colors.pure ^ pdc1->colors.pure) == 1 &&
+ !penum->clip_image
+ ) {
+ /* Do the operation directly into the memory device bitmap. */
+ int ixr = fixed2int_pixround(xcur + penum->x_extent.x) - 1;
+ int line_ix;
+ int ib_left = ix >> 3, ib_right = ixr >> 3;
+ byte *scan_line = scan_line_base((gx_device_memory *) dev, iy);
+ byte save_left, save_right, mask;
+
+ 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 = scan_line[ib_left];
+ save_right = scan_line[ib_right];
+ image_simple_expand(scan_line + (line_ix >> 3), line_x,
+ line_size, buffer, data_x, w, xcur,
+ penum->x_extent.x,
+ ((pdc0->colors.pure == 0) !=
+ (penum->map[0].table.lookup4x1to32[0] == 0) ?
+ 0xff : 0));
+ if (ix & 7)
+ mask = (byte) (0xff00 >> (ix & 7)),
+ scan_line[ib_left] =
+ (save_left & mask) + (scan_line[ib_left] & ~mask);
+ if ((ixr + 1) & 7)
+ mask = (byte) (0xff00 >> ((ixr + 1) & 7)),
+ scan_line[ib_right] =
+ (scan_line[ib_right] & mask) + (save_right & ~mask);
+ if (ih <= 1)
+ return 1;
+ /****** MAY BE UNALIGNED ******/
+ line = scan_line + (line_ix >> 3);
+ if (dxx < 0)
+ ix -= line_width;
+ for (dy = 1; dy < ih; dy++) {
+ int code = (*copy_mono)
+ (dev, line, line_x, line_size, gx_no_bitmap_id,
+ ix, iy + dy, line_width, 1,
+ (gx_color_index)0, (gx_color_index)1);
+
+ if (code < 0)
+ return code;
+ }
+ return 0;
+ } else {
+ line = penum->line;
+ line_size = penum->line_size;
+ line_width = penum->line_width;
+ line_x = ix & (align_bitmap_mod * 8 - 1);
+ image_simple_expand(penum->line, line_x, line_size,
+ buffer, data_x, w, xcur,
+ penum->x_extent.x, 0);
+ }
+
+ /* Finally, transfer the scan line to the device. */
+ if (dxx < 0)
+ ix -= line_width;
+ for (dy = 0; dy < ih; dy++) {
+ int code = copy_portrait(penum, line, line_x, line_size,
+ ix, iy + dy, line_width, 1, dev);
+
+ if (code < 0)
+ return code;
+ }
+
+ return 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 *));
+private int
+image_render_landscape(gx_image_enum * penum, const byte * buffer, int data_x,
+ 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;
+ bool y_neg = penum->dxy < 0;
+
+ if (is_fneg(penum->matrix.yx))
+ ix += iw, iw = -iw, xinc = -1;
+ else
+ xinc = 1;
+ /*
+ * Because of clipping, there may be discontinuous jumps in the values
+ * of ix (xci). If this happens, or if we are at the end of the data or
+ * a client has requested flushing, flush the flipping buffer.
+ */
+ if (ix != penum->xi_next || h == 0) {
+ int xi = penum->xi_next;
+ int code =
+ (xinc > 0 ?
+ copy_landscape(penum, penum->line_xy, xi, y_neg, dev) :
+ copy_landscape(penum, xi, penum->line_xy, y_neg, dev));
+
+ if (code < 0)
+ return code;
+ penum->line_xy = ix;
+ if (h == 0)
+ return code;
+ }
+ for (; iw != 0; iw -= xinc) {
+ if (xinc < 0)
+ --ix;
+ xmod = ix & 7;
+ row = line + xmod * raster;
+ if (orig_row == 0) {
+ image_simple_expand(row, 0, raster,
+ buffer, data_x, w,
+ dda_current(penum->dda.pixel0.y),
+ penum->x_extent.y, 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;
+ }
+ }
+ }
+ penum->xi_next = ix;
+ return 0;
+}
+
+/* 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;
+ int w = x1 - x0;
+ int y = fixed2int_pixround(dda_current(penum->dda.pixel0.y));
+
+ if (w == 0 || line_width == 0)
+ return 0;
+ /* Flip the buffered data from raster x 8 to align_bitmap_mod x */
+ /* 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. */
+ if (w < 0)
+ x0 = x1, w = -w;
+ if (y_neg)
+ y -= line_width;
+ return copy_portrait(penum, flipped, x0 & 7, align_bitmap_mod,
+ x0, y, w, line_width, dev);
+}
diff --git a/pstoraster/gxiinit.c b/pstoraster/gxiinit.c
new file mode 100644
index 000000000..4b799417c
--- /dev/null
+++ b/pstoraster/gxiinit.c
@@ -0,0 +1,921 @@
+/* Copyright (C) 1989, 1995, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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 "gsutil.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 "gxiparam.h"
+#include "gdevmrop.h"
+
+/* ---------------- Generic image support ---------------- */
+
+/* Initialize the common parts of image structures. */
+void
+gs_image_common_t_init(gs_image_common_t * pic)
+{
+ gs_make_identity(&pic->ImageMatrix);
+}
+void
+gs_data_image_t_init(gs_data_image_t * pim, int num_components)
+{
+ int i;
+
+ gs_image_common_t_init((gs_image_common_t *) pim);
+ pim->Width = pim->Height = 0;
+ pim->BitsPerComponent = 1;
+ if (num_components >= 0) {
+ for (i = 0; i < num_components * 2; i += 2)
+ pim->Decode[i] = 0, pim->Decode[i + 1] = 1;
+ } else {
+ for (i = 0; i < num_components * -2; i += 2)
+ pim->Decode[i] = 1, pim->Decode[i + 1] = 0;
+ }
+ pim->Interpolate = false;
+}
+void
+gs_pixel_image_t_init(gs_pixel_image_t * pim, const gs_color_space * color_space)
+{
+ int num_components;
+
+ if (color_space == 0 ||
+ (num_components =
+ gs_color_space_num_components(color_space)) < 0
+ )
+ num_components = 0;
+ gs_data_image_t_init((gs_data_image_t *) pim, num_components);
+ pim->format = gs_image_format_chunky;
+ pim->ColorSpace = color_space;
+ pim->CombineWithColor = false;
+}
+
+/* Initialize the common part of an image-processing enumerator. */
+int
+gx_image_enum_common_init(gx_image_enum_common_t * piec,
+ const gs_image_common_t * pic, const gx_image_enum_procs_t * piep,
+ gx_device * dev, int bits_per_component, int num_components,
+ gs_image_format_t format)
+{
+ piec->image_type = pic->type;
+ piec->procs = piep;
+ piec->dev = dev;
+ piec->id = gs_next_ids(1);
+ switch (format) {
+ case gs_image_format_chunky:
+ piec->num_planes = 1;
+ piec->plane_depths[0] = bits_per_component * num_components;
+ break;
+ case gs_image_format_component_planar:
+ piec->num_planes = num_components;
+ {
+ int i;
+
+ for (i = 0; i < num_components; ++i)
+ piec->plane_depths[i] = bits_per_component;
+ }
+ break;
+ case gs_image_format_bit_planar:
+ piec->num_planes = bits_per_component * num_components;
+ {
+ int i;
+
+ for (i = 0; i < piec->num_planes; ++i)
+ piec->plane_depths[i] = 1;
+ }
+#if 0 /* **************** */
+ break;
+#endif /* **************** */
+ default:
+ return_error(gs_error_rangecheck);
+ }
+ return 0;
+}
+
+/* ---------------- ImageType 1 images ---------------- */
+
+/* Structure descriptors */
+private_st_gx_image_enum();
+public_st_gs_image_common();
+public_st_gs_data_image();
+public_st_gs_pixel_image();
+
+/* Strategy procedures */
+gx_image_strategies_t image_strategies;
+
+/* Define the image type for ImageType 1 images. */
+private const gx_image_type_t image1_type = {
+ gx_begin_image1, gx_data_image_source_size, 1
+};
+private const gx_image_enum_procs_t image1_enum_procs = {
+ gx_image1_plane_data, gx_image1_end_image, gx_image1_flush
+};
+
+/* Define the procedures for initializing gs_image_ts to default values. */
+void
+gs_image_t_init(gs_image_t * pim, const gs_color_space * color_space)
+{
+ gs_pixel_image_t_init((gs_pixel_image_t *) pim, color_space);
+ pim->type = &image1_type;
+ pim->ImageMask = pim->adjust = (color_space == NULL);
+ pim->Alpha = gs_image_alpha_none;
+}
+void
+gs_image_t_init_mask(gs_image_t * pim, bool write_1s)
+{
+ gs_image_t_init(pim, NULL);
+ if (write_1s)
+ pim->Decode[0] = 1, pim->Decode[1] = 0;
+ else
+ pim->Decode[0] = 0, pim->Decode[1] = 1;
+}
+
+/* Compute the source size of an ordinary image with explicit data. */
+int
+gx_data_image_source_size(const gs_imager_state * pis,
+ const gs_image_common_t * pim, gs_int_point * psize)
+{
+ const gs_data_image_t *pdi = (const gs_data_image_t *)pim;
+
+ psize->x = pdi->Width;
+ psize->y = pdi->Height;
+ return 0;
+}
+
+/* Process the next piece of an image with no source data. */
+/* This procedure should never be called. */
+int
+gx_no_image_plane_data(gx_device * dev,
+ gx_image_enum_common_t * info, const gx_image_plane_t * planes, int height)
+{
+ return_error(gs_error_Fatal);
+}
+
+/* Clean up after processing an image with no source data. */
+/* This procedure may be called, but should do nothing. */
+int
+gx_ignore_end_image(gx_device * dev, gx_image_enum_common_t * info,
+ bool draw_last)
+{
+ return 0;
+}
+
+/* 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 == sample_unpack_copy)
+ bps = 1;
+ if (index >= (1 << bps) * st_device_color_max_ptrs) /* done */
+ return 0;
+ ret = ENUM_USING(st_device_color,
+ &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);
+ if (ret == 0) /* don't stop early */
+ ENUM_RETURN(0);
+ 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 == sample_unpack_copy)
+ bps = 1;
+ for (i = 0; i <= 255; i += 255 / ((1 << bps) - 1))
+ RELOC_USING(st_device_color,
+ &eptr->clues[i].dev_color, sizeof(gx_device_color));
+ }
+}
+RELOC_PTRS_END
+#undef eptr
+
+/* Forward declarations */
+private int color_draws_b_w(P2(gx_device * dev,
+ const gx_drawing_color * pdcolor));
+private void image_init_map(P3(byte * map, int map_size, const float *decode));
+private void image_init_colors(P9(gx_image_enum * penum, int bps, int spp,
+ bool multi, const float *decode,
+ const gs_imager_state * pis, gx_device * dev,
+ const gs_color_space * pcs, bool * pdcb));
+
+/* Procedures for unpacking the input data into bytes or fracs. */
+ /*extern sample_unpack_proc(sample_unpack_copy); *//* declared above */
+extern sample_unpack_proc(sample_unpack_1);
+extern sample_unpack_proc(sample_unpack_2);
+extern sample_unpack_proc(sample_unpack_4);
+extern sample_unpack_proc(sample_unpack_8);
+
+sample_unpack_proc((*sample_unpack_12_proc)); /* optional */
+
+/* Start processing an ImageType 1 image. */
+/* Note that since this is actually a begin_typed_image procedure, */
+/* the type of pim is the more abstract one. */
+int
+gx_begin_image1(gx_device * dev,
+ const gs_imager_state * pis, const gs_matrix * pmat,
+ const gs_image_common_t * pic, const gs_int_rect * prect,
+ const gx_drawing_color * pdcolor, const gx_clip_path * pcpath,
+ gs_memory_t * mem, gx_image_enum_common_t ** pinfo)
+{
+ const gs_image_t *pim = (const gs_image_t *)pic;
+ gs_image_format_t format = pim->format;
+ gx_image_enum *penum;
+ const int width = pim->Width;
+ const int height = pim->Height;
+ const int bps = pim->BitsPerComponent;
+ bool masked = pim->ImageMask;
+ const float *decode = pim->Decode;
+ bool multi;
+ int index_bps;
+ const gs_color_space *pcs = pim->ColorSpace;
+ gs_logical_operation_t lop = (pis ? pis->log_op : lop_default);
+ 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, x_extent, y_extent;
+ bool device_color;
+ gs_fixed_rect obox, cbox;
+ fixed adjust;
+
+ if (width < 0 || height < 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 (prect) {
+ if (prect->p.x < 0 || prect->p.y < 0 ||
+ prect->q.x < prect->p.x || prect->q.y < prect->p.y ||
+ prect->q.x > width || prect->q.y > height
+ )
+ return_error(gs_error_rangecheck);
+ }
+ if (pmat == 0)
+ pmat = &ctm_only(pis);
+ if ((code = gs_matrix_invert(&pim->ImageMatrix, &mat)) < 0 ||
+ (code = gs_matrix_multiply(&mat, pmat, &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 == 0)
+ return_error(gs_error_VMerror);
+ gx_image_enum_common_init((gx_image_enum_common_t *) penum, pic,
+ &image1_enum_procs, dev, bps,
+ (masked ? 1 : cs_num_components(pcs)),
+ format);
+ if (prect) {
+ penum->rect.x = prect->p.x, penum->rect.y = prect->p.y;
+ penum->rect.w = prect->q.x - prect->p.x,
+ penum->rect.h = prect->q.y - prect->p.y;
+ if ((code =
+ gs_distance_transform2fixed((const gs_matrix_fixed *)&mat,
+ (floatp) penum->rect.w, (floatp) 0,
+ &x_extent)) < 0 ||
+ (code =
+ gs_distance_transform2fixed((const gs_matrix_fixed *)&mat,
+ (floatp) 0, (floatp) penum->rect.h,
+ &y_extent)) < 0
+ ) {
+ gs_free_object(mem, penum, "gx_default_begin_image");
+ return code;
+ }
+ } else {
+ penum->rect.x = 0, penum->rect.y = 0;
+ penum->rect.w = width, penum->rect.h = height;
+ x_extent = row_extent;
+ y_extent = col_extent;
+ }
+ if ((penum->masked = masked)) { /* This is imagemask. */
+ if (bps != 1 || multi || pcs != NULL || pim->Alpha ||
+ !((decode[0] == 0.0 && decode[1] == 1.0) ||
+ (decode[0] == 1.0 && decode[1] == 0.0))
+ ) {
+ gs_free_object(mem, penum, "gx_default_begin_image");
+ 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 ? lookup4x1to32_inverted :
+ lookup4x1to32_identity),
+ 16 * 4);
+ penum->map[0].decoding = sd_none;
+ spp = 1;
+ adjust = (pim->adjust ? float2fixed(0.25) : fixed_0);
+ lop = rop3_know_S_0(lop);
+ } else { /* This is image, not imagemask. */
+ const gs_color_space_type *pcst = pcs->type;
+ int b_w_color;
+
+ spp = cs_num_components(pcs);
+ if (spp < 0) { /* Pattern not allowed */
+ gs_free_object(mem, penum, "gx_default_begin_image");
+ return_error(gs_error_rangecheck);
+ }
+ if (pim->Alpha)
+ ++spp;
+ if (spp == 1)
+ multi = false;
+ device_color = (*pcst->concrete_space) (pcs, pis) == pcs;
+ image_init_colors(penum, bps, spp, multi, decode, pis, dev,
+ pcs, &device_color);
+ adjust = fixed_0;
+ /* Try to transform non-default RasterOps to something */
+ /* that we implement less expensively. */
+ if (!pim->CombineWithColor)
+ lop = rop3_know_T_0(lop) & ~lop_T_transparent;
+ else {
+ if (rop3_uses_T(lop))
+ switch (color_draws_b_w(dev, pdcolor)) {
+ case 0:
+ lop = rop3_know_T_0(lop);
+ break;
+ case 1:
+ lop = rop3_know_T_1(lop);
+ break;
+ default:
+ ;
+ }
+ }
+ if (lop != rop3_S && /* if best case, no more work needed */
+ !rop3_uses_T(lop) && bps == 1 && spp == 1 &&
+ (b_w_color =
+ color_draws_b_w(dev, &penum->icolor0)) >= 0 &&
+ color_draws_b_w(dev, &penum->icolor1) == (b_w_color ^ 1)
+ ) {
+ if (b_w_color) { /* Swap the colors and invert the RasterOp source. */
+ gx_device_color dcolor;
+
+ dcolor = penum->icolor0;
+ penum->icolor0 = penum->icolor1;
+ penum->icolor1 = dcolor;
+ lop = rop3_invert_S(lop);
+ }
+ /*
+ * At this point, we know that the source pixels
+ * correspond directly to the S input for the raster op,
+ * i.e., icolor0 is black and icolor1 is white.
+ */
+ switch (lop) {
+ case rop3_D & rop3_S:
+ /* Implement this as an inverted mask writing 0s. */
+ penum->icolor1 = penum->icolor0;
+ /* (falls through) */
+ case rop3_D | rop3_not(rop3_S):
+ /* Implement this as an inverted mask writing 1s. */
+ memcpy(&penum->map[0].table.lookup4x1to32[0],
+ lookup4x1to32_inverted, 16 * 4);
+ rmask: /* Fill in the remaining parameters for a mask. */
+ penum->masked = masked = true;
+ color_set_pure(&penum->icolor0, gx_no_color_index);
+ penum->map[0].decoding = sd_none;
+ lop = rop3_T;
+ break;
+ case rop3_D & rop3_not(rop3_S):
+ /* Implement this as a mask writing 0s. */
+ penum->icolor1 = penum->icolor0;
+ /* (falls through) */
+ case rop3_D | rop3_S:
+ /* Implement this as a mask writing 1s. */
+ memcpy(&penum->map[0].table.lookup4x1to32[0],
+ lookup4x1to32_identity, 16 * 4);
+ goto rmask;
+ default:
+ ;
+ }
+ }
+ }
+ penum->device_color = device_color;
+ /*
+ * Adjust width upward for unpacking up to 7 trailing bits in
+ * the row, plus 1 byte for end-of-run, plus up to 7 leading
+ * bits for data_x offset within a packed byte.
+ */
+ bsize = ((bps > 8 ? width * 2 : width) + 15) * spp;
+ buffer = gs_alloc_bytes(mem, bsize, "image buffer");
+ if (buffer == 0) {
+ gs_free_object(mem, penum, "gx_default_begin_image");
+ return_error(gs_error_VMerror);
+ }
+ penum->bps = bps;
+ penum->unpack_bps = bps;
+ penum->log2_xbytes = log2_xbytes;
+ penum->spp = spp;
+ penum->alpha = pim->Alpha;
+ nplanes = (multi ? spp : 1);
+ penum->num_planes = nplanes;
+ spread = nplanes << log2_xbytes;
+ penum->spread = spread;
+ penum->matrix = mat;
+ penum->x_extent = x_extent;
+ penum->y_extent = y_extent;
+ penum->posture =
+ ((x_extent.y | y_extent.x) == 0 ? image_portrait :
+ (x_extent.x | y_extent.y) == 0 ? image_landscape :
+ image_skewed);
+ mtx = float2fixed(mat.tx);
+ mty = float2fixed(mat.ty);
+ penum->pis = pis;
+ penum->pcs = pcs;
+ penum->memory = mem;
+ penum->buffer = buffer;
+ penum->buffer_size = bsize;
+ penum->line = 0;
+ penum->line_size = 0;
+ /*
+ * If we're asked to interpolate in a partial image, we have to
+ * assume that the client either really only is interested in
+ * the given sub-image, or else is constructing output out of
+ * overlapping pieces.
+ */
+ penum->interpolate = pim->Interpolate;
+ penum->use_rop = lop != (masked ? rop3_T : rop3_S);
+#ifdef DEBUG
+ if (gs_debug_c('*')) {
+ if (penum->use_rop)
+ dprintf1("[%03x]", lop);
+ dprintf5("%c%d%c%dx%d ",
+ (masked ? (color_is_pure(pdcolor) ? 'm' : 'h') : 'i'),
+ bps,
+ (penum->posture == image_portrait ? ' ' :
+ penum->posture == image_landscape ? 'L' : 'T'),
+ width, height);
+ }
+#endif
+ penum->slow_loop = 0;
+ if (pcpath == 0) {
+ (*dev_proc(dev, get_clipping_box)) (dev, &obox);
+ cbox = obox;
+ penum->clip_image = 0;
+ } else
+ penum->clip_image =
+ (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 = rop3_T; /* rop device takes care of this */
+ 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.
+ */
+ {
+ 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);
+
+ {
+ int hwx, hwy;
+
+ 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.
+ */
+ if (hwx == 1 && eqx - epx < fixed_1) {
+ fixed 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) {
+ fixed diff =
+ arith_rshift_1(row_extent.y + col_extent.y);
+
+ mty = (((mty + diff) | fixed_half) & -fixed_half) - diff;
+ }
+ }
+ if_debug5('b', "[b]Image: %sspp=%d, bps=%d, mt=(%g,%g)\n",
+ (masked? "masked, " : ""), spp, bps,
+ fixed2float(mtx), fixed2float(mty));
+ if_debug9('b',
+ "[b] cbox=(%g,%g),(%g,%g), obox=(%g,%g),(%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),
+ penum->clip_image);
+ dda_init(penum->dda.row.x, mtx, col_extent.x, height);
+ dda_init(penum->dda.row.y, mty, col_extent.y, height);
+ if (penum->rect.y) {
+ dda_advance(penum->dda.row.x, penum->rect.y);
+ dda_advance(penum->dda.row.y, penum->rect.y);
+ }
+ penum->cur.x = penum->prev.x = dda_current(penum->dda.row.x);
+ penum->cur.y = penum->prev.y = dda_current(penum->dda.row.y);
+ dda_init(penum->dda.pixel0.x, penum->cur.x, row_extent.x,
+ width);
+ dda_init(penum->dda.pixel0.y, penum->cur.y, row_extent.y,
+ width);
+ if (penum->rect.x) {
+ dda_advance(penum->dda.pixel0.x, penum->rect.x);
+ dda_advance(penum->dda.pixel0.y, penum->rect.x);
+ } {
+ fixed ox = dda_current(penum->dda.pixel0.x);
+ fixed oy = dda_current(penum->dda.pixel0.y);
+
+ if (!penum->clip_image) /* i.e., not clip region */
+ penum->clip_image =
+ (fixed_pixround(ox + epx) < fixed_pixround(cbox.p.x) ?
+ image_clip_xmin : 0) +
+ (fixed_pixround(ox + eqx) >= fixed_pixround(cbox.q.x) ?
+ image_clip_xmax : 0) +
+ (fixed_pixround(oy + epy) < fixed_pixround(cbox.p.y) ?
+ image_clip_ymin : 0) +
+ (fixed_pixround(oy + eqy) >= fixed_pixround(cbox.q.y) ?
+ image_clip_ymax : 0);
+ }
+ }
+ penum->y = 0;
+ penum->adjust = adjust;
+ {
+ static const sample_unpack_proc_t procs[4] =
+ {
+ sample_unpack_1, sample_unpack_2,
+ sample_unpack_4, sample_unpack_8
+ };
+
+ if (index_bps == 4) {
+ if ((penum->unpack = sample_unpack_12_proc) == 0) { /* 12-bit samples are not supported. */
+ gx_default_end_image(dev,
+ (gx_image_enum_common_t *) penum,
+ false);
+ return_error(gs_error_rangecheck);
+ }
+ } else {
+ penum->unpack = procs[index_bps];
+ if_debug1('b', "[b]unpack=%d\n", bps);
+ }
+#define use_strategy(sp)\
+ (image_strategies.sp != 0 &&\
+ (penum->render = (*image_strategies.sp)(penum)) != 0)
+ if (
+ use_strategy(interpolate) ||
+ use_strategy(simple) ||
+ use_strategy(fracs) ||
+ use_strategy(mono) ||
+ use_strategy(color)
+ )
+ DO_NOTHING;
+#undef use_strategy
+ else { /* No available strategy can handle this image. */
+ gx_default_end_image(dev, (gx_image_enum_common_t *) penum,
+ false);
+ return_error(gs_error_rangecheck);
+ }
+ }
+ 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,
+ (gx_image_enum_common_t *) penum,
+ false);
+ return_error(gs_error_VMerror);
+ }
+ gx_make_clip_device(cdev, cdev, gx_cpath_list(pcpath));
+ cdev->target = dev;
+ (*dev_proc(cdev, open_device)) ((gx_device *) cdev);
+ penum->clip_dev = cdev;
+ }
+ if (penum->use_rop) { /* Set up the RasterOp source device. */
+ gx_device_rop_texture *rtdev;
+
+ code = gx_alloc_rop_texture_device(&rtdev, mem,
+ "image RasterOp");
+ if (code < 0) {
+ gx_default_end_image(dev, (gx_image_enum_common_t *) penum,
+ false);
+ return code;
+ }
+ gx_make_rop_texture_device(rtdev,
+ (penum->clip_dev != 0 ?
+ (gx_device *) penum->clip_dev :
+ dev), lop, pdcolor);
+ penum->rop_dev = rtdev;
+ }
+#ifdef DEBUG
+ if (gs_debug_c('b')) {
+ dlprintf2("[b]Image: w=%d h=%d", width, height);
+ if (prect)
+ dprintf4(" ((%d,%d),(%d,%d))",
+ prect->p.x, prect->p.y, prect->q.x, prect->q.y);
+ dprintf6(" [%g %g %g %g %g %g]\n",
+ mat.xx, mat.xy, mat.yx, mat.yy, mat.tx, mat.ty);
+ }
+#endif
+ *pinfo = (gx_image_enum_common_t *) penum;
+ return 0;
+}
+
+/* If a drawing color is black or white, return 0 or 1 respectively, */
+/* otherwise return -1. */
+private int
+color_draws_b_w(gx_device * dev, const gx_drawing_color * pdcolor)
+{
+ if (color_is_pure(pdcolor)) {
+ gx_color_value rgb[3];
+
+ (*dev_proc(dev, map_color_rgb)) (dev, gx_dc_pure_color(pdcolor),
+ rgb);
+ if (!(rgb[0] | rgb[1] | rgb[2]))
+ return 0;
+ if ((rgb[0] & rgb[1] & rgb[2]) == gx_max_color_value)
+ return 1;
+ }
+ return -1;
+}
+
+/* Initialize the color mapping tables for a non-mask image. */
+private void
+image_init_colors(gx_image_enum * penum, int bps, int spp, bool multi,
+ const float *decode /*[spp*2] */ , const gs_imager_state * pis, gx_device * dev,
+ const gs_color_space * pcs, bool * pdcb)
+{
+ int ci;
+ static const float default_decode[] =
+ {
+ 0.0, 1.0, 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, lookup4x1to32_identity, 16 * 4);
+ else if (map[0] == 0xff && map[1] == 0)
+ memcpy((byte *) p, lookup4x1to32_inverted, 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,
+ pis, dev, gs_color_select_source);
+ cc.paint.values[0] = real_decode[1];
+ (*pcs->type->remap_color) (&cc, pcs, &penum->icolor1,
+ pis, dev, gs_color_select_source);
+ }
+ }
+
+}
+/* Construct a mapping table for sample values. */
+/* map_size is 2, 4, 16, or 256. Note that 255 % (map_size - 1) == 0, */
+/* so the division 0xffffL / (map_size - 1) is always exact. */
+private void
+image_init_map(byte * map, int map_size, const float *decode)
+{
+ float min_v = decode[0];
+ float diff_v = decode[1] - min_v;
+
+ if (diff_v == 1 || diff_v == -1) { /* We can do the stepping with integers, without overflow. */
+ byte *limit = map + map_size;
+ uint value = min_v * 0xffffL;
+ int diff = diff_v * (0xffffL / (map_size - 1));
+
+ for (; map != limit; map++, value += diff)
+ *map = value >> 8;
+ } else { /* Step in floating point, with clamping. */
+ int i;
+
+ for (i = 0; i < map_size; ++i) {
+ int value = (int)((min_v + diff_v * i / (map_size - 1)) * 255);
+
+ map[i] = (value < 0 ? 0 : value > 255 ? 255 : value);
+ }
+ }
+}
diff --git a/pstoraster/gximage.h b/pstoraster/gximage.h
new file mode 100644
index 000000000..ea28e40e2
--- /dev/null
+++ b/pstoraster/gximage.h
@@ -0,0 +1,283 @@
+/* Copyright (C) 1989, 1995, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Requires gxcpath.h, gxdevmem.h, gxdcolor.h, gzpath.h */
+
+#ifndef gximage_INCLUDED
+# define gximage_INCLUDED
+
+#include "gsiparam.h"
+#include "gxcspace.h"
+#include "strimpl.h" /* for siscale.h */
+#include "siscale.h"
+#include "gxdda.h"
+#include "gxiparam.h"
+#include "gxsample.h"
+
+/* Define the abstract type for the image enumerator state. */
+typedef struct gx_image_enum_s gx_image_enum;
+
+/*
+ * 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 {
+
+ sample_lookup_t 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
+
+/*
+ * Declare the variable that holds the 12-bit unpacking procedure
+ * if 12-bit samples are supported.
+ */
+extern sample_unpack_proc((*sample_unpack_12_proc));
+
+/*
+ * Define the interface for routines used to render a (source) scan line.
+ * If the buffer is the original client's input data, it may be unaligned;
+ * otherwise, it will always be aligned.
+ *
+ * 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 indicate that there is no
+ * more input data; this is necessary because the last scan lines of the
+ * source data may not produce any output.
+ */
+#define irender_proc(proc)\
+ int proc(P6(gx_image_enum *penum, const byte *buffer, int data_x,\
+ uint w, int h, gx_device *dev))
+typedef irender_proc((*irender_proc_t));
+
+/*
+ * Define 'strategy' procedures for selecting imaging methods. Strategies
+ * are called in the order in which they are declared below, 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.
+ *
+ * Note that strategies are defined by procedure members of a structure, so
+ * that they may be omitted from configurations where they are not desired.
+ */
+#define image_strategy_proc(proc)\
+ irender_proc_t proc(P1(gx_image_enum *penum))
+typedef image_strategy_proc((*image_strategy_proc_t));
+typedef struct gx_image_strategies_s {
+ image_strategy_proc_t interpolate;
+ image_strategy_proc_t simple;
+ image_strategy_proc_t fracs;
+ image_strategy_proc_t mono;
+ image_strategy_proc_t color;
+} gx_image_strategies_t;
+extern gx_image_strategies_t image_strategies;
+
+/* 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
+
+struct gx_image_enum_s {
+ gx_image_enum_common;
+ /* We really want the map structure to be long-aligned, */
+ /* so we choose shorter types for some flags. */
+ /* Following are set at structure initialization */
+ 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 */
+ /* (1, 2, 3, 4, or 5 if alpha is allowed) */
+ gs_image_alpha_t alpha; /* Alpha from image structure */
+ /*byte num_planes; *//* spp if colors are separated, */
+ /* 1 otherwise (in common part) */
+ 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 */
+ struct r_ {
+ int x, y, w, h; /* subrectangle being rendered */
+ } rect;
+ gs_fixed_point x_extent, y_extent; /* extent of one row of rect */
+ sample_unpack_proc((*unpack));
+ irender_proc((*render));
+ const gs_imager_state *pis;
+ const gs_color_space *pcs; /* color space of image */
+ gs_memory_t *memory;
+ 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 */
+ 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 must use slower loop */
+ /* (if needed) */
+ 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 */
+ fixed dxx, dxy; /* fixed versions of matrix */
+ /* components (as needed) */
+ 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 y; /* next source y */
+ gs_fixed_point cur, prev; /* device x, y of current & */
+ /* previous row */
+ struct dd_ {
+ gx_dda_fixed_point row; /* DDA for row origin, has been */
+ /* advanced when render proc called */
+ gx_dda_fixed_point pixel0; /* DDA for first pixel of row */
+ } dda;
+ int line_xy; /* x or y value at start of buffered line */
+ int xi_next; /* expected xci of next row */
+ /* (landscape only) */
+ gs_int_point xyi; /* integer origin of row */
+ /* (Interpolate only) */
+ 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[5]; /* 4 colors + alpha */
+ /* 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,pis) m(1,pcs) m(2,dev) m(3,buffer) m(4,line)\
+ m(5,clip_dev) m(6,rop_dev) m(7,scaler)
+#define gx_image_enum_num_ptrs 8
+#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. */
+/* We can special-case this for speed later if we care. */
+#define dev_color_eq(devc1, devc2)\
+ gx_device_color_equal(&(devc1), &(devc2))
+
+#endif /* gximage_INCLUDED */
diff --git a/pstoraster/gximage3.c b/pstoraster/gximage3.c
new file mode 100644
index 000000000..38e348177
--- /dev/null
+++ b/pstoraster/gximage3.c
@@ -0,0 +1,429 @@
+/* Copyright (C) 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* ImageType 3 image implementation */
+#include "math_.h" /* for ceil, floor */
+#include "memory_.h"
+#include "gx.h"
+#include "gserrors.h"
+#include "gsbitops.h"
+#include "gscspace.h"
+#include "gsiparm3.h"
+#include "gsstruct.h"
+#include "gxdevice.h"
+#include "gxdevmem.h"
+#include "gxclipm.h"
+#include "gxiparam.h"
+#include "gxistate.h"
+
+/* Forward references */
+private dev_proc_begin_typed_image(gx_begin_image3);
+private image_enum_proc_plane_data(gx_image3_plane_data);
+private image_enum_proc_end_image(gx_image3_end_image);
+
+/* GC descriptor */
+extern_st(st_gs_pixel_image);
+public_st_gs_image3();
+
+/* Define the image type for ImageType 3 images. */
+private const gx_image_type_t image3_type = {
+ gx_begin_image3, gx_data_image_source_size, 3
+};
+private const gx_image_enum_procs_t image3_enum_procs = {
+ gx_image3_plane_data, gx_image3_end_image
+};
+
+/* Initialize an ImageType 3 image. */
+void
+gs_image3_t_init(gs_image3_t * pim, const gs_color_space * color_space,
+ gs_image3_interleave_type_t interleave_type)
+{
+ gs_pixel_image_t_init((gs_pixel_image_t *) pim, color_space);
+ pim->type = &image3_type;
+ pim->InterleaveType = interleave_type;
+ gs_data_image_t_init(&pim->MaskDict, -1);
+}
+
+/*
+ * We implement ImageType 3 images by interposing a mask clipper in
+ * front of an ordinary ImageType 1 image. Note that we build up the
+ * mask row-by-row as we are processing the image.
+ */
+typedef struct gx_image3_enum_s {
+ gx_image_enum_common;
+ gx_device_memory *mdev;
+ gx_device_mask_clip *pcdev;
+ gx_image_enum_common_t *pixel_info;
+ gx_image_enum_common_t *mask_info;
+ gs_image3_interleave_type_t InterleaveType;
+ int num_components; /* (not counting mask) */
+ int bpc; /* BitsPerComponent */
+ gs_memory_t *memory;
+ int mask_width;
+ int pixel_width;
+ byte *pixel_data; /* (if chunky) */
+ byte *mask_data; /* (if chunky) */
+ int y; /* counts up to max(p'height, m'height) */
+ int pixel_height;
+ int mask_height;
+} gx_image3_enum_t;
+
+gs_private_st_ptrs6(st_image3_enum, gx_image3_enum_t, "gx_image3_enum_t",
+ image3_enum_enum_ptrs, image3_enum_reloc_ptrs,
+ mdev, pcdev, pixel_info, mask_info, pixel_data, mask_data);
+
+/* Begin an ImageType 3 image. */
+private int
+gx_begin_image3(gx_device * dev,
+ const gs_imager_state * pis, const gs_matrix * pmat,
+ const gs_image_common_t * pic, const gs_int_rect * prect,
+ const gx_drawing_color * pdcolor, const gx_clip_path * pcpath,
+ gs_memory_t * mem, gx_image_enum_common_t ** pinfo)
+{
+ const gs_image3_t *pim = (const gs_image3_t *)pic;
+ gx_image3_enum_t *penum;
+ gx_device_memory *mdev;
+ gx_device_mask_clip *pcdev;
+ gs_image_t i_pixel, i_mask;
+ gs_matrix mat;
+ gs_rect mrect;
+ gs_int_point origin;
+ int code;
+
+ /* Validate the parameters. */
+ if (pim->Height <= 0 || pim->MaskDict.Height <= 0)
+ return_error(gs_error_rangecheck);
+ switch (pim->InterleaveType) {
+ default:
+ return_error(gs_error_rangecheck);
+ case interleave_chunky:
+ if (pim->MaskDict.Width != pim->Width ||
+ pim->MaskDict.Height != pim->Height ||
+ pim->MaskDict.BitsPerComponent != pim->BitsPerComponent ||
+ pim->format != gs_image_format_chunky
+ )
+ return_error(gs_error_rangecheck);
+ break;
+ case interleave_scan_lines:
+ if (pim->MaskDict.Height % pim->Height != 0 &&
+ pim->Height % pim->MaskDict.Height != 0
+ )
+ return_error(gs_error_rangecheck);
+ /* falls through */
+ case interleave_separate_source:
+ if (pim->MaskDict.BitsPerComponent != 1)
+ return_error(gs_error_rangecheck);
+ }
+ /****** CHECK FOR COMPATIBLE ImageMatrix ******/
+ penum = gs_alloc_struct(mem, gx_image3_enum_t, &st_image3_enum,
+ "gx_begin_image3");
+ if (penum == 0)
+ return_error(gs_error_VMerror);
+ penum->num_components =
+ gs_color_space_num_components(pim->ColorSpace);
+ gx_image_enum_common_init((gx_image_enum_common_t *) penum,
+ pic, &image3_enum_procs, dev,
+ pim->BitsPerComponent,
+ 1 + penum->num_components,
+ pim->format);
+ if (prect)
+ penum->pixel_width = prect->q.x - prect->p.x,
+ penum->y = prect->p.y,
+ penum->pixel_height = prect->q.y - prect->p.y;
+ else
+ penum->pixel_width = pim->Width,
+ penum->y = 0,
+ penum->pixel_height = pim->Height;
+ penum->mask_width = pim->MaskDict.Width;
+ penum->mask_height = pim->MaskDict.Height;
+ penum->mdev = mdev =
+ gs_alloc_struct(mem, gx_device_memory, &st_device_memory,
+ "gx_begin_image3(mdev)");
+ if (mdev == 0)
+ goto out1;
+ penum->pcdev = pcdev =
+ gs_alloc_struct(mem, gx_device_mask_clip, &st_device_mask_clip,
+ "gx_begin_image3(pcdev)");
+ if (pcdev == 0)
+ goto out2;
+ penum->mask_info = 0;
+ penum->pixel_info = 0;
+ penum->mask_data = 0;
+ penum->pixel_data = 0;
+ if (pim->InterleaveType == interleave_chunky) {
+ /* Allocate row buffers for the mask and pixel data. */
+ penum->pixel_data =
+ gs_alloc_bytes(mem,
+ (penum->pixel_width * pim->BitsPerComponent *
+ penum->num_components + 7) >> 3,
+ "gx_begin_image3(pixel_data)");
+ penum->mask_data =
+ gs_alloc_bytes(mem, (penum->mask_width + 7) >> 3,
+ "gx_begin_image3(mask_data)");
+ if (penum->pixel_data == 0 || penum->mask_data == 0)
+ goto out3;
+ }
+ penum->InterleaveType = pim->InterleaveType;
+ penum->bpc = pim->BitsPerComponent;
+ penum->memory = mem;
+ gs_make_mem_mono_device(mdev, mem, NULL);
+ mdev->bitmap_memory = mem;
+ mrect.p.x = mrect.p.y = 0;
+ mrect.q.x = pim->MaskDict.Width;
+ mrect.q.y = pim->MaskDict.Height;
+ if (pmat == 0)
+ pmat = &ctm_only(pis);
+ if ((code = gs_matrix_invert(&pim->MaskDict.ImageMatrix, &mat)) < 0 ||
+ (code = gs_matrix_multiply(&mat, pmat, &mat)) < 0 ||
+ (code = gs_bbox_transform(&mrect, &mat, &mrect)) < 0
+ )
+ return code;
+ origin.x = floor(mrect.p.x);
+ mdev->width = (int)ceil(mrect.q.x) - origin.x;
+ origin.y = floor(mrect.p.y);
+ mdev->height = (int)ceil(mrect.q.y) - origin.y;
+ gx_device_fill_in_procs((gx_device *) mdev);
+ code = (*dev_proc(mdev, open_device)) ((gx_device *) mdev);
+ if (code < 0)
+ goto out3;
+ mdev->is_open = true;
+ {
+ gx_strip_bitmap bits; /* only gx_bitmap */
+
+ bits.data = mdev->base;
+ bits.raster = mdev->raster;
+ bits.size.x = mdev->width;
+ bits.size.y = mdev->height;
+ bits.id = gx_no_bitmap_id;
+ code = gx_mask_clip_initialize(pcdev, &gs_mask_clip_device,
+ (const gx_bitmap *)&bits, dev,
+ origin.x, origin.y);
+ if (code < 0)
+ goto out4;
+ pcdev->tiles = bits;
+ }
+ gs_image_t_init_mask(&i_mask, false);
+ i_mask.adjust = false;
+ {
+ const gx_image_type_t *type1 = i_mask.type;
+
+ *(gs_data_image_t *)&i_mask = pim->MaskDict;
+ i_mask.type = type1;
+ }
+ {
+ gx_drawing_color dcolor;
+ gs_matrix m_mat;
+
+ (*dev_proc(mdev, fill_rectangle))
+ ((gx_device *) mdev, 0, 0, mdev->width, mdev->height,
+ (gx_color_index) 0);
+ color_set_pure(&dcolor, 1);
+ /*
+ * Adjust the translation for rendering the mask to include a
+ * negative translation by origin.{x,y} in device space.
+ */
+ m_mat = *pmat;
+ m_mat.tx -= origin.x;
+ m_mat.ty -= origin.y;
+ /*
+ * Note that pis = NULL here, since we don't want to have to
+ * create another imager state with default log_op, etc.
+ */
+ code = gx_device_begin_typed_image((gx_device *)mdev, NULL, &m_mat,
+ (const gs_image_common_t *)&i_mask,
+ prect, &dcolor, NULL, mem,
+ &penum->mask_info);
+ if (code < 0)
+ goto out5;
+ }
+ gs_image_t_init(&i_pixel, pim->ColorSpace);
+ {
+ const gx_image_type_t *type1 = i_pixel.type;
+
+ *(gs_pixel_image_t *)&i_pixel = *(const gs_pixel_image_t *)pim;
+ i_pixel.type = type1;
+ }
+ code = gx_device_begin_typed_image((gx_device *) pcdev, pis, pmat,
+ (const gs_image_common_t *)&i_pixel,
+ prect, pdcolor, pcpath, mem, &penum->pixel_info);
+ if (code < 0)
+ goto out6;
+ /*
+ * Compute num_planes and plane_depths from the values in the
+ * enumerators for the mask and the image data.
+ */
+ if (pim->InterleaveType == interleave_chunky) {
+ /* Add the mask data to the depth of the image data. */
+ penum->num_planes = 1;
+ penum->plane_depths[0] =
+ penum->pixel_info->plane_depths[0] *
+ (penum->num_components + 1) / penum->num_components;
+ } else {
+ /* Insert the mask data as a separate plane before the image data. */
+ penum->num_planes = penum->pixel_info->num_planes + 1;
+ penum->plane_depths[0] = 1;
+ memcpy(&penum->plane_depths[1], &penum->pixel_info->plane_depths[0],
+ (penum->num_planes - 1) * sizeof(penum->plane_depths[0]));
+ }
+ *pinfo = (gx_image_enum_common_t *) penum;
+ return 0;
+ out6:gx_image_end(penum->mask_info, false);
+ out5:gs_closedevice((gx_device *) pcdev);
+ out4:gs_closedevice((gx_device *) mdev);
+ out3:gs_free_object(mem, pcdev, "gx_begin_image3(pcdev)");
+ out2:gs_free_object(mem, mdev, "gx_begin_image3(mdev)");
+ out1:gs_free_object(mem, penum->mask_data, "gx_begin_image3(mask_data)");
+ gs_free_object(mem, penum->pixel_data, "gx_begin_image3(pixel_data)");
+ gs_free_object(mem, penum, "gx_begin_image3");
+ code = gs_note_error(gs_error_VMerror);
+ out:return code;
+}
+
+/* Process the next piece of an ImageType 3 image. */
+private int
+gx_image3_plane_data(gx_device * dev,
+ gx_image_enum_common_t * info, const gx_image_plane_t * planes, int height)
+{
+ gx_image3_enum_t *penum = (gx_image3_enum_t *) info;
+ int pixel_height = penum->pixel_height;
+ int mask_height = penum->mask_height;
+ int image_height = max(pixel_height, mask_height);
+ int h = min(height, image_height - penum->y);
+ const gx_image_plane_t *pixel_planes;
+ gx_image_plane_t pixel_plane, mask_plane;
+
+ switch (penum->InterleaveType) {
+ case interleave_chunky:
+ if (h <= 0)
+ return 0;
+ if (h > 1) {
+ /* Do the operation one row at a time. */
+ mask_plane = planes[0];
+ do {
+ int code = gx_image3_plane_data(dev, info, &mask_plane, 1);
+
+ if (code < 0)
+ return code;
+ mask_plane.data += mask_plane.raster;
+ } while (--h);
+ return 0;
+ } {
+ /* Pull apart the source data and the mask data. */
+ int bpc = penum->bpc;
+ int num_components = penum->num_components;
+ int width = penum->pixel_width;
+
+ /* We do this in the simplest (not fastest) way for now. */
+ uint bit_x = bpc * (num_components + 1) * planes[0].data_x;
+
+ sample_load_declare_setup(sptr, sbit,
+ planes[0].data + (bit_x >> 3),
+ bit_x & 7, bpc);
+ sample_store_declare_setup(mptr, mbit, mbbyte, penum->mask_data,
+ 0, 1);
+ sample_store_declare_setup(pptr, pbit, pbbyte, penum->pixel_data,
+ 0, bpc);
+ int x;
+
+ mask_plane.data = mptr;
+ mask_plane.data_x = 0;
+ /* raster doesn't matter */
+ pixel_plane.data = pptr;
+ pixel_plane.data_x = 0;
+ /* raster doesn't matter */
+ pixel_planes = &pixel_plane;
+ for (x = 0; x < width; ++x) {
+ uint value;
+ int i;
+
+ sample_load_next12(value, sptr, sbit, bpc);
+ sample_store_next12(value != 0, mptr, mbit, 1, mbbyte);
+ for (i = 0; i < num_components; ++i) {
+ sample_load_next12(value, sptr, sbit, bpc);
+ sample_store_next12(value, pptr, pbit, bpc, pbbyte);
+ }
+ }
+ sample_store_flush(mptr, mbit, 1, mbbyte);
+ sample_store_flush(pptr, pbit, bpc, pbbyte);
+ }
+ break;
+ case interleave_scan_lines:
+ case interleave_separate_source:
+ mask_plane = planes[0];
+ pixel_planes = planes + 1;
+ break;
+ default: /* not possible */
+ return_error(gs_error_rangecheck);
+ }
+ /*
+ * Process the mask data first, so it will set up the mask
+ * device for clipping the pixel data.
+ */
+ if (mask_plane.data) {
+ int code = gx_image_plane_data(penum->mask_info, &mask_plane, h);
+
+ if (code < 0)
+ return code;
+ }
+ if (pixel_planes[0].data) {
+ int code;
+
+ /*
+ * If necessary, flush any buffered mask data to the mask clipping
+ * device.
+ */
+ if (penum->mask_info->procs->flush)
+ (*penum->mask_info->procs->flush)(penum->mask_info);
+ code = gx_image_plane_data(penum->pixel_info, pixel_planes, h);
+ if (code < 0)
+ return code;
+ penum->y += h;
+ }
+ return penum->y >= image_height;
+}
+
+/* Clean up after processing an ImageType 3 image. */
+private int
+gx_image3_end_image(gx_device * dev, gx_image_enum_common_t * info,
+ bool draw_last)
+{
+ gx_image3_enum_t *penum = (gx_image3_enum_t *) info;
+ gs_memory_t *mem = penum->memory;
+ gx_device_memory *mdev = penum->mdev;
+ int mcode = gx_image_end(penum->mask_info, draw_last);
+ gx_device_mask_clip *pcdev = penum->pcdev;
+ int pcode = gx_image_end(penum->pixel_info, draw_last);
+
+ gs_closedevice((gx_device *) pcdev);
+ gs_closedevice((gx_device *) mdev);
+ gs_free_object(mem, penum->mask_data,
+ "gx_image3_end_image(mask_data)");
+ gs_free_object(mem, penum->pixel_data,
+ "gx_image3_end_image(pixel_data)");
+ gs_free_object(mem, pcdev, "gx_image3_end_image(pcdev)");
+ gs_free_object(mem, mdev, "gx_image3_end_image(mdev)");
+ gs_free_object(mem, penum, "gx_image3_end_image");
+ return (pcode < 0 ? pcode : mcode);
+}
diff --git a/pstoraster/gximage4.c b/pstoraster/gximage4.c
new file mode 100644
index 000000000..14dee709e
--- /dev/null
+++ b/pstoraster/gximage4.c
@@ -0,0 +1,297 @@
+/* Copyright (C) 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* ImageType 4 image implementation */
+#include "memory_.h"
+#include "gx.h"
+#include "gserrors.h"
+#include "gscspace.h"
+#include "gsiparm3.h"
+#include "gsiparm4.h"
+#include "gxiparam.h"
+
+/* Forward references */
+private dev_proc_begin_typed_image(gx_begin_image4);
+private image_enum_proc_plane_data(gx_image4_plane_data);
+private image_enum_proc_end_image(gx_image4_end_image);
+
+/* Define the image type for ImageType 4 images. */
+private const gx_image_type_t image4_type = {
+ gx_begin_image4, gx_data_image_source_size, 4
+};
+private const gx_image_enum_procs_t image4_enum_procs = {
+ gx_image4_plane_data, gx_image4_end_image
+};
+
+/* Initialize an ImageType 4 image. */
+void
+gs_image4_t_init(gs_image4_t * pim, const gs_color_space * color_space)
+{
+ gs_pixel_image_t_init((gs_pixel_image_t *) pim, color_space);
+ pim->type = &image4_type;
+ pim->MaskColor_is_range = false;
+}
+
+/*
+ * We implement ImageType 4 using ImageType 3 (or, if the image is known
+ * to be completely opaque, ImageType 1).
+ */
+typedef struct gx_image4_enum_s {
+ gx_image_enum_common;
+ int num_components;
+ int bpc; /* BitsPerComponent */
+ uint values[gs_image_max_components * 2];
+ gs_memory_t *memory;
+ gx_image_enum_common_t *info; /* info for image3 or image1 */
+ byte *mask; /* one scan line of mask data, 0 if image1 */
+ uint mask_size;
+ int width;
+ int y;
+ int height;
+} gx_image4_enum_t;
+
+gs_private_st_ptrs2(st_image4_enum, gx_image4_enum_t, "gx_image4_enum_t",
+ image4_enum_enum_ptrs, image4_enum_reloc_ptrs, info, mask);
+
+/* Begin an ImageType 4 image. */
+private int
+gx_begin_image4(gx_device * dev,
+ const gs_imager_state * pis, const gs_matrix * pmat,
+ const gs_image_common_t * pic, const gs_int_rect * prect,
+ const gx_drawing_color * pdcolor, const gx_clip_path * pcpath,
+ gs_memory_t * mem, gx_image_enum_common_t ** pinfo)
+{
+ const gs_image4_t *pim = (const gs_image4_t *)pic;
+ int num_components = gs_color_space_num_components(pim->ColorSpace);
+ bool opaque = false;
+ gx_image4_enum_t *penum;
+ uint mask_size = (pim->Width + 7) >> 3;
+ byte *mask = 0;
+ int code;
+
+ penum = gs_alloc_struct(mem, gx_image4_enum_t, &st_image4_enum,
+ "gx_begin_image4");
+ if (penum == 0)
+ return_error(gs_error_VMerror);
+ gx_image_enum_common_init((gx_image_enum_common_t *) penum,
+ pic, &image4_enum_procs, dev,
+ pim->BitsPerComponent,
+ num_components, pim->format);
+ penum->memory = mem;
+ {
+ uint max_value = (1 << pim->BitsPerComponent) - 1;
+ int i;
+
+ for (i = 0; i < num_components * 2; i += 2) {
+ uint c0, c1;
+
+ if (pim->MaskColor_is_range)
+ c0 = pim->MaskColor[i], c1 = pim->MaskColor[i + 1];
+ else
+ c0 = c1 = pim->MaskColor[i >> 1];
+
+ if (c1 > max_value)
+ c1 = max_value;
+ if (c0 > c1) {
+ opaque = true;
+ break;
+ }
+ penum->values[i] = c0;
+ penum->values[i + 1] = c1;
+ }
+ }
+ if (opaque) {
+ /*
+ * This image doesn't need masking at all, since at least one of
+ * the transparency keys can never be matched. Process it as an
+ * ImageType 1 image.
+ */
+ const gx_image_type_t *type;
+ gs_image1_t image1;
+
+ gs_image_t_init(&image1, pim->ColorSpace);
+ type = image1.type;
+ *(gs_pixel_image_t *)&image1 =
+ *(const gs_pixel_image_t *)pim;
+ image1.type = type;
+ penum->mask = 0; /* indicates opaque image */
+ code = gx_device_begin_typed_image(dev, pis, pmat,
+ (gs_image_common_t *)&image1,
+ prect, pdcolor, pcpath, mem, &penum->info);
+ } else if ((mask = gs_alloc_bytes(mem, mask_size,
+ "gx_begin_image4(mask)")) == 0
+ ) {
+ code = gs_note_error(gs_error_VMerror);
+ } else {
+ gs_image3_t image3;
+
+ penum->num_components = num_components;
+ gs_image3_t_init(&image3, pim->ColorSpace, interleave_scan_lines);
+ {
+ const gx_image_type_t *type = image3.type;
+
+ *(gs_pixel_image_t *)&image3 =
+ *(const gs_pixel_image_t *)pim;
+ image3.type = type;
+ }
+ *(gs_data_image_t *)&image3.MaskDict =
+ *(const gs_data_image_t *)pim;
+ image3.MaskDict.BitsPerComponent = 1;
+ /*
+ * The interpretation of Decode is backwards from the sensible
+ * one, but it's an Adobe convention that is now too hard to
+ * change in the code.
+ */
+ image3.MaskDict.Decode[0] = 1;
+ image3.MaskDict.Decode[1] = 0;
+ image3.MaskDict.Interpolate = false;
+ penum->bpc = pim->BitsPerComponent;
+ penum->mask = mask;
+ penum->mask_size = mask_size;
+ if (prect)
+ penum->width = prect->q.x - prect->p.x,
+ penum->y = prect->p.y, penum->height = prect->q.y - prect->p.y;
+ else
+ penum->width = pim->Width,
+ penum->y = 0, penum->height = pim->Height;
+ code = gx_device_begin_typed_image(dev, pis, pmat,
+ (const gs_image_common_t *)&image3,
+ prect, pdcolor, pcpath, mem, &penum->info);
+ }
+ if (code < 0) {
+ gs_free_object(mem, mask, "gx_begin_image4(mask)");
+ gs_free_object(mem, penum, "gx_begin_image4");
+ } else
+ *pinfo = (gx_image_enum_common_t *) penum;
+ return code;
+}
+
+/* Process the next piece of an ImageType 4 image. */
+/* We disregard the depth in the image planes: BitsPerComponent prevails. */
+private int
+gx_image4_plane_data(gx_device * dev,
+ gx_image_enum_common_t * info, const gx_image_plane_t * planes, int height)
+{
+ gx_image4_enum_t *penum = (gx_image4_enum_t *) info;
+ int num_planes = penum->num_planes;
+ int bpc = penum->bpc;
+ int spp = (num_planes > 1 ? 1 : penum->num_components);
+ byte *mask = penum->mask;
+ uint mask_size = (penum->width + 7) >> 3;
+ gx_image_plane_t sources[gs_image_max_components];
+ int h = min(height, penum->height - penum->y);
+
+ if (penum->mask == 0) /* opaque image */
+ return gx_image_plane_data(penum->info, planes, height);
+ if (mask_size > penum->mask_size) {
+ mask = gs_resize_object(penum->memory, mask, mask_size,
+ "gx_image4_data(resize mask)");
+ if (mask == 0)
+ return_error(gs_error_VMerror);
+ penum->mask = mask;
+ penum->mask_size = mask_size;
+ }
+ sources[0].data = mask;
+ sources[0].data_x = 0;
+ sources[0].raster = mask_size;
+ memcpy(sources + 1, planes, num_planes * sizeof(planes[0]));
+ for (; h > 0; ++(penum->y), --h) {
+ int pi;
+ int code;
+
+ memset(mask, 0, (penum->width + 7) >> 3);
+ for (pi = 0; pi < num_planes; ++pi) {
+ byte *mptr = mask;
+ byte mbit = 0x80;
+ uint sx_bit = sources[pi + 1].data_x * bpc;
+ const byte *sptr = sources[pi + 1].data + (sx_bit >> 3);
+ uint sx_shift = sx_bit & 7;
+ int ix;
+
+#define advance_sx()\
+ BEGIN\
+ if ( (sx_shift += bpc) >= 8 ) {\
+ sptr += sx_shift >> 3;\
+ sx_shift &= 7;\
+ }\
+ END
+
+ for (ix = 0; ix < penum->width; ++ix) {
+ int ci;
+
+ for (ci = 0; ci < spp; ++ci) {
+ /*
+ * The following odd-looking computation is, in fact,
+ * correct both for chunky (pi = 0) and planar (ci = 0)
+ * data formats.
+ */
+ int vi = (ci + pi) * 2;
+ uint sample;
+
+ if (bpc <= 8) {
+ sample = (*sptr >> (8 - sx_shift - bpc)) &
+ ((1 << bpc) - 1);
+ } else {
+ /* bpc == 12 */
+ if (sx_shift /* == 4 */ )
+ sample = ((*sptr & 0xf) << 8) + sptr[1];
+ else
+ sample = (*sptr << 8) + (sptr[1] >> 4);
+ }
+ if (sample < penum->values[vi] ||
+ sample > penum->values[vi + 1]
+ )
+ *mptr |= mbit;
+ advance_sx();
+ }
+ if ((mbit >>= 1) == 0)
+ mbit = 0x80, ++mptr;
+ }
+ }
+ code = gx_image_plane_data(penum->info, sources, 1);
+ if (code < 0)
+ return code;
+ for (pi = 1; pi <= num_planes; ++pi)
+ sources[pi].data += sources[pi].raster;
+ }
+#undef advance_sx
+ return penum->y >= penum->height;
+}
+
+/* Clean up after processing an ImageType 4 image. */
+private int
+gx_image4_end_image(gx_device * dev, gx_image_enum_common_t * info,
+ bool draw_last)
+{
+ gx_image4_enum_t *penum = (gx_image4_enum_t *) info;
+ gs_memory_t *mem = penum->memory;
+
+ /* Finish processing the ImageType 3 (or 1) image. */
+ int code = gx_image_end(penum->info, draw_last);
+
+ gs_free_object(mem, penum->mask, "gx_image4_end_image(mask)");
+ gs_free_object(mem, penum, "gx_image4_end_image");
+ return code;
+}
diff --git a/pstoraster/gximono.c b/pstoraster/gximono.c
new file mode 100644
index 000000000..13eb57e50
--- /dev/null
+++ b/pstoraster/gximono.c
@@ -0,0 +1,586 @@
+/* Copyright (C) 1989, 1995, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* General mono-component 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 "gxdevice.h"
+#include "gxcmap.h"
+#include "gxdcolor.h"
+#include "gxistate.h"
+#include "gzpath.h"
+#include "gxdevmem.h"
+#include "gdevmem.h" /* for mem_mono_device */
+#include "gxcpath.h"
+#include "gximage.h"
+#include "gzht.h"
+
+/* ------ Strategy procedure ------ */
+
+/* We can bypass X clipping for portrait mono-component images. */
+private irender_proc(image_render_mono);
+private irender_proc_t
+image_strategy_mono(gx_image_enum * penum)
+{
+ /*
+ * Use the slow loop for imagemask with a halftone,
+ * or for a non-default logical operation.
+ */
+ penum->slow_loop =
+ (penum->masked && !color_is_pure(&penum->icolor1)) ||
+ penum->use_rop;
+ 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");
+ /* Precompute values needed for rasterizing. */
+ penum->dxx =
+ float2fixed(penum->matrix.xx + fixed2float(fixed_epsilon) / 2);
+ return image_render_mono;
+ }
+ return 0;
+}
+
+void
+gs_gximono_init(gs_memory_t * mem)
+{
+ image_strategies.mono = image_strategy_mono;
+}
+
+/* ------ Rendering procedure ------ */
+
+/* Provide a fake map_gray procedure for the DevicePixel color space. */
+private void
+no_map_gray(frac pixel, gx_device_color * pdc, const gs_imager_state * pis,
+ gx_device * dev, gs_color_select_t select)
+{
+ color_set_pure(pdc, frac2byte(pixel));
+}
+
+/*
+ * Rendering procedure for general mono-component images, dealing with
+ * multiple bit-per-sample images, general transformations, and arbitrary
+ * single-component color spaces (DeviceGray, DevicePixel, CIEBasedA,
+ * Separation, Indexed). This procedure handles a single scan line.
+ */
+private int
+image_render_mono(gx_image_enum * penum, const byte * buffer, int data_x,
+ uint w, int h, gx_device * dev)
+{
+ const gs_imager_state *pis = penum->pis;
+ gs_logical_operation_t lop = penum->log_op;
+ const bool masked = penum->masked;
+ const gs_color_space *pcs; /* only set for non-masks */
+ cmap_proc_gray((*map_gray)); /* ditto */
+ cs_proc_remap_color((*remap_color)); /* ditto */
+ gs_client_color cc;
+ gx_device_color *pdevc = &penum->icolor1; /* color for masking */
+ /*
+ * Make sure the cache setup matches the graphics state. Also determine
+ * whether all tiles fit in the cache. We may bypass the latter check
+ * for masked images with a pure color.
+ */
+ bool tiles_fit = (pis ? gx_check_tile_cache(pis) : false);
+/*
+ * Free variables of IMAGE_SET_GRAY:
+ * Read: penum, pis, dev, tiles_fit
+ * Set: pdevc, code, cc
+ */
+#define IMAGE_SET_GRAY(sample_value)\
+ BEGIN\
+ pdevc = &penum->clues[sample_value].dev_color;\
+ if (!color_is_set(pdevc)) {\
+ if (penum->device_color)\
+ (*map_gray)(byte2frac(sample_value), pdevc, pis, dev, gs_color_select_source);\
+ else {\
+ decode_sample(sample_value, cc, 0);\
+ (*remap_color)(&cc, pcs, pdevc, pis, dev, gs_color_select_source);\
+ }\
+ } else if (!color_is_pure(pdevc)) {\
+ if (!tiles_fit) {\
+ code = gx_color_load_select(pdevc, pis, dev, gs_color_select_source);\
+ if ( code < 0 ) return code;\
+ }\
+ }\
+ END
+ gx_dda_fixed_point next; /* (y not used in fast loop) */
+ gx_dda_step_fixed dxx2, dxx3, dxx4; /* (not used in all loops) */
+ register const byte *psrc = buffer + data_x;
+ const byte *endp = psrc + w;
+ const byte *stop = endp;
+ fixed xrun; /* x at start of run */
+ register byte run; /* run value */
+ int htrun = (masked ? 255 : -2); /* halftone run value */
+ int code = 0;
+
+ if (h == 0)
+ return 0;
+ next = penum->dda.pixel0;
+ xrun = dda_current(next.x);
+ if (!masked) {
+ pcs = penum->pcs; /* (may not be set for masks) */
+ if (penum->device_color)
+ map_gray =
+ (gs_color_space_get_index(pcs) ==
+ gs_color_space_index_DeviceGray ?
+ gx_device_cmap_procs(dev)->map_gray :
+ no_map_gray /*DevicePixel */ );
+ else
+ remap_color = pcs->type->remap_color;
+ }
+ run = *psrc;
+ /* Find the last transition in the input. */
+ {
+ byte last = stop[-1];
+
+ while (stop > psrc && stop[-1] == last)
+ --stop;
+ }
+ if (penum->slow_loop || penum->posture != image_portrait) {
+
+ /**************************************************************
+ * Slow case (skewed, rotated, or imagemask with a halftone). *
+ **************************************************************/
+
+ fixed yrun;
+ const fixed pdyx = dda_current(penum->dda.row.x) - penum->cur.x;
+ const fixed pdyy = dda_current(penum->dda.row.y) - penum->cur.y;
+ dev_proc_fill_parallelogram((*fill_pgram)) =
+ dev_proc(dev, fill_parallelogram);
+
+#define xl dda_current(next.x)
+#define ytf dda_current(next.y)
+ yrun = ytf;
+ if (masked) {
+
+ /**********************
+ * Slow case, masked. *
+ **********************/
+
+ pdevc = &penum->icolor1;
+ code = gx_color_load(pdevc, pis, dev);
+ if (code < 0)
+ return code;
+ if (stop <= psrc)
+ goto last;
+ if (penum->posture == image_portrait) {
+
+ /********************************
+ * Slow case, masked, portrait. *
+ ********************************/
+
+ /*
+ * We don't have to worry about the Y DDA, and the fill
+ * regions are rectangles. Calculate multiples of the DDA
+ * step.
+ */
+ fixed ax =
+ (penum->matrix.xx < 0 ? -penum->adjust : penum->adjust);
+ fixed ay =
+ (pdyy < 0 ? -penum->adjust : penum->adjust);
+ fixed dyy = pdyy + (ay << 1);
+
+ yrun -= ay;
+ dda_translate(next.x, -ax);
+ ax <<= 1;
+ 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 a run of zeros. */
+ while (!psrc[0])
+ if (!psrc[1]) {
+ if (!psrc[2]) {
+ if (!psrc[3]) {
+ psrc += 4;
+ dda_state_next(next.x.state, dxx4);
+ continue;
+ }
+ psrc += 3;
+ dda_state_next(next.x.state, dxx3);
+ break;
+ }
+ psrc += 2;
+ dda_state_next(next.x.state, dxx2);
+ break;
+ } else {
+ ++psrc;
+ dda_next(next.x);
+ break;
+ }
+ xrun = xl;
+ if (psrc >= stop)
+ break;
+ for (; *psrc; ++psrc)
+ dda_next(next.x);
+ code = (*fill_pgram)(dev, xrun, yrun,
+ xl - xrun + ax, fixed_0, fixed_0, dyy,
+ pdevc, lop);
+ if (code < 0)
+ return code;
+ if (psrc >= stop)
+ break;
+ }
+
+ } else if (penum->posture == image_landscape) {
+
+ /*********************************
+ * Slow case, masked, landscape. *
+ *********************************/
+
+ /*
+ * We don't have to worry about the X DDA. However, we do
+ * have to take adjustment into account. We don't bother to
+ * optimize this as heavily as the portrait case.
+ */
+ fixed ax =
+ (pdyx < 0 ? -penum->adjust : penum->adjust);
+ fixed dyx = pdyx + (ax << 1);
+ fixed ay =
+ (penum->matrix.xy < 0 ? -penum->adjust : penum->adjust);
+
+ xrun -= ax;
+ dda_translate(next.y, -ay);
+ ay <<= 1;
+ for (;;) {
+ for (; !*psrc; ++psrc)
+ dda_next(next.y);
+ yrun = ytf;
+ if (psrc >= stop)
+ break;
+ for (; *psrc; ++psrc)
+ dda_next(next.y);
+ code = (*fill_pgram)(dev, xrun, yrun, fixed_0,
+ ytf - yrun + ay, dyx, fixed_0,
+ pdevc, lop);
+ if (code < 0)
+ return code;
+ if (psrc >= stop)
+ break;
+ }
+
+ } else {
+
+ /**************************************
+ * Slow case, masked, not orthogonal. *
+ **************************************/
+
+ for (;;) {
+ for (; !*psrc; ++psrc) {
+ dda_next(next.x);
+ dda_next(next.y);
+ }
+ yrun = ytf;
+ xrun = xl;
+ if (psrc >= stop)
+ break;
+ 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 >= stop)
+ break;
+ }
+
+ }
+
+ } else if (penum->posture == image_portrait ||
+ penum->posture == image_landscape
+ ) {
+
+ /**************************************
+ * Slow case, not masked, orthogonal. *
+ **************************************/
+
+ /* In this case, we can fill runs quickly. */
+ /****** DOESN'T DO ADJUSTMENT ******/
+ if (stop <= psrc)
+ goto last;
+ for (;;) {
+ if (*psrc != run) {
+ 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;
+ yrun = ytf;
+ xrun = xl;
+ if (psrc >= stop)
+ break;
+ run = *psrc;
+ }
+ psrc++;
+ dda_next(next.x);
+ dda_next(next.y);
+ }
+ } else {
+
+ /******************************************
+ * Slow case, not masked, not orthogonal. *
+ ******************************************/
+
+ /*
+ * Since we have to check for the end after every pixel
+ * anyway, we may as well avoid the last-run code.
+ */
+ stop = endp;
+ for (;;) {
+ /* We can't skip large constant regions quickly, */
+ /* because this leads to rounding errors. */
+ /* Just fill the region between xrun and xl. */
+ 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;
+ yrun = ytf;
+ xrun = xl;
+ if (psrc > stop) {
+ --psrc;
+ break;
+ }
+ run = psrc[-1];
+ dda_next(next.x);
+ dda_next(next.y); /* harmless if no skew */
+ }
+
+ }
+ /* Fill the last run. */
+ last:if (stop < endp && (*stop || !masked)) {
+ if (!masked) {
+ IMAGE_SET_GRAY(*stop);
+ }
+ dda_advance(next.x, endp - stop);
+ dda_advance(next.y, endp - stop);
+ code = (*fill_pgram) (dev, xrun, yrun, xl - xrun,
+ ytf - yrun, pdyx, pdyy, pdevc, lop);
+ }
+#undef xl
+#undef ytf
+
+ } else {
+
+ /**********************************************************
+ * Fast case: no skew, and not imagemask with a halftone. *
+ **********************************************************/
+
+ const fixed adjust = penum->adjust;
+ const fixed dxx = penum->dxx;
+ 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(pis,
+ fixed2int_ceiling(any_abs(dxx) + (xa << 1)),
+ yt, iht, gs_color_select_source, &phase_x) :
+ -1);
+ int xmin = fixed2int_pixround(penum->clip_outer.p.x);
+ int xmax = fixed2int_pixround(penum->clip_outer.q.x);
+
+#define xl dda_current(next.x)
+ /* Fold the adjustment into xrun and xl, */
+ /* including the +0.5-epsilon for rounding. */
+ xrun = xrun - xa + (fixed_half - fixed_epsilon);
+ dda_translate(next.x, xa + (fixed_half - fixed_epsilon));
+ 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);
+ if (stop > psrc)
+ 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:xrun = xl - xa; /* original xa << 1 */
+ if (psrc > stop) {
+ --psrc;
+ break;
+ }
+ run = psrc[-1];
+ }
+ dda_next(next.x);
+ }
+ /* Fill the last run. */
+ if (*stop != 0 || !masked) {
+ int xi = fixed2int_var(xrun);
+ int wi, xei;
+
+ dda_advance(next.x, endp - stop);
+ wi = fixed2int_var(xl) - xi;
+ if (wi <= 0) {
+ if (wi == 0)
+ goto lmt;
+ 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 lmt;
+ }
+ IMAGE_SET_GRAY(*stop);
+ code = gx_fill_rectangle_device_rop(xi, yt, wi, iht,
+ pdevc, dev, lop);
+ lmt:;
+ }
+
+ }
+#undef xl
+ return (code < 0 ? code : 1);
+}
diff --git a/pstoraster/gxiodev.h b/pstoraster/gxiodev.h
new file mode 100644
index 000000000..0176b07e6
--- /dev/null
+++ b/pstoraster/gxiodev.h
@@ -0,0 +1,190 @@
+/* Copyright (C) 1993, 1994, 1996, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Requires gsmemory.h */
+
+#ifndef gxiodev_INCLUDED
+# define gxiodev_INCLUDED
+
+#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. */
+struct gx_io_device_s {
+ const char *dname; /* the IODevice name */
+ const char *dtype; /* the type returned by c'devparams */
+ gx_io_device_procs procs;
+ void *state; /* (if the IODevice has state) */
+};
+
+#define private_st_io_device() /* in gsiodev.c */\
+ gs_private_st_ptrs1(st_io_device, gx_io_device, "gx_io_device",\
+ io_device_enum_ptrs, io_device_reloc_ptrs, state)
+
+#endif /* gxiodev_INCLUDED */
diff --git a/pstoraster/gxiparam.h b/pstoraster/gxiparam.h
new file mode 100644
index 000000000..7ef10c1ef
--- /dev/null
+++ b/pstoraster/gxiparam.h
@@ -0,0 +1,172 @@
+/* Copyright (C) 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Definitions for implementors of image types */
+
+#ifndef gxiparam_INCLUDED
+# define gxiparam_INCLUDED
+
+#include "gxdevcli.h"
+
+#ifndef gx_image_enum_common_t_DEFINED
+# define gx_image_enum_common_t_DEFINED
+typedef struct gx_image_enum_common_s gx_image_enum_common_t;
+
+#endif
+
+/*
+ * Define the procedures associated with an image enumerator.
+ *
+ * Note that image_plane_data and end_image used to be device procedures;
+ * they still take the device argument first for compatibility. However, in
+ * order to make forwarding begin_image work, the intermediary routines
+ * gx_image_[plane_]data and gx_image_end substitute the device from the
+ * enumerator for the explicit device argument, which is ignored.
+ * Eventually we should fix this by removing the device argument from
+ * gx_device..., just as we have done for text enumeration; but this would
+ * have caused major difficulties with 5.1x retrofitting of this code, and
+ * it's too much work to fix right now. ****** FIX THIS SOMEDAY ******
+ */
+typedef struct gx_image_enum_procs_s {
+
+ /*
+ * Pass the next batch of data for processing.
+ * image_enum_proc_plane_data is defined in gxdevcli.h.
+ */
+
+ image_enum_proc_plane_data((*plane_data));
+
+ /*
+ * End processing an image. We keep this procedure as the last one that
+ * requires initialization, so that we can detect obsolete static
+ * initializers. dev_proc_end_image is defined in gxdevcli.h.
+ */
+#define image_enum_proc_end_image(proc)\
+ dev_proc_end_image(proc)
+
+ image_enum_proc_end_image((*end_image));
+
+ /*
+ * Flush any intermediate buffers to the target device.
+ * We need this for situations where two images interact
+ * (currently, only the mask and the data of ImageType 3).
+ * This procedure is optional (may be 0).
+ */
+#define image_enum_proc_flush(proc)\
+ int proc(P1(gx_image_enum_common_t *info))
+
+ image_enum_proc_flush((*flush));
+
+} gx_image_enum_procs_t;
+
+/*
+ * Define the common prefix of the image enumerator structure. All
+ * implementations of begin[_typed]_image must initialize all of the members
+ * of this structure, by calling gx_image_enum_common_init and then filling
+ * in whatever else they need to.
+ *
+ * Note that the structure includes a unique ID, so that the banding
+ * machinery could in principle keep track of multiple enumerations that may
+ * be in progress simultaneously.
+ */
+#define gx_image_enum_common\
+ const gx_image_type_t *image_type;\
+ const gx_image_enum_procs_t *procs;\
+ gx_device *dev;\
+ gs_id id;\
+ int num_planes;\
+ int plane_depths[gs_image_max_components] /* [num_planes] */
+struct gx_image_enum_common_s {
+ gx_image_enum_common;
+};
+
+/*extern_st(st_gx_image_enum_common); */
+#define public_st_gx_image_enum_common() /* in gdevddrw.c */\
+ gs_public_st_composite(st_gx_image_enum_common, gx_image_enum_common_t,\
+ "gx_image_enum_common_t",\
+ image_enum_common_enum_ptrs, image_enum_common_reloc_ptrs)
+
+/*
+ * Initialize the common part of an image enumerator.
+ */
+int gx_image_enum_common_init(P7(gx_image_enum_common_t * piec,
+ const gs_image_common_t * pic,
+ const gx_image_enum_procs_t * piep,
+ gx_device * dev,
+ int bits_per_component, int num_components,
+ gs_image_format_t format));
+
+/*
+ * Define the structure for defining image types. An image type includes
+ * not only the ImageType index, but also the default implementation of
+ * begin_typed_image.
+ */
+#ifndef gx_image_type_DEFINED
+# define gx_image_type_DEFINED
+typedef struct gx_image_type_s gx_image_type_t;
+#endif
+struct gx_image_type_s {
+
+ dev_proc_begin_typed_image((*begin_typed_image));
+
+ /*
+ * Compute the width and height of the source data. For images with
+ * explicit data, this information is in the gs_data_image_t
+ * structure, but ImageType 2 images must compute it.
+ */
+#define image_proc_source_size(proc)\
+ int proc(P3(const gs_imager_state *pis, const gs_image_common_t *pim,\
+ gs_int_point *psize))
+
+ image_proc_source_size((*source_size));
+
+ /*
+ * We put index last so that if we add more procedures and some
+ * implementor fails to initialize them, we'll get a type error.
+ */
+ int index; /* PostScript ImageType */
+};
+
+/*
+ * Define the procedure for getting the source size of an image with
+ * explicit data.
+ */
+image_proc_source_size(gx_data_image_source_size);
+/*
+ * Define image_plane_data and end_image procedures for image types that
+ * don't have any source data (ImageType 2 and composite images).
+ */
+image_enum_proc_plane_data(gx_no_plane_data);
+image_enum_proc_end_image(gx_ignore_end_image);
+/*
+ * Define the procedures and type data for ImageType 1 images,
+ * which are always included.
+ */
+dev_proc_begin_typed_image(gx_begin_image1);
+image_enum_proc_plane_data(gx_image1_plane_data);
+image_enum_proc_end_image(gx_image1_end_image);
+image_enum_proc_flush(gx_image1_flush);
+
+#endif /* gxiparam_INCLUDED */
diff --git a/pstoraster/gxiscale.c b/pstoraster/gxiscale.c
new file mode 100644
index 000000000..3264b79c6
--- /dev/null
+++ b/pstoraster/gxiscale.c
@@ -0,0 +1,256 @@
+/* Copyright (C) 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Interpolated image procedures */
+#include "gx.h"
+#include "math_.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 "gxdevice.h"
+#include "gxcmap.h"
+#include "gxdcolor.h"
+#include "gxistate.h"
+#include "gzpath.h"
+#include "gxdevmem.h"
+#include "gxcpath.h"
+#include "gximage.h"
+
+/* ------ Strategy procedure ------ */
+
+/* If we're interpolating, use special logic. */
+private irender_proc(image_render_interpolate);
+private irender_proc_t
+image_strategy_interpolate(gx_image_enum * penum)
+{
+ const gs_imager_state *pis = penum->pis;
+ gs_memory_t *mem = penum->memory;
+ stream_IScale_state iss;
+ stream_IScale_state *pss;
+ byte *line;
+ const gs_color_space *pcs = penum->pcs;
+ const gs_color_space *pccs;
+ gs_point dst_xy;
+
+ if (!penum->interpolate)
+ return 0;
+ if (penum->posture != image_portrait || penum->masked ||
+ penum->alpha
+ ) {
+ /* 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->rect.w, (float)penum->rect.h,
+ &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->rect.w;
+ iss.HeightIn = penum->rect.h;
+ pccs = cs_concrete_space(pcs, pis);
+ iss.Colors = cs_num_components(pccs);
+ /* 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;
+ {
+ gx_dda_fixed x0;
+
+ x0 = penum->dda.pixel0.x;
+ if (penum->matrix.xx < 0)
+ dda_advance(x0, penum->rect.w);
+ penum->xyi.x = fixed2int_pixround(dda_current(x0));
+ }
+ penum->xyi.y = fixed2int_pixround(dda_current(penum->dda.pixel0.y));
+ if_debug0('b', "[b]render=interpolate\n");
+ return image_render_interpolate;
+}
+
+void
+gs_gxiscale_init(gs_memory_t * mem)
+{
+ image_strategies.interpolate = image_strategy_interpolate;
+}
+
+/* ------ Rendering for interpolated images ------ */
+
+private int
+image_render_interpolate(gx_image_enum * penum, const byte * buffer,
+ int data_x, uint iw, int h, gx_device * dev)
+{
+ stream_IScale_state *pss = penum->scaler;
+ const gs_imager_state *pis = penum->pis;
+ const gs_color_space *pcs = penum->pcs;
+ gs_logical_operation_t lop = penum->log_op;
+ 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;
+ const byte *bdata = buffer + data_x * c * pss->sizeofPixelIn;
+
+ if (pss->sizeofPixelIn == 1) {
+ /* Easy case: 8-bit device color values. */
+ r.ptr = bdata - 1;
+ } else {
+ /* Messy case: concretize each sample. */
+ int bps = penum->bps;
+ int dc = penum->spp;
+ const byte *pdata = bdata;
+ 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(*(const frac *)pdata, cc, j);
+ }
+ (*pcs->type->concretize_color) (&cc, pcs, psrc,
+ pis);
+ }
+ }
+ 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 = penum->xyi.x;
+ int yo = penum->xyi.y;
+ int width = pss->WidthOut;
+ int dy;
+ const gs_color_space *pconcs = cs_concrete_space(pcs, pis);
+ int bpp = dev->color_info.depth;
+ uint raster = bitmap_raster(width * bpp);
+
+ if (penum->matrix.yy > 0)
+ 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, h == 0);
+ 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, pis, dev,
+ gs_color_select_source);
+ if (color_is_pure(&devc)) {
+ /* Just pack colors into a scan line. */
+ gx_color_index color = devc.colors.pure;
+
+ line_accum(color, bpp);
+ } else {
+ int rcode;
+
+ line_accum_copy(dev, penum->line, bpp,
+ xo, x, raster, ry);
+ rcode = gx_fill_rectangle_device_rop(x, ry,
+ 1, 1, &devc, dev, lop);
+ if (rcode < 0)
+ return rcode;
+ line_accum_skip(bpp);
+ l_xprev = x + 1;
+ }
+ }
+ line_accum_copy(dev, penum->line, bpp,
+ xo, x, raster, ry);
+ penum->line_xy++;
+ }
+ if ((code == 0 && r.ptr == r.limit) || code == EOFC)
+ break;
+ }
+ }
+
+ return (h == 0 ? 0 : 1);
+}
diff --git a/pstoraster/gxistate.h b/pstoraster/gxistate.h
new file mode 100644
index 000000000..4575e2125
--- /dev/null
+++ b/pstoraster/gxistate.h
@@ -0,0 +1,251 @@
+/* Copyright (C) 1995, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Imager state definition */
+
+#ifndef gxistate_INCLUDED
+# define gxistate_INCLUDED
+
+#include "gscsel.h"
+#include "gsrefct.h"
+#include "gsropt.h"
+#include "gxcvalue.h"
+#include "gxfixed.h"
+#include "gxline.h"
+#include "gxmatrix.h"
+#include "gxtmap.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
+ * color modification: alpha, rendering algorithm
+ * overprint flag
+ * rendering tweaks: flatness, fill adjustment, stroke adjust flag,
+ * accurate curves flag, shading smoothness
+ * color rendering information:
+ * halftone, halftone phases
+ * transfer functions
+ * black generation, undercolor removal
+ * CIE rendering tables
+ * halftone and pattern caches
+ * The imager state currently EXCLUDES the following:
+ * graphics state stack
+ * default CTM
+ * path
+ * clipping path
+ * color specification: color, color space
+ * font
+ * device
+ * caches for many of the above
+ */
+
+/*
+ * Define the color rendering state information.
+ * This should be a separate object (or at least a substructure),
+ * but doing this would require editing too much code.
+ */
+
+/* Opaque types referenced by the color rendering state. */
+#ifndef gs_halftone_DEFINED
+# define gs_halftone_DEFINED
+typedef struct gs_halftone_s gs_halftone;
+
+#endif
+#ifndef gx_device_color_DEFINED
+# define gx_device_color_DEFINED
+typedef struct gx_device_color_s gx_device_color;
+
+#endif
+#ifndef gx_device_halftone_DEFINED
+# define gx_device_halftone_DEFINED
+typedef struct gx_device_halftone_s gx_device_halftone;
+
+#endif
+
+/*
+ * We need some special memory management for the components of a
+ * c.r. state, as indicated by the following notations on the elements:
+ * (RC) means the element is reference-counted.
+ * (Shared) means the element is shared among an arbitrary number of
+ * c.r. states and is never freed.
+ * (Owned) means exactly one c.r. state references the element,
+ * and it is guaranteed that no references to it will outlive
+ * the c.r. state itself.
+ */
+
+/* Define the interior structure of a transfer function. */
+typedef struct gx_transfer_colored_s {
+ /* The components must be in this order: */
+ gx_transfer_map *red; /* (RC) */
+ gx_transfer_map *green; /* (RC) */
+ gx_transfer_map *blue; /* (RC) */
+ gx_transfer_map *gray; /* (RC) */
+} gx_transfer_colored;
+typedef union gx_transfer_s {
+ gx_transfer_map *indexed[4]; /* (RC) */
+ gx_transfer_colored colored;
+} gx_transfer;
+
+#define gs_color_rendering_state_common\
+\
+ /* Halftone screen: */\
+\
+ gs_halftone *halftone; /* (RC) */\
+ gs_int_point screen_phase[gs_color_select_count];\
+ /* dev_ht depends on halftone and device resolution. */\
+ gx_device_halftone *dev_ht; /* (Owned) */\
+ /* The contents of ht_cache depend on dev_ht. */\
+ struct gx_ht_cache_s *ht_cache; /* (Shared) by all gstates */\
+\
+ /* Color (device-dependent): */\
+\
+ struct gs_cie_render_s *cie_render; /* (RC) may be 0 */\
+ gx_transfer_map *black_generation; /* (RC) may be 0 */\
+ gx_transfer_map *undercolor_removal; /* (RC) 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; /* members are (RC) */\
+ gx_transfer effective_transfer; /* see below */\
+\
+ /* Color caches: */\
+\
+ /* cie_joint_caches depend on cie_render and */\
+ /* the color space. */\
+ struct gx_cie_joint_caches_s *cie_joint_caches; /* (RC) */\
+ /* cmap_procs depend on the device's color_info. */\
+ const struct gx_color_map_procs_s *cmap_procs; /* static */\
+ /* The contents of pattern_cache depend on the */\
+ /* the color space and the device's color_info and */\
+ /* resolution. */\
+ struct gx_pattern_cache_s *pattern_cache /* (Shared) by all gstates */
+
+/*
+ * Enumerate the reference-counted pointers in a c.r. state. Note that
+ * effective_transfer doesn't contribute to the reference count: it points
+ * either to the same objects as set_transfer, or to objects in a halftone
+ * structure that someone else worries about.
+ */
+#define gs_cr_state_do_rc_ptrs(m)\
+ m(halftone) m(cie_render) m(black_generation) m(undercolor_removal)\
+ m(set_transfer.colored.red) m(set_transfer.colored.green)\
+ m(set_transfer.colored.blue) m(set_transfer.colored.gray)\
+ m(cie_joint_caches)
+
+/* Enumerate the pointers in a c.r. state. */
+#define gs_cr_state_do_ptrs(m)\
+ m(0,halftone) m(1,dev_ht) m(2,ht_cache)\
+ 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)
+#define st_cr_state_num_ptrs 16
+
+/*
+ * Define constant values that can be allocated once and shared among
+ * all imager states in an address space.
+ */
+#ifndef gs_color_space_DEFINED
+# define gs_color_space_DEFINED
+typedef struct gs_color_space_s gs_color_space;
+
+#endif
+typedef struct gs_imager_state_shared_s {
+ rc_header rc;
+ gs_color_space *cs_DeviceGray;
+ gs_color_space *cs_DeviceRGB;
+ gs_color_space *cs_DeviceCMYK;
+} gs_imager_state_shared_t;
+
+#define private_st_imager_state_shared() /* in gsstate.c */\
+ gs_private_st_ptrs3(st_imager_state_shared, gs_imager_state_shared_t,\
+ "gs_imager_state_shared", imager_state_shared_enum_ptrs,\
+ imager_state_shared_reloc_ptrs, cs_DeviceGray, cs_DeviceRGB, cs_DeviceCMYK)
+
+/* Define the imager state structure itself. */
+#define gs_imager_state_common\
+ gs_memory_t *memory;\
+ gs_imager_state_shared_t *shared;\
+ gx_line_params line_params;\
+ gs_matrix_fixed ctm;\
+ gs_logical_operation_t log_op;\
+ gx_color_value alpha;\
+ bool overprint;\
+ float flatness;\
+ gs_fixed_point fill_adjust; /* fattening for fill */\
+ bool stroke_adjust;\
+ bool accurate_curves;\
+ float smoothness;\
+ gs_color_rendering_state_common
+#define gs_imager_state_shared(pis, elt) ((pis)->shared->elt)
+#define st_imager_state_num_ptrs\
+ (st_line_params_num_ptrs + st_cr_state_num_ptrs + 1)
+/* 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)\
+ 0, 0, { gx_line_params_initial },\
+ { scale, 0.0, 0.0, -(scale), 0.0, 0.0 },\
+ lop_default, gx_max_color_value, 0/*false*/, 1.0,\
+ { fixed_half, fixed_half }, 0/*false*/, 0/*false*/, 1.0
+
+#define private_st_imager_state() /* in gsstate.c */\
+ gs_private_st_composite(st_imager_state, gs_imager_state, "gs_imager_state",\
+ imager_state_enum_ptrs, imager_state_reloc_ptrs)
+
+/* Initialize an imager state, other than the parts covered by */
+/* gs_imager_state_initial. */
+/* The halftone, dev_ht, and ht_cache elements are not set or used. */
+int gs_imager_state_initialize(P2(gs_imager_state * pis, gs_memory_t * mem));
+
+/* Release an imager state. */
+void gs_imager_state_release(P1(gs_imager_state * pis));
+
+#endif /* gxistate_INCLUDED */
diff --git a/pstoraster/gxline.h b/pstoraster/gxline.h
new file mode 100644
index 000000000..138fc8967
--- /dev/null
+++ b/pstoraster/gxline.h
@@ -0,0 +1,81 @@
+/* Copyright (C) 1995, 1996, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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;
+ bool adapt;
+ /* The rest of the parameters are computed from the above */
+ float pattern_length; /* total of all pattern elements */
+ 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, 0/*false*/, 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 */
+ float dot_length;
+ bool dot_length_absolute; /* if true, dot_length is 1/72" units */
+ 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 *));
+
+#define gx_set_dash_adapt(pdp, adpt) ((pdp)->adapt = (adpt))
+int gx_set_dot_length(P3(gx_line_params *, floatp, bool));
+
+/* 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, 0.0, 0/*false*/,\
+ { gx_dash_params_initial }
+
+#endif /* gxline_INCLUDED */
diff --git a/pstoraster/gxlum.h b/pstoraster/gxlum.h
new file mode 100644
index 000000000..e4f1cbc59
--- /dev/null
+++ b/pstoraster/gxlum.h
@@ -0,0 +1,50 @@
+/*
+ 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 supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Luminance computation parameters for Ghostscript */
+
+#ifndef gxlum_INCLUDED
+# define gxlum_INCLUDED
+
+/*
+ * 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)
+
+#endif /* gxlum_INCLUDED */
diff --git a/pstoraster/gxmatrix.h b/pstoraster/gxmatrix.h
new file mode 100644
index 000000000..01b9df532
--- /dev/null
+++ b/pstoraster/gxmatrix.h
@@ -0,0 +1,83 @@
+/* Copyright (C) 1989, 1995, 1996, 1997 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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 *));
+
+/*
+ * 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/gxmclip.c b/pstoraster/gxmclip.c
new file mode 100644
index 000000000..a2694318c
--- /dev/null
+++ b/pstoraster/gxmclip.c
@@ -0,0 +1,116 @@
+/* Copyright (C) 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Mask clipping support */
+#include "gx.h"
+#include "gxdevice.h"
+#include "gxdevmem.h"
+#include "gxmclip.h"
+
+/* Structure descriptor */
+public_st_device_mask_clip();
+
+/* GC procedures */
+#define mcdev ((gx_device_mask_clip *)vptr)
+
+private ENUM_PTRS_BEGIN(device_mask_clip_enum_ptrs)
+{
+ if (index < st_gx_strip_bitmap_max_ptrs) {
+ return ENUM_USING(st_gx_strip_bitmap, &mcdev->tiles,
+ sizeof(mcdev->tiles), index);
+ }
+ index -= st_gx_strip_bitmap_max_ptrs;
+ if (index < st_device_memory_max_ptrs) {
+ return ENUM_USING(st_device_memory, &mcdev->mdev,
+ sizeof(mcdev->mdev), index);
+ }
+ ENUM_PREFIX(st_device_forward, st_device_memory_max_ptrs);
+}
+ENUM_PTRS_END
+
+private RELOC_PTRS_BEGIN(device_mask_clip_reloc_ptrs)
+{
+ RELOC_PREFIX(st_device_forward);
+ RELOC_USING(st_gx_strip_bitmap, &mcdev->tiles, sizeof(mcdev->tiles));
+ RELOC_USING(st_device_memory, &mcdev->mdev, sizeof(mcdev->mdev));
+ if (mcdev->mdev.base != 0) {
+ /*
+ * Update the line pointers specially, since they point into the
+ * buffer that is part of the mask clipping device itself.
+ */
+ long diff = (char *)RELOC_OBJ(mcdev) - (char *)mcdev;
+ int i;
+
+ for (i = 0; i < mcdev->mdev.height; ++i)
+ mcdev->mdev.line_ptrs[i] += diff;
+ mcdev->mdev.base = mcdev->mdev.line_ptrs[0];
+ mcdev->mdev.line_ptrs =
+ (void *)((char *)(mcdev->mdev.line_ptrs) + diff);
+ }
+}
+RELOC_PTRS_END
+
+#undef mcdev
+
+/* Initialize a mask clipping device. */
+int
+gx_mask_clip_initialize(gx_device_mask_clip * cdev,
+ const gx_device_mask_clip * proto,
+ const gx_bitmap * bits, gx_device * tdev,
+ int tx, int ty)
+{
+ int buffer_width = bits->size.x;
+ int buffer_height =
+ tile_clip_buffer_size / (bits->raster + sizeof(byte *));
+
+ gx_device_init((gx_device *)cdev, (const gx_device *)proto,
+ NULL, true);
+ cdev->width = tdev->width;
+ cdev->height = tdev->height;
+ cdev->color_info = tdev->color_info;
+ cdev->target = tdev;
+ cdev->phase.x = -tx;
+ cdev->phase.y = -ty;
+ if (buffer_height > bits->size.y)
+ buffer_height = bits->size.y;
+ gs_make_mem_mono_device(&cdev->mdev, 0, 0);
+ for (;;) {
+ if (buffer_height <= 0) {
+ /*
+ * The tile is too wide to buffer even one scan line.
+ * We could do copy_mono in chunks, but for now, we punt.
+ */
+ cdev->mdev.base = 0;
+ return 0;
+ }
+ 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);
+}
diff --git a/pstoraster/gxmclip.h b/pstoraster/gxmclip.h
new file mode 100644
index 000000000..b56a0195b
--- /dev/null
+++ b/pstoraster/gxmclip.h
@@ -0,0 +1,111 @@
+/* Copyright (C) 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Mask clipping device and interface */
+/* Requires gxdevice.h, gxdevmem.h */
+
+#ifndef gxmclip_INCLUDED
+# define gxmclip_INCLUDED
+
+#include "gxclip.h"
+
+/*
+ * ImageType 3 images and Patterns that don't completely fill their
+ * bounding box require the ability to clip against a mask.
+ * The interface declared here doesn't take a position on whether
+ * the mask will be used only in one position (ImageType 3) or in
+ * multiple positions for tiling (Patterns).
+ *
+ * All the information in this file is logically private, but we must expose
+ * the structure definition so that clients can allocate instances in the
+ * stack frame.
+ */
+
+#define tile_clip_buffer_request 300
+#define tile_clip_buffer_size\
+ ((tile_clip_buffer_request / arch_sizeof_long) * arch_sizeof_long)
+typedef struct gx_device_mask_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_mask_clip;
+
+extern_st(st_device_mask_clip);
+#define public_st_device_mask_clip() /* in gxmclip.c */\
+ gs_public_st_composite(st_device_mask_clip, gx_device_mask_clip,\
+ "gx_device_mask_clip", device_mask_clip_enum_ptrs,\
+ device_mask_clip_reloc_ptrs)
+
+/*
+ * Internal routine to initialize a mask clipping device.
+ * We supply an explicit device space origin or phase.
+ * Note that this procedure does not set cdev->tiles.
+ */
+int gx_mask_clip_initialize(P6(gx_device_mask_clip * cdev,
+ const gx_device_mask_clip * proto,
+ const gx_bitmap * bits, gx_device * tdev,
+ int tx, int ty));
+
+/*
+ * Prepare colors for a copy_mono operation.
+ * The arguments of copy_mono are free variables:
+ * dev, data, sourcex, raster, id, x, y, w, y, color0, color1.
+ */
+#define setup_mask_copy_mono(cdev, color, mcolor0, mcolor1)\
+ BEGIN\
+ if ( cdev->mdev.base == 0 ) {\
+ /*\
+ * The tile was too large for us to buffer even one scan line.\
+ * Punt to the very, very slow default implementation of\
+ * copy_mono.\
+ */\
+ return gx_default_copy_mono(dev, data, sourcex, raster, id,\
+ x, y, w, h, color0, color1);\
+ }\
+ if ( color1 != gx_no_color_index ) {\
+ if ( color0 != gx_no_color_index ) {\
+ /* Pre-fill with color0. */\
+ code =\
+ (*dev_proc(dev, 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;\
+ END
+
+#endif /* gxmclip_INCLUDED */
diff --git a/pstoraster/gxobj.h b/pstoraster/gxobj.h
new file mode 100644
index 000000000..8d395a713
--- /dev/null
+++ b/pstoraster/gxobj.h
@@ -0,0 +1,230 @@
+/* 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 supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Memory manager implementation structures for Ghostscript */
+
+#ifndef gxobj_INCLUDED
+# define gxobj_INCLUDED
+
+#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;
+
+#endif /* gxobj_INCLUDED */
diff --git a/pstoraster/gxop1.h b/pstoraster/gxop1.h
new file mode 100644
index 000000000..82b1cc96b
--- /dev/null
+++ b/pstoraster/gxop1.h
@@ -0,0 +1,81 @@
+/* Copyright (C) 1991, 1992, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Type 1 state shared between interpreter and compiled fonts. */
+
+#ifndef gxop1_INCLUDED
+# define gxop1_INCLUDED
+
+/*
+ * 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 *is_ptr;
+
+/* 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)\
+ BEGIN\
+ ptx += c_fixed(dx, xx);\
+ if ( sfc.skewed ) pty += c_fixed(dx, xy);\
+ END
+#define accum_y(dy)\
+ BEGIN\
+ pty += c_fixed(dy, yy);\
+ if ( sfc.skewed ) ptx += c_fixed(dy, yx);\
+ END
+void accum_xy_proc(P3(is_ptr ps, fixed dx, fixed dy));
+
+#define accum_xy(dx,dy)\
+ accum_xy_proc(&s, dx, dy)
+
+/* Define operator procedures. */
+int gs_op1_closepath(P1(is_ptr ps));
+int gs_op1_rrcurveto(P7(is_ptr ps, fixed dx1, fixed dy1,
+ fixed dx2, fixed dy2, fixed dx3, fixed dy3));
+
+#endif /* gxop1_INCLUDED */
diff --git a/pstoraster/gxp1fill.c b/pstoraster/gxp1fill.c
new file mode 100644
index 000000000..5c3fe1ca3
--- /dev/null
+++ b/pstoraster/gxp1fill.c
@@ -0,0 +1,374 @@
+/* Copyright (C) 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* PatternType 1 filling algorithms */
+#include "math_.h"
+#include "gx.h"
+#include "gserrors.h"
+#include "gsrop.h"
+#include "gsmatrix.h"
+#include "gxcspace.h" /* for gscolor2.h */
+#include "gxcolor2.h"
+#include "gxdcolor.h"
+#include "gxdevcli.h"
+#include "gxdevmem.h"
+#include "gxclip2.h"
+#include "gxpcolor.h"
+#include "gxp1fill.h"
+
+/* Define the state for tile filling. */
+typedef struct tile_fill_state_s {
+
+ /* Original arguments */
+
+ const gx_device_color *pdevc; /* pattern color */
+ int x0, y0, w0, h0;
+ gs_logical_operation_t lop;
+ const gx_rop_source_t *source;
+
+ /* Variables set at initialization */
+
+ gx_device_tile_clip cdev;
+ gx_device *pcdev; /* original device or &cdev */
+ const gx_strip_bitmap *tmask;
+
+ /* Following are only for uncolored patterns */
+
+ dev_color_proc_fill_rectangle((*fill_rectangle));
+
+ /* Following are only for colored patterns */
+
+ const gx_rop_source_t *rop_source;
+ gx_device *orig_dev;
+ int xoff, yoff; /* set dynamically */
+
+} tile_fill_state_t;
+
+/* Initialize the filling state. */
+private int
+tile_fill_init(tile_fill_state_t * ptfs, const gx_device_color * pdevc,
+ gx_device * dev)
+{
+ gx_color_tile *m_tile = pdevc->mask.m_tile;
+
+ ptfs->pdevc = pdevc;
+ if (m_tile == 0) { /* no clipping */
+ ptfs->pcdev = dev;
+ return 0;
+ }
+ ptfs->pcdev = (gx_device *) & ptfs->cdev;
+ ptfs->tmask = &m_tile->tmask;
+ return tile_clip_initialize(&ptfs->cdev, ptfs->tmask, dev, 0, 0);
+}
+
+/*
+ * Fill with non-standard X and Y stepping.
+ * ptile is pdevc->colors.pattern.{m,p}_tile.
+ * tbits_or_tmask is whichever of tbits and tmask is supplying
+ * the tile size.
+ * This implementation could be sped up considerably!
+ */
+private int
+tile_by_steps(tile_fill_state_t * ptfs, int x0, int y0, int w0, int h0,
+ const gx_color_tile * ptile,
+ const gx_strip_bitmap * tbits_or_tmask,
+ int (*fill_proc) (P5(const tile_fill_state_t * ptfs,
+ int x, int y, int w, int h)))
+{
+ int x1 = x0 + w0, y1 = y0 + h0;
+ int i0, i1, j0, j1, i, j;
+ gs_matrix step_matrix; /* translated by phase */
+ int code;
+
+ ptfs->x0 = x0, ptfs->w0 = w0;
+ ptfs->y0 = y0, ptfs->h0 = h0;
+ step_matrix = ptile->step_matrix;
+ {
+ gs_rect bbox; /* bounding box in device space */
+ gs_rect ibbox; /* bounding box in stepping space */
+ double bbw = ptile->bbox.q.x - ptile->bbox.p.x;
+ double bbh = ptile->bbox.q.y - ptile->bbox.p.y;
+
+ bbox.p.x = x0, bbox.p.y = y0;
+ bbox.q.x = x1, bbox.q.y = y1;
+ gs_bbox_transform_inverse(&bbox, &step_matrix, &ibbox);
+ if_debug10('T',
+ "[T]x,y=(%d,%d) w,h=(%d,%d) => (%g,%g),(%g,%g), offset=(%g,%g)\n",
+ x0, y0, w0, h0,
+ ibbox.p.x, ibbox.p.y, ibbox.q.x, ibbox.q.y,
+ step_matrix.tx, step_matrix.ty);
+ i0 = (int)ceil(ibbox.p.x - bbw - 0.000001);
+ i1 = (int)floor(ibbox.q.x + 0.000001);
+ j0 = (int)ceil(ibbox.p.y - bbh - 0.000001);
+ j1 = (int)floor(ibbox.q.y + 0.000001);
+ }
+ 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 x = (int)(step_matrix.xx * i +
+ step_matrix.yx * j + step_matrix.tx);
+ int y = (int)(step_matrix.xy * i +
+ step_matrix.yy * j + step_matrix.ty);
+ int w = tbits_or_tmask->size.x;
+ int h = tbits_or_tmask->size.y;
+ int xoff, yoff;
+
+ if_debug4('T', "[T]i=%d j=%d x,y=(%d,%d)", i, j, x, 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_debug6('T', "=>(%d,%d) w,h=(%d,%d) x/yoff=(%d,%d)\n",
+ x, y, w, h, xoff, yoff);
+ if (w > 0 && h > 0) {
+ if (ptfs->pcdev == (gx_device *) & ptfs->cdev)
+ tile_clip_set_phase(&ptfs->cdev,
+ imod(xoff - x, ptfs->tmask->rep_width),
+ imod(yoff - y, ptfs->tmask->rep_height));
+ /* Set the offsets for colored pattern fills */
+ ptfs->xoff = xoff;
+ ptfs->yoff = yoff;
+ code = (*fill_proc) (ptfs, x, y, w, h);
+ if (code < 0)
+ return code;
+ }
+ }
+ return 0;
+}
+
+/* Fill a rectangle with a colored Pattern. */
+/* Note that we treat this as "texture" for RasterOp. */
+private int
+tile_colored_fill(const tile_fill_state_t * ptfs,
+ int x, int y, int w, int h)
+{
+ gx_color_tile *ptile = ptfs->pdevc->colors.pattern.p_tile;
+ gs_logical_operation_t lop = ptfs->lop;
+ const gx_rop_source_t *source = ptfs->source;
+ const gx_rop_source_t *rop_source = ptfs->rop_source;
+ gx_device *dev = ptfs->orig_dev;
+ int xoff = ptfs->xoff, yoff = ptfs->yoff;
+ gx_strip_bitmap *bits = &ptile->tbits;
+ const byte *data = bits->data;
+ bool full_transfer = (w == ptfs->w0 && h == ptfs->h0);
+ gx_bitmap_id source_id =
+ (full_transfer ? rop_source->id : gx_no_bitmap_id);
+ int code;
+
+ if (source == NULL && lop_no_S_is_T(lop))
+ code = (*dev_proc(ptfs->pcdev, copy_color))
+ (ptfs->pcdev, data + bits->raster * yoff, xoff,
+ bits->raster,
+ (full_transfer ? bits->id : gx_no_bitmap_id),
+ x, y, w, h);
+ else {
+ 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 = ptile->tbits.size.x;
+ data_tile.size.y = data_tile.rep_height = ptile->tbits.size.y;
+ data_tile.id = bits->id;
+ data_tile.shift = data_tile.rep_shift = 0;
+ code = (*dev_proc(dev, strip_copy_rop))
+ (dev,
+ rop_source->sdata + (y - ptfs->y0) * rop_source->sraster,
+ rop_source->sourcex + (x - ptfs->x0),
+ rop_source->sraster, source_id,
+ (rop_source->use_scolors ? rop_source->scolors : NULL),
+ &data_tile, NULL,
+ x, y, w, h,
+ imod(xoff - x, data_tile.rep_width),
+ imod(yoff - y, data_tile.rep_height),
+ lop);
+ }
+ return code;
+}
+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;
+ gx_rop_source_t no_source;
+ gx_strip_bitmap *bits;
+ tile_fill_state_t state;
+ int code;
+
+ if (ptile == 0) /* null pattern */
+ return 0;
+ if (rop_source == NULL)
+ set_rop_no_source(rop_source, no_source, dev);
+ bits = &ptile->tbits;
+ code = tile_fill_init(&state, pdevc, dev);
+ if (code < 0)
+ return code;
+ if (ptile->is_simple) {
+ int px = imod(-(int)(ptile->step_matrix.tx + 0.5), bits->rep_width);
+ int py = imod(-(int)(ptile->step_matrix.ty + 0.5), bits->rep_height);
+
+ if (state.pcdev != dev)
+ tile_clip_set_phase(&state.cdev, px, py);
+ if (source == NULL && lop_no_S_is_T(lop))
+ code = (*dev_proc(state.pcdev, strip_tile_rectangle))
+ (state.pcdev, bits, x, y, w, h,
+ gx_no_color_index, gx_no_color_index, px, py);
+ else
+ code = (*dev_proc(state.pcdev, strip_copy_rop))
+ (state.pcdev,
+ 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, px, py, lop);
+ } else {
+ state.lop = lop;
+ state.source = source;
+ state.rop_source = rop_source;
+ state.orig_dev = dev;
+ code = tile_by_steps(&state, x, y, w, h, ptile,
+ &ptile->tbits, tile_colored_fill);
+ }
+ return code;
+}
+
+/* Fill a rectangle with an uncolored Pattern. */
+/* Note that we treat this as "texture" for RasterOp. */
+private int
+tile_masked_fill(const tile_fill_state_t * ptfs,
+ int x, int y, int w, int h)
+{
+ if (ptfs->source == NULL)
+ return (*ptfs->fill_rectangle)
+ (ptfs->pdevc, x, y, w, h, ptfs->pcdev, ptfs->lop, NULL);
+ else {
+ const gx_rop_source_t *source = ptfs->source;
+ gx_rop_source_t step_source;
+
+ step_source.sdata = source->sdata + (y - ptfs->y0) * source->sraster;
+ step_source.sourcex = source->sourcex + (x - ptfs->x0);
+ step_source.sraster = source->sraster;
+ step_source.id = (w == ptfs->w0 && h == ptfs->h0 ?
+ source->id : gx_no_bitmap_id);
+ step_source.scolors[0] = source->scolors[0];
+ step_source.scolors[1] = source->scolors[1];
+ step_source.use_scolors = source->use_scolors;
+ return (*ptfs->fill_rectangle)
+ (ptfs->pdevc, x, y, w, h, ptfs->pcdev, ptfs->lop, &step_source);
+ }
+}
+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.m_tile;
+ tile_fill_state_t state;
+ int code;
+
+ /*
+ * This routine should never be called if there is no masking,
+ * but we leave the checks below just in case.
+ */
+ code = tile_fill_init(&state, pdevc, dev);
+ if (code < 0)
+ return code;
+ if (state.pcdev != dev) {
+ int px = imod(-(int)(ptile->step_matrix.tx + 0.5),
+ ptile->tmask.rep_width);
+ int py = imod(-(int)(ptile->step_matrix.ty + 0.5),
+ ptile->tmask.rep_height);
+
+ tile_clip_set_phase(&state.cdev, px, py);
+ }
+ if (state.pcdev == dev || ptile->is_simple)
+ return (*gx_dc_type_data_pure.fill_rectangle)
+ (pdevc, x, y, w, h, state.pcdev, lop, source);
+ else {
+ state.lop = lop;
+ state.source = source;
+ state.fill_rectangle = gx_dc_type_data_pure.fill_rectangle;
+ return tile_by_steps(&state, x, y, w, h, ptile, &ptile->tmask,
+ tile_masked_fill);
+ }
+}
+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.m_tile;
+ tile_fill_state_t state;
+ int code;
+
+ code = tile_fill_init(&state, pdevc, dev);
+ if (code < 0)
+ return code;
+ if (state.pcdev == dev || ptile->is_simple)
+ return (*gx_dc_type_data_ht_binary.fill_rectangle)
+ (pdevc, x, y, w, h, state.pcdev, lop, source);
+ else {
+ state.lop = lop;
+ state.source = source;
+ state.fill_rectangle = gx_dc_type_data_ht_binary.fill_rectangle;
+ return tile_by_steps(&state, x, y, w, h, ptile, &ptile->tmask,
+ tile_masked_fill);
+ }
+}
+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.m_tile;
+ tile_fill_state_t state;
+ int code;
+
+ code = tile_fill_init(&state, pdevc, dev);
+ if (code < 0)
+ return code;
+ if (state.pcdev == dev || ptile->is_simple)
+ return (*gx_dc_type_data_ht_colored.fill_rectangle)
+ (pdevc, x, y, w, h, state.pcdev, lop, source);
+ else {
+ state.lop = lop;
+ state.source = source;
+ state.fill_rectangle = gx_dc_type_data_ht_colored.fill_rectangle;
+ return tile_by_steps(&state, x, y, w, h, ptile, &ptile->tmask,
+ tile_masked_fill);
+ }
+}
diff --git a/pstoraster/gxp1fill.h b/pstoraster/gxp1fill.h
new file mode 100644
index 000000000..5a35d8c44
--- /dev/null
+++ b/pstoraster/gxp1fill.h
@@ -0,0 +1,40 @@
+/* Copyright (C) 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* PatternType 1 filling algorithm interface */
+
+#ifndef gxp1fill_INCLUDED
+# define gxp1fill_INCLUDED
+
+/*
+ * We use 'masked_fill_rect' instead of 'masked_fill_rectangle'
+ * in order to limit identifier lengths to 32 characters.
+ */
+dev_color_proc_fill_rectangle(gx_dc_pattern_fill_rectangle);
+dev_color_proc_fill_rectangle(gx_dc_pure_masked_fill_rect);
+dev_color_proc_fill_rectangle(gx_dc_binary_masked_fill_rect);
+dev_color_proc_fill_rectangle(gx_dc_colored_masked_fill_rect);
+
+#endif /* gxp1fill_INCLUDED */
diff --git a/pstoraster/gxpageq.h b/pstoraster/gxpageq.h
new file mode 100644
index 000000000..cd0100c8f
--- /dev/null
+++ b/pstoraster/gxpageq.h
@@ -0,0 +1,192 @@
+/* Copyright (C) 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Page queue implementation */
+
+/* Initial version 2/1/98 by John Desrosiers (soho@crl.com) */
+/* 7/17/98 L. Peter Deutsch (ghost@aladdin.com) edited to conform to
+ Ghostscript coding standards */
+/* 8/7/98 ghost@aladdin.com fixed bugs in #define st_... statements */
+
+#ifndef gxpageq_INCLUDED
+# define gxpageq_INCLUDED
+
+# include "gsmemory.h"
+# include "gxband.h"
+# include "gxsync.h"
+
+/* --------------- Data type definitions --------------------- */
+
+typedef enum {
+ gx_page_queue_action_partial_page,
+ gx_page_queue_action_full_page,
+ gx_page_queue_action_copy_page,
+ gx_page_queue_action_terminate
+} gx_page_queue_action_t;
+
+#ifndef gx_page_queue_DEFINED
+# define gx_page_queue_DEFINED
+typedef struct gx_page_queue_s gx_page_queue;
+#endif
+
+/*
+ * Define a page queue entry object.
+ */
+typedef struct gx_page_queue_entry_s {
+ gx_band_page_info page_info;
+ gx_page_queue_action_t action; /* action code */
+ int num_copies; /* number of copies to render */
+ struct gx_page_queue_entry_s *next; /* link to next in queue */
+ gx_page_queue *queue; /* link to queue the entry is in */
+} gx_page_queue_entry;
+
+#define private_st_gx_page_queue_entry()\
+ gs_private_st_ptrs2(st_gx_page_queue_entry, gx_page_queue_entry,\
+ "gx_page_queue_entry",\
+ gx_page_queue_entry_enum_ptrs, gx_page_queue_entry_reloc_ptrs,\
+ next, queue)
+
+/*
+ * Define the structure used to manage a page queue
+ * A page queue is a monitor-locked FIFO which holds completed command
+ * list files ready for rendering.
+ */
+struct gx_page_queue_s {
+ gs_memory_t *memory; /* allocator used to allocate entries */
+ gx_monitor_t *monitor; /* used to serialize access to this structure */
+ int entry_count; /* # elements in page_queue */
+ bool dequeue_in_progress; /* true between start/ & end_dequeue */
+ gx_semaphore_t *render_req_sema; /* sema signalled when page queued */
+ bool enable_render_done_signal; /* enable signals to render_done_sema */
+ gx_semaphore_t *render_done_sema; /* semaphore signaled when (partial) page rendered */
+ gx_page_queue_entry *last_in; /* if <> 0, Last-in queue entry */
+ gx_page_queue_entry *first_in; /* if <> 0, First-in queue entry */
+ gx_page_queue_entry *reserve_entry; /* spare allocation */
+};
+
+#define private_st_gx_page_queue()\
+ gs_private_st_ptrs4(st_gx_page_queue, gx_page_queue, "gx_page_queue",\
+ gx_page_queue_enum_ptrs, gx_page_queue_reloc_ptrs,\
+ monitor, first_in, last_in, reserve_entry);
+
+/* -------------- Public Procedure Declaraions --------------------- */
+
+/* Allocate a page queue. */
+gx_page_queue *gx_page_queue_alloc(P1(gs_memory_t *mem));
+
+/* All page queue entries must be allocated by this routine. Allocated */
+/* entries are initialized & ready to go */
+/* rets ptr to allocated object, 0 if VM error */
+gx_page_queue_entry *
+gx_page_queue_entry_alloc(P1(
+ gx_page_queue * queue /* queue that entry is being alloc'd for */
+ ));
+
+/* All page queues entries must be destroyed by this routine */
+void gx_page_queue_entry_free(P1(
+ gx_page_queue_entry * entry /* entry to free up */
+ ));
+
+/* Init a page queue; this must be done before it can be used. This routine */
+/* allocates & inits various necessary structures and will fail if insufficient */
+/* memory is available. */
+/* -ve error code, or 0 */
+int gx_page_queue_init(P2(
+ gx_page_queue * queue, /* page queue to init */
+ gs_memory_t * memory /* allocator for dynamic memory */
+ ));
+
+/* Destroy a page queue which was initialized by gx_page_queue_init. Any */
+/* page queue entries in the queue are released and destroyed; dynamic */
+/* allocations are released. */
+void gx_page_queue_dnit(P1(
+ gx_page_queue * queue /* page queue to dnit */
+ ));
+
+/* If there are any pages in queue, wait until one of them finishes rendering. */
+/* Typically called by writer's out-of-memory error handlers that want to wait */
+/* until some memory has been freed. */
+/* rets 0 if no pages were waiting for rendering, 1 if actually waited */
+int gx_page_queue_wait_one_page(P1(
+ gx_page_queue * queue /* queue to wait on */
+ ));
+
+/* Wait until all (if any) pages in queue have finished rendering. Typically */
+/* called by writer operations which need to drain the page queue before */
+/* continuing. */
+void gx_page_queue_wait_until_empty(P1(
+ gx_page_queue * queue /* page queue to wait on */
+ ));
+
+/* Add a pageq queue entry to the end of the page queue. If an unsatisfied */
+/* reader thread has an outstanding gx_page_queue_start_deque(), wake it up. */
+void gx_page_queue_enqueue(P1(
+ gx_page_queue_entry * entry /* entry to add */
+ ));
+
+/* Allocate & construct a pageq entry, then to the end of the pageq as */
+/* in gx_page_queue_enqueue. If unable to allocate a new pageq entry, uses */
+/* the pre-allocated reserve entry held in the pageq. When using the reserve */
+/* pageq entry, wait until enough pages have been rendered to allocate a new */
+/* reserve for next time -- this should always succeed & returns eFatal if not. */
+/* Unless the reserve was used, does not wait for any rendering to complete. */
+/* Typically called by writer when it has a (partial) page ready for rendering. */
+/* rets 0 ok, gs_error_Fatal if error */
+int gx_page_queue_add_page(P4(
+ gx_page_queue * queue, /* page queue to add to */
+ gx_page_queue_action_t action, /* action code to queue */
+ const gx_band_page_info * page_info, /* bandinfo incl. bandlist */
+ int page_count /* # of copies to print if final "print,"
+ /* 0 if partial page, -1 if cancel */
+ ));
+
+/* Retrieve the least-recently added queue entry from the pageq. If no */
+/* entry is available, waits on a signal from gx_page_queue_enqueue. Must */
+/* eventually be followed by a call to gx_page_queue_finish_dequeue for the */
+/* same pageq entry. */
+/* Even though the pageq is actually removed from the pageq, a mark is made in */
+/* the pageq to indicate that the pageq is not "empty" until the */
+/* gx_page_queue_finish_dequeue; this is for the benefit of */
+/* gx_page_queue_wait_???, since the completing the current page's rendering */
+/* may free more memory. */
+/* Typically called by renderer thread loop, which looks like: */
+/* do */
+/* { gx_page_queue_start_deqeueue(...); */
+/* render_retrieved_entry(...); */
+/* gx_page_queue_finish_dequeue(...); */
+/* } */
+/* while (some condition); */
+gx_page_queue_entry * /* removed entry */
+gx_page_queue_start_dequeue(P1(
+ gx_page_queue * queue /* page queue to retrieve from */
+ ));
+
+/* Free the pageq entry, then signal any waiting threads. */
+/* Typically used to indicate completion of rendering the pageq entry. */
+void gx_page_queue_finish_dequeue(P1(
+ gx_page_queue_entry * entry /* entry that was retrieved to delete */
+ ));
+
+#endif /*!defined(gxpageq_h_INCLUDED) */
diff --git a/pstoraster/gxpaint.c b/pstoraster/gxpaint.c
new file mode 100644
index 000000000..3ac973619
--- /dev/null
+++ b/pstoraster/gxpaint.c
@@ -0,0 +1,81 @@
+/* Copyright (C) 1995, 1996, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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_clip_path *pcpath;
+ int code = gx_effective_clip_path(pgs, &pcpath);
+ gx_fill_params params;
+
+ if (code < 0)
+ return code;
+ 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 = (adjust_x | adjust_y) != 0;
+ return (*dev_proc(dev, fill_path))
+ (dev, (const gs_imager_state *)pgs, ppath, &params, pdevc, pcpath);
+}
+
+/* Stroke a path for drawing or saving. */
+int
+gx_stroke_fill(gx_path * ppath, gs_state * pgs)
+{
+ gx_device *dev = gs_currentdevice_inline(pgs);
+ gx_clip_path *pcpath;
+ int code = gx_effective_clip_path(pgs, &pcpath);
+ gx_stroke_params params;
+
+ if (code < 0)
+ return code;
+ params.flatness = (pgs->in_cachedevice > 1 ? 0.0 : pgs->flatness);
+ return (*dev_proc(dev, stroke_path))
+ (dev, (const gs_imager_state *)pgs, ppath, &params,
+ pgs->dev_color, pcpath);
+}
+
+int
+gx_stroke_add(gx_path * ppath, gx_path * to_path, gs_state * pgs)
+{
+ gx_stroke_params params;
+
+ params.flatness = (pgs->in_cachedevice > 1 ? 0.0 : pgs->flatness);
+ return gx_stroke_path_only(ppath, to_path, pgs->device,
+ (const gs_imager_state *)pgs,
+ &params, NULL, NULL);
+}
diff --git a/pstoraster/gxpaint.h b/pstoraster/gxpaint.h
new file mode 100644
index 000000000..17afc1d05
--- /dev/null
+++ b/pstoraster/gxpaint.h
@@ -0,0 +1,125 @@
+/* Copyright (C) 1994, 1995, 1996, 1997 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Requires gsropt.h, gxfixed.h, gxpath.h */
+
+#ifndef gxpaint_INCLUDED
+# define gxpaint_INCLUDED
+
+#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. If the amount is too large to fit in
+ * a gs_fixed_point, return gs_error_limitcheck.
+ */
+int gx_stroke_path_expansion(P3(const gs_imager_state *,
+ const gx_path *, gs_fixed_point *));
+
+/* Backward compatibility */
+#define gx_stroke_expansion(pis, ppt)\
+ gx_stroke_path_expansion(pis, (const gx_path *)0, ppt)
+
+/*
+ * 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));
+
+#endif /* gxpaint_INCLUDED */
diff --git a/pstoraster/gxpath.c b/pstoraster/gxpath.c
new file mode 100644
index 000000000..bf94c2299
--- /dev/null
+++ b/pstoraster/gxpath.c
@@ -0,0 +1,829 @@
+/* Copyright (C) 1989, 1995, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Internal path management 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 int 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') ) dlprintf(msg), gx_print_segment(pseg);
+#else
+# define trace_segment(msg, pseg) DO_NOTHING
+#endif
+
+/* Check 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_path_segments();
+private_st_segment();
+private_st_line();
+private_st_line_close();
+private_st_curve();
+private_st_subpath();
+
+/* ------ Initialize/free paths ------ */
+
+private rc_free_proc(rc_free_path_segments);
+private rc_free_proc(rc_free_path_segments_local);
+
+private void
+gx_path_init_contents(gx_path * ppath)
+{
+ ppath->box_last = 0;
+ ppath->first_subpath = ppath->current_subpath = 0;
+ ppath->subpath_count = 0;
+ ppath->curve_count = 0;
+ path_update_newpath(ppath);
+ ppath->bbox_set = 0;
+}
+
+/*
+ * Initialize a path contained in an already-heap-allocated object,
+ * optionally allocating its segments.
+ */
+private int
+path_alloc_segments(gx_path_segments ** ppsegs, gs_memory_t * mem,
+ client_name_t cname)
+{
+ rc_alloc_struct_1(*ppsegs, gx_path_segments, &st_path_segments,
+ mem, return_error(gs_error_VMerror), cname);
+ (*ppsegs)->rc.free = rc_free_path_segments;
+ return 0;
+}
+int
+gx_path_init_contained_shared(gx_path * ppath, const gx_path * shared,
+ gs_memory_t * mem, client_name_t cname)
+{
+ if (shared) {
+ if (shared->segments == &shared->local_segments) {
+ lprintf1("Attempt to share (local) segments of path 0x%lx!\n",
+ (ulong) shared);
+ return_error(gs_error_Fatal);
+ }
+ *ppath = *shared;
+ rc_increment(ppath->segments);
+ } else {
+ int code = path_alloc_segments(&ppath->segments, mem, cname);
+
+ if (code < 0)
+ return code;
+ gx_path_init_contents(ppath);
+ }
+ ppath->memory = mem;
+ ppath->allocation = path_allocated_contained;
+ return 0;
+}
+
+/*
+ * Allocate a path on the heap, and initialize it. If shared is NULL,
+ * allocate a segments object; if shared is an existing path, share its
+ * segments.
+ */
+gx_path *
+gx_path_alloc_shared(const gx_path * shared, 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;
+ if (shared) {
+ if (shared->segments == &shared->local_segments) {
+ lprintf1("Attempt to share (local) segments of path 0x%lx!\n",
+ (ulong) shared);
+ gs_free_object(mem, ppath, cname);
+ return 0;
+ }
+ *ppath = *shared;
+ rc_increment(ppath->segments);
+ } else {
+ int code = path_alloc_segments(&ppath->segments, mem, cname);
+
+ if (code < 0) {
+ gs_free_object(mem, ppath, cname);
+ return 0;
+ }
+ gx_path_init_contents(ppath);
+ }
+ ppath->memory = mem;
+ ppath->allocation = path_allocated_on_heap;
+ return ppath;
+}
+
+/*
+ * Initialize a stack-allocated path. This doesn't allocate anything,
+ * but may still share the segments.
+ */
+int
+gx_path_init_local_shared(gx_path * ppath, const gx_path * shared,
+ gs_memory_t * mem)
+{
+ if (shared) {
+ if (shared->segments == &shared->local_segments) {
+ lprintf1("Attempt to share (local) segments of path 0x%lx!\n",
+ (ulong) shared);
+ return_error(gs_error_Fatal);
+ }
+ *ppath = *shared;
+ rc_increment(ppath->segments);
+ } else {
+ rc_init_free(&ppath->local_segments, mem, 1,
+ rc_free_path_segments_local);
+ ppath->segments = &ppath->local_segments;
+ gx_path_init_contents(ppath);
+ }
+ ppath->memory = mem;
+ ppath->allocation = path_allocated_on_stack;
+ return 0;
+}
+
+/*
+ * Ensure that a path owns its segments, by copying the segments if
+ * they currently have multiple references.
+ */
+int
+gx_path_unshare(gx_path * ppath)
+{
+ int code = 0;
+
+ if (gx_path_is_shared(ppath))
+ code = path_alloc_copy(ppath);
+ return code;
+}
+
+/*
+ * Free a path by releasing its segments if they have no more references.
+ * This also frees the path object iff it was allocated by gx_path_alloc.
+ */
+void
+gx_path_free(gx_path * ppath, client_name_t cname)
+{
+ rc_decrement(ppath->segments, cname);
+ /* Clean up pointers for GC. */
+ ppath->box_last = 0;
+ ppath->segments = 0; /* Nota bene */
+ if (ppath->allocation == path_allocated_on_heap)
+ gs_free_object(ppath->memory, ppath, cname);
+}
+
+/*
+ * Assign one path to another, adjusting reference counts appropriately.
+ * Note that this requires that segments of the two paths (but not the path
+ * objects themselves) were allocated with the same allocator. Note also
+ * that since it does the equivalent of a gx_path_new(ppto), it may allocate
+ * a new segments object for ppto.
+ */
+int
+gx_path_assign_preserve(gx_path * ppto, gx_path * ppfrom)
+{
+ gx_path_segments *fromsegs = ppfrom->segments;
+ gx_path_segments *tosegs = ppto->segments;
+ gs_memory_t *mem = ppto->memory;
+ gx_path_allocation_t allocation = ppto->allocation;
+
+ if (fromsegs == &ppfrom->local_segments) {
+ /* We can't use ppfrom's segments object. */
+ if (tosegs == &ppto->local_segments || gx_path_is_shared(ppto)) {
+ /* We can't use ppto's segments either. Allocate a new one. */
+ int code = path_alloc_segments(&tosegs, ppto->memory,
+ "gx_path_assign");
+
+ if (code < 0)
+ return code;
+ rc_decrement(ppto->segments, "gx_path_assign");
+ } else {
+ /* Use ppto's segments object. */
+ rc_free_path_segments_local(tosegs->rc.memory, tosegs,
+ "gx_path_assign");
+ }
+ tosegs->contents = fromsegs->contents;
+ ppfrom->segments = tosegs;
+ rc_increment(tosegs); /* for reference from ppfrom */
+ } else {
+ /* We can use ppfrom's segments object. */
+ rc_increment(fromsegs);
+ rc_decrement(tosegs, "gx_path_assign");
+ }
+ *ppto = *ppfrom;
+ ppto->memory = mem;
+ ppto->allocation = allocation;
+ return 0;
+}
+
+/*
+ * Assign one path to another and free the first path at the same time.
+ * (This may do less work than assign_preserve + free.)
+ */
+int
+gx_path_assign_free(gx_path * ppto, gx_path * ppfrom)
+{ /*
+ * Detect the special case where both paths have non-shared local
+ * segments, since we can avoid allocating new segments in this
+ * case.
+ */
+ if (ppto->segments == &ppto->local_segments &&
+ ppfrom->segments == &ppfrom->local_segments &&
+ !gx_path_is_shared(ppto)
+ ) {
+#define fromsegs (&ppfrom->local_segments)
+#define tosegs (&ppto->local_segments)
+ gs_memory_t *mem = ppto->memory;
+ gx_path_allocation_t allocation = ppto->allocation;
+
+ rc_free_path_segments_local(tosegs->rc.memory, tosegs,
+ "gx_path_assign_free");
+ /* We record a bogus reference to fromsegs, which */
+ /* gx_path_free will undo. */
+ *ppto = *ppfrom;
+ rc_increment(fromsegs);
+ ppto->segments = tosegs;
+ ppto->memory = mem;
+ ppto->allocation = allocation;
+#undef fromsegs
+#undef tosegs
+ } else {
+ /* In all other cases, just do assign + free. */
+ int code = gx_path_assign_preserve(ppto, ppfrom);
+
+ if (code < 0)
+ return code;
+ }
+ gx_path_free(ppfrom, "gx_path_assign_free");
+ return 0;
+}
+
+/*
+ * Free the segments of a path when their reference count goes to zero.
+ * We do this in reverse order so as to maximize LIFO allocator behavior.
+ * We don't have to worry about cleaning up pointers, because we're about
+ * to free the segments object.
+ */
+private void
+rc_free_path_segments_local(gs_memory_t * mem, void *vpsegs,
+ client_name_t cname)
+{
+ gx_path_segments *psegs = (gx_path_segments *) vpsegs;
+ segment *pseg;
+
+ if (psegs->contents.subpath_first == 0)
+ return; /* empty path */
+ pseg = (segment *) psegs->contents.subpath_current->last;
+ while (pseg) {
+ segment *prev = pseg->prev;
+
+ trace_segment("[P]release", pseg);
+ gs_free_object(mem, pseg, cname);
+ pseg = prev;
+ }
+}
+private void
+rc_free_path_segments(gs_memory_t * mem, void *vpsegs, client_name_t cname)
+{
+ rc_free_path_segments_local(mem, vpsegs, cname);
+ gs_free_object(mem, vpsegs, cname);
+}
+
+/* ------ Incremental path building ------ */
+
+/* Guarantee that a path's segments are not shared with any other path. */
+#define path_unshare(ppath)\
+ BEGIN\
+ if ( gx_path_is_shared(ppath) ) {\
+ int code_;\
+ if( (code_ = path_alloc_copy(ppath)) < 0 ) return code_;\
+ }\
+ END
+
+/* Macro for opening the current subpath. */
+/* ppath points to the path; sets psub to ppath->current_subpath. */
+#define path_open()\
+ BEGIN\
+ if ( !path_is_drawing(ppath) ) {\
+ int code_;\
+ if ( !path_position_valid(ppath) )\
+ return_error(gs_error_nocurrentpoint);\
+ code_ = gx_path_new_subpath(ppath);\
+ if ( code_ < 0 ) return code_;\
+ }\
+ END
+
+/* Macros for allocating path segments. */
+/* Note that they assume that ppath points to the path. */
+/* We have to split the macro into two because of limitations */
+/* on the size of a single statement (sigh). */
+#define path_alloc_segment(pseg,ctype,pstype,stype,snotes,cname)\
+ path_unshare(ppath);\
+ psub = ppath->current_subpath;\
+ if( !(pseg = gs_alloc_struct(ppath->memory, ctype, pstype, cname)) )\
+ return_error(gs_error_VMerror);\
+ pseg->type = stype, pseg->notes = snotes, pseg->next = 0
+#define path_alloc_link(pseg)\
+ { segment *prev = psub->last;\
+ prev->next = (segment *)pseg;\
+ pseg->prev = prev;\
+ psub->last = (segment *)pseg;\
+ }
+
+/* Make a new path (newpath). */
+int
+gx_path_new(gx_path * ppath)
+{
+ gx_path_segments *psegs = ppath->segments;
+
+ if (gx_path_is_shared(ppath)) {
+ int code = path_alloc_segments(&ppath->segments, ppath->memory,
+ "gx_path_new");
+
+ if (code < 0)
+ return code;
+ rc_decrement(psegs, "gx_path_new");
+ } else {
+ rc_free_path_segments_local(psegs->rc.memory, psegs, "gx_path_new");
+ }
+ gx_path_init_contents(ppath);
+ return 0;
+}
+
+/* Open a new subpath. */
+/* The client must invoke path_update_xxx. */
+private int
+gx_path_new_subpath(gx_path * ppath)
+{
+ subpath *psub;
+ subpath *spp;
+
+ path_alloc_segment(spp, subpath, &st_subpath, s_start, sn_none,
+ "gx_path_new_subpath");
+ spp->last = (segment *) spp;
+ spp->curve_count = 0;
+ spp->is_closed = 0;
+ spp->pt = ppath->position;
+ 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(gx_path * ppath, fixed x, fixed y)
+{
+ if (ppath->bbox_set)
+ check_in_bbox(ppath, x, y);
+ ppath->position.x = x;
+ ppath->position.y = y;
+ path_update_moveto(ppath);
+ return 0;
+}
+
+/* Add a relative point to the current path (rmoveto). */
+int
+gx_path_add_relative_point(gx_path * ppath, fixed dx, fixed dy)
+{
+ if (!path_position_in_range(ppath))
+ return_error((path_position_valid(ppath) ? gs_error_limitcheck :
+ gs_error_nocurrentpoint));
+ {
+ fixed nx = ppath->position.x + dx, ny = ppath->position.y + dy;
+
+ /* Check for overflow in addition. */
+ if (((nx ^ dx) < 0 && (ppath->position.x ^ dx) >= 0) ||
+ ((ny ^ dy) < 0 && (ppath->position.y ^ dy) >= 0)
+ )
+ return_error(gs_error_limitcheck);
+ if (ppath->bbox_set)
+ check_in_bbox(ppath, nx, ny);
+ ppath->position.x = nx;
+ ppath->position.y = ny;
+ }
+ path_update_moveto(ppath);
+ 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_notes(gx_path * ppath, fixed x, fixed y, segment_notes notes)
+{
+ subpath *psub;
+ 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, notes,
+ "gx_path_add_line");
+ path_alloc_link(lp);
+ path_set_point(lp, x, y);
+ path_update_draw(ppath);
+ trace_segment("[P]", (segment *) lp);
+ return 0;
+}
+
+/* Add multiple lines to the current path. */
+/* Note that all lines have the same notes. */
+int
+gx_path_add_lines_notes(gx_path * ppath, const gs_fixed_point * ppts, int count,
+ segment_notes notes)
+{
+ subpath *psub;
+ segment *prev;
+ line_segment *lp = 0;
+ int i;
+ int code = 0;
+
+ if (count <= 0)
+ return 0;
+ path_unshare(ppath);
+ path_open();
+ psub = ppath->current_subpath;
+ 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;
+ lp->type = s_line;
+ lp->notes = notes;
+ 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,
+ path_update_draw(ppath);
+ 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_notes(gx_path * ppath,
+ fixed x1, fixed y1, fixed x2, fixed y2, fixed x3, fixed y3,
+ segment_notes notes)
+{
+ subpath *psub;
+ 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, notes,
+ "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++;
+ path_update_draw(ppath);
+ 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_notes(gx_path * ppath,
+fixed x3, fixed y3, fixed xt, fixed yt, floatp fraction, segment_notes notes)
+{
+ fixed x0 = ppath->position.x, y0 = ppath->position.y;
+
+ return gx_path_add_curve_notes(ppath,
+ x0 + (fixed) ((xt - x0) * fraction),
+ y0 + (fixed) ((yt - y0) * fraction),
+ x3 + (fixed) ((xt - x3) * fraction),
+ y3 + (fixed) ((yt - y3) * fraction),
+ x3, y3, notes | sn_from_arc);
+}
+
+/* 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)
+{
+ 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->outside_position = ppfrom->outside_position;
+ ppath->state_flags = ppfrom->state_flags;
+ /* Reset the source path. */
+ gx_path_init_contents(ppfrom);
+ 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)
+{
+ int code;
+ gs_fixed_rect bbox;
+
+ switch (mode) {
+ default: /* shouldn't happen! */
+ gx_path_new(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:
+ gx_path_bbox(from_path, &bbox);
+ code = gx_path_add_rectangle(to_path, bbox.p.x, bbox.p.y,
+ bbox.q.x, bbox.q.y);
+ break;
+ case cpm_false_charboxpath:
+ gx_path_bbox(from_path, &bbox);
+ code = gx_path_add_point(to_path, bbox.p.x, bbox.p.y);
+ if (code >= 0)
+ code = gx_path_add_line(to_path, bbox.q.x, bbox.q.y);
+ break;
+ }
+ if (code < 0)
+ return code;
+ gx_path_new(from_path);
+ return 0;
+}
+
+/* Close the current subpath. */
+int
+gx_path_close_subpath_notes(gx_path * ppath, segment_notes notes)
+{
+ subpath *psub;
+ line_close_segment *lp;
+ int code;
+
+ if (!path_subpath_open(ppath))
+ return 0;
+ if (path_last_is_moveto(ppath)) { /* The last operation was a moveto: create a subpath. */
+ code = gx_path_new_subpath(ppath);
+ if (code < 0)
+ return code;
+ }
+ path_alloc_segment(lp, line_close_segment, &st_line_close,
+ s_line_close, notes, "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;
+ path_update_closepath(ppath);
+ 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_notes(gx_path * ppath, segment_notes notes)
+{
+ 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_notes(ppath, notes);
+}
+
+/* ------ Internal routines ------ */
+
+/*
+ * Copy the current path, because it was shared.
+ */
+private int
+path_alloc_copy(gx_path * ppath)
+{
+ gx_path path_new;
+ int code;
+
+ gx_path_init_local(&path_new, ppath->memory);
+ code = gx_path_copy(ppath, &path_new);
+ if (code < 0) {
+ gx_path_free(&path_new, "path_alloc_copy error");
+ return code;
+ }
+ return gx_path_assign_free(ppath, &path_new);
+}
+
+/* ------ Debugging printout ------ */
+
+#ifdef DEBUG
+
+/* Print out a path with a label */
+void
+gx_dump_path(const gx_path * ppath, const char *tag)
+{
+ dlprintf2("[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;
+
+ dlprintf5(" state_flags=%d subpaths=%d, curves=%d, point=(%f,%f)\n",
+ ppath->state_flags, ppath->subpath_count, ppath->curve_count,
+ fixed2float(ppath->position.x),
+ fixed2float(ppath->position.y));
+ dlprintf5(" 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);
+ dlprintf4(" segments=0x%lx (refct=%ld, first=0x%lx, current=0x%lx)\n",
+ (ulong) ppath->segments, (long)ppath->segments->rc.ref_count,
+ (ulong) ppath->segments->contents.subpath_first,
+ (ulong) ppath->segments->contents.subpath_current);
+ while (pseg) {
+ dlputs("");
+ gx_print_segment(pseg);
+ pseg = pseg->next;
+ }
+}
+private void
+gx_print_segment(const segment * pseg)
+{
+ double px = fixed2float(pseg->pt.x);
+ double py = fixed2float(pseg->pt.y);
+ char out[80];
+
+ sprintf(out, " 0x%lx<0x%lx,0x%lx>:%u",
+ (ulong) pseg, (ulong) pseg->prev, (ulong) pseg->next, pseg->notes);
+ switch (pseg->type) {
+ case s_start:{
+ const subpath *const psub = (const subpath *)pseg;
+
+ dprintf5("%s: %1.4f %1.4f moveto\t%% #curves=%d last=0x%lx\n",
+ out, px, py, psub->curve_count, (ulong) psub->last);
+ break;
+ }
+ case s_curve:{
+ const curve_segment *const pcur = (const curve_segment *)pseg;
+
+ dprintf7("%s: %1.4f %1.4f %1.4f %1.4f %1.4f %1.4f curveto\n",
+ out, fixed2float(pcur->p1.x), fixed2float(pcur->p1.y),
+ fixed2float(pcur->p2.x), fixed2float(pcur->p2.y), px, py);
+ break;
+ }
+ case s_line:
+ dprintf3("%s: %1.4f %1.4f lineto\n", out, px, py);
+ break;
+ case s_line_close:{
+ const line_close_segment *const plc =
+ (const line_close_segment *)pseg;
+
+ dprintf4("%s: closepath\t%% %1.4f %1.4f 0x%lx\n",
+ out, px, py, (ulong) (plc->sub));
+ break;
+ }
+ default:
+ dprintf4("%s: %1.4f %1.4f <type 0x%x>\n", out, px, py, pseg->type);
+ }
+}
+
+#endif /* DEBUG */
diff --git a/pstoraster/gxpath.h b/pstoraster/gxpath.h
new file mode 100644
index 000000000..88516e103
--- /dev/null
+++ b/pstoraster/gxpath.h
@@ -0,0 +1,317 @@
+/* Copyright (C) 1989, 1995, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Requires gxfixed.h */
+
+#ifndef gxpath_INCLUDED
+# define gxpath_INCLUDED
+
+#include "gscpm.h"
+#include "gslparam.h"
+#include "gspenum.h"
+#include "gsrect.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
+
+/* Define 'notes' that describe the role of a path segment. */
+/* These are only for internal use; a normal segment's notes are 0. */
+typedef enum {
+ sn_none = 0,
+ sn_not_first = 1, /* segment is in curve/arc and not first */
+ sn_from_arc = 2 /* segment is part of an arc */
+} segment_notes;
+
+/* Debugging routines */
+#ifdef DEBUG
+void gx_dump_path(P2(const gx_path *, const char *));
+void gx_path_print(P1(const gx_path *));
+
+#endif
+
+/* Path memory management */
+
+/*
+ * Path memory management is unfortunately a little tricky. The
+ * implementation details are in gzpath.h: we only present the API here.
+ *
+ * Path objects per se may be allocated in 3 different ways: on the
+ * C stack, as separate objects in the heap, or (for the graphics state
+ * only) contained in a larger heap-allocated object.
+ *
+ * Any number of paths may share segments. The segments are stored in
+ * their own, reference-counted object, and are freed when there are no
+ * more references to that object.
+ */
+
+/*
+ * Allocate a path on the heap, and initialize it. If shared is NULL,
+ * allocate a segments object; if shared is an existing path, share its
+ * segments.
+ */
+gx_path *gx_path_alloc_shared(P3(const gx_path * shared, gs_memory_t * mem,
+ client_name_t cname));
+
+#define gx_path_alloc(mem, cname)\
+ gx_path_alloc_shared(NULL, mem, cname)
+/*
+ * Initialize a path contained in an already-heap-allocated object,
+ * optionally allocating its segments.
+ */
+int gx_path_init_contained_shared(P4(gx_path * ppath, const gx_path * shared,
+ gs_memory_t * mem, client_name_t cname));
+
+#define gx_path_alloc_contained(ppath, mem, cname)\
+ gx_path_init_contained_shared(ppath, NULL, mem, cname)
+/*
+ * Initialize a stack-allocated path. This doesn't allocate anything,
+ * but may still share the segments. Note that it returns an error if
+ * asked to share the segments of another local path.
+ */
+int gx_path_init_local_shared(P3(gx_path * ppath, const gx_path * shared,
+ gs_memory_t * mem));
+
+#define gx_path_init_local(ppath, mem)\
+ (void)gx_path_init_local_shared(ppath, NULL, mem) /* can't fail */
+
+/*
+ * Ensure that a path owns its segments, by copying the segments if
+ * they currently have multiple references.
+ */
+int gx_path_unshare(P1(gx_path * ppath));
+
+/*
+ * Free a path by releasing its segments if they have no more references.
+ * This also frees the path object iff it was allocated by gx_path_alloc.
+ */
+void gx_path_free(P2(gx_path * ppath, client_name_t cname));
+
+/*
+ * Assign one path to another, adjusting reference counts appropriately.
+ * Note that this requires that segments of the two paths (but not the path
+ * objects themselves) were allocated with the same allocator. Note also
+ * that if ppfrom is stack-allocated, ppto is not, and ppto's segments are
+ * currently shared, gx_path_assign must do the equivalent of a
+ * gx_path_new(ppto), which allocates a new segments object for ppto.
+ */
+int gx_path_assign_preserve(P2(gx_path * ppto, gx_path * ppfrom));
+
+/*
+ * Assign one path to another and free the first path at the same time.
+ * (This may do less work than assign_preserve + free.)
+ */
+int gx_path_assign_free(P2(gx_path * ppto, gx_path * ppfrom));
+
+/* Path constructors */
+/* Note that all path constructors have an implicit initial gx_path_unshare. */
+
+int gx_path_new(P1(gx_path *)),
+ gx_path_add_point(P3(gx_path *, fixed, fixed)),
+ gx_path_add_relative_point(P3(gx_path *, fixed, fixed)),
+ gx_path_add_line_notes(P4(gx_path *, fixed, fixed, segment_notes)),
+ gx_path_add_lines_notes(P4(gx_path *, const gs_fixed_point *, int, segment_notes)),
+ 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_notes(P8(gx_path *, fixed, fixed, fixed, fixed, fixed, fixed, segment_notes)),
+ gx_path_add_partial_arc_notes(P7(gx_path *, fixed, fixed, fixed, fixed, floatp, segment_notes)),
+ gx_path_add_path(P2(gx_path *, gx_path *)),
+ gx_path_close_subpath_notes(P2(gx_path *, segment_notes)),
+ /* We have to remove the 'subpath' from the following name */
+ /* to keep it unique in the first 23 characters. */
+ gx_path_pop_close_notes(P2(gx_path *, segment_notes));
+
+/* 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
+/*
+ * Backward-compatible constructors that don't take a notes argument.
+ */
+#define gx_path_add_line(ppath, x, y)\
+ gx_path_add_line_notes(ppath, x, y, sn_none)
+#define gx_path_add_lines(ppath, pts, count)\
+ gx_path_add_lines_notes(ppath, pts, count, sn_none)
+#define gx_path_add_curve(ppath, x1, y1, x2, y2, x3, y3)\
+ gx_path_add_curve_notes(ppath, x1, y1, x2, y2, x3, y3, sn_none)
+#define gx_path_add_partial_arc(ppath, x3, y3, xt, yt, fraction)\
+ gx_path_add_partial_arc_notes(ppath, x3, y3, xt, yt, fraction, sn_none)
+#define gx_path_close_subpath(ppath)\
+ gx_path_close_subpath_notes(ppath, sn_none)
+#define gx_path_pop_close_subpath(ppath)\
+ gx_path_pop_close_notes(ppath, sn_none)
+
+/* Path accessors */
+
+gx_path *gx_current_path(P1(const gs_state *));
+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_monotonic(P1(const gx_path *));
+typedef enum {
+ prt_none = 0,
+ prt_open = 1, /* only 3 sides */
+ prt_fake_closed = 2, /* 4 lines, no closepath */
+ prt_closed = 3 /* 3 or 4 lines + closepath */
+} gx_path_rectangular_type;
+
+gx_path_rectangular_type
+gx_path_is_rectangular(P2(const gx_path *, gs_fixed_rect *));
+
+#define gx_path_is_rectangle(ppath, pbox)\
+ (gx_path_is_rectangular(ppath, pbox) != prt_none)
+/* Inline versions of the above */
+#define gx_path_is_null_inline(ppath)\
+ (gx_path_is_void(ppath) && !path_position_valid(ppath))
+
+/* Path transformers */
+
+/* gx_path_copy_reducing is internal. */
+typedef enum {
+ pco_none = 0,
+ pco_monotonize = 1, /* make curves monotonic */
+ pco_accurate = 2 /* flatten with accurate tangents at ends */
+} gx_path_copy_options;
+int gx_path_copy_reducing(P4(const gx_path * ppath_old, gx_path * ppath_new,
+ fixed fixed_flatness,
+ gx_path_copy_options options));
+
+#define gx_path_copy(old, new)\
+ gx_path_copy_reducing(old, new, max_fixed, pco_none)
+#define gx_path_add_flattened(old, new, flatness)\
+ gx_path_copy_reducing(old, new, float2fixed(flatness), pco_none)
+#define gx_path_add_flattened_accurate(old, new, flatness, accurate)\
+ gx_path_copy_reducing(old, new, float2fixed(flatness),\
+ (accurate ? pco_accurate : pco_none))
+#define gx_path_add_monotonized(old, new)\
+ gx_path_copy_reducing(old, new, max_fixed, pco_monotonize)
+int gx_path_add_dash_expansion(P3(const gx_path * /*old */ , gx_path * /*new */ , const gs_imager_state *)),
+ gx_path_copy_reversed(P2(const gx_path * /*old */ , gx_path * /*new */ )),
+ 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 */
+
+segment_notes
+gx_path_enum_notes(P1(const gs_path_enum *));
+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
+
+/* Graphics state clipping */
+int gx_clip_to_rectangle(P2(gs_state *, gs_fixed_rect *));
+int gx_clip_to_path(P1(gs_state *));
+int gx_default_clip_box(P2(const gs_state *, gs_fixed_rect *));
+int gx_effective_clip_path(P2(gs_state *, gx_clip_path **));
+
+/* 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
+
+/* Opaque type for a clipping path enumerator. */
+typedef struct gs_cpath_enum_s gs_cpath_enum;
+
+/*
+ * Provide similar memory management for clip paths to what we have for
+ * paths (see above for details).
+ */
+gx_clip_path *gx_cpath_alloc_shared(P3(const gx_clip_path * shared,
+ gs_memory_t * mem,
+ client_name_t cname));
+
+#define gx_cpath_alloc(mem, cname)\
+ gx_cpath_alloc_shared(NULL, mem, cname)
+int gx_cpath_init_contained_shared(P4(gx_clip_path * pcpath,
+ const gx_clip_path * shared,
+ gs_memory_t * mem,
+ client_name_t cname));
+
+#define gx_cpath_alloc_contained(pcpath, mem, cname)\
+ gx_cpath_init_contained_shared(pcpath, NULL, mem, cname)
+int gx_cpath_init_local_shared(P3(gx_clip_path * pcpath,
+ const gx_clip_path * shared,
+ gs_memory_t * mem));
+
+#define gx_cpath_init_local(pcpath, mem)\
+ (void)gx_cpath_init_local_shared(pcpath, NULL, mem) /* can't fail */
+int gx_cpath_unshare(P1(gx_clip_path * pcpath));
+void gx_cpath_free(P2(gx_clip_path * pcpath, client_name_t cname));
+int gx_cpath_assign_preserve(P2(gx_clip_path * pcpto, gx_clip_path * pcpfrom));
+int gx_cpath_assign_free(P2(gx_clip_path * pcpto, gx_clip_path * pcpfrom));
+
+/* Clip path constructors and accessors */
+
+int
+ gx_cpath_reset(P1(gx_clip_path *)), /* from_rectangle ((0,0),(0,0)) */
+ gx_cpath_from_rectangle(P2(gx_clip_path *, gs_fixed_rect *)),
+ gx_cpath_clip(P4(gs_state *, gx_clip_path *, gx_path *, int)),
+ gx_cpath_scale_exp2(P3(gx_clip_path *, int, int)),
+ gx_cpath_to_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 *));
+
+/* Enumerate a clipping path. This interface does not copy the path. */
+/* However, it does write into the path's "visited" flags. */
+int gx_cpath_enum_init(P2(gs_cpath_enum *, gx_clip_path *));
+int gx_cpath_enum_next(P2(gs_cpath_enum *, gs_fixed_point[3])); /* 0 when done */
+
+segment_notes
+gx_cpath_enum_notes(P1(const gs_cpath_enum *));
+
+#endif /* gxpath_INCLUDED */
diff --git a/pstoraster/gxpath2.c b/pstoraster/gxpath2.c
new file mode 100644
index 000000000..a033e7151
--- /dev/null
+++ b/pstoraster/gxpath2.c
@@ -0,0 +1,487 @@
+/* Copyright (C) 1989, 1995, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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. */
+public_st_path_enum();
+
+/* Read the current point of a path. */
+int
+gx_path_current_point(const gx_path * ppath, gs_fixed_point * ppt)
+{
+ if (!path_position_valid(ppath))
+ 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. */
+ int code = gx_path_current_point(ppath, &pbox->p);
+
+ if (code < 0) { /*
+ * Don't return garbage, in case the caller doesn't
+ * check the return code.
+ */
+ pbox->p.x = pbox->p.y = 0;
+ }
+ pbox->q = pbox->p;
+ return code;
+ }
+ /* 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;
+ const segment *pseg = ppath->box_last;
+
+ if (pseg == 0) { /* box is uninitialized */
+ pseg = (const 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 ((const 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. */
+#undef gx_path_has_curves
+bool
+gx_path_has_curves(const gx_path * ppath)
+{
+ return gx_path_has_curves_inline(ppath);
+}
+#define gx_path_has_curves(ppath)\
+ gx_path_has_curves_inline(ppath)
+
+/* Test if a path has no segments. */
+#undef gx_path_is_void
+bool
+gx_path_is_void(const gx_path * ppath)
+{
+ return gx_path_is_void_inline(ppath);
+}
+#define gx_path_is_void(ppath)\
+ gx_path_is_void_inline(ppath)
+
+/* Test if a path has no elements at all. */
+bool
+gx_path_is_null(const gx_path * ppath)
+{
+ return gx_path_is_null_inline(ppath);
+}
+
+/*
+ * Test if a subpath 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).
+ */
+gx_path_rectangular_type
+gx_subpath_is_rectangular(const subpath * pseg0, gs_fixed_rect * pbox,
+ const subpath ** ppnext)
+{
+ const segment *pseg1, *pseg2, *pseg3, *pseg4;
+ gx_path_rectangular_type type;
+
+ if (pseg0->curve_count == 0 &&
+ (pseg1 = pseg0->next) != 0 &&
+ (pseg2 = pseg1->next) != 0 &&
+ (pseg3 = pseg2->next) != 0
+ ) {
+ if ((pseg4 = pseg3->next) == 0 || pseg4->type == s_start)
+ type = prt_open; /* M, L, L, L */
+ else if (pseg4->type != s_line) /* must be s_line_close */
+ type = prt_closed; /* M, L, L, L, C */
+ else if (pseg4->pt.x != pseg0->pt.x ||
+ pseg4->pt.y != pseg0->pt.y
+ )
+ return prt_none;
+ else if (pseg4->next == 0 || pseg4->next->type == s_start)
+ type = prt_fake_closed; /* Mo, L, L, L, Lo */
+ else if (pseg4->next->type != s_line) /* must be s_line_close */
+ type = prt_closed; /* Mo, L, L, L, Lo, C */
+ else
+ return prt_none;
+ {
+ 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 type;
+ }
+ }
+ }
+ return prt_none;
+}
+/* Test if an entire path to be filled is a rectangle. */
+gx_path_rectangular_type
+gx_path_is_rectangular(const gx_path * ppath, gs_fixed_rect * pbox)
+{
+ const subpath *pnext;
+
+ return
+ (gx_path_subpath_count(ppath) == 1 ?
+ gx_subpath_is_rectangular(ppath->first_subpath, pbox, &pnext) :
+ prt_none);
+}
+
+/* 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 (path_position_valid(ppath))
+ 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.
+ * NOTE: in releases 5.01 and earlier, the implicit line added by closepath
+ * became the first segment of the reversed path. Starting in release
+ * 5.02, the code follows the Adobe implementation, in which this line
+ * becomes the *last* segment of the reversed path. This can produce some
+ * quite counter-intuitive results.
+ */
+int
+gx_path_copy_reversed(const gx_path * ppath_old, gx_path * ppath)
+{
+ const subpath *psub = ppath_old->first_subpath;
+ int code;
+
+#ifdef DEBUG
+ if (gs_debug_c('P'))
+ gx_dump_path(ppath_old, "before reversepath");
+#endif
+ nsp:while (psub) {
+ const segment *pseg = psub->last;
+ const segment *prev;
+ segment_notes prev_notes =
+ (pseg == (const segment *)psub ? sn_none :
+ psub->next->notes);
+ segment_notes notes;
+
+ if (!psub->is_closed) {
+ code = gx_path_add_point(ppath, pseg->pt.x, pseg->pt.y);
+ if (code < 0)
+ return code;
+ }
+ for (;; pseg = prev, prev_notes = notes) {
+ prev = pseg->prev;
+ notes = pseg->notes;
+ prev_notes = (prev_notes & sn_not_first) |
+ (notes & ~sn_not_first);
+ switch (pseg->type) {
+ case s_start:
+ /* Finished subpath */
+ if (psub->is_closed) {
+ code =
+ gx_path_close_subpath_notes(ppath,
+ prev_notes);
+ if (code < 0)
+ return code;
+ }
+ psub = (const subpath *)psub->last->next;
+ goto nsp;
+ case s_curve:
+ {
+ const curve_segment *pc =
+ (const curve_segment *)pseg;
+
+ code = gx_path_add_curve_notes(ppath,
+ pc->p2.x, pc->p2.y,
+ pc->p1.x, pc->p1.y,
+ prev->pt.x, prev->pt.y, prev_notes);
+ break;
+ }
+ case s_line:
+ code = gx_path_add_line_notes(ppath,
+ prev->pt.x, prev->pt.y, prev_notes);
+ break;
+ case s_line_close:
+ /* Skip the closing line. */
+ code = gx_path_add_point(ppath, prev->pt.x,
+ prev->pt.y);
+ break;
+ }
+ if (code < 0)
+ return code;
+ }
+ /* not reached */
+ }
+#undef sn_not_end
+ if (ppath_old->first_subpath == 0 &&
+ path_last_is_moveto(ppath_old)
+ ) { /* The path consists only of a single moveto. */
+ code = gx_path_add_point(ppath, ppath_old->position.x,
+ ppath_old->position.y);
+ if (code < 0)
+ return code;
+ }
+#ifdef DEBUG
+ if (gs_debug_c('P'))
+ gx_dump_path(ppath, "after reversepath");
+#endif
+ return 0;
+}
+
+/* ------ 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_path_enum, cname);
+}
+
+/* Start enumerating a path. */
+int
+gx_path_enum_init(gs_path_enum * penum, const gx_path * ppath)
+{
+ penum->memory = 0; /* path not copied */
+ penum->path = ppath;
+ penum->copied_path = 0; /* not copied */
+ penum->pseg = (const segment *)ppath->first_subpath;
+ penum->moveto_done = false;
+ penum->notes = sn_none;
+ 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 (path_last_is_moveto(ppath) && !penum->moveto_done) { /* Handle a trailing moveto */
+ penum->moveto_done = true;
+ penum->notes = sn_none;
+ ppts[0] = ppath->position;
+ return gs_pe_moveto;
+ }
+ return 0;
+ }
+ penum->pseg = pseg->next;
+ penum->notes = pseg->notes;
+ 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);
+ }
+}
+
+/* Return the notes from the last-enumerated segment. */
+segment_notes
+gx_path_enum_notes(const gs_path_enum * penum)
+{
+ return penum->notes;
+}
+
+/* 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 (path_last_is_moveto(ppath) && 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/gxpcache.h b/pstoraster/gxpcache.h
new file mode 100644
index 000000000..b4f81a65e
--- /dev/null
+++ b/pstoraster/gxpcache.h
@@ -0,0 +1,61 @@
+/* Copyright (C) 1997 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Definition of Pattern cache */
+
+#ifndef gxpcache_INCLUDED
+# define gxpcache_INCLUDED
+
+/*
+ * Define a cache for rendered Patterns. This is currently an open
+ * hash table with single probing (no reprobing) and round-robin
+ * replacement. Obviously, we can do better in both areas.
+ */
+#ifndef gx_pattern_cache_DEFINED
+# define gx_pattern_cache_DEFINED
+typedef struct gx_pattern_cache_s gx_pattern_cache;
+
+#endif
+#ifndef gx_color_tile_DEFINED
+# define gx_color_tile_DEFINED
+typedef struct gx_color_tile_s gx_color_tile;
+
+#endif
+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;
+ void (*free_all) (P1(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)
+
+#endif /* gxpcache_INCLUDED */
diff --git a/pstoraster/gxpcmap.c b/pstoraster/gxpcmap.c
new file mode 100644
index 000000000..e43530af5
--- /dev/null
+++ b/pstoraster/gxpcmap.c
@@ -0,0 +1,666 @@
+/* Copyright (C) 1993, 1995, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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"
+#include "gzpath.h" /* for tweaking sharing flags */
+#include "gzcpath.h" /* ditto */
+
+/* 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_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_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);
+private dev_proc_get_bits_rectangle(pattern_accum_get_bits_rectangle);
+
+/* 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),
+ { /*
+ * NOTE: all drawing procedures must be defaulted,
+ * not forwarded.
+ */
+ pattern_accum_open,
+ NULL,
+ NULL,
+ NULL,
+ pattern_accum_close,
+ NULL,
+ NULL,
+ pattern_accum_fill_rectangle,
+ gx_default_tile_rectangle,
+ pattern_accum_copy_mono,
+ pattern_accum_copy_color,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ gx_default_copy_alpha,
+ NULL,
+ 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,
+ gx_default_strip_copy_rop,
+ gx_get_largest_clipping_box,
+ gx_default_begin_typed_image,
+ pattern_accum_get_bits_rectangle,
+ NULL,
+ NULL,
+ NULL,
+ gx_default_text_begin
+ },
+ 0, /* target */
+ 0, 0, 0, 0 /* bitmap_memory, bits, mask, instance */
+};
+
+/* Allocate a pattern accumulator, with an initial refct of 0. */
+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;
+ gx_device_init((gx_device *) adev,
+ (const gx_device *)&gs_pattern_accum_device,
+ mem, true);
+ 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.
+ *
+ * Note that mask and bits accumulators are only created if necessary.
+ */
+private int
+pattern_accum_open(gx_device * dev)
+{
+ gx_device_pattern_accum *const padev = (gx_device_pattern_accum *) dev;
+ const gs_pattern_instance *pinst = padev->instance;
+ gs_memory_t *mem = padev->bitmap_memory;
+ gx_device_memory *mask = 0;
+ gx_device_memory *bits = 0;
+ /*
+ * The client should preset the target, because the device for which the
+ * pattern is being rendered may not (in general, will not) be the same
+ * as the one that was current when the pattern was instantiated.
+ */
+ gx_device *target =
+ (padev->target == 0 ? gs_currentdevice(pinst->saved) :
+ padev->target);
+ int width = pinst->size.x;
+ int height = pinst->size.y;
+ int code = 0;
+ bool mask_open = false;
+
+#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;
+
+ if (pinst->uses_mask) {
+ mask = gs_alloc_struct( mem,
+ gx_device_memory,
+ &st_device_memory,
+ "pattern_accum_open(mask)"
+ );
+ if (mask == 0)
+ return_error(gs_error_VMerror);
+ 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) {
+ mask_open = true;
+ memset(mask->base, 0, mask->raster * mask->height);
+ }
+ }
+
+ if (code >= 0) {
+ 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)
+ code = gs_note_error(gs_error_VMerror);
+ else {
+ 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)");
+ if (mask != 0) {
+ if (mask_open)
+ (*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)
+{
+ gx_device_pattern_accum *const padev = (gx_device_pattern_accum *) 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;
+ }
+ if (padev->mask != 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)
+{
+ gx_device_pattern_accum *const padev = (gx_device_pattern_accum *) dev;
+
+ if (padev->bits)
+ (*dev_proc(padev->target, fill_rectangle))
+ (padev->target, x, y, w, h, color);
+ if (padev->mask)
+ return (*dev_proc(padev->mask, fill_rectangle))
+ ((gx_device *) padev->mask, x, y, w, h, (gx_color_index) 1);
+ else
+ return 0;
+}
+
+/* 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)
+{
+ gx_device_pattern_accum *const padev = (gx_device_pattern_accum *) dev;
+
+ if (padev->bits)
+ (*dev_proc(padev->target, copy_mono))
+ (padev->target, data, data_x, raster, id, x, y, w, h,
+ color0, color1);
+ if (padev->mask) {
+ 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);
+ } else
+ return 0;
+}
+
+/* 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)
+{
+ gx_device_pattern_accum *const padev = (gx_device_pattern_accum *) dev;
+
+ if (padev->bits)
+ (*dev_proc(padev->target, copy_color))
+ (padev->target, data, data_x, raster, id, x, y, w, h);
+ if (padev->mask)
+ return (*dev_proc(padev->mask, fill_rectangle))
+ ((gx_device *) padev->mask, x, y, w, h, (gx_color_index) 1);
+ else
+ return 0;
+}
+
+/* Read back a rectangle of bits. */
+/****** SHOULD USE MASK TO DEFINE UNREAD AREA *****/
+private int
+pattern_accum_get_bits_rectangle(gx_device * dev, const gs_int_rect * prect,
+ gs_get_bits_params_t * params, gs_int_rect ** unread)
+{
+ gx_device_pattern_accum *const padev = (gx_device_pattern_accum *) dev;
+
+ return (*dev_proc(padev->target, get_bits_rectangle))
+ (padev->target, prect, params, unread);
+}
+
+/* ------ Color space implementation ------ */
+
+/* Free all entries in a pattern cache. */
+private bool
+pattern_cache_choose_all(gx_color_tile * ctile, void *proc_data)
+{
+ return true;
+}
+private void
+pattern_cache_free_all(gx_pattern_cache * pcache)
+{
+ gx_pattern_cache_winnow(pcache, pattern_cache_choose_all, NULL);
+}
+
+/* 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;
+ pcache->free_all = pattern_cache_free_all;
+ for (i = 0; i < num_tiles; tiles++, i++) {
+ tiles->id = gx_no_bitmap_id;
+ /* Clear the pointers to pacify the GC. */
+ uid_set_invalid(&tiles->uid);
+ tiles->tbits.data = 0;
+ tiles->tmask.data = 0;
+ tiles->index = i;
+ }
+ return pcache;
+}
+/* Ensure that an imager has a Pattern cache. */
+private int
+ensure_pattern_cache(gs_imager_state * pis)
+{
+ if (pis->pattern_cache == 0) {
+ gx_pattern_cache *pcache =
+ gx_pattern_alloc_cache(pis->memory,
+ gx_pat_cache_default_tiles(),
+ gx_pat_cache_default_bits());
+
+ if (pcache == 0)
+ return_error(gs_error_VMerror);
+ pis->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. */
+/* Note that this does not free any of the data in the accumulator */
+/* device, but it may zero out the bitmap_memory pointers to prevent */
+/* the accumulated bitmaps from being freed when the device is closed. */
+private void make_bitmap(P3(gx_strip_bitmap *, const gx_device_memory *, gx_bitmap_id));
+int
+gx_pattern_cache_add_entry(gs_imager_state * pis,
+ gx_device_pattern_accum * padev, gx_color_tile ** pctile)
+{
+ gx_device_memory *mbits = padev->bits;
+ 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(pis);
+
+ if (code < 0)
+ return code;
+ pcache = pis->pattern_cache;
+ /*
+ * Check whether the pattern completely fills its box.
+ * If so, we can avoid the expensive masking operations
+ * when using the pattern.
+ */
+ if (mmask != 0) {
+ 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->uid = pinst->template.uid;
+ ctile->tiling_type = pinst->template.TilingType;
+ ctile->step_matrix = pinst->step_matrix;
+ ctile->bbox = pinst->bbox;
+ ctile->is_simple = pinst->is_simple;
+ if (mbits != 0) {
+ make_bitmap(&ctile->tbits, mbits, gs_next_ids(1));
+ mbits->bitmap_memory = 0; /* don't free the bits */
+ } else
+ ctile->tbits.data = 0;
+ if (mmask != 0) {
+ make_bitmap(&ctile->tmask, mmask, id);
+ mmask->bitmap_memory = 0; /* don't free the bits */
+ } else
+ ctile->tmask.data = 0;
+ 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;
+}
+
+/* Purge selected entries from the pattern cache. */
+void
+gx_pattern_cache_winnow(gx_pattern_cache * pcache,
+ bool(*proc) (P2(gx_color_tile * ctile, void *proc_data)), void *proc_data)
+{
+ uint i;
+
+ if (pcache == 0) /* no cache created yet */
+ return;
+ for (i = 0; i < pcache->num_tiles; ++i) {
+ gx_color_tile *ctile = &pcache->tiles[i];
+
+ if (ctile->id != gx_no_bitmap_id && (*proc) (ctile, proc_data))
+ gx_pattern_cache_free_entry(pcache, ctile);
+ }
+}
+
+/* Reload a (non-null) Pattern color into the cache. */
+/* *pdc is already set, except for colors.pattern.p_tile and mask.m_tile. */
+int
+gx_pattern_load(gx_device_color * pdc, const gs_imager_state * pis,
+ gx_device * dev, gs_color_select_t select)
+{
+ gx_device_pattern_accum adev;
+ gs_pattern_instance *pinst = pdc->mask.ccolor.pattern;
+ gs_state *saved;
+ gx_color_tile *ctile;
+ gs_memory_t *mem = pis->memory;
+ int code;
+
+ if (gx_pattern_cache_lookup(pdc, pis, dev, select))
+ return 0;
+ /* We REALLY don't like the following cast.... */
+ code = ensure_pattern_cache((gs_imager_state *) pis);
+ if (code < 0)
+ return code;
+ gx_device_init((gx_device *) & adev,
+ (const gx_device *)&gs_pattern_accum_device,
+ NULL, true);
+ gx_device_forward_fill_in_procs((gx_device_forward *) & adev); /* (should only do once) */
+ adev.target = dev;
+ 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 = pis->pattern_cache;
+ gx_set_device_only(saved, (gx_device *) & adev);
+ code = (*pinst->template.PaintProc) (&pdc->mask.ccolor, saved);
+ if (code < 0) {
+ (*dev_proc(&adev, close_device)) ((gx_device *) & adev);
+ gs_state_free(saved);
+ return code;
+ }
+ /* We REALLY don't like the following cast.... */
+ code = gx_pattern_cache_add_entry((gs_imager_state *) pis, &adev,
+ &ctile);
+ if (code >= 0) {
+ if (!gx_pattern_cache_lookup(pdc, pis, dev, select)) {
+ lprintf("Pattern cache lookup failed after insertion!\n");
+ code = gs_note_error(gs_error_Fatal);
+ }
+ }
+ /* Free the bookkeeping structures, except for the bits and mask */
+ /* iff they are still needed. */
+ (*dev_proc(&adev, close_device)) ((gx_device *) & adev);
+#ifdef DEBUG
+ if (gs_debug_c('B')) {
+ if (adev.mask)
+ 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
+ gs_state_free(saved);
+ return code;
+}
+
+/* Remap a Pattern color. */
+cs_proc_remap_color(gx_remap_Pattern); /* check the prototype */
+int
+gx_remap_Pattern(const gs_client_color * pc, const gs_color_space * pcs,
+ gx_device_color * pdc, const gs_imager_state * pis, gx_device * dev,
+ gs_color_select_t select)
+{
+ gs_pattern_instance *pinst = pc->pattern;
+ int code;
+
+ pdc->mask.ccolor = *pc;
+ if (pinst == 0) {
+ /* Null pattern */
+ color_set_null_pattern(pdc);
+ 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, pis, dev, select);
+ if (code < 0)
+ return code;
+ if (pdc->type == gx_dc_type_pure)
+ pdc->type = &gx_dc_pure_masked;
+ else if (pdc->type == gx_dc_type_ht_binary)
+ pdc->type = &gx_dc_binary_masked;
+ else if (pdc->type == gx_dc_type_ht_colored)
+ pdc->type = &gx_dc_colored_masked;
+ else
+ return_error(gs_error_unregistered);
+ } else
+ color_set_null_pattern(pdc);
+ pdc->mask.id = pinst->id;
+ pdc->mask.m_tile = 0;
+ return gx_pattern_load(pdc, pis, dev, select);
+}
diff --git a/pstoraster/gxpcolor.h b/pstoraster/gxpcolor.h
new file mode 100644
index 000000000..9b3a0f124
--- /dev/null
+++ b/pstoraster/gxpcolor.h
@@ -0,0 +1,138 @@
+/* Copyright (C) 1993, 1995, 1996, 1997 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Requires gsmatrix.h, gxdevice.h, gxdevmem.h, gxcolor2.h, gxdcolor.h */
+
+#ifndef gxpcolor_INCLUDED
+# define gxpcolor_INCLUDED
+
+#include "gxpcache.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_type_t
+ 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. ------ */
+ /* Note that the id is a generated instance ID, */
+ /* and has no relation to the template's gs_uid. */
+ gx_bitmap_id id;
+ int depth;
+ /* We do, however, copy the template's gs_uid, */
+ /* for use in selective cache purging. */
+ gs_uid uid;
+ /* ------ The following are the cache '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 step_matrix; /* tiling space -> device space, */
+ /* see gxcolor2.h for details */
+ gs_rect bbox; /* bbox of tile in tiling space */
+ 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 the Pattern cache. */
+ /*#include "gxpcache.h" *//* (above) */
+
+/* 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. */
+/* Note that this does not free any of the data in the accumulator */
+/* device, but it may zero out the bitmap_memory pointers to prevent */
+/* the accumulated bitmaps from being freed when the device is closed. */
+int gx_pattern_cache_add_entry(P3(gs_imager_state *, gx_device_pattern_accum *,
+ gx_color_tile **));
+
+/* Look up a pattern color in the cache. */
+bool gx_pattern_cache_lookup(P4(gx_device_color *, const gs_imager_state *,
+ gx_device *, gs_color_select_t));
+
+/* Purge selected entries from the pattern cache. */
+void gx_pattern_cache_winnow(P3(gx_pattern_cache *,
+ bool (*)(P2(gx_color_tile *, void *)),
+ void *));
+
+#endif /* gxpcolor_INCLUDED */
diff --git a/pstoraster/gxpcopy.c b/pstoraster/gxpcopy.c
new file mode 100644
index 000000000..cf917f10e
--- /dev/null
+++ b/pstoraster/gxpcopy.c
@@ -0,0 +1,824 @@
+/* Copyright (C) 1992, 1995, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Path copying and flattening */
+#include "math_.h"
+#include "gx.h"
+#include "gserrors.h"
+#include "gconfigv.h" /* for USE_FPU */
+#include "gxfixed.h"
+#include "gxfarith.h"
+#include "gzpath.h"
+
+/* Forward declarations */
+private void adjust_point_to_tangent(P3(segment *, const segment *,
+ const gs_fixed_point *));
+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, gx_path_copy_options options)
+{
+ const segment *pseg;
+
+ /*
+ * Since we're going to be adding to the path, unshare it
+ * before we start.
+ */
+ int code = gx_path_unshare(ppath);
+
+ if (code < 0)
+ return code;
+#ifdef DEBUG
+ if (gs_debug_c('P'))
+ gx_dump_path(ppath_old, "before reducing");
+#endif
+ pseg = (const segment *)(ppath_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 (options & pco_monotonize)
+ code = monotonize_internal(ppath, pc);
+ else
+ code = gx_path_add_curve_notes(ppath,
+ pc->p1.x, pc->p1.y, pc->p2.x, pc->p2.y,
+ pc->pt.x, pc->pt.y, pseg->notes);
+ } else {
+ fixed x0 = ppath->position.x;
+ fixed y0 = ppath->position.y;
+ int k = gx_curve_log2_samples(x0, y0, pc,
+ fixed_flatness);
+ segment_notes notes = pseg->notes;
+ segment *start;
+ curve_segment cseg;
+
+ if (options & pco_accurate) { /* Add an extra line, which will become */
+ /* the tangent segment. */
+ code = gx_path_add_line_notes(ppath, x0, y0,
+ notes);
+ if (code < 0)
+ break;
+ start = ppath->current_subpath->last;
+ notes |= sn_not_first;
+ }
+ cseg = *pc;
+ code = gx_flatten_sample(ppath, k, &cseg, notes);
+ if (options & pco_accurate) { /*
+ * Adjust the first and last segments so that
+ * they line up with the tangents.
+ */
+ segment *end = ppath->current_subpath->last;
+
+ if (code < 0 ||
+ (code = gx_path_add_line_notes(ppath,
+ ppath->position.x,
+ ppath->position.y,
+ pseg->notes | sn_not_first)) < 0
+ )
+ break;
+ adjust_point_to_tangent(start, start->next,
+ &pc->p1);
+ adjust_point_to_tangent(end, end->prev,
+ &pc->p2);
+ }
+ }
+ break;
+ }
+ case s_line:
+ code = gx_path_add_line_notes(ppath,
+ pseg->pt.x, pseg->pt.y, pseg->notes);
+ 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 < 0) {
+ gx_path_new(ppath);
+ return code;
+ }
+ pseg = pseg->next;
+ }
+ if (path_last_is_moveto(ppath_old))
+ gx_path_add_point(ppath, ppath_old->position.x,
+ ppath_old->position.y);
+#ifdef DEBUG
+ if (gs_debug_c('P'))
+ gx_dump_path(ppath, "after reducing");
+#endif
+ return 0;
+}
+
+/*
+ * Adjust one end of a line (the first or last line of a flattened curve)
+ * so it falls on the curve tangent. The closest point on the line from
+ * (0,0) to (C,D) to a point (U,V) -- i.e., the point on the line at which
+ * a perpendicular line from the point intersects it -- is given by
+ * T = (C*U + D*V) / (C^2 + D^2)
+ * (X,Y) = (C*T,D*T)
+ * However, any smaller value of T will also work: the one we actually
+ * use is 0.25 * the value we just derived. We must check that
+ * numerical instabilities don't lead to a negative value of T.
+ */
+private void
+adjust_point_to_tangent(segment * pseg, const segment * next,
+ const gs_fixed_point * p1)
+{
+ const fixed x0 = pseg->pt.x, y0 = pseg->pt.y;
+ const fixed fC = p1->x - x0, fD = p1->y - y0;
+
+ /*
+ * By far the commonest case is that the end of the curve is
+ * horizontal or vertical. Check for this specially, because
+ * we can handle it with far less work (and no floating point).
+ */
+ if (fC == 0) {
+ /* Vertical tangent. */
+ const fixed DT = arith_rshift(next->pt.y - y0, 2);
+
+ if (fD == 0)
+ return; /* anomalous case */
+ if_debug1('2', "[2]adjusting vertical: DT = %g\n",
+ fixed2float(DT));
+ if (DT > 0)
+ pseg->pt.y = DT + y0;
+ } else if (fD == 0) {
+ /* Horizontal tangent. */
+ const fixed CT = arith_rshift(next->pt.x - x0, 2);
+
+ if_debug1('2', "[2]adjusting horizontal: CT = %g\n",
+ fixed2float(CT));
+ if (CT > 0)
+ pseg->pt.x = CT + x0;
+ } else {
+ /* General case. */
+ const double C = fC, D = fD;
+ const double T = (C * (next->pt.x - x0) + D * (next->pt.y - y0)) /
+ (C * C + D * D);
+
+ if_debug3('2', "[2]adjusting: C = %g, D = %g, T = %g\n",
+ C, D, T);
+ if (T > 0) {
+ pseg->pt.x = arith_rshift((fixed) (C * T), 2) + x0;
+ pseg->pt.y = arith_rshift((fixed) (D * T), 2) + y0;
+ }
+ }
+}
+
+/* ---------------- 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
+
+#ifdef DEBUG
+private void
+dprint_curve(const char *str, fixed x0, fixed y0, const curve_segment * pc)
+{
+ dlprintf9("%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
+
+/* 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;
+ /* Compute prc->a..c taking into account reversal of xy0/3 */
+ /* in curve_x_at_y. */
+ {
+ fixed w0, w1, w2, w3;
+
+ if (y0 < y3)
+ w0 = x0, w1 = x1, w2 = x2, w3 = x3;
+ else
+ w0 = x3, w1 = x2, w2 = x1, w3 = x0;
+ curve_points_to_coefficients(w0, w1, w2, w3,
+ prc->a, prc->b, prc->c, v01, v12);
+ }
+ prc->double_set = false;
+ prc->fixed_limit =
+ (coeffs_fit(prc->a, prc->b, prc->c) ? (1 << k) - 1 : -1);
+ /* Initialize the cache. */
+ prc->cache.ky0 = prc->cache.ky3 = y0;
+ prc->cache.xl = x0;
+ prc->cache.xd = 0;
+}
+
+/*
+ * 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 if floating point arithmetic is reasonably fast, 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.
+ */
+#define SUBDIVIDE_X USE_FPU_FIXED
+fixed
+gx_curve_x_at_y(curve_cursor * prc, fixed y)
+{
+ fixed xl, xd;
+ fixed yd, yrel;
+
+ /* Check the cache before doing anything else. */
+ if (y >= prc->cache.ky0 && y <= prc->cache.ky3) {
+ yd = prc->cache.ky3 - prc->cache.ky0;
+ yrel = y - prc->cache.ky0;
+ xl = prc->cache.xl;
+ xd = prc->cache.xd;
+ goto done;
+ } {
+#define x0 prc->p0.x
+#define y0 prc->p0.y
+ const curve_segment *pc = prc->pc;
+
+ /* Reduce case testing by ensuring y3 >= y0. */
+ fixed cy0 = y0, cy1, cy2, cy3 = y3;
+ fixed cx0;
+
+#if SUBDIVIDE_X
+ fixed cx1, cx2, cx3;
+
+#else
+ int t = 0;
+
+#endif
+ int k, i;
+
+ if (cy0 > cy3)
+ cx0 = x3,
+#if SUBDIVIDE_X
+ cx1 = x2, cx2 = x1, cx3 = x0,
+#endif
+ cy0 = y3, cy1 = y2, cy2 = y1, cy3 = y0;
+ else
+ cx0 = x0,
+#if SUBDIVIDE_X
+ cx1 = x1, cx2 = x2, cx3 = x3,
+#endif
+ cy1 = y1, cy2 = y2;
+#define midpoint_fast(a,b)\
+ arith_rshift_1((a) + (b) + 1)
+ for (i = k = prc->k; i > 0; --i) {
+ fixed ym = midpoint_fast(cy1, cy2);
+ fixed yn = ym + arith_rshift(cy0 - cy1 - cy2 + cy3 + 4, 3);
+
+#if SUBDIVIDE_X
+ fixed xm = midpoint_fast(cx1, cx2);
+ fixed xn = xm + arith_rshift(cx0 - cx1 - cx2 + cx3 + 4, 3);
+
+#else
+ t <<= 1;
+#endif
+
+ if (y < yn)
+#if SUBDIVIDE_X
+ cx1 = midpoint_fast(cx0, cx1),
+ cx2 = midpoint_fast(cx1, xm),
+ cx3 = xn,
+#endif
+ cy1 = midpoint_fast(cy0, cy1),
+ cy2 = midpoint_fast(cy1, ym),
+ cy3 = yn;
+ else
+#if SUBDIVIDE_X
+ cx2 = midpoint_fast(cx2, cx3),
+ cx1 = midpoint_fast(xm, cx2),
+ cx0 = xn,
+#else
+ t++,
+#endif
+ cy2 = midpoint_fast(cy2, cy3),
+ cy1 = midpoint_fast(ym, cy2),
+ cy0 = yn;
+ }
+#if SUBDIVIDE_X
+ xl = cx0;
+ xd = cx3 - cx0;
+#else
+ {
+ 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)
+
+ /* use multiply if possible */
+#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 (t <= prc->fixed_limit) { /* We can do everything in integer/fixed point. */
+ int t2 = t * t, t3 = t2 * t;
+ int t3d = (t2 + t) * 3 + 1, t2d = t + t + 1;
+
+ xl = compute_fixed(a, b, c) + cx0;
+ 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) + cx0;
+ xdf = compute_diff_floating(fa, fb, fc);
+ if (any_abs(xlf - xl) > fixed_epsilon ||
+ any_abs(xdf - xd) > fixed_epsilon
+ )
+ dlprintf9("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
+ } else { /*
+ * Either t3 (and maybe t2) won't fit in an int, or more
+ * likely the result of the multiplies won't fit.
+ */
+#define fa prc->da
+#define fb prc->db
+#define fc prc->dc
+ if (!prc->double_set) {
+ setup_floating(fa, fb, fc, a, b, c);
+ prc->double_set = true;
+ }
+ 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. If we have slow floating point,
+ * do the computation in double-precision fixed point,
+ * otherwise do it in fixed point.
+ */
+ long t2 = (long)t * t, t3 = t2 * t;
+ long t3d = (t2 + t) * 3 + 1, t2d = t + t + 1;
+
+ xl = compute_floating(fa, fb, fc) + cx0;
+ xd = compute_diff_floating(fa, fb, fc);
+ } else { /*
+ * t3 (and maybe t2) don't even fit in a long.
+ * Do the entire computation in floating point.
+ */
+ double t2 = (double)t * t, t3 = t2 * t;
+ double t3d = (t2 + t) * 3 + 1, t2d = t + t + 1;
+
+ xl = compute_floating(fa, fb, fc) + cx0;
+ xd = compute_diff_floating(fa, fb, fc);
+ }
+#undef fa
+#undef fb
+#undef fc
+ }
+ }
+#endif /* (!)SUBDIVIDE_X */
+
+ /* Update the cache. */
+ prc->cache.ky0 = cy0;
+ prc->cache.ky3 = cy3;
+ prc->cache.xl = xl;
+ prc->cache.xd = xd;
+ yd = cy3 - cy0;
+ yrel = y - cy0;
+#undef x0
+#undef y0
+ }
+ done:
+ /*
+ * Now interpolate linearly between current and next.
+ * We know that 0 <= yrel < yd.
+ * It's unlikely but possible that cy0 = y = cy3:
+ * handle this case specially.
+ */
+ if (yrel == 0)
+ return xl;
+ /*
+ * Compute in fixed point if possible.
+ */
+#define half_fixed_bits ((fixed)1 << (sizeof(fixed) * 4))
+ if (yrel < half_fixed_bits) {
+ if (xd >= 0) {
+ if (xd < half_fixed_bits)
+ return (ufixed) xd *(ufixed) yrel / (ufixed) yd + xl;
+ } else {
+ if (xd > -half_fixed_bits)
+ return -(fixed) ((ufixed) (-xd) * (ufixed) yrel / (ufixed) yd) + xl;
+ }
+ }
+#undef half_fixed_bits
+ return fixed_mult_quo(xd, yrel, yd) + xl;
+}
+
+#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;
+ segment_notes notes = pc->notes;
+ 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] - t[0]) / (1 - t[0]),
+ pcd + 1, pcd + 2);
+ }
+
+ /* Monotonize in X. */
+ for (pcs = pcd, pcd = cs, 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] - t[0]) / (1 - t[0]),
+ pcd + 1, pcd + 2);
+ }
+ pcd += nz + 1;
+ x0 = pcd[-1].pt.x;
+ y0 = pcd[-1].pt.y;
+ }
+ 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 {
+ dlprintf1("[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_notes(ppath, pcs->p1.x, pcs->p1.y,
+ pcs->p2.x, pcs->p2.y,
+ pcs->pt.x, pcs->pt.y,
+ notes |
+ (i > 0 ? sn_not_first :
+ sn_none));
+
+ 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 allows us to rasterize curves
+ * directly without pre-flattening. 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;
+
+ /*
+ * We need to return the zeros in the correct order.
+ * We know that root is non-negative, but a3f may be either
+ * positive or negative, so we need to check the ordering
+ * explicitly.
+ */
+ 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) {
+ if (nzeros && a3f < 0) /* order is reversed */
+ pst[1] = *pst, *pst = z;
+ else
+ 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..72d0f93f4
--- /dev/null
+++ b/pstoraster/gxpdash.c
@@ -0,0 +1,189 @@
+/* Copyright (C) 1995, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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(P4(const subpath *, gx_path *,
+ const gs_imager_state *,
+ const gx_dash_params *));
+int
+gx_path_add_dash_expansion(const gx_path * ppath_old, gx_path * ppath,
+ const gs_imager_state * pis)
+{
+ const subpath *psub;
+ const gx_dash_params *dash = &gs_currentlineparams(pis)->dash;
+ int code = 0;
+
+ if (dash->pattern_size == 0)
+ return gx_path_copy(ppath_old, ppath);
+ for (psub = ppath_old->first_subpath; psub != 0 && code >= 0;
+ psub = (const subpath *)psub->last->next
+ )
+ code = subpath_expand_dashes(psub, ppath, pis, dash);
+ return code;
+}
+
+private int
+subpath_expand_dashes(const subpath * psub, gx_path * ppath,
+ const gs_imager_state * pis, const gx_dash_params * dash)
+{
+ const float *pattern = dash->pattern;
+ int count, index;
+ bool ink_on;
+ float elt_length;
+ 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;
+ segment_notes notes = ~sn_not_first;
+ 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. Note that drawing != 0 implies ink_on.
+ */
+ top:count = dash->pattern_size;
+ ink_on = dash->init_ink_on;
+ index = dash->init_index;
+ elt_length = 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;
+ double length, dx, dy;
+ double scale = 1;
+ double left;
+
+ 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.0 / fixed_1);
+ if (gs_imager_currentdashadapt(pis)) {
+ double reps = length / dash->pattern_length;
+
+ scale = reps / ceil(reps);
+ /* Ensure we're starting at the start of a */
+ /* repetition. (This shouldn't be necessary, */
+ /* but it is.) */
+ count = dash->pattern_size;
+ ink_on = dash->init_ink_on;
+ index = dash->init_index;
+ elt_length = dash->init_dist_left * scale;
+ }
+ }
+ left = length;
+ while (left > elt_length) { /* We are using up the line segment. */
+ double fraction = elt_length / length;
+ fixed nx = x + (fixed) (dx * fraction);
+ fixed ny = y + (fixed) (dy * fraction);
+
+ if (ink_on) {
+ if (drawing >= 0)
+ code = gx_path_add_line_notes(ppath, nx, ny,
+ notes & pseg->notes);
+ notes |= sn_not_first;
+ } else {
+ if (drawing > 0) /* done */
+ return 0;
+ code = gx_path_add_point(ppath, nx, ny);
+ notes &= ~sn_not_first;
+ drawing = 0;
+ }
+ if (code < 0)
+ return code;
+ left -= elt_length;
+ ink_on = !ink_on;
+ if (++index == count)
+ index = 0;
+ elt_length = pattern[index] * scale;
+ x = nx, y = ny;
+ }
+ elt_length -= left;
+ /* Handle the last dash of a segment. */
+ on:if (ink_on) {
+ if (drawing >= 0) {
+ code =
+ (pseg->type == s_line_close && drawing > 0 ?
+ gx_path_close_subpath_notes(ppath,
+ notes & pseg->notes) :
+ gx_path_add_line_notes(ppath, sx, sy,
+ notes & pseg->notes));
+ notes |= sn_not_first;
+ }
+ } else {
+ code = gx_path_add_point(ppath, sx, sy);
+ notes &= ~sn_not_first;
+ if (elt_length < fixed2float(fixed_epsilon) &&
+ (pseg->next == 0 || pseg->next->type == s_start)
+ ) { /*
+ * Ink is off, but we're within epsilon of the end
+ * of the dash element, and at the end of the
+ * subpath. "Stretch" a little so we get a dot.
+ */
+ if (code < 0)
+ return code;
+ elt_length = 0;
+ ink_on = true;
+ if (++index == count)
+ index = 0;
+ elt_length = pattern[index] * scale;
+ goto on;
+ }
+ if (drawing > 0) /* done */
+ return code;
+ 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/gxpflat.c b/pstoraster/gxpflat.c
new file mode 100644
index 000000000..f5c962285
--- /dev/null
+++ b/pstoraster/gxpflat.c
@@ -0,0 +1,455 @@
+/* Copyright (C) 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Path flattening algorithms */
+#include "gx.h"
+#include "gxarith.h"
+#include "gxfixed.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
+
+/* ---------------- 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 (indicated by flatness = 0), 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 == 0) { /* 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);
+ /*
+ * The following statement is split up to work around a
+ * bug in the gcc 2.7.2 optimizer on H-P RISC systems.
+ */
+ uint qtmp = d - (d >> 2) /* 3/4 * D */ +fixed_flat - 1;
+ uint q = qtmp / fixed_flat;
+
+ 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));
+ /* Now we want to set k = ceiling(log2(q) / 2). */
+ for (k = 0; q > 1;)
+ k++, q = (q + 3) >> 2;
+ if_debug1('2', " k=%d\n", k);
+ }
+ return k;
+}
+
+/*
+ * 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.
+ */
+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
+}
+
+/*
+ * 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.
+ */
+int
+gx_flatten_sample(gx_path * ppath, int k, curve_segment * pc,
+ segment_notes notes)
+{
+ 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')) {
+ dlprintf4("[3]x0=%f y0=%f x1=%f y1=%f\n",
+ fixed2float(x0), fixed2float(y0),
+ fixed2float(x1), fixed2float(y1));
+ dlprintf5(" 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_notes(ppath, x3, y3, notes);
+ }
+ 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 = gx_flatten_sample(ppath, k, &cseg, notes);
+ if (code < 0)
+ return code;
+ notes |= sn_not_first;
+ 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')) {
+ dlprintf4("[3]dx=%f+%d, dy=%f+%d\n",
+ fixed2float(idx), rdx,
+ fixed2float(idy), rdy);
+ dlprintf4(" d2x=%f+%d, d2y=%f+%d\n",
+ fixed2float(id2x), rd2x,
+ fixed2float(id2y), rd2y);
+ dlprintf4(" 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 (notes & sn_not_first)
+ code = gx_path_add_lines_notes(ppath, points, max_points,
+ notes);
+ else {
+ code = gx_path_add_line_notes(ppath, points[0].x,
+ points[0].y, notes);
+ if (code < 0)
+ return code;
+ code = gx_path_add_lines_notes(ppath, points,
+ max_points - 1, notes | sn_not_first);
+ }
+ if (code < 0)
+ return code;
+ ppt = points;
+ notes |= sn_not_first;
+ }
+ 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));
+ if (ppt > points) {
+ int count = ppt + 1 - points;
+ gs_fixed_point *pts = points;
+
+ if (!(notes & sn_not_first)) {
+ int code = gx_path_add_line_notes(ppath,
+ points[0].x, points[0].y,
+ notes);
+
+ if (code < 0)
+ return code;
+ ++pts, --count;
+ notes |= sn_not_first;
+ }
+ ppt->x = x3, ppt->y = y3;
+ return gx_path_add_lines_notes(ppath, pts, count, notes);
+ }
+ return gx_path_add_line_notes(ppath, x3, y3, notes);
+}
+
+#undef x1
+#undef y1
+#undef x2
+#undef y2
+#undef x3
+#undef y3
diff --git a/pstoraster/gxropc.h b/pstoraster/gxropc.h
new file mode 100644
index 000000000..48f8b2aa2
--- /dev/null
+++ b/pstoraster/gxropc.h
@@ -0,0 +1,53 @@
+/* Copyright (C) 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Internals for RasterOp compositing */
+
+#ifndef gxropc_INCLUDED
+# define gxropc_INCLUDED
+
+#include "gsropc.h"
+#include "gxcomp.h"
+
+/* Define RasterOp-compositing objects. */
+typedef struct gs_composite_rop_s {
+ gs_composite_common;
+ gs_composite_rop_params_t params;
+} gs_composite_rop_t;
+
+#define private_st_composite_rop() /* in gsropc.c */\
+ gs_private_st_ptrs1(st_composite_rop, gs_composite_rop_t,\
+ "gs_composite_rop_t", composite_rop_enum_ptrs, composite_rop_reloc_ptrs,\
+ params.texture)
+
+/*
+ * Initialize a RasterOp compositing function from parameters.
+ * We make this visible so that clients can allocate gs_composite_rop_t
+ * objects on the stack, to reduce memory manager overhead.
+ */
+void gx_init_composite_rop(P2(gs_composite_rop_t * pcte,
+ const gs_composite_rop_params_t * params));
+
+#endif /* gxropc_INCLUDED */
diff --git a/pstoraster/gxsample.c b/pstoraster/gxsample.c
new file mode 100644
index 000000000..e8f0bdd28
--- /dev/null
+++ b/pstoraster/gxsample.c
@@ -0,0 +1,211 @@
+/* Copyright (C) 1997 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Sample unpacking procedures */
+#include "gx.h"
+#include "gxsample.h"
+
+/* ---------------- Lookup tables ---------------- */
+
+/*
+ * Define standard tables for spreading 1-bit input data.
+ * Note that these 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
+const bits32 lookup4x1to32_identity[16] =
+{map4tox(0L, 0xffL, 0xff00L, 0xff0000L, 0xff000000L)};
+const bits32 lookup4x1to32_inverted[16] =
+{map4tox(0xffffffffL, 0xffL, 0xff00L, 0xff0000L, 0xff000000L)};
+
+#else /* !arch_is_big_endian */
+const bits32 lookup4x1to32_identity[16] =
+{map4tox(0L, 0xff000000L, 0xff0000L, 0xff00L, 0xffL)};
+const bits32 lookup4x1to32_inverted[16] =
+{map4tox(0xffffffffL, 0xff000000L, 0xff0000L, 0xff00L, 0xffL)};
+
+#endif
+
+/* ---------------- Unpacking procedures ---------------- */
+
+const byte *
+sample_unpack_copy(byte * bptr, int *pdata_x, const byte * data, int data_x,
+ uint dsize, const sample_lookup_t * ignore_ptab, int spread)
+{ /* We're going to use the data right away, so no copying is needed. */
+ *pdata_x = data_x;
+ return data;
+}
+
+const byte *
+sample_unpack_1(byte * bptr, int *pdata_x, const byte * data, int data_x,
+ uint dsize, const sample_lookup_t * ptab, int spread)
+{
+ const byte *psrc = data + (data_x >> 3);
+ int left = dsize - (data_x >> 3);
+
+ if (spread == 1) {
+ bits32 *bufp = (bits32 *) bptr;
+ const bits32 *map = &ptab->lookup4x1to32[0];
+ uint b;
+
+ if (left & 1) {
+ b = psrc[0];
+ bufp[0] = map[b >> 4];
+ bufp[1] = map[b & 0xf];
+ psrc++, bufp += 2;
+ }
+ left >>= 1;
+ while (left--) {
+ b = psrc[0];
+ bufp[0] = map[b >> 4];
+ bufp[1] = map[b & 0xf];
+ b = psrc[1];
+ bufp[2] = map[b >> 4];
+ bufp[3] = map[b & 0xf];
+ psrc += 2, bufp += 4;
+ }
+ } else {
+ byte *bufp = bptr;
+ const byte *map = &ptab->lookup8[0];
+
+ while (left--) {
+ uint b = *psrc++;
+
+ *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;
+ }
+ }
+ *pdata_x = data_x & 7;
+ return bptr;
+}
+
+const byte *
+sample_unpack_2(byte * bptr, int *pdata_x, const byte * data, int data_x,
+ uint dsize, const sample_lookup_t * ptab, int spread)
+{
+ const byte *psrc = data + (data_x >> 2);
+ int left = dsize - (data_x >> 2);
+
+ if (spread == 1) {
+ bits16 *bufp = (bits16 *) bptr;
+ const bits16 *map = &ptab->lookup2x2to16[0];
+
+ while (left--) {
+ uint b = *psrc++;
+
+ *bufp++ = map[b >> 4];
+ *bufp++ = map[b & 0xf];
+ }
+ } else {
+ byte *bufp = bptr;
+ const byte *map = &ptab->lookup8[0];
+
+ while (left--) {
+ unsigned b = *psrc++;
+
+ *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;
+ }
+ }
+ *pdata_x = data_x & 3;
+ return bptr;
+}
+
+const byte *
+sample_unpack_4(byte * bptr, int *pdata_x, const byte * data, int data_x,
+ uint dsize, const sample_lookup_t * ptab, int spread)
+{
+ byte *bufp = bptr;
+ const byte *psrc = data + (data_x >> 1);
+ int left = dsize - (data_x >> 1);
+ const byte *map = &ptab->lookup8[0];
+
+ while (left--) {
+ uint b = *psrc++;
+
+ *bufp = map[b >> 4];
+ bufp += spread;
+ *bufp = map[b & 0xf];
+ bufp += spread;
+ }
+ *pdata_x = data_x & 1;
+ return bptr;
+}
+
+const byte *
+sample_unpack_8(byte * bptr, int *pdata_x, const byte * data, int data_x,
+ uint dsize, const sample_lookup_t * ptab, int spread)
+{
+ byte *bufp = bptr;
+ const byte *psrc = data + data_x;
+
+ *pdata_x = 0;
+ if (spread == 1) {
+ if (ptab->lookup8[0] != 0 ||
+ ptab->lookup8[255] != 255
+ ) {
+ uint left = dsize - data_x;
+ const byte *map = &ptab->lookup8[0];
+
+ while (left--)
+ *bufp++ = map[*psrc++];
+ } else { /* No copying needed, and we'll use the data right away. */
+ return psrc;
+ }
+ } else {
+ int left = dsize - data_x;
+ const byte *map = &ptab->lookup8[0];
+
+ for (; left--; psrc++, bufp += spread)
+ *bufp = map[*psrc];
+ }
+ return bptr;
+}
diff --git a/pstoraster/gxsample.h b/pstoraster/gxsample.h
new file mode 100644
index 000000000..81447d82a
--- /dev/null
+++ b/pstoraster/gxsample.h
@@ -0,0 +1,87 @@
+/* Copyright (C) 1997 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Sample lookup and expansion */
+
+#ifndef gxsample_INCLUDED
+# define gxsample_INCLUDED
+
+/*
+ * The following union implements the expansion of sample
+ * values from N bits to 8, and a possible linear transformation.
+ */
+typedef union sample_lookup_s {
+ 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] */
+} sample_lookup_t;
+
+/*
+ * Define identity and inverted expansion lookups for 1-bit input values.
+ * These can be cast to a const sample_lookup_t.
+ */
+extern const bits32 lookup4x1to32_identity[16];
+
+#define sample_lookup_1_identity\
+ ((const sample_lookup_t *)lookup4x1to32_identity)
+extern const bits32 lookup4x1to32_inverted[16];
+
+#define sample_lookup_1_inverted\
+ ((const sample_lookup_t *)lookup4x1to32_inverted)
+
+/*
+ * Define procedures to unpack and shuffle image data 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.
+ *
+ * The original data start at sample data_x relative to data.
+ * bptr points to the buffer normally used to deliver the unpacked data.
+ * The unpacked data are at sample *pdata_x relative to the return value.
+ *
+ * Note that this procedure may return either a pointer to the buffer, or
+ * a pointer to the original data.
+ */
+#define sample_unpack_proc(proc)\
+ const byte *proc(P7(byte *bptr, int *pdata_x, const byte *data, int data_x,\
+ uint dsize, const sample_lookup_t *ptab, int spread))
+typedef sample_unpack_proc((*sample_unpack_proc_t));
+
+/*
+ * Declare the 1-for-1 unpacking procedure.
+ */
+sample_unpack_proc(sample_unpack_copy);
+/*
+ * Declare unpacking procedures for 1, 2, 4, and 8 bits per pixel,
+ * with optional spreading of the result.
+ */
+sample_unpack_proc(sample_unpack_1);
+sample_unpack_proc(sample_unpack_2);
+sample_unpack_proc(sample_unpack_4);
+sample_unpack_proc(sample_unpack_8);
+
+#endif /* gxsample_INCLUDED */
diff --git a/pstoraster/gxshade.c b/pstoraster/gxshade.c
new file mode 100644
index 000000000..f1891dd90
--- /dev/null
+++ b/pstoraster/gxshade.c
@@ -0,0 +1,348 @@
+/* Copyright (C) 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Shading rendering support */
+#include "math_.h"
+#include "gx.h"
+#include "gserrors.h"
+#include "gsrect.h"
+#include "gxcspace.h"
+#include "gscie.h" /* requires gscspace.h */
+#include "gxdevcli.h"
+#include "gxistate.h"
+#include "gxdht.h" /* for computing # of different colors */
+#include "gxpaint.h"
+#include "gxshade.h"
+
+/* ================ Packed coordinate streams ================ */
+
+/* Forward references */
+private int cs_next_packed_value(P3(shade_coord_stream_t *, int, uint *));
+private int cs_next_array_value(P3(shade_coord_stream_t *, int, uint *));
+private int cs_next_packed_decoded(P4(shade_coord_stream_t *, int,
+ const float[2], float *));
+private int cs_next_array_decoded(P4(shade_coord_stream_t *, int,
+ const float[2], float *));
+
+/* Initialize a packed value stream. */
+void
+shade_next_init(shade_coord_stream_t * cs,
+ const gs_shading_mesh_params_t * params,
+ const gs_imager_state * pis)
+{
+ cs->params = params;
+ cs->pctm = &pis->ctm;
+ if (data_source_is_stream(params->DataSource)) {
+ cs->s = params->DataSource.data.strm;
+ } else {
+ sread_string(&cs->ds, params->DataSource.data.str.data,
+ params->DataSource.data.str.size);
+ cs->s = &cs->ds;
+ }
+ if (data_source_is_array(params->DataSource)) {
+ cs->get_value = cs_next_array_value;
+ cs->get_decoded = cs_next_array_decoded;
+ } else {
+ cs->get_value = cs_next_packed_value;
+ cs->get_decoded = cs_next_packed_decoded;
+ }
+ cs->left = 0;
+}
+
+/* Get the next (integer) value from a packed value stream. */
+/* 1 <= num_bits <= sizeof(uint) * 8. */
+private int
+cs_next_packed_value(shade_coord_stream_t * cs, int num_bits, uint * pvalue)
+{
+ uint bits = cs->bits;
+ int left = cs->left;
+
+ if (left >= num_bits) {
+ /* We can satisfy this request with the current buffered bits. */
+ cs->left = left -= num_bits;
+ *pvalue = (bits >> left) & ((1 << num_bits) - 1);
+ } else {
+ /* We need more bits. */
+ int needed = num_bits - left;
+ uint value = bits & ((1 << left) - 1); /* all the remaining bits */
+
+ for (; needed >= 8; needed -= 8) {
+ int b = sgetc(cs->s);
+
+ if (b < 0)
+ return_error(gs_error_rangecheck);
+ value = (value << 8) + b;
+ }
+ if (needed == 0) {
+ cs->left = 0;
+ *pvalue = value;
+ } else {
+ int b = sgetc(cs->s);
+
+ if (b < 0)
+ return_error(gs_error_rangecheck);
+ cs->bits = b;
+ cs->left = left = 8 - needed;
+ *pvalue = (value << needed) + (b >> left);
+ }
+ }
+ return 0;
+}
+
+/* Get the next (integer) value from an unpacked array. */
+private int
+cs_next_array_value(shade_coord_stream_t * cs, int num_bits, uint * pvalue)
+{
+ float value;
+ uint read;
+
+ if (sgets(cs->s, (byte *)&value, sizeof(float), &read) < 0 ||
+ read != sizeof(float) || value < 0 || value >= (1 << num_bits) ||
+ value != (int)value
+ )
+ return_error(gs_error_rangecheck);
+ *pvalue = (uint) value;
+ return 0;
+}
+
+/* Get the next decoded floating point value. */
+private int
+cs_next_packed_decoded(shade_coord_stream_t * cs, int num_bits,
+ const float decode[2], float *pvalue)
+{
+ uint value;
+ int code = cs->get_value(cs, num_bits, &value);
+ double max_value = (double)(uint) ((1 << num_bits) - 1);
+
+ if (code < 0)
+ return code;
+ *pvalue =
+ (decode == 0 ? value / max_value :
+ decode[0] + value * (decode[1] - decode[0]) / max_value);
+ return 0;
+}
+
+/* Get the next floating point value from an array, without decoding. */
+private int
+cs_next_array_decoded(shade_coord_stream_t * cs, int num_bits,
+ const float decode[2], float *pvalue)
+{
+ float value;
+ uint read;
+
+ if (sgets(cs->s, (byte *)&value, sizeof(float), &read) < 0 ||
+ read != sizeof(float)
+ )
+ return_error(gs_error_rangecheck);
+ *pvalue = value;
+ return 0;
+}
+
+/* Get the next flag value. */
+/* Note that this always starts a new data byte. */
+int
+shade_next_flag(shade_coord_stream_t * cs, int BitsPerFlag)
+{
+ uint flag;
+ int code;
+
+ cs->left = 0; /* start a new byte if packed */
+ code = cs->get_value(cs, BitsPerFlag, &flag);
+ return (code < 0 ? code : flag);
+}
+
+/* Get one or more coordinate pairs. */
+int
+shade_next_coords(shade_coord_stream_t * cs, gs_fixed_point * ppt,
+ int num_points)
+{
+ int num_bits = cs->params->BitsPerCoordinate;
+ const float *decode = cs->params->Decode;
+ int code = 0;
+ int i;
+
+ for (i = 0; i < num_points; ++i) {
+ float x, y;
+
+ if ((code = cs->get_decoded(cs, num_bits, decode, &x)) < 0 ||
+ (code = cs->get_decoded(cs, num_bits, decode, &y)) < 0 ||
+ (code = gs_point_transform2fixed(cs->pctm, x, y, &ppt[i])) < 0
+ )
+ break;
+ }
+ return code;
+}
+
+/* Get a color. Currently all this does is look up Indexed colors. */
+int
+shade_next_color(shade_coord_stream_t * cs, float *pc)
+{
+ const float *decode = cs->params->Decode + 4; /* skip coord decode */
+ const gs_color_space *pcs = cs->params->ColorSpace;
+ gs_color_space_index index = gs_color_space_get_index(pcs);
+ int num_bits = cs->params->BitsPerComponent;
+
+ if (index == gs_color_space_index_Indexed) {
+ uint i;
+ int code = cs->get_value(cs, num_bits, &i);
+
+ if (code < 0)
+ return code;
+ /****** DO INDEXED LOOKUP TO pc[] ******/
+ } else {
+ int i, code;
+ int ncomp = gs_color_space_num_components(pcs);
+
+ for (i = 0; i < ncomp; ++i)
+ if ((code = cs->get_decoded(cs, num_bits, decode + i * 2, &pc[i])) < 0)
+ return code;
+ }
+ return 0;
+}
+
+/* Get the next vertex for a mesh element. */
+int
+shade_next_vertex(shade_coord_stream_t * cs, mesh_vertex_t * vertex)
+{
+ int code = shade_next_coords(cs, &vertex->p, 1);
+
+ if (code >= 0)
+ code = shade_next_color(cs, vertex->cc);
+ return code;
+}
+
+/* ================ Shading rendering ================ */
+
+/* Initialize the common parts of the recursion state. */
+void
+shade_init_fill_state(shading_fill_state_t * pfs, const gs_shading_t * psh,
+ gx_device * dev, gs_imager_state * pis)
+{
+ const gs_color_space *pcs = psh->params.ColorSpace;
+ float max_error = pis->smoothness;
+ /*
+ * There's no point in trying to achieve smoothness beyond what
+ * the device can implement, i.e., the number of representable
+ * colors times the number of halftone levels.
+ */
+ long num_colors =
+ max(dev->color_info.max_gray, dev->color_info.max_color) + 1;
+ const gs_range *ranges = 0;
+ int ci;
+
+ pfs->dev = dev;
+ pfs->pis = pis;
+ pfs->num_components = gs_color_space_num_components(pcs);
+top:
+ switch ( gs_color_space_get_index(pcs) )
+ {
+ case gs_color_space_index_Indexed:
+ pcs = gs_cspace_base_space(pcs);
+ goto top;
+ case gs_color_space_index_CIEDEFG:
+ ranges = pcs->params.defg->RangeDEFG.ranges;
+ break;
+ case gs_color_space_index_CIEDEF:
+ ranges = pcs->params.def->RangeDEF.ranges;
+ break;
+ case gs_color_space_index_CIEABC:
+ ranges = pcs->params.abc->RangeABC.ranges;
+ break;
+ case gs_color_space_index_CIEA:
+ ranges = &pcs->params.a->RangeA;
+ break;
+ default:
+ break;
+ }
+ if (num_colors <= 32) {
+ /****** WRONG FOR MULTI-PLANE HALFTONES ******/
+ num_colors *= pis->dev_ht->order.num_levels;
+ }
+ if (max_error < 1.0 / num_colors)
+ max_error = 1.0 / num_colors;
+ for (ci = 0; ci < pfs->num_components; ++ci)
+ pfs->cc_max_error[ci] =
+ (ranges == 0 ? max_error :
+ max_error * (ranges[ci].rmax - ranges[ci].rmin));
+}
+
+/* Transform a bounding box into device space. */
+int
+shade_bbox_transform2fixed(const gs_rect * rect, const gs_imager_state * pis,
+ gs_fixed_rect * rfixed)
+{
+ gs_rect dev_rect;
+ int code = gs_bbox_transform(rect, &ctm_only(pis), &dev_rect);
+
+ if (code >= 0) {
+ rfixed->p.x = float2fixed(dev_rect.p.x);
+ rfixed->p.y = float2fixed(dev_rect.p.y);
+ rfixed->q.x = float2fixed(dev_rect.q.x);
+ rfixed->q.y = float2fixed(dev_rect.q.y);
+ }
+ return code;
+}
+
+/* Check whether 4 colors fall within the smoothness criterion. */
+bool
+shade_colors4_converge(const gs_client_color cc[4],
+ const shading_fill_state_t * pfs)
+{
+ int ci;
+
+ for (ci = 0; ci < pfs->num_components; ++ci) {
+ float
+ c0 = cc[0].paint.values[ci], c1 = cc[1].paint.values[ci],
+ c2 = cc[2].paint.values[ci], c3 = cc[3].paint.values[ci];
+ float min01, max01, min23, max23;
+
+ if (c0 < c1)
+ min01 = c0, max01 = c1;
+ else
+ min01 = c1, max01 = c0;
+ if (c2 < c3)
+ min23 = c2, max23 = c3;
+ else
+ min23 = c3, max23 = c2;
+ if (max(max01, max23) - min(min01, min23) > pfs->cc_max_error[ci])
+ return false;
+ }
+ return true;
+}
+
+/* Fill one piece of a shading. */
+int
+shade_fill_path(const shading_fill_state_t * pfs, gx_path * ppath,
+ gx_device_color * pdevc)
+{
+ gx_fill_params params;
+
+ params.rule = -1; /* irrelevant */
+ params.adjust = pfs->pis->fill_adjust;
+ params.flatness = 0; /* irrelevant */
+ params.fill_zero_width = false;
+ return (*dev_proc(pfs->dev, fill_path)) (pfs->dev, pfs->pis, ppath,
+ &params, pdevc, NULL);
+}
diff --git a/pstoraster/gxshade.h b/pstoraster/gxshade.h
new file mode 100644
index 000000000..20ebb15d3
--- /dev/null
+++ b/pstoraster/gxshade.h
@@ -0,0 +1,247 @@
+/* Copyright (C) 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Internal definitions for shading rendering */
+
+#ifndef gxshade_INCLUDED
+# define gxshade_INCLUDED
+
+#include "gsshade.h"
+#include "gxfixed.h" /* for gxmatrix.h */
+#include "gxmatrix.h" /* for gs_matrix_fixed */
+#include "stream.h"
+
+/*
+ All shadings are defined with respect to some parameter that varies
+ continuously over some range; the shading defines a mapping from the
+ parameter values to colors and user space coordinates. Here are the
+ mappings for the 7 currently defined shading types:
+
+ Type Param space Param => color Param => User space
+ ---- ----------- -------------- -------------------
+ 1 2-D Domain Function Matrix
+ 2 1-D Domain Function + Extend perp. to Coords
+ 3 1-D Domain Function + Extend circles per Coords
+ 4,5 triangle x Gouraud interp. on Gouraud interp. on
+ 2-D in tri. Decode => corner triangle corners
+ values => Function
+ 6 patch x (u,v) Decode => bilinear Sc + Sd - Sb on each patch
+ in patch interp. on corner
+ values => Function
+ 7 see 6 see 6 Sum(i) Sum(j) Pij*Bi(u)*Bj(v)
+
+ To be able to render a portion of a shading usefully, we must be able to
+ do two things:
+
+ - Determine what range of parameter values is sufficient to cover
+ the region being filled;
+
+ - Evaluate the color at enough points to fill the region (in
+ device space).
+
+ Note that the latter may be implemented by a mix of evaluation and
+ interpolation, especially for types 3, 6, and 7 where an exact mapping
+ may be prohibitively expensive.
+
+ Except for type 3, where circles turn into ellipses, the CTM can get
+ folded into the parameter => user space mapping, since in all other
+ cases, the mapping space is closed under linear transformations of
+ the output.
+ */
+
+/* Define types and rendering procedures for the individual shadings. */
+typedef struct gs_shading_Fb_s {
+ gs_shading_head_t head;
+ gs_shading_Fb_params_t params;
+} gs_shading_Fb_t;
+shading_fill_rectangle_proc(gs_shading_Fb_fill_rectangle);
+
+typedef struct gs_shading_A_s {
+ gs_shading_head_t head;
+ gs_shading_A_params_t params;
+} gs_shading_A_t;
+shading_fill_rectangle_proc(gs_shading_A_fill_rectangle);
+
+typedef struct gs_shading_R_s {
+ gs_shading_head_t head;
+ gs_shading_R_params_t params;
+} gs_shading_R_t;
+shading_fill_rectangle_proc(gs_shading_R_fill_rectangle);
+
+typedef struct gs_shading_FfGt_s {
+ gs_shading_head_t head;
+ gs_shading_FfGt_params_t params;
+} gs_shading_FfGt_t;
+shading_fill_rectangle_proc(gs_shading_FfGt_fill_rectangle);
+
+typedef struct gs_shading_LfGt_s {
+ gs_shading_head_t head;
+ gs_shading_LfGt_params_t params;
+} gs_shading_LfGt_t;
+shading_fill_rectangle_proc(gs_shading_LfGt_fill_rectangle);
+
+typedef struct gs_shading_Cp_s {
+ gs_shading_head_t head;
+ gs_shading_Cp_params_t params;
+} gs_shading_Cp_t;
+shading_fill_rectangle_proc(gs_shading_Cp_fill_rectangle);
+
+typedef struct gs_shading_Tpp_s {
+ gs_shading_head_t head;
+ gs_shading_Tpp_params_t params;
+} gs_shading_Tpp_t;
+shading_fill_rectangle_proc(gs_shading_Tpp_fill_rectangle);
+
+/* We should probably get this from somewhere else.... */
+#define max_color_components 4
+
+/* Define a stream for decoding packed coordinate values. */
+typedef struct shade_coord_stream_s shade_coord_stream_t;
+struct shade_coord_stream_s {
+ stream ds; /* stream if DataSource isn't one already -- */
+ /* first for GC-ability (maybe unneeded?) */
+ stream *s; /* DataSource or &ds */
+ uint bits; /* shifted bits of current byte */
+ int left; /* # of bits left in bits */
+ const gs_shading_mesh_params_t *params;
+ const gs_matrix_fixed *pctm;
+ int (*get_value)(P3(shade_coord_stream_t *cs, int num_bits, uint *pvalue));
+ int (*get_decoded)(P4(shade_coord_stream_t *cs, int num_bits,
+ const float decode[2], float *pvalue));
+};
+
+/* Define one vertex of a mesh. */
+typedef struct mesh_vertex_s {
+ gs_fixed_point p;
+ float cc[max_color_components];
+} mesh_vertex_t;
+
+/* Initialize a packed value stream. */
+void shade_next_init(P3(shade_coord_stream_t * cs,
+ const gs_shading_mesh_params_t * params,
+ const gs_imager_state * pis));
+
+/* Get the next flag value. */
+int shade_next_flag(P2(shade_coord_stream_t * cs, int BitsPerFlag));
+
+/* Get one or more coordinate pairs. */
+int shade_next_coords(P3(shade_coord_stream_t * cs, gs_fixed_point * ppt,
+ int num_points));
+
+/* Get a color. Currently all this does is look up Indexed colors. */
+int shade_next_color(P2(shade_coord_stream_t * cs, float *pc));
+
+/* Get the next vertex for a mesh element. */
+int shade_next_vertex(P2(shade_coord_stream_t * cs, mesh_vertex_t * vertex));
+
+/*
+ Currently, all shading fill procedures follow the same algorithm:
+
+ - Conservatively inverse-transform the rectangle being filled to a linear
+ or rectangular range of values in the parameter space.
+
+ - Compute the color values at the extrema of the range.
+
+ - If possible, compute the parameter range corresponding to a single
+ device pixel.
+
+ - Recursively do the following, passing the parameter range and extremal
+ color values as the recursion arguments:
+
+ - If the color values are equal to within the tolerance given by the
+ smoothness in the graphics state, or if the range of parameters maps
+ to a single device pixel, fill the range with the (0) or (0,0) color.
+
+ - Otherwise, subdivide and recurse. If the parameter range is 2-D,
+ subdivide the axis with the largest color difference.
+
+ For shadings based on a function, if the function is not monotonic, the
+ smoothness test must only be applied when the parameter range extrema are
+ all interpolated from the same entries in the Function. (We don't
+ currently do this.)
+
+ */
+
+/* Define the common structure for recursive subdivision. */
+#define shading_fill_state_common\
+ gx_device *dev;\
+ gs_imager_state *pis;\
+ int num_components; /* # of color components */\
+ float cc_max_error[max_color_components]
+typedef struct shading_fill_state_s {
+ shading_fill_state_common;
+} shading_fill_state_t;
+
+/* Initialize the common parts of the recursion state. */
+void shade_init_fill_state(P4(shading_fill_state_t * pfs,
+ const gs_shading_t * psh, gx_device * dev,
+ gs_imager_state * pis));
+
+/* Transform a bounding box into device space. */
+int shade_bbox_transform2fixed(P3(const gs_rect * rect,
+ const gs_imager_state * pis,
+ gs_fixed_rect * rfixed));
+
+/* Check whether 4 colors fall within the smoothness criterion. */
+bool shade_colors4_converge(P2(const gs_client_color cc[4],
+ const shading_fill_state_t * pfs));
+
+/* Fill one piece of a shading. */
+#ifndef gx_device_color_DEFINED
+# define gx_device_color_DEFINED
+typedef struct gx_device_color_s gx_device_color;
+#endif
+int shade_fill_path(P3(const shading_fill_state_t * pfs, gx_path * ppath,
+ gx_device_color * pdevc));
+
+#endif /* gxshade_INCLUDED */
+
+#if 0 /*************************************************************** */
+
+/*
+ * Here is a sketch of what will be needed to generalize Patterns for
+ * (the) new PatternType(s).
+ */
+typedef struct gs_pattern_instance_s {
+ rc_header rc; /* ?? */
+ const gs_pattern_type_t *type;
+ gs_uid XUID; /* ?? */
+ gs_state *saved; /* ?? */
+ void *data;
+} gs_pattern_instance_t;
+typedef struct gs_pattern1_instance_data_s {
+ ...
+} gs_pattern1_instance_data_t;
+
+#define gs_pattern2_instance_data_common\
+ const gs_shading_t *shading;\
+ gx_device_color *background;\
+ const gs_color_space *color_space;\
+ gs_matrix param_to_device_matrix
+typedef struct gs_pattern2_instance_data_common_s {
+ gs_pattern2_instance_data_common;
+} gs_pattern2_instance_data_common_t;
+
+#endif /*************************************************************** */
diff --git a/pstoraster/gxshade1.c b/pstoraster/gxshade1.c
new file mode 100644
index 000000000..ee51512fe
--- /dev/null
+++ b/pstoraster/gxshade1.c
@@ -0,0 +1,527 @@
+/* Copyright (C) 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Rendering for non-mesh shadings */
+#include "math_.h"
+#include "gx.h"
+#include "gserrors.h"
+#include "gsmatrix.h" /* for gscoord.h */
+#include "gscoord.h"
+#include "gspath.h"
+#include "gxcspace.h"
+#include "gxdcolor.h"
+#include "gxfarith.h"
+#include "gxfixed.h"
+#include "gxistate.h"
+#include "gxpath.h"
+#include "gxshade.h"
+
+/* ================ Utilities ================ */
+
+/* Check whether 2 colors fall within the smoothness criterion. */
+private bool
+shade_colors2_converge(const gs_client_color cc[2],
+ const shading_fill_state_t * pfs)
+{
+ int ci;
+
+ for (ci = pfs->num_components - 1; ci >= 0; --ci)
+ if (fabs(cc[1].paint.values[ci] - cc[0].paint.values[ci]) >
+ pfs->cc_max_error[ci]
+ )
+ return false;
+ return true;
+}
+
+/* Fill a user space rectangle that is also a device space rectangle. */
+private int
+shade_fill_device_rectangle(const shading_fill_state_t * pfs,
+ const gs_fixed_point * p0,
+ const gs_fixed_point * p1,
+ gx_device_color * pdevc)
+{
+ gs_imager_state *pis = pfs->pis;
+ fixed xmin, ymin, xmax, ymax;
+ int x, y;
+
+ if (p0->x < p1->x)
+ xmin = p0->x, xmax = p1->x;
+ else
+ xmin = p1->x, xmax = p0->x;
+ if (p0->y < p1->y)
+ ymin = p0->y, ymax = p1->y;
+ else
+ ymin = p1->y, ymax = p0->y;
+ /****** NOT QUITE RIGHT FOR PIXROUND ******/
+ xmin -= pis->fill_adjust.x;
+ xmax += pis->fill_adjust.x;
+ ymin -= pis->fill_adjust.y;
+ ymax += pis->fill_adjust.y;
+ x = fixed2int_var(xmin);
+ y = fixed2int_var(ymin);
+ return
+ gx_fill_rectangle_device_rop(x, y,
+ fixed2int_var(xmax) - x,
+ fixed2int_var(ymax) - y,
+ pdevc, pfs->dev, pis->log_op);
+}
+
+/* ================ Specific shadings ================ */
+
+/* ---------------- Function-based shading ---------------- */
+
+typedef struct Fb_fill_state_s {
+ shading_fill_state_common;
+ const gs_shading_Fb_t *psh;
+ gs_matrix_fixed ptm; /* parameter space -> device space */
+ bool orthogonal; /* true iff ptm is xxyy or xyyx */
+} Fb_fill_state_t;
+
+private int
+Fb_fill_region(const Fb_fill_state_t * pfs, gs_client_color cc[4],
+ floatp x0, floatp y0, floatp x1, floatp y1)
+{
+ const gs_shading_Fb_t * const psh = pfs->psh;
+ gs_imager_state *pis = pfs->pis;
+
+top:
+ if (!shade_colors4_converge(cc, (const shading_fill_state_t *)pfs)) {
+ /*
+ * The colors don't converge. Does the region color more than
+ * a single pixel?
+ */
+ gs_rect region;
+
+ region.p.x = x0, region.p.y = y0;
+ region.q.x = x1, region.q.y = y1;
+ gs_bbox_transform(&region, (const gs_matrix *)&pfs->ptm, &region);
+ if (region.q.x - region.p.x > 1 || region.q.y - region.p.y > 1)
+ goto recur;
+ {
+ /*
+ * More precisely, does the bounding box of the region,
+ * taking fill adjustment into account, span more than 1
+ * pixel center in either X or Y?
+ */
+ fixed ax = pis->fill_adjust.x;
+ int nx =
+ fixed2int_pixround(float2fixed(region.q.x) + ax) -
+ fixed2int_pixround(float2fixed(region.p.x) - ax);
+ fixed ay = pis->fill_adjust.y;
+ int ny =
+ fixed2int_pixround(float2fixed(region.q.y) + ay) -
+ fixed2int_pixround(float2fixed(region.p.y) - ay);
+
+ if ((nx > 1 && ny != 0) || (ny > 1 && nx != 0))
+ goto recur;
+ }
+ /* We could do the 1-pixel case a lot faster! */
+ }
+ /* Fill the region with the color. */
+ {
+ gx_device_color dev_color;
+ const gs_color_space *pcs = psh->params.ColorSpace;
+ gs_fixed_point pts[4];
+ int code;
+
+ if_debug0('|', "[|]... filling region\n");
+ (*pcs->type->restrict_color)(&cc[0], pcs);
+ (*pcs->type->remap_color)(&cc[0], pcs, &dev_color, pis,
+ pfs->dev, gs_color_select_texture);
+ gs_point_transform2fixed(&pfs->ptm, x0, y0, &pts[0]);
+ gs_point_transform2fixed(&pfs->ptm, x1, y1, &pts[2]);
+ if (pfs->orthogonal) {
+ code =
+ shade_fill_device_rectangle((const shading_fill_state_t *)pfs,
+ &pts[0], &pts[2], &dev_color);
+ } else {
+ gx_path *ppath = gx_path_alloc(pis->memory, "Fb_fill");
+
+ gs_point_transform2fixed(&pfs->ptm, x1, y0, &pts[1]);
+ gs_point_transform2fixed(&pfs->ptm, x0, y1, &pts[3]);
+ gx_path_add_point(ppath, pts[0].x, pts[0].y);
+ gx_path_add_lines(ppath, pts + 1, 3);
+ code = shade_fill_path((const shading_fill_state_t *)pfs,
+ ppath, &dev_color);
+ gx_path_free(ppath, "Fb_fill");
+ }
+ return code;
+ }
+
+ /*
+ * No luck. Subdivide the region and recur.
+ *
+ * We should subdivide on the axis that has the largest color
+ * discrepancy, but for now we subdivide on the axis with the
+ * largest coordinate difference.
+ */
+recur:
+ {
+ gs_client_color mid[2];
+ gs_client_color rcc[4];
+ gs_function_t *pfn = psh->params.Function;
+ float v[2];
+ int code;
+
+ if (y1 - y0 > x1 - x0) {
+ /* Subdivide in Y. */
+ float ym = (y0 + y1) * 0.5;
+
+ if_debug1('|', "[|]dividing at y=%g\n", ym);
+ v[1] = ym;
+ v[0] = x0;
+ code = gs_function_evaluate(pfn, v, mid[0].paint.values);
+ if (code < 0)
+ return code;
+ v[0] = x1;
+ code = gs_function_evaluate(pfn, v, mid[1].paint.values);
+ if (code < 0)
+ return code;
+ rcc[0].paint = cc[0].paint;
+ rcc[1].paint = cc[1].paint;
+ rcc[2].paint = mid[0].paint;
+ rcc[3].paint = mid[1].paint;
+ code = Fb_fill_region(pfs, rcc, x0, y0, x1, ym);
+ cc[0].paint = mid[0].paint;
+ cc[1].paint = mid[1].paint;
+ y0 = ym;
+ } else {
+ /* Subdivide in X. */
+ float xm = (x0 + x1) * 0.5;
+
+ if_debug1('|', "[|]dividing at x=%g\n", xm);
+ v[0] = xm;
+ v[1] = y0;
+ code = gs_function_evaluate(pfn, v, mid[0].paint.values);
+ if (code < 0)
+ return code;
+ v[1] = y1;
+ code = gs_function_evaluate(pfn, v, mid[2].paint.values);
+ if (code < 0)
+ return code;
+ rcc[0].paint = cc[0].paint;
+ rcc[1].paint = mid[0].paint;
+ rcc[2].paint = cc[2].paint;
+ rcc[3].paint = mid[1].paint;
+ code = Fb_fill_region(pfs, rcc, x0, y0, xm, y1);
+ cc[0].paint = mid[0].paint;
+ cc[2].paint = mid[1].paint;
+ x0 = xm;
+ }
+ if (code < 0)
+ return code;
+ }
+ goto top;
+}
+
+int
+gs_shading_Fb_fill_rectangle(const gs_shading_t * psh0, const gs_rect * rect,
+ gx_device * dev, gs_imager_state * pis)
+{
+ const gs_shading_Fb_t * const psh = (const gs_shading_Fb_t *)psh0;
+ gs_matrix save_ctm;
+ int xi, yi, code;
+ float x[2], y[2];
+ Fb_fill_state_t state;
+ gs_client_color cc[4];
+
+ shade_init_fill_state((shading_fill_state_t *) & state, psh0, dev, pis);
+ state.psh = psh;
+ /****** HACK FOR FIXED-POINT MATRIX MULTIPLY ******/
+ gs_currentmatrix((gs_state *) pis, &save_ctm);
+ gs_concat((gs_state *) pis, &psh->params.Matrix);
+ state.ptm = pis->ctm;
+ gs_setmatrix((gs_state *) pis, &save_ctm);
+ state.orthogonal = is_xxyy(&state.ptm) || is_xyyx(&state.ptm);
+ /* Compute the parameter X and Y ranges. */
+ {
+ gs_rect pbox;
+
+ gs_bbox_transform_inverse(rect, &psh->params.Matrix, &pbox);
+ x[0] = max(pbox.p.x, psh->params.Domain[0]);
+ x[1] = min(pbox.q.x, psh->params.Domain[1]);
+ y[0] = max(pbox.p.y, psh->params.Domain[2]);
+ y[1] = min(pbox.q.y, psh->params.Domain[3]);
+ }
+ for (xi = 0; xi < 2; ++xi)
+ for (yi = 0; yi < 2; ++yi) {
+ float v[2];
+
+ v[0] = x[xi], v[1] = y[yi];
+ gs_function_evaluate(psh->params.Function, v,
+ cc[yi * 2 + xi].paint.values);
+ }
+ code = Fb_fill_region(&state, cc, x[0], y[0], x[1], y[1]);
+ return code;
+}
+
+/* ---------------- Axial shading ---------------- */
+
+typedef struct A_fill_state_s {
+ shading_fill_state_common;
+ const gs_shading_A_t *psh;
+ gs_rect rect;
+ gs_point delta;
+ double length, dd;
+} A_fill_state_t;
+
+/* Note t0 and t1 vary over [0..1], not the Domain. */
+private int
+A_fill_region(const A_fill_state_t * pfs, gs_client_color cc[2],
+ floatp t0, floatp t1)
+{
+ const gs_shading_A_t * const psh = pfs->psh;
+
+top:
+ if (!shade_colors2_converge(cc, (const shading_fill_state_t *)pfs)) {
+ /*
+ * The colors don't converge. Is the stripe less than 1 pixel wide?
+ */
+ if (pfs->length * (t1 - t0) > 1)
+ goto recur;
+ }
+ /* Fill the region with the color. */
+ {
+ gx_device_color dev_color;
+ const gs_color_space *pcs = psh->params.ColorSpace;
+ gs_imager_state *pis = pfs->pis;
+ double
+ x0 = psh->params.Coords[0] + pfs->delta.x * t0,
+ y0 = psh->params.Coords[1] + pfs->delta.y * t0;
+ double
+ x1 = psh->params.Coords[0] + pfs->delta.x * t1,
+ y1 = psh->params.Coords[1] + pfs->delta.y * t1;
+ gs_fixed_point pts[4];
+ int code;
+
+ (*pcs->type->restrict_color)(&cc[0], pcs);
+ (*pcs->type->remap_color)(&cc[0], pcs, &dev_color, pis,
+ pfs->dev, gs_color_select_texture);
+ if (x0 == x1) {
+ /* Stripe is horizontal. */
+ x0 = pfs->rect.p.x;
+ x1 = pfs->rect.q.x;
+ } else if (y0 == y1) {
+ /* Stripe is vertical. */
+ y0 = pfs->rect.p.y;
+ y1 = pfs->rect.q.y;
+ } else {
+ /*
+ * Stripe is neither horizontal nor vertical.
+ * Extend it to the edges of the rectangle.
+ */
+ gx_path *ppath = gx_path_alloc(pis->memory, "A_fill");
+ double dist = max(pfs->rect.q.x - pfs->rect.p.x,
+ pfs->rect.q.y - pfs->rect.p.y);
+ double denom = hypot(pfs->delta.x, pfs->delta.y);
+ double dx = dist * pfs->delta.y / denom,
+ dy = -dist * pfs->delta.x / denom;
+
+ if_debug6('|', "[|]p0=(%g,%g), p1=(%g,%g), dxy=(%g,%g)\n",
+ x0, y0, x1, y1, dx, dy);
+ gs_point_transform2fixed(&pis->ctm, x0 - dx, y0 - dy, &pts[0]);
+ gs_point_transform2fixed(&pis->ctm, x0 + dx, y0 + dy, &pts[1]);
+ gs_point_transform2fixed(&pis->ctm, x1 + dx, y1 + dy, &pts[2]);
+ gs_point_transform2fixed(&pis->ctm, x1 - dx, y1 - dy, &pts[3]);
+ gx_path_add_point(ppath, pts[0].x, pts[0].y);
+ gx_path_add_lines(ppath, pts + 1, 3);
+ code = shade_fill_path((const shading_fill_state_t *)pfs,
+ ppath, &dev_color);
+ gx_path_free(ppath, "A_fill");
+ return code;
+ }
+ /* Stripe is horizontal or vertical. */
+ gs_point_transform2fixed(&pis->ctm, x0, y0, &pts[0]);
+ gs_point_transform2fixed(&pis->ctm, x1, y1, &pts[1]);
+ return
+ shade_fill_device_rectangle((const shading_fill_state_t *)pfs,
+ &pts[0], &pts[1], &dev_color);
+ }
+
+ /*
+ * No luck. Subdivide the interval and recur.
+ */
+recur:
+ {
+ gs_client_color ccm, rcc[2];
+ gs_function_t *pfn = psh->params.Function;
+ float tm = (t0 + t1) * 0.5;
+ float dm = tm * pfs->dd + psh->params.Domain[0];
+
+ gs_function_evaluate(pfn, &dm, ccm.paint.values);
+ rcc[0].paint = cc[0].paint;
+ rcc[1].paint = ccm.paint;
+ A_fill_region(pfs, rcc, t0, tm);
+ cc[0].paint = ccm.paint;
+ t0 = tm;
+ goto top;
+ }
+}
+
+int
+gs_shading_A_fill_rectangle(const gs_shading_t * psh0, const gs_rect * rect,
+ gx_device * dev, gs_imager_state * pis)
+{
+ const gs_shading_A_t *const psh = (const gs_shading_A_t *)psh0;
+ A_fill_state_t state;
+ gs_client_color cc[2];
+ float d0 = psh->params.Domain[0], d1 = psh->params.Domain[1], dd = d1 - d0;
+ float t[2];
+ gs_point dist;
+ int i;
+
+ shade_init_fill_state((shading_fill_state_t *) & state, psh0, dev, pis);
+ state.psh = psh;
+ state.rect = *rect;
+ /* Compute the parameter range. */
+ t[0] = d0;
+ t[1] = d1;
+/****** INTERSECT Domain WITH rect ******/
+ for (i = 0; i < 2; ++i)
+ gs_function_evaluate(psh->params.Function, &t[i],
+ cc[i].paint.values);
+ state.delta.x = psh->params.Coords[2] - psh->params.Coords[0];
+ state.delta.y = psh->params.Coords[3] - psh->params.Coords[1];
+ gs_distance_transform(state.delta.x, state.delta.y, &ctm_only(pis),
+ &dist);
+ state.length = hypot(dist.x, dist.y); /* device space line length */
+ state.dd = dd;
+/****** DOESN'T HANDLE Extend ******/
+ return A_fill_region(&state, cc, (t[0] - d0) / dd, (t[1] - d0) / dd);
+}
+
+/* ---------------- Radial shading ---------------- */
+
+typedef struct R_fill_state_s {
+ shading_fill_state_common;
+ const gs_shading_R_t *psh;
+ gs_rect rect;
+ gs_point delta;
+ double dr, width, dd;
+} R_fill_state_t;
+
+/* Note t0 and t1 vary over [0..1], not the Domain. */
+private int
+R_fill_region(const R_fill_state_t * pfs, gs_client_color cc[2],
+ floatp t0, floatp t1)
+{
+ const gs_shading_R_t * const psh = pfs->psh;
+
+top:
+ if (!shade_colors2_converge(cc, (const shading_fill_state_t *)pfs)) {
+ /*
+ * The colors don't converge. Is the annulus less than 1 pixel wide?
+ */
+ if (pfs->width * (t1 - t0) > 1)
+ goto recur;
+ /* We could do the 1-pixel case a lot faster! */
+ }
+ /* Fill the region with the color. */
+ {
+ gx_device_color dev_color;
+ const gs_color_space *pcs = psh->params.ColorSpace;
+ gs_imager_state *pis = pfs->pis;
+ double
+ x0 = psh->params.Coords[0] + pfs->delta.x * t0,
+ y0 = psh->params.Coords[1] + pfs->delta.y * t0,
+ r0 = psh->params.Coords[2] + pfs->dr * t0;
+ double
+ x1 = psh->params.Coords[0] + pfs->delta.x * t1,
+ y1 = psh->params.Coords[1] + pfs->delta.y * t1,
+ r1 = psh->params.Coords[2] + pfs->dr * t1;
+ gx_path *ppath = gx_path_alloc(pis->memory, "R_fill");
+ int code;
+
+ (*pcs->type->restrict_color)(&cc[0], pcs);
+ (*pcs->type->remap_color)(&cc[0], pcs, &dev_color, pis,
+ pfs->dev, gs_color_select_texture);
+ if ((code = gs_imager_arc_add(ppath, pis, false, x0, y0, r0,
+ 0.0, 360.0, false)) >= 0 &&
+ (code = gs_imager_arc_add(ppath, pis, true, x1, y1, r1,
+ 0.0, 360.0, false)) >= 0
+ ) {
+ code = shade_fill_path((const shading_fill_state_t *)pfs,
+ ppath, &dev_color);
+ }
+ gx_path_free(ppath, "R_fill");
+ return code;
+ }
+
+ /*
+ * No luck. Subdivide the interval and recur.
+ */
+recur:
+ {
+ gs_client_color ccm, rcc[2];
+ gs_function_t *pfn = psh->params.Function;
+ float tm = (t0 + t1) * 0.5;
+ float dm = tm * pfs->dd + psh->params.Domain[0];
+
+ gs_function_evaluate(pfn, &dm, ccm.paint.values);
+ rcc[0].paint = cc[0].paint;
+ rcc[1].paint = ccm.paint;
+ R_fill_region(pfs, rcc, t0, tm);
+ cc[0].paint = ccm.paint;
+ t0 = tm;
+ goto top;
+ }
+}
+
+int
+gs_shading_R_fill_rectangle(const gs_shading_t * psh0, const gs_rect * rect,
+ gx_device * dev, gs_imager_state * pis)
+{
+ const gs_shading_R_t *const psh = (const gs_shading_R_t *)psh0;
+ R_fill_state_t state;
+ gs_client_color cc[2];
+ float d0 = psh->params.Domain[0], d1 = psh->params.Domain[1], dd = d1 - d0;
+ float t[2];
+ int i;
+
+ shade_init_fill_state((shading_fill_state_t *) & state, psh0, dev, pis);
+ state.psh = psh;
+ state.rect = *rect;
+ /* Compute the parameter range. */
+ t[0] = d0;
+ t[1] = d1;
+/****** INTERSECT Domain WITH rect ******/
+ for (i = 0; i < 2; ++i)
+ gs_function_evaluate(psh->params.Function, &t[i],
+ cc[i].paint.values);
+ state.delta.x = psh->params.Coords[3] - psh->params.Coords[0];
+ state.delta.y = psh->params.Coords[4] - psh->params.Coords[1];
+ state.dr = psh->params.Coords[5] - psh->params.Coords[2];
+ /*
+ * Compute the annulus width in its thickest direction. This is
+ * only used for a conservative check, so it can be pretty crude
+ * (and it is!).
+ */
+ state.width =
+ (fabs(pis->ctm.xx) + fabs(pis->ctm.xy) + fabs(pis->ctm.yx) +
+ fabs(pis->ctm.yy)) * fabs(state.dr);
+ state.dd = dd;
+/****** DOESN'T HANDLE Extend ******/
+ return R_fill_region(&state, cc, (t[0] - d0) / dd, (t[1] - d0) / dd);
+}
diff --git a/pstoraster/gxshade4.c b/pstoraster/gxshade4.c
new file mode 100644
index 000000000..f4daab43d
--- /dev/null
+++ b/pstoraster/gxshade4.c
@@ -0,0 +1,285 @@
+/* Copyright (C) 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Rendering for Gouraud triangle shadings */
+#include "memory_.h"
+#include "gx.h"
+#include "gserrors.h"
+#include "gsmatrix.h" /* for gscoord.h */
+#include "gscoord.h"
+#include "gxcspace.h"
+#include "gxdcolor.h"
+#include "gxdevcli.h"
+#include "gxistate.h"
+#include "gxpath.h"
+#include "gxshade.h"
+#include "gxshade4.h"
+
+/* ---------------- Triangle mesh filling ---------------- */
+
+/* Initialize the fill state for triangle shading. */
+void
+mesh_init_fill_state(mesh_fill_state_t * pfs, const gs_shading_mesh_t * psh,
+ const gs_rect * rect, gx_device * dev,
+ gs_imager_state * pis)
+{
+ shade_init_fill_state((shading_fill_state_t *) pfs,
+ (const gs_shading_t *)psh, dev, pis);
+ pfs->pshm = psh;
+ shade_bbox_transform2fixed(rect, pis, &pfs->rect);
+}
+
+#define SET_MIN_MAX_3(vmin, vmax, a, b, c)\
+ if ( a < b ) vmin = a, vmax = b; else vmin = b, vmax = a;\
+ if ( c < vmin ) vmin = c; else if ( c > vmax ) vmax = c
+
+int
+mesh_fill_triangle(const mesh_fill_state_t * pfs, const mesh_vertex_t *va,
+ const mesh_vertex_t *vb, const mesh_vertex_t *vc,
+ bool check)
+{
+ const gs_shading_mesh_t *psh = pfs->pshm;
+ int ci;
+
+ /*
+ * Fill the triangle with vertices at va->p, vb->p, and vc->p
+ * with color va->cc.
+ * If check is true, check for whether the triangle is entirely
+ * inside the rectangle, entirely outside, or partly inside;
+ * if check is false, assume the triangle is entirely inside.
+ */
+ if (check) {
+ fixed xmin, ymin, xmax, ymax;
+
+ SET_MIN_MAX_3(xmin, xmax, va->p.x, vb->p.x, vc->p.x);
+ SET_MIN_MAX_3(ymin, ymax, va->p.y, vb->p.y, vc->p.y);
+ if (xmin >= pfs->rect.p.x && xmax <= pfs->rect.q.x &&
+ ymin >= pfs->rect.p.y && ymax <= pfs->rect.q.y
+ ) {
+ /* The triangle is entirely inside the rectangle. */
+ check = false;
+ } else if (xmin >= pfs->rect.q.x || xmax <= pfs->rect.p.x ||
+ ymin >= pfs->rect.q.y || ymax <= pfs->rect.p.y
+ ) {
+ /* The triangle is entirely outside the rectangle. */
+ return 0;
+ }
+ }
+ /* Check whether the colors fall within the smoothness criterion. */
+ for (ci = 0; ci < pfs->num_components; ++ci) {
+ float c0 = va->cc[ci], c1 = vb->cc[ci], c2 = vc->cc[ci];
+ float cmin, cmax;
+
+ SET_MIN_MAX_3(cmin, cmax, c0, c1, c2);
+ if (cmax - cmin > pfs->cc_max_error[ci])
+ goto recur;
+ }
+ /* Fill the triangle with the color. */
+ {
+ gx_device_color dev_color;
+ const gs_color_space *pcs = psh->params.ColorSpace;
+ gs_imager_state *pis = pfs->pis;
+ gs_client_color fcc;
+ int code;
+
+ memcpy(&fcc.paint, va->cc, sizeof(fcc.paint));
+ (*pcs->type->restrict_color)(&fcc, pcs);
+ (*pcs->type->remap_color)(&fcc, pcs, &dev_color, pis,
+ pfs->dev, gs_color_select_texture);
+/****** SHOULD ADD adjust ON ANY OUTSIDE EDGES ******/
+#if 0
+ {
+ gx_path *ppath = gx_path_alloc(pis->memory, "Gt_fill");
+
+ gx_path_add_point(ppath, va->p.x, va->p.y);
+ gx_path_add_line(ppath, vb->p.x, vb->p.y);
+ gx_path_add_line(ppath, vc->p.x, vc->p.y);
+ code = shade_fill_path((const shading_fill_state_t *)pfs,
+ ppath, &dev_color);
+ gx_path_free(ppath, "Gt_fill");
+ }
+#else
+ code = (*dev_proc(pfs->dev, fill_triangle))
+ (pfs->dev, va->p.x, va->p.y,
+ vb->p.x - va->p.x, vb->p.y - va->p.y,
+ vc->p.x - va->p.x, vc->p.y - va->p.y,
+ &dev_color, pis->log_op);
+#endif
+ return code;
+ }
+ /*
+ * Subdivide the triangle and recur. The only subdivision method
+ * that doesn't seem to create anomalous shapes divides the
+ * triangle in 4, using the midpoints of each side.
+ */
+recur:
+ {
+ mesh_vertex_t vab, vac, vbc;
+ int i;
+ int code;
+
+#define MIDPOINT_FAST(a,b) arith_rshift_1((a) + (b) + 1)
+ vab.p.x = MIDPOINT_FAST(va->p.x, vb->p.x);
+ vab.p.y = MIDPOINT_FAST(va->p.y, vb->p.y);
+ vac.p.x = MIDPOINT_FAST(va->p.x, vc->p.x);
+ vac.p.y = MIDPOINT_FAST(va->p.y, vc->p.y);
+ vbc.p.x = MIDPOINT_FAST(vb->p.x, vc->p.x);
+ vbc.p.y = MIDPOINT_FAST(vb->p.y, vc->p.y);
+#undef MIDPOINT_FAST
+ for (i = 0; i < pfs->num_components; ++i) {
+ float ta = va->cc[i], tb = vb->cc[i], tc = vc->cc[i];
+
+ vab.cc[i] = (ta + tb) * 0.5;
+ vac.cc[i] = (ta + tc) * 0.5;
+ vbc.cc[i] = (tb + tc) * 0.5;
+ }
+ /* Do the "A" triangle. */
+ code = mesh_fill_triangle(pfs, va, &vab, &vac, check);
+ if (code < 0)
+ return code;
+ /* Do the central triangle. */
+ code = mesh_fill_triangle(pfs, &vab, &vac, &vbc, check);
+ if (code < 0)
+ return code;
+ /* Do the "C" triangle. */
+ code = mesh_fill_triangle(pfs, &vac, &vbc, vc, check);
+ if (code < 0)
+ return code;
+ /* Do the "B" triangle. */
+ return mesh_fill_triangle(pfs, &vab, vb, &vbc, check);
+ }
+}
+
+/* ---------------- Gouraud triangle shadings ---------------- */
+
+private int
+Gt_next_vertex(const gs_shading_mesh_t * psh, shade_coord_stream_t * cs,
+ mesh_vertex_t * vertex)
+{
+ int code = shade_next_vertex(cs, vertex);
+
+ if (code >= 0 && psh->params.Function) {
+ /* Decode the color with the function. */
+ code = gs_function_evaluate(psh->params.Function, vertex->cc,
+ vertex->cc);
+ }
+ return code;
+}
+
+inline private int
+Gt_fill_triangle(const mesh_fill_state_t * pfs, const mesh_vertex_t * va,
+ const mesh_vertex_t * vb, const mesh_vertex_t * vc)
+{
+ return mesh_fill_triangle(pfs, va, vb, vc, true);
+}
+
+int
+gs_shading_FfGt_fill_rectangle(const gs_shading_t * psh0, const gs_rect * rect,
+ gx_device * dev, gs_imager_state * pis)
+{
+ const gs_shading_FfGt_t * const psh = (const gs_shading_FfGt_t *)psh0;
+ mesh_fill_state_t state;
+ shade_coord_stream_t cs;
+ int num_bits = psh->params.BitsPerFlag;
+ int flag;
+ mesh_vertex_t va, vb, vc;
+
+ mesh_init_fill_state(&state, (const gs_shading_mesh_t *)psh, rect,
+ dev, pis);
+ shade_next_init(&cs, (const gs_shading_mesh_params_t *)&psh->params,
+ pis);
+ while ((flag = shade_next_flag(&cs, num_bits)) >= 0) {
+ int code;
+
+ switch (flag) {
+ default:
+ return_error(gs_error_rangecheck);
+ case 0:
+ if ((code = Gt_next_vertex(state.pshm, &cs, &va)) < 0 ||
+ (code = shade_next_flag(&cs, num_bits)) < 0 ||
+ (code = Gt_next_vertex(state.pshm, &cs, &vb)) < 0 ||
+ (code = shade_next_flag(&cs, num_bits)) < 0
+ )
+ return code;
+ goto v2;
+ case 1:
+ va = vb;
+ case 2:
+ vb = vc;
+v2: if ((code = Gt_next_vertex(state.pshm, &cs, &vc)) < 0 ||
+ (code = Gt_fill_triangle(&state, &va, &vb, &vc)) < 0
+ )
+ return code;
+ }
+ }
+ return 0;
+}
+
+int
+gs_shading_LfGt_fill_rectangle(const gs_shading_t * psh0, const gs_rect * rect,
+ gx_device * dev, gs_imager_state * pis)
+{
+ const gs_shading_LfGt_t * const psh = (const gs_shading_LfGt_t *)psh0;
+ mesh_fill_state_t state;
+ shade_coord_stream_t cs;
+ mesh_vertex_t *vertex;
+ mesh_vertex_t next;
+ int per_row = psh->params.VerticesPerRow;
+ int i, code = 0;
+
+ mesh_init_fill_state(&state, (const gs_shading_mesh_t *)psh, rect,
+ dev, pis);
+ shade_next_init(&cs, (const gs_shading_mesh_params_t *)&psh->params,
+ pis);
+ vertex = (mesh_vertex_t *)
+ gs_alloc_byte_array(pis->memory, per_row, sizeof(*vertex),
+ "gs_shading_LfGt_render");
+ if (vertex == 0)
+ return_error(gs_error_VMerror);
+ for (i = 0; i < per_row; ++i)
+ if ((code = Gt_next_vertex(state.pshm, &cs, &vertex[i])) < 0)
+ goto out;
+ while (!seofp(cs.s)) {
+ code = Gt_next_vertex(state.pshm, &cs, &next);
+ if (code < 0)
+ goto out;
+ for (i = 1; i < per_row; ++i) {
+ code = Gt_fill_triangle(&state, &vertex[i - 1], &vertex[i], &next);
+ if (code < 0)
+ goto out;
+ vertex[i - 1] = next;
+ code = Gt_next_vertex(state.pshm, &cs, &next);
+ if (code < 0)
+ goto out;
+ code = Gt_fill_triangle(&state, &vertex[i], &vertex[i - 1], &next);
+ if (code < 0)
+ goto out;
+ }
+ vertex[per_row - 1] = next;
+ }
+out:
+ gs_free_object(pis->memory, vertex, "gs_shading_LfGt_render");
+ return code;
+}
diff --git a/pstoraster/gxshade4.h b/pstoraster/gxshade4.h
new file mode 100644
index 000000000..5a6189468
--- /dev/null
+++ b/pstoraster/gxshade4.h
@@ -0,0 +1,58 @@
+/* Copyright (C) 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Internal definitions for triangle shading rendering */
+
+#ifndef gxshade4_INCLUDED
+# define gxshade4_INCLUDED
+
+/*
+ * Define the fill state structure for triangle shadings. This is used
+ * both for the Gouraud triangle shading types and for the Coons and
+ * tensor patch types.
+ *
+ * The shading pointer is named pshm rather than psh in case subclasses
+ * also want to store a pointer of a more specific type.
+ */
+#define mesh_fill_state_common\
+ shading_fill_state_common;\
+ const gs_shading_mesh_t *pshm;\
+ gs_fixed_rect rect
+typedef struct mesh_fill_state_s {
+ mesh_fill_state_common;
+} mesh_fill_state_t;
+
+/* Initialize the fill state for triangle shading. */
+void mesh_init_fill_state(P5(mesh_fill_state_t * pfs,
+ const gs_shading_mesh_t * psh,
+ const gs_rect * rect,
+ gx_device * dev, gs_imager_state * pis));
+
+/* Fill one triangle in a mesh. */
+int mesh_fill_triangle(P5(const mesh_fill_state_t * pfs,
+ const mesh_vertex_t *va, const mesh_vertex_t *vb,
+ const mesh_vertex_t *vc, bool check_clipping));
+
+#endif /* gxshade4_INCLUDED */
diff --git a/pstoraster/gxshade6.c b/pstoraster/gxshade6.c
new file mode 100644
index 000000000..4934e33e8
--- /dev/null
+++ b/pstoraster/gxshade6.c
@@ -0,0 +1,566 @@
+/* Copyright (C) 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Rendering for Coons and tensor patch shadings */
+#include "memory_.h"
+#include "gx.h"
+#include "gserrors.h"
+#include "gsmatrix.h" /* for gscoord.h */
+#include "gscoord.h"
+#include "gxcspace.h"
+#include "gxdcolor.h"
+#include "gxistate.h"
+#include "gxshade.h"
+#include "gxshade4.h"
+#include "gzpath.h"
+
+/* ================ Utilities ================ */
+
+/* Define one segment (vertex and next control points) of a curve. */
+typedef struct patch_curve_s {
+ mesh_vertex_t vertex;
+ gs_fixed_point control[2];
+} patch_curve_t;
+
+/* Get colors for patch vertices. */
+private int
+shade_next_colors(shade_coord_stream_t * cs, patch_curve_t * curves,
+ int num_vertices)
+{
+ int i, code = 0;
+
+ for (i = 0; i < num_vertices && code >= 0; ++i)
+ code = shade_next_color(cs, curves[i].vertex.cc);
+ return code;
+}
+
+/* Get a Bezier or tensor patch element. */
+private int
+shade_next_curve(shade_coord_stream_t * cs, patch_curve_t * curve)
+{
+ int code = shade_next_coords(cs, &curve->vertex.p, 1);
+
+ if (code >= 0)
+ code = shade_next_coords(cs, curve->control,
+ countof(curve->control));
+ return code;
+}
+
+/* Define a color to be used in curve rendering. */
+/* This may be a real client color, or a parametric function argument. */
+typedef struct patch_color_s {
+ float t; /* parametric value */
+ gs_client_color cc;
+} patch_color_t;
+
+/*
+ * Parse the next patch out of the input stream. Return 1 if done,
+ * 0 if patch, <0 on error.
+ */
+private int
+shade_next_patch(shade_coord_stream_t * cs, int BitsPerFlag,
+patch_curve_t curve[4], gs_fixed_point interior[4] /* 0 for Coons patch */ )
+{
+ int flag = shade_next_flag(cs, BitsPerFlag);
+ int num_colors, code;
+
+ if (flag < 0)
+ return 1; /* no more data */
+ switch (flag & 3) {
+ default:
+ return_error(gs_error_rangecheck); /* not possible */
+ case 0:
+ if ((code = shade_next_curve(cs, &curve[0])) < 0 ||
+ (code = shade_next_coords(cs, &curve[1].vertex.p, 1)) < 0
+ )
+ return code;
+ num_colors = 4;
+ goto vx;
+ case 1:
+ curve[0] = curve[1], curve[1].vertex = curve[2].vertex;
+ goto v3;
+ case 2:
+ curve[0] = curve[2], curve[1].vertex = curve[3].vertex;
+ goto v3;
+ case 3:
+ curve[1].vertex = curve[0].vertex, curve[0] = curve[3];
+v3: num_colors = 2;
+vx: if ((code = shade_next_coords(cs, curve[1].control, 2)) < 0 ||
+ (code = shade_next_curve(cs, &curve[2])) < 0 ||
+ (code = shade_next_curve(cs, &curve[3])) < 0 ||
+ (interior != 0 &&
+ (code = shade_next_coords(cs, interior, 4)) < 0) ||
+ (code = shade_next_colors(cs, &curve[4 - num_colors],
+ num_colors)) < 0
+ )
+ return code;
+ }
+ return 0;
+}
+
+/* Define the common state for rendering Coons and tensor patches. */
+typedef struct patch_fill_state_s {
+ mesh_fill_state_common;
+ const gs_function_t *Function;
+} patch_fill_state_t;
+
+/* Calculate the interpolated color at a given point. */
+/* Note that we must do this twice for bilinear interpolation. */
+private void
+patch_interpolate_color(patch_color_t * ppc, const patch_color_t * ppc0,
+ const patch_color_t * ppc1, const patch_fill_state_t * pfs, floatp t)
+{
+ if (pfs->Function)
+ ppc->t = ppc0->t + t * (ppc1->t - ppc0->t);
+ else {
+ int ci;
+
+ for (ci = pfs->num_components - 1; ci >= 0; --ci)
+ ppc->cc.paint.values[ci] =
+ ppc0->cc.paint.values[ci] +
+ t * (ppc1->cc.paint.values[ci] - ppc0->cc.paint.values[ci]);
+ }
+}
+
+/* Resolve a patch color using the Function if necessary. */
+private void
+patch_resolve_color(patch_color_t * ppc, const patch_fill_state_t * pfs)
+{
+ if (pfs->Function)
+ gs_function_evaluate(pfs->Function, &ppc->t, ppc->cc.paint.values);
+}
+
+/* ================ Specific shadings ================ */
+
+/*
+ * The curves are stored in a clockwise or counter-clockwise order that maps
+ * to the patch definition in a non-intuitive way:
+ */
+/* The starting points of the curves: */
+#define C1START 0
+#define D1START 0
+#define C2START 3
+#define D2START 1
+/* The control points of the curves (x means reversed order): */
+#define C1CTRL 0
+#define D1XCTRL 3
+#define C2XCTRL 2
+#define D2CTRL 1
+/* The end points of the curves: */
+#define C1END 1
+#define D1END 3
+#define C2END 2
+#define D2END 2
+
+/* ---------------- Common code ---------------- */
+
+/* Evaluate a curve at a given point. */
+private void
+curve_eval(gs_fixed_point * pt, const gs_fixed_point * p0,
+ const gs_fixed_point * p1, const gs_fixed_point * p2,
+ const gs_fixed_point * p3, floatp t)
+{
+ fixed a, b, c, d;
+ fixed t01, t12;
+
+ d = p0->x;
+ curve_points_to_coefficients(d, p1->x, p2->x, p3->x,
+ a, b, c, t01, t12);
+ pt->x = (fixed) (((a * t + b) * t + c) * t + d);
+ d = p0->y;
+ curve_points_to_coefficients(d, p1->y, p2->y, p3->y,
+ a, b, c, t01, t12);
+ pt->y = (fixed) (((a * t + b) * t + c) * t + d);
+ if_debug3('2', "[2]t=%g => (%g,%g)\n", t, fixed2float(pt->x),
+ fixed2float(pt->y));
+}
+
+/*
+ * Merge two arrays of splits, sorted in increasing order.
+ * Return the number of entries in the result, which might be less than
+ * n1 + n2 (if an a1 entry is equal to an a2 entry).
+ * a1 or a2 may overlap out as long as a1 - out >= n2 or a2 - out >= n1
+ * respectively.
+ */
+private int
+merge_splits(double *out, const double *a1, int n1, const double *a2, int n2)
+{
+ double *p = out;
+ int i1 = 0, i2 = 0;
+
+ /*
+ * We would like to write the body of the loop as an assignement
+ * with a conditional expression on the right, but gcc 2.7.2.3
+ * generates incorrect code if we do this.
+ */
+ while (i1 < n1 || i2 < n2)
+ if (i1 == n1)
+ *p++ = a2[i2++];
+ else if (i2 == n2 || a1[i1] < a2[i2])
+ *p++ = a1[i1++];
+ else if (a1[i1] > a2[i2])
+ *p++ = a2[i2++];
+ else
+ i1++, *p++ = a2[i2++];
+ return p - out;
+}
+
+/* Split a curve in both X and Y. Return the number of split points. */
+private int
+split_xy(double out[4], const patch_curve_t * curve, const gs_fixed_point * p3)
+{
+ double tx[2], ty[2];
+
+ return merge_splits(out, tx,
+ gx_curve_monotonic_points(curve->vertex.p.x,
+ curve->control[0].x,
+ curve->control[1].x,
+ p3->x, tx),
+ ty,
+ gx_curve_monotonic_points(curve->vertex.p.y,
+ curve->control[0].y,
+ curve->control[1].y,
+ p3->y, ty));
+}
+
+/*
+ * Compute the joint split points of 2 curves.
+ * Return the number of split points.
+ */
+private int
+split2_xy(double out[8], const patch_curve_t * curve1,
+ const gs_fixed_point * p31, const patch_curve_t * curve2,
+ const gs_fixed_point * p32)
+{
+ double t1[4], t2[4];
+
+ return merge_splits(out, t1, split_xy(t1, curve1, p31),
+ t2, split_xy(t2, curve2, p32));
+}
+
+private int
+patch_fill(const patch_fill_state_t * pfs, const patch_curve_t curve[4],
+ const gs_fixed_point interior[4],
+ void (*transform) (P5(gs_fixed_point *, const patch_curve_t[4],
+ const gs_fixed_point[4], floatp, floatp)))
+{ /*
+ * The specification says the output must appear to be produced in
+ * order of increasing values of v, and for equal v, in order of
+ * increasing u. However, all we actually have to do is follow this
+ * order with respect to sub-patches that might overlap, which can
+ * only occur as a result of non-monotonic curves; we can render
+ * each monotonic sub-patch in any order we want. Therefore, we
+ * begin by breaking up the patch into pieces that are monotonic
+ * with respect to all 4 edges. Since each edge has no more than
+ * 2 X and 2 Y split points (for a total of 4), taking both edges
+ * together we have a maximum of 8 split points for each axis.
+ *
+ * The current documentation doesn't say how the 4 curves
+ * correspond to the 'u' or 'v' edges. Pending clarification from
+ * Adobe, we assume the 1st and 3rd are the 'u' edges and the
+ * 2nd and 4th are the 'v' edges.
+ ****** CHECK AGAINST UPDATED DOC ******
+ */
+ double u[9], v[9];
+ int nu = split2_xy(u, &curve[0], &curve[1].vertex.p,
+ &curve[2], &curve[3].vertex.p);
+ int nv = split2_xy(v, &curve[1], &curve[2].vertex.p,
+ &curve[3], &curve[0].vertex.p);
+ int iu, iv, ju, jv, ku, kv;
+ double du, dv;
+ double v0, v1, vn, u0, u1, un;
+ patch_color_t c0, c1, c2, c3;
+ /*
+ * At some future time, we should set check = false if the curves
+ * fall entirely within the bounding rectangle. (Only a small
+ * performance optimization, to avoid making this check for each
+ * triangle.)
+ */
+ bool check = true;
+
+#ifdef DEBUG
+ if (gs_debug_c('2')) {
+ int k;
+
+ dlputs("[2]patch curves:\n");
+ for (k = 0; k < 4; ++k)
+ dprintf6(" (%g,%g) (%g,%g)(%g,%g)\n",
+ fixed2float(curve[k].vertex.p.x),
+ fixed2float(curve[k].vertex.p.y),
+ fixed2float(curve[k].control[0].x),
+ fixed2float(curve[k].control[0].y),
+ fixed2float(curve[k].control[1].x),
+ fixed2float(curve[k].control[1].y));
+ }
+#endif
+ /* Add boundary values to simplify the iteration. */
+ u[nu] = 1;
+ v[nv] = 1;
+
+ /*
+ * We're going to fill the curves by flattening them and then filling
+ * the resulting triangles. Start by computing the number of
+ * segments required for flattening each side of the patch.
+ */
+ {
+ fixed flatness = float2fixed(pfs->pis->flatness);
+ int i;
+ int log2_k[4];
+
+ for (i = 0; i < 4; ++i) {
+ curve_segment cseg;
+
+ cseg.p1 = curve[i].control[0];
+ cseg.p2 = curve[i].control[1];
+ cseg.pt = curve[(i + 1) & 3].vertex.p;
+ log2_k[i] =
+ gx_curve_log2_samples(curve[i].vertex.p.x, curve[i].vertex.p.y,
+ &cseg, flatness);
+ }
+ ku = 1 << max(log2_k[0], log2_k[2]);
+ kv = 1 << max(log2_k[1], log2_k[3]);
+ }
+ /*
+ * Since ku and kv are powers of 2, and since log2(k) is surely less
+ * than the number of bits in the mantissa of a double, 1/k ...
+ * (k-1)/k can all be represented exactly as doubles.
+ */
+ du = 1.0 / ku;
+ dv = 1.0 / kv;
+
+ /* Precompute the colors at the corners. */
+
+#define PATCH_SET_COLOR(c, v)\
+ if ( pfs->Function ) c.t = v.cc[0];\
+ else memcpy(c.cc.paint.values, v.cc, sizeof(c.cc.paint.values))
+
+ PATCH_SET_COLOR(c0, curve[0].vertex);
+ PATCH_SET_COLOR(c1, curve[1].vertex);
+ PATCH_SET_COLOR(c2, curve[2].vertex);
+ PATCH_SET_COLOR(c3, curve[3].vertex);
+
+#undef PATCH_SET_COLOR
+
+ /* Now iterate over the sub-patches. */
+ for (iv = 0, jv = 0, v0 = 0, v1 = vn = dv; jv < kv; v0 = v1, v1 = vn) {
+ patch_color_t cv[4];
+
+ /* Subdivide the interval if it crosses a split point. */
+
+#define CHECK_SPLIT(ix, jx, x1, xn, dx, ax)\
+ if (x1 > ax[ix])\
+ x1 = ax[ix++];\
+ else {\
+ xn += dx;\
+ jx++;\
+ if (x1 == ax[ix])\
+ ix++;\
+ }
+
+ CHECK_SPLIT(iv, jv, v1, vn, dv, v);
+
+ patch_interpolate_color(&cv[0], &c0, &c3, pfs, v0);
+ patch_interpolate_color(&cv[1], &c0, &c3, pfs, v1);
+ patch_interpolate_color(&cv[2], &c1, &c2, pfs, v0);
+ patch_interpolate_color(&cv[3], &c1, &c2, pfs, v1);
+
+ for (iu = 0, ju = 0, u0 = 0, u1 = un = du; ju < ku; u0 = u1, u1 = un) {
+ patch_color_t cu[4];
+ int code;
+
+ CHECK_SPLIT(iu, ju, u1, un, du, u);
+
+#undef CHECK_SPLIT
+
+ patch_interpolate_color(&cu[0], &cv[0], &cv[2], pfs, u0);
+ patch_resolve_color(&cu[0], pfs);
+ patch_interpolate_color(&cu[1], &cv[0], &cv[2], pfs, u1);
+ patch_resolve_color(&cu[1], pfs);
+ patch_interpolate_color(&cu[2], &cv[1], &cv[3], pfs, u1);
+ patch_resolve_color(&cu[2], pfs);
+ patch_interpolate_color(&cu[3], &cv[1], &cv[3], pfs, u0);
+ patch_resolve_color(&cu[3], pfs);
+ if_debug6('2', "[2]u[%d]=(%g,%g), v[%d]=(%g,%g)\n",
+ iu, u0, u1, iv, v0, v1);
+
+ /* Fill the sub-patch given by ((u0,v0),(u1,v1)). */
+ {
+ mesh_vertex_t mv[4];
+
+ (*transform)(&mv[0].p, curve, interior, u0, v0);
+ (*transform)(&mv[1].p, curve, interior, u1, v0);
+ (*transform)(&mv[2].p, curve, interior, u1, v1);
+ (*transform)(&mv[3].p, curve, interior, u0, v1);
+ memcpy(&mv[0].cc, cu[0].cc.paint.values, sizeof(mv[0].cc));
+ memcpy(&mv[1].cc, cu[1].cc.paint.values, sizeof(mv[1].cc));
+ memcpy(&mv[2].cc, cu[2].cc.paint.values, sizeof(mv[2].cc));
+ memcpy(&mv[3].cc, cu[3].cc.paint.values, sizeof(mv[3].cc));
+ code = mesh_fill_triangle((const mesh_fill_state_t *)pfs,
+ &mv[0], &mv[1], &mv[2], check);
+ if (code < 0)
+ return code;
+ code = mesh_fill_triangle((const mesh_fill_state_t *)pfs,
+ &mv[2], &mv[3], &mv[0], check);
+ if (code < 0)
+ return code;
+ }
+ }
+ }
+ return 0;
+}
+
+/* ---------------- Coons patch shading ---------------- */
+
+/* Calculate the device-space coordinate corresponding to (u,v). */
+private void
+Cp_transform(gs_fixed_point * pt, const patch_curve_t curve[4],
+ const gs_fixed_point ignore_interior[4], floatp u, floatp v)
+{
+ double co_u = 1.0 - u, co_v = 1.0 - v;
+ gs_fixed_point c1u, d1v, c2u, d2v;
+
+ curve_eval(&c1u, &curve[C1START].vertex.p,
+ &curve[C1CTRL].control[0], &curve[C1CTRL].control[1],
+ &curve[C1END].vertex.p, u);
+ curve_eval(&d1v, &curve[D1START].vertex.p,
+ &curve[D1XCTRL].control[1], &curve[D1XCTRL].control[0],
+ &curve[D1END].vertex.p, v);
+ curve_eval(&c2u, &curve[C2START].vertex.p,
+ &curve[C2XCTRL].control[1], &curve[C2XCTRL].control[0],
+ &curve[C2END].vertex.p, u);
+ curve_eval(&d2v, &curve[D2START].vertex.p,
+ &curve[D2CTRL].control[0], &curve[D2CTRL].control[1],
+ &curve[D2END].vertex.p, v);
+#define COMPUTE_COORD(xy)\
+ pt->xy = (fixed)\
+ ((co_v * c1u.xy + v * c2u.xy) + (co_u * d1v.xy + u * d2v.xy) -\
+ (co_v * (co_u * curve[C1START].vertex.p.xy +\
+ u * curve[C1END].vertex.p.xy) +\
+ v * (co_u * curve[C2START].vertex.p.xy +\
+ u * curve[C2END].vertex.p.xy)))
+ COMPUTE_COORD(x);
+ COMPUTE_COORD(y);
+#undef COMPUTE_COORD
+ if_debug4('2', "[2](u=%g,v=%g) => (%g,%g)\n",
+ u, v, fixed2float(pt->x), fixed2float(pt->y));
+}
+
+int
+gs_shading_Cp_fill_rectangle(const gs_shading_t * psh0, const gs_rect * rect,
+ gx_device * dev, gs_imager_state * pis)
+{
+ const gs_shading_Cp_t * const psh = (const gs_shading_Cp_t *)psh0;
+ patch_fill_state_t state;
+ shade_coord_stream_t cs;
+ patch_curve_t curve[4];
+ int code;
+
+ mesh_init_fill_state((mesh_fill_state_t *) & state,
+ (const gs_shading_mesh_t *)psh0, rect, dev, pis);
+ state.Function = psh->params.Function;
+ shade_next_init(&cs, (const gs_shading_mesh_params_t *)&psh->params,
+ pis);
+ while ((code = shade_next_patch(&cs, psh->params.BitsPerFlag,
+ curve, NULL)) == 0 &&
+ (code = patch_fill(&state, curve, NULL, Cp_transform)) >= 0
+ )
+ DO_NOTHING;
+ return min(code, 0);
+}
+
+/* ---------------- Tensor product patch shading ---------------- */
+
+/* Calculate the device-space coordinate corresponding to (u,v). */
+private void
+Tpp_transform(gs_fixed_point * pt, const patch_curve_t curve[4],
+ const gs_fixed_point interior[4], floatp u, floatp v)
+{
+ double Bu[4], Bv[4];
+ gs_fixed_point pts[4][4];
+ int i, j;
+ fixed x = 0, y = 0;
+
+ /* Compute the Bernstein polynomials of u and v. */
+ {
+ double u2 = u * u, co_u = 1.0 - u, co_u2 = co_u * co_u;
+ double v2 = v * v, co_v = 1.0 - v, co_v2 = co_v * co_v;
+
+ Bu[0] = co_u * co_u2, Bu[1] = 3 * u * co_u2,
+ Bu[2] = 3 * u2 * co_u, Bu[3] = u * u2;
+ Bv[0] = co_v * co_v2, Bv[1] = 3 * v * co_v2,
+ Bv[2] = 3 * v2 * co_v, Bv[3] = v * v2;
+ }
+
+ /* Arrange the points into an indexable order. */
+ pts[0][0] = curve[0].vertex.p;
+ pts[1][0] = curve[0].control[0];
+ pts[2][0] = curve[0].control[1];
+ pts[3][0] = curve[1].vertex.p;
+ pts[3][1] = curve[1].control[0];
+ pts[3][2] = curve[1].control[1];
+ pts[3][3] = curve[2].vertex.p;
+ pts[2][3] = curve[2].control[0];
+ pts[1][3] = curve[2].control[1];
+ pts[0][3] = curve[3].vertex.p;
+ pts[0][2] = curve[3].control[0];
+ pts[0][1] = curve[3].control[1];
+ pts[1][1] = interior[0];
+ pts[2][1] = interior[1];
+ pts[2][2] = interior[2];
+ pts[1][2] = interior[3];
+
+ /* Now compute the actual point. */
+ for (i = 0; i < 4; ++i)
+ for (j = 0; j < 4; ++j) {
+ double coeff = Bu[i] * Bv[j];
+
+ x += pts[i][j].x * coeff, y += pts[i][j].y * coeff;
+ }
+ pt->x = x, pt->y = y;
+}
+
+int
+gs_shading_Tpp_fill_rectangle(const gs_shading_t * psh0, const gs_rect * rect,
+ gx_device * dev, gs_imager_state * pis)
+{
+ const gs_shading_Tpp_t * const psh = (const gs_shading_Tpp_t *)psh0;
+ patch_fill_state_t state;
+ shade_coord_stream_t cs;
+ patch_curve_t curve[4];
+ gs_fixed_point interior[4];
+ int code;
+
+ mesh_init_fill_state((mesh_fill_state_t *) & state,
+ (const gs_shading_mesh_t *)psh0, rect, dev, pis);
+ state.Function = psh->params.Function;
+ shade_next_init(&cs, (const gs_shading_mesh_params_t *)&psh->params,
+ pis);
+ while ((code = shade_next_patch(&cs, psh->params.BitsPerFlag,
+ curve, interior)) == 0 &&
+ (code = patch_fill(&state, curve, interior, Tpp_transform)) >= 0
+ )
+ DO_NOTHING;
+ return min(code, 0);
+}
diff --git a/pstoraster/gxstate.h b/pstoraster/gxstate.h
new file mode 100644
index 000000000..49ea57a91
--- /dev/null
+++ b/pstoraster/gxstate.h
@@ -0,0 +1,86 @@
+/* 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 supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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.
+ *
+ * As of release 4.36, the copy procedure is superseded by copy_for
+ * (although it will still be called if there is no copy_for procedure).
+ */
+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 enum {
+ copy_for_gsave, /* from = current, to = new(saved) */
+ copy_for_grestore, /* from = saved, to = current */
+ copy_for_gstate, /* from = current, to = new(copy) */
+ copy_for_setgstate, /* from = stored, to = current */
+ copy_for_copygstate, /* from & to are specified explicitly */
+ copy_for_currentgstate /* from = current, to = stored */
+} gs_state_copy_reason_t;
+
+/* Note that the 'from' argument of copy_for is not const. */
+/* This is deliberate -- some clients need this. */
+typedef int (*gs_state_copy_for_proc_t) (P3(void *to, void *from,
+ gs_state_copy_reason_t reason));
+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_copy_for_proc_t copy_for;
+} 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..77ff1094d
--- /dev/null
+++ b/pstoraster/gxstroke.c
@@ -0,0 +1,1319 @@
+/* Copyright (C) 1989, 1995, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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 "gxfarith.h"
+#include "gxmatrix.h"
+#include "gscoord.h"
+#include "gsdevice.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
+
+/*
+ * For some reason, we commented out the optimization for portrait,
+ * landscape, and uniform (non-scaled) transformations. We have no record
+ * of why we did this, and we don't know what bugs re-enabling it may
+ * introduce.
+ */
+#define OPTIMIZE_ORIENTATION
+
+/*
+ * 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 and
+ * triangular joins, the maximum expansion on each side (in user space) is
+ * K * line_width/2
+ * where K is determined as follows:
+ * If the path is only a single line segment, K = 1;
+ * if triangular joins, K = 2;
+ * if miter joins, K = miter_limit;
+ * otherwise, K = 1.
+ * If the amount is too large to fit in a gs_fixed_point, return
+ * gs_error_limitcheck.
+ *
+ * If the miter limit is very large, the foregoing computation will produce
+ * a result that is much too large; we would like to tighten this up at
+ * some point in the future.
+ */
+int
+gx_stroke_path_expansion(const gs_imager_state * pis, const gx_path * ppath,
+ gs_fixed_point * ppt)
+{
+ const subpath *psub = ppath->first_subpath;
+ const segment *pseg;
+ double expand =
+ (!gx_path_has_curves(ppath) && gx_path_subpath_count(ppath) <= 1 &&
+ (psub == 0 || (pseg = psub->next) == 0 ||
+ (pseg = pseg->next) == 0 || pseg->type == s_line_close) ? 1.0 :
+ pis->line_params.join == gs_join_miter ?
+ pis->line_params.miter_limit :
+ pis->line_params.join == gs_join_triangle ? 2.0 : 1.0) *
+ 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;
+ {
+ float exx = expand * max(cx1, cx2);
+ float exy = expand * max(cy1, cy2);
+ int code = set_float2fixed_vars(ppt->x, exx);
+
+ if (code < 0)
+ return code;
+ return set_float2fixed_vars(ppt->y, exy);
+ }
+}
+
+/*
+ * 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 *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 *ep_ptr;
+typedef const endpoint *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 *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 bool width_is_thin(P1(pl_ptr));
+private void adjust_stroke(P3(pl_ptr, const gs_imager_state *, bool));
+private int line_join_points(P5(const gx_line_params * pgs_lp,
+ pl_ptr plp, pl_ptr nplp,
+ gs_fixed_point * join_points,
+ const gs_matrix * pmat));
+private void compute_caps(P1(pl_ptr));
+private int add_points(P4(gx_path *, const gs_fixed_point *,
+ int, bool));
+private int add_round_cap(P2(gx_path *, const_ep_ptr));
+private int cap_points(P3(gs_line_cap, const_ep_ptr,
+ gs_fixed_point * /*[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, */
+/* code, ppath, exit(label). */
+#define fill_stroke_path(thin)\
+ if(to_path==&stroke_path_body && !gx_path_is_void(&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, NULL);\
+ gx_path_free(&stroke_path_body, "fill_stroke_path");\
+ if ( code < 0 ) goto exit;\
+ gx_path_init_local(&stroke_path_body, 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 proc(P10(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 *, int))
+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.
+ *
+ * Note that gx_stroke_path_only with to_path != NULL may clip the path to
+ * the clipping path, as for to_path == NULL. This is almost never
+ * what is wanted.
+ */
+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;
+ 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 =
+ (
+#ifdef OPTIMIZE_ORIENTATION
+ 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) :
+ /* We should optimize uniform rotated coordinate systems */
+ /* here as well, but we don't. */
+#endif
+ (uniform = 0,
+ reflected = xy * yx > xx * yy,
+ orient_other));
+ segment_notes not_first =
+ (!is_fzero(pis->line_params.dot_length) ? sn_not_first : sn_none);
+ float line_width = pgs_lp->half_width; /* (*half* the line width) */
+ bool always_thin;
+ double line_width_and_scale, device_line_width_scale;
+ double device_dot_length = pgs_lp->dot_length * fixed_1;
+ const subpath *psub;
+
+#ifdef DEBUG
+ if (gs_debug_c('o')) {
+ int count = pgs_lp->dash.pattern_size;
+ int i;
+
+ dlprintf3("[o]half_width=%f, cap=%d, join=%d,\n",
+ pgs_lp->half_width, (int)pgs_lp->cap, (int)pgs_lp->join);
+ dlprintf2(" miter_limit=%f, miter_check=%f,\n",
+ pgs_lp->miter_limit, pgs_lp->miter_check);
+ dlprintf1(" dash pattern=%d", count);
+ for (i = 0; i < count; i++)
+ dprintf1(",%f", pgs_lp->dash.pattern[i]);
+ dputs(",\n");
+ dlprintf4("\toffset=%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;
+
+ if (gx_stroke_path_expansion(pis, ppath, &expansion) < 0) {
+ /* The expansion is so large it caused a limitcheck. */
+ ibox.p.x = ibox.p.y = min_fixed;
+ ibox.q.x = ibox.q.y = max_fixed;
+ } else {
+ expansion.x += pis->fill_adjust.x;
+ expansion.y += pis->fill_adjust.y;
+ /*
+ * It's theoretically possible for the following computations to
+ * overflow, so we need to check for this.
+ */
+ ibox.p.x = (ibox.p.x < min_fixed + expansion.x ? min_fixed :
+ ibox.p.x - expansion.x);
+ ibox.p.y = (ibox.p.y < min_fixed + expansion.y ? min_fixed :
+ ibox.p.y - expansion.y);
+ ibox.q.x = (ibox.q.x > max_fixed - expansion.x ? max_fixed :
+ ibox.q.x + expansion.x);
+ ibox.q.y = (ibox.q.y > max_fixed - expansion.y ? max_fixed :
+ ibox.q.y + expansion.y);
+ }
+ }
+ /* Check the expanded bounding box against the clipping regions. */
+ if (pcpath)
+ gx_cpath_inner_box(pcpath, &cbox);
+ else if (pdevc)
+ (*dev_proc(dev, get_clipping_box)) (dev, &cbox);
+ else {
+ /* This is strokepath, not stroke. Don't clip. */
+ cbox = ibox;
+ }
+ if (!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;
+
+ if (pcpath) {
+ 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);
+ } else
+ rect_intersect(ibox, cbox);
+ 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.
+ *
+ * If there is a clipping path, set up a clipping device.
+ */
+ if (pcpath) {
+ gx_make_clip_device(&cdev, &cdev, gx_cpath_list(pcpath));
+ cdev.target = dev;
+ cdev.max_fill_band = dev->max_fill_band;
+ dev = (gx_device *) & cdev;
+ (*dev_proc(dev, open_device)) (dev);
+ }
+ }
+ fill_params.rule = gx_rule_winding_number;
+ fill_params.flatness = pis->flatness;
+#ifdef USE_FILL_ADJUSTMENT
+ fill_params.fill_zero_width =
+ (pis->fill_adjust.x | pis->fill_adjust.y) != 0;
+#else
+ fill_params.fill_zero_width = false;
+#endif
+ 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. */
+ double xsq = xx * xx + xy * xy;
+ double ysq = yx * yx + yy * yy;
+ double cross = xx * yx + xy * yy;
+
+ if (cross < 0)
+ cross = 0;
+ always_thin =
+ ((max(xsq, ysq) + cross) * line_width * line_width
+ < 0.25);
+ }
+ }
+ }
+ if_debug7('o', "[o]ctm=(%g,%g,%g,%g,%g,%g) thin=%d\n",
+ xx, xy, yx, yy, pis->ctm.tx, pis->ctm.ty, always_thin);
+ if (device_dot_length != 0) {
+ /*
+ * Compute the dot length in device space. We can't do this
+ * quite right for non-uniform coordinate systems; too bad.
+ */
+ gs_matrix mat;
+ const gs_matrix *pmat;
+
+ if (pgs_lp->dot_length_absolute) {
+ gs_deviceinitialmatrix(pdev, &mat);
+ pmat = &mat;
+ } else
+ pmat = (const gs_matrix *)&pis->ctm;
+ device_dot_length *= fabs(pmat->xy) + fabs(pmat->yy);
+ }
+ /* Start by flattening the path. We should do this on-the-fly.... */
+ if (!gx_path_has_curves(ppath)) { /* don't need to flatten */
+ if (!ppath->first_subpath)
+ return 0;
+ spath = ppath;
+ } else {
+ gx_path_init_local(&fpath, ppath->memory);
+ if ((code = gx_path_add_flattened_accurate(ppath, &fpath,
+ params->flatness, pis->accurate_curves)) < 0
+ )
+ return code;
+ spath = &fpath;
+ }
+ if (dash_count) {
+ gx_path_init_local(&dpath, ppath->memory);
+ code = gx_path_add_dash_expansion(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_local(&stroke_path_body, ppath->memory);
+ }
+ for (psub = spath->first_subpath; psub != 0;) {
+ int index = 0;
+ const segment *pseg = (const segment *)psub;
+ 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 or
+ * the dot length is non-zero, draw the caps, otherwise
+ * do nothing.
+ */
+ if (!(pgs_lp->cap == gs_cap_round ||
+ pgs_lp->dot_length != 0)
+ )
+ break;
+ /*
+ * Orient the dot according to the previous segment if
+ * any, or else the next segment if any, or else a
+ * vertical line.
+ */
+ {
+ const segment *end = psub->prev;
+
+ if (end != 0 && (end->pt.x != x || end->pt.y != y))
+ sx = end->pt.x, sy = end->pt.y;
+ else if (pseg != 0 &&
+ (pseg->pt.x != x || pseg->pt.y != y)
+ )
+ sx = pseg->pt.x, sy = pseg->pt.y;
+ }
+ /*
+ * Compute the properly oriented dot length, and then
+ * draw the dot like a very short line.
+ */
+ udx = sx - x, udy = sy - y;
+ if ((udx | udy) == 0)
+ udy = fixed_1;
+ {
+ double scale = device_dot_length /
+ hypot((double)udx, (double)udy);
+
+ /*
+ * If we're using butt caps, make sure the "line" is
+ * long enough to show up.
+ */
+ if (pgs_lp->cap == gs_cap_butt) {
+ fixed dmax = max(any_abs(udx), any_abs(udy));
+
+ if (dmax * scale < fixed_1)
+ scale = (float)fixed_1 / dmax;
+ }
+ udx = (fixed) (udx * scale);
+ udy = (fixed) (udy * scale);
+ if ((udx | udy) == 0)
+ udy = fixed_epsilon;
+ sx = x + udx;
+ sy = y + udy;
+ }
+ /*
+ * Back up 1 segment to keep the bookkeeping straight.
+ */
+ pseg = (pseg != 0 ? pseg->prev : psub->last);
+ goto d;
+ }
+ if (always_thin) {
+ pl.e.cdelta.x = pl.e.cdelta.y = 0;
+ pl.width.x = pl.width.y = 0;
+ pl.thin = true;
+ } else {
+ 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 = width_is_thin(&pl);
+ }
+ if (!pl.thin) {
+ adjust_stroke(&pl, pis, false);
+ compute_caps(&pl);
+ }
+ }
+ if (index++) {
+ int first;
+ pl_ptr lptr;
+
+ if (pgs_lp->join == gs_join_none &&
+ !(pseg->notes & not_first)
+ ) {
+ /* Fake the end of a subpath so we get */
+ /* caps instead of joins. */
+ first = 0;
+ lptr = 0;
+ index = 1;
+ } else {
+ first = (is_closed ? 1 : index - 2);
+ lptr = &pl;
+ }
+ code = (*line_proc) (to_path, first, &pl_prev, lptr,
+ pdevc, dev, pis, params, &cbox,
+ uniform);
+ if (code < 0)
+ goto exit;
+ fill_stroke_path(always_thin);
+ } else
+ pl_first = pl;
+ 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 statement. */
+ pl_ptr lptr =
+ (!is_closed ||
+ (pgs_lp->join == gs_join_none &&
+ !((pseg == 0 ? (const segment *)spath->first_subpath :
+ pseg)->notes & not_first)) ?
+ (pl_ptr) 0 : (pl_ptr) & pl_first);
+
+ code = (*line_proc) (to_path, index - 1, &pl_prev, lptr,
+ pdevc, dev, pis, params, &cbox, uniform);
+ if (code < 0)
+ goto exit;
+ fill_stroke_path(always_thin);
+ }
+ psub = (const subpath *)pseg;
+ }
+ exit:
+ if (to_path == &stroke_path_body)
+ gx_path_free(&stroke_path_body, "gx_stroke_path_only error"); /* (only needed if error) */
+ if (dash_count)
+ gx_path_free(&dpath, "gx_stroke_path exit(dash path)");
+ exf:
+ if (ppath->curve_count)
+ gx_path_free(&fpath, "gx_stroke_path exit(flattened path)");
+ return code;
+}
+
+/* ------ Internal routines ------ */
+
+/*
+ * Test whether a line is thin, i.e., whether the half-width, measured
+ * perpendicular to the line in device space, is less than 0.5 pixel.
+ * Unfortunately, the width values we computed are perpendicular to the
+ * line in *user* space, so we may have to do some extra work.
+ */
+private bool
+width_is_thin(pl_ptr plp)
+{
+ fixed dx, dy, wx = plp->width.x, wy = plp->width.y;
+
+ /* If the line is horizontal or vertical, things are easy. */
+ if ((dy = plp->e.p.y - plp->o.p.y) == 0)
+ return any_abs(wy) < fixed_half;
+ if ((dx = plp->e.p.x - plp->o.p.x) == 0)
+ return any_abs(wx) < fixed_half;
+
+ /*
+ * If both horizontal and vertical widths are less than
+ * 0.5, the line is thin.
+ */
+ if (any_abs(wx) < fixed_half && any_abs(wy) < fixed_half)
+ return true;
+
+ /*
+ * We have to do this the hard way, by actually computing the
+ * perpendicular distance. The distance from the point (U,V)
+ * from a line from (0,0) to (C,D) is
+ * abs(C*V - D*U) / sqrt(C^2 + D^2)
+ * In this case, (U,V) is plp->width, and (C,D) is (dx,dy).
+ */
+ {
+ double C = dx, D = dy;
+ double num = C * wy - D * wx;
+ double denom = hypot(C, D);
+
+ /* both num and denom are scaled by fixed_scale^2, */
+ /* so we don't need to do any de-scaling for the test. */
+ return fabs(num) < denom * 0.5;
+ }
+}
+
+/* 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
+adjust_stroke(pl_ptr plp, const gs_imager_state * pis, bool thin)
+{
+ fixed *pw;
+ fixed *pov;
+ fixed *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')) {
+ dlprintf4("[o]Intersect %f,%f(%f/%f)",
+ fixed2float(pp1->x), fixed2float(pp1->y),
+ fixed2float(pd1->x), fixed2float(pd1->y));
+ dlprintf4(" & %f,%f(%f/%f),\n",
+ fixed2float(pp2->x), fixed2float(pp2->y),
+ fixed2float(pd2->x), fixed2float(pd2->y));
+ dlprintf3("\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);
+}
+
+/* Set up the width and delta parameters for a thin line. */
+/* We only approximate the width and height. */
+private void
+set_thin_widths(register pl_ptr plp)
+{
+ fixed dx = plp->e.p.x - plp->o.p.x, dy = plp->e.p.y - plp->o.p.y;
+
+#define TRSIGN(v, c) ((v) >= 0 ? (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, fixed_half);
+ } else {
+ plp->width.y = plp->e.cdelta.x = 0;
+ plp->width.x = -(plp->e.cdelta.y = TRSIGN(dy, fixed_half));
+ }
+#undef TRSIGN
+}
+
+/* Draw a line on the device. */
+/* Treat no join the same as a bevel join. */
+private int
+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,
+ int uniform)
+{
+ const fixed lix = plp->o.p.x;
+ const fixed liy = plp->o.p.y;
+ const fixed litox = plp->e.p.x;
+ const fixed litoy = plp->e.p.y;
+
+ 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 && (nplp == 0 || !nplp->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;
+
+ 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,
+ (uniform ? (gs_matrix *) 0 :
+ &ctm_only(pis)));
+ if (code < 0)
+ return code;
+ if (nplp != 0) {
+ 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;
+ } {
+ const gs_fixed_point *bevel = points + 2;
+
+ /* 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, uniform);
+}
+
+/* Add a segment to the path. This handles all the complex cases. */
+private int
+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,
+ int uniform)
+{
+ 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,
+ (uniform ? (gs_matrix *) 0 : &ctm_only(pis)));
+ 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
+add_points(gx_path * ppath, const gs_fixed_point * 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. */
+/* Treat no join the same as a bevel join. */
+/* If pmat != 0, we must inverse-transform the distances for */
+/* the miter check. */
+private int
+line_join_points(const gx_line_params * pgs_lp, pl_ptr plp, pl_ptr nplp,
+ gs_fixed_point * join_points, const gs_matrix * pmat)
+{
+ gs_line_join join = pgs_lp->join;
+
+#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).
+ * ccw will be true iff nplp.o.co (nplp.o.p + width) is
+ * counter-clockwise from plp.e.ce (plp.e.p + width),
+ * in which case we want tan(a-b) rather than tan(b-a).
+ *
+ * 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);
+ 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 (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;
+ }
+ return 5;
+ }
+ /*
+ * 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 (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.
+ * However, if we have a non-uniform coordinate
+ * system (indicated by pmat != 0), we must do the
+ * computations in user space.
+ */
+ float check = pgs_lp->miter_check;
+ double u1 = plp->e.cdelta.y, v1 = plp->e.cdelta.x;
+ double u2 = nplp->o.cdelta.y, v2 = nplp->o.cdelta.x;
+ double num, denom;
+
+ if (pmat) {
+ gs_point pt;
+
+ gs_distance_transform_inverse(v1, u1, pmat, &pt);
+ v1 = pt.x, u1 = pt.y;
+ gs_distance_transform_inverse(v2, u2, pmat, &pt);
+ v2 = pt.x, u2 = pt.y;
+ /*
+ * We need to recompute ccw according to the
+ * relative positions of the lines in user space.
+ * We repeat the computation described above,
+ * using the cdelta values instead of the widths.
+ * Because the definition of ccw above is inverted
+ * from the intuitive one (for historical reasons),
+ * we actually have to do the test backwards.
+ */
+ ccw = v1 * u2 < v2 * u1;
+#ifdef DEBUG
+ {
+ double a1 = atan2(u1, v1), a2 = atan2(u2, v2), dif = a1 - a2;
+
+ if (dif < 0)
+ dif += 2 * M_PI;
+ else if (dif >= 2 * M_PI)
+ dif -= 2 * M_PI;
+ if (dif != 0 && (dif < M_PI) != ccw)
+ lprintf8("ccw wrong: tan(a1=%g)=%g/%g, tan(a2=%g)=%g,%g, dif=%g, ccw=%d\n",
+ a1, u1, v1, a2, u2, v2, dif, ccw);
+ }
+#endif
+ }
+ num = u1 * v2 - u2 * v1;
+ denom = u1 * u2 + v1 * v2;
+ /*
+ * 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')) {
+ dlprintf4("[o]Miter check: u1/v1=%f/%f, u2/v2=%f/%f,\n",
+ u1, v1, u2, v2);
+ dlprintf3(" 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);
+ }
+ }
+ return 4;
+}
+/* ---------------- 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
+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')) {
+ dlprintf4("[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));
+ dlprintf4("\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
+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
+cap_points(gs_line_cap type, const_ep_ptr endp,
+ gs_fixed_point * 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/gxsync.h b/pstoraster/gxsync.h
new file mode 100644
index 000000000..9f6446a68
--- /dev/null
+++ b/pstoraster/gxsync.h
@@ -0,0 +1,81 @@
+/* Copyright (C) 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Interface to synchronization primitives */
+
+/* Initial version 2/1/98 by John Desrosiers (soho@crl.com) */
+
+#if !defined(gxsync_INCLUDED)
+ #define gxsync_INCLUDED
+
+#include "gpsync.h"
+#include "gsmemory.h"
+
+/* This module abstracts the platform-specific synchronization primitives. */
+/* Since these routines will see heavy use, performance is important. */
+
+/* ----- Semaphore interface ----- */
+/* These have the usual queued, counting semaphore semantics: at init time, */
+/* the event count is set to 0 ('wait' will wait until 1st signal). */
+typedef struct gx_semaphore_s {
+ gs_memory_t *memory; /* allocator to free memory */
+ gp_semaphore native; /* MUST BE LAST last since length is undef'd */
+ /* platform-dep impl, len is gp_semaphore_sizeof() */
+} gx_semaphore_t;
+
+gx_semaphore_t * /* returns a new semaphore, 0 if error */
+ gx_semaphore_alloc(P1(
+ gs_memory_t * memory /* memory allocator to use */
+ ));
+void
+ gx_semaphore_free(P1(
+ gx_semaphore_t * sema /* semaphore to delete */
+ ));
+
+ #define gx_semaphore_wait(sema) gp_semaphore_wait(&(sema)->native)
+ #define gx_semaphore_signal(sema) gp_semaphore_signal(&(sema)->native)
+
+
+/* ----- Monitor interface ----- */
+/* These have the usual monitor semantics: at init time, */
+/* the event count is set to 1 (1st 'enter' succeeds immediately). */
+typedef struct gx_monitor_s {
+ gs_memory_t *memory; /* allocator to free memory */
+ gp_monitor native; /* platform-dep impl, len is gp_monitor_sizeof() */
+} gx_monitor_t;
+
+gx_monitor_t * /* returns a new monitor, 0 if error */
+ gx_monitor_alloc(P1(
+ gs_memory_t * memory /* memory allocator to use */
+ ));
+void
+ gx_monitor_free(P1(
+ gx_monitor_t * mon /* monitor to delete */
+ ));
+
+ #define gx_monitor_enter(sema) gp_monitor_enter(&(sema)->native)
+ #define gx_monitor_leave(sema) gp_monitor_leave(&(sema)->native)
+
+#endif /* !defined(gxsync_INCLUDED) */
diff --git a/pstoraster/gxtext.h b/pstoraster/gxtext.h
new file mode 100644
index 000000000..d1d51e314
--- /dev/null
+++ b/pstoraster/gxtext.h
@@ -0,0 +1,121 @@
+/* Copyright (C) 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Driver text interface implementation support */
+
+#ifndef gxtext_INCLUDED
+# define gxtext_INCLUDED
+
+#include "gstext.h"
+
+/* EVERYTHING IN THIS FILE IS SUBJECT TO CHANGE WITHOUT NOTICE. */
+
+/*
+ * Define the control parameter for setting text metrics.
+ */
+typedef enum {
+ TEXT_SET_CHAR_WIDTH,
+ TEXT_SET_CACHE_DEVICE,
+ TEXT_SET_CACHE_DEVICE2
+} gs_text_cache_control_t;
+
+/*
+ * Define the procedures associated with text display.
+ */
+struct gs_text_enum_procs_s {
+
+ /*
+ * Process the text. Then client should call this repeatedly until
+ * it returns <= 0. (> 0 means the client must intervene.)
+ */
+
+#define text_enum_proc_process(proc)\
+ int proc(P1(gs_text_enum_t *penum))
+
+ text_enum_proc_process((*process));
+
+ /*
+ * Set the character width and optionally bounding box,
+ * and enable caching.
+ */
+
+#define text_enum_proc_set_cache(proc)\
+ int proc(P3(gs_text_enum_t *penum, const double *values,\
+ gs_text_cache_control_t control))
+
+ text_enum_proc_set_cache((*set_cache));
+
+};
+
+/* Abstract types */
+#ifndef gs_imager_state_DEFINED
+# define gs_imager_state_DEFINED
+typedef struct gs_imager_state_s gs_imager_state;
+
+#endif
+#ifndef gx_device_color_DEFINED
+# define gx_device_color_DEFINED
+typedef struct gx_device_color_s gx_device_color;
+
+#endif
+#ifndef gs_font_DEFINED
+# define gs_font_DEFINED
+typedef struct gs_font_s gs_font;
+
+#endif
+#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
+
+/*
+ * Define the driver procedure for text.
+ */
+#define dev_t_proc_text_begin(proc, dev_t)\
+ int proc(P9(dev_t *dev,\
+ gs_imager_state *pis,\
+ const gs_text_params_t *text,\
+ const gs_font *font,\
+ gx_path *path, /* unless DO_NONE & !RETURN_WIDTH */\
+ const gx_device_color *pdcolor, /* DO_DRAW */\
+ const gx_clip_path *pcpath, /* DO_DRAW */\
+ gs_memory_t *memory,\
+ gs_text_enum_t **ppenum))
+#define dev_proc_text_begin(proc)\
+ dev_t_proc_text_begin(proc, gx_device)
+
+/*
+ * Begin processing text. This calls the device procedure, and also
+ * initializes the common parts of the enumerator.
+ */
+dev_proc_text_begin(gx_device_text_begin);
+
+#endif /* gxtext_INCLUDED */
diff --git a/pstoraster/gxtmap.h b/pstoraster/gxtmap.h
new file mode 100644
index 000000000..bb5c0b29e
--- /dev/null
+++ b/pstoraster/gxtmap.h
@@ -0,0 +1,58 @@
+/* Copyright (C) 1994, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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. There are two forms of this.
+ * The original form passed only the transfer map itself as an argument:
+ */
+typedef float (*gs_mapping_proc) (P2(floatp, const gx_transfer_map *));
+
+/*
+ * Later, we recognized that this procedure should really be a general
+ * closure:
+ */
+typedef float (*gs_mapping_closure_proc_t) (P3(floatp value,
+ const gx_transfer_map * pmap,
+ const void *proc_data));
+typedef struct gs_mapping_closure_s {
+ gs_mapping_closure_proc_t proc;
+ const void *data;
+} gs_mapping_closure_t;
+
+#endif /* gxtmap_INCLUDED */
diff --git a/pstoraster/gxtype1.c b/pstoraster/gxtype1.c
new file mode 100644
index 000000000..fccd3c0e3
--- /dev/null
+++ b/pstoraster/gxtype1.c
@@ -0,0 +1,518 @@
+/* Copyright (C) 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Adobe Type 1 font interpreter support */
+#include "math_.h"
+#include "gx.h"
+#include "gserrors.h"
+#include "gsccode.h"
+#include "gsline.h"
+#include "gsstruct.h"
+#include "gxarith.h"
+#include "gxfixed.h"
+#include "gxistate.h"
+#include "gxmatrix.h"
+#include "gxcoord.h"
+#include "gxfont.h"
+#include "gxfont1.h"
+#include "gxtype1.h"
+#include "gzpath.h"
+
+/*
+ * The routines in this file are used for both Type 1 and Type 2
+ * charstring interpreters.
+ */
+
+/*
+ * 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;
+
+ RELOC_CONST_STRING_VAR(ipsp->char_string);
+ ipsp->ip = ipsp->char_string.data + diff;
+ }
+} RELOC_PTRS_END
+#undef pcis
+
+/* ------ Interpreter entry point ------ */
+
+private int
+gs_no_charstring_interpret(gs_type1_state * pcis, const gs_const_string * str,
+ int *pindex)
+{
+ return_error(gs_error_rangecheck);
+}
+int (*gs_charstring_interpreter[3])
+ (P3(gs_type1_state * pcis, const gs_const_string * str, int *pindex)) = {
+ gs_no_charstring_interpret,
+ gs_no_charstring_interpret,
+ gs_no_charstring_interpret
+};
+
+/*
+ * 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 othersubr # is stored for callothersubr.
+ */
+int
+gs_type1_interpret(gs_type1_state * pcis, const gs_const_string * str,
+ int *pindex)
+{
+ return (*gs_charstring_interpreter[pcis->pfont->data.CharstringType])
+ (pcis, str, pindex);
+}
+
+/* ------ Interpreter services ------ */
+
+#define s (*ps)
+
+/* We export this for the Type 2 charstring interpreter. */
+void
+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;
+ pcis->have_hintmask = false;
+ pcis->num_hints = 0;
+ pcis->seac_accent = -1;
+
+ /* 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. */
+void
+gs_type1_finish_init(gs_type1_state * pcis, gs_op1_state * 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->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_xxyy(&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;
+}
+
+/* ------ 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 dx, dy;
+ int code;
+
+ /* Check for and suppress a microscopic closing line. */
+ if ((psub = ppath->current_subpath) != 0 &&
+ (pseg = psub->last) != 0 &&
+ (dx = pseg->pt.x - psub->pt.x,
+ any_abs(dx) < float2fixed(0.1)) &&
+ (dy = pseg->pt.y - psub->pt.y,
+ any_abs(dy) < float2fixed(0.1))
+ )
+ switch (pseg->type) {
+ case s_line:
+ code = gx_path_pop_close_subpath(sppath);
+ break;
+ case s_curve:
+ /*
+ * Unfortunately, there is no "s_curve_close". (Maybe there
+ * should be?) Just adjust the final point of the curve so it
+ * is identical to the closing point.
+ */
+ pseg->pt = psub->pt;
+#define pcseg ((curve_segment *)pseg)
+ pcseg->p2.x -= dx;
+ pcseg->p2.y -= dy;
+#undef pcseg
+ /* falls through */
+ default:
+ /* What else could it be?? */
+ code = gx_path_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
+
+/* Record the side bearing and character width. */
+int
+gs_type1_sbw(gs_type1_state * pcis, fixed lsbx, fixed lsby, fixed wx, fixed wy)
+{
+ if (!pcis->sb_set)
+ pcis->lsb.x = lsbx, pcis->lsb.y = lsby,
+ pcis->sb_set = true; /* needed for accented chars */
+ if (!pcis->width_set)
+ pcis->width.x = wx, pcis->width.y = wy,
+ 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));
+ return 0;
+}
+
+/*
+ * Handle a seac. Do the base character now; when it finishes (detected
+ * in endchar), do the accent. Note that we pass only 4 operands on the
+ * stack, and pass asb separately.
+ */
+int
+gs_type1_seac(gs_type1_state * pcis, const fixed * cstack, fixed asb,
+ ip_state * ipsp)
+{
+ gs_font_type1 *pfont = pcis->pfont;
+ gs_const_string bcstr;
+ int code;
+
+ /* Save away all the operands. */
+ pcis->seac_accent = fixed2int_var(cstack[3]);
+ pcis->save_asb = asb - pcis->lsb.x;
+ pcis->save_adxy.x = cstack[0];
+ pcis->save_adxy.y = cstack[1];
+ pcis->os_count = 0; /* clear */
+ /* Ask the caller to provide the base character's CharString. */
+ code = (*pfont->data.procs->seac_data)
+ (pfont, fixed2int_var(cstack[2]), &bcstr);
+ if (code != 0)
+ return code;
+ /* Continue with the supplied string. */
+ ipsp->char_string = bcstr;
+ return 0;
+}
+
+/*
+ * Handle the end of a character. Return 0 if this is really the end of a
+ * character, or 1 if we still have to process the accent of a seac.
+ * In the latter case, the interpreter control stack has been set up to
+ * point to the start of the accent's CharString; the caller must
+ * also set ptx/y to pcis->position.x/y.
+ */
+int
+gs_type1_endchar(gs_type1_state * pcis)
+{
+ gs_imager_state *pis = pcis->pis;
+ gx_path *ppath = pcis->path;
+
+ if (pcis->seac_accent >= 0) { /* We just finished the base character of a seac. */
+ /* Do the accent. */
+ gs_font_type1 *pfont = pcis->pfont;
+ gs_op1_state s;
+ gs_const_string astr;
+ int achar = pcis->seac_accent;
+ int code;
+
+ pcis->seac_accent = -1;
+ /* Reset the coordinate system origin */
+ sfc = pcis->fc;
+ ptx = pcis->origin.x, pty = pcis->origin.y;
+ pcis->asb_diff = pcis->save_asb;
+ pcis->adxy = pcis->save_adxy;
+ /*
+ * We're going to add in the lsb of the accented character when
+ * we encounter its [h]sbw, so don't do it now.
+ */
+ accum_xy(pcis->adxy.x, pcis->adxy.y);
+ ppath->position.x = pcis->position.x = ptx;
+ ppath->position.y = pcis->position.y = pty;
+ pcis->os_count = 0; /* clear */
+ /* Clear the ipstack, in case the base character */
+ /* ended inside a subroutine. */
+ pcis->ips_count = 1;
+ /* Remove any base character hints. */
+ reset_stem_hints(pcis);
+ /* Ask the caller to provide the accent's CharString. */
+ code = (*pfont->data.procs->seac_data) (pfont, achar, &astr);
+ if (code < 0)
+ return code;
+ /* Continue with the supplied string. */
+ pcis->ips_count = 1;
+ pcis->ipstack[0].char_string = astr;
+ return 1;
+ }
+ if (pcis->hint_next != 0 || path_is_drawing(ppath))
+ 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(ppath, &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_imager_setflat(pis, pcis->flatness);
+ return 0;
+}
diff --git a/pstoraster/gxtype1.h b/pstoraster/gxtype1.h
new file mode 100644
index 000000000..8e393741b
--- /dev/null
+++ b/pstoraster/gxtype1.h
@@ -0,0 +1,346 @@
+/* Copyright (C) 1990, 1995, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Private Adobe Type 1 / Type 2 charstring interpreter definitions */
+
+#ifndef gxtype1_INCLUDED
+# define gxtype1_INCLUDED
+
+#include "gscrypt1.h"
+#include "gstype1.h"
+#include "gxop1.h"
+
+/* This file defines the structures for the state of a Type 1 / */
+/* Type 2 charstring 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 have to retain replaced hints
+ * so that we can make consistent rounding choices for stem edges.
+ * This is clunky, but I don't see any other way to do it.
+ *
+ * The Type 2 charstring documentation says that the total number of hints
+ * is limited to 96, but since we store horizontal and vertical hints
+ * separately, we must set max_stems large enough to allow either one to
+ * get this big.
+ */
+#define max_total_stem_hints 96
+#define max_stems 96
+typedef struct {
+ fixed v0, v1; /* coordinates (widened a little) */
+ fixed dv0, dv1; /* adjustment values */
+ ushort index; /* sequential index of hint, */
+ /* needed for implementing hintmask */
+ ushort active; /* true if hint is active (hintmask) */
+} stem_hint;
+typedef struct {
+ int count;
+ int current; /* cache cursor for search */
+ /*
+ * For dotsection and Type 1 Charstring hint replacement,
+ * we store active hints at the bottom of the table, and
+ * replaced hints at the top.
+ */
+ int replaced_count; /* # of replaced hints at top */
+ 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;
+
+/* Get the next byte from a CharString. It may or may not be encrypted. */
+#define charstring_this(ch, state, encrypted)\
+ (encrypted ? decrypt_this(ch, state) : ch)
+#define charstring_next(ch, state, chvar, encrypted)\
+ (encrypted ? (chvar = decrypt_this(ch, state),\
+ decrypt_skip_next(ch, state)) :\
+ (chvar = ch))
+#define charstring_skip_next(ch, state, encrypted)\
+ (encrypted ? decrypt_skip_next(ch, state) : 0)
+
+#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 48 /* per Type 2 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) */
+ bool have_hintmask; /* true if using a hint mask */
+ /* (Type 2 charstrings only) */
+ int num_hints; /* number of hints (Type 2 only) */
+ gs_fixed_point lsb; /* left side bearing (char coords) */
+ gs_fixed_point width; /* character width (char coords) */
+ int seac_accent; /* accent character code for seac, */
+ /* or -1 */
+ fixed save_asb; /* save seac asb */
+ gs_fixed_point save_adxy; /* save seac adx/ady */
+ fixed asb_diff; /* seac asb - accented char lsb.x, */
+ /* needed to adjust Flex endpoint */
+ gs_fixed_point adxy; /* seac accent displacement, */
+ /* needed to adjust currentpoint */
+ gs_fixed_point position; /* save unadjusted position */
+ /* when returning temporarily */
+ /* to caller */
+ int flex_path_state_flags; /* 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 */
+ fixed transient_array[32]; /* Type 2 transient array, */
+ /* will be variable-size someday */
+};
+
+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)
+
+/* ------ Shared Type 1 / Type 2 interpreter fragments ------ */
+
+/* Declare the array of charstring interpreters, indexed by CharstringType. */
+extern int (*gs_charstring_interpreter[3])
+ (P3(gs_type1_state * pcis, const gs_const_string * str, int *pindex));
+
+/* Copy the operand stack out of the saved state. */
+#define init_cstack(cstack, csp, pcis)\
+ BEGIN\
+ if ( pcis->os_count == 0 )\
+ csp = cstack - 1;\
+ else\
+ { memcpy(cstack, pcis->ostack, pcis->os_count * sizeof(fixed));\
+ csp = &cstack[pcis->os_count - 1];\
+ }\
+ END
+
+/* Decode and push a 1-byte number. */
+#define decode_push_num1(csp, c)\
+ (*++csp = int2fixed(c_value_num1(c)))
+
+/* Decode and push a 2-byte number. */
+#define decode_push_num2(csp, c, cip, state, encrypted)\
+ BEGIN\
+ uint c2 = *cip++;\
+ int cn;\
+\
+ cn = charstring_this(c2, state, encrypted);\
+ if ( c < c_neg2_0 )\
+ { if_debug2('1', "[1] (%d)+%d\n", c_value_pos2(c, 0), cn);\
+ *++csp = int2fixed(c_value_pos2(c, 0) + (int)cn);\
+ }\
+ else\
+ { if_debug2('1', "[1] (%d)-%d\n", c_value_neg2(c, 0), cn);\
+ *++csp = int2fixed(c_value_neg2(c, 0) - (int)cn);\
+ }\
+ charstring_skip_next(c2, state, encrypted);\
+ END
+
+/* Decode a 4-byte number, but don't push it, because Type 1 and Type 2 */
+/* charstrings scale it differently. */
+#if arch_sizeof_long > 4
+# define sign_extend_num4(lw)\
+ lw = (lw ^ 0x80000000L) - 0x80000000L
+#else
+# define sign_extend_num4(lw) DO_NOTHING
+#endif
+#define decode_num4(lw, cip, state, encrypted)\
+ BEGIN\
+ int i;\
+ uint c4;\
+\
+ lw = 0;\
+ for ( i = 4; --i >= 0; )\
+ { charstring_next(*cip, state, c4, encrypted);\
+ lw = (lw << 8) + c4;\
+ cip++;\
+ }\
+ sign_extend_num4(lw);\
+ END
+
+/* ------ Shared Type 1 / Type 2 charstring utilities ------ */
+
+void gs_type1_finish_init(P2(gs_type1_state * pcis, is_ptr ps));
+
+int gs_type1_sbw(P5(gs_type1_state * pcis, fixed sbx, fixed sby,
+ fixed wx, fixed wy));
+
+int gs_type1_seac(P4(gs_type1_state * pcis, const fixed * cstack,
+ fixed asb_diff, ip_state * ipsp));
+
+int gs_type1_endchar(P1(gs_type1_state * pcis));
+
+/* ----- 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_replace_stem_hints(P1(gs_type1_state *)),
+#define replace_stem_hints(pcis)\
+ (apply_path_hints(pcis, false),\
+ type1_replace_stem_hints(pcis))
+ 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)
+
+#endif /* gxtype1_INCLUDED */
diff --git a/pstoraster/gxxfont.h b/pstoraster/gxxfont.h
new file mode 100644
index 000000000..ae119d0f3
--- /dev/null
+++ b/pstoraster/gxxfont.h
@@ -0,0 +1,180 @@
+/* Copyright (C) 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 supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* External font interface for Ghostscript library */
+
+#ifndef gxxfont_INCLUDED
+# define gxxfont_INCLUDED
+
+#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 {
+ const 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: ENUM_RETURN(gx_device_enum_ptr((gx_device *)(((stype *)vptr)->de)));\
+ 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)
+
+#endif /* gxxfont_INCLUDED */
diff --git a/pstoraster/gzacpath.h b/pstoraster/gzacpath.h
new file mode 100644
index 000000000..11d07187f
--- /dev/null
+++ b/pstoraster/gzacpath.h
@@ -0,0 +1,60 @@
+/* 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 supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Requires gxdevice.h, gzcpath.h */
+
+#ifndef gzacpath_INCLUDED
+# define gzacpath_INCLUDED
+
+/*
+ * Device for accumulating a rectangle list. This device can clip
+ * the list being accumulated with a clipping rectangle on the fly:
+ * we use this to clip clipping paths to band boundaries when
+ * rendering a band list.
+ */
+typedef struct gx_device_cpath_accum_s {
+ gx_device_common;
+ gs_memory_t *list_memory;
+ gs_int_rect clip_box;
+ 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));
+
+/* Set the accumulator's clipping box. */
+void gx_cpath_accum_set_cbox(P2(gx_device_cpath_accum * padev,
+ const gs_fixed_rect * pbox));
+
+/* Finish accumulating a clipping path. */
+/* Note that this releases the old contents of the 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));
+
+#endif /* gzacpath_INCLUDED */
diff --git a/pstoraster/gzcpath.h b/pstoraster/gzcpath.h
new file mode 100644
index 000000000..e9f108d77
--- /dev/null
+++ b/pstoraster/gzcpath.h
@@ -0,0 +1,106 @@
+/* Copyright (C) 1994, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Requires gzpath.h. */
+
+#ifndef gzcpath_INCLUDED
+# define gzcpath_INCLUDED
+
+#include "gxcpath.h"
+
+/*
+ * The reference counting considerations for clip paths are the same as
+ * for paths. We need a separate reference count for the clip list,
+ * since its existence and lifetime are not necessarily the same as
+ * those of the path.
+ */
+
+typedef struct gx_clip_rect_list_s {
+ rc_header rc;
+ gx_clip_list list;
+} gx_clip_rect_list;
+
+#define private_st_clip_rect_list() /* in gxcpath.c */\
+ gs_private_st_ptrs_add0(st_clip_rect_list, gx_clip_rect_list,\
+ "gx_clip_rect_list", clip_rect_list_enum_ptrs, clip_rect_list_reloc_ptrs,\
+ st_clip_list, list)
+
+/* gx_clip_path is a 'subclass' of gx_path. */
+struct gx_clip_path_s {
+ gx_path path;
+ gx_clip_rect_list local_list;
+ 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_rect_list *rect_list;
+ bool path_valid; /* path representation is valid */
+ /* 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 + 1)
+
+/* Inline accessors. */
+#define gx_cpath_is_shared(pcpath)\
+ ((pcpath)->rect_list->rc.ref_count > 1)
+#define gx_cpath_list(pcpath)\
+ (&(pcpath)->rect_list->list)
+
+/* Define the structure for enumerating a clipping list. */
+typedef enum {
+ visit_left = 1,
+ visit_right = 2
+} cpe_visit_t;
+typedef enum {
+ cpe_scan, cpe_left, cpe_right, cpe_close, cpe_done
+} cpe_state_t;
+struct gs_cpath_enum_s {
+ gs_path_enum path_enum; /* used iff clipping path exists as a path, */
+ /* must be first for subclassing */
+ bool using_path;
+ gx_clip_rect *visit; /* scan pointer for finding next start */
+ gx_clip_rect *rp; /* scan pointer for current rectangle */
+ cpe_visit_t first_visit;
+ cpe_state_t state;
+ bool have_line;
+ gs_int_point line_end;
+ bool any_rectangles;
+};
+
+#define private_st_cpath_enum() /* in gxcpath.c */\
+ gs_private_st_suffix_add2(st_cpath_enum, gs_cpath_enum, "gs_cpath_enum",\
+ cpath_enum_enum_ptrs, cpath_enum_reloc_ptrs, st_path_enum,\
+ visit, rp)
+
+#endif /* gzcpath_INCLUDED */
diff --git a/pstoraster/gzht.h b/pstoraster/gzht.h
new file mode 100644
index 000000000..49f5fc2e2
--- /dev/null
+++ b/pstoraster/gzht.h
@@ -0,0 +1,203 @@
+/* Copyright (C) 1989, 1995, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Requires gxdevice.h, gxdcolor.h */
+
+#ifndef gzht_INCLUDED
+# define gzht_INCLUDED
+
+#include "gscsel.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));
+int gx_ht_alloc_client_order(P6(gx_ht_order * porder, uint width, uint height,
+ uint num_levels, uint num_bits, 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_bit(P3(gx_ht_bit * bit, int width, int bit_num));
+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/memory. */
+int gs_screen_order_init_memory(P5(gx_ht_order *, const gs_state *,
+ gs_screen_halftone *, bool, gs_memory_t *));
+
+#define gs_screen_order_init(porder, pgs, phsp, accurate)\
+ gs_screen_order_init_memory(porder, pgs, phsp, accurate, pgs->memory)
+
+/* Prepare to sample a spot screen. */
+/* This is the second half of gs_screen_init_accurate/memory. */
+int gs_screen_enum_init_memory(P5(gs_screen_enum *, const gx_ht_order *,
+ gs_state *, gs_screen_halftone *,
+ gs_memory_t *));
+
+#define gs_screen_enum_init(penum, porder, pgs, phsp)\
+ gs_screen_enum_init_memory(penum, porder, pgs, phsp, pgs->memory)
+
+/* Process an entire screen plane. */
+int gx_ht_process_screen_memory(P5(gs_screen_enum * penum, gs_state * pgs,
+ gs_screen_halftone * phsp, bool accurate,
+ gs_memory_t * mem));
+
+#define gx_ht_process_screen(penum, pgs, phsp, accurate)\
+ gx_ht_process_screen_memory(penum, pgs, phsp, accurate, pgs->memory)
+
+/*
+ * 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 *const fc_color_quo[8];
+
+#define fractional_color(f, maxv)\
+ ((maxv) <= 7 ? fc_color_quo[maxv][f] : frac_color_(f, maxv))
+
+/* ------ Halftone cache procedures ------ */
+
+/* Allocate/free 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));
+void gx_ht_free_cache(P2(gs_memory_t *, gx_ht_cache *));
+
+/* 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 *));
+
+/* Make the cache order current, and return whether */
+/* there is room for all possible tiles in the cache. */
+bool gx_check_tile_cache(P1(const gs_imager_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(P6(const gs_imager_state * pis, int w, int y, int h,
+ gs_color_select_t select, int *ppx));
+
+/* Make a given level current in a halftone cache. */
+gx_ht_tile *gx_render_ht(P2(gx_ht_cache *, int));
+
+/* ------ Device halftone management ------ */
+
+/* Release a gx_ht_order by freeing its components. */
+/* (Don't free the gx_device_halftone itself.) */
+void gx_ht_order_release(P3(gx_ht_order * porder, gs_memory_t * mem, bool free_cache));
+
+/*
+ * Install a device halftone in an imager state.
+ * Note that this does not read or update the client halftone.
+ */
+int gx_imager_dev_ht_install(P4(gs_imager_state * pis,
+ const gx_device_halftone * pdht,
+ gs_halftone_type type,
+ const gx_device * dev));
+
+/*
+ * Install a new halftone in the graphics state. Note that we copy the top
+ * level of the gs_halftone and the gx_device_halftone, and take ownership
+ * of any substructures.
+ */
+int gx_ht_install(P3(gs_state *, const gs_halftone *,
+ const gx_device_halftone *));
+
+/* Reestablish the effective transfer functions, taking into account */
+/* any overrides from halftone dictionaries. */
+/* Some compilers object to names longer than 31 characters.... */
+void gx_imager_set_effective_xfer(P1(gs_imager_state * pis));
+void gx_set_effective_transfer(P1(gs_state * pgs));
+
+
+#endif /* gzht_INCLUDED */
diff --git a/pstoraster/gzline.h b/pstoraster/gzline.h
new file mode 100644
index 000000000..f50d39641
--- /dev/null
+++ b/pstoraster/gzline.h
@@ -0,0 +1,41 @@
+/* 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 supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Line parameter implementation */
+
+#ifndef gzline_INCLUDED
+# define gzline_INCLUDED
+
+#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)
+#define st_line_params_num_ptrs 1
+
+/* Internal accessor for line parameters in graphics state */
+const gx_line_params *gs_currentlineparams(P1(const gs_imager_state *));
+
+#endif /* gzline_INCLUDED */
diff --git a/pstoraster/gzpath.h b/pstoraster/gzpath.h
new file mode 100644
index 000000000..26c6f2121
--- /dev/null
+++ b/pstoraster/gzpath.h
@@ -0,0 +1,385 @@
+/* Copyright (C) 1989, 1995, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Requires gxfixed.h */
+
+#ifndef gzpath_INCLUDED
+# define gzpath_INCLUDED
+
+#include "gxpath.h"
+#include "gsmatrix.h"
+#include "gsrefct.h"
+#include "gsstruct.h" /* for extern_st */
+
+/*
+ * Paths are represented as a linked list of line or curve segments,
+ * similar to what pathforall reports.
+ */
+
+/*
+ * Define path segment types: segment start, line, or Bezier curve.
+ * We have a special type for the line added by closepath.
+ */
+typedef enum {
+ s_start,
+ s_line,
+ s_line_close,
+ s_curve
+} segment_type;
+
+/* Define the common structure for all segments. */
+#define segment_common\
+ segment *prev;\
+ segment *next;\
+ ushort /*segment_type*/ type;\
+ ushort /*segment_notes*/ notes;\
+ 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;
+
+/*
+ * Define 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_suffix_add0_local(st_curve, curve_segment, "curve",\
+ segment_enum_ptrs, segment_reloc_ptrs, st_segment)
+
+/*
+ * Define 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. */
+gx_path_rectangular_type
+gx_subpath_is_rectangular(P3(const subpath * pstart, gs_fixed_rect * pbox,
+ const subpath ** ppnext));
+
+#define gx_subpath_is_rectangle(pstart, pbox, ppnext)\
+ (gx_subpath_is_rectangular(pstart, pbox, ppnext) != prt_none)
+
+/* 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));
+
+/*
+ * If necessary, find the values of t (never more than 2) which split the
+ * curve into monotonic parts. Return the number of split points.
+ */
+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 *));
+
+/* Flatten a partial curve by sampling (internal procedure). */
+int gx_flatten_sample(P4(gx_path *, int, curve_segment *, segment_notes));
+
+/* Initialize a cursor for rasterizing a monotonic curve. */
+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; /* scaled double versions of a, b, c */
+ bool double_set; /* true if da/b/c set */
+ int fixed_limit; /* can do in fixed point if t <= limit */
+ /* Following are updated dynamically. */
+ struct ccc_ { /* one-element cache */
+ fixed ky0, ky3; /* key (range) */
+ fixed xl, xd; /* value */
+ } cache;
+} 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));
+
+/*
+ * The path state flags reflect the most recent operation on the path
+ * as follows:
+ * Operation position_valid subpath_open is_drawing
+ * newpath no no no
+ * moveto yes yes no
+ * lineto/curveto yes yes yes
+ * closepath yes no no
+ * If position_valid is true, outside_range reflects whether the most
+ * recent operation went outside of the representable coordinate range.
+ * If this is the case, the corresponding member of position (x and/or y)
+ * is min_fixed or max_fixed, and outside_position is the true position.
+ */
+/*
+ */
+typedef enum {
+ /* Individual flags. These may be or'ed together, per above. */
+ psf_position_valid = 1,
+ psf_subpath_open = 2,
+ psf_is_drawing = 4,
+ psf_outside_range = 8,
+ /* Values stored by path building operations. */
+ psf_last_newpath = 0,
+ psf_last_moveto = psf_position_valid | psf_subpath_open,
+ psf_last_draw = psf_position_valid | psf_subpath_open | psf_is_drawing,
+ psf_last_closepath = psf_position_valid
+} gx_path_state_flags;
+
+/*
+ * Individual tests
+ */
+#define path_position_valid(ppath)\
+ (((ppath)->state_flags & psf_position_valid) != 0)
+#define path_subpath_open(ppath)\
+ (((ppath)->state_flags & psf_subpath_open) != 0)
+#define path_is_drawing(ppath)\
+ (((ppath)->state_flags & psf_is_drawing) != 0)
+#define path_outside_range(ppath)\
+ (((ppath)->state_flags & psf_outside_range) != 0)
+/*
+ * Composite tests
+ */
+#define path_last_is_moveto(ppath)\
+ (((ppath)->state_flags & ~psf_outside_range) == psf_last_moveto)
+#define path_position_in_range(ppath)\
+ (((ppath)->state_flags & (psf_position_valid + psf_outside_range)) ==\
+ psf_position_valid)
+#define path_start_outside_range(ppath)\
+ ((ppath)->state_flags != 0 &&\
+ ((ppath)->start_flags & psf_outside_range) != 0)
+/*
+ * Updating operations
+ */
+#define path_update_newpath(ppath)\
+ ((ppath)->state_flags = psf_last_newpath)
+#define path_update_moveto(ppath)\
+ ((ppath)->state_flags = (ppath)->start_flags = psf_last_moveto)
+#define path_update_draw(ppath)\
+ ((ppath)->state_flags = psf_last_draw)
+#define path_update_closepath(ppath)\
+ ((ppath)->state_flags = psf_last_closepath)
+#define path_set_outside_position(ppath, px, py)\
+ ((ppath)->outside_position.x = (px),\
+ (ppath)->outside_position.y = (py),\
+ (ppath)->state_flags |= psf_outside_range)
+
+/*
+ * In order to be able to reclaim path segments at the right time, we need
+ * to reference-count them. To minimize disruption, we would like to do
+ * this by creating a structure (gx_path_segments) consisting of only a
+ * reference count that counts the number of paths that share the same
+ * segments. (Logically, we should put the segments themselves --
+ * first/last_subpath, subpath/curve_count -- in this object, but that would
+ * cause too much disruption to existing code.) However, we need to put at
+ * least first_subpath and current_subpath in this structure so that we can
+ * free the segments when the reference count becomes zero.
+ */
+typedef struct gx_path_segments_s {
+ rc_header rc;
+ struct psc_ {
+ subpath *subpath_first;
+ subpath *subpath_current;
+ } contents;
+} gx_path_segments;
+
+#define private_st_path_segments() /* in gxpath.c */\
+ gs_private_st_ptrs2(st_path_segments, gx_path_segments, "path segments",\
+ path_segments_enum_ptrs, path_segments_reloc_ptrs,\
+ contents.subpath_first, contents.subpath_current)
+
+/* Record how a path was allocated, so freeing will do the right thing. */
+typedef enum {
+ path_allocated_on_stack, /* on stack */
+ path_allocated_contained, /* inside another object */
+ path_allocated_on_heap /* on the heap */
+} gx_path_allocation_t;
+
+/* Here is the actual structure of a path. */
+struct gx_path_s {
+ /*
+ * In order to be able to have temporary paths allocated entirely
+ * on the stack, we include a segments structure within the path,
+ * used only for this purpose. In order to avoid having the
+ * path's segments pointer point into the middle of an object,
+ * the segments structure must come first.
+ *
+ * Note that since local_segments is used only for temporary paths
+ * on the stack, and not for path structures in allocated memory,
+ * we don't declare any pointers in it for the GC. (As it happens,
+ * there aren't any such pointers at the moment, but this could
+ * change.)
+ */
+ gx_path_segments local_segments;
+ gs_memory_t *memory;
+ gx_path_allocation_t allocation; /* how this path was allocated */
+ gx_path_segments *segments;
+ gs_fixed_rect bbox; /* bounding box (in device space) */
+ segment *box_last; /* bbox incorporates segments */
+ /* up to & including this one */
+#define first_subpath segments->contents.subpath_first /* (hack) */
+#define current_subpath segments->contents.subpath_current /* (ditto) */
+ int subpath_count;
+ int curve_count;
+ gs_fixed_point position; /* current position */
+ gs_point outside_position; /* position if outside_range is set */
+ gs_point outside_start; /* outside_position of last moveto */
+ byte /*gx_path_state_flags */ start_flags; /* flags of moveto */
+ byte /*gx_path_state_flags */ state_flags; /* (see above) */
+ byte /*bool */ bbox_set; /* true if setbbox is in effect */
+};
+
+/* st_path should be private, but it's needed for the clip_path subclass. */
+extern_st(st_path);
+#define public_st_path() /* in gxpath.c */\
+ gs_public_st_ptrs2(st_path, gx_path, "path",\
+ path_enum_ptrs, path_reloc_ptrs, segments, box_last)
+#define st_path_max_ptrs 2
+
+/* Path enumeration structure */
+struct gs_path_enum_s {
+ gs_memory_t *memory;
+ gs_matrix mat; /* CTM for inverse-transforming points */
+ const segment *pseg;
+ 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? */
+ segment_notes notes; /* notes from most recent segment */
+};
+
+/* We export st_path_enum only so that st_cpath_enum can subclass it. */
+extern_st(st_path_enum);
+#define public_st_path_enum() /* in gxpath2.c */\
+ gs_public_st_ptrs3(st_path_enum, gs_path_enum, "gs_path_enum",\
+ path_enum_enum_ptrs, path_enum_reloc_ptrs, pseg, path, copied_path)
+
+/* Inline path accessors. */
+#define gx_path_has_curves_inline(ppath)\
+ ((ppath)->curve_count != 0)
+#define gx_path_has_curves(ppath)\
+ gx_path_has_curves_inline(ppath)
+#define gx_path_is_void_inline(ppath)\
+ ((ppath)->first_subpath == 0)
+#define gx_path_is_void(ppath)\
+ gx_path_is_void_inline(ppath)
+#define gx_path_subpath_count(ppath)\
+ ((ppath)->subpath_count)
+#define gx_path_is_shared(ppath)\
+ ((ppath)->segments->rc.ref_count > 1)
+
+/* 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)\
+ ( !path_position_valid(ppath) ? 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)\
+ ( !path_position_in_range(ppath) || ppath->bbox_set ?\
+ gx_path_add_relative_point(ppath, dx, dy) :\
+ (ppath->position.x += dx, ppath->position.y += dy,\
+ path_update_moveto(ppath), 0) )
+
+#endif /* gzpath_INCLUDED */
diff --git a/pstoraster/gzstate.h b/pstoraster/gzstate.h
new file mode 100644
index 000000000..0a189911b
--- /dev/null
+++ b/pstoraster/gzstate.h
@@ -0,0 +1,137 @@
+/* Copyright (C) 1989, 1995, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Private graphics state definition for Ghostscript library */
+
+#ifndef gzstate_INCLUDED
+# define gzstate_INCLUDED
+
+#include "gscpm.h"
+#include "gsrefct.h"
+#include "gxdcolor.h"
+#include "gxistate.h"
+#include "gsstate.h"
+#include "gxstate.h"
+
+/* Opaque types referenced by the graphics state. */
+#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 gs_color_space_DEFINED
+# define gs_color_space_DEFINED
+typedef struct gs_color_space_s gs_color_space;
+
+#endif
+#ifndef gs_client_color_DEFINED
+# define gs_client_color_DEFINED
+typedef struct gs_client_color_s gs_client_color;
+
+#endif
+#ifndef gs_font_DEFINED
+# define gs_font_DEFINED
+typedef struct gs_font_s gs_font;
+
+#endif
+
+/* Graphics state structure. */
+
+struct gs_state_s {
+ gs_imager_state_common; /* imager state, must be first */
+ gs_state *saved; /* previous state from gsave */
+
+ /* 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: */
+
+ gx_path *path;
+ gx_clip_path *clip_path;
+ gx_clip_path *view_clip; /* (may be 0, or have rule = 0) */
+ bool clamp_coordinates; /* if true, clamp out-of-range */
+ /* coordinates; if false, */
+ /* report a limitcheck */
+ /* Effective clip path cache */
+ gs_id effective_clip_id; /* (key) clip path id */
+ gs_id effective_view_clip_id; /* (key) view clip path id */
+ gx_clip_path *effective_clip_path; /* (value) effective clip path, */
+ /* possibly = clip_path or view_clip */
+ bool effective_clip_shared; /* true iff e.c.p. = c.p. or v.c. */
+
+ /* Color (device-independent): */
+
+ gs_color_space *color_space;
+ gs_client_color *ccolor;
+
+ /* Color caches: */
+
+ gx_device_color *dev_color;
+
+ /* 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 setcachedevice but not */
+ /* actually caching, */
+ /* 2 if in setcachedevice and */
+ /* actually caching */
+ 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)
+
+#endif /* gzstate_INCLUDED */
diff --git a/pstoraster/ialloc.c b/pstoraster/ialloc.c
new file mode 100644
index 000000000..9caf55b61
--- /dev/null
+++ b/pstoraster/ialloc.c
@@ -0,0 +1,314 @@
+/* Copyright (C) 1993, 1995, 1996, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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 "ipacked.h"
+#include "iutil.h"
+#include "ivmspace.h"
+#include "store.h"
+
+/*
+ * Define global and local instances.
+ */
+gs_dual_memory_t gs_imemory;
+
+/* Initialize the allocator */
+void
+ialloc_init(gs_raw_memory_t * rmem, uint chunk_size, bool level2)
+{
+ gs_ref_memory_t *ilmem = ialloc_alloc_state(rmem, chunk_size);
+ gs_ref_memory_t *igmem =
+ (level2 ?
+ ialloc_alloc_state(rmem, chunk_size) :
+ ilmem);
+ gs_ref_memory_t *ismem = ialloc_alloc_state(rmem, 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 ================ */
+
+/* Register a ref root. */
+int
+gs_register_ref_root(gs_memory_t *mem, gs_gc_root_t *root,
+ void **pp, client_name_t cname)
+{
+ return gs_register_root(mem, root, ptr_ref_type, pp, cname);
+}
+
+/*
+ * 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, */
+ /* and we aren't about to overflow the maximum run length, use it. */
+ if (mem->cc.rtop == mem->cc.cbot &&
+ num_refs < (mem->cc.ctop - mem->cc.cbot) / sizeof(ref) &&
+ mem->cc.rtop - (byte *) mem->cc.rcur + num_refs * sizeof(ref) <
+ max_size_st_refs
+ ) {
+ ref *end;
+
+ obj = (ref *) mem->cc.rtop - 1; /* back up over last ref */
+ if_debug4('A', "[a%d:+$ ]%s(%u) = 0x%lx\n", mem->space,
+ client_name_string(cname), num_refs, (ulong) obj);
+ mem->cc.rcur[-1].o_size += num_refs * sizeof(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", mem->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", mem->space,
+ client_name_string(cname), diff, (ulong) obj);
+ mem->lost.refs += 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))
+ DO_NOTHING; /* 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",
+ mem->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 = mem;
+ cl.cp = mem->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",
+ mem->space, client_name_string(cname),
+ num_refs, (ulong) obj);
+ alloc_free_chunk(cl.cp, mem);
+ return;
+ }
+ }
+ /* Punt, but fill the array with nulls so that there won't be */
+ /* dangling references to confuse the garbage collector. */
+ if_debug4('A', "[a%d:-$#]%s(%u) 0x%lx\n", mem->space,
+ client_name_string(cname), num_refs, (ulong) obj);
+ {
+ uint size;
+
+ switch (r_type(parr)) {
+ case t_shortarray:
+ size = num_refs * sizeof(ref_packed);
+ break;
+ case t_mixedarray:{
+ /* We have to parse the array to compute the storage size. */
+ uint i = 0;
+ const ref_packed *p = parr->value.packed;
+
+ for (; i < num_refs; ++i)
+ p = packed_next(p);
+ size = (const byte *)p - (const byte *)parr->value.packed;
+ break;
+ }
+ case t_array:
+ size = num_refs * sizeof(ref);
+ break;
+ default:
+ lprintf3("Unknown type 0x%x in free_ref_array(%u,0x%lx)!",
+ r_type(parr), num_refs, (ulong) obj);
+ return;
+ }
+ /* If there are any leftover packed elements, we don't */
+ /* worry about them, since they can't be dangling references. */
+ refset_null(obj, size / sizeof(ref));
+ mem->lost.refs += size;
+ }
+}
+
+/* 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..b24b574dd
--- /dev/null
+++ b/pstoraster/ialloc.h
@@ -0,0 +1,125 @@
+/* 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 supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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_raw_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..4fbfe892a
--- /dev/null
+++ b/pstoraster/iastate.h
@@ -0,0 +1,35 @@
+/* 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 supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Requires gsmemory.h, gsstruct.h */
+
+#ifndef iastate_INCLUDED
+# define iastate_INCLUDED
+
+#include "gxalloc.h"
+#include "istruct.h"
+#include "ialloc.h"
+
+#endif /* iastate_INCLUDED */
diff --git a/pstoraster/iastruct.h b/pstoraster/iastruct.h
new file mode 100644
index 000000000..11080d0fd
--- /dev/null
+++ b/pstoraster/iastruct.h
@@ -0,0 +1,34 @@
+/* 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 supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Interpreter memory manager implementation structures */
+
+#ifndef iastruct_INCLUDED
+# define iastruct_INCLUDED
+
+#include "gxobj.h"
+#include "ialloc.h"
+
+#endif /* iastruct_INCLUDED */
diff --git a/pstoraster/ibnum.c b/pstoraster/ibnum.c
new file mode 100644
index 000000000..e48c8105d
--- /dev/null
+++ b/pstoraster/ibnum.c
@@ -0,0 +1,222 @@
+/* Copyright (C) 1990, 1996, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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;
+
+ if (r_size(op) < 4 || bp[0] != bt_num_array_value)
+ return_error(e_rangecheck);
+ format = bp[1];
+ if (!num_is_valid(format) ||
+ 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 or unsigned) short. */
+uint
+sdecodeushort(const byte * p, int format)
+{
+ int a = p[0], b = p[1];
+
+ return (num_is_lsb(format) ? (b << 8) + a : (a << 8) + b);
+}
+int
+sdecodeshort(const byte * p, int format)
+{
+ int v = (int)sdecodeushort(p, format);
+
+ return (v & 0x7fff) - (v & 0x8000);
+}
+
+/* Decode a (32-bit, signed) long. */
+long
+sdecodelong(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);
+
+ /*
+ * The following is only needed if sizeof(long) > 4, but it does
+ * no harm if sizeof(long) == 4.
+ */
+ return (v ^ 0x80000000L) - 0x80000000L;
+}
+
+/* Decode a float. We assume that native floats occupy 32 bits. */
+float
+sdecodefloat(const byte * p, int format)
+{
+ bits32 lnum = (bits32) sdecodelong(p, format);
+ float fnum;
+
+#if !arch_floats_are_IEEE
+ if (format != num_float_native) {
+ /* We know IEEE floats take 32 bits. */
+ /* 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
+#endif
+ fnum = *(float *)&lnum;
+ return fnum;
+}
diff --git a/pstoraster/ibnum.h b/pstoraster/ibnum.h
new file mode 100644
index 000000000..7d91fadfb
--- /dev/null
+++ b/pstoraster/ibnum.h
@@ -0,0 +1,71 @@
+/* Copyright (C) 1990, 1996, 1997 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Requires stream.h */
+
+#ifndef ibnum_INCLUDED
+# define ibnum_INCLUDED
+
+/* 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 *));
+int sdecodeshort(P2(const byte *, int));
+uint sdecodeushort(P2(const byte *, int));
+long sdecodelong(P2(const byte *, int));
+float sdecodefloat(P2(const byte *, int));
+
+#endif /* ibnum_INCLUDED */
diff --git a/pstoraster/iccinit0.c b/pstoraster/iccinit0.c
new file mode 100644
index 000000000..c7b60c7a3
--- /dev/null
+++ b/pstoraster/iccinit0.c
@@ -0,0 +1,31 @@
+/* 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 supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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..fcc34e879
--- /dev/null
+++ b/pstoraster/ichar.h
@@ -0,0 +1,72 @@
+/* Copyright (C) 1994, 1996, 1997 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Requires gxchar.h */
+
+#ifndef ichar_INCLUDED
+# define ichar_INCLUDED
+
+/*
+ * 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,
+ * and for error recovery (t_integer);
+ * a slot for the saved d-stack depth ditto (t_integer);
+ * a slot for the saved gstate level 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 8
+#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 esgslevel(ep) ((ep)[-5])
+#define sgslevel esgslevel(esp)
+#define eseproc(ep) ((ep)[-6])
+#define seproc eseproc(esp)
+
+/* Procedures exported by zchar.c for zchar1.c, zchar2.c, and/or zcharcid.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_free(P1(int));
+
+#endif /* ichar_INCLUDED */
diff --git a/pstoraster/icharout.h b/pstoraster/icharout.h
new file mode 100644
index 000000000..2e8993466
--- /dev/null
+++ b/pstoraster/icharout.h
@@ -0,0 +1,59 @@
+/* Copyright (C) 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Interface to zcharout.c */
+
+#ifndef icharout_INCLUDED
+# define icharout_INCLUDED
+
+/* 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,
+ double 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 double psb[2],
+ const double pwidth[2], const gs_rect * pbbox,
+ int (*cont_fill) (P1(os_ptr)),
+ int (*cont_stroke) (P1(os_ptr))));
+
+#endif /* icharout_INCLUDED */
diff --git a/pstoraster/icie.h b/pstoraster/icie.h
new file mode 100644
index 000000000..69ec0fa93
--- /dev/null
+++ b/pstoraster/icie.h
@@ -0,0 +1,96 @@
+/* Copyright (C) 1995, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Internal definitions for interpreter CIE color handling */
+
+#ifndef icie_INCLUDED
+# define icie_INCLUDED
+
+/*
+ * 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 *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 *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 (*finish_proc) (P1(os_ptr)),
+ gs_ref_memory_t * imem, void *data));
+int cie_prepare_cache(P6(const gs_range * domain, const ref * proc,
+ cie_cache_floats * pcache, void *container,
+ gs_ref_memory_t * imem, client_name_t cname));
+int cie_prepare_caches_4(P9(const gs_range * domains, const ref * procs,
+ cie_cache_floats * pc0,
+ cie_cache_floats * pc1,
+ cie_cache_floats * pc2,
+ cie_cache_floats * pc3 /* may be 0 */,
+ void *container,
+ gs_ref_memory_t * imem, client_name_t cname));
+#define cie_prepare_cache3(d3,p3,c3,pcie,imem,cname)\
+ cie_prepare_caches_4((d3)->ranges, p3,\
+ &(c3)->floats, &(c3)[1].floats, &(c3)[2].floats,\
+ NULL, pcie, imem, cname)
+#define cie_prepare_cache4(d4,p4,c4,pcie,imem,cname)\
+ cie_prepare_caches_4((d4)->ranges, p4,\
+ &(c4)->floats, &(c4)[1].floats, &(c4)[2].floats,\
+ &(c4)[3].floats, pcie, imem, cname)
+
+int cie_cache_joint(P2(const ref_cie_render_procs *, gs_state *));
+
+#endif /* icie_INCLUDED */
diff --git a/pstoraster/icolor.h b/pstoraster/icolor.h
new file mode 100644
index 000000000..ea12e0b9b
--- /dev/null
+++ b/pstoraster/icolor.h
@@ -0,0 +1,54 @@
+/* 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 supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Declarations for transfer function & similar cache remapping */
+
+#ifndef icolor_INCLUDED
+# define icolor_INCLUDED
+
+/* 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));
+
+#endif /* icolor_INCLUDED */
diff --git a/pstoraster/iconfig.c b/pstoraster/iconfig.c
new file mode 100644
index 000000000..ca60afb2b
--- /dev/null
+++ b/pstoraster/iconfig.c
@@ -0,0 +1,74 @@
+/* Copyright (C) 1995, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Configuration-dependent tables and initialization for interpreter */
+#include "stdio_.h" /* stdio for stream.h */
+#include "gstypes.h"
+#include "gsmemory.h" /* for iminst.h */
+#include "gconf.h"
+#include "iref.h"
+#include "ivmspace.h"
+#include "opdef.h"
+#include "iminst.h"
+
+/* Define the default values for an interpreter instance. */
+const gs_main_instance gs_main_instance_init_values =
+{gs_main_instance_default_init_values};
+
+/* 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,len)\
+ { { (t_string<<r_type_shift) + a_readonly + avm_foreign, len }, s },
+#define psfile_(fns,len) string_(fns,len)
+const ref_(const char *) gs_init_file_array[] = {
+#include "gconf.h"
+ string_(0, 0)
+};
+#undef psfile_
+
+/* Set up the emulator name string array similarly. */
+#define emulator_(ems,len) string_(ems,len)
+const ref_(const char *) gs_emulator_name_array[] = {
+#include "gconf.h"
+ string_(0, 0)
+};
+#undef emulator_
+
+/* Initialize the operators. */
+ /* Declare the externs. */
+#define oper_(xx_op_defs) extern const op_def xx_op_defs[];
+#include "gconf.h"
+oper_(interp_op_defs) /* Interpreter operators */
+#undef oper_
+
+const op_def *const op_defs_all[] = {
+ /* Create the table. */
+#define oper_(defs) defs,
+#include "gconf.h"
+ oper_(interp_op_defs) /* Interpreter operators */
+#undef oper_
+ 0
+};
diff --git a/pstoraster/icontext.c b/pstoraster/icontext.c
new file mode 100644
index 000000000..84f00f2ec
--- /dev/null
+++ b/pstoraster/icontext.c
@@ -0,0 +1,275 @@
+/* Copyright (C) 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Context state operations */
+#include "ghost.h"
+#include "gsstruct.h" /* for gxalloc.h */
+#include "gxalloc.h"
+#include "errors.h"
+#include "stream.h" /* for files.h */
+#include "files.h"
+#include "idict.h"
+#include "igstate.h"
+#include "icontext.h"
+#include "interp.h"
+#include "isave.h"
+#include "dstack.h"
+#include "estack.h"
+#include "ostack.h"
+#include "store.h"
+
+/* Define the initial stack sizes. */
+#define DSTACK_INITIAL 20
+#define ESTACK_INITIAL 250
+#define OSTACK_INITIAL 200
+
+/* Per-context state stored in statics */
+extern ref ref_array_packing;
+extern ref ref_binary_object_format;
+extern long zrand_state;
+
+ /*extern ref ref_stdio[3]; *//* in files.h */
+
+/* Initialization procedures */
+void zrand_state_init(P1(long *));
+
+/* GC descriptors */
+extern_st(st_ref_stack);
+#define pcst ((gs_context_state_t *)vptr)
+private
+CLEAR_MARKS_PROC(context_state_clear_marks)
+{
+ r_clear_attrs(&pcst->userparams, l_mark);
+}
+private
+ENUM_PTRS_BEGIN(context_state_enum_ptrs) return 0;
+
+ENUM_PTR3(0, gs_context_state_t, dstack, estack, ostack);
+ENUM_PTR3(3, gs_context_state_t, pgs, stdio[0].value.pstruct,
+ stdio[1].value.pstruct);
+case 6:
+ENUM_RETURN_REF(&pcst->userparams);
+ENUM_PTRS_END
+private RELOC_PTRS_BEGIN(context_state_reloc_ptrs);
+RELOC_PTR3(gs_context_state_t, dstack, estack, ostack);
+RELOC_PTR3(gs_context_state_t, pgs, stdio[0].value.pstruct,
+ stdio[1].value.pstruct);
+RELOC_REF_VAR(pcst->userparams);
+r_clear_attrs(&pcst->userparams, l_mark);
+RELOC_PTRS_END
+#undef pcst
+public_st_context_state();
+
+/* Allocate the state of a context. */
+ int
+ context_state_alloc(gs_context_state_t ** ppcst,
+ const gs_dual_memory_t * dmem)
+{
+ gs_ref_memory_t *mem = dmem->space_local;
+ gs_context_state_t *pcst = *ppcst;
+ int code;
+ int i;
+
+ if (pcst == 0) {
+ pcst = gs_alloc_struct((gs_memory_t *) mem, gs_context_state_t,
+ &st_context_state, "context_state_alloc");
+ if (pcst == 0)
+ return_error(e_VMerror);
+ }
+ code = gs_interp_alloc_stacks(mem, pcst);
+ if (code < 0)
+ goto x0;
+ pcst->pgs = int_gstate_alloc(mem);
+ if (pcst->pgs == 0) {
+ code = gs_note_error(e_VMerror);
+ goto x1;
+ }
+ pcst->memory = *dmem;
+ make_false(&pcst->array_packing);
+ make_int(&pcst->binary_object_format, 0);
+ zrand_state_init(&pcst->rand_state);
+ pcst->usertime_total = 0;
+ pcst->keep_usertime = false;
+ { /*
+ * Create an empty userparams dictionary of the right size.
+ * If we can't determine the size, pick an arbitrary one.
+ */
+ ref *puserparams;
+ uint size;
+
+ if (dict_find_string(systemdict, "userparams", &puserparams) >= 0)
+ size = dict_length(puserparams);
+ else
+ size = 20;
+ code = dict_alloc(pcst->memory.space_local, size, &pcst->userparams);
+ if (code < 0)
+ goto x2;
+ /* PostScript code initializes the user parameters. */
+ }
+ /* The initial stdio values are bogus.... */
+ make_file(&pcst->stdio[0], 0, 1, invalid_file_entry);
+ make_file(&pcst->stdio[1], 0, 1, invalid_file_entry);
+ for (i = countof(pcst->memory.spaces.indexed); --i >= 0;)
+ if (dmem->spaces.indexed[i] != 0)
+ ++(dmem->spaces.indexed[i]->num_contexts);
+ *ppcst = pcst;
+ return 0;
+ x2:gs_state_free(pcst->pgs);
+ x1:gs_interp_free_stacks(mem, pcst);
+ x0:if (*ppcst == 0)
+ gs_free_object((gs_memory_t *) mem, pcst, "context_state_alloc");
+ return code;
+}
+
+/* Load the interpreter state from a context. */
+int
+context_state_load(const gs_context_state_t * pcst)
+{
+ gs_ref_memory_t *lmem = iimemory_local;
+ uint space = r_space(systemdict);
+ int code;
+
+ d_stack = *pcst->dstack;
+ e_stack = *pcst->estack;
+ o_stack = *pcst->ostack;
+ igs = pcst->pgs;
+ gs_imemory = pcst->memory;
+ ref_array_packing = pcst->array_packing;
+ ref_binary_object_format = pcst->binary_object_format;
+ zrand_state = pcst->rand_state;
+ /*
+ * Set systemdict.userparams to the saved copy, and then
+ * set the actual user parameters. Be careful to disable both
+ * space checking and save checking while we do this.
+ */
+ r_set_space(systemdict, avm_max);
+ alloc_set_not_in_save(idmemory);
+ code = dict_put_string(systemdict, "userparams", &pcst->userparams);
+ if (code >= 0)
+ code = set_user_params(&pcst->userparams);
+ ref_stdio[0] = pcst->stdio[0];
+ ref_stdio[1] = pcst->stdio[1];
+ if (iimemory_local != lmem) {
+ /*
+ * Switch references in systemdict to local objects.
+ * userdict.localdicts holds these objects.
+ */
+ const ref *puserdict =
+ ref_stack_index(&d_stack, ref_stack_count(&d_stack) - 1 -
+ dstack_userdict_index);
+ ref *plocaldicts;
+
+ if (dict_find_string(puserdict, "localdicts", &plocaldicts) > 0 &&
+ r_has_type(plocaldicts, t_dictionary)
+ ) {
+ dict_copy(plocaldicts, systemdict);
+ }
+ }
+ r_set_space(systemdict, space);
+ if (idmemory->save_level > 0)
+ alloc_set_in_save(idmemory);
+ esfile_clear_cache();
+ dict_set_top(); /* reload dict stack cache */
+ return code;
+}
+
+/* Store the interpreter state in a context. */
+int
+context_state_store(gs_context_state_t * pcst)
+{
+ ref_stack_cleanup(&d_stack);
+ ref_stack_cleanup(&e_stack);
+ ref_stack_cleanup(&o_stack);
+ *pcst->dstack = d_stack;
+ *pcst->estack = e_stack;
+ *pcst->ostack = o_stack;
+ pcst->pgs = igs;
+ pcst->memory = gs_imemory;
+ pcst->array_packing = ref_array_packing;
+ pcst->binary_object_format = ref_binary_object_format;
+ pcst->rand_state = zrand_state;
+ /*
+ * The user parameters in systemdict.userparams are kept
+ * up to date by PostScript code, but we still need to save
+ * systemdict.userparams to get the correct l_new flag.
+ */
+ {
+ ref *puserparams;
+
+ if (dict_find_string(systemdict, "userparams", &puserparams) < 0)
+ return_error(e_Fatal);
+ pcst->userparams = *puserparams;
+ }
+ pcst->stdio[0] = ref_stdio[0];
+ pcst->stdio[1] = ref_stdio[1];
+ return 0;
+}
+
+/* Free the state of a context. */
+bool
+context_state_free(gs_context_state_t * pcst)
+{
+ gs_ref_memory_t *mem = pcst->memory.space_local;
+ int freed = 0;
+ int i;
+
+ /*
+ * If this context is the last one referencing a particular VM
+ * (local / global / system), free the entire VM space;
+ * otherwise, just free the context-related structures.
+ */
+ for (i = countof(pcst->memory.spaces.indexed); --i >= 0;) {
+ if (pcst->memory.spaces.indexed[i] != 0 &&
+ !--(pcst->memory.spaces.indexed[i]->num_contexts)
+ ) {
+/****** FREE THE ENTIRE SPACE ******/
+ freed |= 1 << i;
+ }
+ }
+ /*
+ * If we freed any spaces at all, we must have freed the local
+ * VM where the context structure and its substructures were
+ * allocated.
+ */
+ if (freed)
+ return freed;
+ {
+ gs_state *pgs = pcst->pgs;
+
+ gs_grestoreall(pgs);
+ /* Patch the saved pointer so we can do the last grestore. */
+ {
+ gs_state *saved = gs_state_saved(pgs);
+
+ gs_state_swap_saved(saved, saved);
+ }
+ gs_grestore(pgs);
+ gs_state_swap_saved(pgs, (gs_state *) 0);
+ gs_state_free(pgs);
+ }
+/****** FREE USERPARAMS ******/
+ gs_interp_free_stacks(mem, pcst);
+ return false;
+}
diff --git a/pstoraster/icontext.h b/pstoraster/icontext.h
new file mode 100644
index 000000000..b0152924d
--- /dev/null
+++ b/pstoraster/icontext.h
@@ -0,0 +1,59 @@
+/* Copyright (C) 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Externally visible context state */
+/* Requires iref.h, stdio_.h */
+
+#ifndef icontext_INCLUDED
+# define icontext_INCLUDED
+
+#include "gsstruct.h" /* for extern_st */
+#include "icstate.h"
+
+/* Declare the GC descriptor for context states. */
+extern_st(st_context_state);
+
+/*
+ * Define the procedure for resetting user parameters when switching
+ * contexts. This is defined in either zusparam.c or inouparm.c.
+ */
+extern int set_user_params(P1(const ref * paramdict));
+
+/* Allocate the state of a context, always in local VM. */
+/* If *ppcst == 0, allocate the state object as well. */
+int context_state_alloc(P2(gs_context_state_t ** ppcst,
+ const gs_dual_memory_t * dmem));
+
+/* Load the state of the interpreter from a context. */
+int context_state_load(P1(const gs_context_state_t *));
+
+/* Store the state of the interpreter into a context. */
+int context_state_store(P1(gs_context_state_t *));
+
+/* Free the contents of the state of a context, always to its local VM. */
+/* Return a mask of which of its VMs, if any, we freed. */
+int context_state_free(P1(gs_context_state_t *));
+
+#endif /* icontext_INCLUDED */
diff --git a/pstoraster/icsmap.h b/pstoraster/icsmap.h
new file mode 100644
index 000000000..9521052fb
--- /dev/null
+++ b/pstoraster/icsmap.h
@@ -0,0 +1,45 @@
+/* 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 supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Interface to shared routines for loading the cached color space maps. */
+
+#ifndef icsmap_INCLUDED
+# define icsmap_INCLUDED
+
+/*
+ * 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))));
+
+#endif /* icsmap_INCLUDED */
diff --git a/pstoraster/icstate.h b/pstoraster/icstate.h
new file mode 100644
index 000000000..1183e9d04
--- /dev/null
+++ b/pstoraster/icstate.h
@@ -0,0 +1,74 @@
+/* Copyright (C) 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Externally visible context state */
+/* Requires iref.h */
+
+#ifndef icstate_INCLUDED
+# define icstate_INCLUDED
+
+#include "imemory.h"
+
+/*
+ * Define the externally visible state of an interpreter context.
+ * If we aren't supporting Display PostScript features, there is only
+ * a single context.
+ */
+#ifndef gs_context_state_t_DEFINED
+# define gs_context_state_t_DEFINED
+typedef struct gs_context_state_s gs_context_state_t;
+#endif
+#ifndef ref_stack_DEFINED
+# define ref_stack_DEFINED
+typedef struct ref_stack_s ref_stack;
+#endif
+struct gs_context_state_s {
+ ref_stack *dstack;
+ ref_stack *estack;
+ ref_stack *ostack;
+ gs_state *pgs;
+ gs_dual_memory_t memory;
+ ref array_packing; /* t_boolean */
+ ref binary_object_format; /* t_integer */
+ long rand_state; /* (not in Red Book) */
+ long usertime_total; /* total accumulated usertime, */
+ /* not counting current time if running */
+ bool keep_usertime; /* true if context ever executed usertime */
+ /* View clipping is handled in the graphics state. */
+ ref userparams; /* t_dictionary */
+ ref stdio[2]; /* t_file */
+};
+
+/*
+ * We make st_context_state public because interp.c must allocate one,
+ * and zcontext.c must subclass it.
+ */
+/*extern_st(st_context_state); *//* in icontext.h */
+#define public_st_context_state() /* in icontext.c */\
+ gs_public_st_complex_only(st_context_state, gs_context_state_t,\
+ "gs_context_state_t", context_state_clear_marks,\
+ context_state_enum_ptrs, context_state_reloc_ptrs, 0)
+
+#endif /* icstate_INCLUDED */
diff --git a/pstoraster/idebug.c b/pstoraster/idebug.c
new file mode 100644
index 000000000..a472c5b76
--- /dev/null
+++ b/pstoraster/idebug.c
@@ -0,0 +1,299 @@
+/* Copyright (C) 1989, 1995, 1996, 1997 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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 "opdef.h"
+
+/* Table of type name strings */
+static const char *const 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#%u)", (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#%u)", (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--) { /* The double cast in the next line is to pacify some */
+ /* unreasonably picky compilers. */
+ dprintf2("..%04x: 0x%04x ", (uint) (ulong) p & 0xffff,
+ r_type_attrs(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);
+ /* The double cast in the next line is to pacify some */
+ /* unreasonably picky compilers. */
+ dprintf3("..%04x%c 0x%02x ",
+ (uint) (ulong) 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..bd03b4cfd
--- /dev/null
+++ b/pstoraster/idebug.h
@@ -0,0 +1,48 @@
+/* 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 supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Prototypes for debugging procedures in idebug.c */
+
+#ifndef idebug_INCLUDED
+# define idebug_INCLUDED
+
+/* 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));
+
+#endif /* idebug_INCLUDED */
diff --git a/pstoraster/idict.c b/pstoraster/idict.c
new file mode 100644
index 000000000..1281a63fc
--- /dev/null
+++ b/pstoraster/idict.c
@@ -0,0 +1,780 @@
+/*
+ Copyright 1993-2000 by Easy Software Products.
+ Copyright 1989, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Dictionary implementation */
+#include "string_.h" /* for strlen */
+#include "ghost.h"
+#include "errors.h"
+#include "imemory.h"
+#include "idebug.h" /* for debug_print_name */
+#include "inamedef.h"
+#include "iname.h"
+#include "ipacked.h"
+#include "isave.h" /* for value cache in names */
+#include "store.h"
+#include "idict.h" /* interface definition */
+#include "idictdef.h"
+#include "iutil.h"
+#include "ivmspace.h" /* for store check */
+
+/*
+ * Dictionaries per se aren't supposed to know anything about the
+ * dictionary stack, let alone the interpreter's dictionary stack.
+ * Unfortunately, there is are two design couplings between them:
+ * dictionary stacks cache some of the elements of their top dictionary
+ * (requiring updating when that dictionary grows or is unpacked),
+ * and names may cache a pointer to their definition (requiring a
+ * check whether a dictionary appears on the dictionary stack).
+ * Therefore, we patch in a few relevant definitions here.
+ ****** WE'D REALLY LIKE TO FIX THIS, BUT WE DON'T SEE HOW. ******
+ */
+#include "idstack.h"
+/* The following are copied from dstack.h. */
+extern dict_stack_t idict_stack;
+
+#define systemdict (&idict_stack.system_dict)
+#define dict_set_top() dstack_set_top(&idict_stack);
+#define dict_is_permanent_on_dstack(pdict)\
+ dstack_dict_is_permanent(&idict_stack, pdict)
+
+/*
+ * 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;
+
+/* 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 */
+
+/* Wrapper for dict_find */
+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 =
+ dict_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))
+ dlprintf3("[d]lookups=%ld 1probe=%ld 2probe=%ld\n",
+ dn_lookups, dn_1probe, dn_2probe);
+ return code;
+}
+#define dict_find real_dict_find
+#endif
+
+/* Round up the size of a dictionary. Return 0 if too large. */
+uint
+dict_round_size_small(uint rsize)
+{
+ return (rsize > dict_max_size ? 0 : rsize);
+}
+uint
+dict_round_size_large(uint rsize)
+{ /* Round up to a power of 2 if not huge. */
+ /* If the addition overflows, the new rsize will be zero, */
+ /* which will (correctly) be interpreted as a limitcheck. */
+ if (rsize > dict_max_non_huge)
+ return (rsize > dict_max_size ? 0 : rsize);
+ while (rsize & (rsize - 1))
+ rsize = (rsize | (rsize - 1)) + 1;
+ return (rsize <= dict_max_size ? rsize : dict_max_non_huge);
+}
+
+/* Create a dictionary using the given allocator. */
+int
+dict_alloc(gs_ref_memory_t * mem, uint size, ref * pdref)
+{
+ ref arr;
+ int code =
+ gs_alloc_ref_array(mem, &arr, a_all, sizeof(dict) / sizeof(ref),
+ "dict_alloc");
+ dict *pdict;
+ ref dref;
+
+ if (code < 0)
+ return code;
+ pdict = (dict *) arr.value.refs;
+ make_tav_new(&dref, t_dictionary, r_space(&arr) | a_all,
+ pdict, pdict);
+ make_struct(&pdict->memory, avm_foreign, mem);
+ code = dict_create_contents(size, &dref, dict_default_pack);
+ if (code < 0) {
+ gs_free_ref_array(mem, &arr, "dict_alloc");
+ return code;
+ }
+ *pdref = dref;
+ return 0;
+}
+/* Create unpacked keys for a dictionary. */
+/* The keys are allocated using the same allocator as the dictionary. */
+private int
+dict_create_unpacked_keys(uint asize, const ref * pdref)
+{
+ dict *pdict = pdref->value.pdict;
+ gs_ref_memory_t *mem = dict_memory(pdict);
+ int code;
+
+ code = gs_alloc_ref_array(mem, &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 */
+ }
+ 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;
+ gs_ref_memory_t *mem = dict_memory(pdict);
+ uint asize = dict_round_size((size == 0 ? 1 : size));
+ int code;
+ uint i;
+
+ if (asize == 0 || asize > max_array_size - 1) /* too large */
+ return_error(e_limitcheck);
+ asize++; /* allow room for wraparound entry */
+ code = gs_alloc_ref_array(mem, &pdict->values, a_all, asize,
+ "dict_create_contents(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 = gs_alloc_ref_array(mem, &arr, a_all, ksize,
+ "dict_create_contents(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);
+ } else if (*okp == packed_key_deleted)
+ r_set_attrs(nkp, a_executable);
+ if (!ref_must_save(&old_keys))
+ gs_free_ref_array(dict_memory(pdict), &old_keys,
+ "dict_unpack(old keys)");
+ dict_set_top(); /* just in case */
+ }
+ return 0;
+}
+
+/*
+ * 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(*ppvalue = packed_search_value_pointer,
+ return 1,
+ if (pslot == 0) pslot = kp, goto miss);
+ packed_search_2(*ppvalue = packed_search_value_pointer,
+ return 1,
+ if (pslot == 0) pslot = kp, 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 + dict_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 *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);
+}
+
+/*
+ * 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);
+}
+
+/* Return the maximum index of a slot within a dictionary. */
+uint
+dict_max_index(const ref * pdref /* t_dictionary */ )
+{
+ return npairs(pdref->value.pdict) - 1;
+}
+
+/* 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;
+}
+
+/* Resize a dictionary. */
+int
+dict_resize(ref * pdref, uint new_size)
+{
+ dict *pdict = pdref->value.pdict;
+ gs_ref_memory_t *mem = dict_memory(pdict);
+ dict dnew;
+ ref drto;
+ int code;
+
+ if (new_size < d_length(pdict)) {
+ if (!dict_auto_expand)
+ return_error(e_dictfull);
+ new_size = d_length(pdict);
+ }
+ make_tav_new(&drto, t_dictionary, r_space(pdref) | a_all,
+ pdict, &dnew);
+ dnew.memory = pdict->memory;
+ if ((code = dict_create_contents(new_size, &drto, dict_is_packed(pdict))) < 0)
+ 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
+ gs_free_ref_array(mem, &pdict->values, "dict_resize(old values)");
+ if (ref_must_save(&pdict->keys))
+ ref_do_save(pdref, &pdict->keys, "dict_resize(keys)");
+ else
+ gs_free_ref_array(mem, &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);
+ 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 increased the size. */
+ ulong new_size = (ulong) d_maxlength(pdict) * 3 / 2 + 2;
+
+#if arch_sizeof_int < arch_sizeof_long
+ if (new_size > max_uint)
+ new_size = max_uint;
+#endif
+ if (new_size > npairs(pdict)) {
+ int code = dict_resize(pdref, (uint) new_size);
+
+ if (code >= 0)
+ return code;
+ /* new_size was too big. */
+ if (npairs(pdict) < dict_max_size) {
+ code = dict_resize(pdref, dict_max_size);
+ if (code >= 0)
+ return code;
+ }
+ if (npairs(pdict) == d_maxlength(pdict)) { /* Can't do it. */
+ return code;
+ }
+ /* We can't grow to new_size, but we can grow to npairs. */
+ new_size = npairs(pdict);
+ }
+ /* 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;
+}
diff --git a/pstoraster/idict.h b/pstoraster/idict.h
new file mode 100644
index 000000000..7f1925ed9
--- /dev/null
+++ b/pstoraster/idict.h
@@ -0,0 +1,268 @@
+/* Copyright (C) 1989, 1995, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Interfaces for Ghostscript dictionary package */
+
+#ifndef idict_INCLUDED
+# define idict_INCLUDED
+
+/*
+ * 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. */
+ ref memory; /* foreign t_struct, the allocator that */
+ /* created this dictionary */
+#define dict_memory(pdict) r_ptr(&(pdict)->memory, gs_ref_memory_t)
+};
+
+/*
+ * 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.
+ */
+#ifndef gs_ref_memory_DEFINED
+# define gs_ref_memory_DEFINED
+typedef struct gs_ref_memory_s gs_ref_memory_t;
+
+#endif
+int dict_alloc(P3(gs_ref_memory_t *, uint maxlength, ref * pdref));
+
+#define dict_create(maxlen, pdref)\
+ dict_alloc(iimemory, maxlen, 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 *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));
+
+/*
+ * Return the maximum index of a slot within a dictionary.
+ * Note that this may be greater than maxlength.
+ */
+uint dict_max_index(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..max_index-1].
+ */
+int dict_value_index(P2(const ref * pdref, const ref * pvalue));
+
+/*
+ * Given an index in [0..max_index-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));
+
+/*
+ * The following are some internal details that are used in both the
+ * implementation and some high-performance clients.
+ */
+
+/* On machines with reasonable amounts of memory, we round up dictionary
+ * sizes to the next power of 2 whenever possible, to allow us to use
+ * masking rather than division for computing the hash index.
+ * Unfortunately, if we required this, it would cut the maximum size of a
+ * dictionary in half. Therefore, on such machines, we distinguish
+ * "huge" dictionaries (dictionaries whose size is larger than the largest
+ * power of 2 less than dict_max_size) as a special case:
+ *
+ * - If the top dictionary on the stack is huge, we set the dtop
+ * parameters so that the fast inline lookup will always fail.
+ *
+ * - For general lookup, we use the slower hash_mod algorithm for
+ * huge dictionaries.
+ */
+#define dict_max_non_huge ((uint)(max_array_size / 2 + 1))
+
+/* 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)
+
+/* Hash an arbitrary non-negative or unsigned integer into a dictionary. */
+#define dict_hash_mod_rem(hash, size) ((hash) % (size))
+#define dict_hash_mod_mask(hash, size) ((hash) & ((size) - 1))
+#define dict_hash_mod_small(hash, size) dict_hash_mod_rem(hash, size)
+#define dict_hash_mod_inline_small(hash, size) dict_hash_mod_rem(hash, size)
+#define dict_hash_mod_large(hash, size)\
+ (size > dict_max_non_huge ? dict_hash_mod_rem(hash, size) :\
+ dict_hash_mod_mask(hash, size))
+#define dict_hash_mod_inline_large(hash, size) dict_hash_mod_mask(hash, size)
+/* Round up the requested size of a dictionary. Return 0 if too big. */
+uint dict_round_size_small(P1(uint rsize));
+uint dict_round_size_large(P1(uint rsize));
+
+/* Choose the algorithms depending on the size of memory. */
+#if arch_small_memory
+# define dict_hash_mod(h, s) dict_hash_mod_small(h, s)
+# define dict_hash_mod_inline(h, s) dict_hash_mod_inline_small(h, s)
+# define dict_round_size(s) dict_round_size_small(s)
+#else
+# ifdef DEBUG
+# define dict_hash_mod(h, s)\
+ (gs_debug_c('.') ? dict_hash_mod_small(h, s) :\
+ dict_hash_mod_large(h, s))
+# define dict_hash_mod_inline(h, s)\
+ (gs_debug_c('.') ? dict_hash_mod_inline_small(h, s) :\
+ dict_hash_mod_inline_large(h, s))
+# define dict_round_size(s)\
+ (gs_debug_c('.') ? dict_round_size_small(s) :\
+ dict_round_size_large(s))
+# else
+# define dict_hash_mod(h, s) dict_hash_mod_large(h, s)
+# define dict_hash_mod_inline(h, s) dict_hash_mod_inline_large(h, s)
+# define dict_round_size(s) dict_round_size_large(s)
+# endif
+#endif
+
+#endif /* idict_INCLUDED */
diff --git a/pstoraster/idictdef.h b/pstoraster/idictdef.h
new file mode 100644
index 000000000..68abe42ad
--- /dev/null
+++ b/pstoraster/idictdef.h
@@ -0,0 +1,128 @@
+/* Copyright (C) 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Internals of dictionary implementation */
+
+#ifndef idictdef_INCLUDED
+# define idictdef_INCLUDED
+
+/*
+ * A dictionary of capacity M is a structure containing the following
+ * 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).
+ *
+ * memory - a foreign t_struct referencing the allocator used to
+ * create this dictionary, which will also be used to expand or
+ * unpack it if necessary.
+ *
+ * C < M is possible because on large-memory systems, we usually round up M
+ * so that M is a power of 2 (see idict.h for details); 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 macros 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_value_pointer (pdict->values.value.refs + (kp - kbot))
+#define packed_search_body(found1,found2,del,miss)\
+ { if_debug2('D', "[D]probe 0x%lx: 0x%x\n", (ulong)kp, *kp);\
+ if ( *kp == kpack )\
+ { found1;\
+ found2;\
+ }\
+ 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(found1,found2,del,miss)\
+ const ref_packed *kbot = pdict->keys.value.packed;\
+ register const ref_packed *kp;\
+ for ( kp = kbot + dict_hash_mod(hash, size) + 1; ; kp-- )\
+ packed_search_body(found1,found2,del,miss)
+#define packed_search_2(found1,found2,del,miss)\
+ for ( kp += size; ; kp-- )\
+ packed_search_body(found1,found2,del,miss)
+
+#endif /* idictdef_INCLUDED */
diff --git a/pstoraster/idparam.c b/pstoraster/idparam.c
new file mode 100644
index 000000000..842f29dba
--- /dev/null
+++ b/pstoraster/idparam.c
@@ -0,0 +1,371 @@
+/* Copyright (C) 1992, 1995, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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 *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 *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 *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 *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 *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 *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 *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 = float_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 *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 *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..cf01cff1f
--- /dev/null
+++ b/pstoraster/idparam.h
@@ -0,0 +1,90 @@
+/* Copyright (C) 1992, 1993, 1994, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Interface to idparam.c */
+
+#ifndef idparam_INCLUDED
+# define idparam_INCLUDED
+
+#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 *kstr,
+ bool defaultval, bool * pvalue));
+int dict_int_param(P6(const ref * pdict, const char *kstr,
+ int minval, int maxval, int defaultval, int *pvalue));
+int dict_int_null_param(P6(const ref * pdict, const char *kstr,
+ int minval, int maxval, int defaultval,
+ int *pvalue));
+int dict_uint_param(P6(const ref * pdict, const char *kstr,
+ uint minval, uint maxval, uint defaultval,
+ uint * pvalue));
+int dict_float_param(P4(const ref * pdict, const char *kstr,
+ floatp defaultval, float *pvalue));
+int dict_int_array_param(P4(const ref * pdict, const char *kstr,
+ uint maxlen, int *ivec));
+int dict_float_array_param(P5(const ref * pdict, const char *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 *kstr, ref * pproc,
+ bool defaultval));
+int dict_matrix_param(P3(const ref * pdict, const char *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));
+
+#endif /* idparam_INCLUDED */
diff --git a/pstoraster/idstack.c b/pstoraster/idstack.c
new file mode 100644
index 000000000..744dde405
--- /dev/null
+++ b/pstoraster/idstack.c
@@ -0,0 +1,249 @@
+/* Copyright (C) 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Implementation of dictionary stacks */
+#include "ghost.h"
+#include "idict.h"
+#include "idictdef.h"
+#include "idstack.h"
+#include "inamedef.h"
+#include "iname.h"
+#include "ipacked.h"
+#include "iutil.h"
+#include "ivmspace.h"
+
+/* Debugging statistics */
+#ifdef DEBUG
+#include "idebug.h"
+long ds_lookups; /* total lookups */
+long ds_1probe; /* successful lookups on only 1 probe */
+long ds_2probe; /* successful lookups on 2 probes */
+
+/* Wrapper for dstack_find_name_by_index */
+ref *real_dstack_find_name_by_index(P2(dict_stack_t * pds, uint nidx));
+ref *
+dstack_find_name_by_index(dict_stack_t * pds, uint nidx)
+{
+ ref *pvalue = real_dstack_find_name_by_index(pds, nidx);
+ dict *pdict = pds->stack.p->value.pdict;
+
+ ds_lookups++;
+ if (dict_is_packed(pdict)) {
+ uint hash =
+ dict_hash_mod(dict_name_index_hash(nidx), npairs(pdict)) + 1;
+
+ if (pdict->keys.value.packed[hash] ==
+ pt_tag(pt_literal_name) + nidx
+ )
+ ds_1probe++;
+ else if (pdict->keys.value.packed[hash - 1] ==
+ pt_tag(pt_literal_name) + nidx
+ )
+ ds_2probe++;
+ }
+ /* Do the cheap flag test before the expensive remainder test. */
+ if (gs_debug_c('d') && !(ds_lookups % 1000))
+ dlprintf3("[d]lookups=%ld 1probe=%ld 2probe=%ld\n",
+ ds_lookups, ds_1probe, ds_2probe);
+ return pvalue;
+}
+#define dstack_find_name_by_index real_dstack_find_name_by_index
+#endif
+
+/* Check whether a dictionary is one of the permanent ones on the d-stack. */
+bool
+dstack_dict_is_permanent(const dict_stack_t * pds, const ref * pdref)
+{
+ dict *pdict = pdref->value.pdict;
+ int i;
+
+ if (pds->stack.extension_size == 0) { /* Only one block of d-stack. */
+ for (i = 0; i < pds->min_size; ++i)
+ if (pds->stack.bot[i].value.pdict == pdict)
+ return true;
+ } else { /* More than one block of d-stack. */
+ uint count = ref_stack_count(&pds->stack);
+
+ for (i = count - pds->min_size; i < count; ++i)
+ if (ref_stack_index(&pds->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 *
+dstack_find_name_by_index(dict_stack_t * pds, uint nidx)
+{
+ ds_ptr pdref = pds->stack.p;
+
+/* 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);
+ dlputs("[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 packed_search_value_pointer,
+ DO_NOTHING, goto miss);
+ packed_search_2(DO_NOTHING,
+ return packed_search_value_pointer,
+ DO_NOTHING, break);
+ miss:;
+ } else {
+ ref *kbot = pdict->keys.value.refs;
+ register ref *kp;
+ int wrap = 0;
+
+ /* Search the dictionary */
+ for (kp = kbot + dict_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-- > pds->stack.bot);
+ /* The name isn't in the top dictionary block. */
+ /* If there are other blocks, search them now (more slowly). */
+ if (!pds->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 = pds->stack.p + 1 - pds->stack.bot;
+ uint size = ref_stack_count(&pds->stack);
+ ref *pvalue;
+
+ name_index_ref(nidx, &key);
+ for (; i < size; i++) {
+ if (dict_find(ref_stack_index(&pds->stack, i),
+ &key, &pvalue) > 0
+ )
+ return pvalue;
+ }
+ }
+ return (ref *) 0;
+#undef hash
+}
+
+/* Set the cached values computed from the top entry on the dstack. */
+/* See idstack.h for details. */
+private const ref_packed no_packed_keys[2] =
+{packed_key_deleted, packed_key_empty};
+void
+dstack_set_top(dict_stack_t * pds)
+{
+ ds_ptr dsp = pds->stack.p;
+ 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)
+ ) {
+ pds->top_keys = pdict->keys.value.packed;
+ pds->top_npairs = npairs(pdict);
+ pds->top_values = pdict->values.value.refs;
+ } else {
+ pds->top_keys = no_packed_keys;
+ pds->top_npairs = 1;
+ }
+ if (!r_has_attr(dict_access_ref(dsp), a_write))
+ pds->def_space = -1;
+ else
+ pds->def_space = r_space(dsp);
+}
+
+/* After a garbage collection, scan the permanent dictionaries and */
+/* update the cached value pointers in names. */
+void
+dstack_gc_cleanup(dict_stack_t * pds)
+{
+ uint count = ref_stack_count(&pds->stack);
+ uint dsi;
+
+ for (dsi = pds->min_size; dsi > 0; --dsi) {
+ const dict *pdict =
+ ref_stack_index(&pds->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/idstack.h b/pstoraster/idstack.h
new file mode 100644
index 000000000..887b10eb3
--- /dev/null
+++ b/pstoraster/idstack.h
@@ -0,0 +1,125 @@
+/* Copyright (C) 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Generic dictionary stack API */
+
+#ifndef idstack_INCLUDED
+# define idstack_INCLUDED
+
+#include "istack.h"
+
+/* Define the dictionary stack structure. */
+typedef struct dict_stack_s {
+
+ ref_stack stack; /* the actual stack of dictionaries */
+
+/*
+ * 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.
+ */
+ uint min_size; /* size of stack after clearing */
+
+ int userdict_index; /* index of userdict on stack */
+
+/*
+ * 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.
+ */
+ int def_space;
+
+/*
+ * 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.
+ */
+ const ref_packed *top_keys;
+ uint top_npairs;
+ ref *top_values;
+
+/*
+ * Cache a copy of the bottom entry on the stack, which is never deleted.
+ */
+ ref system_dict;
+
+} dict_stack_t;
+
+/*
+ * 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 dstack_set_top(P1(dict_stack_t *));
+
+/* Check whether a dictionary is one of the permanent ones on the d-stack. */
+bool dstack_dict_is_permanent(P2(const dict_stack_t *, const ref *));
+
+/* Define the type of pointers into the dictionary stack. */
+typedef s_ptr ds_ptr;
+typedef const_s_ptr const_ds_ptr;
+
+/* Clean up a dictionary stack after a garbage collection. */
+void dstack_gc_cleanup(P1(dict_stack_t *));
+
+/*
+ * Define a special fast entry for name lookup on a dictionary stack.
+ * 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 *dstack_find_name_by_index(P2(dict_stack_t *, uint));
+
+/*
+ * 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 dstack_find_name_by_index_inline(pds,nidx,htemp)\
+ ((pds)->top_keys[htemp = dict_hash_mod_inline(dict_name_index_hash(nidx),\
+ (pds)->top_npairs) + 1] == pt_tag(pt_literal_name) + (nidx) ?\
+ (pds)->top_values + htemp : dstack_find_name_by_index(pds, nidx))
+/*
+ * Define a similar macro that only checks the top dictionary on the stack.
+ */
+#define if_dstack_find_name_by_index_top(pds,nidx,htemp,pvslot)\
+ if ( (((pds)->top_keys[htemp = dict_hash_mod_inline(dict_name_index_hash(nidx),\
+ (pds)->top_npairs) + 1] == pt_tag(pt_literal_name) + (nidx)) ?\
+ ((pvslot) = (pds)->top_values + (htemp), 1) :\
+ 0)\
+ )
+
+#endif /* idstack_INCLUDED */
diff --git a/pstoraster/iestack.h b/pstoraster/iestack.h
new file mode 100644
index 000000000..751f99bcd
--- /dev/null
+++ b/pstoraster/iestack.h
@@ -0,0 +1,56 @@
+/* Copyright (C) 1989, 1992, 1993, 1994, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Generic execution stack API */
+
+#ifndef iestack_INCLUDED
+# define iestack_INCLUDED
+
+#include "istack.h"
+
+/* Define the execution stack structure. */
+typedef struct exec_stack_s {
+
+ ref_stack stack; /* the actual execution stack */
+
+/*
+ * 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().
+ */
+ ref *current_file;
+
+} exec_stack_t;
+
+/* Define pointers into the execution stack. */
+typedef s_ptr es_ptr;
+typedef const_s_ptr const_es_ptr;
+
+#endif /* iestack_INCLUDED */
diff --git a/pstoraster/ifilter.h b/pstoraster/ifilter.h
new file mode 100644
index 000000000..9d0803bd7
--- /dev/null
+++ b/pstoraster/ifilter.h
@@ -0,0 +1,89 @@
+/* Copyright (C) 1994, 1995, 1996, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Requires oper.h, stream.h, strimpl.h */
+
+#ifndef ifilter_INCLUDED
+# define ifilter_INCLUDED
+
+#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 a standard report_error procedure for filters, */
+/* that records the error message in $error.errorinfo. */
+stream_proc_report_error(filter_report_error);
+
+/*
+ * 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)
+
+#endif /* ifilter_INCLUDED */
diff --git a/pstoraster/ifont.h b/pstoraster/ifont.h
new file mode 100644
index 000000000..857a18741
--- /dev/null
+++ b/pstoraster/ifont.h
@@ -0,0 +1,97 @@
+/* Copyright (C) 1989, 1991, 1993, 1994, 1996, 1997 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Interpreter internal font representation */
+
+#ifndef ifont_INCLUDED
+# define ifont_INCLUDED
+
+#include "gsccode.h" /* for gs_glyph */
+#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 */
+ ref GlobalSubrs; /* from Private dictionary, */
+ /* for Type 2 charstrings */
+ } type1;
+ struct _f42 {
+ ref sfnts;
+ ref GlyphDirectory;
+ } 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, double 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));
+bool zfont_mark_glyph_name(P2(gs_glyph glyph, void *ignore_data));
+
+#endif /* ifont_INCLUDED */
diff --git a/pstoraster/ifunc.h b/pstoraster/ifunc.h
new file mode 100644
index 000000000..26c4c2fc0
--- /dev/null
+++ b/pstoraster/ifunc.h
@@ -0,0 +1,58 @@
+/* Copyright (C) 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Internal interpreter interfaces for Functions */
+
+#ifndef ifunc_INCLUDED
+# define ifunc_INCLUDED
+
+/* Define build procedures for the various function types. */
+#define build_function_proc(proc)\
+ int proc(P4(const_os_ptr op, const gs_function_params_t *params, int depth,\
+ gs_function_t **ppfn))
+build_function_proc(build_function_undefined);
+
+/* Define the table of build procedures, indexed by FunctionType. */
+extern build_function_proc((*build_function_procs[5]));
+
+/* Build a function structure from a PostScript dictionary. */
+int fn_build_sub_function(P3(const ref * op, gs_function_t ** ppfn, int depth));
+
+#define fn_build_function(op, ppfn)\
+ fn_build_sub_function(op, ppfn, 0)
+
+/* Allocate an array of function objects. */
+int ialloc_function_array(P2(uint count, gs_function_t *** pFunctions));
+
+/*
+ * Collect a heap-allocated array of floats. If the key is missing, set
+ * *pparray = 0 and return 0; otherwise set *pparray and return the number
+ * of elements. Note that 0-length arrays are acceptable, so if the value
+ * returned is 0, the caller must check whether *pparray == 0.
+ */
+int fn_build_float_array(P5(const ref * op, const char *kstr, bool required,
+ bool even, const float **pparray));
+
+#endif /* ifunc_INCLUDED */
diff --git a/pstoraster/igc.c b/pstoraster/igc.c
new file mode 100644
index 000000000..9305e77e1
--- /dev/null
+++ b/pstoraster/igc.c
@@ -0,0 +1,1316 @@
+/* Copyright (C) 1993, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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 */
+
+/* Define whether to force all garbage collections to be global. */
+private bool I_FORCE_GLOBAL_GC = false;
+
+/* Define whether to bypass the collector entirely. */
+private bool I_BYPASS_GC = false;
+
+/* Avoid including all of iname.h. */
+extern name_table *the_gs_name_table;
+
+/* 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 during GC */
+ 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 additional allocation */\
+ ((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(P1(name_table *));
+private int gc_trace(P3(gs_gc_root_t *, gc_state_t *, gc_mark_stack *));
+private int gc_rescan_chunk(P3(chunk_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 */
+private ptr_proc_reloc(igc_reloc_struct_ptr, void);
+
+string_proc_reloc(igc_reloc_string); /* in igcstr.c */
+const_string_proc_reloc(igc_reloc_const_string); /* in igcstr.c */
+ptr_proc_reloc(igc_reloc_ref_ptr, ref_packed); /* in igcref.c */
+refs_proc_reloc(igc_reloc_refs); /* in igcref.c */
+
+/* Define this GC's procedure vector. */
+private const gc_procs_with_refs_t igc_procs =
+{
+ igc_reloc_struct_ptr, igc_reloc_string, igc_reloc_const_string,
+ igc_reloc_ref_ptr, igc_reloc_refs
+};
+
+/* 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) igc_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 *str)
+{
+ if (gs_debug_c('6')) {
+ dlprintf1("[6]---------------- end %s ----------------\n",
+ (const char *)str);
+ fflush(dstderr);
+ }
+}
+static const char *const depth_dots_string = "..........";
+private const char *
+depth_dots(const ms_entry * sp, const gc_mark_stack * pms)
+{
+ int depth = sp - pms->entries - 1;
+ const gc_mark_stack *pss = pms;
+
+ while ((pss = pss->prev) != 0)
+ depth += pss->count - 1;
+ return depth_dots_string + (depth >= 10 ? 0 : 10 - depth);
+}
+#else
+# define end_phase(str) DO_NOTHING
+#endif
+void
+gs_reclaim(vm_spaces * pspaces, bool global)
+{
+ vm_spaces spaces;
+
+#define nspaces (i_vm_max + 1)
+ gs_gc_root_t space_roots[nspaces];
+ int max_trace; /* max space to trace */
+ int min_collect; /* min space to collect */
+ int 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;
+
+ /* Optionally force global GC for debugging. */
+ if (I_FORCE_GLOBAL_GC)
+ global = true;
+
+ /* Determine which spaces we are collecting. */
+ spaces = *pspaces;
+ if (space_global != space_local)
+ max_trace = i_vm_local;
+ else
+ max_trace = i_vm_global;
+ min_collect = (global ? 1 : max_trace);
+
+#define for_spaces(i, n)\
+ for ( i = 1; i <= n; ++i )
+#define for_collected_spaces(i)\
+ for ( i = min_collect; i <= max_trace; ++i )
+#define for_space_mems(i, mem)\
+ for ( mem = spaces.indexed[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_collected_chunks(mem, cp)\
+ for_collected_spaces(ispace) for_space_chunks(ispace, mem, cp)
+#define for_roots(n, mem, rp)\
+ for_spaces(ispace, n)\
+ for ( mem = spaces.indexed[ispace], rp = mem->roots; rp != 0; rp = rp->next )
+
+ /* Initialize the state. */
+ state.procs = &igc_procs;
+ state.loc.memory = spaces.named.global; /* any one will do */
+
+ state.loc.cp = 0;
+ state.spaces = spaces;
+ state.min_collect = min_collect << r_space_shift;
+ state.relocating_untraced = false;
+ state.heap = state.loc.memory->parent;
+ state.ntable = the_gs_name_table;
+
+ /* Register the allocators themselves as roots, */
+ /* so we mark and relocate the change and save lists properly. */
+
+ for_spaces(ispace, max_trace)
+ gs_register_struct_root((gs_memory_t *) spaces.indexed[ispace],
+ &space_roots[ispace],
+ (void **)&spaces.indexed[ispace],
+ "gc_top_level");
+
+ end_phase("register space roots");
+
+#ifdef DEBUG
+
+ /* Pre-validate the state. This shouldn't be necessary.... */
+
+ for_spaces(ispace, max_trace)
+ ialloc_validate_memory(spaces.indexed[ispace], &state);
+
+ end_phase("pre-validate pointers");
+
+#endif
+
+ if (I_BYPASS_GC) { /* Don't collect at all. */
+ goto no_collect;
+ }
+ /* Clear marks in spaces to be collected. */
+
+ for_collected_spaces(ispace)
+ for_space_chunks(ispace, mem, cp) {
+ gc_objects_clear_marks(cp);
+ gc_strings_set_marks(cp, false);
+ }
+
+ 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(max_trace, mem, rp) {
+ void *vptr = *rp->p;
+
+ if_debug_root('6', "[6]unmarking root", rp);
+ (*rp->ptype->unmark) (vptr, &state);
+ }
+
+ end_phase("clear root marks");
+
+ if (global)
+ gc_unmark_names(state.ntable);
+
+ /* 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(max_trace, 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 reachable objects. */
+
+ {
+ int more = 0;
+
+ /* Mark from roots. */
+
+ for_roots(max_trace, mem, rp) {
+ if_debug_root('6', "[6]marking root", rp);
+ more |= gc_trace(rp, &state, mark_stack);
+ }
+
+ end_phase("mark");
+
+ /* If this is a local GC, mark from non-local chunks. */
+
+ if (!global)
+ for_chunks(min_collect - 1, mem, cp)
+ more |= gc_trace_chunk(cp, &state, mark_stack);
+
+ /* Handle mark stack overflow. */
+
+ while (more < 0) { /* stack overflowed */
+ more = 0;
+ for_chunks(max_trace, mem, cp)
+ more |= gc_rescan_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;
+
+ if (pms->on_heap)
+ gs_free_object(state.heap, pms, "gc mark stack");
+ else
+ gs_alloc_fill(pms, gs_alloc_fill_free,
+ sizeof(*pms) + sizeof(ms_entry) * pms->count);
+ pms = prev;
+ }
+ }
+
+ end_phase("free mark stack");
+
+ if (global) {
+ gc_trace_finish(&state);
+ names_trace_finish(state.ntable, &state);
+
+ end_phase("finish trace");
+ }
+ /* Clear marks and relocation in spaces that are only being traced. */
+ /* We have to clear the marks first, because we want the */
+ /* relocation to wind up as o_untraced, not o_unmarked. */
+
+ for_chunks(min_collect - 1, mem, cp)
+ gc_objects_clear_marks(cp);
+
+ end_phase("post-clear marks");
+
+ for_chunks(min_collect - 1, mem, cp)
+ gc_clear_reloc(cp);
+
+ end_phase("clear reloc");
+
+ /* 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_collected_spaces(i)
+ gs_enable_free((gs_memory_t *) spaces.indexed[i], false);
+ }
+
+ /* Compute relocation based on marks, in the spaces */
+ /* we are going to compact. Also finalize freed objects. */
+
+ for_collected_chunks(mem, cp) {
+ gc_objects_set_reloc(cp);
+ gc_strings_set_reloc(cp);
+ }
+
+ /* Re-enable freeing. */
+ {
+ int i;
+
+ for_collected_spaces(i)
+ gs_enable_free((gs_memory_t *) spaces.indexed[i], true);
+ }
+
+ end_phase("set reloc");
+
+ /* Relocate pointers. */
+
+ state.relocating_untraced = true;
+ for_chunks(min_collect - 1, mem, cp)
+ gc_do_reloc(cp, mem, &state);
+ state.relocating_untraced = false;
+ for_collected_chunks(mem, cp)
+ gc_do_reloc(cp, mem, &state);
+
+ end_phase("relocate chunks");
+
+ for_roots(max_trace, 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;
+
+ igc_reloc_refs((ref_packed *) pref,
+ (ref_packed *) (pref + 1),
+ &state);
+ } else
+ *rp->p = (*rp->ptype->reloc) (*rp->p, &state);
+ if_debug3('6', "[6]relocated root 0x%lx: 0x%lx -> 0x%lx\n",
+ (ulong) rp, (ulong) rp->p, (ulong) * rp->p);
+ }
+
+ end_phase("relocate roots");
+
+ /* Compact data. We only do this for spaces we are collecting. */
+
+ for_collected_spaces(ispace) {
+ 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_collected_spaces(ispace)
+ 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_collected_spaces(ispace) { /* Reverse the pointers. */
+ alloc_save_t *curr;
+ alloc_save_t *prev = 0;
+ alloc_save_t *next;
+ gs_memory_status_t total;
+
+ for (curr = spaces.indexed[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.indexed[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");
+
+ no_collect:
+
+ /* Unregister the allocator roots. */
+
+ for_spaces(ispace, max_trace)
+ gs_unregister_root((gs_memory_t *) spaces.indexed[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, max_trace)
+ ialloc_validate_memory(spaces.indexed[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;
+#ifdef DEBUG
+ if (pre->o_type != &st_free)
+ debug_check_object(pre, cp, NULL);
+#endif
+ 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, pre->o_type);
+ 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(name_table * nt)
+{
+ register uint i;
+
+ names_unmark_all(nt);
+ for (i = 0; i < op_array_table_global.count; i++) {
+ name_index_t nidx = op_array_table_global.nx_table[i];
+
+ names_index_ptr(nt, nidx)->mark = 1;
+ }
+ for (i = 0; i < op_array_table_local.count; i++) {
+ name_index_t nidx = op_array_table_local.nx_table[i];
+
+ names_index_ptr(nt, 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_rescan_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]rescanning 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, pre->o_type);
+ more |= gc_trace(&root, pstate, pmstack);
+ }
+ }
+ END_OBJECTS_SCAN
+ return more;
+}
+
+/* Mark starting from all the objects in a chunk. */
+/* We assume that pstate->min_collect > avm_system, */
+/* so we don't have to trace names. */
+private int
+gc_trace_chunk(chunk_t * cp, gc_state_t * pstate, gc_mark_stack * pmstack)
+{
+ gs_gc_root_t root;
+ void *comp;
+ int more = 0;
+ int min_trace = pstate->min_collect;
+
+ root.p = &comp;
+ if_debug_chunk('6', "[6]marking from chunk", cp);
+ SCAN_CHUNK_OBJECTS(cp)
+ DO_ALL
+ {
+ 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)) { /* No packed refs need tracing. */
+ rp++;
+ } else {
+ if (r_space((ref *) rp) >= min_trace) {
+ r_clear_attrs((ref *) rp, l_mark);
+ more |= gc_trace(&root, pstate,
+ pmstack);
+ }
+ rp += packed_per_ref;
+ }
+ }
+ } else if (!o_is_unmarked(pre)) {
+ if (!o_is_untraced(pre))
+ o_set_unmarked(pre);
+ if (pre->o_type != &st_free) {
+ struct_proc_clear_marks((*proc)) =
+ pre->o_type->clear_marks;
+
+ root.ptype = ptr_struct_type;
+ comp = pre + 1;
+ if (proc != 0)
+ (*proc) (comp, size, pre->o_type);
+ 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_extend_stack(P2(gc_mark_stack *, gc_state_t *));
+private int
+gc_trace(gs_gc_root_t * rp, gc_state_t * pstate, gc_mark_stack * pmstack)
+{
+ int min_trace = pstate->min_collect;
+ 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;
+ int new = 0;
+ void *nptr = *rp->p;
+ name_table *nt = pstate->ntable;
+
+#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;
+
+ /*
+ * The following should really be an if..else, but that
+ * would force unnecessary is_refs tests.
+ */
+ if (sp->is_refs)
+ goto do_refs;
+
+ /* ---------------- Structure ---------------- */
+
+ do_struct:
+ {
+ obj_header_t *ptr = sp->ptr;
+
+ 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 */
+ stop = pms->entries + pms->count - 1;
+ sp = stop;
+ continue;
+ }
+ debug_check_object(ptr - 1, NULL, NULL);
+ ts:if_debug4('7', " [7]%smarking %s 0x%lx[%u]",
+ depth_dots(sp, pms),
+ struct_type_name_string(ptr[-1].o_type),
+ (ulong) ptr, sp->index);
+ mproc = ptr[-1].o_type->enum_ptrs;
+ /* The cast in the following statement is the one */
+ /* place we need to break 'const' to make the */
+ /* template for pointer enumeration work. */
+ if (mproc == gs_no_struct_enum_ptrs ||
+ (ptp = (*mproc)
+ (ptr, pre_obj_contents_size(ptr - 1),
+ sp->index, (const void **)&nptr,
+ ptr[-1].o_type, pstate)) == 0
+ ) {
+ if_debug0('7', " - done\n");
+ sp--;
+ continue;
+ }
+ sp->index++;
+ if_debug1('7', " = 0x%lx\n", (ulong) nptr);
+ /* Descend into nptr, whose pointer type is ptp. */
+ if (ptp == ptr_struct_type) {
+ sp[1].index = 0;
+ sp[1].is_refs = false;
+ if (sp == stop)
+ goto push;
+ if (!ptr_struct_mark(nptr, pstate))
+ goto ts;
+ new |= 1;
+ (++sp)->ptr = nptr;
+ goto do_struct;
+ } else if (ptp == ptr_ref_type) {
+ sp[1].index = 1;
+ sp[1].is_refs = true;
+ if (sp == stop)
+ goto push;
+ new |= 1;
+ (++sp)->ptr = nptr;
+ goto do_refs;
+ } else { /* We assume this is some non-pointer- */
+ /* containing type. */
+ if ((*ptp->mark) (nptr, pstate))
+ new |= 1;
+ goto ts;
+ }
+ }
+
+ /* ---------------- Refs ---------------- */
+
+ do_refs:
+ {
+ ref_packed *pptr = sp->ptr;
+ ref *rptr;
+
+ tr:if (!sp->index) {
+ --sp;
+ continue;
+ }
+ --(sp->index);
+ if_debug3('8', " [8]%smarking refs 0x%lx[%u]\n",
+ depth_dots(sp, pms), (ulong) pptr, sp->index);
+ if (r_is_packed((ref *) pptr)) {
+ if (!r_has_pmark(pptr)) {
+ r_set_pmark(pptr);
+ new |= 1;
+ if (r_packed_is_name(pptr)) {
+ name_index_t nidx = packed_name_index(pptr);
+ name *pname = names_index_ptr(nt, nidx);
+
+ mark_name(nidx, pname);
+ }
+ }
+ ++pptr;
+ goto tr;
+ }
+ if (r_has_attr((ref *) pptr, l_mark)) {
+ pptr = (ref_packed *) ((ref *) pptr + 1);
+ goto tr;
+ }
+ rptr = (ref *) pptr; /* * const beyond here */
+ r_set_attrs(rptr, l_mark);
+ new |= 1;
+ if (r_space(rptr) < min_trace) { /* Note that this always picks up all scalars. */
+ pptr = (ref_packed *) (rptr + 1);
+ goto tr;
+ }
+ sp->ptr = rptr + 1;
+ switch (r_type(rptr)) {
+ /* Struct cases */
+ case t_file:
+ nptr = rptr->value.pfile;
+ rs:sp[1].is_refs = false;
+ sp[1].index = 0;
+ if (sp == stop) {
+ ptp = ptr_struct_type;
+ break;
+ }
+ if (!ptr_struct_mark(nptr, pstate))
+ goto nr;
+ new |= 1;
+ (++sp)->ptr = nptr;
+ goto do_struct;
+ 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;
+ goto nr;
+ }
+ rrp:
+ rrc:sp[1].is_refs = true;
+ if (sp == stop)
+ break;
+ new |= 1;
+ (++sp)->ptr = nptr;
+ goto do_refs;
+ case t_mixedarray:
+ case t_shortarray:
+ nptr = (void *)rptr->value.packed; /* discard const */
+ goto rr;
+ case t_name:
+ mark_name(names_index(nt, rptr), rptr->value.pname);
+ nr:pptr = (ref_packed *) (rptr + 1);
+ goto tr;
+ case t_string:
+ if (gc_string_mark(rptr->value.bytes, r_size(rptr), true, pstate))
+ new |= 1;
+ goto nr;
+ case t_oparray:
+ nptr = (void *)rptr->value.const_refs; /* discard const */
+ sp[1].index = 1;
+ goto rrc;
+ default:
+ goto nr;
+ }
+ }
+
+ /* ---------------- Recursion ---------------- */
+
+ push:
+ if (sp == stop) { /* The current segment is full. */
+ int new_added = gc_extend_stack(pms, pstate);
+
+ if (new_added) {
+ new |= new_added;
+ continue;
+ }
+ 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;
+}
+/* Link to, attempting to allocate if necessary, */
+/* another chunk of mark stack. */
+private int
+gc_extend_stack(gc_mark_stack * pms, gc_state_t * pstate)
+{
+ if (pms->next == 0) { /* Try to allocate another segment. */
+ uint count;
+
+ for (count = ms_size_desired; count >= ms_size_min; count >>= 1) {
+ pms->next = (gc_mark_stack *)
+ gs_alloc_bytes_immovable(pstate->heap,
+ sizeof(gc_mark_stack) +
+ sizeof(ms_entry) * count,
+ "gc mark stack");
+ if (pms->next != 0)
+ break;
+ }
+ if (pms->next == 0) { /* The mark stack overflowed. */
+ ms_entry *sp = pms->entries + pms->count - 1;
+ byte *cptr = sp->ptr; /* container */
+ chunk_t *cp = gc_locate(cptr, pstate);
+ int new = 1;
+
+ 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;
+ return new;
+ }
+ gc_init_mark_stack(pms->next, count);
+ pms->next->prev = pms;
+ pms->next->on_heap = true;
+ }
+ return 0;
+}
+
+/* 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)
+{
+ name_table *nt = pstate->ntable;
+ name_index_t nidx = 0;
+ bool marked = false;
+
+ while ((nidx = names_next_valid_index(nt, nidx)) != 0) {
+ name *pname = names_index_ptr(nt, 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(names_index_ptr_sub_table(nt, 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)
+{
+ byte *pfree = (byte *) & cp->chead->free;
+
+ gc_init_reloc(cp);
+ SCAN_CHUNK_OBJECTS(cp)
+ DO_ALL
+ const struct_shared_procs_t *procs =
+ pre->o_type->shared;
+
+ if (procs != 0)
+ (*procs->clear_reloc) (pre, size);
+ o_set_untraced(pre);
+ if (!pre->o_large)
+ pre->o_back = ((byte *) pre - pfree) >> obj_back_shift;
+ 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 *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, pre->o_type, 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. */
+private void /*obj_header_t */ *
+igc_reloc_struct_ptr(const void /*obj_header_t */ *obj, gc_state_t * gcst)
+{
+ const obj_header_t *const optr = (const obj_header_t *)obj;
+ const void *robj;
+
+ if (obj == 0)
+ return print_reloc(obj, "NULL", 0);
+ 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 */
+}
+
+/* ------ 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 *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..876edc8f9
--- /dev/null
+++ b/pstoraster/igc.h
@@ -0,0 +1,102 @@
+/* Copyright (C) 1994, 1995, 1996, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Internal interfaces in Ghostscript GC */
+
+#ifndef igc_INCLUDED
+# define igc_INCLUDED
+
+#include "istruct.h"
+
+/* 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 */
+#ifndef name_table_DEFINED
+# define name_table_DEFINED
+typedef struct name_table_s name_table;
+
+#endif
+struct gc_state_s {
+ const gc_procs_with_refs_t *procs; /* must be first */
+ chunk_locator_t loc;
+ vm_spaces spaces;
+ int min_collect; /* avm_space */
+ bool relocating_untraced; /* if true, we're relocating */
+ /* pointers from untraced spaces */
+ gs_raw_memory_t *heap; /* for extending mark stack */
+ name_table *ntable; /* (implicitly referenced by names) */
+};
+
+/* Exported by igcref.c for igc.c */
+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
+
+#endif /* igc_INCLUDED */
diff --git a/pstoraster/igcref.c b/pstoraster/igcref.c
new file mode 100644
index 000000000..39cdb6345
--- /dev/null
+++ b/pstoraster/igcref.c
@@ -0,0 +1,680 @@
+/* Copyright (C) 1994, 1995, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* ref garbage collector for Ghostscript */
+#include "memory_.h"
+#include "ghost.h"
+#include "gsexit.h"
+#include "gsstruct.h" /* for gxalloc.h included by iastate.h */
+#include "iname.h"
+#include "iastate.h"
+#include "idebug.h"
+#include "igc.h"
+#include "ipacked.h"
+#include "store.h" /* for ref_assign_inline */
+
+/* Define whether to trace every step of relocating ref pointers. */
+#if 0
+# define rputc(c) dputc(c)
+#else
+# define rputc(c) DO_NOTHING
+#endif
+
+/* Forward references */
+ptr_proc_reloc(igc_reloc_ref_ptr, ref_packed);
+refs_proc_reloc(igc_reloc_refs);
+
+/*
+ * 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);
+
+ igc_reloc_refs((ref_packed *) beg, (ref_packed *) end, gcst);
+ ref_struct_clear_marks(vptr, size, pstype);
+} 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,
+ const gs_memory_struct_type_t * pstype)
+{
+ ref_packed *rp = (ref_packed *) vptr;
+ ref_packed *end = (ref_packed *) ((byte *) vptr + size);
+
+ /* 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')) {
+ dlprintf1(" [8]unmark packed 0x%lx ", (ulong) rp);
+ debug_print_ref((const ref *)rp);
+ dputs("\n");
+ }
+#endif
+ r_clear_pmark(rp);
+ rp++;
+ } else { /* full-size ref */
+#ifdef DEBUG
+ if (gs_debug_c('8')) {
+ dlprintf1(" [8]unmark ref 0x%lx ", (ulong) rp);
+ debug_print_ref((ref *) rp);
+ dputs("\n");
+ }
+#endif
+ r_clear_attrs((ref *) rp, l_mark);
+ rp += packed_per_ref;
+ if (rp >= (ref_packed *) end)
+ 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.
+ */
+
+/* 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 *//* Store the relocation here if possible. */
+ if (!ref_type_uses_size_or_null(r_type((ref *) rp))) {
+ 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 {
+#else
+ int i;
+
+ /*
+ * Note: align_packed_per_ref is typically
+ * 2 or 4 for 32-bit processors.
+ */
+#define all_marked (align_packed_per_ref * lp_mark)
+# if align_packed_per_ref == 2
+# if arch_sizeof_long == arch_sizeof_short * 2
+# undef all_marked
+# define all_marked ( ((long)lp_mark << (sizeof(short) * 8)) + lp_mark )
+# define marked (*(long *)rp & all_marked)
+# else
+# define marked ((*rp & lp_mark) + (rp[1] & lp_mark))
+# endif
+# else
+# if align_packed_per_ref == 4
+# define marked ((*rp & lp_mark) + (rp[1] & lp_mark) +\
+ (rp[2] & lp_mark) + (rp[3] & lp_mark))
+# else
+ uint marked = *rp & lp_mark;
+
+ for (i = 1; i < align_packed_per_ref; i++)
+ marked += rp[i] & lp_mark;
+# endif
+# endif
+ /*
+ * Now marked is lp_mark * the number of marked
+ * packed refs in the aligned block, except for
+ * a couple of special cases above.
+ */
+ switch (marked) {
+ case all_marked:
+ if_debug2('8',
+ " [8]packed refs 0x%lx..0x%lx are marked\n",
+ (ulong) rp,
+ (ulong) (rp + (align_packed_per_ref - 1)));
+ rp += align_packed_per_ref;
+ break;
+ default:
+ /* At least one packed ref in the block */
+ /* is marked: Keep the whole block. */
+ for (i = align_packed_per_ref; i--; rp++) {
+ r_set_pmark(rp);
+ if_debug1('8',
+ " [8]packed ref 0x%lx is marked\n",
+ (ulong) rp);
+ }
+ break;
+ case 0:
+#endif
+ if_debug2('8', " [8]%d packed ref(s) at 0x%lx are unmarked\n",
+ align_packed_per_ref, (ulong) rp);
+ {
+ uint rel = reloc + freed;
+
+ /* Change this to an integer so we can */
+ /* store the relocation here. */
+ *rp = pt_tag(pt_integer) +
+ min(rel, packed_max_value);
+ }
+ 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 {
+ if_debug1('8', " [8]ref 0x%lx is marked\n",
+ (ulong) pref);
+ /* Store the relocation here if possible. */
+ if (!ref_type_uses_size_or_null(r_type(pref))) {
+ 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 {
+ if (!ref_type_uses_size_or_null(r_type(pref)))
+ 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,
+ const gs_memory_struct_type_t * pstype, gc_state_t * gcst)
+{
+ igc_reloc_refs((ref_packed *) vptr,
+ (ref_packed *) ((char *)vptr + size),
+ gcst);
+}
+/* Relocate the contents of a block of refs. */
+/* If gcst->relocating_untraced is true, we are relocating pointers from an */
+/* untraced space, so relocate all refs, not just marked ones. */
+void
+igc_reloc_refs(ref_packed * from, ref_packed * to, gc_state_t * gcst)
+{
+ int min_trace = gcst->min_collect;
+ ref_packed *rp = from;
+ bool do_all = gcst->relocating_untraced;
+
+ 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) || do_all) &&
+ r_space(pref) >= min_trace
+ )
+ switch (r_type(pref)) {
+ /* Struct cases */
+ case t_file:
+ RELOC_VAR(pref->value.pfile);
+ break;
+ case t_device:
+ RELOC_VAR(pref->value.pdevice);
+ break;
+ case t_fontID:
+ case t_struct:
+ case t_astruct:
+ RELOC_VAR(pref->value.pstruct);
+ break;
+ /* Non-trivial non-struct cases */
+ case t_dictionary:
+ rputc('d');
+ pref->value.pdict =
+ (dict *) igc_reloc_ref_ptr((ref_packed *) pref->value.pdict, gcst);
+ break;
+ case t_array:
+ {
+ uint size = r_size(pref);
+
+ if (size != 0) { /* value.refs might be NULL *//*
+ * If the array is large, we allocated it in its
+ * own object (at least originally -- this might
+ * be a pointer to a subarray.) In this case,
+ * we know it is the only object in its
+ * containing st_refs object, so we know that
+ * the mark containing the relocation appears
+ * just after it.
+ */
+ if (size < max_size_st_refs / sizeof(ref)) {
+ rputc('a');
+ pref->value.refs =
+ (ref *) igc_reloc_ref_ptr(
+ (ref_packed *) pref->value.refs, gcst);
+ } else {
+ rputc('A');
+ /*
+ * See the t_shortarray case below for why we
+ * decrement size.
+ */
+ --size;
+ pref->value.refs =
+ (ref *) igc_reloc_ref_ptr(
+ (ref_packed *) (pref->value.refs + size),
+ gcst) - size;
+ }
+ }
+ }
+ break;
+ case t_mixedarray:
+ if (r_size(pref) != 0) { /* value.refs might be NULL */
+ rputc('m');
+ pref->value.packed =
+ igc_reloc_ref_ptr(pref->value.packed, gcst);
+ }
+ break;
+ case t_shortarray:
+ {
+ uint size = r_size(pref);
+
+ /*
+ * Since we know that igc_reloc_ref_ptr works by
+ * scanning forward, and we know that all the
+ * elements of this array itself are marked, we can
+ * save some scanning time by relocating the pointer
+ * to the end of the array rather than the
+ * beginning.
+ */
+ if (size != 0) { /* value.refs might be NULL */
+ rputc('s');
+ /*
+ * igc_reloc_ref_ptr has to be able to determine
+ * whether the pointer points into a space that
+ * isn't being collected. It does this by
+ * checking whether the referent of the pointer
+ * is marked. For this reason, we have to pass
+ * a pointer to the last real element of the
+ * array, rather than just beyond it.
+ */
+ --size;
+ pref->value.packed =
+ igc_reloc_ref_ptr(pref->value.packed + size,
+ gcst) - size;
+ }
+ }
+ break;
+ case t_name:
+ {
+ void *psub = name_ref_sub_table(pref);
+ void *rsub = RELOC_OBJ(psub); /* gcst implicit */
+
+ 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);
+
+ RELOC_STRING_VAR(str);
+ pref->value.bytes = str.data;
+ }
+ break;
+ case t_oparray:
+ rputc('o');
+ pref->value.const_refs =
+ (const ref *)igc_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 *
+igc_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;
+
+ /*
+ * Iff this pointer points into a space that wasn't traced,
+ * the referent won't be marked. In this case, we shouldn't
+ * do any relocation. Check for this first.
+ */
+ if (r_is_packed(rp)) {
+ if (!r_has_pmark(rp))
+ return (ref_packed *) rp;
+ } else {
+ if (!r_has_attr((const ref *)rp, l_mark))
+ return (ref_packed *) rp;
+ }
+ for (;;) {
+ if (r_is_packed(rp)) { /*
+ * Normally, an unmarked packed ref will be an
+ * integer whose value is the amount of relocation.
+ * However, the relocation value might have been
+ * too large to fit. If this is the case, for
+ * each such unmarked packed ref we pass over,
+ * we have to decrement the final relocation.
+ */
+ rputc((*rp & lp_mark ? '1' : '0'));
+ if (!(*rp & lp_mark)) {
+ if (*rp != pt_tag(pt_integer) + packed_max_value) { /* This is a stored relocation value. */
+ rputc('\n');
+ return print_reloc(prp, "ref",
+ (ref_packed *)
+ ((const char *)prp -
+ (*rp & packed_value_mask) + dec));
+ }
+ /*
+ * We know this is the first of an aligned block
+ * of packed refs. Skip over the entire block,
+ * decrementing the final relocation.
+ */
+ dec += sizeof(ref_packed) * align_packed_per_ref;
+ rp += align_packed_per_ref;
+ } else
+ rp++;
+ continue;
+ }
+ if (!ref_type_uses_size_or_null(r_type((const ref *)rp))) { /* reloc is in r_size */
+ rputc('\n');
+ 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)));
+ }
+ rputc('u');
+ 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..1847ab51c
--- /dev/null
+++ b/pstoraster/igcstr.c
@@ -0,0 +1,393 @@
+/* Copyright (C) 1994, 1995, 1996, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* String GC routines for Ghostscript */
+#include "memory_.h"
+#include "ghost.h"
+#include "gsmdebug.h"
+#include "gsstruct.h"
+#include "iastate.h"
+#include "igcstr.h"
+
+/* Forward references */
+private bool gc_mark_string(P4(const byte *, uint, bool, const chunk_t *));
+
+/* (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, 0, cp->smark_size);
+ if (mark)
+ gc_mark_string(cp->sbase, cp->climit - cp->sbase, true, cp);
+ }
+}
+
+/* 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
+
+/* (Un)mark a string in a known chunk. Return true iff any new marks. */
+private bool
+gc_mark_string(const byte * ptr, uint size, bool set, const chunk_t * cp)
+{
+ uint offset = ptr - cp->sbase;
+ bword *bp = (bword *) (cp->smark + ((offset & -bword_bits) >> 3));
+ uint bn = offset & (bword_bits - 1);
+ bword m = bword_1s << bn;
+ uint left = size;
+ bword marks = 0;
+
+ 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;
+ }
+ }
+ return marks != 0;
+}
+
+/* Mark a string. Return true if any new marks. */
+bool
+gc_string_mark(const byte * ptr, uint size, bool set, gc_state_t * gcst)
+{
+ const chunk_t *cp;
+ bool marks;
+
+ 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')) {
+ dlprintf2("[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
+ marks = gc_mark_string(ptr, size, set, cp);
+#ifdef DEBUG
+ if (gs_debug_c('5')) {
+ dlprintf4("[5]%s%smarked 0x%lx[%u]",
+ (marks ? "" : "already "), (set ? "" : "un"),
+ (ulong) ptr, size);
+ dprintstr();
+ dputc('\n');
+ }
+#endif
+ return marks;
+}
+
+/* Clear the relocation for strings. */
+/* This requires setting the marks. */
+void
+gc_strings_clear_reloc(chunk_t * cp)
+{
+ if (cp->sreloc != 0) {
+ gc_strings_set_marks(cp, true);
+ if_debug1('6', "[6]clearing string reloc 0x%lx\n",
+ (ulong) cp->sreloc);
+ gc_strings_set_reloc(cp);
+ }
+}
+
+/* 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
+igc_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
+igc_reloc_const_string(gs_const_string * sptr, gc_state_t * gcst)
+{ /* We assume the representation of byte * and const byte * is */
+ /* the same.... */
+ igc_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;
+
+ dlprintf1("[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..3d980e720
--- /dev/null
+++ b/pstoraster/igcstr.h
@@ -0,0 +1,41 @@
+/* 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 supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Internal interface to string garbage collector */
+
+#ifndef igcstr_INCLUDED
+# define igcstr_INCLUDED
+
+/* 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 *));
+
+#endif /* igcstr_INCLUDED */
diff --git a/pstoraster/igstate.h b/pstoraster/igstate.h
new file mode 100644
index 000000000..18675c4a6
--- /dev/null
+++ b/pstoraster/igstate.h
@@ -0,0 +1,181 @@
+/* Copyright (C) 1989, 1995, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Ghostscript interpreter graphics state definition */
+
+#ifndef igstate_INCLUDED
+# define igstate_INCLUDED
+
+#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)
+
+/* DeviceN names and tint transform */
+typedef struct ref_device_n_params_s {
+ ref layer_names, tint_transform;
+} ref_device_n_params;
+
+/* 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_device_n_params device_n;
+ 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 DeviceN, 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 null. */
+ ref pagedevice; /* page device (dictionary|null) */
+} int_gstate;
+
+#define clear_pagedevice(pigs) make_null(&(pigs)->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 );\
+ }
+
+/* Create the gstate for a new context. */
+/* We export this so that fork can use it. */
+gs_state *int_gstate_alloc(P1(gs_ref_memory_t * mem));
+
+/* 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)
+
+#endif /* igstate_INCLUDED */
diff --git a/pstoraster/iht.h b/pstoraster/iht.h
new file mode 100644
index 000000000..56eb8d41b
--- /dev/null
+++ b/pstoraster/iht.h
@@ -0,0 +1,37 @@
+/* Copyright (C) 1997 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Procedures exported by zht.c for zht1.c and zht2.c */
+
+#ifndef iht_INCLUDED
+# define iht_INCLUDED
+
+int zscreen_params(P2(os_ptr op, gs_screen_halftone * phs));
+
+int zscreen_enum_init(P7(os_ptr op, const gx_ht_order * porder,
+ gs_screen_halftone * phs, ref * pproc, int npop,
+ int (*finish_proc) (P1(os_ptr)), gs_memory_t * mem));
+
+#endif /* iht_INCLUDED */
diff --git a/pstoraster/iimage.h b/pstoraster/iimage.h
new file mode 100644
index 000000000..f4fed3ee1
--- /dev/null
+++ b/pstoraster/iimage.h
@@ -0,0 +1,46 @@
+/* Copyright (C) 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Requires gscspace.h, gxiparam.h */
+
+#ifndef iimage_INCLUDED
+# define iimage_INCLUDED
+
+/* These procedures are exported by zimage.c for other modules. */
+
+/* Exported for zcolor1.c and zdpnext.c */
+int zimage_opaque_setup(P5(os_ptr op, bool multi, gs_image_alpha_t alpha,
+ const gs_color_space * pcs, int npop));
+
+/* Exported for zimage2.c */
+int zimage_setup(P4(const gs_pixel_image_t * pim, const ref * sources,
+ bool uses_color, int npop));
+
+/* Exported for zcolor3.c */
+int zimage_data_setup(P4(const gs_pixel_image_t * pim,
+ gx_image_enum_common_t * pie,
+ const ref * sources, int npop));
+
+#endif /* iimage_INCLUDED */
diff --git a/pstoraster/iimage2.h b/pstoraster/iimage2.h
new file mode 100644
index 000000000..c9554dbe4
--- /dev/null
+++ b/pstoraster/iimage2.h
@@ -0,0 +1,50 @@
+/* Copyright (C) 1997 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Requires gsiparam.h */
+
+#ifndef iimage2_INCLUDED
+# define iimage2_INCLUDED
+
+/* These procedures are exported by zimage2.c for other modules. */
+
+/*
+ * Define a structure for image parameters other than those defined
+ * in the gs_*image*_t structure.
+ */
+typedef struct image_params_s {
+ bool MultipleDataSources;
+ ref DataSource[gs_image_max_components];
+ const float *pDecode;
+} image_params;
+
+/* Extract and check parameters for an image. */
+int data_image_params(P6(const ref * op, gs_data_image_t * pim,
+ image_params * pip, bool require_DataSource,
+ int num_components, int max_bits_per_component));
+int pixel_image_params(P4(const ref * op, gs_pixel_image_t * pim,
+ image_params * pip, int max_bits_per_component));
+
+#endif /* iimage2_INCLUDED */
diff --git a/pstoraster/iinit.c b/pstoraster/iinit.c
new file mode 100644
index 000000000..d2c919d35
--- /dev/null
+++ b/pstoraster/iinit.c
@@ -0,0 +1,511 @@
+/* Copyright (C) 1989, 1995, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Initialize internally known objects for Ghostscript interpreter */
+#include "string_.h"
+#include "ghost.h"
+#include "gscdefs.h"
+#include "gsexit.h"
+#include "gsstruct.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. */
+/*
+ * Define the (initial) sizes of the various system dictionaries. 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. Note that these sizes must be large enough to get us through
+ * initialization, since we start up in Level 1 mode where dictionaries
+ * don't expand automatically.
+ */
+/* The size of systemdict can be set in the makefile. */
+#ifndef SYSTEMDICT_SIZE
+# define SYSTEMDICT_SIZE 601
+#endif
+#ifndef SYSTEMDICT_LEVEL2_SIZE
+# define SYSTEMDICT_LEVEL2_SIZE 941
+#endif
+/* The size of level2dict, if applicable, can be set in the makefile. */
+#ifndef LEVEL2DICT_SIZE
+# define LEVEL2DICT_SIZE 233
+#endif
+/* Ditto the size of ll3dict. */
+#ifndef LL3DICT_SIZE
+# define LL3DICT_SIZE 43
+#endif
+/* Ditto the size of filterdict. */
+#ifndef FILTERDICT_SIZE
+# define FILTERDICT_SIZE 43
+#endif
+/* Define an arbitrary size for the operator procedure tables. */
+#ifndef OP_ARRAY_TABLE_SIZE
+# define OP_ARRAY_TABLE_SIZE 180
+#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)
+
+/* Define the list of error names. */
+const char *const gs_error_names[] =
+{
+ ERROR_NAMES
+};
+
+/* The operator tables */
+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 */
+
+/* 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
+ },
+ {
+ "ll3dict", LL3DICT_SIZE, false
+ },
+ {
+ "globaldict", 0, false
+ },
+ {
+ "userdict", 0, true
+ },
+ {
+ "filterdict", FILTERDICT_SIZE, false
+ },
+#endif
+};
+/* systemdict and globaldict are magically inserted at the bottom */
+const char *const 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 imain.c. */
+/* This is very slow, but we only call it a couple of times. */
+bool
+gs_have_level2(void)
+{
+ const op_def *const *tptr;
+
+ 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, ref idicts[])
+{
+ int i;
+
+ /* systemdict was created specially. */
+ if (!strcmp(iname, "systemdict"))
+ return systemdict;
+ for (i = 0; i < countof(initial_dictionaries); i++) {
+ const char *dname = initial_dictionaries[i].name;
+ const int dsize = initial_dictionaries[i].size;
+
+ if (!strcmp(iname, dname)) {
+ ref *dref = &idicts[i];
+
+ if (r_has_type(dref, t_null)) {
+ gs_ref_memory_t *mem =
+ (initial_dictionaries[i].local ?
+ iimemory_local : iimemory_global);
+ int code = dict_alloc(mem, dsize, dref);
+
+ if (code < 0)
+ return 0; /* disaster */
+ }
+ return dref;
+ }
+ }
+
+ /*
+ * Name mentioned in some op_def, but not in initial_dictionaries.
+ * Punt.
+ */
+ return 0;
+}
+
+/* Initialize objects other than operators. In particular, */
+/* initialize the dictionaries that hold operator definitions. */
+void
+obj_init(void)
+{
+ bool level2 = gs_have_level2();
+
+ /* Initialize the language level. */
+ make_int(&ref_language_level, 1);
+
+ /*
+ * Create systemdict. The context machinery requires that
+ * we do this before initializing the interpreter.
+ */
+ dict_alloc(iimemory_global,
+ (level2 ? SYSTEMDICT_LEVEL2_SIZE : SYSTEMDICT_SIZE),
+ systemdict);
+
+ /* Initialize the interpreter. */
+ gs_interp_init();
+
+ {
+#define icount countof(initial_dictionaries)
+ ref idicts[icount];
+ int i;
+ const op_def *const *tptr;
+
+ min_dstack_size = MIN_DSTACK_SIZE;
+
+ refset_null(idicts, icount);
+
+ /* Put systemdict on the dictionary stack. */
+ if (level2) {
+ dsp += 2;
+ /*
+ * For the moment, let globaldict be an alias for systemdict.
+ */
+ dsp[-1] = *systemdict;
+ min_dstack_size++;
+ } else {
+ ++dsp;
+ }
+ *dsp = *systemdict;
+
+ /* 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, idicts);
+ }
+
+ /* Set up the initial dstack. */
+ for (i = 0; i < countof(initial_dstack); i++) {
+ const char *dname = initial_dstack[i];
+
+ ++dsp;
+ if (!strcmp(dname, "userdict"))
+ dstack_userdict_index = dsp - dsbot;
+ ref_assign(dsp, make_initial_dict(dname, idicts));
+ }
+
+ /* Enter names of referenced initial dictionaries into systemdict. */
+ initial_enter_name("systemdict", 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)
+{
+ const op_def *const *tptr;
+
+ /* 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;
+ const op_def *const *tptr;
+ const op_def *def;
+ const char *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(const op_def *),
+ "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, NULL, (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, NULL,
+ (void **)&op_array_table_global.root_p,
+ "op_array_table(global)");
+ gs_register_struct_root(imemory, NULL,
+ (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, NULL,
+ (void **)&op_array_table_local.root_p,
+ "op_array_table(local)");
+ gs_register_struct_root(imemory, NULL,
+ (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..a7b90c54f
--- /dev/null
+++ b/pstoraster/ilevel.h
@@ -0,0 +1,39 @@
+/* Copyright (C) 1992, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Interpreter language level interface */
+
+#ifndef ilevel_INCLUDED
+# define ilevel_INCLUDED
+
+/* The current interpreter language level */
+extern ref ref_language_level;
+
+#define LANGUAGE_LEVEL ((int)ref_language_level.value.intval)
+#define LL2_ENABLED (LANGUAGE_LEVEL >= 2)
+#define LL3_ENABLED (LANGUAGE_LEVEL >= 3)
+#define level2_enabled LL2_ENABLED /* backward compatibility */
+
+#endif /* ilevel_INCLUDED */
diff --git a/pstoraster/ilocate.c b/pstoraster/ilocate.c
new file mode 100644
index 000000000..8ae5ddaf7
--- /dev/null
+++ b/pstoraster/ilocate.c
@@ -0,0 +1,439 @@
+/* Copyright (C) 1995, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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 "idict.h"
+#include "igc.h" /* for gc_state_t */
+#include "igcstr.h" /* for prototype */
+#include "iname.h"
+#include "ipacked.h"
+#include "isstate.h"
+#include "iutil.h" /* for packed_get */
+#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 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;
+ struct sm_ {
+ chunk_t cc;
+ uint rsize;
+ ref rlast;
+ } save[countof(dmem->spaces.indexed)];
+
+ 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 < countof(save); 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) {
+ save[i].cc = *pcc;
+ *pcc = mem->cc;
+ }
+ if (rcur != 0) {
+ save[i].rsize = rcur[-1].o_size;
+ rcur[-1].o_size = mem->cc.rtop - (byte *) rcur;
+ /* Create the final ref, reserved for the GC. */
+ save[i].rlast = ((ref *) mem->cc.rtop)[-1];
+ make_mark((ref *) mem->cc.rtop - 1);
+ }
+ }
+
+ /* Validate memory. */
+
+ for (i = 0; i < countof(save); i++)
+ if (dmem->spaces.indexed[i] != 0)
+ ialloc_validate_memory(dmem->spaces.indexed[i], &state);
+
+ /* Undo temporary changes. */
+
+ for (i = 0; i < countof(save); 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 = save[i].rsize;
+ ((ref *) mem->cc.rtop)[-1] = save[i].rlast;
+ }
+ if (pcc != 0)
+ *pcc = save[i].cc;
+ }
+}
+void
+ialloc_validate_memory(const gs_ref_memory_t * mem, gc_state_t * gcst)
+{
+ const gs_ref_memory_t *smem;
+ int level;
+
+ for (smem = mem, level = 0; smem != 0;
+ smem = &smem->saved->state, --level
+ ) {
+ const chunk_t *cp;
+ int i;
+
+ if_debug3('6', "[6]validating memory 0x%lx, space %d, level %d\n",
+ (ulong) mem, mem->space, level);
+ /* Validate chunks. */
+ for (cp = smem->cfirst; cp != 0; cp = cp->cnext)
+ ialloc_validate_chunk(cp, gcst);
+ /* Validate freelists. */
+ for (i = 0; i < num_freelists; ++i) {
+ uint free_size = i << log2_obj_align_mod;
+ const obj_header_t *pfree;
+
+ for (pfree = mem->freelists[i]; pfree != 0;
+ pfree = *(const obj_header_t * const *)pfree
+ ) {
+ uint size = pfree[-1].o_size;
+
+ if (pfree[-1].o_type != &st_free) {
+ lprintf3("Non-free object 0x%lx(%u) on freelist %i!\n",
+ (ulong) pfree, size, i);
+ break;
+ }
+ if (size < free_size - obj_align_mask || size > free_size) {
+ lprintf3("Object 0x%lx(%u) size wrong on freelist %i!\n",
+ (ulong) pfree, size, i);
+ break;
+ }
+ }
+ }
+ };
+}
+
+/* Check the validity of an object's size. */
+inline private bool
+object_size_valid(const obj_header_t * pre, uint size, const chunk_t * cp)
+{
+ return (pre->o_large ? (const byte *)pre == cp->cbase :
+ size <= cp->ctop - (const byte *)(pre + 1));
+}
+
+/* Validate all the objects in a chunk. */
+private void ialloc_validate_ref(P2(const ref *, gc_state_t *));
+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
+ if (pre->o_type == &st_free) {
+ if (!object_size_valid(pre, size, cp))
+ lprintf3("Bad free object 0x%lx(%lu), in chunk 0x%lx!\n",
+ (ulong) (pre + 1), (ulong) size, (ulong) cp);
+ } else
+ 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)) {
+ ref unpacked;
+
+ packed_get(rp, &unpacked);
+ ialloc_validate_ref(&unpacked, gcst);
+ rp++;
+ } else {
+ ialloc_validate_ref((const ref *)rp, gcst);
+ rp += packed_per_ref;
+ }
+ } else {
+ struct_proc_enum_ptrs((*proc)) = pre->o_type->enum_ptrs;
+ uint index = 0;
+ const void *ptr;
+ gs_ptr_type_t ptype;
+
+ if (proc != gs_no_struct_enum_ptrs)
+ for (; (ptype = (*proc) (pre + 1, size, index, &ptr, pre->o_type, NULL)) != 0; ++index)
+ if (ptr == 0)
+ DO_NOTHING;
+ else if (ptype == ptr_struct_type)
+ ialloc_validate_object(ptr, NULL, gcst);
+ else if (ptype == ptr_ref_type)
+ ialloc_validate_ref(ptr, gcst);
+ }
+ END_OBJECTS_SCAN
+}
+/* Validate a ref. */
+private void
+ialloc_validate_ref(const ref * pref, gc_state_t * gcst)
+{
+ const void *optr;
+ const ref *rptr;
+ const char *tname;
+ uint size;
+
+ if (!gs_debug_c('?'))
+ return; /* no check */
+ if (r_space(pref) == avm_foreign)
+ return;
+ 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;
+ 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;
+ case t_array:
+ if (r_size(pref) == 0)
+ break;
+ rptr = pref->value.refs;
+ size = r_size(pref);
+ tname = "array";
+cka: if (!gc_locate(rptr, gcst)) {
+ lprintf3("At 0x%lx, %s 0x%lx not in any chunk\n",
+ (ulong) pref, tname, (ulong) rptr);
+ break;
+ } {
+ uint i;
+
+ for (i = 0; i < size; ++i) {
+ const ref *elt = rptr + i;
+
+ if (r_is_packed(elt))
+ lprintf5("At 0x%lx, %s 0x%lx[%u] element %u is not a ref\n",
+ (ulong) pref, tname, (ulong) rptr, size, i);
+ }
+ }
+ break;
+ case t_shortarray:
+ case t_mixedarray:
+ if (r_size(pref) == 0)
+ break;
+ optr = pref->value.packed;
+ if (!gc_locate(optr, gcst))
+ lprintf2("At 0x%lx, packed array 0x%lx not in any chunk\n",
+ (ulong) pref, (ulong) optr);
+ break;
+ case t_dictionary:
+ {
+ const dict *pdict = pref->value.pdict;
+
+ if (!r_has_type(&pdict->values, t_array) ||
+ !r_is_array(&pdict->keys) ||
+ !r_has_type(&pdict->count, t_integer) ||
+ !r_has_type(&pdict->maxlength, t_integer)
+ )
+ lprintf2("At 0x%lx, invalid dict 0x%lx\n",
+ (ulong) pref, (ulong) pdict);
+ rptr = (const ref *)pdict;
+ }
+ size = sizeof(dict) / sizeof(ref);
+ tname = "dict";
+ goto cka;
+ }
+}
+
+/* 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) ptr);
+ return; /*gs_abort(); */
+ }
+ }
+ if (otype == &st_free) {
+ lprintf3("Reference to free object 0x%lx(%lu), in chunk 0x%lx!\n",
+ (ulong) ptr, (ulong) size, (ulong) cp);
+ gs_abort();
+ }
+ if ((cp != 0 && !object_size_valid(pre, size, cp)) ||
+ 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) ptr, (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..642715643
--- /dev/null
+++ b/pstoraster/imain.c
@@ -0,0 +1,664 @@
+/* Copyright (C) 1989, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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 *
+gs_main_instance_default(void)
+{ /* Determine whether the instance has been initialized. */
+ if (the_gs_main_instance.memory_chunk_size == 0)
+ the_gs_main_instance = gs_main_instance_init_values;
+ 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;
+
+/* Define the interpreter's name table. We'll move it somewhere better */
+/* eventually.... */
+name_table *the_gs_name_table;
+
+/* ------ Imported data ------ */
+
+/* Configuration information imported from gconfig.c and iinit.c. */
+extern const char *gs_init_file;
+extern const byte gs_init_string[];
+extern const uint gs_init_string_sizeof;
+extern const ref gs_init_file_array[];
+extern const ref gs_emulator_name_array[];
+
+/* ------ Forward references ------ */
+
+private int gs_run_init_file(P3(gs_main_instance *, int *, ref *));
+private void print_resource_usage(P3(const gs_main_instance *,
+ gs_dual_memory_t *,
+ const char *));
+
+/* ------ Initialization ------ */
+
+/* A handy way to declare and execute an initialization procedure: */
+#define call_init(proc)\
+BEGIN extern void proc(P0()); proc(); END
+
+/* 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)
+{
+ gs_memory_t *heap;
+
+ /* Set our versions of stdin/out/err. */
+ gs_stdin = minst->fstdin = in;
+ gs_stdout = minst->fstdout = out;
+ gs_stderr = minst->fstderr = 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();
+ gp_get_usertime(minst->base_time);
+ /* Initialize the imager. */
+ heap = gs_lib_init0(gs_stdout);
+ minst->heap = heap;
+ /* Initialize the file search paths. */
+ make_array(&minst->lib_path.container, avm_foreign, max_lib_paths,
+ (ref *) gs_alloc_byte_array(heap, 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_raw_memory_t *) & gs_memory_default,
+ minst->memory_chunk_size,
+ gs_have_level2());
+ gs_lib_init1((gs_memory_t *) imemory_system);
+ alloc_save_init(idmemory);
+ }
+ {
+ gs_memory_t *mem = imemory_system;
+ name_table *nt = names_init(minst->name_table_size, mem);
+
+ if (nt == 0) {
+ puts("name_init failed");
+ gs_exit(1);
+ }
+ the_gs_name_table = nt;
+ gs_register_struct_root(mem, NULL, (void **)&the_gs_name_table,
+ "the_gs_name_table");
+ }
+ call_init(obj_init); /* requires name_init */
+ minst->init_done = 1;
+ }
+}
+
+/* Initialization to be done before running any files. */
+private void
+init2_make_string_array(const ref * srefs, const char *aname)
+{
+ const ref *ifp = srefs;
+ ref ifa;
+
+ for (; ifp->value.bytes != 0; ifp++);
+ make_tasv(&ifa, t_array, a_readonly | avm_foreign,
+ ifp - srefs, const_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 */
+
+ /* 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;
+ }
+ if (gs_debug_c(':'))
+ print_resource_usage(minst, &gs_imemory, "Start");
+}
+
+/* ------ 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 = ".runstringbegin";
+ 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 */
+
+ if (gs_debug_c(':'))
+ print_resource_usage(minst, &gs_imemory, "Final");
+ /* 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 ------ */
+
+/* Print resource usage statistics. */
+private void
+print_resource_usage(const gs_main_instance * minst, gs_dual_memory_t * dmem,
+ const char *msg)
+{
+ ulong allocated = 0, used = 0;
+ long utime[2];
+
+ gp_get_usertime(utime);
+ {
+ int i;
+
+ for (i = 0; i < countof(dmem->spaces.indexed); ++i) {
+ gs_ref_memory_t *mem = dmem->spaces.indexed[i];
+
+ if (mem != 0 && (i == 0 || mem != dmem->spaces.indexed[i - 1])) {
+ gs_memory_status_t status;
+
+ gs_memory_status((gs_memory_t *) mem, &status);
+ allocated += status.allocated;
+ used += status.used;
+ }
+ }
+ }
+ dprintf4("%% %s time = %g, memory allocated = %lu, used = %lu\n",
+ msg, utime[0] - minst->base_time[0] +
+ (utime[1] - minst->base_time[1]) / 1000000000.0,
+ allocated, used);
+}
+
+/* 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..fb1eac51d
--- /dev/null
+++ b/pstoraster/imain.h
@@ -0,0 +1,276 @@
+/* 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 supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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/imainarg.c b/pstoraster/imainarg.c
new file mode 100644
index 000000000..e8069d3a1
--- /dev/null
+++ b/pstoraster/imainarg.c
@@ -0,0 +1,849 @@
+/* Copyright (C) 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Command line parsing and dispatching */
+#include "ctype_.h"
+#include "memory_.h"
+#include "string_.h"
+#include "ghost.h"
+#include "gp.h"
+#include "gsargs.h"
+#include "gscdefs.h"
+#include "gsmalloc.h" /* for gs_malloc_limit */
+#include "gsmdebug.h"
+#include "gxdevice.h"
+#include "gxdevmem.h"
+#include "gsdevice.h"
+#include "stream.h"
+#include "errors.h"
+#include "estack.h"
+#include "ialloc.h"
+#include "strimpl.h" /* for sfilter.h */
+#include "sfilter.h" /* for iscan.h */
+#include "ostack.h" /* must precede iscan.h */
+#include "iscan.h"
+#include "imain.h"
+#include "imainarg.h"
+#include "iminst.h"
+#include "iname.h"
+#include "store.h"
+#include "files.h" /* requires stream.h */
+#include "interp.h"
+#include "iutil.h"
+#include "ivmspace.h"
+
+/* Import operator procedures */
+extern int zflush(P1(os_ptr));
+extern int zflushpage(P1(os_ptr));
+
+#ifndef GS_LIB
+# define GS_LIB "GS_LIB"
+#endif
+
+#ifndef GS_OPTIONS
+# define GS_OPTIONS "GS_OPTIONS"
+#endif
+
+#ifndef GS_MAX_LIB_DIRS
+# define GS_MAX_LIB_DIRS 25
+#endif
+
+#ifndef GS_BUG_MAILBOX
+# define GS_BUG_MAILBOX "ghost@aladdin.com"
+#endif
+
+#define MAX_BUFFERED_SIZE 1024
+
+/* Note: sscanf incorrectly defines its first argument as char * */
+/* rather than const char *. This accounts for the ugly casts below. */
+
+/* Redefine puts to use fprintf, so it will work even without stdio. */
+#undef puts
+private void
+fpputs(const char *str)
+{
+ fprintf(stdout, "%s\n", str);
+}
+#define puts(str) fpputs(str)
+
+/* Other imported data */
+extern const char *const gs_doc_directory;
+extern const char *const gs_lib_default_path;
+extern const ref gs_emulator_name_array[];
+
+/* Forward references */
+#define runInit 1
+#define runFlush 2
+#define runBuffer 4
+private int swproc(P3(gs_main_instance *, const char *, arg_list *));
+private void argproc(P2(gs_main_instance *, const char *));
+private void run_buffered(P2(gs_main_instance *, const char *));
+private int esc_strlen(P1(const char *));
+private void esc_strcat(P2(char *, const char *));
+private void runarg(P5(gs_main_instance *, const char *, const char *, const char *, int));
+private void run_string(P3(gs_main_instance *, const char *, int));
+private void run_finish(P3(int, int, ref *));
+
+/* Forward references for help printout */
+private void print_help(P1(gs_main_instance *));
+private void print_revision(P0());
+private void print_version(P0());
+private void print_usage(P0());
+private void print_devices(P0());
+private void print_emulators(P0());
+private void print_paths(P1(gs_main_instance *));
+private void print_help_trailer(P0());
+
+/* ------ Main program ------ */
+
+/* Process the command line with a given instance. */
+private FILE *
+gs_main_arg_fopen(const char *fname, void *vminst)
+{
+ gs_main_set_lib_paths((gs_main_instance *) vminst);
+ return lib_fopen(fname);
+}
+#define arg_heap_copy(str) arg_copy(str, &gs_memory_default)
+int
+gs_main_init_with_args(gs_main_instance * minst, int argc, char *argv[])
+{
+ const char *arg;
+ arg_list args;
+ FILE *stdfiles[3];
+
+ gs_get_real_stdio(stdfiles);
+ arg_init(&args, (const char **)argv, argc,
+ gs_main_arg_fopen, (void *)minst);
+ gs_main_init0(minst, stdfiles[0], stdfiles[1], stdfiles[2],
+ GS_MAX_LIB_DIRS);
+ {
+ int len = 0;
+ int code = gp_getenv(GS_LIB, (char *)0, &len);
+
+ if (code < 0) { /* key present, value doesn't fit */
+ char *path = (char *)gs_alloc_bytes(minst->heap, len, "GS_LIB");
+
+ gp_getenv(GS_LIB, path, &len); /* can't fail */
+ minst->lib_path.env = path;
+ }
+ }
+ minst->lib_path.final = gs_lib_default_path;
+ gs_main_set_lib_paths(minst);
+ /* Prescan the command line for --help and --version. */
+ {
+ int i;
+ bool helping = false;
+
+ for (i = 1; i < argc; ++i)
+ if (!strcmp(argv[i], "--")) { /* A PostScript program will be interpreting all the */
+ /* remaining switches, so stop scanning. */
+ helping = false;
+ break;
+ } else if (!strcmp(argv[i], "--help")) {
+ print_help(minst);
+ helping = true;
+ } else if (!strcmp(argv[i], "--version")) {
+ print_version();
+ puts(""); /* \n */
+ helping = true;
+ }
+ if (helping)
+ gs_exit(gs_exit_INFO);
+ }
+ /* Execute files named in the command line, */
+ /* processing options along the way. */
+ /* Wait until the first file name (or the end */
+ /* of the line) to finish initialization. */
+ minst->run_start = true;
+
+ {
+ int len = 0;
+ int code = gp_getenv(GS_OPTIONS, (char *)0, &len);
+
+ if (code < 0) { /* key present, value doesn't fit */
+ char *opts =
+ (char *)gs_alloc_bytes(minst->heap, len, "GS_OPTIONS");
+
+ gp_getenv(GS_OPTIONS, opts, &len); /* can't fail */
+ arg_push_memory_string(&args, opts, minst->heap);
+ }
+ }
+ while ((arg = arg_next(&args)) != 0) {
+ switch (*arg) {
+ case '-':
+ if (swproc(minst, arg, &args) < 0)
+ fprintf(stdout,
+ "Unknown switch %s - ignoring\n", arg);
+ break;
+ default:
+ argproc(minst, arg);
+ }
+ }
+
+ gs_main_init2(minst);
+
+ return 0;
+}
+
+/* Run the 'start' procedure (after processing the command line). */
+/* Note that this procedure exits rather than returning. */
+void
+gs_main_run_start(gs_main_instance * minst)
+{
+ run_string(minst, "systemdict /start get exec", runFlush);
+}
+
+/* Process switches */
+private int
+swproc(gs_main_instance * minst, const char *arg, arg_list * pal)
+{
+ char sw = arg[1];
+ ref vtrue;
+
+ make_true(&vtrue);
+ arg += 2; /* skip - and letter */
+ switch (sw) {
+ default:
+ return -1;
+ case 0: /* read stdin as a file */
+ minst->run_start = false; /* don't run 'start' */
+ /* Set NOPAUSE so showpage won't try to read from stdin. */
+ swproc(minst, "-dNOPAUSE", pal);
+ gs_main_init2(minst); /* Finish initialization */
+ /* We delete this only to make Ghostview work properly. */
+/**************** This is WRONG. ****************/
+ /*gs_stdin_is_interactive = false; */
+ run_string(minst, ".runstdin", runFlush);
+ break;
+ case '-': /* run with command line args */
+ case '+':
+ pal->expand_ats = false;
+ case '@': /* ditto with @-expansion */
+ {
+ const char *psarg = arg_next(pal);
+
+ if (psarg == 0) {
+ fprintf(stdout, "Usage: gs ... -%c file.ps arg1 ... argn\n", sw);
+ arg_finit(pal);
+ gs_exit(1);
+ }
+ psarg = arg_heap_copy(psarg);
+ gs_main_init2(minst);
+ run_string(minst, "userdict/ARGUMENTS[", 0);
+ while ((arg = arg_next(pal)) != 0)
+ runarg(minst, "", arg_heap_copy(arg), "", runInit);
+ runarg(minst, "]put", psarg, ".runfile", runInit | runFlush);
+ gs_exit(0);
+ }
+ case 'A': /* trace allocator */
+ switch (*arg) {
+ case 0:
+ gs_alloc_debug = 1;
+ break;
+ case '-':
+ gs_alloc_debug = 0;
+ break;
+ default:
+ puts("-A may only be followed by -");
+ gs_exit(1);
+ }
+ break;
+ case 'B': /* set run_string buffer size */
+ if (*arg == '-')
+ minst->run_buffer_size = 0;
+ else {
+ uint bsize;
+
+ if (sscanf((const char *)arg, "%u", &bsize) != 1 ||
+ bsize <= 0 || bsize > MAX_BUFFERED_SIZE
+ ) {
+ fprintf(stdout, "-B must be followed by - or size between 1 and %u\n", MAX_BUFFERED_SIZE);
+ gs_exit(1);
+ }
+ minst->run_buffer_size = bsize;
+ }
+ break;
+ case 'c': /* code follows */
+ {
+ bool ats = pal->expand_ats;
+
+ gs_main_init2(minst);
+ pal->expand_ats = false;
+ while ((arg = arg_next(pal)) != 0) {
+ char *sarg;
+
+ if (arg[0] == '@' ||
+ (arg[0] == '-' && !isdigit(arg[1]))
+ )
+ break;
+ sarg = arg_heap_copy(arg);
+ runarg(minst, "", sarg, ".runstring", 0);
+ }
+ if (arg != 0)
+ arg_push_string(pal, arg_heap_copy(arg));
+ pal->expand_ats = ats;
+ break;
+ }
+ case 'E': /* log errors */
+ switch (*arg) {
+ case 0:
+ gs_log_errors = 1;
+ break;
+ case '-':
+ gs_log_errors = 0;
+ break;
+ default:
+ puts("-E may only be followed by -");
+ gs_exit(1);
+ }
+ break;
+ case 'f': /* run file of arbitrary name */
+ if (*arg != 0)
+ argproc(minst, arg);
+ break;
+ case 'F': /* run file with buffer_size = 1 */
+ if (!*arg) {
+ puts("-F requires a file name");
+ gs_exit(1);
+ } {
+ uint bsize = minst->run_buffer_size;
+
+ minst->run_buffer_size = 1;
+ argproc(minst, arg);
+ minst->run_buffer_size = bsize;
+ }
+ break;
+ case 'g': /* define device geometry */
+ {
+ long width, height;
+ ref value;
+
+ gs_main_init1(minst);
+ if (sscanf((const char *)arg, "%ldx%ld", &width, &height) != 2) {
+ puts("-g must be followed by <width>x<height>");
+ gs_exit(1);
+ }
+ make_int(&value, width);
+ initial_enter_name("DEVICEWIDTH", &value);
+ make_int(&value, height);
+ initial_enter_name("DEVICEHEIGHT", &value);
+ initial_enter_name("FIXEDMEDIA", &vtrue);
+ break;
+ }
+ case 'h': /* print help */
+ case '?': /* ditto */
+ print_help(minst);
+ gs_exit(gs_exit_INFO);
+ case 'I': /* specify search path */
+ gs_main_add_lib_path(minst, arg_heap_copy(arg));
+ break;
+ case 'K': /* set malloc limit */
+ {
+ long msize = 0;
+
+ sscanf((const char *)arg, "%ld", &msize);
+ if (msize <= 0 || msize > max_long >> 10) {
+ fprintf(stdout, "-K<numK> must have 1 <= numK <= %ld\n",
+ max_long >> 10);
+ gs_exit(1);
+ }
+ gs_malloc_limit = msize << 10;
+ }
+ break;
+ case 'M': /* set memory allocation increment */
+ {
+ unsigned msize = 0;
+
+ sscanf((const char *)arg, "%u", &msize);
+#if arch_ints_are_short
+ if (msize <= 0 || msize >= 64) {
+ puts("-M must be between 1 and 63");
+ gs_exit(1);
+ }
+#endif
+ minst->memory_chunk_size = msize << 10;
+ }
+ break;
+ case 'N': /* set size of name table */
+ {
+ unsigned nsize = 0;
+
+ sscanf((const char *)arg, "%d", &nsize);
+#if arch_ints_are_short
+ if (nsize < 2 || nsize > 64) {
+ puts("-N must be between 2 and 64");
+ gs_exit(1);
+ }
+#endif
+ minst->name_table_size = (ulong) nsize << 10;
+ }
+ break;
+ case 'P': /* choose whether search '.' first */
+ if (!strcmp(arg, ""))
+ minst->search_here_first = true;
+ else if (!strcmp(arg, "-"))
+ minst->search_here_first = false;
+ else {
+ puts("Only -P or -P- is allowed.");
+ gs_exit(1);
+ }
+ break;
+ case 'q': /* quiet startup */
+ gs_main_init1(minst);
+ initial_enter_name("QUIET", &vtrue);
+ break;
+ case 'r': /* define device resolution */
+ {
+ float xres, yres;
+ ref value;
+
+ gs_main_init1(minst);
+ switch (sscanf((const char *)arg, "%fx%f", &xres, &yres)) {
+ default:
+ puts("-r must be followed by <res> or <xres>x<yres>");
+ gs_exit(1);
+ case 1: /* -r<res> */
+ yres = xres;
+ case 2: /* -r<xres>x<yres> */
+ make_real(&value, xres);
+ initial_enter_name("DEVICEXRESOLUTION", &value);
+ make_real(&value, yres);
+ initial_enter_name("DEVICEYRESOLUTION", &value);
+ initial_enter_name("FIXEDRESOLUTION", &vtrue);
+ }
+ break;
+ }
+ case 'D': /* define name */
+ case 'd':
+ case 'S': /* define name as string */
+ case 's':
+ {
+ char *adef = arg_heap_copy(arg);
+ char *eqp = strchr(adef, '=');
+ bool isd = (sw == 'D' || sw == 'd');
+ ref value;
+
+ if (eqp == NULL)
+ eqp = strchr(adef, '#');
+ /* Initialize the object memory, scanner, and */
+ /* name table now if needed. */
+ gs_main_init1(minst);
+ if (eqp == adef) {
+ puts("Usage: -dname, -dname=token, -sname=string");
+ gs_exit(1);
+ }
+ if (eqp == NULL) {
+ if (isd)
+ make_true(&value);
+ else
+ make_empty_string(&value, a_readonly);
+ } else {
+ int code;
+ uint space = icurrent_space;
+
+ *eqp++ = 0;
+ ialloc_set_space(idmemory, avm_system);
+ if (isd) {
+ stream astream;
+ scanner_state state;
+
+ sread_string(&astream,
+ (const byte *)eqp, strlen(eqp));
+ scanner_state_init(&state, false);
+ code = scan_token(&astream, &value, &state);
+ if (code) {
+ puts("-dname= must be followed by a valid token");
+ gs_exit(1);
+ }
+ if (r_has_type_attrs(&value, t_name,
+ a_executable)) {
+ ref nsref;
+
+ name_string_ref(&value, &nsref);
+#define string_is(nsref, str, len)\
+ (r_size(&(nsref)) == (len) &&\
+ !strncmp((const char *)(nsref).value.const_bytes, str, (len)))
+ if (string_is(nsref, "null", 4))
+ make_null(&value);
+ else if (string_is(nsref, "true", 4))
+ make_true(&value);
+ else if (string_is(nsref, "false", 5))
+ make_false(&value);
+ else {
+ puts("-dvar=name requires name=null, true, or false");
+ gs_exit(1);
+ }
+#undef name_is_string
+ }
+ } else {
+ int len = strlen(eqp);
+ char *str =
+ (char *)gs_alloc_bytes(minst->heap,
+ (uint) len, "-s");
+
+ if (str == 0) {
+ lprintf("Out of memory!\n");
+ gs_exit(1);
+ }
+ memcpy(str, eqp, len);
+ make_const_string(&value,
+ a_readonly | avm_foreign,
+ len, (const byte *)str);
+ }
+ ialloc_set_space(idmemory, space);
+ }
+ /* Enter the name in systemdict. */
+ initial_enter_name(adef, &value);
+ break;
+ }
+ case 'u': /* undefine name */
+ if (!*arg) {
+ puts("-u requires a name to undefine.");
+ gs_exit(1);
+ }
+ gs_main_init1(minst);
+ initial_remove_name(arg);
+ break;
+ case 'v': /* print revision */
+ print_revision();
+ gs_exit(0);
+/*#ifdef DEBUG */
+ /*
+ * Here we provide a place for inserting debugging code that can be
+ * run in place of the normal interpreter code.
+ */
+ case 'X':
+ gs_main_init2(minst);
+ {
+ int xec; /* exit_code */
+ ref xeo; /* error_object */
+
+#define start_x()\
+ gs_main_run_string_begin(minst, 1, &xec, &xeo)
+#define run_x(str)\
+ gs_main_run_string_continue(minst, str, strlen(str), 1, &xec, &xeo)
+#define stop_x()\
+ gs_main_run_string_end(minst, 1, &xec, &xeo)
+ start_x();
+ run_x("\216\003abc");
+ run_x("== flush\n");
+ stop_x();
+ }
+ gs_exit(0);
+/*#endif */
+ case 'Z':
+ {
+ byte value = (*arg == '-' ? (++arg, 0) : 0xff);
+
+ while (*arg)
+ gs_debug[*arg++ & 127] = value;
+ }
+ break;
+ }
+ return 0;
+}
+
+/* Define versions of strlen and strcat that encode strings in hex. */
+/* This is so we can enter escaped characters regardless of whether */
+/* the Level 1 convention of ignoring \s in strings-within-strings */
+/* is being observed (sigh). */
+private int
+esc_strlen(const char *str)
+{
+ return strlen(str) * 2 + 2;
+}
+private void
+esc_strcat(char *dest, const char *src)
+{
+ char *d = dest + strlen(dest);
+ const char *p;
+ static const char *const hex = "0123456789abcdef";
+
+ *d++ = '<';
+ for (p = src; *p; p++) {
+ byte c = (byte) * p;
+
+ *d++ = hex[c >> 4];
+ *d++ = hex[c & 0xf];
+ }
+ *d++ = '>';
+ *d = 0;
+}
+
+/* Process file names */
+private void
+argproc(gs_main_instance * minst, const char *arg)
+{
+ if (minst->run_buffer_size) {
+ /* Run file with run_string. */
+ run_buffered(minst, arg);
+ } else {
+ /* Run file directly in the normal way. */
+ runarg(minst, "", arg, ".runfile", runInit | runFlush);
+ }
+}
+private void
+run_buffered(gs_main_instance * minst, const char *arg)
+{
+ FILE *in = gp_fopen(arg, gp_fmode_rb);
+ int exit_code;
+ ref error_object;
+ int code;
+
+ if (in == 0) {
+ fprintf(stdout, "Unable to open %s for reading", arg);
+ gs_exit(1);
+ }
+ gs_main_init2(minst);
+ code = gs_main_run_string_begin(minst, minst->user_errors,
+ &exit_code, &error_object);
+ if (!code) {
+ char buf[MAX_BUFFERED_SIZE];
+ int count;
+
+ code = e_NeedInput;
+ while ((count = fread(buf, 1, minst->run_buffer_size, in)) > 0) {
+ code = gs_main_run_string_continue(minst, buf, count,
+ minst->user_errors,
+ &exit_code, &error_object);
+ if (code != e_NeedInput)
+ break;
+ }
+ if (code == e_NeedInput) {
+ code = gs_main_run_string_end(minst, minst->user_errors,
+ &exit_code, &error_object);
+ }
+ }
+ fclose(in);
+ zflush(osp);
+ zflushpage(osp);
+ run_finish(code, exit_code, &error_object);
+}
+private void
+runarg(gs_main_instance * minst, const char *pre, const char *arg,
+ const char *post, int options)
+{
+ int len = strlen(pre) + esc_strlen(arg) + strlen(post) + 1;
+ char *line;
+
+ if (options & runInit)
+ gs_main_init2(minst); /* Finish initialization */
+ line = (char *)gs_alloc_bytes(minst->heap, len, "argproc");
+ if (line == 0) {
+ lprintf("Out of memory!\n");
+ gs_exit(1);
+ }
+ strcpy(line, pre);
+ esc_strcat(line, arg);
+ strcat(line, post);
+ run_string(minst, line, options);
+}
+private void
+run_string(gs_main_instance * minst, const char *str, int options)
+{
+ int exit_code;
+ ref error_object;
+ int code = gs_main_run_string(minst, str, minst->user_errors,
+ &exit_code, &error_object);
+
+ if ((options & runFlush) || code != 0) {
+ zflush(osp); /* flush stdout */
+ zflushpage(osp); /* force display update */
+ }
+ run_finish(code, exit_code, &error_object);
+}
+private void
+run_finish(int code, int exit_code, ref * perror_object)
+{
+ switch (code) {
+ case 0:
+ break;
+ case e_Quit:
+ gs_exit(0);
+ case e_Fatal:
+ eprintf1("Unrecoverable error, exit code %d\n", exit_code);
+ gs_exit(exit_code);
+ default:
+ gs_debug_dump_stack(code, perror_object);
+ gs_exit_with_code(255, code);
+ }
+}
+
+/* ---------------- Print information ---------------- */
+
+/*
+ * Help strings. We have to break them up into parts, because
+ * the Watcom compiler has a limit of 510 characters for a single token.
+ * For PC displays, we want to limit the strings to 24 lines.
+ */
+private const char help_usage1[] = "\
+Usage: gs [switches] [file1.ps file2.ps ...]\n\
+Most frequently used switches: (you can use # in place of =)\n\
+ -dNOPAUSE no pause after page | -q `quiet', fewer messages\n\
+ -g<width>x<height> page size in pixels | -r<res> pixels/inch resolution\n";
+private const char help_usage2[] = "\
+ -sDEVICE=<devname> select device | -dBATCH exit after last file\n\
+ -sOutputFile=<file> select output file: - for stdout, |command for pipe,\n\
+ embed %d or %ld for page #\n";
+private const char help_trailer[] = "\
+For more information, see %s%sUse.htm.\n\
+Report bugs to %s, using the form in Bug-form.htm.\n";
+private const char help_devices[] = "Available devices:";
+private const char help_emulators[] = "Input formats:";
+private const char help_paths[] = "Search path:";
+
+/* Print the standard help message. */
+private void
+print_help(gs_main_instance * minst)
+{
+ print_revision();
+ print_usage();
+ print_emulators();
+ print_devices();
+ print_paths(minst);
+ print_help_trailer();
+}
+
+/* Print the revision, revision date, and copyright. */
+private void
+print_revision(void)
+{
+ fprintf(stdout, "%s ", gs_product);
+ print_version();
+ fprintf(stdout, " (%d-%d-%d)\n%s\n",
+ (int)(gs_revisiondate / 10000),
+ (int)(gs_revisiondate / 100 % 100),
+ (int)(gs_revisiondate % 100),
+ gs_copyright);
+}
+
+/* Print the version number. */
+private void
+print_version(void)
+{
+ fprintf(stdout, "%d.%02d",
+ (int)(gs_revision / 100),
+ (int)(gs_revision % 100));
+}
+
+/* Print usage information. */
+private void
+print_usage(void)
+{
+ fprintf(stdout, "%s", help_usage1);
+ fprintf(stdout, "%s", help_usage2);
+}
+
+/* Print the list of available devices. */
+private void
+print_devices(void)
+{
+ fprintf(stdout, "%s", help_devices);
+ {
+ int i;
+ int pos = 100;
+ const gx_device *pdev;
+
+ for (i = 0; (pdev = gs_getdevice(i)) != 0; i++) {
+ const char *dname = gs_devicename(pdev);
+ int len = strlen(dname);
+
+ if (pos + 1 + len > 76)
+ fprintf(stdout, "\n "), pos = 2;
+ fprintf(stdout, " %s", dname);
+ pos += 1 + len;
+ }
+ }
+ fprintf(stdout, "\n");
+}
+
+/* Print the list of language emulators. */
+private void
+print_emulators(void)
+{
+ fprintf(stdout, "%s", help_emulators);
+ {
+ const ref *pes;
+
+ for (pes = gs_emulator_name_array;
+ pes->value.const_bytes != 0; pes++
+ )
+ fprintf(stdout, " %s", pes->value.const_bytes);
+ }
+ fprintf(stdout, "\n");
+}
+
+/* Print the search paths. */
+private void
+print_paths(gs_main_instance * minst)
+{
+ fprintf(stdout, "%s", help_paths);
+ gs_main_set_lib_paths(minst);
+ {
+ uint count = r_size(&minst->lib_path.list);
+ uint i;
+ int pos = 100;
+ char fsepr[3];
+
+ fsepr[0] = ' ', fsepr[1] = gp_file_name_list_separator,
+ fsepr[2] = 0;
+ for (i = 0; i < count; ++i) {
+ const ref *prdir =
+ minst->lib_path.list.value.refs + i;
+ uint len = r_size(prdir);
+ const char *sepr = (i == count - 1 ? "" : fsepr);
+
+ if (1 + pos + strlen(sepr) + len > 76)
+ fprintf(stdout, "\n "), pos = 2;
+ fprintf(stdout, " ");
+ /*
+ * This is really ugly, but it's necessary.
+ * We wish we could just do:
+ fwrite(prdir->value.bytes, 1, len, stdout);
+ */
+ {
+ const char *p = (const char *)prdir->value.bytes;
+ uint j;
+
+ for (j = len; j; j--)
+ fprintf(stdout, "%c", *p++);
+ }
+ fprintf(stdout, sepr);
+ pos += 1 + len + strlen(sepr);
+ }
+ }
+ fprintf(stdout, "\n");
+}
+
+/* Print the help trailer. */
+private void
+print_help_trailer(void)
+{
+ fprintf(stdout, help_trailer, gs_doc_directory,
+ gp_file_name_concat_string(gs_doc_directory,
+ strlen(gs_doc_directory),
+ "Use.htm", 7),
+ GS_BUG_MAILBOX);
+}
diff --git a/pstoraster/imainarg.h b/pstoraster/imainarg.h
new file mode 100644
index 000000000..d135f2b52
--- /dev/null
+++ b/pstoraster/imainarg.h
@@ -0,0 +1,54 @@
+/* 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 supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* argv/argc interface to imainarg.c */
+
+#ifndef imainarg_INCLUDED
+# define imainarg_INCLUDED
+
+/* Define an opaque type for an interpreter instance. See imain.h. */
+#ifndef gs_main_instance_DEFINED
+# define gs_main_instance_DEFINED
+typedef struct gs_main_instance_s gs_main_instance;
+
+#endif
+
+/*
+ * As a shortcut for very high-level clients, we define a single call
+ * that does the equivalent of command line invocation, passing argc
+ * and argv. This call includes calling init0 through init2.
+ * argv should really be const char *[], but ANSI C requires writable
+ * strings (which, however, it forbids the callee to modify!).
+ */
+int gs_main_init_with_args(P3(gs_main_instance * minst, int argc,
+ char *argv[]));
+
+/*
+ * Run the 'start' procedure (after processing the command line).
+ * Note that this procedure exits rather than returning.
+ */
+void gs_main_run_start(P1(gs_main_instance * minst));
+
+#endif /* imainarg_INCLUDED */
diff --git a/pstoraster/imemory.h b/pstoraster/imemory.h
new file mode 100644
index 000000000..40e7b69c9
--- /dev/null
+++ b/pstoraster/imemory.h
@@ -0,0 +1,99 @@
+/* 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 supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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.
+ */
+
+#include "gsalloc.h"
+
+#ifndef gs_ref_memory_DEFINED
+# define gs_ref_memory_DEFINED
+typedef struct gs_ref_memory_s gs_ref_memory_t;
+#endif
+
+ /* 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));
+
+/* Register a ref root. This just calls gs_register_root. */
+/* Note that ref roots are a little peculiar: they assume that */
+/* the ref * that they point to points to a *statically* allocated ref. */
+int gs_register_ref_root(P4(gs_memory_t *mem, gs_gc_root_t *root,
+ void **pp, client_name_t 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..3b671f95c
--- /dev/null
+++ b/pstoraster/iminst.h
@@ -0,0 +1,92 @@
+/* Copyright (C) 1995, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Definition of interpreter instance */
+/* Requires stdio_.h, gsmemory.h, iref.h */
+
+#ifndef iminst_INCLUDED
+# define iminst_INCLUDED
+
+#ifndef gs_main_instance_DEFINED
+# define gs_main_instance_DEFINED
+typedef struct gs_main_instance_s gs_main_instance;
+
+#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;
+ gs_memory_t *heap; /* (C) heap allocator */
+ uint memory_chunk_size; /* 'wholesale' allocation unit */
+ ulong name_table_size;
+ uint run_buffer_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) */
+ long base_time[2]; /* starting usertime */
+};
+
+/*
+ * Note that any file that uses the following definition of default values
+ * must include gconfig.h, because of SEARCH_HERE_FIRST.
+ */
+#define gs_main_instance_default_init_values\
+ 0, 0, 0, 0, 20000, 0, 0, -1, 0, SEARCH_HERE_FIRST, 1
+extern const gs_main_instance gs_main_instance_init_values;
+
+#endif /* iminst_INCLUDED */
diff --git a/pstoraster/iname.c b/pstoraster/iname.c
new file mode 100644
index 000000000..118ff15f2
--- /dev/null
+++ b/pstoraster/iname.c
@@ -0,0 +1,637 @@
+/* Copyright (C) 1989, 1995, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Name lookup for Ghostscript interpreter */
+#include "memory_.h"
+#include "string_.h"
+#include "ghost.h"
+#include "gsstruct.h"
+#include "gxobj.h" /* for o_set_unmarked */
+#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 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 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
+};
+
+/* Structure descriptors */
+gs_private_st_composite(st_name_sub_table, name_sub_table, "name_sub_table",
+ name_sub_enum_ptrs, name_sub_reloc_ptrs);
+gs_private_st_composite(st_name_table, name_table, "name_table",
+ name_table_enum_ptrs, name_table_reloc_ptrs);
+
+/* 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;
+
+ dlprintf1("[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 a name table */
+name_table *
+names_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)");
+ 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 = names_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;
+ names_trace_finish(nt, NULL);
+ return nt;
+}
+
+/* Get the allocator for the name table. */
+gs_memory_t *
+names_memory(const name_table * nt)
+{
+ return 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
+names_ref(name_table * nt, const byte * ptr, uint size, ref * pref, int enterflag)
+{
+ 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 = names_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 = names_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 = names_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,
+ "names_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
+names_string_ref(const name_table * nt, const ref * pnref /* t_name */ ,
+ ref * psref /* result, t_string */ )
+{
+ name *pname = pnref->value.pname;
+
+ 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
+names_from_string(name_table * nt, const ref * psref, ref * pnref)
+{
+ int exec = r_has_attr(psref, a_executable);
+ int code = names_ref(nt, 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
+names_enter_string(name_table * nt, const char *str, ref * pref)
+{
+ return names_ref(nt, (const byte *)str, strlen(str), pref, 0);
+}
+
+/* Invalidate the value cache for a name. */
+void
+names_invalidate_value_cache(name_table * nt, const ref * pnref)
+{
+ pnref->value.pname->pvalue = pv_other;
+}
+
+/* Convert between names and indices. */
+#undef names_index
+name_index_t
+names_index(const name_table * nt, const ref * pnref)
+{
+ return names_index_inline(nt, pnref);
+}
+void
+names_index_ref(const name_table * nt, name_index_t index, ref * pnref)
+{
+ names_index_ref_inline(nt, index, pnref);
+}
+name *
+names_index_ptr(const name_table * nt, name_index_t index)
+{
+ return names_index_ptr_inline(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. */
+name_index_t
+names_next_valid_index(name_table * nt, name_index_t nidx)
+{
+ 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
+names_unmark_all(name_table * 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)
+ names_index_ptr(nt, 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
+names_mark_index(name_table * nt, name_index_t nidx)
+{
+ name *pname = names_index_ptr(nt, 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 */ *
+names_ref_sub_table(name_table * nt, 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 - (names_index_inline(nt, pnref) & nt_sub_index_mask);
+}
+void /*obj_header_t */ *
+names_index_ptr_sub_table(name_table * nt, name_index_t index, name * pname)
+{
+ return pname - (index & nt_sub_index_mask);
+}
+
+/*
+ * Clean up the name table after the trace/mark phase of a garbage
+ * collection, by removing names that aren't marked. gcst == NULL indicates
+ * we're doing this for initialization or restore rather than for a GC.
+ */
+void
+names_trace_finish(name_table * nt, gc_state_t * gcst)
+{
+ uint *phash = &nt->hash[0];
+ uint i;
+
+ for (i = 0; i < nt_hash_size; phash++, i++) {
+ name_index_t prev = 0;
+ name *pnprev;
+ name_index_t nidx = *phash;
+
+ while (nidx != 0) {
+ name *pname = names_index_ptr_inline(nt, nidx);
+ name_index_t next = name_next_index(nidx, pname);
+
+ if (pname->mark) {
+ 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; i--;) {
+ name_sub_table *sub = nt->sub_tables[i];
+
+ if (sub != 0) {
+ name_scan_sub(nt, i, true);
+ if (nt->sub_tables[i] == 0 && gcst != 0) { /* Mark the just-freed sub-table as unmarked. */
+ o_set_unmarked((obj_header_t *) sub - 1);
+ }
+ }
+ 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
+names_restore(name_table * nt, alloc_save_t * save)
+{ /* We simply mark all names older than the save, */
+ /* and let names_trace_finish 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 =
+ names_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);
+ }
+ }
+ names_trace_finish(nt, 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 name_extension_bits > 0
+ 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);
+ }
+#endif
+ 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;
+
+ dlprintf1("[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) {
+ /* Back up over a final run of deleted sub-tables. */
+ do {
+ --sub_index;
+ } while (nt->sub_tables[sub_index] == 0);
+ nt->sub_count = sub_index + 1;
+ if (nt->sub_next > sub_index)
+ nt->sub_next = sub_index;
+ } else 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;
+ ENUM_RETURN(ntptr->sub_tables[index]);
+}
+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++)
+ RELOC_VAR(*sub);
+ /*
+ * 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
+#undef ntptr
+
+private ENUM_PTRS_BEGIN_PROC(name_sub_enum_ptrs)
+{
+ return 0;
+}
+ENUM_PTRS_END_PROC
+private RELOC_PTRS_BEGIN(name_sub_reloc_ptrs)
+{
+ name *pname = ((name_sub_table *) vptr)->names;
+ uint i;
+
+ for (i = 0; i < nt_sub_size; ++pname, ++i) {
+ if (pname->string_bytes != 0 && !pname->foreign_string) {
+ gs_const_string nstr;
+
+ nstr.data = pname->string_bytes;
+ nstr.size = pname->string_size;
+ RELOC_CONST_STRING_VAR(nstr);
+ pname->string_bytes = nstr.data;
+ }
+ }
+}
+RELOC_PTRS_END
diff --git a/pstoraster/iname.h b/pstoraster/iname.h
new file mode 100644
index 000000000..134221646
--- /dev/null
+++ b/pstoraster/iname.h
@@ -0,0 +1,105 @@
+/* Copyright (C) 1989, 1995, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Interpreter's name table interface */
+
+#ifndef iname_INCLUDED
+# define iname_INCLUDED
+
+#include "inames.h"
+
+/*
+ * This file defines those parts of the name table API that refer to the
+ * interpreter's distinguished instance. Procedures in this file begin
+ * with name_.
+ */
+
+/* ---------------- Procedural interface ---------------- */
+
+/* Define the interpreter's name table. */
+extern name_table *the_gs_name_table;
+
+/* Backward compatibility */
+#define the_name_table() ((const name_table *)the_gs_name_table)
+
+/* Get the allocator for the name table. */
+#define name_memory()\
+ names_memory(the_gs_name_table)
+
+/*
+ * Look up and/or enter a name in the name table.
+ * See inames.h for the values of enterflag, and the possible return values.
+ */
+#define name_ref(ptr, size, pnref, enterflag)\
+ names_ref(the_gs_name_table, ptr, size, pnref, enterflag)
+#define name_string_ref(pnref, psref)\
+ names_string_ref(the_gs_name_table, pnref, psref)
+/*
+ * name_enter_string calls name_ref with a (permanent) C string.
+ */
+#define name_enter_string(str, pnref)\
+ names_enter_string(the_gs_name_table,str, pnref)
+/*
+ * name_from_string essentially implements cvn.
+ * It always enters the name, and copies the executable attribute.
+ */
+#define name_from_string(psref, pnref)\
+ names_from_string(the_gs_name_table, psref, pnref)
+
+/* Compare two names for equality. */
+#define name_eq(pnref1, pnref2)\
+ names_eq(pnref1, pnref2)
+
+/* Invalidate the value cache for a name. */
+#define name_invalidate_value_cache(pnref)\
+ names_invalidate_value_cache(the_gs_name_table, pnref)
+
+/* Convert between names and indices. */
+#define name_index(pnref) /* ref => index */\
+ names_index(the_gs_name_table, pnref)
+#define name_index_ptr(nidx) /* index => name */\
+ names_index_ptr(the_gs_name_table, nidx)
+#define name_index_ref(nidx, pnref) /* index => ref */\
+ names_index_ref(the_gs_name_table, nidx, pnref)
+
+/* Get the index of the next valid name. */
+/* The argument is 0 or a valid index. */
+/* Return 0 if there are no more. */
+#define name_next_valid_index(nidx)\
+ names_next_valid_index(the_gs_name_table, nidx)
+
+/* Mark a name for the garbage collector. */
+/* Return true if this is a new mark. */
+#define name_mark_index(nidx)\
+ names_mark_index(the_gs_name_table, nidx)
+
+/* Get the object (sub-table) containing a name. */
+/* The garbage collector needs this so it can relocate pointers to names. */
+#define name_ref_sub_table(pnref)\
+ names_ref_sub_table(the_gs_name_table, pnref)
+#define name_index_ptr_sub_table(nidx, pnref)\
+ names_index_ptr_sub_table(the_gs_name_table, nidx, pnref)
+
+#endif /* iname_INCLUDED */
diff --git a/pstoraster/inamedef.h b/pstoraster/inamedef.h
new file mode 100644
index 000000000..4feb52279
--- /dev/null
+++ b/pstoraster/inamedef.h
@@ -0,0 +1,230 @@
+/* Copyright (C) 1989, 1995, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Name table definition */
+
+#ifndef inamedef_INCLUDED
+# define inamedef_INCLUDED
+
+#include "inames.h"
+#include "gconfigv.h" /* defines EXTEND_NAMES */
+#include "gsstruct.h" /* for gc_state_t */
+
+/*
+ * 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 inames.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 names_index_inline(nt_ignored, pnref)\
+ ( ((uint)((pnref)->value.pname->my_extension) << 16) + r_size(pnref) )
+#else
+# define names_index_inline(nt_ignored, pnref) r_size(pnref)
+#endif
+#define names_index(nt_ignored, pnref) names_index_inline(nt_ignored, pnref)
+ /* index => name */
+#define names_index_ptr_inline(nt, nidx)\
+ ((nt)->sub_tables[(nidx) >> nt_log2_sub_size]->names + ((nidx) & nt_sub_index_mask))
+ /* index => ref */
+#define names_index_ref_inline(nt, nidx, pnref)\
+ make_name(pnref, nidx, names_index_ptr_inline(nt, nidx));
+/* Backward compatibility */
+#define name_index_inline(pnref) names_index_inline(ignored, pnref)
+#define name_index_ptr_inline(nt, pnref) names_index_ptr_inline(nt, pnref)
+#define name_index_ref_inline(nt, nidx, pnref)\
+ names_index_ref_inline(nt, nidx, pnref)
+
+ /* 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 names_unmark_all(P1(name_table * nt));
+
+/* Finish tracing the name table by putting free names on the free list. */
+void names_trace_finish(P2(name_table * nt, gc_state_t * gcst));
+
+/* ------ 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 names_restore(P2(name_table * nt, alloc_save_t * save));
+
+/* ---------------- 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))
+
+#endif /* inamedef_INCLUDED */
diff --git a/pstoraster/inames.h b/pstoraster/inames.h
new file mode 100644
index 000000000..7eb6e4633
--- /dev/null
+++ b/pstoraster/inames.h
@@ -0,0 +1,114 @@
+/* Copyright (C) 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Name table interface */
+
+#ifndef inames_INCLUDED
+# define inames_INCLUDED
+
+/*
+ * This file defines those parts of the name table API that depend neither
+ * on the implementation nor on the existence of a single distinguished
+ * instance. Procedures in this file begin with names_.
+ */
+
+/* ---------------- Interface types ---------------- */
+
+#ifndef name_table_DEFINED
+# define name_table_DEFINED
+typedef struct name_table_s name_table;
+
+#endif
+
+typedef uint name_index_t;
+
+/* ---------------- Constant values ---------------- */
+
+extern const uint name_max_string;
+
+/* ---------------- Procedural interface ---------------- */
+
+/* Allocate and initialize a name table. */
+name_table *names_init(P2(ulong size, gs_memory_t * mem));
+
+/* Get the allocator for a name table. */
+gs_memory_t *names_memory(P1(const name_table * nt));
+
+/*
+ * 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 names_memory allocator).
+ * Possible errors: VMerror, limitcheck (if string is too long or if
+ * we have assigned all possible name indices).
+ */
+int names_ref(P5(name_table * nt, const byte * ptr, uint size, ref * pnref,
+ int enterflag));
+void names_string_ref(P3(const name_table * nt, const ref * pnref, ref * psref));
+
+/*
+ * names_enter_string calls names_ref with a (permanent) C string.
+ */
+int names_enter_string(P3(name_table * nt, const char *str, ref * pnref));
+
+/*
+ * names_from_string essentially implements cvn.
+ * It always enters the name, and copies the executable attribute.
+ */
+int names_from_string(P3(name_table * nt, const ref * psref, ref * pnref));
+
+/* Compare two names for equality. */
+#define names_eq(pnref1, pnref2)\
+ ((pnref1)->value.pname == (pnref2)->value.pname)
+
+/* Invalidate the value cache for a name. */
+void names_invalidate_value_cache(P2(name_table * nt, const ref * pnref));
+
+/* Convert between names and indices. */
+name_index_t names_index(P2(const name_table * nt, const ref * pnref)); /* ref => index */
+name *names_index_ptr(P2(const name_table * nt, name_index_t nidx)); /* index => name */
+void names_index_ref(P3(const name_table * nt, name_index_t 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. */
+name_index_t names_next_valid_index(P2(name_table * nt, name_index_t nidx));
+
+/* Mark a name for the garbage collector. */
+/* Return true if this is a new mark. */
+bool names_mark_index(P2(name_table * nt, name_index_t nidx));
+
+/* Get the object (sub-table) containing a name. */
+/* The garbage collector needs this so it can relocate pointers to names. */
+void /*obj_header_t */ *
+ names_ref_sub_table(P2(name_table * nt, const ref * pnref));
+void /*obj_header_t */ *
+ names_index_ptr_sub_table(P3(name_table * nt, name_index_t nidx, name * pname));
+
+#endif /* inames_INCLUDED */
diff --git a/pstoraster/interp.c b/pstoraster/interp.c
new file mode 100644
index 000000000..af723357e
--- /dev/null
+++ b/pstoraster/interp.c
@@ -0,0 +1,1554 @@
+/* Copyright (C) 1989, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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 "icontext.h"
+#include "inamedef.h"
+#include "iname.h" /* for the_name_table */
+#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"
+
+/*
+ * We may or may not optimize the handling of the special fast operators
+ * in packed arrays. If we do this, they run much faster when packed, but
+ * slightly slower when not packed.
+ */
+#define PACKED_SPECIAL_OPS 1
+
+/*
+ * Pseudo-operators (procedures of type t_oparray) record
+ * the operand and dictionary stack pointers, and restore them if an error
+ * occurs during the execution of the procedure and if the procedure hasn't
+ * (net) decreased the depth of the stack. While this obviously doesn't
+ * do all the work of restoring the state if a pseudo-operator gets an
+ * error, it's a big help. The only downside is that pseudo-operators run
+ * a little slower.
+ */
+
+/* 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 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;
+
+/*
+ * Apply an operator. When debugging, we route all operator calls
+ * through a procedure.
+ */
+#ifdef DEBUG
+private int
+call_operator(int (*op_proc) (P1(os_ptr)), os_ptr op)
+{
+ int code = (*op_proc) (op);
+
+ return code;
+}
+#else
+# define call_operator(proc, op) ((*(proc))(op))
+#endif
+
+/* 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 *));
+private int oparray_pop(P1(os_ptr));
+private int oparray_cleanup(P1(os_ptr));
+
+/* 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).
+ */
+#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
+
+/* Interpreter state variables */
+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 */
+extern_st(st_ref_stack);
+#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)
+op_stack_t iop_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)
+exec_stack_t iexec_stack;
+
+#define DS_REFS_SIZE(body_size)\
+ (stack_block_refs + (body_size))
+dict_stack_t idict_stack;
+
+ /*#define d_stack (idict_stack.stack) *//* in dstack.h */
+
+/* Define a pointer to the current interpreter context state. */
+gs_context_state_t *gs_interp_context_state_current;
+
+/* 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;
+
+#define make_null_proc(pref)\
+ make_empty_const_array(pref, a_executable + a_readonly)
+
+/* Initialize the interpreter. */
+void
+gs_interp_init(void)
+{ /* Create and initialize a ocntext state. */
+ gs_context_state_t *pcst = 0;
+ int code = context_state_alloc(&pcst, &gs_imemory);
+
+ if (code < 0 || (code = context_state_load(pcst)) < 0) {
+ lprintf1("Fatal error %d in gs_interp_init!", code);
+/****** ABORT ******/
+ }
+ gs_interp_context_state_current = pcst;
+ gs_register_struct_root(imemory_local, NULL,
+ (void **)&gs_interp_context_state_current,
+ "gs_interp_init(gs_icst_root)");
+}
+/*
+ * Create initial stacks for the interpreter.
+ * We export this for creating new contexts.
+ */
+int
+gs_interp_alloc_stacks(gs_ref_memory_t * smem, gs_context_state_t * pcst)
+{
+ ref stk;
+
+#define REFS_SIZE_OSTACK OS_REFS_SIZE(MAX_OSTACK)
+#define REFS_SIZE_ESTACK ES_REFS_SIZE(MAX_ESTACK)
+#define REFS_SIZE_DSTACK DS_REFS_SIZE(MAX_DSTACK)
+ gs_alloc_ref_array(smem, &stk, 0,
+ REFS_SIZE_OSTACK + REFS_SIZE_ESTACK +
+ REFS_SIZE_DSTACK, "gs_interp_alloc_stacks");
+
+ {
+ ref_stack *pos = pcst->ostack =
+ gs_alloc_struct((gs_memory_t *) smem, ref_stack, &st_ref_stack,
+ "gs_interp_alloc_stacks(ostack)");
+
+ r_set_size(&stk, REFS_SIZE_OSTACK);
+ ref_stack_init(pos, &stk, OS_GUARD_UNDER, OS_GUARD_OVER, NULL,
+ smem);
+ pos->underflow_error = e_stackunderflow;
+ pos->overflow_error = e_stackoverflow;
+ ref_stack_set_max_count(pos, MAX_OSTACK);
+ }
+
+ {
+ ref_stack *pes = pcst->estack =
+ gs_alloc_struct((gs_memory_t *) smem, ref_stack, &st_ref_stack,
+ "gs_interp_alloc_stacks(estack)");
+ ref euop;
+
+ stk.value.refs += REFS_SIZE_OSTACK;
+ r_set_size(&stk, REFS_SIZE_ESTACK);
+ make_oper(&euop, 0, estack_underflow);
+ ref_stack_init(pes, &stk, ES_GUARD_UNDER, ES_GUARD_OVER, &euop,
+ smem);
+ pes->underflow_error = e_ExecStackUnderflow;
+ pes->overflow_error = e_execstackoverflow;
+/**************** E-STACK EXPANSION IS NYI. ****************/
+ pes->allow_expansion = false;
+ ref_stack_set_max_count(pes, MAX_ESTACK);
+ }
+
+ {
+ ref_stack *pds = pcst->dstack =
+ gs_alloc_struct((gs_memory_t *) smem, ref_stack, &st_ref_stack,
+ "gs_interp_alloc_stacks(dstack)");
+
+ stk.value.refs += REFS_SIZE_ESTACK;
+ r_set_size(&stk, REFS_SIZE_DSTACK);
+ ref_stack_init(pds, &stk, 0, 0, NULL, smem);
+ pds->underflow_error = e_dictstackunderflow;
+ pds->overflow_error = e_dictstackoverflow;
+ ref_stack_set_max_count(pds, MAX_DSTACK);
+ }
+
+#undef REFS_SIZE_OSTACK
+#undef REFS_SIZE_ESTACK
+#undef REFS_SIZE_DSTACK
+ return 0;
+}
+/*
+ * Free the stacks when destroying a context. This is the inverse of
+ * create_stacks.
+ */
+void
+gs_interp_free_stacks(gs_ref_memory_t * smem, gs_context_state_t * pcst)
+{ /* Free the stacks in inverse order of allocation. */
+ ref_stack_free(pcst->dstack, (gs_memory_t *) smem,
+ "gs_interp_free_stacks(dstack)");
+ ref_stack_free(pcst->estack, (gs_memory_t *) smem,
+ "gs_interp_free_stacks(estack)");
+ ref_stack_free(pcst->ostack, (gs_memory_t *) smem,
+ "gs_interp_free_stacks(ostack)");
+}
+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. */
+ gs_gc_root_t epref_root;
+
+ gc_signal = 0;
+ /* Make sure that doref will get relocated properly if */
+ /* a garbage collection happens with epref == &doref. */
+ gs_register_ref_root(imemory_system, &epref_root,
+ (void **)&epref,
+ "gs_call_interpret(epref)");
+ code = (*idmemory->reclaim) (idmemory, -1);
+ gs_unregister_root(imemory_system, &epref_root,
+ "gs_call_interpret(epref)");
+ 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 < 0 && 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 *const 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 || r_type(iosp) >= tx_next_op)
+ ) {
+ 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('.');
+ dlprintf3("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: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;
+ opst: /* Prepare to call a t_oparray procedure in *pvalue. */
+ store_state(iesp);
+ oppr: /* Record the stack depths in case of failure. */
+ if (iesp >= estop - 3)
+ return_with_error_iref(e_execstackoverflow);
+ iesp += 4;
+ osp = iosp; /* ref_stack_count_inline needs this */
+ make_mark_estack(iesp - 3, es_other, oparray_cleanup);
+ make_int(iesp - 2, ref_stack_count_inline(&o_stack));
+ make_int(iesp - 1, ref_stack_count_inline(&d_stack));
+ make_op_estack(iesp, oparray_pop);
+ goto pr;
+ 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 = call_operator(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 = names_index(int_nt, 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 opst;
+ 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 = call_operator(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);
+ /* If the updated string isn't empty, push it back */
+ /* on the e-stack. */
+ {
+ uint size = sbufavailable(&ss);
+
+ if (size) {
+ 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, size);
+ }
+ }
+ 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 oppr;
+ }
+ /* 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.
+ *
+ * The (int) cast in the next line is required
+ * because some compilers don't allow arithmetic
+ * involving two different enumerated types.
+ */
+# define case_xop(xop) case xop - (int)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 = call_operator(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) {
+ names_index_ref(int_nt, 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);
+ /*
+ * We need a real object to return as the error object.
+ * (It only has to last long enough to store in
+ * *perror_object.)
+ */
+ make_null_proc(&ierror.full);
+ ierror.obj = iref = &ierror.full;
+ 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);
+
+}
+
+/* Pop the bookkeeping information for a normal exit from a t_oparray. */
+private int
+oparray_pop(os_ptr op)
+{
+ esp -= 3;
+ return o_pop_estack;
+}
+
+/* Restore the stack pointers after an error inside a t_oparray procedure. */
+/* This procedure is called only from pop_estack. */
+private int
+oparray_cleanup(os_ptr op)
+{ /* esp points just below the cleanup procedure. */
+ es_ptr ep = esp;
+ uint ocount_old = (uint) ep[2].value.intval;
+ uint dcount_old = (uint) ep[3].value.intval;
+ uint ocount = ref_stack_count(&o_stack);
+ uint dcount = ref_stack_count(&d_stack);
+
+ if (ocount > ocount_old)
+ ref_stack_pop(&o_stack, ocount - ocount_old);
+ if (dcount > dcount_old) {
+ ref_stack_pop(&d_stack, dcount - dcount_old);
+ dict_set_top();
+ }
+ return 0;
+}
+
+/* ------ Initialization procedure ------ */
+
+const op_def interp_op_defs[] =
+{
+ /* Internal operators */
+ {"0%interp_exit", interp_exit},
+ {"0%oparray_pop", oparray_pop},
+ op_def_end(0)
+};
diff --git a/pstoraster/interp.h b/pstoraster/interp.h
new file mode 100644
index 000000000..0182b5ad9
--- /dev/null
+++ b/pstoraster/interp.h
@@ -0,0 +1,95 @@
+/* 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 supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Internal interfaces to interp.c and iinit.c */
+
+#ifndef interp_INCLUDED
+# define interp_INCLUDED
+
+/* ------ 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());
+
+#ifndef gs_context_state_t_DEFINED
+# define gs_context_state_t_DEFINED
+typedef struct gs_context_state_s gs_context_state_t;
+
+#endif
+
+/* Define a pointer to the current interpreter context state. */
+extern gs_context_state_t *gs_interp_context_state_current;
+
+/*
+ * Create initial stacks for the interpreter.
+ * We export this for creating new contexts.
+ */
+int gs_interp_alloc_stacks(P2(gs_ref_memory_t * smem,
+ gs_context_state_t * pcst));
+
+/*
+ * Free the stacks when destroying a context. This is the inverse of
+ * create_stacks.
+ */
+void gs_interp_free_stacks(P2(gs_ref_memory_t * smem,
+ gs_context_state_t * pcst));
+
+/* Reset the interpreter. */
+void gs_interp_reset(P0());
+
+#endif /* interp_INCLUDED */
diff --git a/pstoraster/iostack.h b/pstoraster/iostack.h
new file mode 100644
index 000000000..ab3ae86fa
--- /dev/null
+++ b/pstoraster/iostack.h
@@ -0,0 +1,45 @@
+/* Copyright (C) 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Generic operand stack API */
+
+#ifndef iostack_INCLUDED
+# define iostack_INCLUDED
+
+#include "istack.h"
+
+/* Define pointers into the operand stack. */
+typedef s_ptr os_ptr;
+typedef const_s_ptr const_os_ptr;
+
+/* Define the operand stack structure. */
+/* Currently this is just a generic ref stack. */
+typedef struct op_stack_s {
+
+ ref_stack stack; /* the actual operand stack */
+
+} op_stack_t;
+
+#endif /* iostack_INCLUDED */
diff --git a/pstoraster/ipacked.h b/pstoraster/ipacked.h
new file mode 100644
index 000000000..651a11a12
--- /dev/null
+++ b/pstoraster/ipacked.h
@@ -0,0 +1,136 @@
+/* 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 supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Packed array format for Ghostscript */
+
+#ifndef ipacked_INCLUDED
+# define ipacked_INCLUDED
+
+/*
+
+ 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_shift 12
+#define lp_mark (1 << lp_mark_shift)
+#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)
+
+#endif /* ipacked_INCLUDED */
diff --git a/pstoraster/iparam.c b/pstoraster/iparam.c
new file mode 100644
index 000000000..112eba079
--- /dev/null
+++ b/pstoraster/iparam.c
@@ -0,0 +1,1080 @@
+/* Copyright (C) 1993, 1995, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Interpreter implementations of parameter dictionaries */
+#include "memory_.h"
+#include "string_.h"
+#include "ghost.h"
+#include "errors.h"
+#include "oper.h" /* for check_type */
+#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 ================ */
+
+/* Convert a key to a ref. */
+private int
+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);
+}
+
+/* Fill in a gs_param_key_t from a name or int ref. */
+private int
+ref_to_key(const ref * pref, gs_param_key_t * key)
+{
+ if (r_has_type(pref, t_name)) {
+ ref nref;
+
+ name_string_ref(pref, &nref);
+ key->data = nref.value.const_bytes;
+ key->size = r_size(&nref);
+ } else if (r_has_type(pref, t_integer)) {
+ char istr[22]; /* big enough for signed 64-bit value */
+ int len;
+ byte *buf;
+
+ sprintf(istr, "%ld", pref->value.intval);
+ len = strlen(istr);
+ /* GC will take care of freeing this: */
+ buf = ialloc_string(len, "ref_to_key");
+ if (!buf)
+ return_error(e_VMerror);
+ key->data = buf;
+ key->size = len;
+ } else
+ return_error(e_typecheck);
+ return 0;
+}
+
+/* ================ Writing parameters to refs ================ */
+
+/* ---------------- Generic writing procedures ---------------- */
+
+private param_proc_begin_xmit_collection(ref_param_begin_write_collection);
+private param_proc_end_xmit_collection(ref_param_end_write_collection);
+private param_proc_xmit_typed(ref_param_write_typed);
+private param_proc_next_key(ref_param_get_next_key);
+private param_proc_requested(ref_param_requested);
+private const gs_param_list_procs ref_write_procs =
+{
+ ref_param_write_typed,
+ ref_param_begin_write_collection,
+ ref_param_end_write_collection,
+ ref_param_get_next_key,
+ NULL, /* request */
+ ref_param_requested
+};
+private int ref_array_param_requested(P5(const gs_param_list *, gs_param_name,
+ ref *, uint, client_name_t));
+private int ref_param_write(P3(iparam_list *, gs_param_name, const ref *));
+private int ref_param_write_string_value(P2(ref *, const gs_param_string *));
+private int ref_param_write_name_value(P2(ref *, const gs_param_string *));
+private int
+ref_param_make_int(ref * pe, const void *pvalue, uint i)
+{
+ make_int_new(pe, ((const gs_param_int_array *)pvalue)->data[i]);
+ return 0;
+}
+private int
+ref_param_make_float(ref * pe, const void *pvalue, uint i)
+{
+ make_real_new(pe, ((const gs_param_float_array *)pvalue)->data[i]);
+ return 0;
+}
+private int
+ref_param_make_string(ref * pe, const void *pvalue, uint i)
+{
+ return ref_param_write_string_value(pe,
+ &((const gs_param_string_array *)pvalue)->data[i]);
+}
+private int
+ref_param_make_name(ref * pe, const void *pvalue, uint i)
+{
+ return ref_param_write_name_value(pe,
+ &((const gs_param_string_array *)pvalue)->data[i]);
+}
+private int
+ref_param_write_typed_array(gs_param_list * plist, gs_param_name pkey,
+ void *pvalue, uint count,
+ int (*make) (P3(ref *, const void *, uint)))
+{
+ iparam_list *const iplist = (iparam_list *) plist;
+ ref value;
+ uint i;
+ ref *pe;
+ int code;
+
+ if ((code = ref_array_param_requested(plist, pkey, &value, count,
+ "ref_param_write_typed_array")) <= 0)
+ return code;
+ for (i = 0, pe = value.value.refs; i < count; ++i, ++pe)
+ if ((code = (*make) (pe, pvalue, i)) < 0)
+ return code;
+ return ref_param_write(iplist, pkey, &value);
+}
+private int
+ref_param_begin_write_collection(gs_param_list * plist, gs_param_name pkey,
+ gs_param_dict * pvalue,
+ gs_param_collection_type_t coll_type)
+{
+ dict_param_list *dlist =
+ (dict_param_list *) ialloc_bytes(size_of(dict_param_list),
+ "ref_param_begin_write_collection");
+ int code;
+
+ if (dlist == 0)
+ return_error(e_VMerror);
+ if (coll_type != gs_param_collection_array) {
+ ref dref;
+
+ code = dict_create(pvalue->size, &dref);
+ if (code >= 0) {
+ code = dict_param_list_write(dlist, &dref, NULL);
+ dlist->int_keys = coll_type == gs_param_collection_dict_int_keys;
+ }
+ } else {
+ ref aref;
+
+ code = ialloc_ref_array(&aref, a_all, pvalue->size,
+ "ref_param_begin_write_collection");
+ if (code >= 0)
+ code = array_indexed_param_list_write(dlist, &aref, NULL);
+ }
+ if (code < 0)
+ ifree_object(dlist, "ref_param_begin_write_collection");
+ else
+ pvalue->list = (gs_param_list *) dlist;
+ return code;
+}
+private int
+ref_param_end_write_collection(gs_param_list * plist, gs_param_name pkey,
+ gs_param_dict * pvalue)
+{
+ iparam_list *const iplist = (iparam_list *) plist;
+ int code = ref_param_write(iplist, pkey,
+ &((dict_param_list *) pvalue->list)->dict);
+
+ ifree_object(pvalue->list, "ref_param_end_write_collection");
+ return code;
+}
+private int
+ref_param_write_typed(gs_param_list * plist, gs_param_name pkey,
+ gs_param_typed_value * pvalue)
+{
+ iparam_list *const iplist = (iparam_list *) plist;
+ ref value;
+ int code = 0;
+
+ switch (pvalue->type) {
+ case gs_param_type_null:
+ make_null(&value);
+ break;
+ case gs_param_type_bool:
+ make_bool(&value, pvalue->value.b);
+ break;
+ case gs_param_type_int:
+ make_int(&value, pvalue->value.i);
+ break;
+ case gs_param_type_long:
+ make_int(&value, pvalue->value.l);
+ break;
+ case gs_param_type_float:
+ make_real(&value, pvalue->value.f);
+ break;
+ case gs_param_type_string:
+ if (!ref_param_requested(plist, pkey))
+ return 0;
+ code = ref_param_write_string_value(&value, &pvalue->value.s);
+ break;
+ case gs_param_type_name:
+ if (!ref_param_requested(plist, pkey))
+ return 0;
+ code = ref_param_write_name_value(&value, &pvalue->value.n);
+ break;
+ case gs_param_type_int_array:
+ return ref_param_write_typed_array(plist, pkey, &pvalue->value.ia,
+ pvalue->value.ia.size,
+ ref_param_make_int);
+ case gs_param_type_float_array:
+ return ref_param_write_typed_array(plist, pkey, &pvalue->value.fa,
+ pvalue->value.fa.size,
+ ref_param_make_float);
+ case gs_param_type_string_array:
+ return ref_param_write_typed_array(plist, pkey, &pvalue->value.sa,
+ pvalue->value.sa.size,
+ ref_param_make_string);
+ case gs_param_type_name_array:
+ return ref_param_write_typed_array(plist, pkey, &pvalue->value.na,
+ pvalue->value.na.size,
+ ref_param_make_name);
+ case gs_param_type_dict:
+ case gs_param_type_dict_int_keys:
+ case gs_param_type_array:
+ return ref_param_begin_write_collection(plist, pkey,
+ &pvalue->value.d,
+ pvalue->type - gs_param_type_dict);
+ default:
+ return_error(e_typecheck);
+ }
+ if (code < 0)
+ return code;
+ return ref_param_write(iplist, pkey, &value);
+}
+
+/* Check whether a given parameter was requested. */
+private int
+ref_param_requested(const gs_param_list * plist, gs_param_name pkey)
+{
+ const iparam_list *const ciplist = (const iparam_list *)plist;
+ ref kref;
+ ref *ignore_value;
+
+ if (!r_has_type(&ciplist->u.w.wanted, t_dictionary))
+ return -1;
+ if (ref_param_key(ciplist, pkey, &kref) < 0)
+ return -1; /* 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
+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
+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;
+}
+
+/* Prepare to write a name value. */
+private int
+ref_param_write_name_value(ref * pref, const gs_param_string * pvalue)
+{
+ return name_ref(pvalue->data, pvalue->size, pref,
+ (pvalue->persistent ? 0 : 1));
+}
+
+/* Generic routine for writing a ref parameter. */
+private int
+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
+ref_param_write_init(iparam_list * plist, const ref * pwanted)
+{
+ plist->procs = &ref_write_procs;
+ plist->memory = imemory;
+ 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)
+{
+ stack_param_list *const 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++;
+ return 0;
+}
+
+/* Implementation for enumerating parameters on a stack */
+private int /* ret 0 ok, 1 if EOF, or -ve err */
+stack_param_enumerate(iparam_list * plist, gs_param_enumerator_t * penum,
+ gs_param_key_t * key, ref_type * type)
+{
+ int code;
+ stack_param_list *const splist = (stack_param_list *) plist;
+ long index = penum->intval;
+ ref *stack_element;
+
+ do {
+ stack_element =
+ ref_stack_index(splist->pstack, index + 1 + splist->skip);
+ if (!stack_element)
+ return 1;
+ } while (index += 2, !r_has_type(stack_element, t_name));
+ *type = r_type(stack_element);
+ code = ref_to_key(stack_element, key);
+ penum->intval = index;
+ return code;
+}
+
+int
+stack_param_list_write(stack_param_list * plist, ref_stack * pstack,
+ const ref * pwanted)
+{
+ plist->u.w.write = stack_param_write;
+ ref_param_write_init((iparam_list *) plist, pwanted);
+ plist->enumerate = stack_param_enumerate;
+ plist->pstack = pstack;
+ plist->skip = 0;
+ 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);
+}
+
+/* Implementation for enumerating parameters in a dictionary */
+private int /* ret 0 ok, 1 if EOF, or -ve err */
+dict_param_enumerate(iparam_list * plist, gs_param_enumerator_t * penum,
+ gs_param_key_t * key, ref_type * type)
+{
+ ref elt[2];
+ int code;
+ dict_param_list *const pdlist = (dict_param_list *) plist;
+ int index =
+ (penum->intval != 0 ? penum->intval : dict_first(&pdlist->dict));
+
+ index = dict_next(&pdlist->dict, index, elt);
+ if (index < 0)
+ return 1;
+ *type = r_type(&elt[1]);
+ code = ref_to_key(&elt[1], key);
+ penum->intval = index;
+ return code;
+}
+
+int
+dict_param_list_write(dict_param_list * plist, ref * pdict, const ref * pwanted)
+{
+ check_dict_write(*pdict);
+ plist->u.w.write = dict_param_write;
+ plist->enumerate = dict_param_enumerate;
+ ref_param_write_init((iparam_list *) plist, pwanted);
+ plist->dict = *pdict;
+ return 0;
+}
+
+/* Implementation for getting parameters to an indexed array. */
+private int
+array_indexed_param_write(iparam_list * plist, const ref * pkey,
+ const ref * pvalue)
+{
+ const ref *const arr = &((dict_param_list *) plist)->dict;
+ ref *eltp;
+
+ if (!r_has_type(pkey, t_integer))
+ return_error(e_typecheck);
+ check_int_ltu(*pkey, r_size(arr));
+ store_check_dest(arr, pvalue);
+ eltp = arr->value.refs + pkey->value.intval;
+ ref_assign_old(arr, eltp, pvalue, "array_indexed_param_write");
+ return 0;
+}
+int
+array_indexed_param_list_write(dict_param_list * plist, ref * parray,
+ const ref * pwanted)
+{
+ check_array(*parray);
+ check_write(*parray);
+ plist->u.w.write = array_indexed_param_write;
+ ref_param_write_init((iparam_list *) plist, pwanted);
+ plist->dict = *parray;
+ plist->int_keys = true;
+ return 0;
+}
+
+/* ================ Reading refs to parameters ================ */
+
+/* ---------------- Generic reading procedures ---------------- */
+
+private param_proc_begin_xmit_collection(ref_param_begin_read_collection);
+private param_proc_end_xmit_collection(ref_param_end_read_collection);
+private param_proc_xmit_typed(ref_param_read_typed);
+
+/*private param_proc_next_key(ref_param_get_next_key); already dec'ld above */
+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_typed,
+ ref_param_begin_read_collection,
+ ref_param_end_read_collection,
+ ref_param_get_next_key,
+ NULL, /* request */
+ NULL, /* requested */
+ ref_param_read_get_policy,
+ ref_param_read_signal_error,
+ ref_param_read_commit
+};
+private int ref_param_read(P4(iparam_list *, gs_param_name,
+ iparam_loc *, int));
+private int ref_param_read_string_value(P2(const iparam_loc *,
+ gs_param_string *));
+private int ref_param_read_array(P3(iparam_list *, gs_param_name,
+ iparam_loc *));
+
+#define iparam_note_error(loc, code)\
+ gs_note_error(*(loc).presult = code)
+#define iparam_check_type(loc, typ)\
+ if ( !r_has_type((loc).pvalue, typ) )\
+ return iparam_note_error(loc, e_typecheck)
+#define iparam_check_read(loc)\
+ if ( !r_has_attr((loc).pvalue, a_read) )\
+ return iparam_note_error(loc, e_invalidaccess)
+
+private int
+ref_param_read_int_array(gs_param_list * plist, gs_param_name pkey,
+ gs_param_int_array * pvalue)
+{
+ iparam_list *const iplist = (iparam_list *) plist;
+ iparam_loc loc;
+ int code = ref_param_read_array(iplist, pkey, &loc);
+ int *piv;
+ uint size;
+ long 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++) {
+ ref elt;
+
+ array_get(loc.pvalue, i, &elt);
+ if (!r_has_type(&elt, t_integer)) {
+ code = gs_note_error(e_typecheck);
+ break;
+ }
+#if arch_sizeof_int < arch_sizeof_long
+ if (elt.value.intval != (int)elt.value.intval) {
+ code = gs_note_error(e_rangecheck);
+ break;
+ }
+#endif
+ piv[i] = (int)elt.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_list *const iplist = (iparam_list *) plist;
+ iparam_loc loc;
+ ref aref, elt;
+ int code = ref_param_read_array(iplist, pkey, &loc);
+ float *pfv;
+ uint size;
+ long i;
+
+ 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);
+ aref = *loc.pvalue;
+ loc.pvalue = &elt;
+ for (i = 0; code >= 0 && i < size; i++) {
+ array_get(&aref, i, &elt);
+ code = float_param(&elt, pfv + i);
+ }
+ 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_list *const iplist = (iparam_list *) plist;
+ iparam_loc loc;
+ ref aref, elt;
+ int code = ref_param_read_array(iplist, pkey, &loc);
+ gs_param_string *psv;
+ uint size;
+ long i;
+
+ if (code != 0)
+ return code;
+ 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);
+ aref = *loc.pvalue;
+ loc.pvalue = &elt;
+ for (i = 0; code >= 0 && i < size; i++) {
+ array_get(&aref, i, &elt);
+ 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_collection(gs_param_list * plist, gs_param_name pkey,
+ gs_param_dict * pvalue,
+ gs_param_collection_type_t coll_type)
+{
+ iparam_list *const iplist = (iparam_list *) plist;
+ iparam_loc loc;
+ bool int_keys = coll_type != 0;
+ int code = ref_param_read(iplist, pkey, &loc, -1);
+ dict_param_list *dlist;
+
+ if (code != 0)
+ return code;
+ dlist = (dict_param_list *)
+ ialloc_bytes(size_of(dict_param_list),
+ "ref_param_begin_read_collection");
+ if (dlist == 0)
+ return_error(e_VMerror);
+ if (r_has_type(loc.pvalue, t_dictionary)) {
+ code = dict_param_list_read(dlist, loc.pvalue, NULL, false);
+ dlist->int_keys = int_keys;
+ if (code >= 0)
+ pvalue->size = dict_length(loc.pvalue);
+ } else if (int_keys && r_is_array(loc.pvalue)) {
+ code = array_indexed_param_list_read(dlist, loc.pvalue, NULL, false);
+ if (code >= 0)
+ pvalue->size = r_size(loc.pvalue);
+ } else
+ code = gs_note_error(e_typecheck);
+ if (code < 0) {
+ ifree_object(dlist, "ref_param_begin_write_collection");
+ return iparam_note_error(loc, code);
+ }
+ pvalue->list = (gs_param_list *) dlist;
+ return 0;
+}
+private int
+ref_param_end_read_collection(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_collection");
+ return 0;
+}
+private int
+ref_param_read_typed(gs_param_list * plist, gs_param_name pkey,
+ gs_param_typed_value * pvalue)
+{
+ iparam_list *const iplist = (iparam_list *) plist;
+ iparam_loc loc;
+ ref elt;
+ int code = ref_param_read(iplist, pkey, &loc, -1);
+
+ if (code != 0)
+ return code;
+ switch (r_type(loc.pvalue)) {
+ case t_array:
+ case t_mixedarray:
+ case t_shortarray:
+ iparam_check_read(loc);
+ if (r_size(loc.pvalue) <= 0) {
+ /* 0-length array; can't get type info */
+ pvalue->type = gs_param_type_array;
+ pvalue->value.d.list = 0;
+ pvalue->value.d.size = 0;
+ return 0;
+ }
+ /* Get array type based on type of 1st element of array */
+ array_get(loc.pvalue, 0, &elt);
+ switch (r_type(&elt)) { /* redundant key lookup, but cached */
+ case t_integer:
+ pvalue->type = gs_param_type_int_array;
+ return ref_param_read_int_array(plist, pkey, &pvalue->value.ia);
+ case t_real:
+ pvalue->type = gs_param_type_float_array;
+ return ref_param_read_float_array(plist, pkey, &pvalue->value.fa);
+ case t_string:
+ pvalue->type = gs_param_type_string_array;
+ return ref_param_read_string_array(plist, pkey, &pvalue->value.sa);
+ case t_name:
+ pvalue->type = gs_param_type_name_array;
+ return ref_param_read_string_array(plist, pkey, &pvalue->value.na);
+ default:
+ break;
+ }
+ return gs_note_error(e_typecheck);
+ case t_boolean:
+ pvalue->type = gs_param_type_bool;
+ pvalue->value.b = loc.pvalue->value.boolval;
+ return 0;
+ case t_dictionary:
+ code = ref_param_begin_read_collection(plist, pkey,
+ &pvalue->value.d, gs_param_collection_dict_any);
+ if (code < 0)
+ return code;
+ pvalue->type = gs_param_type_dict;
+
+ /* fixup new dict's type & int_keys field if contents have int keys */
+ {
+ gs_param_enumerator_t enumr;
+ gs_param_key_t key;
+ ref_type keytype;
+
+ param_init_enumerator(&enumr);
+ if (!(*((iparam_list *) plist)->enumerate)
+ ((iparam_list *) pvalue->value.d.list, &enumr, &key, &keytype)
+ && keytype == t_integer) {
+ ((dict_param_list *) pvalue->value.d.list)->int_keys = 1;
+ pvalue->type = gs_param_type_dict_int_keys;
+ }
+ }
+ return 0;
+ case t_integer:
+ pvalue->type = gs_param_type_long;
+ pvalue->value.l = loc.pvalue->value.intval;
+ return 0;
+ case t_name:
+ pvalue->type = gs_param_type_name;
+ return ref_param_read_string_value(&loc, &pvalue->value.n);
+ case t_null:
+ pvalue->type = gs_param_type_null;
+ return 0;
+ case t_real:
+ pvalue->value.f = loc.pvalue->value.realval;
+ pvalue->type = gs_param_type_float;
+ return 0;
+ case t_string:
+ pvalue->type = gs_param_type_string;
+ return ref_param_read_string_value(&loc, &pvalue->value.s);
+ default:
+ break;
+ }
+ return gs_note_error(e_typecheck);
+}
+
+private int
+ref_param_read_get_policy(gs_param_list * plist, gs_param_name pkey)
+{
+ iparam_list *const iplist = (iparam_list *) plist;
+ ref *pvalue;
+
+ if (!(r_has_type(&iplist->u.r.policies, t_dictionary) &&
+ dict_find_string(&iplist->u.r.policies, pkey, &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_list *const iplist = (iparam_list *) plist;
+ 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)
+{
+ iparam_list *const iplist = (iparam_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;
+}
+private int
+ref_param_get_next_key(gs_param_list * plist, gs_param_enumerator_t * penum,
+ gs_param_key_t * key)
+{
+ ref_type keytype; /* result not needed here */
+ iparam_list *const pilist = (iparam_list *) plist;
+
+ return (*pilist->enumerate) (pilist, penum, key, &keytype);
+}
+
+/* ---------------- Internal routines ---------------- */
+
+/* Read a string value. */
+private int
+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:
+ return iparam_note_error(*ploc, e_typecheck);
+ }
+ return 0;
+}
+
+/* Read an array (or packed array) parameter. */
+private int
+ref_param_read_array(iparam_list * plist, gs_param_name pkey, iparam_loc * ploc)
+{
+ int code = ref_param_read(plist, pkey, ploc, -1);
+
+ if (code != 0)
+ return code;
+ if (!r_is_array(ploc->pvalue))
+ return iparam_note_error(*ploc, e_typecheck);
+ iparam_check_read(*ploc);
+ return 0;
+}
+
+/* Generic routine for reading a ref parameter. */
+private int
+ref_param_read(iparam_list * plist, gs_param_name pkey, iparam_loc * ploc,
+ int type)
+{
+ iparam_list *const iplist = (iparam_list *) plist;
+ 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 ---------------- */
+
+/* Implementation for putting parameters from an empty collection. */
+private int
+empty_param_read(iparam_list * plist, const ref * pkey, iparam_loc * ploc)
+{
+ return 1;
+}
+
+/* Initialize for reading parameters. */
+private int
+ref_param_read_init(iparam_list * plist, uint count, const ref * ppolicies,
+ bool require_all)
+{
+ plist->procs = &ref_read_procs;
+ plist->memory = imemory;
+ 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 indexed array. */
+private int
+array_indexed_param_read(iparam_list * plist, const ref * pkey, iparam_loc * ploc)
+{
+ ref *const arr = &((dict_param_list *) plist)->dict;
+
+ check_type(*pkey, t_integer);
+ if (pkey->value.intval < 0 || pkey->value.intval >= r_size(arr))
+ return 1;
+ ploc->pvalue = arr->value.refs + pkey->value.intval;
+ ploc->presult = &plist->results[pkey->value.intval];
+ *ploc->presult = 1;
+ return 0;
+}
+int
+array_indexed_param_list_read(dict_param_list * plist, const ref * parray,
+ const ref * ppolicies, bool require_all)
+{
+ iparam_list *const iplist = (iparam_list *) plist;
+ int code;
+
+ check_read_type(*parray, t_array);
+ plist->u.r.read = array_indexed_param_read;
+ plist->dict = *parray;
+ code = ref_param_read_init(iplist, r_size(parray), ppolicies,
+ require_all);
+ plist->int_keys = true;
+ return code;
+}
+
+/* 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;
+}
+
+/* Implementation for enumerating parameters in an array */
+private int /* ret 0 ok, 1 if EOF, or -ve err */
+array_param_enumerate(iparam_list * plist, gs_param_enumerator_t * penum,
+ gs_param_key_t * key, ref_type * type)
+{
+ int index = penum->intval;
+ ref *bot = ((array_param_list *) plist)->bot;
+ ref *ptr = bot + index;
+ ref *top = ((array_param_list *) plist)->top;
+
+ for (; ptr < top; ptr += 2) {
+ index += 2;
+
+ if (r_has_type(ptr, t_name)) {
+ int code = ref_to_key(ptr, key);
+
+ *type = r_type(ptr);
+ penum->intval = index;
+ return code;
+ }
+ }
+ return 1;
+}
+
+int
+array_param_list_read(array_param_list * plist, ref * bot, uint count,
+ const ref * ppolicies, bool require_all)
+{
+ iparam_list *const iplist = (iparam_list *) plist;
+
+ if (count & 1)
+ return_error(e_rangecheck);
+ plist->u.r.read = array_param_read;
+ plist->enumerate = array_param_enumerate;
+ 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)
+{
+ stack_param_list *const 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;
+ }
+ }
+ return 1;
+}
+int
+stack_param_list_read(stack_param_list * plist, ref_stack * pstack, uint skip,
+ const ref * ppolicies, bool require_all)
+{
+ iparam_list *const iplist = (iparam_list *) plist;
+ uint count = ref_stack_counttomark(pstack);
+
+ if (count == 0)
+ return_error(e_unmatchedmark);
+ count -= skip + 1;
+ if (count & 1)
+ return_error(e_rangecheck);
+ plist->u.r.read = stack_param_read;
+ plist->enumerate = stack_param_enumerate;
+ 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)
+{
+ ref const *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)];
+ *ploc->presult = 1;
+ return 0;
+}
+int
+dict_param_list_read(dict_param_list * plist, const ref * pdict,
+ const ref * ppolicies, bool require_all)
+{
+ iparam_list *const iplist = (iparam_list *) plist;
+ uint count;
+
+ if (pdict == 0) {
+ plist->u.r.read = empty_param_read;
+ count = 0;
+ } else {
+ check_dict_read(*pdict);
+ plist->u.r.read = dict_param_read;
+ plist->dict = *pdict;
+ count = dict_max_index(pdict) + 1;
+ }
+ plist->enumerate = dict_param_enumerate;
+ return ref_param_read_init(iplist, count, ppolicies, require_all);
+}
diff --git a/pstoraster/iparam.h b/pstoraster/iparam.h
new file mode 100644
index 000000000..bec2b4160
--- /dev/null
+++ b/pstoraster/iparam.h
@@ -0,0 +1,118 @@
+/* Copyright (C) 1993, 1995, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Requires ialloc.h, istack.h */
+
+#ifndef iparam_INCLUDED
+# define iparam_INCLUDED
+
+#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 (*enumerate)(P4(iparam_list *, gs_param_enumerator_t *, gs_param_key_t *, ref_type *));\
+ 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; /* dictionary or array */
+} 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 */
+/*
+ * For dict_param_list_read (only), the second parameter may be NULL,
+ * equivalent to an empty dictionary.
+ * The 3rd (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_indexed_param_list_read(P4(dict_param_list *, const ref * /*t_*array */ ,
+ const ref *, bool));
+int array_indexed_param_list_write(P3(dict_param_list *, ref * /*t_*array */ ,
+ 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")
+
+#endif /* iparam_INCLUDED */
diff --git a/pstoraster/iparray.h b/pstoraster/iparray.h
new file mode 100644
index 000000000..ce86b5f0e
--- /dev/null
+++ b/pstoraster/iparray.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 supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Packed array constructor for Ghostscript */
+
+#ifndef iparray_INCLUDED
+# define iparray_INCLUDED
+
+/*
+ * 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));
+
+#endif /* iparray_INCLUDED */
diff --git a/pstoraster/ireclaim.c b/pstoraster/ireclaim.c
new file mode 100644
index 000000000..eb2433132
--- /dev/null
+++ b/pstoraster/ireclaim.c
@@ -0,0 +1,163 @@
+/* Copyright (C) 1995, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Interpreter's interface to garbage collector */
+#include "ghost.h"
+#include "errors.h"
+#include "gsstruct.h"
+#include "iastate.h"
+#include "icontext.h"
+#include "interp.h"
+#include "isave.h" /* for isstate.h */
+#include "isstate.h" /* for mem->saved->state */
+#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 preparation and cleanup routines. */
+extern void ialloc_gc_prepare(P1(gs_ref_memory_t *));
+
+/* 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;
+ gs_vmreclaim(dmem, global);
+
+ ialloc_set_limit(mem);
+ ialloc_reset_requested(dmem);
+ return 0;
+}
+
+/* Interpreter entry to garbage collector. */
+private void
+gs_vmreclaim(gs_dual_memory_t * dmem, bool global)
+{
+ gs_ref_memory_t *lmem = dmem->space_local;
+ gs_ref_memory_t *gmem = dmem->space_global;
+ gs_ref_memory_t *smem = dmem->space_system;
+ int code = context_state_store(gs_interp_context_state_current);
+
+/****** ABORT IF code < 0 ******/
+ alloc_close_chunk(lmem);
+ if (gmem != lmem)
+ alloc_close_chunk(gmem);
+ alloc_close_chunk(smem);
+
+ /* Prune the file list so it won't retain potentially collectible */
+ /* files. */
+
+ {
+ int i;
+
+ for (i = (global ? i_vm_system : i_vm_local);
+ i < countof(dmem->spaces.indexed);
+ ++i
+ ) {
+ gs_ref_memory_t *mem = dmem->spaces.indexed[i];
+
+ if (mem == 0 || (i > 0 && mem == dmem->spaces.indexed[i - 1]))
+ continue;
+ for (;; mem = &mem->saved->state) {
+ ialloc_gc_prepare(mem);
+ if (mem->saved == 0)
+ break;
+ }
+ }
+ }
+
+ /* Do the actual collection. */
+
+ gs_reclaim(&dmem->spaces, global);
+
+ /* Reload the context state. */
+
+ code = context_state_load(gs_interp_context_state_current);
+/****** ABORT IF code < 0 ******/
+
+ /* Update the cached value pointers in names. */
+
+ dicts_gc_cleanup();
+
+ /* Reopen the active chunks. */
+
+ alloc_open_chunk(smem);
+ if (gmem != lmem)
+ alloc_open_chunk(gmem);
+ alloc_open_chunk(lmem);
+
+ /* Update caches not handled by context_state_load. */
+
+ {
+ uint dcount = ref_stack_count(&d_stack);
+
+ *systemdict = *ref_stack_index(&d_stack, dcount - 1);
+ }
+}
+
+/* ------ Initialization procedure ------ */
+
+const op_def ireclaim_l2_op_defs[] =
+{
+ op_def_end(ireclaim_init)
+};
diff --git a/pstoraster/iref.h b/pstoraster/iref.h
new file mode 100644
index 000000000..ab273fd2e
--- /dev/null
+++ b/pstoraster/iref.h
@@ -0,0 +1,437 @@
+/* Copyright (C) 1989, 1995, 1996, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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 log2_sizeof_ref_packed arch_log2_sizeof_short
+#define sizeof_ref_packed (1 << log2_sizeof_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:
+ * - type_name_strings, type_print_strings, and type_properties below;
+ * - 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.
+ */
+ 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 a table giving properties of types, similar to the table used
+ * by the isxxx functions (macros) in <ctype.h>.
+ */
+#define _rtype_uses_access 1 /* type uses w/r/x attributes */
+#define _rtype_uses_size 2
+#define _rtype_is_null 4
+#define _rtype_is_dictionary 8
+extern const byte ref_type_properties[1 << 6]; /* r_type_bits */
+
+#define ref_type_properties_data\
+ 0, /* t__invalid */\
+ 0, /* t_boolean */\
+ _rtype_uses_access | _rtype_is_dictionary, /* t_dictionary */\
+ _rtype_uses_access | _rtype_uses_size, /* t_file */\
+ _rtype_uses_access | _rtype_uses_size, /* t_array */\
+ _rtype_uses_access | _rtype_uses_size, /* t_mixedarray */\
+ _rtype_uses_access | _rtype_uses_size, /* t_shortarray */\
+ _rtype_uses_access | _rtype_uses_size, /* (unused array type) */\
+ 0, /* t_struct */\
+ _rtype_uses_access, /* t_astruct */\
+ 0, /* t_fontID */\
+ 0, /* t_integer */\
+ 0, /* t_mark */\
+ _rtype_uses_size, /* t_name */\
+ _rtype_is_null, /* t_null, uses size only on e-stack */\
+ _rtype_uses_size, /* t_operator */\
+ 0, /* t_real */\
+ 0, /* t_save */\
+ _rtype_uses_access | _rtype_uses_size, /* t_string */\
+ _rtype_uses_access, /* t_device */\
+ _rtype_uses_size, /* t_oparray */\
+ /*\
+ * The remaining types are the extended pseudo-types used by the\
+ * interpreter for operators. We need to fill up the table.\
+ */\
+ _rtype_uses_size,_rtype_uses_size,_rtype_uses_size, /*24*/\
+ _rtype_uses_size,_rtype_uses_size,_rtype_uses_size,_rtype_uses_size, /*28*/\
+ _rtype_uses_size,_rtype_uses_size,_rtype_uses_size,_rtype_uses_size, /*32*/\
+ _rtype_uses_size,_rtype_uses_size,_rtype_uses_size,_rtype_uses_size, /*36*/\
+ _rtype_uses_size,_rtype_uses_size,_rtype_uses_size,_rtype_uses_size, /*40*/\
+ _rtype_uses_size,_rtype_uses_size,_rtype_uses_size,_rtype_uses_size, /*44*/\
+ _rtype_uses_size,_rtype_uses_size,_rtype_uses_size,_rtype_uses_size, /*48*/\
+ _rtype_uses_size,_rtype_uses_size,_rtype_uses_size,_rtype_uses_size, /*52*/\
+ _rtype_uses_size,_rtype_uses_size,_rtype_uses_size,_rtype_uses_size, /*56*/\
+ _rtype_uses_size,_rtype_uses_size,_rtype_uses_size,_rtype_uses_size, /*60*/\
+ _rtype_uses_size,_rtype_uses_size,_rtype_uses_size,_rtype_uses_size /*64 */
+#define _rtype_has(rtype,props)\
+ ((ref_type_properties[rtype] & (props)) != 0)
+#define ref_type_uses_access(rtype)\
+ _rtype_has(rtype, _rtype_uses_access)
+#define ref_type_uses_size(rtype)\
+ _rtype_has(rtype, _rtype_uses_size)
+#define ref_type_uses_size_or_null(rtype)\
+ _rtype_has(rtype, _rtype_uses_size | _rtype_is_null)
+/*
+ * 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 *));
+
+/* 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.
+ * In order to convince the compiler, we have to do the cast before
+ * indexing into the structure.
+ */
+#define r_type_xe(rp)\
+ type_xe_(((const ushort *)(rp))[offset_of(ref, tas.type_attrs) / sizeof(ushort)])
+#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 data for initializing an empty array or string. */
+#define empty_ref_data(type, attrs)\
+ { /*tas*/ { /*type_attrs*/ ((type) << r_type_shift) | (attrs),\
+ /*rsize*/ 0 } }
+
+/* Define the size of a ref. */
+#define arch_sizeof_ref sizeof(ref)
+/* Define the required alignment for refs. */
+/* We assume all alignment values are powers of 2. */
+#define arch_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..803371189
--- /dev/null
+++ b/pstoraster/isave.c
@@ -0,0 +1,1068 @@
+/* Copyright (C) 1993, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Save/restore manager for Ghostscript interpreter */
+#include "ghost.h"
+#include "memory_.h"
+#include "errors.h"
+#include "gsexit.h"
+#include "gsstruct.h"
+#include "stream.h" /* for linking for forgetsave */
+#include "iastate.h"
+#include "inamedef.h"
+#include "iname.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 save/restore routines */
+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;
+
+/* Some compilers try to substitute macro args in string literals! */
+#define print_save(str, spacen, sav)\
+ if_debug5('u', "[u]%s space %u 0x%lx: cdata = 0x%lx, id = %lu\n",\
+ str, spacen, (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.
+ */
+
+/*
+ * 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)
+ ENUM_RETURN((byte *) ptr->where - ptr->offset);
+else
+ ENUM_RETURN_REF(ptr->where);
+case 2:
+ENUM_RETURN_REF(&ptr->contents);
+ENUM_PTRS_END
+private RELOC_PTRS_BEGIN(change_reloc_ptrs)
+{
+ RELOC_VAR(ptr->next);
+ switch (ptr->offset) {
+ case ac_offset_static:
+ break;
+ case ac_offset_ref:
+ RELOC_REF_PTR_VAR(ptr->where);
+ break;
+ default:
+ {
+ byte *obj = (byte *) ptr->where - ptr->offset;
+
+ RELOC_VAR(obj);
+ ptr->where = (ref_packed *) (obj + ptr->offset);
+ }
+ break;
+ }
+ if (r_is_packed(&ptr->contents))
+ r_clear_pmark((ref_packed *) & ptr->contents);
+ else {
+ RELOC_REF_VAR(ptr->contents);
+ 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;
+ alloc_set_not_in_save(dmem);
+}
+
+/* Record that we are in a save. */
+void
+alloc_set_in_save(gs_dual_memory_t *dmem)
+{
+ dmem->test_mask = dmem->new_mask = l_new;
+}
+
+/* Record that we are not in a save. */
+void
+alloc_set_not_in_save(gs_dual_memory_t *dmem)
+{
+ dmem->test_mask = ~0;
+ dmem->new_mask = 0;
+}
+
+/* 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;
+
+ 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++;
+ alloc_set_in_save(dmem);
+ 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_raw_alloc_struct_immovable(mem->parent, &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;
+ if_debug2('u', "[u%u]file_save 0x%lx\n",
+ mem->space, (ulong) mem->streams);
+ mem->streams = 0;
+ 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')) {
+ dlprintf1("[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 id of) the innermost externally visible save object, */
+/* i.e., the innermost save with a non-zero ID. */
+ulong
+alloc_save_current_id(const gs_dual_memory_t * dmem)
+{
+ const alloc_save_t *save = dmem->space_local->saved;
+
+ while (save != 0 && save->id == 0)
+ save = save->state.saved;
+ return save->id;
+}
+alloc_save_t *
+alloc_save_current(const gs_dual_memory_t * dmem)
+{
+ return alloc_find_save(dmem, alloc_save_current_id(dmem));
+}
+
+/* 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),
+ * and there is only one context using this global VM
+ * (the normal case, in which global VM is saved by the
+ * outermost save), 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 &&
+ dmem->space_global->num_contexts == 1
+ ) {
+ 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);
+ }
+ alloc_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')) {
+ dlputs("[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)
+{
+#ifdef DEBUG
+ if (mem) {
+ /* Note restoring of the file list. */
+ if_debug4('u', "[u%u]file_restore 0x%lx => 0x%lx for 0x%lx\n",
+ mem->space, (ulong)mem->streams,
+ (ulong)sprev->state.streams, (ulong) sprev);
+ }
+#endif
+
+ /* Remove entries from font and character caches. */
+ font_restore(sprev);
+
+ /* Adjust the name table. */
+ if (sprev->restore_names)
+ names_restore(the_gs_name_table, sprev);
+}
+
+/* Release memory for a restore. */
+private void
+restore_free(gs_ref_memory_t * mem)
+{
+ /* Free chunks allocated since the save. */
+ gs_free_all((gs_memory_t *) mem);
+}
+
+/* Forget a save, by merging this level with the next outer one. */
+private void file_forget_save(P1(gs_ref_memory_t *));
+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(mem);
+ combine_space(mem); /* combine memory */
+ } else {
+ forget_changes(mem);
+ save_set_new(mem, false);
+ file_forget_save(mem);
+ 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);
+ file_forget_save(mem);
+ combine_space(mem);
+ }
+ alloc_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->lost.objects += omem->lost.objects;
+ mem->lost.refs += omem->lost.refs;
+ mem->lost.strings += omem->lost.strings;
+ 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;
+}
+/* Update the streams list when forgetting a save. */
+private void
+file_forget_save(gs_ref_memory_t * mem)
+{
+ const alloc_save_t *save = mem->saved;
+ stream *streams = mem->streams;
+ stream *saved_streams = save->state.streams;
+
+ if_debug4('u', "[u%d]file_forget_save 0x%lx + 0x%lx for 0x%lx\n",
+ mem->space, (ulong) streams, (ulong) saved_streams,
+ (ulong) save);
+ if (streams == 0)
+ mem->streams = saved_streams;
+ else if (saved_streams != 0) {
+ while (streams->next != 0)
+ streams = streams->next;
+ streams->next = saved_streams;
+ saved_streams->prev = streams;
+ }
+}
+
+/* ------ 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. */
+ if (to_new)
+ while (1) {
+ if (r_is_packed(prp))
+ prp++;
+ else {
+ ((ref *) prp)->tas.type_attrs |= l_new;
+ prp += packed_per_ref;
+ if (prp >= next)
+ break;
+ }
+ } else
+ while (1) {
+ if (r_is_packed(prp))
+ prp++;
+ else {
+ ((ref *) prp)->tas.type_attrs &= ~l_new;
+ prp += packed_per_ref;
+ if (prp >= next)
+ break;
+ }
+ }
+ } 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)) {
+ ref *const rp = (ref *) prp;
+
+ rp->tas.type_attrs =
+ (rp->tas.type_attrs & ~l_new) + new;
+ }
+ }
+}
diff --git a/pstoraster/isave.h b/pstoraster/isave.h
new file mode 100644
index 000000000..147863ec8
--- /dev/null
+++ b/pstoraster/isave.h
@@ -0,0 +1,133 @@
+/* Copyright (C) 1991, 1995, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Requires imemory.h */
+
+#ifndef isave_INCLUDED
+# define isave_INCLUDED
+
+/*
+ * 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 id of) the innermost externally visible save object. */
+ulong alloc_save_current_id(P1(const gs_dual_memory_t *));
+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));
+
+/* ------ Internals ------ */
+
+/* Record that we are in a save. */
+void alloc_set_in_save(P1(gs_dual_memory_t *));
+
+/* Record that we are not in a save. */
+void alloc_set_not_in_save(P1(gs_dual_memory_t *));
+
+#endif /* isave_INCLUDED */
diff --git a/pstoraster/iscan.c b/pstoraster/iscan.c
new file mode 100644
index 000000000..59a7574b8
--- /dev/null
+++ b/pstoraster/iscan.c
@@ -0,0 +1,1131 @@
+/*
+ Copyright 1993-2000 by Easy Software Products.
+ Copyright 1989, 1995, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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 RELOC_REF_VAR */
+#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 *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(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(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:
+if (ssptr->s_scan_type == scanning_none ||
+ !ssptr->s_da.is_dynamic
+)
+ ENUM_RETURN(0);
+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;
+ENUM_RETURN_REF(&ssarray);
+ENUM_PTRS_END
+private RELOC_PTRS_BEGIN(scanner_reloc_ptrs)
+{
+ if (ssptr->s_scan_type != scanning_none && ssptr->s_da.is_dynamic) {
+ RELOC_STRING_VAR(ssptr->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) {
+ RELOC_REF_VAR(ssarray);
+ r_clear_attrs(&ssarray, l_mark);
+ }
+}
+RELOC_PTRS_END
+#undef ssptr
+/* Structure type */
+public_st_scanner_state();
+
+/* 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('%')) {
+ dlprintf2("[%%%%%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('%')) {
+ dlprintf2("[%% %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(stream * s, ref * pref, scanner_state * pstate)
+{
+ ref *myref = pref;
+ int retcode = 0;
+ 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];
+ const byte *const decoder = scan_char_decoder;
+ int status;
+ int sign;
+ const bool check_only = pstate->s_check_only;
+ 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;
+ sstate.s_check_only = check_only;
+ 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:
+ goto top;
+ 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);
+ if (!check_only)
+ da.next = w.ptr + 1;
+ switch (status) {
+ case 0:
+ status = s->end_status;
+ if (status < 0) {
+ if (status == EOFC) {
+ if (check_only) {
+ retcode = scan_Refill;
+ scan_type = scanning_string;
+ goto suspend;
+ } else
+ sreturn(e_syntaxerror);
+ }
+ break;
+ }
+ s_process_read_buf(s);
+ continue;
+ case 1:
+ if (!check_only) {
+ 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 (check_only) {
+ make_empty_array(myref, 0);
+ ref_stack_pop(&o_stack, size);
+ } else 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;
+ }
+ break;
+ }
+#undef comment_line
+ /*NOTREACHED */
+ case EOFC:
+ if (pstack != 0) {
+ if (check_only)
+ goto pause;
+ 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 '.':
+ sign = 0;
+ nr: /*
+ * Skip a leading sign, if any, by conditionally passing
+ * sptr + 1 rather than sptr. Also, if the last character
+ * in the buffer is a CR, we must stop the scan 1 character
+ * early, to be sure that we can test for CR+LF within the
+ * buffer, by passing endptr rather than endptr + 1.
+ */
+ retcode = scan_number(sptr + (sign & 1),
+ endptr /*(*endptr == char_CR ? endptr : endptr + 1) */ ,
+ sign, myref, &newptr);
+ 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 '+':
+ sign = 1;
+ goto nr;
+ case '-':
+ sign = -1;
+ 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. */
+ da.base = (byte *) sptr;
+ da.is_dynamic = false;
+ {
+ const byte *endp1 = endptr - 1;
+
+ 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) {
+ 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..cb174ad8b
--- /dev/null
+++ b/pstoraster/iscan.h
@@ -0,0 +1,161 @@
+/* Copyright (C) 1992, 1996, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Requires gsstruct.h, ostack.h, stream.h */
+
+#ifndef iscan_INCLUDED
+# define iscan_INCLUDED
+
+#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 *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) */
+ bool s_check_only; /* true if just checking for syntax errors */
+ /* and complete statements (no value) */
+ 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_check(pstate, from_string, check_only)\
+ ((pstate)->s_scan_type = scanning_none,\
+ (pstate)->s_pstack = 0,\
+ (pstate)->s_from_string = from_string,\
+ (pstate)->s_check_only = check_only)
+#define scanner_state_init(pstate, from_string)\
+ scanner_state_init_check(pstate, from_string, false)
+
+/*
+ * 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));
+
+#endif /* iscan_INCLUDED */
diff --git a/pstoraster/iscanbin.c b/pstoraster/iscanbin.c
new file mode 100644
index 000000000..63cdcc157
--- /dev/null
+++ b/pstoraster/iscanbin.c
@@ -0,0 +1,768 @@
+/* Copyright (C) 1989, 1992, 1993, 1994, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Ghostscript binary token scanner and writer */
+#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 "btoken.h"
+#include "ibnum.h"
+
+/* Define the binary token types. */
+typedef enum {
+ BT_SEQ = 128, /* binary object sequence: */
+ BT_SEQ_IEEE_MSB = 128, /* IEEE floats, big-endian */
+ BT_SEQ_IEEE_LSB = 129, /* IEEE float, little-endian */
+ BT_SEQ_NATIVE_MSB = 130, /* native floats, big-endian */
+ BT_SEQ_NATIVE_LSB = 131, /* native floats, 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
+} bin_token_type_t;
+
+#define MIN_BIN_TOKEN_TYPE 128
+#define MAX_BIN_TOKEN_TYPE 159
+#define NUM_BIN_TOKEN_TYPES (MAX_BIN_TOKEN_TYPE - MIN_BIN_TOKEN_TYPE + 1)
+
+/* Define the number of required initial bytes for binary tokens. */
+private const byte bin_token_bytes[NUM_BIN_TOKEN_TYPES] =
+{
+ 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 the number formats for those binary tokens that need them. */
+private const byte bin_token_num_formats[NUM_BIN_TOKEN_TYPES] =
+{
+ num_msb + num_float_IEEE, /* BT_SEQ_IEEE_MSB */
+ num_lsb + num_float_IEEE, /* BT_SEQ_IEEE_LSB */
+ num_msb + num_float_native, /* BT_SEQ_NATIVE_MSB */
+ num_lsb + num_float_native, /* BT_SEQ_NATIVE_LSB */
+ num_msb + num_int32, /* BT_INT32_MSB */
+ num_lsb + num_int32, /* BT_INT32_LSB */
+ num_msb + num_int16, /* BT_INT16_MSB */
+ num_lsb + num_int16, /* BT_INT16_LSB */
+ 0, /* BT_INT8, not used */
+ 0, /* BT_FIXED, not used */
+ num_msb + num_float_IEEE, /* BT_FLOAT_IEEE_MSB */
+ num_lsb + num_float_IEEE, /* BT_FLOAT_IEEE_LSB */
+ num_float_native, /* BT_FLOAT_NATIVE */
+ 0, /* BT_BOOLEAN, not used */
+ 0, /* BT_STRING_256, not used */
+ num_msb, /* BT_STRING_64K_MSB */
+ num_lsb /* BT_STRING_64K_LSB */
+ /* rest not used */
+};
+
+/* Binary object sequence element types */
+typedef enum {
+ BS_TYPE_NULL = 0,
+ BS_TYPE_INTEGER = 1,
+ BS_TYPE_REAL = 2,
+ BS_TYPE_NAME = 3,
+ BS_TYPE_BOOLEAN = 4,
+ BS_TYPE_STRING = 5,
+ BS_TYPE_EVAL_NAME = 6,
+ BS_TYPE_ARRAY = 9,
+ BS_TYPE_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_TYPE_DICTIONARY = 15
+} bin_seq_type_t;
+
+#define BS_EXECUTABLE 128
+#define SIZEOF_BIN_SEQ_OBJ ((uint)8)
+
+/* Current binary format (in iscan.c) */
+extern ref ref_binary_object_format;
+
+/* 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). */
+int
+scan_binary_token(stream * s, ref * pref, scanner_state * pstate)
+{
+ scan_binary_state *const pbs = &pstate->s_ss.binary;
+
+ s_declare_inline(s, p, rlimit);
+ int num_format, code;
+ uint arg;
+ uint wanted;
+ uint rcnt;
+
+ s_begin_inline(s, p, rlimit);
+ wanted = bin_token_bytes[*p - MIN_BIN_TOKEN_TYPE] - 1;
+ rcnt = rlimit - p;
+ if (rcnt < wanted) {
+ s_end_inline(s, p - 1, rlimit);
+ pstate->s_scan_type = scanning_none;
+ return scan_Refill;
+ }
+ num_format = bin_token_num_formats[*p - MIN_BIN_TOKEN_TYPE];
+ switch (*p) {
+ case BT_SEQ_IEEE_MSB:
+ case BT_SEQ_IEEE_LSB:
+ case BT_SEQ_NATIVE_MSB:
+ case BT_SEQ_NATIVE_LSB:{
+ uint top_size = p[1];
+ uint hsize, size;
+
+ pbs->num_format = num_format;
+ if (top_size == 0) {
+ /* Extended header (2-byte array size, 4-byte length) */
+ ulong lsize;
+
+ if (rcnt < 7) {
+ s_end_inline(s, p - 1, rlimit);
+ pstate->s_scan_type = scanning_none;
+ return scan_Refill;
+ }
+ top_size = sdecodeushort(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 = sdecodeushort(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_INT8:
+ make_int(pref, (p[1] ^ 128) - 128);
+ s_end_inline(s, p + 1, rlimit);
+ return 0;
+ 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) {
+ s_end_inline(s, p - 1, rlimit);
+ pstate->s_scan_type = scanning_none;
+ return scan_Refill;
+ }
+ code = sdecode_number(p + 2, num_format, pref);
+ goto rnum;
+ case BT_INT32_MSB:
+ case BT_INT32_LSB:
+ case BT_INT16_MSB:
+ case BT_INT16_LSB:
+ case BT_FLOAT_IEEE_MSB:
+ case BT_FLOAT_IEEE_LSB:
+ case BT_FLOAT_NATIVE:
+ 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;
+ }
+ s_end_inline(s, p + wanted, rlimit);
+ return 0;
+ case BT_BOOLEAN:
+ arg = p[1];
+ if (arg & ~1)
+ return_error(e_syntaxerror);
+ make_bool(pref, arg);
+ s_end_inline(s, p + 1, rlimit);
+ return 0;
+ case BT_STRING_256:
+ arg = *++p;
+ goto str;
+ case BT_STRING_64K_MSB:
+ case BT_STRING_64K_LSB:
+ arg = sdecodeushort(p + 1, num_format);
+ p += 2;
+ 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, p[1], pref);
+ goto lname;
+ case BT_EXECNAME_SYSTEM:
+ code = array_get(system_names_p, p[1], pref);
+ goto xname;
+ case BT_LITNAME_USER:
+ code = array_get(user_names_p, p[1], pref);
+ lname:
+ if (code < 0)
+ return code;
+ if (!r_has_type(pref, t_name))
+ return_error(e_undefined);
+ s_end_inline(s, p + 1, rlimit);
+ return 0;
+ case BT_EXECNAME_USER:
+ code = array_get(user_names_p, 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);
+ s_end_inline(s, p + 1, rlimit);
+ return 0;
+ case BT_NUM_ARRAY:
+ num_format = p[1];
+ if (!num_is_valid(num_format))
+ return_error(e_syntaxerror);
+ arg = sdecodeushort(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)
+{
+ scan_binary_state *const pbs = &pstate->s_ss.binary;
+ 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.
+ */
+private int
+scan_bos_continue(register stream * s, ref * pref, scanner_state * pstate)
+{
+ scan_binary_state *const pbs = &pstate->s_ss.binary;
+ s_declare_inline(s, p, rlimit);
+ uint max_array_index = pbs->max_array_index;
+ uint min_string_index = pbs->min_string_index;
+ int num_format = pbs->num_format;
+ 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; p += SIZEOF_BIN_SEQ_OBJ, index++) {
+ ref *op = abase + index;
+ uint osize;
+ long value;
+ uint atype, attrs;
+
+ 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;
+ }
+ attrs = (p[1] & 128 ? a_executable : 0);
+ switch (p[1] & 0x7f) {
+ case BS_TYPE_NULL:
+ make_null(op);
+ break;
+ case BS_TYPE_INTEGER:
+ make_int(op, sdecodelong(p + 5, num_format));
+ break;
+ case BS_TYPE_REAL:{
+ float vreal;
+
+ osize = sdecodeushort(p + 3, num_format);
+ if (osize != 0) { /* fixed-point number */
+ value = sdecodelong(p + 5, num_format);
+ vreal = (float)ldexp((double)value, -osize);
+ } else {
+ vreal = sdecodefloat(p + 5, num_format);
+ }
+ make_real(op, vreal);
+ break;
+ }
+ case BS_TYPE_BOOLEAN:
+ make_bool(op, sdecodelong(p + 5, num_format) != 0);
+ break;
+ case BS_TYPE_STRING:
+ osize = sdecodeushort(p + 3, num_format);
+ attrs |= a_all;
+ str:
+ if (osize == 0) {
+ /* For zero-length strings, the offset */
+ /* doesn't matter, and may be zero. */
+ make_empty_string(op, attrs);
+ break;
+ }
+ value = sdecodelong(p + 5, num_format);
+ if (value < max_array_index * SIZEOF_BIN_SEQ_OBJ ||
+ value + osize > size
+ )
+ return_error(e_syntaxerror);
+ if (value < min_string_index) {
+ /* We have to (re)allocate the strings. */
+ uint str_size = size - value;
+ 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 = value;
+ }
+ make_string(op, attrs | icurrent_space, osize,
+ pstate->s_da.base +
+ (value - min_string_index));
+ break;
+ case BS_TYPE_EVAL_NAME:
+ attrs |= a_readonly; /* mark as executable for later */
+ /* falls through */
+ case BS_TYPE_NAME:
+ osize = sdecodeushort(p + 3, num_format);
+ value = sdecodelong(p + 5, num_format);
+ switch (osize) {
+ case 0:
+ code = array_get(user_names_p, value, op);
+ goto usn;
+ case 0xffff:
+ code = array_get(system_names_p, value, op);
+ usn:
+ if (code < 0)
+ return code;
+ if (!r_has_type(op, t_name))
+ return_error(e_undefined);
+ r_set_attrs(op, attrs);
+ break;
+ default:
+ goto str;
+ }
+ break;
+ case BS_TYPE_ARRAY:
+ osize = sdecodeushort(p + 3, num_format);
+ atype = t_array;
+ arr:
+ value = sdecodelong(p + 5, num_format);
+ if (value + osize > min_string_index ||
+ value & (SIZEOF_BIN_SEQ_OBJ - 1)
+ )
+ return_error(e_syntaxerror);
+ {
+ uint aindex = value / SIZEOF_BIN_SEQ_OBJ;
+
+ max_array_index =
+ max(max_array_index, aindex + osize);
+ make_tasv_new(op, atype,
+ attrs | a_all | icurrent_space,
+ osize, refs, abase + aindex);
+ }
+ break;
+ case BS_TYPE_DICTIONARY: /* EXTENSION */
+ osize = sdecodeushort(p + 3, num_format);
+ if ((osize & 1) != 0 && osize != 1)
+ return_error(e_syntaxerror);
+ atype = t_mixedarray; /* mark as dictionary */
+ goto arr;
+ case BS_TYPE_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)
+{
+ scan_binary_state *const pbs = &pstate->s_ss.binary;
+ 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)
+{
+ scan_binary_state *const pbs = &pstate->s_ss.binary;
+ 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 attrs =
+ (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, attrs);
+ }
+ /* falls through */
+ case t_name:
+ if (r_has_attr(op, a_read)) { /* BS_TYPE_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;
+}
+
+/* ---------------- Writing ---------------- */
+
+int
+encode_binary_token(const ref * obj, long *ref_offset, long *char_offset,
+ byte * str)
+{
+ bin_seq_type_t type;
+ uint size = 0;
+ long value;
+ ref nstr;
+
+ switch (r_type(obj)) {
+ case t_null:
+ type = BS_TYPE_NULL;
+ goto tx;
+ case t_mark:
+ type = BS_TYPE_MARK;
+ goto tx;
+ case t_integer:
+ type = BS_TYPE_INTEGER;
+ value = obj->value.intval;
+ break;
+ case t_real:
+ type = BS_TYPE_REAL;
+ /***** DOESN'T HANDLE NON-IEEE NATIVE *****/
+ if (sizeof(obj->value.realval) == sizeof(int)) {
+ value = *(const int *)&obj->value.realval;
+ } else {
+ /****** CAN'T HANDLE IT ******/
+ return_error(e_rangecheck);
+ }
+ break;
+ case t_boolean:
+ type = BS_TYPE_BOOLEAN;
+ value = obj->value.boolval;
+ break;
+ case t_array:
+ type = BS_TYPE_ARRAY;
+ size = r_size(obj);
+ goto aod;
+ case t_dictionary: /* EXTENSION */
+ type = BS_TYPE_DICTIONARY;
+ size = dict_length(obj) << 1;
+ aod:value = *ref_offset;
+ *ref_offset += size * (ulong) SIZEOF_BIN_SEQ_OBJ;
+ break;
+ case t_string:
+ type = BS_TYPE_STRING;
+nos:
+ size = r_size(obj);
+ value = *char_offset;
+ *char_offset += size;
+ break;
+ case t_name:
+ type = BS_TYPE_NAME;
+ name_string_ref(obj, &nstr);
+ r_copy_attrs(&nstr, a_executable, obj);
+ obj = &nstr;
+ goto nos;
+ default:
+ return_error(e_rangecheck);
+ }
+ {
+ byte s0 = (byte) size, s1 = (byte) (size >> 8);
+ byte v0 = (byte) value, v1 = (byte) (value >> 8), v2 = (byte) (value >> 16),
+ v3 = (byte) (value >> 24);
+ int order = (int)ref_binary_object_format.value.intval - 1;
+
+ if (order & 1) {
+ /* Store little-endian */
+ str[2] = s0, str[3] = s1;
+ str[4] = v0, str[5] = v1, str[6] = v2, str[7] = v3;
+ } else {
+ /* Store big-endian */
+ str[2] = s1, str[3] = s0;
+ str[4] = v3, str[5] = v2, str[6] = v1, str[7] = v0;
+ }
+ }
+tx:
+ if (r_has_attr(obj, a_executable))
+ type += BS_EXECUTABLE;
+ str[0] = (byte) type;
+ return 0;
+}
diff --git a/pstoraster/iscannum.c b/pstoraster/iscannum.c
new file mode 100644
index 000000000..07137418a
--- /dev/null
+++ b/pstoraster/iscannum.c
@@ -0,0 +1,398 @@
+/* Copyright (C) 1994, 1995, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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"
+
+/*
+ * Warning: this file has a "spaghetti" control structure. But since this
+ * code accounts for over 10% of the execution time of some PostScript
+ * files, this is one of the few places we feel this is justified.
+ */
+
+/*
+ * 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(const byte * str, const byte * end, int sign,
+ ref * pref, const byte ** psp)
+{
+ const byte *sp = str;
+#define GET_NEXT(cvar, sp, end_action)\
+ if ( sp >= end ) { end_action; } else cvar = *sp++
+ /*
+ * 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;
+ int c, d;
+ const byte *const decoder = scan_char_decoder;
+#define IS_DIGIT(d, c)\
+ ((d = decoder[c]) < 10)
+
+ GET_NEXT(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 '.'. */
+ GET_NEXT(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) {
+ GET_NEXT(c, sp, goto iret);
+ if (!IS_DIGIT(d, c))
+ break;
+ if (WOULD_OVERFLOW(ival, d, max_int))
+ goto i2l;
+ }
+ ind: /* We saw a non-digit while accumulating an integer in ival. */
+ switch (c) {
+ case '.':
+ GET_NEXT(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 '#':
+ {
+ const uint radix = (uint)ival;
+ ulong uval = 0, lmax;
+
+ 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) {
+ case 2:
+ shift = 1, lmax = max_ulong >> 1;
+ break;
+ case 4:
+ shift = 2, lmax = max_ulong >> 2;
+ break;
+ case 8:
+ shift = 3, lmax = max_ulong >> 3;
+ break;
+ case 16:
+ shift = 4, lmax = max_ulong >> 4;
+ break;
+ case 32:
+ shift = 5, lmax = max_ulong >> 5;
+ break;
+ default: /* can't happen */
+ return_error(e_rangecheck);
+ }
+ for (;; uval = (uval << shift) + d) {
+ GET_NEXT(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) {
+ GET_NEXT(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);
+ }
+ }
+ 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
+ ) {
+ GET_NEXT(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;
+ GET_NEXT(c, sp, goto lret);
+ if (!IS_DIGIT(d, c))
+ break;
+ }
+ switch (c) {
+ case '.':
+ GET_NEXT(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;
+ GET_NEXT(c, sp, c = EOFC);
+ if (!IS_DIGIT(d, c))
+ break;
+ }
+ switch (c) {
+ case '.':
+ GET_NEXT(c, sp, c = EOFC);
+ exp10 = 0;
+ goto fs;
+ default:
+ *psp = sp;
+ code = 1;
+ /* falls through */
+ case EOFC:
+ if (sign < 0)
+ dval = -dval;
+ goto rret;
+ case 'e':
+ case 'E':
+ exp10 = 0;
+ goto fs;
+ case '#':
+ return_error(e_syntaxerror);
+ }
+
+ /* 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--;
+ GET_NEXT(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--;
+ GET_NEXT(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--;
+ GET_NEXT(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;
+
+ GET_NEXT(c, sp, return_error(e_syntaxerror));
+ switch (c) {
+ case '-':
+ esign = 1;
+ case '+':
+ GET_NEXT(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) {
+ GET_NEXT(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);
+ }
+rret:
+ make_real_new(pref, dval);
+ return code;
+}
diff --git a/pstoraster/iscannum.h b/pstoraster/iscannum.h
new file mode 100644
index 000000000..e44b7c93c
--- /dev/null
+++ b/pstoraster/iscannum.h
@@ -0,0 +1,36 @@
+/* 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 supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Interface to Ghostscript number scanner */
+
+#ifndef iscannum_INCLUDED
+# define iscannum_INCLUDED
+
+/* 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));
+
+#endif /* iscannum_INCLUDED */
diff --git a/pstoraster/isstate.h b/pstoraster/isstate.h
new file mode 100644
index 000000000..146c71248
--- /dev/null
+++ b/pstoraster/isstate.h
@@ -0,0 +1,46 @@
+/* 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 supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Requires isave.h */
+
+#ifndef isstate_INCLUDED
+# define isstate_INCLUDED
+
+/* 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)
+
+#endif /* isstate_INCLUDED */
diff --git a/pstoraster/istack.c b/pstoraster/istack.c
new file mode 100644
index 000000000..2fc191e08
--- /dev/null
+++ b/pstoraster/istack.c
@@ -0,0 +1,588 @@
+/* Copyright (C) 1992, 1995, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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 RELOC_REF_VAR */
+#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:
+ENUM_RETURN_REF(&sptr->current);
+ENUM_PTRS_END
+private RELOC_PTRS_BEGIN(ref_stack_reloc_ptrs)
+{
+ /* 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;
+
+ RELOC_REF_VAR(sptr->current);
+ 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
+} RELOC_PTRS_END
+/* Structure type for a ref_stack. */
+public_st_ref_stack();
+
+/* Initialize a stack. */
+void
+ref_stack_init(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->margin = 0;
+ pstack->body_size = avail;
+
+ pstack->bot_guard = bot_guard;
+ pstack->top_guard = top_guard;
+ pstack->block_size = size;
+ pstack->data_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 = ref_stack_count_inline(pstack);
+
+ 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;
+}
+
+/* Set the margin between the limit and the top of the stack. */
+/* Note that this may require allocating a block. */
+int
+ref_stack_set_margin(ref_stack * pstack, uint margin)
+{
+ if (margin <= pstack->margin) {
+ refset_null(pstack->top + 1, pstack->margin - margin);
+ } else {
+ if (margin > pstack->data_size >> 1)
+ return_error(e_rangecheck);
+ if (pstack->top - pstack->p < margin) {
+ uint used = pstack->p + 1 - pstack->bot;
+ uint keep = pstack->data_size - margin;
+ int code = ref_stack_push_block(pstack, keep, used - keep);
+
+ if (code < 0)
+ return code;
+ }
+ }
+ pstack->margin = margin;
+ pstack->body_size = pstack->data_size - margin;
+ pstack->top = pstack->bot + pstack->body_size - 1;
+ 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;
+ ref_stack_enum_t rsenum;
+
+ ref_stack_enum_begin(&rsenum, pstack);
+ do {
+ uint count = rsenum.size;
+ const ref *p = rsenum.ptr + count - 1;
+
+ for (; count; count--, p--)
+ if (r_has_type(p, t_mark))
+ return scanned + (rsenum.size - count + 1);
+ scanned += rsenum.size;
+ } while (ref_stack_enum_next(&rsenum));
+ return 0;
+}
+
+/* Do the store check for storing elements of a stack into an array. */
+/* May return e_invalidaccess. */
+int
+ref_stack_store_check(const ref_stack * pstack, ref * parray, uint count,
+ uint skip)
+{
+ uint space = r_space(parray);
+
+ if (space != avm_local) {
+ uint left = count, pass = skip;
+ ref_stack_enum_t rsenum;
+
+ ref_stack_enum_begin(&rsenum, pstack);
+ do {
+ ref *ptr = rsenum.ptr;
+ uint size = rsenum.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;
+ }
+ } while (ref_stack_enum_next(&rsenum));
+ }
+ 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;
+ ref_stack_enum_t rsenum;
+
+ if (count > ref_stack_count(pstack) || count > r_size(parray))
+ return_error(e_rangecheck);
+ if (check) {
+ int code = ref_stack_store_check(pstack, parray, count, skip);
+
+ if (code < 0)
+ return code;
+ }
+ to = parray->value.refs + count;
+ left = count, pass = skip;
+ ref_stack_enum_begin(&rsenum, pstack);
+ do {
+ ref *from = rsenum.ptr;
+ uint size = rsenum.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;
+ }
+ } while (ref_stack_enum_next(&rsenum));
+ 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(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(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;
+ 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) + pstack->bot_guard;
+ next = pcur->next;
+ /*
+ * If the contents of the two blocks won't fit in a single block, we
+ * move up the used part of the top block, and copy up as much of
+ * the contents of the next block under it as will fit. If 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) {
+ /*
+ * The contents of the two blocks won't fit into a single block.
+ * On the assumption that we're recovering from a local stack
+ * underflow and need to increase the number of contiguous
+ * elements available, move up the used part of the top block, and
+ * copy up as much of the contents of the next block under it 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 {
+ /*
+ * The contents of the two blocks will fit into a single block.
+ * Copy the used part of the top block to the top of the next
+ * block, and free the top block.
+ */
+ 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;
+ 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 (request > pstack->data_size)
+ 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(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,
+ added);
+ if (code < 0) {
+ /* Back out. */
+ ref_stack_pop(pstack, count - needed + added);
+ 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(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);
+ /* 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->data_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;
+ pstack->p = pstack->bot + keep - 1;
+ pstack->extension_size += pstack->body_size;
+ pstack->extension_used += move;
+ return 0;
+}
+
+/* Begin enumerating the blocks of a stack. */
+void
+ref_stack_enum_begin(ref_stack_enum_t *prse, const ref_stack *pstack)
+{
+ prse->block = (ref_stack_block *)pstack->current.value.refs;
+ prse->ptr = pstack->bot;
+ prse->size = pstack->p + 1 - pstack->bot;
+}
+
+bool
+ref_stack_enum_next(ref_stack_enum_t *prse)
+{
+ ref_stack_block *block =
+ prse->block = (ref_stack_block *)prse->block->next.value.refs;
+
+ if (block == 0)
+ return false;
+ prse->ptr = block->used.value.refs;
+ prse->size = r_size(&block->used);
+ return true;
+}
+
+/* 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);
+}
+
+/*
+ * Free the entire contents of a stack, including the bottom block.
+ * The client must free the ref_stack itself. Note that after calling
+ * ref_stack_release, the stack is no longer usable.
+ */
+void
+ref_stack_release(ref_stack * pstack)
+{
+ ref_stack_clear(pstack);
+ /* Free the original (bottom) block. */
+ gs_free_ref_array(pstack->memory, &pstack->current,
+ "ref_stack_release");
+}
+
+/*
+ * Release a stack and then free the ref_stack object.
+ */
+void
+ref_stack_free(ref_stack * pstack, gs_memory_t * mem, client_name_t cname)
+{
+ ref_stack_release(pstack);
+ gs_free_object(mem, pstack, cname);
+}
+
+/* ------ Internal routines ------ */
+
+/* Initialize the guards and body of a stack block. */
+private void
+init_block(ref_stack * pstack, ref * psb, uint used)
+{
+ ref *brefs = psb->value.refs;
+ uint i;
+ 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);
+ } {
+ ref_stack_block *const pblock = (ref_stack_block *) brefs;
+
+ 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..deacf36d2
--- /dev/null
+++ b/pstoraster/istack.h
@@ -0,0 +1,256 @@
+/* Copyright (C) 1992, 1995, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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.
+ *
+ * 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.
+ */
+
+typedef ref *s_ptr;
+typedef const ref *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 put
+ * guard elements at the top and bottom of each stack block (see dstack.h,
+ * estack.h, and ostack.h for details of the individual stacks). Note that
+ * 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 = */
+ /* bot + data_size */
+ 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 */
+ uint margin; /* # of slots to leave between limit */
+ /* and top */
+ uint body_size; /* data_size - margin */
+ /* 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 data_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 st_ref_stack_num_ptrs 1 /* current */
+
+/* ------ Procedural interface ------ */
+
+/* Initialize a stack. */
+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));
+
+/* Set the margin between the limit and the top of the stack. */
+/* Note that this may require allocating a block. */
+int ref_stack_set_margin(P2(ref_stack *, uint));
+
+/* Return the number of elements on a stack. */
+uint ref_stack_count(P1(const ref_stack *));
+
+#define ref_stack_count_inline(pstk)\
+ ((pstk)->p + 1 - (pstk)->bot + (pstk)->extension_used)
+
+/* 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 *));
+
+/*
+ * Do the store check for storing 'count' elements of a stack, starting
+ * 'skip' elements below the top, into an array. Return 0 or e_invalidaccess.
+ */
+int ref_stack_store_check(P4(const ref_stack * pstack, ref * parray,
+ uint count, uint skip));
+
+/*
+ * Store 'count elements of a stack, starting 'skip' 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 * pstack, ref * parray, uint count,
+ uint skip, int age, bool check, client_name_t cname));
+
+/* 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));
+
+/*
+ * Enumerate the blocks of a stack from top to bottom, as follows:
+
+ ref_stack_enum_t rsenum;
+
+ ref_stack_enum_begin(&rsenum, pstack);
+ do {
+ ... process rsenum.size refs starting at rsenum.ptr ...
+ } while (ref_stack_enum_next(&rsenum));
+
+ */
+typedef struct ref_stack_enum_s {
+ ref_stack_block *block;
+ ref *ptr;
+ uint size;
+} ref_stack_enum_t;
+void ref_stack_enum_begin(P2(ref_stack_enum_t *, const ref_stack *));
+bool ref_stack_enum_next(P1(ref_stack_enum_t *));
+
+/* Define a previous enumeration structure, for backward compatibility. */
+#define STACK_LOOP_BEGIN(pstack, ptrv, sizev)\
+{ ref_stack_enum_t enum_;\
+ ref_stack_enum_begin(&enum_, pstack);\
+ do {\
+ ref *ptrv = enum_.ptr;\
+ uint sizev = enum_.size;
+#define STACK_LOOP_END(ptrv, sizev)\
+ } while (ref_stack_enum_next(&enum_));\
+}
+
+/* Clean up a stack for garbage collection. */
+void ref_stack_cleanup(P1(ref_stack *));
+
+/*
+ * Free the entire contents of a stack, including the bottom block.
+ * The client must free the ref_stack itself. Note that after calling
+ * ref_stack_release, the stack is no longer usable.
+ */
+void ref_stack_release(P1(ref_stack *));
+
+/*
+ * Release a stack and then free the ref_stack object.
+ */
+void ref_stack_free(P3(ref_stack * pstack, gs_memory_t * mem,
+ client_name_t cname));
+
+#endif /* istack_INCLUDED */
diff --git a/pstoraster/istream.h b/pstoraster/istream.h
new file mode 100644
index 000000000..f1019f49b
--- /dev/null
+++ b/pstoraster/istream.h
@@ -0,0 +1,43 @@
+/* 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 supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Requires scommon.h, ostack.h */
+
+#ifndef istream_INCLUDED
+# define istream_INCLUDED
+
+/* 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))));
+
+#endif /* istream_INCLUDED */
diff --git a/pstoraster/istruct.h b/pstoraster/istruct.h
new file mode 100644
index 000000000..082327028
--- /dev/null
+++ b/pstoraster/istruct.h
@@ -0,0 +1,98 @@
+/* Copyright (C) 1994, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Interpreter-level extension of gsstruct.h */
+
+#ifndef istruct_INCLUDED
+# define istruct_INCLUDED
+
+#include "gsstruct.h"
+
+/* ================ Refs ================ */
+
+/*
+ * 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)
+
+/* The structure type descriptor for (blocks of) refs. */
+/* This is defined in igc.c and exported for isave.c. */
+extern_st(st_refs);
+
+/*
+ * Extend the GC procedure vector to include refs.
+ */
+#define refs_proc_reloc(proc)\
+ void proc(P3(ref_packed *from, ref_packed *to, gc_state_t *gcst))
+typedef struct gc_procs_with_refs_s {
+ gc_procs_common;
+ /* Relocate a pointer to a ref[_packed]. */
+ ptr_proc_reloc((*reloc_ref_ptr), ref_packed);
+ /* Relocate a block of ref[_packed]s. */
+ refs_proc_reloc((*reloc_refs));
+} gc_procs_with_refs_t;
+
+#undef gc_proc
+#define gc_proc(gcst, proc) ((*(const gc_procs_with_refs_t **)(gcst))->proc)
+
+/*
+ * Define enumeration and relocation macros analogous to those for
+ * structures and strings. (We should go back and change the names of
+ * those macros to be consistent which these, which are better, but it's
+ * not worth the trouble.)
+ */
+#define ENUM_RETURN_REF(ptr)\
+ BEGIN *pep = (const void *)(ptr); return ptr_ref_type; END
+#define ENUM_RETURN_REF_MEMBER(typ, memb)\
+ ENUM_RETURN_REF(&((typ *)vptr)->memb)
+#define RELOC_REF_PTR_VAR(ptrvar)\
+ ptrvar = (*gc_proc(gcst, reloc_ref_ptr))((const void *)(ptrvar), gcst)
+#define RELOC_REF_PTR_MEMBER(typ, memb)\
+ RELOC_REF_PTR_VAR(((typ *)vptr)->memb)
+#define RELOC_REFS(from, upto)\
+ (*gc_proc(gcst, reloc_refs))((ref_packed *)(from), (ref_packed *)(upto), gcst)
+#define RELOC_REF_VAR(refvar)\
+ RELOC_REFS(&(refvar), &(refvar) + 1)
+
+/*
+ * 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..55d1fdaa1
--- /dev/null
+++ b/pstoraster/iutil.c
@@ -0,0 +1,676 @@
+/* Copyright (C) 1989, 1995, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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 "gsccode.h" /* for gxfont.h */
+#include "gsmatrix.h"
+#include "gsutil.h"
+#include "gxfont.h"
+
+/* ------ Object utilities ------ */
+
+/* Define the table of ref type properties. */
+const byte ref_type_properties[] =
+{
+ ref_type_properties_data
+};
+
+/* Copy refs from one place to another. */
+int
+refcpy_to_old(ref * aref, uint index, const ref * from,
+ uint size, client_name_t cname)
+{
+ 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(ref * to, const ref * from, uint size)
+{
+ while (size--)
+ ref_assign_new(to, from), to++, from++;
+}
+
+/* Fill a new object with nulls. */
+void
+refset_null(ref * to, uint size)
+{
+ while (size--)
+ make_null_new(to), to++;
+}
+
+/* Compare two objects for equality. */
+bool
+obj_eq(const ref * pref1, 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_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 the 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_save:
+ return (pref2->value.saveid == pref1->value.saveid);
+ 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_struct:
+ case t_astruct:
+ return (pref1->value.pstruct == pref2->value.pstruct);
+ case t_fontID:
+ { /*
+ * In the Adobe implementations, different scalings of a
+ * font have "equal" FIDs, so we do the same.
+ */
+ const gs_font *pfont1 = r_ptr(pref1, gs_font);
+ const gs_font *pfont2 = r_ptr(pref2, gs_font);
+
+ while (pfont1->base != pfont1)
+ pfont1 = pfont1->base;
+ while (pfont2->base != pfont2)
+ pfont2 = pfont2->base;
+ return (pfont1 == pfont2);
+ }
+ }
+ return false; /* shouldn't happen! */
+}
+
+/* Compare two objects for identity. */
+bool
+obj_ident_eq(const ref * pref1, 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.
+ */
+private void ensure_dot(P1(char *));
+int
+obj_cvp(const ref * op, byte * str, uint len, uint * prlen,
+ const byte ** pchars, bool full_print)
+{
+ if (full_print)
+ switch (r_btype(op)) {
+ case t_boolean:
+ case t_integer:
+ 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!
+ */
+ if (!full_print)
+ break;
+ {
+ char buf[30]; /* big enough for any float or double */
+ float value = op->value.realval;
+ float scanned;
+ uint plen;
+
+ sprintf(buf, "%g", value);
+ sscanf(buf, "%f", &scanned);
+ if (scanned != value)
+ sprintf(buf, "%.9g", value);
+ ensure_dot(buf);
+ *prlen = plen = strlen(buf);
+ if (plen > len)
+ return_error(e_rangecheck);
+ memcpy(str, buf, plen);
+ return 0;
+ }
+ default:
+ return_error(e_typecheck);
+ }
+ return obj_cvs(op, str, len, prlen, pchars);
+}
+int
+obj_cvs(const ref * op, byte * str, uint len, uint * prlen,
+ const byte ** pchars)
+{
+ char buf[30]; /* big enough for any float or double */
+ const byte *pstr = (const byte *)buf;
+ uint plen;
+ ref nref;
+
+ 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:
+ sprintf(buf, "%g", op->value.realval);
+ ensure_dot(buf);
+ 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;
+}
+/*
+ * Make sure the converted form of a real number has a decimal point. This
+ * is needed for compatibility with Adobe (and other) interpreters.
+ */
+private void
+ensure_dot(char *buf)
+{
+ if (strchr(buf, '.') == NULL) {
+ 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);
+ }
+ }
+}
+
+/* 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);
+ const op_def *const *opp = op_def_table;
+ const op_def *const *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 + index_long;
+
+ ref_assign(pref, pvalue);
+ }
+ break;
+ 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);
+ }
+ break;
+ case t_shortarray:
+ {
+ const ref_packed *packed = aref->value.packed + index_long;
+
+ packed_get(packed, pref);
+ }
+ break;
+ default:
+ return_error(e_typecheck);
+ }
+ return 0;
+}
+
+/* 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(const ref * bot, 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, double *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);
+}
+/* float_params doesn't bother to keep track of the mask. */
+int
+float_params(const ref * op, int count, float *pval)
+{
+ for (pval += count; --count >= 0; --op)
+ switch (r_type(op)) {
+ case t_real:
+ *--pval = op->value.realval;
+ break;
+ case t_integer:
+ *--pval = op->value.intval;
+ break;
+ case t__invalid:
+ return_error(e_stackunderflow);
+ default:
+ return_error(e_typecheck);
+ }
+ return 0;
+}
+
+/* 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, double *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;
+}
+int
+float_param(const ref * op, float *pparam)
+{
+ double dval;
+ int code = real_param(op, &dval);
+
+ if (code >= 0)
+ *pparam = (float)dval; /* can't overflow */
+ return code;
+}
+
+/* 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. */
+int
+make_reals(ref * op, const double *pval, int count)
+{
+ /* This should return e_limitcheck if any real is too large */
+ /* to fit into a float on the stack. */
+ for (; count--; op++, pval++)
+ make_real(op, *pval);
+ return 0;
+}
+int
+make_floats(ref * op, const float *pval, int count)
+{
+ /* This should return e_undefinedresult for infinities. */
+ for (; count--; op++, pval++)
+ make_real(op, *pval);
+ return 0;
+}
+
+/* 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;
+ ref values[6];
+ const ref *pvalues;
+
+ if (r_has_type(op, t_array))
+ pvalues = op->value.refs;
+ else {
+ int i;
+
+ for (i = 0; i < 6; ++i) {
+ code = array_get(op, (long)i, &values[i]);
+ if (code < 0)
+ return code;
+ }
+ pvalues = values;
+ }
+ check_read(*op);
+ if (r_size(op) != 6)
+ return_error(e_rangecheck);
+ code = float_params(pvalues + 5, 6, (float *)pmat);
+ return (code < 0 ? code : 0);
+}
+
+/* Write a matrix operand. */
+/* Return 0 if OK, error code if not. */
+int
+write_matrix(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..ea0489a29
--- /dev/null
+++ b/pstoraster/iutil.h
@@ -0,0 +1,121 @@
+/* Copyright (C) 1991, 1995, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Requires imemory.h, ostack.h */
+
+#ifndef iutil_INCLUDED
+# define iutil_INCLUDED
+
+/* ------ 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));
+/* obj_cvs is equivalent to obj_cvp with full_print = false. */
+int obj_cvs(P5(const ref * op, byte * str, uint len, uint * prlen,
+ const byte ** pchars));
+
+/* 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, double *));
+
+/* float_params can lose accuracy for large integers. */
+int float_params(P3(const ref *, int, float *));
+
+/* Get a single real parameter. */
+/* The only possible error is e_typecheck. */
+int real_param(P2(const ref *, double *));
+
+/* float_param can lose accuracy for large integers. */
+int float_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. */
+/* Return e_limitcheck for infinities or double->float overflow. */
+int make_reals(P3(ref *, const double *, int));
+int make_floats(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 *));
+
+#endif /* iutil_INCLUDED */
diff --git a/pstoraster/iutil2.c b/pstoraster/iutil2.c
new file mode 100644
index 000000000..c49022510
--- /dev/null
+++ b/pstoraster/iutil2.c
@@ -0,0 +1,154 @@
+/* Copyright (C) 1993, 1994, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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 "idict.h"
+#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 *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 *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;
+}
+
+/* Read a password from, or write a password into, a dictionary */
+/* (presumably systemdict). */
+private int
+dict_find_password(ref ** ppvalue, const ref * pdref, const char *kstr)
+{
+ ref *pvalue;
+
+ if (dict_find_string(pdref, kstr, &pvalue) <= 0)
+ return_error(e_undefined);
+ if (!r_has_type(pvalue, t_string) ||
+ r_has_attrs(pvalue, a_read) ||
+ pvalue->value.const_bytes[0] >= r_size(pvalue)
+ )
+ return_error(e_rangecheck);
+ *ppvalue = pvalue;
+ return 0;
+}
+int
+dict_read_password(password * ppass, const ref * pdref, const char *pkey)
+{
+ ref *pvalue;
+ int code = dict_find_password(&pvalue, pdref, pkey);
+
+ if (code < 0)
+ return code;
+ if (pvalue->value.const_bytes[0] > MAX_PASSWORD)
+ return_error(e_rangecheck); /* limitcheck? */
+ memcpy(ppass->data, pvalue->value.const_bytes + 1,
+ (ppass->size = pvalue->value.const_bytes[0]));
+ return 0;
+}
+int
+dict_write_password(const password * ppass, ref * pdref, const char *pkey)
+{
+ ref *pvalue;
+ int code = dict_find_password(&pvalue, pdref, pkey);
+
+ if (code < 0)
+ return code;
+ if (ppass->size >= r_size(pvalue))
+ return_error(e_rangecheck);
+ memcpy(pvalue->value.bytes + 1, ppass->data,
+ (pvalue->value.bytes[0] = ppass->size));
+ return 0;
+}
diff --git a/pstoraster/iutil2.h b/pstoraster/iutil2.h
new file mode 100644
index 000000000..ff76d87f6
--- /dev/null
+++ b/pstoraster/iutil2.h
@@ -0,0 +1,56 @@
+/* Copyright (C) 1993, 1994, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Interface to procedures in iutil2.c */
+
+#ifndef iutil2_INCLUDED
+# define iutil2_INCLUDED
+
+/* ------ Password utilities ------ */
+
+/* Define the password structure. */
+/* NOTE: MAX_PASSWORD must match the initial password lengths in gs_lev2.ps. */
+#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}}
+
+/* Transmit a password to or from a parameter list. */
+int param_read_password(P3(gs_param_list *, const char *, password *));
+int param_write_password(P3(gs_param_list *, const char *, 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 *));
+
+/* Read a password from, or write a password into, a dictionary */
+/* (presumably systemdict). */
+int dict_read_password(P3(password *, const ref *, const char *));
+int dict_write_password(P3(const password *, ref *, const char *));
+
+#endif /* iutil2_INCLUDED */
diff --git a/pstoraster/ivmspace.h b/pstoraster/ivmspace.h
new file mode 100644
index 000000000..dd51d91e1
--- /dev/null
+++ b/pstoraster/ivmspace.h
@@ -0,0 +1,111 @@
+/* Copyright (C) 1992, 1993, 1994, 1996, 1997 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Local/global space management */
+/* Requires iref.h */
+
+#ifndef ivmspace_INCLUDED
+# define ivmspace_INCLUDED
+
+#include "gsgc.h"
+
+/*
+ * 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)
+/*
+ * The i_vm_xxx values are defined in gsgc.h.
+ */
+typedef enum {
+ avm_foreign = (i_vm_foreign << r_space_shift),
+ avm_system = (i_vm_system << r_space_shift),
+ avm_global = (i_vm_global << r_space_shift),
+ avm_local = (i_vm_local << r_space_shift),
+ avm_max = avm_local
+} avm_space;
+
+#define r_space(rp) (avm_space)(r_type_attrs(rp) & a_space)
+#define r_space_index(rp) ((int)r_space(rp) >> r_space_shift)
+#define r_set_space(rp,space) r_store_attrs(rp, a_space, (uint)space)
+
+/*
+ * 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..6b8769020
--- /dev/null
+++ b/pstoraster/main.h
@@ -0,0 +1,101 @@
+/* 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 supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Backward-compatible interface to gsmain.c */
+
+#ifndef main_INCLUDED
+# define main_INCLUDED
+
+#include "imain.h"
+#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)
+
+#endif /* main_INCLUDED */
diff --git a/pstoraster/malloc_.h b/pstoraster/malloc_.h
new file mode 100644
index 000000000..5180b3124
--- /dev/null
+++ b/pstoraster/malloc_.h
@@ -0,0 +1,63 @@
+/* Copyright (C) 1989, 1992, 1996, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Generic substitute for Unix malloc.h */
+
+#ifndef malloc__INCLUDED
+# define malloc__INCLUDED
+
+/* 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)
+# if defined(_POSIX_SOURCE) || (defined(__STDC__) && (!defined(sun) || defined(__svr4__))) /* >>> */
+# include <stdlib.h>
+# else /* Ancient breakage */
+extern char *malloc();
+extern void free();
+
+# endif
+# else
+# if defined(_HPUX_SOURCE) || defined(__CONVEX__) || defined(__convex__) || defined(__OSF__) || defined(__386BSD__) || defined(_POSIX_SOURCE) || 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
+
+#endif /* malloc__INCLUDED */
diff --git a/pstoraster/math_.h b/pstoraster/math_.h
new file mode 100644
index 000000000..e04e3d4d2
--- /dev/null
+++ b/pstoraster/math_.h
@@ -0,0 +1,97 @@
+/* Copyright (C) 1989, 1995, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Generic substitute for math.h */
+
+#ifndef math__INCLUDED
+# define math__INCLUDED
+
+/* 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 */
+
+#endif /* math__INCLUDED */
diff --git a/pstoraster/memory_.h b/pstoraster/memory_.h
new file mode 100644
index 000000000..27e0da922
--- /dev/null
+++ b/pstoraster/memory_.h
@@ -0,0 +1,107 @@
+/* Copyright (C) 1989, 1992, 1993, 1994, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Generic substitute for Unix memory.h */
+
+#ifndef memory__INCLUDED
+# define memory__INCLUDED
+
+/* 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.
+ */
+ /*
+ * The exceptions vastly outnumber the BSD4_2 "rule":
+ * these tests should be the other way around....
+ */
+# if defined(VMS) || defined(_POSIX_SOURCE) || (defined(__STDC__) && (!defined(sun) || defined(__svr4__))) || defined(_HPUX_SOURCE) || defined(__WATCOMC__) || defined(THINK_C) || defined(bsdi) || defined(__FreeBSD) || (defined(_MSC_VER) && _MSC_VER >= 1000)
+# 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__ */
+
+#endif /* memory__INCLUDED */
diff --git a/pstoraster/opcheck.h b/pstoraster/opcheck.h
new file mode 100644
index 000000000..43fdca099
--- /dev/null
+++ b/pstoraster/opcheck.h
@@ -0,0 +1,86 @@
+/* Copyright (C) 1993, 1995, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Requires ialloc.h (for imemory), iref.h, errors.h */
+
+#ifndef opcheck_INCLUDED
+# define opcheck_INCLUDED
+
+/*
+ * 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)\
+ BEGIN if ( !r_has_type(&rf,typ) ) return_error(e_typecheck); END
+#define check_stype_only(rf,styp)\
+ BEGIN if ( !r_has_stype(&rf,imemory,styp) ) return_error(e_typecheck); END
+/* Check for array */
+#define check_array_else(rf,errstat)\
+ BEGIN if ( !r_has_type(&rf, t_array) ) errstat; END
+#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)\
+ BEGIN if ( !r_is_proc(&rf) ) return_error(check_proc_failed(&rf)); END
+#define check_proc_only(rf) check_proc(rf)
+
+/* Check for read, write, or execute access. */
+#define check_access(rf,acc1)\
+ BEGIN if ( !r_has_attr(&rf,acc1) ) return_error(e_invalidaccess); END
+#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)\
+ BEGIN\
+ if ( !r_has_type_attrs(&rf,typ,acc1) )\
+ return_error((!r_has_type(&rf,typ) ? e_typecheck : e_invalidaccess));\
+ END
+#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)\
+ BEGIN\
+ check_type(orf, t_integer);\
+ if ( (ulong)(orf).value.intval > (u) ) return_error(e_rangecheck);\
+ END
+#define check_int_leu_only(rf, u)\
+ BEGIN\
+ check_type_only(rf, t_integer);\
+ if ( (ulong)(rf).value.intval > (u) ) return_error(e_rangecheck);\
+ END
+#define check_int_ltu(orf, u)\
+ BEGIN\
+ check_type(orf, t_integer);\
+ if ( (ulong)(orf).value.intval >= (u) ) return_error(e_rangecheck);\
+ END
+
+#endif /* opcheck_INCLUDED */
diff --git a/pstoraster/opdef.h b/pstoraster/opdef.h
new file mode 100644
index 000000000..d593a017f
--- /dev/null
+++ b/pstoraster/opdef.h
@@ -0,0 +1,164 @@
+/* Copyright (C) 1991, 1995, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Operator definition interface for Ghostscript */
+
+#ifndef opdef_INCLUDED
+# define opdef_INCLUDED
+
+/*
+ * 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).
+ */
+
+/*
+ * Define the structure for initializing the operator table. Each operator
+ * file zxxx.c declares an array of these as follows:
+
+ const op_def * const zxxx_op_defs[] =
+ {
+ {"1name", zname},
+ ...
+ op_def_end(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. For backward
+ * compatibility with an older convention, we also allow (deprecated)
+
+ BEGIN_OP_DEFS(my_defs) {
+ {"1name", zname},
+ ...
+ END_OP_DEFS(iproc) }
+
+ * 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 *oname;
+ op_proc_p proc;
+} op_def;
+
+#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_begin_ll3() op_def_begin_dict("ll3dict")
+#define op_def_is_begin_dict(def) ((def)->proc == 0)
+#define op_def_end(iproc) {0, (op_proc_p)iproc}
+
+/*
+ * Define the table of pointers to all operator definition tables.
+ */
+extern const op_def *const op_defs_all[];
+
+/*
+ * Formerly, we needed to define each op_defs table as a procedure that
+ * returns the actual table, because of cross-segment linking restrictions
+ * in the 16-bit Borland C compiler for MS Windows. This is no longer
+ * relevant, but for backward compatibility, we need to retain
+ * BEGIN/END_OP_DEFS with the same syntax. This involves a kludge to create
+ * a dummy procedure to match up with a closing brace, and another kludge to
+ * prevent a "defined but not used" warning.
+ */
+
+#define BEGIN_OP_DEFS(xx_op_defs)\
+const op_def xx_op_defs[] =
+
+#define END_OP_DEFS(iproc)\
+ op_def_end(iproc)\
+};\
+static int op_defs_dummy(void)\
+{ return 1 || op_defs_dummy(); /* generates the least wasted code */
+
+/*
+ * 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)
+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 *));
+
+#endif /* opdef_INCLUDED */
diff --git a/pstoraster/oper.h b/pstoraster/oper.h
new file mode 100644
index 000000000..31c02275b
--- /dev/null
+++ b/pstoraster/oper.h
@@ -0,0 +1,108 @@
+/* Copyright (C) 1989, 1995, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Definitions for Ghostscript operators */
+
+#ifndef oper_INCLUDED
+# define oper_INCLUDED
+
+#include "errors.h"
+#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
+
+#endif /* oper_INCLUDED */
diff --git a/pstoraster/opextern.h b/pstoraster/opextern.h
new file mode 100644
index 000000000..831c95a94
--- /dev/null
+++ b/pstoraster/opextern.h
@@ -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 supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Externally accessible operator declarations */
+
+#ifndef opextern_INCLUDED
+# define opextern_INCLUDED
+
+/*
+ * 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 zcopy(P1(os_ptr));
+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));
+
+#endif /* opextern_INCLUDED */
diff --git a/pstoraster/ostack.h b/pstoraster/ostack.h
new file mode 100644
index 000000000..bf134dc90
--- /dev/null
+++ b/pstoraster/ostack.h
@@ -0,0 +1,94 @@
+/* Copyright (C) 1991, 1994, 1996, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Definitions for Ghostscript operand stack */
+
+#ifndef ostack_INCLUDED
+# define ostack_INCLUDED
+
+#include "iostack.h"
+
+/* Define the operand stack pointers. */
+extern op_stack_t iop_stack;
+
+#define o_stack (iop_stack.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. */
+#define push(n)\
+ BEGIN\
+ if ( (op += (n)) > ostop )\
+ { o_stack.requested = (n); return_error(e_stackoverflow); }\
+ else osp = op;\
+ END
+
+/*
+ * 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
+ * .get/.putdeviceparams .gethardwareparams
+ */
+
+#endif /* ostack_INCLUDED */
diff --git a/pstoraster/pdf2dsc.ps b/pstoraster/pdf2dsc.ps
new file mode 100644
index 000000000..77365d5cb
--- /dev/null
+++ b/pstoraster/pdf2dsc.ps
@@ -0,0 +1,127 @@
+% Copyright (C) 1994, 1995, 1996, 1997 Aladdin Enterprises. All rights reserved.
+%
+% This file is part of GNU Ghostscript.
+%
+% GNU Ghostscript is distributed in the hope that it will be useful, but
+% WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+% to anyone for the consequences of using it or for whether it serves any
+% particular purpose or works at all, unless he says so in writing. Refer
+% to the GNU General Public License for full details.
+%
+% Everyone is granted permission to copy, modify and redistribute GNU
+% Ghostscript, but only under the conditions described in the GNU General
+% Public License. A copy of this license is supposed to have been given
+% to you along with GNU Ghostscript so you can know your rights and
+% responsibilities. It should be in a file named COPYING. Among other
+% things, the copyright notice and this notice must be preserved on all
+% copies.
+%
+% Aladdin Enterprises supports the work of the GNU Project, but is not
+% affiliated with the Free Software Foundation or the GNU Project. GNU
+% Ghostscript, as distributed by Aladdin Enterprises, does not require any
+% GNU software to build or run it.
+
+% $Id: pdf2dsc.ps 1012 2000-04-12 14:20:26Z mike $
+% pdf2dsc.ps
+% read pdf file and produce DSC "index" file.
+%
+% Input file is named PDFname
+% Output file is named DSCname
+%
+% Run using:
+% gs -dNODISPLAY -sPDFname=pdffilename -sDSCname=tempfilename pdf2dsc.ps
+% Then display the PDF file with
+% gs tempfilename
+%
+% Modified by Johannes Plass <plass@dipmza.physik.uni-mainz.de> 1996-11-05:
+% Adds BoundingBox and Orientation if available.
+% Modified by rjl/lpd 9/19/96
+% Updates for compatibility with modified pdf_*.ps code for handling
+% page ranges (i.e., partial files) better.
+% Modified by Geoff Keating <Geoff.Keating@anu.edu.au> 7/3/96:
+% include Title and CreationDate DSC comments (these are displayed by
+% Ghostview);
+% reduce the size of typical output files by a factor of about 3.
+% Modified by L. Peter Deutsch 3/18/96:
+% Removes unnecessary and error-prone code duplicated from pdf_main.ps
+% Modified by L. Peter Deutsch for GS 3.33
+% Originally by Russell Lang 1995-04-26
+
+/DSCfile DSCname (w) file def
+/DSCstring 255 string def
+ GS_PDF_ProcSet begin
+ pdfdict begin
+ PDFname (r) file
+ pdfopen begin
+% setup for loop (init increment limit)
+ /FirstPage where { pop FirstPage } { 1 } ifelse
+ 1
+ /LastPage where { pop LastPage } { pdfpagecount } ifelse
+% write header and prolog
+DSCfile (%!PS-Adobe-3.0\n) writestring
+Trailer /Info knownoget
+ {
+ dup /Title knownoget
+ {
+ DSCfile (%%Title: ) writestring
+ DSCfile exch write==
+ }
+ if
+ /CreationDate knownoget
+ {
+ DSCfile (%%CreationDate: ) writestring
+ DSCfile exch write==
+ }
+ if
+ }
+if
+DSCfile (%%Pages: ) writestring
+DSCfile 1 index 3 index sub 1 add DSCstring cvs writestring
+DSCfile (\n%%EndComments\n) writestring
+DSCfile (%%BeginProlog\n) writestring
+DSCfile (/Page null def\n/Page# 0 def\n/PDFSave null def\n/DSCPageCount 0 def\n) writestring
+DSCfile (/DoPDFPage {dup /Page# exch store pdfgetpage pdfshowpage } def\n) writestring
+DSCfile (GS_PDF_ProcSet begin\npdfdict begin\n) writestring
+DSCfile (%%EndProlog\n) writestring
+DSCfile (%%BeginSetup\n) writestring
+DSCfile PDFname write==only
+DSCfile ( \(r\) file pdfopen begin\n) writestring
+DSCfile (%%EndSetup\n) writestring
+% process each page
+ {
+DSCfile (%%Page: ) writestring
+DSCfile 1 index DSCstring cvs writestring
+DSCfile ( ) writestring
+DSCfile 1 index DSCstring cvs writestring
+DSCfile (\n) writestring
+
+%BEGIN ##jp##
+dup pdfgetpage /CropBox knownoget { % file maxpage pageno cropbox
+ DSCfile (%%PageBoundingBox: ) writestring
+ {DSCfile exch write=only DSCfile ( ) writestring} forall
+ DSCfile (\n) writestring
+} if
+dup pdfgetpage /Rotate knownoget { % file maxpage pageno angle
+ DSCfile (%%PageOrientation: ) writestring
+ 90 div cvi 4 mod dup 0 lt {4 add} if
+ [(Portrait) (Landscape) (UpsideDown) (Seascape)] exch get
+ DSCfile exch writestring
+ DSCfile (\n) writestring
+} if
+%END ##jp##
+
+DSCfile exch DSCstring cvs writestring
+DSCfile ( DoPDFPage\n) writestring
+ } for
+ currentdict pdfclose
+ end
+ end
+ end
+% write trailer
+DSCfile (%%Trailer\n) writestring
+DSCfile (currentdict pdfclose\nend\nend\nend\n) writestring
+DSCfile (%%EOF\n) writestring
+% close output file and exit
+DSCfile closefile
+quit
+% end of pdf2dsc.ps
diff --git a/pstoraster/pdf_base.ps b/pstoraster/pdf_base.ps
new file mode 100644
index 000000000..d8cf23914
--- /dev/null
+++ b/pstoraster/pdf_base.ps
@@ -0,0 +1,589 @@
+% Copyright (C) 1994, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+%
+% This file is part of GNU Ghostscript.
+%
+% GNU Ghostscript is distributed in the hope that it will be useful, but
+% WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+% to anyone for the consequences of using it or for whether it serves any
+% particular purpose or works at all, unless he says so in writing. Refer
+% to the GNU General Public License for full details.
+%
+% Everyone is granted permission to copy, modify and redistribute GNU
+% Ghostscript, but only under the conditions described in the GNU General
+% Public License. A copy of this license is supposed to have been given
+% to you along with GNU Ghostscript so you can know your rights and
+% responsibilities. It should be in a file named COPYING. Among other
+% things, the copyright notice and this notice must be preserved on all
+% copies.
+%
+% Aladdin Enterprises supports the work of the GNU Project, but is not
+% affiliated with the Free Software Foundation or the GNU Project. GNU
+% Ghostscript, as distributed by Aladdin Enterprises, does not require any
+% GNU software to build or run it.
+
+% $Id: pdf_base.ps 1012 2000-04-12 14:20:26Z mike $
+% 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,
+% streams, and name/number trees; 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
+
+% 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
+
+% Read a token from a file, recognizing the PDF 1.2 #nn escape convention.
+% This should be done in C!
+/.pdftoken { % <file> .pdftoken <obj> -true-
+ % <file> .pdftoken -false-
+ token {
+ dup type /nametype eq {
+ dup xcheck {
+ true
+ } {
+ PDFversion 1.2 ge {
+ dup .namestring (#) search {
+ name#escape cvn exch pop
+ } {
+ pop
+ } ifelse
+ } if true
+ } ifelse
+ } {
+ true
+ } ifelse
+ } {
+ false
+ } ifelse
+} bind def
+/name#escape % <post> <(#)> <pre> name#escape <string>
+{ exch pop
+ 1 index 2 () /SubFileDecode filter dup (x) readhexstring
+ % Stack: post pre stream char t/f
+ not { /.pdftoken cvx /syntaxerror signalerror } if
+ exch closefile concatstrings
+ exch 2 1 index length 2 sub getinterval
+ (#) search { name#escape } if concatstrings
+} 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 stack depth, file and opdict
+ % bound into it.
+ 1 index cvlit count 2 sub 3 1 roll mark mark 5 2 roll
+ { % Stack: ..operands.. count opdict file
+ .pdftoken not { (%%EOF) cvn cvx } if
+ dup xcheck
+ { DEBUG { dup == flush } if
+ 2 copy .knownget
+ { exch pop exch pop exch pop exec }
+ { BXlevel 0 le
+ { (%stderr) (w) file
+ dup (****************Unknown operator: ) writestring
+ dup 2 index .writecvs dup (\n) writestring flushfile
+ }
+ if pop pop
+ count exch sub { pop } repeat % pop all the operands
+ }
+ ifelse
+ }
+ { exch pop 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
+
+% Execute a file, like .pdfrun, for a marking context.
+% This temporarily rebinds LocalResources.
+/.pdfruncontext { % <resdict> <file> <opdict> .pdfruncontext -
+ /.pdfrun load /LocalResources LocalResources /store load
+ /LocalResources 8 -1 roll store 4 .execn
+} 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 lget
+ 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 ltype /stringtype eq
+ { % Convert Generations from a string to an array.
+ larray Generations llength lgrowto dup
+ 0 1 2 index llength 1 sub
+ { Generations 1 index lget lput 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 lput
+ } bind def
+
+% ================================ Objects ================================ %
+
+% Since we may have more than 64K objects, we have to use a 2-D array to
+% hold them (and the parallel Generations structure).
+/lshift 9 def
+/lnshift lshift neg def
+/lsubmask 1 lshift bitshift 1 sub def
+/lsublen lsubmask 1 add def
+/larray { % - larray <larray>
+ [ [] ]
+} bind def
+/lstring { % - lstring <lstring>
+ [ () ]
+} bind def
+/ltype { % <lseq> type <type
+ 0 get type
+} bind def
+/lget { % <lseq> <index> lget <value>
+ dup //lsubmask and 3 1 roll //lnshift bitshift get exch get
+} bind def
+/lput { % <lseq> <index> <value> lput -
+ 3 1 roll
+ dup //lsubmask and 4 1 roll //lnshift bitshift get
+ 3 1 roll put
+} bind def
+/llength { % <lseq> llength <length>
+ dup length 1 sub dup //lshift bitshift
+ 3 1 roll get length add
+} bind def
+% lgrowto assumes newlength > llength(lseq)
+/growto { % <string/array> <length> growto <string'/array'>
+ 1 index type /stringtype eq { string } { array } ifelse
+ 2 copy copy pop exch pop
+} bind def
+/lgrowto { % <lseq> <newlength> lgrowto <lseq'>
+ dup //lsubmask add //lnshift bitshift dup 3 index length gt {
+ % Add more sub-arrays. Start by completing the last existing one.
+ % Stack: lseq newlen newtoplen
+ 3 -1 roll dup llength 1 sub //lsubmask or 1 add lgrowto
+ % Stack: newlen newtoplen lseq
+ [ exch aload pop
+ counttomark 2 add -1 roll % newtoplen
+ counttomark sub { dup 0 0 getinterval lsublen growto } repeat
+ dup 0 0 getinterval ] exch
+ } {
+ pop
+ } ifelse
+ % Expand the last sub-array.
+ 1 sub //lsubmask and 1 add
+ exch dup dup length 1 sub 2 copy
+ % Stack: newsublen lseq lseq len-1 lseq len-1
+ get 5 -1 roll growto put
+} bind def
+
+% We keep track of PDF objects using the following PostScript variables:
+%
+% Generations (lstring): Generations[N] holds 1+ the current
+% generation number for object number N. (As far as we can tell,
+% this is needed only for error checking.) For free objects,
+% Generations[N] is 0.
+%
+% Objects (larray): 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.
+%
+% GlobalObjects (dictionary): If object N has been resolved in
+% global VM, GlobalObjects[N] is the same as Objects[N]
+% (except that GlobalObjects itself is stored in global VM,
+% so the entry will not be deleted at the end of the page).
+%
+% IsGlobal (lstring): IsGlobal[N] = 1 iff object N was resolved in
+% global VM. This is an accelerator to avoid having to do a
+% dictionary lookup in GlobalObjects when resolving every object.
+
+% Initialize the PDF object tables.
+/initPDFobjects { % - initPDFobjects -
+ /Objects larray def
+ /Generations lstring def
+ .currentglobal true .setglobal
+ /GlobalObjects 20 dict def
+ .setglobal
+ /IsGlobal lstring def
+} bind def
+
+% Grow the tables to a specified size.
+/growPDFobjects { % <minsize> growPDFobjects -
+ dup Objects llength gt {
+ dup Objects exch lgrowto /Objects exch def
+ } if
+ dup Generations llength gt {
+ dup Generations exch lgrowto /Generations exch def
+ } if
+ dup IsGlobal llength gt {
+ dup IsGlobal exch lgrowto /IsGlobal exch def
+ } if
+ pop
+} bind def
+
+% 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 1 index lget dup xcheck exch type /integertype eq and {
+ IsGlobal 1 index lget 0 eq {
+ pop true
+ } {
+ % Update Objects from GlobalObjects
+ DEBUG { (%Global=>local: ) print dup == } if
+ Objects exch GlobalObjects 1 index get lput false
+ } ifelse
+ } {
+ pop false
+ } ifelse
+} 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 { f } { pop pop pop null } ifelse
+ } bind def
+
+/checkgeneration { % <object#> <generation#> checkgeneration <object#> <OK>
+ Generations 2 index lget 1 sub 1 index eq {
+ pop true
+ } {
+ Generations 2 index lget 0 eq {
+ (Warning: reference to free object: )
+ } {
+ (Warning: wrong generation: )
+ } ifelse print 1 index =only ( ) print =only ( R) = false
+ } ifelse
+} bind def
+/R { % <object#> <generation#> R <object>
+ 1 index unresolved?
+ { /resolveR cvx 3 packedarray cvx }
+ { checkgeneration { Objects exch lget } { 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 {
+ % The only global objects we bother to save are
+ % (resource) dictionaries.
+ 1 index dup gcheck exch type /dicttype eq and {
+ DEBUG { (%Local=>global: ) print dup == } if
+ GlobalObjects 1 index 3 index put
+ IsGlobal 1 index 1 put
+ } if
+ Objects exch 2 index lput
+ } {
+ 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
+ }
+ { % Don't cache if the generation # is wrong.
+ pop pop null
+ }
+ ifelse exch PDFfile exch setfileposition
+ }
+ { pop Objects exch lget
+ }
+ 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,
+% if the stream is not an external one.
+% /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. We used to try to guess whether the file included
+% extraneous \r and/or \n characters, but we no longer attempt to do so,
+% especially since the PDF 1.2 specification states flatly that the only
+% legal terminators following the 'stream' keyword are \n or \r\n, both of
+% which are properly skipped and discarded by the token operator.
+/stream { % <dict> stream <modified_dict>
+ dup /F known dup PDFsource PDFfile eq or {
+ not {
+ dup /File PDFfile put
+ dup /FilePosition PDFfile fileposition put
+ DEBUG { (%FilePosition: ) print dup /FilePosition get == } if
+ } if
+ PDFfile fileposition 1 index /Length oget add
+ PDFfile exch setfileposition
+ } {
+ pop
+ % We're already reading from a stream, which we can't reposition.
+ % Capture the sub-stream contents in a string.
+ 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
+/endstream {
+ exit
+} bind def
+
+% Extract and apply filters.
+/filterparms { % <dict> <DPkey> <Fkey> filterparms
+ % <dict> <parms> <filternames>
+ exch 2 index exch .knownget not { null } if
+ exch 2 index exch .knownget not { { } } if
+ dup type /nametype eq {
+ 1 array astore
+ 1 index null ne { exch 1 array astore exch } if
+ } if
+} bind def
+/applyfilters { % <parms> <source> <filternames> applyfilters <stream>
+ 2 index null eq {
+ { filter }
+ } {
+ { % Stack: parms stream 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
+} 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>
+ 1 index /F .knownget {
+ % This stream is stored on an external file.
+ (r) file exch
+ /FDecodeParms /FFilter filterparms
+ % Stack: readdata? dict parms filternames
+ 4 -1 roll exch
+ pdf_decrypt_stream
+ apply_filters
+ } {
+ exch dup /FilePosition .knownget {
+ 1 index /File get exch setfileposition
+ } if
+ % Stack: readdata? dict
+ /DecodeParms /Filter filterparms
+ % 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
+ } {
+ applyfilters
+ } ifelse
+ } ifelse
+ % Stack: readdata? dict file
+ exch pop exch pop
+} bind def
+
+% ============================ Name/number trees ============================ %
+
+/nameoget { % <nametree> <key> nameoget <obj|null>
+ exch /Names exch .treeget
+} bind def
+
+/numoget { % <numtree> <key> numoget <obj|null>
+ exch /Nums exch .treeget
+} bind def
+
+/.treeget { % <key> <leafkey> <tree> .treeget <obj|null>
+ dup /Kids knownoget {
+ exch pop .branchget
+ } {
+ exch get .leafget
+ } ifelse
+} bind def
+
+/.branchget { % <key> <leafkey> <kids> .branchget <obj|null>
+ dup length 0 eq {
+ pop pop pop null
+ } {
+ dup length -1 bitshift 2 copy oget
+ % Stack: key leafkey kids mid kids[mid]
+ dup /Limits oget aload pop
+ % Stack: key leafkey kids mid kids[mid] min max
+ 6 index lt {
+ pop pop
+ 1 add 1 index length 1 index sub getinterval .branchget
+ } {
+ 5 index gt {
+ pop
+ 0 exch getinterval .branchget
+ } {
+ exch pop exch pop .treeget
+ } ifelse
+ } ifelse
+ } ifelse
+} bind def
+
+/.leafget { % <key> <pairs> .leafget <obj|null>
+ dup length 2 eq {
+ dup 0 get 2 index eq { 1 oget } { pop null } ifelse
+ exch pop
+ } {
+ dup length -1 bitshift -2 and 2 copy oget
+ % Stack: key pairs mid pairs[mid]
+ 3 index gt { 0 exch } { 1 index length 1 index sub } ifelse
+ getinterval .leafget
+ } ifelse
+} bind def
+
+end % pdfdict
+.setglobal
diff --git a/pstoraster/pdf_draw.ps b/pstoraster/pdf_draw.ps
new file mode 100644
index 000000000..a12f36cfc
--- /dev/null
+++ b/pstoraster/pdf_draw.ps
@@ -0,0 +1,626 @@
+% Copyright (C) 1994, 1995, 1997, 1998 Aladdin Enterprises. All rights reserved.
+%
+% This file is part of GNU Ghostscript.
+%
+% GNU Ghostscript is distributed in the hope that it will be useful, but
+% WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+% to anyone for the consequences of using it or for whether it serves any
+% particular purpose or works at all, unless he says so in writing. Refer
+% to the GNU General Public License for full details.
+%
+% Everyone is granted permission to copy, modify and redistribute GNU
+% Ghostscript, but only under the conditions described in the GNU General
+% Public License. A copy of this license is supposed to have been given
+% to you along with GNU Ghostscript so you can know your rights and
+% responsibilities. It should be in a file named COPYING. Among other
+% things, the copyright notice and this notice must be preserved on all
+% copies.
+%
+% Aladdin Enterprises supports the work of the GNU Project, but is not
+% affiliated with the Free Software Foundation or the GNU Project. GNU
+% Ghostscript, as distributed by Aladdin Enterprises, does not require any
+% GNU software to build or run it.
+
+% $Id: pdf_draw.ps 1012 2000-04-12 14:20:26Z mike $
+% pdf_draw.ps
+% PDF drawing operations (graphics, text, and images).
+
+% We don't handle the following PDF 1.0/1.1 elements yet: (identified by
+% style strings, except in a few known fonts
+% font descriptor resources, except for MissingWidth
+% text clipping modes
+% 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 ================================ %
+
+% ---------------- Functions ---------------- %
+
+/fnrdict mark
+ 0 { .resolvefn0 }
+ 2 { }
+ 3 { .resolvefn3 }
+.dicttomark readonly def
+
+/.resolvefn0 {
+ % Don't lose our place in PDFfile.
+ PDFfile fileposition exch
+ dup true resolvestream
+ % The stream isn't positionable, so read all the data now.
+ % Stack: filepos fndict stream
+ 1 index /Range oget length 2 idiv 2 index /BitsPerSample oget mul
+ 2 index /Size oget { mul } forall
+ 7 add 8 idiv string
+ 1 index exch readstring pop exch closefile
+ % Stack: filepos fndict data
+ exch dup length 1 add dict .copydict
+ dup /DataSource 4 -1 roll put
+ .buildfunction
+ exch PDFfile exch setfileposition
+} bdef
+
+/.resolvefn3 {
+ dup /Functions oget mark exch dup { resolvefunction } forall
+ counttomark -1 roll astore cleartomark
+} bdef
+
+/resolvefunction { % <fndict> resolvefunction <function>
+ dup /FunctionType oget //fnrdict exch get exec
+} bdef
+
+/resolveidfunction {
+ dup /Identity eq { pop { } } { resolvefunction } ifelse
+} bdef
+
+% ---------------- Halftones ---------------- %
+
+/spotfunctions mark
+ /Round {
+ abs exch abs 2 copy add 1 le {
+ dup mul exch dup mul add 1 exch sub
+ } {
+ 1 sub dup mul exch 1 sub dup mul add 1 sub
+ } ifelse
+ }
+ /Diamond {
+ abs exch abs 2 copy add .75 le {
+ dup mul exch dup mul add 1 exch sub
+ } {
+ 2 copy add 1.23 le {
+ .85 mul add 1 exch sub
+ } {
+ 1 sub dup mul exch 1 sub dup mul add 1 sub
+ } ifelse
+ } ifelse
+ }
+ /Ellipse {
+ abs exch abs 2 copy 3 mul exch 4 mul add 3 sub dup 0 lt {
+ pop dup mul exch .75 div dup mul add 4 div 1 exch sub
+ } {
+ dup 1 gt {
+ pop 1 exch sub dup mul exch 1 exch sub
+ .75 div dup mul add 4 div 1 sub
+ } {
+ .5 exch sub exch pop exch pop
+ } ifelse
+ } ifelse
+ }
+ /EllipseA { dup mul .9 mul exch dup mul add 1 exch sub }
+ /InvertedEllipseA { dup mul .9 mul exch dup mul add 1 sub }
+ /EllipseB { dup 5 mul 8 div mul exch dup mul exch add sqrt 1 exch sub }
+ /EllipseC { dup mul .9 mul exch dup mul add 1 exch sub }
+ /InvertedEllipseC { dup mul .9 mul exch dup mul add 1 sub }
+ /Line { exch pop abs neg }
+ /LineX { pop }
+ /LineY { exch pop }
+ /Square { abs exch abs 2 copy lt { exch } if pop neg }
+ /Cross { abs exch abs 2 copy gt { exch } if pop neg }
+ /Rhomboid { abs exch abs 0.9 mul add 2 div }
+ /DoubleDot { 2 {360 mul sin 2 div exch } repeat add }
+ /InvertedDoubleDot { 2 {360 mul sin 2 div exch } repeat add neg }
+ /SimpleDot { dup mul exch dup mul add 1 exch sub }
+ /InvertedSimpleDot { dup mul exch dup mul add 1 sub }
+ /CosineDot { 180 mul cos exch 180 mul cos add 2 div }
+ /Double { exch 2 div exch 2 { 360 mul sin 2 div exch } repeat add }
+ /InvertedDouble {
+ exch 2 div exch 2 { 360 mul sin 2 div exch } repeat add neg
+ }
+.dicttomark readonly def
+
+/htrdict mark
+ 1 { .resolveht1 }
+ 5 { .resolveht5 }
+ % We don't support types 6, 10, or 16 yet.
+.dicttomark readonly def
+
+/.resolveht1 {
+ mark exch {
+ oforce
+ 1 index /SpotFunction eq {
+ dup type /nametype eq
+ { //spotfunctions exch get } { resolvefunction }
+ ifelse
+ } {
+ 1 index /TransferFunction eq {
+ resolveidfunction
+ } if
+ } ifelse
+ } forall .dicttomark
+} bdef
+
+/.resolveht5 {
+ mark exch {
+ oforce dup type /dicttype eq { resolvehalftone } if
+ } forall .dicttomark
+} bdef
+
+/resolvehalftone { % <dict> resolvehalftone <halftone>
+ dup /HalftoneType get //htrdict exch get exec
+} bdef
+
+% ---------------- Graphics state management ---------------- %
+
+/cmmatrix matrix def
+drawopdict begin
+ % Graphics state stack
+ /q { q } def
+ /Q { Q } def
+ % Graphics state setting
+ /cm { //cmmatrix astore concat } def
+ /i /setflat load def
+ /J /setlinecap load def
+ /d /setdash load def
+ /j /setlinejoin load def
+ /w /setlinewidth load def
+ /M /setmiterlimit load def
+ /gs { gs } def
+end
+
+/gsparamdict mark
+ /Type { pop }
+ /SA { setstrokeadjust }
+ /OP { setoverprint }
+ /BG { resolveidfunction setblackgeneration }
+ /UCR { resolveidfunction setundercolorremoval }
+ /TR { resolveidfunction settransfer }
+ /HT {
+ dup /Default eq {
+ pop .setdefaultscreen
+ } {
+ %****** NOT FINISHED ******
+ resolvehalftone sethalftone
+ } ifelse
+ }
+ /HTP { aload pop sethalftonephase }
+ /SM { setsmoothness }
+ /Font { aload pop Tf }
+ /LW { setlinewidth }
+ /LC { setlinecap }
+ /LJ { setlinejoin }
+ /ML { setmiterlimit }
+ /D { aload pop setdash }
+ /FL { setflat }
+ /RI { ri }
+.dicttomark readonly def
+/gs { % <gsres> gs -
+ Page /ExtGState rget {
+ { oforce exch gsparamdict exch .knownget { exec } { pop } ifelse }
+ forall
+ } if
+} bdef
+
+% ---------------- Color setting ---------------- %
+
+% The keys here are resolved (PostScript, not PDF) color space names.
+/csncompdict mark
+ /DeviceGray { pop 1 }
+ /DeviceRGB { pop 3 }
+ /DeviceCMYK { pop 4 }
+ /CIEBasedA { pop 1 }
+ /CIEBasedABC { pop 3 }
+ /Separation { pop 1 }
+ /DeviceN { 2 oget length }
+.dicttomark readonly def
+
+% Perhaps some of the values in the following need to be modified
+% depending on the WhitePoint value....
+/cslabinit mark
+ /DecodeABC [{16 add 116 div} bind {500 div} bind {200 div} bind]
+ /MatrixABC [1 1 1 1 0 0 0 0 -1]
+ /DecodeLMN [
+ {dup 6 29 div ge {dup dup mul mul} {4 29 div sub 108 841 div mul} ifelse
+ 0.9505 mul} bind
+ {dup 6 29 div ge {dup dup mul mul} {4 29 div sub 108 841 div mul} ifelse
+ } bind
+ {dup 6 29 div ge {dup dup mul mul} {4 29 div sub 108 841 div mul} ifelse
+ 1.0890 mul} bind
+ ]
+.dicttomark readonly def
+
+/csrdict mark
+ /DeviceGray {
+ /DefaultGray Page /ColorSpace rget { exch pop resolvecolorspace } if
+ }
+ /DeviceRGB {
+ /DefaultRGB Page /ColorSpace rget { exch pop resolvecolorspace } if
+ }
+ /DeviceCMYK { }
+ /CalGray {
+ 1 oget dup /Gamma knownoget {
+ exch dup length 1 add dict .copydict
+ dup /DecodeA 4 -1 roll /exp load 2 packedarray cvx put
+ } if
+ /CIEBasedA exch 2 array astore
+ }
+ /CalRGB {
+ 1 oget dup /Gamma known 1 index /Matrix known or {
+ dup length 2 add dict .copydict
+ dup /Matrix knownoget { 1 index /MatrixABC 3 -1 roll put } if
+ dup /Gamma knownoget {
+ [ exch { /exp load 2 packedarray cvx } forall
+ ] 1 index /DecodeABC 3 -1 roll put
+ } if
+ }
+ if /CIEBasedABC exch 2 array astore
+ }
+ /CalCMYK {
+ pop /DeviceCMYK % not defined by Adobe
+ }
+ /Lab {
+ 1 oget dup length 3 add dict .copydict
+ dup /Range knownoget not { [-100 100 -100 100] } if
+ [-100 0 null null null null] dup 2 4 -1 roll putinterval
+ 1 index /RangeABC 3 -1 roll put
+ //cslabinit exch copy
+ /CIEBasedABC exch 2 array astore
+ }
+ /ICCBased {
+ % Since the PostScript interpreter doesn't support these yet,
+ % always substitute an alternate space.
+ dup /Alternate knownoget {
+ exch pop
+ } {
+ /N get { null /DeviceGray null /DeviceRGB /DeviceCMYK } exch get
+ } ifelse resolvecolorspace
+ }
+ /Separation {
+ aload pop exch oforce resolvecolorspace exch oforce resolvefunction
+ 4 array astore
+ }
+ /DeviceN {
+ aload pop 3 -1 roll oforce
+ 3 -1 roll oforce resolvecolorspace
+ 3 -1 roll oforce resolvefunction
+ 4 array astore
+ }
+ /Indexed {
+ aload pop 3 -1 roll resolvecolorspace 3 1 roll
+ oforce dup type /stringtype eq {
+ pop
+ } {
+ % The color lookup table is a stream.
+ % Get its contents. Don't lose our place in PDFfile.
+ % Stack: /Indexed basespace hival lookup
+ PDFfile fileposition 5 1 roll true resolvestream
+ % Stack: filepos /Indexed basespace hival lookupstream
+ 1 index 1 add
+ % Stack: filepos /Indexed basespace hival lookupstream len
+ 3 index
+ dup dup type /arraytype eq { 0 get } if
+ //csncompdict exch get exec mul
+ string readstring pop
+ % Stack: filepos /Indexed basespace hival table
+ 5 -1 roll PDFfile exch setfileposition
+ }
+ ifelse 4 array astore
+ }
+ /Pattern
+ { dup type /nametype ne
+ { dup length 1 ge
+ { 1 get resolvecolorspace
+ /Pattern exch 2 array astore
+ }
+ if
+ }
+ if
+ }
+.dicttomark readonly def
+
+/cssubst { % <csname> cssubst <cspace'> true
+ % <csname> cssubst false
+ dup resolvecolorspace
+ dup 1 index ne { exch pop true } { pop pop false } ifelse
+} bdef
+
+/csresolve % <csresourcename> csresolve <cspace>
+ { dup Page /ColorSpace rget
+ { exch pop resolvecolorspace }
+ { /undefined cvx signalerror }
+ ifelse
+ } bdef
+/resolvecolorspace { % <cspace> resolvecolorspace <cspace'>
+ dup dup type /arraytype eq { 0 get } if
+ //csrdict exch .knownget {
+ exec dup type /nametype ne { dup length 1 eq { 0 get } if } if
+ } {
+ csresolve
+ } ifelse
+} bdef
+
+/scresolve % <c0> ... scresolve <multi>
+ { % We can't really make sc[n] and SC[N] work, because
+ % the color space information isn't available at
+ % conversion time; so we hack it by assuming that
+ % all the operands on the stack are used, and that
+ % if the top operand is a name, it's a Pattern resource.
+ dup type /nametype eq
+ { Page /Pattern rget { resolvepattern } { null } ifelse }
+ if
+ count 1 gt
+ } bdef
+/resolvepattern { % <patternstreamdict> resolvepattern <patterndict>
+ % Don't do the resolvestream now: just capture the data
+ % from the file if necessary.
+ dup length dict copy
+ dup /FilePosition .knownget {
+ 1 index /File get dup fileposition 3 1 roll
+ % Stack: dict savepos pos file
+ dup 3 -1 roll setfileposition
+ dup 3 index /Length get string readstring pop
+ % Stack: dict savepos file string
+ 3 1 roll exch setfileposition
+ 1 index /File 3 -1 roll put
+ dup /FilePosition undef
+ } if
+ dup /PaintProc {
+ DEBUG { (%Begin PaintProc) = flush } if
+ % For uncolored patterns, we have to unbind the current
+ % color and color space before running the PaintProc.
+ % There's no harm in doing this for colored patterns,
+ % so for simplicity, we always do it.
+ q
+ null sc1 null SC1
+ false resolvestream pdfopdict .pdfrun
+ Q
+ DEBUG { (%End PaintProc) = flush } if
+ } put
+ DEBUG {
+ (%Pattern: ) print dup === flush
+ } if
+} bdef
+
+drawopdict begin
+ /g { /DeviceGray cssubst { cs sc1 } { g } ifelse } bdef
+ /rg { /DeviceRGB cssubst { cs sc* } { rg } ifelse } bdef
+ /k { k } bdef
+ /cs { csresolve cs } bdef
+ /sc { scresolve { sc* } { sc1 } ifelse } bdef
+ /scn /sc load def
+ /G { /DeviceGray cssubst { CS SC1 } { G } ifelse } bdef
+ /RG { /DeviceRGB cssubst { CS SC* } { RG } ifelse } bdef
+ /K { K } bdef
+ /CS { csresolve CS } bdef
+ /SC { scresolve { SC* } { SC1 } ifelse } bdef
+ /SCN /SC load def
+end
+
+% ---------------- Paths ---------------- %
+
+drawopdict begin
+ % Path construction
+ /m /moveto load def
+ /l /lineto load def
+ /c /curveto load def
+ /v { currentpoint 6 2 roll curveto } def
+ /y { 2 copy curveto } def
+ /re {
+ 4 2 roll moveto exch dup 0 rlineto 0 3 -1 roll rlineto neg 0 rlineto
+ closepath
+ } def
+ /h /closepath load 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 }
+ /PS { DoPS }
+.dicttomark readonly def
+
+% Note that the keys in defaultdecodedict are resolved (PostScript, not PDF)
+% color space names.
+/01_1 [0 1] readonly def
+/01_3 [0 1 0 1 0 1] readonly def
+/01_4 [0 1 0 1 0 1 0 1] readonly def
+/defaultdecodedict mark
+ /DeviceGray { pop //01_1 }
+ /DeviceRGB { pop //01_3 }
+ /DeviceCMYK { pop //01_4 }
+ /CIEBasedA { 1 get /RangeA .knownget not { //01_1 } if }
+ /CIEBasedABC { 1 get /RangeABC .knownget not { //01_3 } if }
+ /Separation { pop //01_1 }
+ /DeviceN {
+ dup 1 oget length [ exch {0 1} repeat ] readonly
+ }
+.dicttomark readonly def
+
+% dict makeimagedict dict
+/makeimagedict
+ { dup length dict
+ 1 index /ColorSpace knownoget
+ { dup resolvecolorspace
+ dup type /arraytype eq { 0 get } {pop} ifelse
+ 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 ColorSpace exch exec }
+ ifelse
+ }
+ ifelse
+ }
+ if def
+ /Interpolate 2 copy knownoget { def } { pop } ifelse
+ /ImageMatrix Width 0 0 Height neg 0 Height 6 array astore 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
+ /DataSource exch def
+ currentdict end
+} bdef
+
+/DoImage {
+ makeimagedict
+ dup /Mask knownoget {
+ dup type /arraytype eq {
+ 1 index /ImageType 4 put
+ 1 index /MaskColor 3 -1 roll put
+ } {
+ % Mask is a stream, another Image XObject.
+ makeimagedict
+ 4 dict begin
+ /ImageType 3 def
+ /InterleaveType 3 def
+ /MaskDict exch def
+ /DataDict exch def
+ currentdict end
+ } ifelse
+ } if
+ dup /ImageMask get
+ { setfillcolor imagemask }
+ { dup /ColorSpace get csset setcolorspace pop image }
+ ifelse
+} bdef
+
+/DoForm {
+ dup length dict copy
+ dup [ /pop load 2 index /Resources knownoget { oforce } { 0 dict } ifelse
+ 3 index false /resolvestream cvx
+ pdfopdict /.pdfruncontext cvx
+ ] cvx /PaintProc exch put
+ execform
+} bdef
+
+/DoPS {
+ true resolvestream cvx exec
+} bdef
+
+drawopdict begin
+ /Do
+ { PDFfile fileposition exch
+ dup Page /XObject rget not { /undefined cvx signalerror } if
+ exch pop 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 /Fl /FlateDecode
+ /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 { 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..3c400ff6b
--- /dev/null
+++ b/pstoraster/pdf_font.ps
@@ -0,0 +1,601 @@
+% Copyright (C) 1994, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+%
+% This file is part of GNU Ghostscript.
+%
+% GNU Ghostscript is distributed in the hope that it will be useful, but
+% WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+% to anyone for the consequences of using it or for whether it serves any
+% particular purpose or works at all, unless he says so in writing. Refer
+% to the GNU General Public License for full details.
+%
+% Everyone is granted permission to copy, modify and redistribute GNU
+% Ghostscript, but only under the conditions described in the GNU General
+% Public License. A copy of this license is supposed to have been given
+% to you along with GNU Ghostscript so you can know your rights and
+% responsibilities. It should be in a file named COPYING. Among other
+% things, the copyright notice and this notice must be preserved on all
+% copies.
+%
+% Aladdin Enterprises supports the work of the GNU Project, but is not
+% affiliated with the Free Software Foundation or the GNU Project. GNU
+% Ghostscript, as distributed by Aladdin Enterprises, does not require any
+% GNU software to build or run it.
+
+% $Id: pdf_font.ps 1012 2000-04-12 14:20:26Z mike $
+% 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 0 exch {
+ % Stack: enc' code element
+ 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
+ } {
+ % Work around a bug in pdfTeX, which can generate Encoding
+ % vectors containing nulls.
+ 1 index null ne {
+ Metrics 3 1 roll put
+ } {
+ pop pop
+ } ifelse
+ }
+ 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
+ % Work around the abovementioned pdfTeX bug.
+ dup null ne {
+ 2 copy known not { 2 copy 4 index put } if pop
+ } {
+ pop
+ } ifelse
+ }
+ forall pop pop pop
+ exch Encoding Metrics end
+ }
+ { null
+ }
+ ifelse
+ } bdef
+
+% ---------------- Descriptors ---------------- %
+
+% Partial descriptors for the 14 built-in fonts. Note that
+% from PDF 1.1 to PDF 1.2, the meaning of the Flag 6 in the FontDescriptor
+% object has undergone a subtle change in its meaning which has serious
+% consequences for searching with Acrobat:
+% In PDF 1.1, the flag meant: Font has StandardEncoding
+% In PDF 1.2, the flag means: Font has (subset of) StandardRomanCharacterSet
+/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>
+ % If the font isn't available, synthesize one based on
+ % its descriptor.
+ dup /Font resourcestatus {
+ pop pop findfont
+ } {
+ pop dup /FontDescriptor knownoget {
+ % Right now, all we look at are the Flags for fixed-width,
+ % serif, italic, and force-bold.
+ /Flags oget
+ dup 16#40 and -4 bitshift
+ 1 index 16#40000 and -15 bitshift add
+ exch 3 and add {
+ Helvetica Courier Times-Roman Courier
+ Helvetica-Oblique Courier-Oblique Times-Italic Courier-Oblique
+ Helvetica-Bold Courier-Bold Times-Bold Courier-Bold
+ Helvetica-BoldOblique Courier-BoldOblique Times-BoldItalic Courier-BoldOblique
+ } exch get findfont
+ } {
+ % No descriptor available, use the default algorithm.
+ findfont
+ } ifelse
+ } ifelse 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
+/readtype1dict 5 dict dup begin
+ /definefont {
+ dup wcheck not { dup length dict copy } if
+ exch pop savedFontName exch
+ //systemdict /definefont get exec
+ } bdef
+ /eexec {
+ 55665 /eexecDecode filter
+ //systemdict begin readtype1dictcopy begin cvx stopped
+ currentdict readtype1dictcopy eq { end } if
+ currentdict //systemdict eq { end } if
+ { stop } if
+ } bdef
+end readonly def
+/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
+ % Rebind definefont so we can substitute the FontName
+ % from the descriptor.
+ //readtype1dict dup length 2 add dict copy begin
+ 1 index /FontDescriptor oget /FontName oget /savedFontName exch def
+ /readtype1dictcopy currentdict def
+ { run } aload pop count 1 sub 2 packedarray cvx exec
+ end 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
+ /Resources 1 index /Resources knownoget { oforce } { 0 dict } ifelse 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 {
+ % Stack: font glyphname
+ 1 index /CharProcs get exch oget
+ PDFfile fileposition exch
+ false resolvestream
+ % Stack: font filepos stream
+ % Don't let setgcolor set the color inside the BuildGlyph
+ % procedure, because this causes an /undefined error.
+ q null /FillColor gput null /StrokeColor gput
+ 2 index /Resources get exch pdfopdict .pdfruncontext
+ Q
+ PDFfile exch setfileposition pop
+ } 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
+
+% Read an embedded TrueType font.
+/readtruetype % <font-resource> <stream-dict> readtruetype <font>
+ { % This is much simpler than readtype1, because we don't
+ % have to deal with the tripartite .PFB format.
+ PDFfile fileposition 3 1 roll
+ true resolvestream readfontfilter .loadttfont
+ dup /FontName get exch definefont exch pop
+ PDFfile 3 -1 roll setfileposition
+ } bdef
+
+% ---------------- Type 0 fonts ---------------- %
+
+% Predefine the known CMaps, but only create them on demand.
+/knownCMaps mark
+ /Identity-H { /Identity-H 0 makeIdentityCMap }
+ /Identity-V { /Identity-V 1 makeIdentityCMap }
+.dicttomark def
+
+/makeIdentityCMap { % <cmapname> <wmode> .makeIdentityCMap -
+ .currentglobal true .setglobal 3 1 roll
+ /CIDInit /ProcSet findresource begin
+ 12 dict begin
+ /WMode exch def
+ /CMapName exch def
+ begincmap
+ /CIDSystemInfo 3 dict dup begin
+ /Registry (Adobe) def
+ /Ordering (Japan1) def
+ /Supplement 0 def
+ end def
+ %/CMapName ... def
+ /CMapVersion 1 def
+ /CMapType 1 def
+ %/WMode ... def
+ 2 begincodespacerange
+ <0001> <00ff>
+ <0100> <ffff>
+ endcodespacerange
+ 2 begincidrange
+ <0001> <00ff> 1
+ <0100> <ffff> 256
+ endcidrange
+ endcmap
+ CMapName currentdict /CMap defineresource
+ knownCMaps CMapName 2 index put
+ end % CMap
+ end % CIDInit ProcSet
+ exch .setglobal
+} bdef
+
+/buildType0 % <Type0-font-resource> buildType0 <font>
+{ 10 dict begin
+ /FontType 0 def
+ /FontMatrix 1 index /FontMatrix knownoget not { matrix } if def
+ /FontName 1 index /BaseFont get def
+ /FMapType 9 def
+ /Encoding [
+ 0 1 4 index /DescendantFonts oget length 1 sub { } for
+ ] def
+ /FDepVector [
+ 2 index /DescendantFonts oget { exec resourcefont } forall
+ ] def
+ /CMap 1 index /Encoding oget
+ dup type /nametype eq {
+ dup /CMap resourcestatus {
+ pop pop /CMap findresource
+ } {
+ knownCMaps 1 index .knownget
+ { exch pop exec } { /undefined signalerror } ifelse
+ } ifelse
+ } {
+ resolvestream
+ } ifelse
+ def
+ FontName currentdict end definefont exch pop
+} bdef
+
+% ---------------- CIDFontType0/2 fonts ---------------- %
+
+% Insert metrics into a CIDFont, by saving the PDF W, W2, DW, and DW2
+% arrays and using a (currently very inefficient) CDevProc.
+/addCIDmetrics { % <CIDFont-resource> addCIDmetrics <fontdict>
+ dup /BaseFont get /CIDFont findresource
+ dup length 5 add dict .copydict
+ dup /FID undef
+ dup /UniqueID undef
+ dup /XUID undef
+ % Insert the widths into the font.
+ {W W2 DW DW2} {
+ % Stack: pdfresource newfont key
+ 2 index 1 index .knownget {
+ 2 index 3 1 roll put
+ } {
+ pop
+ } ifelse
+ } forall
+ dup /CDevProc 1 index /CIDWProc load /exec load 3 packedarray cvx put
+ exch pop
+} bdef
+
+% Apply the [D]W[2] metrics to a character before displaying.
+/CIDWProc { % <w0x> <w0y> <llx> <lly> <urx> <ury>
+ % <w1x> <w1y> <vx> <vy> <cid> <font> CIDWproc
+ % <w0x'> ... <vy'>
+ begin
+ % Look up and apply [D]W
+ 10 index
+ currentdict /DW .knownget { 1000 div exch pop } if
+ currentdict /W .knownget {
+ % Search the W array for the CID.
+ % ****** NOT IMPLEMENTED YET ******
+ pop
+ } if
+ 0 13 2 roll 11 -2 roll pop pop
+ % Look up and apply [D]W2
+ % ****** NOT IMPLEMENTED YET ******
+ pop end
+} bdef
+
+/buildCIDType0 { % <CIDFontType0-font-resource> buildCIDType0 <font>
+ addCIDmetrics dup /CIDFontName get exch /CIDFont defineresource
+} bdef
+
+/buildCIDType2 { % <CIDFontType2-font-resource> buildCIDType2 <font>
+ addCIDmetrics
+ %****** Handle CIDToGIDMap ******
+ dup /BaseFont get exch /CIDFont defineresource
+} bdef
+
+% ---------------- Other embedded fonts ---------------- %
+
+/fontloadprocs mark
+ /Type1C /readType1C cvx
+.dicttomark readonly def
+
+% Read an embedded compressed font.
+/readType1C % <font-resource> <stream-dict> readType1C <font>
+ { PDFfile fileposition 3 1 roll
+ dup true resolvestream dup readfontfilter
+ % Stack: pos resource streamdict stream filter
+ 3 index /FontDescriptor oget /FontName oget
+ 1 index FRD
+ closefile closefile pop
+ PDFfile 3 -1 roll setfileposition
+ /FontDescriptor oget /FontName oget findfont
+ } bdef
+
+% ---------------- Font lookup ---------------- %
+
+/fonttypeprocs mark % <font-resource> -proc- <font>
+ /Type0 /buildType0 cvx
+ /Type1 /buildType1 cvx
+ /MMType1 1 index
+ /Type3 /buildType3 cvx
+ /TrueType /buildTrueType cvx
+ /CIDFontType0 /buildCIDType0 cvx
+ /CIDFontType2 /buildCIDType2 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
+ { % Stack: font-res font-res font-desc
+ dup /FontFile knownoget
+ { exch pop 1 index 3 1 roll readtype1 adjustfont true }
+ { dup /FontFile2 knownoget
+ { exch pop 1 index 3 1 roll readtruetype adjustfont true }
+ { /FontFile3 knownoget
+ { 1 index exch dup /Subtype get fontloadprocs exch get exec adjustfont true }
+ { false }
+ ifelse
+ }
+ ifelse
+ }
+ ifelse
+ }
+ { false }
+ ifelse
+ % Stack: font-res font-res false
+ % -or-: font-res font true
+ not
+ { dup /Subtype get fonttypeprocs exch get exec }
+ if
+ 2 copy /PSFont exch put
+ exch pop
+ }
+ ifelse
+ } bdef
+
+drawopdict begin
+ /d0 /setcharwidth load def
+ /d1 /setcachedevice load def
+ /Tf
+ { 1 index Page /Font rget not { 1 index /undefinedfont signalerror } if
+ resourcefont exch Tf pop
+ } 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..a140e77a1
--- /dev/null
+++ b/pstoraster/pdf_main.ps
@@ -0,0 +1,523 @@
+% Copyright (C) 1994, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+%
+% This file is part of GNU Ghostscript.
+%
+% GNU Ghostscript is distributed in the hope that it will be useful, but
+% WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+% to anyone for the consequences of using it or for whether it serves any
+% particular purpose or works at all, unless he says so in writing. Refer
+% to the GNU General Public License for full details.
+%
+% Everyone is granted permission to copy, modify and redistribute GNU
+% Ghostscript, but only under the conditions described in the GNU General
+% Public License. A copy of this license is supposed to have been given
+% to you along with GNU Ghostscript so you can know your rights and
+% responsibilities. It should be in a file named COPYING. Among other
+% things, the copyright notice and this notice must be preserved on all
+% copies.
+%
+% Aladdin Enterprises supports the work of the GNU Project, but is not
+% affiliated with the Free Software Foundation or the GNU Project. GNU
+% Ghostscript, as distributed by Aladdin Enterprises, does not require any
+% GNU software to build or run it.
+
+% $Id: pdf_main.ps 1012 2000-04-12 14:20:26Z mike $
+% pdf_main.ps
+% PDF file- and page-level operations.
+
+/.setlanguagelevel where { pop 2 .setlanguagelevel } if
+.currentglobal true .setglobal
+/pdfdict where { pop } { /pdfdict 100 dict def } ifelse
+pdfdict begin
+
+% Patch in an obsolete variable used by some third-party software.
+/#? false def
+
+% 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
+ % PDF 1.1 operators
+ /BX { /BXlevel BXlevel 1 add store } bind
+ /EX { /BXlevel BXlevel 1 sub store } bind
+ /PS { cvx exec } bind
+ % PDF 1.2 operators
+ /BMC { pop } bind
+ /BDC { pop pop } bind
+ /EMC { }
+ /MP { pop } bind
+ /DP { pop pop } bind
+.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 .runps
+ }
+ ifelse
+ }
+ { closefile
+ }
+ ifelse
+ } bind odef
+/runpdf % <file> runpdf -
+ { userdict begin
+ /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
+end % systemdict
+% Redefine the procedure that the C code uses for running piped input.
+% It is OK to use { (%stdin) run } here, because a startjob cannot occur.
+/.runstdin {
+ { (%stdin) run } execute0
+} bind def
+
+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
+ % In some PDF files, this position actually points to
+ % white space before the xref line. Skip over this here.
+ { PDFfile fileposition PDFfile read pop 32 gt { exit } if pop
+ } loop
+ 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 growPDFobjects
+ PDFfile fileposition 3 -1 roll
+ { Objects 2 index lget null eq % later update might have set it
+ { Objects 2 index 2 index cvx lput }
+ 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 header, trailer, and cross-reference.
+/pdfopen % <file> pdfopen <dict>
+ { pdfdict readonly pop % can't do it any earlier than this
+ 15 dict begin
+ /LocalResources 0 dict def
+ /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 cvr /PDFversion exch def
+ 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
+ % Stack: PDFfile endpos
+ % The top of the stack is now the file position of the last
+ % non-garbage character.
+ % We can't use prevline to check for the %%EOF, because if the
+ % %%EOF isn't followed by an EOL, prevline will read past the
+ % end of the file, and the file will get closed.
+ 5 sub 2 copy setfileposition
+ 1 index (xxxxxx) readstring pop
+ dup (\015%%EOF) eq exch (\012%%EOF) eq or not
+ { /pdfopen cvx /syntaxerror signalerror } if
+ 1 add setfileposition
+ prevline cvi % xref start position
+ exch PDFfile exch setfileposition
+ prevline (startxref) linene { /pdfopen cvx /syntaxerror signalerror } if
+ pop
+ % Stack: xrefpos
+ initPDFobjects
+ % 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
+ % Copy bookmarks (outline) to the output.
+ Trailer /Root oget /Outlines knownoget
+ { /First knownoget
+ { { dup writeoutline /Next knownoget not { exit } if } loop } 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
+ PDFfile closefile
+ end
+ } 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 value of a resource on a given page.
+/rget { % <resname> <pagedict> <restype> rget <value> -true-
+ % <resname> <pagedict> <restype> rget -false-
+ LocalResources 1 index knownoget {
+ 3 index knownoget
+ } {
+ false
+ } ifelse {
+ exch pop exch pop exch pop true
+ } {
+ exch /Resources pget {
+ exch knownoget { exch knownoget } { pop false } ifelse
+ } {
+ pop 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
+/resolvedest { % <name|string|other> resolvedest <other|null>
+ dup type /nametype eq {
+ Trailer /Root oget /Dests oget exch knownoget not { null } if
+ } {
+ dup type /stringtype eq {
+ Trailer /Root oget /Names oget /Dests knownoget {
+ exch nameoget
+ } {
+ pop null
+ } ifelse
+ } if
+ } ifelse
+} bind def
+/linkdest { % <link|outline> linkdest
+ % ([/Page <n>] /View <view> | ) <link|outline>
+ dup /Dest knownoget
+ { resolvedest
+ dup type /dicttype eq { /D get } if
+ dup null eq
+ { pop }
+ { 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
+ }
+ ifelse
+ }
+ if
+} bind def
+% <pagedict> mark ... -proc- -
+/namedactions 8 dict dup begin
+ /FirstPage {
+ /Page 1 3 -1 roll
+ } def
+ /LastPage {
+ counttomark 2 add index pdfpagecount /Page exch 3 -1 roll
+ } def
+ /NextPage {
+ counttomark 2 add index pdfpagenumber 1 add /Page exch 3 -1 roll
+ } def
+ /PrevPage {
+ counttomark 2 add index pdfpagenumber 1 sub /Page exch 3 -1 roll
+ } def
+end readonly def
+% <pagedict> <annotdict> -proc- -
+/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 dup /A knownoget {
+ dup /D knownoget {
+ exch pop exch dup length dict copy dup /Dest 4 -1 roll put
+ } {
+ /N knownoget { % Assume /S /Named
+ namedactions exch .knownget { exec } if
+ } if
+ } ifelse
+ } if
+ linkdest pop /LNK pdfmark
+ } bind def
+end readonly 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
+ /DSCPageCount DSCPageCount 1 add store
+ } 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
+ { 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.
+ 2 dict begin
+ /BXlevel 0 def
+ matrix currentmatrix beginpage setmatrix
+ /Contents knownoget not { 0 array } if
+ dup type /arraytype ne { 1 array astore } if
+ { oforce false resolvestream pdfopdict .pdfrun } forall
+ endpage
+ end % scratch dict
+ grestore
+ } bind def
+
+end % pdfdict
+.setglobal
diff --git a/pstoraster/pdf_ops.ps b/pstoraster/pdf_ops.ps
new file mode 100644
index 000000000..97b05523a
--- /dev/null
+++ b/pstoraster/pdf_ops.ps
@@ -0,0 +1,406 @@
+% Copyright (C) 1994, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+%
+% This file is part of GNU Ghostscript.
+%
+% GNU Ghostscript is distributed in the hope that it will be useful, but
+% WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+% to anyone for the consequences of using it or for whether it serves any
+% particular purpose or works at all, unless he says so in writing. Refer
+% to the GNU General Public License for full details.
+%
+% Everyone is granted permission to copy, modify and redistribute GNU
+% Ghostscript, but only under the conditions described in the GNU General
+% Public License. A copy of this license is supposed to have been given
+% to you along with GNU Ghostscript so you can know your rights and
+% responsibilities. It should be in a file named COPYING. Among other
+% things, the copyright notice and this notice must be preserved on all
+% copies.
+%
+% Aladdin Enterprises supports the work of the GNU Project, but is not
+% affiliated with the Free Software Foundation or the GNU Project. GNU
+% Ghostscript, as distributed by Aladdin Enterprises, does not require any
+% GNU software to build or run it.
+
+% $Id: pdf_ops.ps 1012 2000-04-12 14:20:26Z mike $
+% pdf_ops.ps
+% Definitions for most of the PDF operators.
+
+.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
+
+userdict /GS_PDF_ProcSet 127 dict dup begin
+
+% ---------------- Abbreviations ---------------- %
+
+/bdef { bind def } bind def
+
+% ---------------- 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
+% Some PDF files have excess Q operators!
+/Q {
+ currentdict /self .knownget { exec //nodict eq { end grestore } if } if
+} bdef
+
+% ---------------- 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
+/nullpattern1 mark
+ /PatternType 1 /PaintType 1 /TilingType 3 /BBox [0 0 0 0]
+ /XStep 1 /YStep 1 /PaintProc { }
+.dicttomark readonly def
+/nullpattern2 nullpattern1 dup length dict copy readonly def
+
+% Each entry in the color space dictionary is a procedure of the form
+% <cspace> -proc- <cspace> <initial-color>
+/CSdict mark
+ /DeviceGray { 0 } bind
+ /DeviceRGB { [0 0 0] cvx } bind
+ /DeviceCMYK { [0 0 0 1] cvx } bind
+ /CIEBasedA { 0 } bind
+ /CIEBasedABC { [0 0 0] cvx } bind
+ /Separation { 1 } bind
+ /DeviceN { % What is the correct value??
+ [ 1 index 1 get length { 1 } repeat ] cvx
+ } bind
+ /Indexed { 0 } bind
+ /Pattern {
+ dup type /nametype eq 1 index length 1 eq or {
+ //nullpattern1 matrix makepattern
+ } {
+ //nullpattern2 matrix makepattern 1 index 1 get csset
+ % Stack: patternspace nullpattern basecolor basespace
+ pop [ 3 1 roll dup type /arraytype eq { aload pop } if
+ counttomark -1 roll ] cvx
+ } ifelse
+ } bind
+.dicttomark readonly def
+/csset % <cspace> csset <color> <cspace>
+ { dup dup type /nametype ne { 0 get } if //CSdict exch get exec exch
+ } bdef
+
+/g { //csdevgray fcput } bdef
+/G { //csdevgray scput } bdef
+/rg { 3 array astore cvx //csdevrgb fcput } bdef
+/RG { 3 array astore cvx //csdevrgb scput } bdef
+/k { 4 array astore cvx //csdevcmyk fcput } bdef
+/K { 4 array astore cvx //csdevcmyk scput } bdef
+/cs { csset fcput } bdef
+/CS { csset scput } bdef
+% We have to break up sc according to the number of operands.
+/sc1 { /FillColor gput } bdef
+/SC1 { /StrokeColor gput } bdef
+/sc* { /FillColor load astore pop } bdef
+/SC* { /StrokeColor load astore pop } bdef
+
+% ---------------- Color installation ---------------- %
+
+% Establish a given color (and color space) as current.
+/setfillcolor { FillColor FillColorSpace setgcolor } def
+/setstrokecolor { StrokeColor StrokeColorSpace setgcolor } def
+/Cdict 15 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
+ /Separation /CIEBasedA load def
+ /DeviceN /CIEBasedA load def
+ /Indexed /CIEBasedA load def
+ /Pattern
+ { dup currentcolorspace eq { pop } { setcolorspace } ifelse
+ dup /Matrix get makepattern setcolor
+ } bdef
+end def
+/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
+
+% ---------------- Path painting and clipping ---------------- %
+
+/S { setstrokecolor /stroke fsexec } bdef
+/f { setfillcolor /fill fsexec } bdef
+/f* { setfillcolor /eofill fsexec } bdef
+/n { newpath } bdef % don't allow n to get bound in
+/s { closepath S } bdef
+/B { gsave setfillcolor fill grestore S } bdef
+/b { closepath B } bdef
+/B* { gsave setfillcolor eofill grestore S } bdef
+/b* { closepath B* } bdef
+
+% Clipping:
+
+/Wdict 4 dict dup begin
+/S { gsave setstrokecolor stroke grestore n } bdef
+/f { gsave setfillcolor fill grestore n } bdef
+/f* { gsave setfillcolor eofill grestore n } bdef
+/n { end clip newpath } bdef
+end readonly def
+/W { //Wdict begin } bdef
+/W*dict 4 dict dup begin
+Wdict { def } forall
+/n { end eoclip newpath } bdef
+end readonly def
+/W* { //W*dict begin } bdef
+
+% ---------------- 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
+ } bdef
+/ET
+ { TextSaveMatrix setmatrix
+ } bdef
+/Tc { /TextSpacing gput { showfirst } /Show gput } bdef
+/TL { /TextLeading gput } bdef
+/Tr { /TextRenderingMode gput { showfirst } /Show gput } bdef
+/Ts { /TextRise gput settextstate } bdef
+/Tw { /WordSpacing gput { showfirst } /Show gput } bdef
+/Tz { 100 div /TextHScaling gput settextstate } bdef
+
+% ---------------- Font control ---------------- %
+
+/Tf % <font> <scale> Tf -
+ { dup 1 eq { pop } { scalefont } ifelse
+ dup setfont /TextFont gput
+ } bdef
+
+% Read a CFF font.
+/FRD % <resdict> <file> FRD -
+ { /FontSetInit /ProcSet findresource begin ReadData
+ } bdef
+
+% 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 { dup neg /TextLeading gput Td } bdef
+/T* { 0 TextLeading neg Td } bdef
+/Tm
+ { TextMatrix astore pop settextstate
+ 0 0 /TextOrigin load astore pop
+ 0 0 moveto
+ } bdef
+
+% ---------------- 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
+ % Implement the combination of t3 and false charpath.
+ % Note that we must use cshow for this, because we
+ % can't parse multi-byte strings any other way.
+ % Stack: string xword xchar
+ { pop pop (x) dup 0 3 index put false charpath
+ % Stack: xword xchar ccode
+ 3 copy 32 eq { add } { exch pop } ifelse 0 rmoveto pop
+ }
+ 4 -1 roll cshow pop pop
+ textrenderingprocs TextRenderingMode get exec
+ }
+ }
+ ifelse
+ }
+ ifelse /Show gput
+ } bdef
+/showfirst { setshowstate Show } def
+
+/Tj { Show } bdef
+/' { T* Show } bdef
+/" { exch Tc exch Tw T* Show } bdef
+/TJ
+ { { dup type /stringtype eq
+ { Show
+ }
+ { -1000 div
+ currentfont /ScaleMatrix .knownget { 0 get mul } if
+ 0 rmoveto
+ }
+ ifelse
+ }
+ forall
+ } bdef
+
+/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/pdf_sec.ps b/pstoraster/pdf_sec.ps
new file mode 100644
index 000000000..b1a46a635
--- /dev/null
+++ b/pstoraster/pdf_sec.ps
@@ -0,0 +1,65 @@
+% Copyright (C) 1996, 1997 Aladdin Enterprises. All rights reserved.
+%
+% This file is part of GNU Ghostscript.
+%
+% GNU Ghostscript is distributed in the hope that it will be useful, but
+% WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+% to anyone for the consequences of using it or for whether it serves any
+% particular purpose or works at all, unless he says so in writing. Refer
+% to the GNU General Public License for full details.
+%
+% Everyone is granted permission to copy, modify and redistribute GNU
+% Ghostscript, but only under the conditions described in the GNU General
+% Public License. A copy of this license is supposed to have been given
+% to you along with GNU Ghostscript so you can know your rights and
+% responsibilities. It should be in a file named COPYING. Among other
+% things, the copyright notice and this notice must be preserved on all
+% copies.
+%
+% Aladdin Enterprises supports the work of the GNU Project, but is not
+% affiliated with the Free Software Foundation or the GNU Project. GNU
+% Ghostscript, as distributed by Aladdin Enterprises, does not require any
+% GNU software to build or run it.
+
+% $Id: pdf_sec.ps 1012 2000-04-12 14:20:26Z mike $
+% 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
+% NOTE: these URLs are referenced in the error message below.
+
+/.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
+ { (\n\n) print
+ ( **** The PDF input file uses encryption and cannot be processed.\n) print
+ ( **** Please get and install the patch available from\n) print
+ ( **** http://www.ozemail.com.au/~geoffk/pdfencrypt/pdf_sec.ps\n) print
+ (\n\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/pipe_.h b/pstoraster/pipe_.h
new file mode 100644
index 000000000..9354d9f81
--- /dev/null
+++ b/pstoraster/pipe_.h
@@ -0,0 +1,42 @@
+/* Copyright (C) 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Declaration of popen and pclose */
+
+#ifndef pipe__INCLUDED
+# define pipe__INCLUDED
+
+#include "stdio_.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. Unfortunately, this sometimes causes
+ * more trouble than it cures.
+ */
+extern FILE *popen( /* P2(const char *, const char *) */ );
+extern int pclose(P1(FILE *));
+
+#endif /* pipe__INCLUDED */
diff --git a/pstoraster/pstoraster.c b/pstoraster/pstoraster.c
new file mode 100644
index 000000000..dbbf84497
--- /dev/null
+++ b/pstoraster/pstoraster.c
@@ -0,0 +1,230 @@
+/*
+ * "$Id$"
+ *
+ * PostScript RIP filter main entry for the Common UNIX Printing System
+ * (CUPS).
+ *
+ * Copyright 1993-2000 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...
+ */
+
+#define bool bool_ /* (maybe not needed) */
+#define uchar uchar_
+#define uint uint_
+#define ushort ushort_
+#define ulong ulong_
+
+#include <cups/cups.h>
+#include <cups/string.h>
+#include <stdlib.h>
+
+#undef bool
+#undef uchar
+#undef uint
+#undef ushort
+#undef ulong
+
+#include "ghost.h"
+#include "imain.h"
+#include "iminst.h"
+#include "istack.h"
+#include "interp.h"
+#include "ostack.h"
+#include "opextern.h"
+#include "gscdefs.h"
+#include "store.h"
+
+
+/*
+ * Globals...
+ */
+
+const char *cupsProfile = NULL;
+
+
+/*
+ * 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 exit_code; /* Exit code */
+ ref error_object; /* Error object */
+ char *content_type; /* CONTENT_TYPE environment variable */
+ int num_options; /* Number of job options */
+ cups_option_t *options; /* Job options */
+
+
+ /*
+ * 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);
+
+ /*
+ * Grab any job options...
+ */
+
+ num_options = cupsParseOptions(argv[5], 0, &options);
+
+ cupsProfile = cupsGetOption("profile", num_options, options);
+
+ /*
+ * 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);
+ initial_enter_name("BATCH", &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("DEVICE", "pswrite");
+ }
+
+ define_string("OutputFile", "-");
+ define_string("FONTPATH", CUPS_DATADIR "/fonts");
+
+ /*
+ * Start the interpreter...
+ */
+
+ gs_main_init2(minst);
+ 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..d01513568
--- /dev/null
+++ b/pstoraster/sa85x.h
@@ -0,0 +1,51 @@
+/* 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 supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Requires scommon.h; strimpl.h if any templates are referenced */
+
+#ifndef sa85x_INCLUDED
+# define sa85x_INCLUDED
+
+/* 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;
+
+#endif /* sa85x_INCLUDED */
diff --git a/pstoraster/sbcp.c b/pstoraster/sbcp.c
new file mode 100644
index 000000000..c7c735027
--- /dev/null
+++ b/pstoraster/sbcp.c
@@ -0,0 +1,259 @@
+/* Copyright (C) 1993, 1994, 1996, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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 * 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();
+
+/* Initialize the state */
+private int
+s_BCPD_init(stream_state * st)
+{
+ stream_BCPD_state *const ss = (stream_BCPD_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)
+{
+ stream_BCPD_state *const ss = (stream_BCPD_state *) st;
+ 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..157c5f997
--- /dev/null
+++ b/pstoraster/sbhc.c
@@ -0,0 +1,291 @@
+/* Copyright (C) 1994, 1995, 1996, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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();
+
+/* Initialize BoundedHuffmanEncode filter. */
+private int
+s_BHCE_reinit(stream_state * st)
+{
+ stream_BHCE_state *const ss = (stream_BHCE_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)
+{
+ stream_BHCE_state *const ss = (stream_BHCE_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)
+{
+ stream_BHCE_state *const ss = (stream_BHCE_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)
+{
+ stream_BHCE_state *const ss = (stream_BHCE_state *) st;
+ 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);
+}
+
+/* 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 hcd_initial_bits 7 /* arbitrary, >= 1 and <= 8 */
+
+/* Initialize BoundedHuffmanDecode filter. */
+private int
+s_BHCD_reinit(stream_state * st)
+{
+ stream_BHCD_state *const ss = (stream_BHCD_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)
+{
+ stream_BHCD_state *const ss = (stream_BHCD_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)
+{
+ stream_BHCD_state *const ss = (stream_BHCD_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)
+{
+ stream_BHCD_state *const ss = (stream_BHCD_state *) st;
+
+ 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;
+}
+
+/* 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..e0317cabc
--- /dev/null
+++ b/pstoraster/sbhc.h
@@ -0,0 +1,98 @@
+/* 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 supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Requires strimpl.h */
+
+#ifndef sbhc_INCLUDED
+# define sbhc_INCLUDED
+
+#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
+
+#endif /* sbhc_INCLUDED */
diff --git a/pstoraster/sbtx.h b/pstoraster/sbtx.h
new file mode 100644
index 000000000..e1a3324bf
--- /dev/null
+++ b/pstoraster/sbtx.h
@@ -0,0 +1,45 @@
+/* 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 supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Requires scommon.h; strimpl.h if any templates are referenced */
+
+#ifndef sbtx_INCLUDED
+# define sbtx_INCLUDED
+
+/* 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_BTE_template;
+extern const stream_template s_BTD_template;
+
+#endif /* sbtx_INCLUDED */
diff --git a/pstoraster/sbwbs.c b/pstoraster/sbwbs.c
new file mode 100644
index 000000000..1523ac7b1
--- /dev/null
+++ b/pstoraster/sbwbs.c
@@ -0,0 +1,535 @@
+/* Copyright (C) 1994, 1995, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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();
+
+/* Initialize */
+private int
+s_buffered_no_block_init(stream_state * st)
+{
+ stream_buffered_state *const ss = (stream_buffered_state *) st;
+
+ ss->buffer = 0;
+ ss->filling = true;
+ ss->bpos = 0;
+ return 0;
+}
+private int
+s_buffered_block_init(stream_state * st)
+{
+ stream_buffered_state *const ss = (stream_buffered_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)
+{
+ stream_buffered_state *const ss = (stream_buffered_state *) st;
+ 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)
+{
+ stream_buffered_state *const ss = (stream_buffered_state *) st;
+
+ gs_free_object(st->memory, ss->buffer, "buffer");
+}
+
+/* ------ Common code for Burrows/Wheeler block sorting filters ------ */
+
+private_st_BWBS_state();
+private void s_BWBS_release(P1(stream_state *));
+
+/* Initialize */
+private int
+bwbs_init(stream_state * st, uint osize)
+{
+ stream_BWBS_state *const ss = (stream_BWBS_state *) st;
+ 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)
+{
+ stream_BWBS_state *const ss = (stream_BWBS_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)
+{
+ stream_BWBS_state *const ss = (stream_BWBS_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)
+{
+ stream_BWBS_state *const ss = (stream_BWBS_state *) st;
+ 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)
+{
+ stream_BWBS_state *const ss = (stream_BWBS_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)
+{
+ stream_BWBS_state *const ss = (stream_BWBS_state *) st;
+ 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;
+}
+
+/* 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..0018673b8
--- /dev/null
+++ b/pstoraster/sbwbs.h
@@ -0,0 +1,78 @@
+/* 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 supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Requires scommon.h; strimpl.h if any templates are referenced */
+
+#ifndef sbwbs_INCLUDED
+# define sbwbs_INCLUDED
+
+/* 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;
+
+#endif /* sbwbs_INCLUDED */
diff --git a/pstoraster/scanchar.h b/pstoraster/scanchar.h
new file mode 100644
index 000000000..8c5d25d21
--- /dev/null
+++ b/pstoraster/scanchar.h
@@ -0,0 +1,75 @@
+/* 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 supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Requires scommon.h */
+
+#ifndef scanchar_INCLUDED
+# define scanchar_INCLUDED
+
+/*
+ * 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
+
+#endif /* scanchar_INCLUDED */
diff --git a/pstoraster/scantab.c b/pstoraster/scantab.c
new file mode 100644
index 000000000..f36243603
--- /dev/null
+++ b/pstoraster/scantab.c
@@ -0,0 +1,112 @@
+/* Copyright (C) 1994, 1995, 1997 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Scanner table for PostScript/PDF tokens */
+#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_name, ctype_name,
+ ctype_name, ctype_name, ctype_name,
+ ctype_space, /* TAB (\t) */
+ ctype_space, /* LF (\n) */
+ ctype_name,
+ 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_name, 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/scf.h b/pstoraster/scf.h
new file mode 100644
index 000000000..f8f08bcc5
--- /dev/null
+++ b/pstoraster/scf.h
@@ -0,0 +1,213 @@
+/* Copyright (C) 1992, 1995, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Common definitions for CCITTFax encoding and decoding filters */
+
+#ifndef scf_INCLUDED
+# define scf_INCLUDED
+
+#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 ------ */
+
+/*
+ * The maximum possible length of a scan line is determined by the
+ * requirement that 3 runs have to fit into the stream buffer.
+ * A run of length N requires approximately ceil(N / 2560) makeup codes,
+ * hence 1.5 * ceil(N / 2560) bytes. Taking the largest safe stream
+ * buffer size as 32K, we arrive at the following maximum width:
+ */
+#if arch_sizeof_int > 2
+# define cfe_max_width (2560 * 32000 * 2 / 3)
+#else
+# define cfe_max_width (max_int - 40) /* avoid overflows */
+#endif
+/* The +5 in cfe_max_code_bytes is a little conservative. */
+#define cfe_max_code_bytes(width) ((width) / 2560 * 3 / 2 + 5)
+
+typedef hce_code cfe_run;
+
+/* 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;
+typedef struct cf_runs_s {
+ cfe_run termination[64];
+ cfe_run make_up[41];
+} cf_runs;
+extern const cf_runs
+ cf_white_runs, cf_black_runs;
+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 cf_white_decode[];
+
+#define cfd_black_initial_bits 7
+extern const cfd_node cf_black_decode[];
+
+#define cfd_2d_initial_bits 7
+extern const cfd_node cf_2d_decode[];
+
+#define cfd_uncompressed_initial_bits 6 /* must be 6 */
+extern const cfd_node 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)\
+BEGIN\
+ rlen = cf_byte_run_length[count & 7][data ^ 0xff];\
+ if ( rlen >= 8 ) { /* run extends past byte boundary */\
+ 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;\
+END
+
+/* 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)\
+BEGIN\
+ rlen = cf_byte_run_length[count & 7][data];\
+ if ( rlen >= 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;\
+END
+
+#endif /* scf_INCLUDED */
diff --git a/pstoraster/scfd.c b/pstoraster/scfd.c
new file mode 100644
index 000000000..2d9a34296
--- /dev/null
+++ b/pstoraster/scfd.c
@@ -0,0 +1,809 @@
+/* Copyright (C) 1992, 1995, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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();
+
+/* Set default parameter values. */
+private void
+s_CFD_set_defaults(register stream_state * st)
+{
+ stream_CFD_state *const ss = (stream_CFD_state *) st;
+
+ s_CFD_set_defaults_inline(ss);
+}
+
+/* Initialize CCITTFaxDecode filter */
+private int
+s_CFD_init(stream_state * st)
+{
+ stream_CFD_state *const ss = (stream_CFD_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);
+ /* Because skip_white_pixels can look as many as 4 bytes ahead, */
+ /* we need to allow 4 extra bytes at the end of the row buffers. */
+ ss->lbuf = gs_alloc_bytes(st->memory, raster + 4, "CFD lbuf");
+ ss->lprev = 0;
+ if (ss->lbuf == 0)
+ return ERRC;
+/****** WRONG ******/
+ if (ss->K != 0) {
+ ss->lprev = gs_alloc_bytes(st->memory, raster + 4, "CFD lprev");
+ if (ss->lprev == 0)
+ return ERRC;
+/****** WRONG ******/
+ /* Clear the initial reference line for 2-D encoding. */
+ memset(ss->lbuf, white, raster);
+ /* Ensure that the scan of the reference line will stop. */
+ ss->lbuf[raster] = 0xa0;
+ }
+ 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 || ss->EndOfBlock ? -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)
+{
+ stream_CFD_state *const ss = (stream_CFD_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. */
+#ifdef DEBUG
+# define IF_DEBUG(expr) expr
+#else
+# define IF_DEBUG(expr) DO_NOTHING
+#endif
+#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 )\
+ { IF_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 )\
+ {\
+ 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;\
+ break;\
+ 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;\
+ }\
+ }\
+ 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)
+{
+ stream_CFD_state *const ss = (stream_CFD_state *) st;
+ 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;
+
+#ifdef DEBUG
+ const byte *rstart = pr->ptr;
+ const byte *wstart = pw->ptr;
+
+#endif
+
+ top:
+#ifdef DEBUG
+ {
+ hcd_declare_state;
+ hcd_load_state();
+ if_debug8('w', "\
+[w]CFD_process top: eol_count=%d, k_left=%d, rows_left=%d\n\
+ bits=0x%lx, bits_left=%d, read %u, wrote %u%s\n",
+ eol_count, k_left, rows_left,
+ (ulong) bits, bits_left,
+ (uint) (p - rstart), (uint) (pw->ptr - wstart),
+ (ss->skipping_damage ? ", skipping damage" : ""));
+ }
+#endif
+ 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 EndOfLine is true, we want to include the byte padding
+ * in the string of initial zeros in the EOL. If EndOfLine
+ * is false, we aren't sure what we should do....
+ */
+ if (ss->EncodedByteAlign & !ss->EndOfLine)
+ 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;
+ /*
+ * According to Adobe, the decoder should always check for
+ * the EOD sequence, regardless of EndOfBlock: the Red Book's
+ * documentation of EndOfBlock is wrong.
+ */
+ if (eol_count == (ss->K < 0 ? 2 : 6)) {
+ status = EOFC;
+ goto out;
+ }
+ }
+ 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;
+}
+
+/*
+ * 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 = ss->run_color;
+ int status;
+ int bcnt;
+
+ cfd_load_state();
+ if_debug1('w', "[w1]entry run_color = %d\n", ss->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 -2:
+ ss->run_color = 0;
+ goto hww;
+ case -1:
+ ss->run_color = 0;
+ goto hbw;
+ case 1:
+ ss->run_color = 0;
+ goto hwb;
+ case 2:
+ ss->run_color = 0;
+ goto hbb;
+ /*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)
+ dlprintf2("[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)
+ goto hww;
+ else
+ goto hbb;
+ 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();
+ ss->invert = invert;
+ return status;
+ out0:status = 0;
+ goto out;
+ /*
+ * We handle horizontal decoding here, so that we can
+ * branch back into it if we run out of input data.
+ */
+ /* White, then black. */
+ hww:get_run(cf_white_decode, cfd_white_initial_bits, rlen,
+ " white", outww);
+ if ((count -= rlen) < end_count) {
+ status = ERRC;
+ goto out;
+ }
+ skip_data(rlen, hww);
+ /* Handle the second half of a white-black horizontal code. */
+ 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;
+ outww:ss->run_color = -2;
+ goto out0;
+ outwb:ss->run_color = 1;
+ goto out0;
+ /* Black, then white. */
+ hbb:get_run(cf_black_decode, cfd_black_initial_bits, rlen,
+ " black", outbb);
+ if ((count -= rlen) < end_count) {
+ status = ERRC;
+ goto out;
+ }
+ invert_data(rlen, black_byte, goto hbb, ihbb);
+ /* Handle the second half of a black-white horizontal code. */
+ 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;
+ outbb:ss->run_color = 2;
+ goto out0;
+ outbw:ss->run_color = -1;
+ goto out0;
+}
+
+#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..1d6dc99f7
--- /dev/null
+++ b/pstoraster/scfdtab.c
@@ -0,0 +1,942 @@
+/* Copyright (C) 1992, 1993, 1998 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. */
+
+/*$Id$ */
+/* Tables for CCITTFaxDecode filter. */
+
+#include "std.h"
+#include "scommon.h" /* for scf.h */
+#include "scf.h"
+
+/* White decoding table. */
+const cfd_node 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 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 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 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..ec416bc66
--- /dev/null
+++ b/pstoraster/scfe.c
@@ -0,0 +1,536 @@
+/* Copyright (C) 1992, 1995, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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
+
+typedef struct stats_runs_s {
+ ulong termination[64];
+ ulong make_up[41];
+} stats_runs_t;
+private stats_runs_t stats_white_runs, stats_black_runs;
+
+#define COUNT_RUN(tab, i) (tab)[i]++;
+
+private void
+print_run_stats(const stats_runs_t * stats)
+{
+ int i;
+ ulong total;
+
+ for (i = 0, total = 0; i < 41; i++)
+ dprintf1(" %lu", stats->make_up[i]),
+ total += stats->make_up[i];
+ dprintf1(" total=%lu\n\t", total);
+ for (i = 0, total = 0; i < 64; i++)
+ dprintf1(" %lu", stats->termination[i]),
+ total += stats->termination[i];
+ dprintf1(" total=%lu\n", total);
+}
+
+#else /* !DEBUG */
+
+#define COUNT_RUN(cnt, i) DO_NOTHING
+
+#endif /* DEBUG */
+
+/* Put a run onto the output stream. */
+/* Free variables: q, bits, bits_left. */
+
+#define CF_PUT_RUN(ss, lenv, rt, stats)\
+BEGIN\
+ cfe_run rr;\
+\
+ if ( lenv >= 64 ) {\
+ hce_store_state();\
+ q = cf_put_long_run(ss, q, lenv, &rt);\
+ hce_load_state();\
+ lenv &= 63;\
+ }\
+ rr = rt.termination[lenv];\
+ COUNT_RUN(stats.termination, lenv);\
+ hc_put_value(ss, q, rr.code, rr.code_length);\
+END
+
+private byte *
+cf_put_long_run(stream_CFE_state * ss, byte * q, int lenv, const cf_runs * prt)
+{
+ hce_declare_state;
+ cfe_run rr;
+
+#ifdef DEBUG
+ stats_runs_t *pstats =
+ (prt == &cf_white_runs ? &stats_white_runs : &stats_black_runs);
+
+#endif
+
+ hce_load_state();
+ while (lenv >= 2560 + 64) {
+ rr = prt->make_up[40];
+ COUNT_RUN(pstats->make_up, 40);
+ hc_put_value(ss, q, rr.code, rr.code_length);
+ lenv -= 2560;
+ }
+ rr = prt->make_up[lenv >> 6];
+ COUNT_RUN(pstats->make_up, lenv >> 6);
+ hc_put_value(ss, q, rr.code, rr.code_length);
+ hce_store_state();
+ return q;
+}
+
+#define CF_PUT_WHITE_RUN(ss, lenv)\
+ CF_PUT_RUN(ss, lenv, cf_white_runs, stats_white_runs)
+
+#define CF_PUT_BLACK_RUN(ss, lenv)\
+ CF_PUT_RUN(ss, lenv, cf_black_runs, stats_black_runs)
+
+/* ------ CCITTFaxEncode ------ */
+
+private_st_CFE_state();
+
+private void s_CFE_release(P1(stream_state *));
+
+/* Set default parameter values. */
+private void
+s_CFE_set_defaults(register stream_state * st)
+{
+ stream_CFE_state *const ss = (stream_CFE_state *) st;
+
+ s_CFE_set_defaults_inline(ss);
+}
+
+/* Initialize CCITTFaxEncode filter */
+private int
+s_CFE_init(register stream_state * st)
+{
+ stream_CFE_state *const ss = (stream_CFE_state *) st;
+ int columns = ss->Columns;
+
+ /*
+ * The worst case for encoding is alternating white and black pixels.
+ * For 1-D encoding, the worst case is 9 bits per 2 pixels; for 2-D
+ * (horizontal), 12 bits per 2 pixels. To fill out a scan line,
+ * we may add up to 6 12-bit EOL codes.
+ */
+ int code_bytes =
+ ((columns * (ss->K == 0 ? 9 : 12)) >> 4) + 20; /* add slop */
+ int raster = ss->raster =
+ round_up((columns + 7) >> 3, ss->DecodedByteAlign);
+
+ s_hce_init_inline(ss);
+ ss->lbuf = ss->lprev = ss->lcode = 0; /* in case we have to release */
+ if (columns > cfe_max_width)
+ return ERRC;
+/****** WRONG ******/
+ /* Because skip_white_pixels can look as many as 4 bytes ahead, */
+ /* we need to allow 4 extra bytes at the end of the row buffers. */
+ ss->lbuf = gs_alloc_bytes(st->memory, raster + 4, "CFE lbuf");
+ ss->lcode = gs_alloc_bytes(st->memory, code_bytes, "CFE lcode");
+ if (ss->lbuf == 0 || ss->lcode == 0) {
+ s_CFE_release(st);
+ return ERRC;
+/****** WRONG ******/
+ }
+ if (ss->K != 0) {
+ ss->lprev = gs_alloc_bytes(st->memory, raster + 4, "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->read_count = raster;
+ ss->write_count = 0;
+ ss->k_left = (ss->K > 0 ? 1 : ss->K);
+ ss->max_code_bytes = code_bytes;
+ return 0;
+}
+
+/* Release the filter. */
+private void
+s_CFE_release(stream_state * st)
+{
+ stream_CFE_state *const ss = (stream_CFE_state *) st;
+
+ gs_free_object(st->memory, ss->lprev, "CFE lprev(close)");
+ gs_free_object(st->memory, ss->lcode, "CFE lcode(close)");
+ gs_free_object(st->memory, ss->lbuf, "CFE lbuf(close)");
+}
+
+/* Flush the buffer */
+private void cf_encode_1d(P3(stream_CFE_state *, const byte *,
+ stream_cursor_write *));
+private void cf_encode_2d(P4(stream_CFE_state *, const byte *,
+ stream_cursor_write *, const byte *));
+private int
+s_CFE_process(stream_state * st, stream_cursor_read * pr,
+ stream_cursor_write * pw, bool last)
+{
+ stream_CFE_state *const ss = (stream_CFE_state *) st;
+ const byte *rlimit = pr->limit;
+ byte *wlimit = pw->limit;
+ int raster = ss->raster;
+ byte end_mask = 1 << (-ss->Columns & 7);
+ int status = 0;
+
+ for (;;) {
+ stream_cursor_write w;
+
+ if_debug2('w', "[w]CFE: read_count = %d, write_count=%d,\n",
+ ss->read_count, ss->write_count);
+ if_debug6('w', " pr = 0x%lx(%d)0x%lx, pw = 0x%lx(%d)0x%lx\n",
+ (ulong) pr->ptr, (int)(rlimit - pr->ptr), (ulong) rlimit,
+ (ulong) pw->ptr, (int)(wlimit - pw->ptr), (ulong) wlimit);
+ if (ss->write_count) {
+ /* Copy more of an encoded line to the caller. */
+ int wcount = wlimit - pw->ptr;
+ int ccount = min(wcount, ss->write_count);
+
+ memcpy(pw->ptr + 1, ss->lcode + ss->code_bytes - ss->write_count,
+ ccount);
+ pw->ptr += ccount;
+ if ((ss->write_count -= ccount) > 0) {
+ status = 1;
+ break;
+ }
+ }
+ if (ss->read_count) {
+ /* Copy more of an unencoded line from the caller. */
+ int rcount = rlimit - pr->ptr;
+ int ccount = min(rcount, ss->read_count);
+
+ if (rcount == 0 && last)
+ break;
+ memcpy(ss->lbuf + raster - ss->read_count,
+ pr->ptr + 1, ccount);
+ pr->ptr += ccount;
+ if ((ss->read_count -= ccount) != 0)
+ break;
+ }
+ /*
+ * We have a full scan line in lbuf. Ensure that it ends with
+ * two polarity changes.
+ */
+ {
+ byte *end = ss->lbuf + raster - 1;
+ 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);
+ }
+ /*
+ * Write the output directly to the caller's buffer if it's large
+ * enough, otherwise to our own buffer.
+ */
+ if (wlimit - pw->ptr >= ss->max_code_bytes) {
+ w = *pw;
+ } else {
+ w.ptr = ss->lcode - 1;
+ w.limit = w.ptr + ss->max_code_bytes;
+ }
+#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
+ /*
+ * Write an EOL (actually a "beginning of line") if requested.
+ */
+ 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;
+
+ hce_declare_state;
+
+ hce_load_state();
+ 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, w.ptr, rp);
+ hce_store_state();
+ } else if (ss->EncodedByteAlign)
+ ss->bits_left &= ~7;
+ /* Encode the line. */
+ if (ss->K == 0)
+ cf_encode_1d(ss, ss->lbuf, &w); /* pure 1-D */
+ else if (ss->K < 0)
+ cf_encode_2d(ss, ss->lbuf, &w, ss->lprev); /* pure 2-D */
+ else if (--(ss->k_left)) /* mixed, use 2-D */
+ cf_encode_2d(ss, ss->lbuf, &w, ss->lprev);
+ else { /* mixed, use 1-D */
+ cf_encode_1d(ss, ss->lbuf, &w);
+ ss->k_left = ss->K;
+ }
+ /*
+ * If we didn't write directly to the client's buffer, schedule
+ * the output data to be written.
+ */
+ if (w.limit == wlimit)
+ pw->ptr = w.ptr;
+ else
+ ss->write_count = ss->code_bytes = w.ptr - (ss->lcode - 1);
+ if (ss->K != 0) {
+ /* In 2-D modes, swap the current and previous scan lines. */
+ byte *temp = ss->lbuf;
+
+ ss->lbuf = ss->lprev;
+ ss->lprev = temp;
+ }
+ /* Note that the input buffer needs refilling. */
+ ss->read_count = raster;
+ }
+ /*
+ * When we exit from the loop, we know that write_count = 0, and
+ * there is no line waiting to be processed in the input buffer.
+ */
+ 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 - ss->bits_left + i * rp->code_length;
+ byte *q = pw->ptr;
+
+ hce_declare_state;
+
+ if (wlimit - q < (bits_to_write + 7) >> 3) {
+ status = 1;
+ goto out;
+ }
+ hce_load_state();
+ if (ss->EncodedByteAlign)
+ bits_left &= ~7;
+ while (--i >= 0)
+ hc_put_code(ss, q, rp);
+ /* Force out the last byte or bytes. */
+ pw->ptr = hc_put_last_bits((stream_hc_state *) ss, q);
+ }
+ out:
+ if_debug9('w', "[w]CFE exit %d: read_count = %d, write_count = %d,\n pr = 0x%lx(%d)0x%lx; pw = 0x%lx(%d)0x%lx\n",
+ status, ss->read_count, ss->write_count,
+ (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) {
+ dlputs("[w]white runs:");
+ print_run_stats(&stats_white_runs);
+ dlputs("[w]black runs:");
+ print_run_stats(&stats_black_runs);
+ }
+#endif
+ return status;
+}
+
+/* Encode a 1-D scan line. */
+private void
+cf_encode_1d(stream_CFE_state * ss, const byte * lbuf, stream_cursor_write * pw)
+{
+ uint count = ss->raster << 3;
+ byte *q = pw->ptr;
+ int end_count = -ss->Columns & 7;
+ int rlen;
+
+ hce_declare_state;
+ const byte *p = lbuf;
+ byte invert = (ss->BlackIs1 ? 0 : 0xff);
+
+ /* Invariant: data = p[-1] ^ invert. */
+ uint data = *p++ ^ invert;
+
+ hce_load_state();
+ while (count != end_count) {
+ /* Parse a white run. */
+ 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);
+ }
+ hce_store_state();
+ pw->ptr = q;
+}
+
+/* Encode a 2-D scan line. */
+private void
+cf_encode_2d(stream_CFE_state * ss, const byte * lbuf, stream_cursor_write * pw,
+ const byte * lprev)
+{
+ byte invert_white = (ss->BlackIs1 ? 0 : 0xff);
+ byte invert = invert_white;
+ uint count = ss->raster << 3;
+ int end_count = -ss->Columns & 7;
+ const byte *p = lbuf;
+ byte *q = pw->ptr;
+ uint data = *p++ ^ invert;
+
+ 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 *count_bit = initial_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;
+
+ /* 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);
+ a0 = prev_count;
+ goto pass;
+ }
+ }
+ /* Check for vertical coding. */
+ if (diff <= 3 && diff >= -3) {
+ /* Use vertical coding. */
+ const cfe_run *cp = &cf2_run_vertical[diff + 3];
+
+ if_debug5('W', "[W]vertical %d: count = %d, a1 = %d, b1 = %d, new count = %d\n",
+ diff, a0, a1, b1, count);
+ 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
+ }
+ }
+ hce_store_state();
+ pw->ptr = q;
+}
+
+/* Stream template */
+const stream_template s_CFE_template =
+{
+ &st_CFE_state, s_CFE_init, s_CFE_process, 1, 1,
+ s_CFE_release, s_CFE_set_defaults
+};
diff --git a/pstoraster/scfetab.c b/pstoraster/scfetab.c
new file mode 100644
index 000000000..287d02075
--- /dev/null
+++ b/pstoraster/scfetab.c
@@ -0,0 +1,170 @@
+/* Copyright (C) 1992, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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 hce_entry to make the file smaller. */
+#define RUN(c,len) hce_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 =
+RUN(run_eol_code_value, run_eol_code_length);
+
+/* Define the 1-D code that signals uncompressed data. */
+const cfe_run cf1_run_uncompressed =
+RUN(0xf, 12);
+
+/* Define the 2-D run codes. */
+const cfe_run cf2_run_pass =
+RUN(cf2_run_pass_value, cf2_run_pass_length);
+const cfe_run cf2_run_vertical[7] =
+{
+ RUN(0x3, 7),
+ RUN(0x3, 6),
+ RUN(0x3, 3),
+ RUN(0x1, 1),
+ RUN(0x2, 3),
+ RUN(0x2, 6),
+ RUN(0x2, 7)
+};
+const cfe_run cf2_run_horizontal =
+RUN(cf2_run_horizontal_value, cf2_run_horizontal_length);
+const cfe_run cf2_run_uncompressed =
+RUN(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 =
+RUN((run_eol_code_value << 1) + 1, run_eol_code_length + 1);
+const cfe_run cf2_run_eol_2d =
+RUN((run_eol_code_value << 1) + 0, run_eol_code_length + 1);
+
+/* White run codes. */
+const cf_runs cf_white_runs =
+{
+ { /* Termination codes */
+ RUN(0x35, 8), RUN(0x7, 6), RUN(0x7, 4), RUN(0x8, 4),
+ RUN(0xb, 4), RUN(0xc, 4), RUN(0xe, 4), RUN(0xf, 4),
+ RUN(0x13, 5), RUN(0x14, 5), RUN(0x7, 5), RUN(0x8, 5),
+ RUN(0x8, 6), RUN(0x3, 6), RUN(0x34, 6), RUN(0x35, 6),
+ RUN(0x2a, 6), RUN(0x2b, 6), RUN(0x27, 7), RUN(0xc, 7),
+ RUN(0x8, 7), RUN(0x17, 7), RUN(0x3, 7), RUN(0x4, 7),
+ RUN(0x28, 7), RUN(0x2b, 7), RUN(0x13, 7), RUN(0x24, 7),
+ RUN(0x18, 7), RUN(0x2, 8), RUN(0x3, 8), RUN(0x1a, 8),
+ RUN(0x1b, 8), RUN(0x12, 8), RUN(0x13, 8), RUN(0x14, 8),
+ RUN(0x15, 8), RUN(0x16, 8), RUN(0x17, 8), RUN(0x28, 8),
+ RUN(0x29, 8), RUN(0x2a, 8), RUN(0x2b, 8), RUN(0x2c, 8),
+ RUN(0x2d, 8), RUN(0x4, 8), RUN(0x5, 8), RUN(0xa, 8),
+ RUN(0xb, 8), RUN(0x52, 8), RUN(0x53, 8), RUN(0x54, 8),
+ RUN(0x55, 8), RUN(0x24, 8), RUN(0x25, 8), RUN(0x58, 8),
+ RUN(0x59, 8), RUN(0x5a, 8), RUN(0x5b, 8), RUN(0x4a, 8),
+ RUN(0x4b, 8), RUN(0x32, 8), RUN(0x33, 8), RUN(0x34, 8)
+ },
+ { /* Make-up codes */
+ RUN(0, 0) /* dummy */ , RUN(0x1b, 5), RUN(0x12, 5), RUN(0x17, 6),
+ RUN(0x37, 7), RUN(0x36, 8), RUN(0x37, 8), RUN(0x64, 8),
+ RUN(0x65, 8), RUN(0x68, 8), RUN(0x67, 8), RUN(0xcc, 9),
+ RUN(0xcd, 9), RUN(0xd2, 9), RUN(0xd3, 9), RUN(0xd4, 9),
+ RUN(0xd5, 9), RUN(0xd6, 9), RUN(0xd7, 9), RUN(0xd8, 9),
+ RUN(0xd9, 9), RUN(0xda, 9), RUN(0xdb, 9), RUN(0x98, 9),
+ RUN(0x99, 9), RUN(0x9a, 9), RUN(0x18, 6), RUN(0x9b, 9),
+ RUN(0x8, 11), RUN(0xc, 11), RUN(0xd, 11), RUN(0x12, 12),
+ RUN(0x13, 12), RUN(0x14, 12), RUN(0x15, 12), RUN(0x16, 12),
+ RUN(0x17, 12), RUN(0x1c, 12), RUN(0x1d, 12), RUN(0x1e, 12),
+ RUN(0x1f, 12)
+ }
+};
+
+/* Black run codes. */
+const cf_runs cf_black_runs =
+{
+ { /* Termination codes */
+ RUN(0x37, 10), RUN(0x2, 3), RUN(0x3, 2), RUN(0x2, 2),
+ RUN(0x3, 3), RUN(0x3, 4), RUN(0x2, 4), RUN(0x3, 5),
+ RUN(0x5, 6), RUN(0x4, 6), RUN(0x4, 7), RUN(0x5, 7),
+ RUN(0x7, 7), RUN(0x4, 8), RUN(0x7, 8), RUN(0x18, 9),
+ RUN(0x17, 10), RUN(0x18, 10), RUN(0x8, 10), RUN(0x67, 11),
+ RUN(0x68, 11), RUN(0x6c, 11), RUN(0x37, 11), RUN(0x28, 11),
+ RUN(0x17, 11), RUN(0x18, 11), RUN(0xca, 12), RUN(0xcb, 12),
+ RUN(0xcc, 12), RUN(0xcd, 12), RUN(0x68, 12), RUN(0x69, 12),
+ RUN(0x6a, 12), RUN(0x6b, 12), RUN(0xd2, 12), RUN(0xd3, 12),
+ RUN(0xd4, 12), RUN(0xd5, 12), RUN(0xd6, 12), RUN(0xd7, 12),
+ RUN(0x6c, 12), RUN(0x6d, 12), RUN(0xda, 12), RUN(0xdb, 12),
+ RUN(0x54, 12), RUN(0x55, 12), RUN(0x56, 12), RUN(0x57, 12),
+ RUN(0x64, 12), RUN(0x65, 12), RUN(0x52, 12), RUN(0x53, 12),
+ RUN(0x24, 12), RUN(0x37, 12), RUN(0x38, 12), RUN(0x27, 12),
+ RUN(0x28, 12), RUN(0x58, 12), RUN(0x59, 12), RUN(0x2b, 12),
+ RUN(0x2c, 12), RUN(0x5a, 12), RUN(0x66, 12), RUN(0x67, 12)
+ },
+ { /* Make-up codes. */
+ RUN(0, 0) /* dummy */ , RUN(0xf, 10), RUN(0xc8, 12), RUN(0xc9, 12),
+ RUN(0x5b, 12), RUN(0x33, 12), RUN(0x34, 12), RUN(0x35, 12),
+ RUN(0x6c, 13), RUN(0x6d, 13), RUN(0x4a, 13), RUN(0x4b, 13),
+ RUN(0x4c, 13), RUN(0x4d, 13), RUN(0x72, 13), RUN(0x73, 13),
+ RUN(0x74, 13), RUN(0x75, 13), RUN(0x76, 13), RUN(0x77, 13),
+ RUN(0x52, 13), RUN(0x53, 13), RUN(0x54, 13), RUN(0x55, 13),
+ RUN(0x5a, 13), RUN(0x5b, 13), RUN(0x64, 13), RUN(0x65, 13),
+ RUN(0x8, 11), RUN(0xc, 11), RUN(0xd, 11), RUN(0x12, 12),
+ RUN(0x13, 12), RUN(0x14, 12), RUN(0x15, 12), RUN(0x16, 12),
+ RUN(0x17, 12), RUN(0x1c, 12), RUN(0x1d, 12), RUN(0x1e, 12),
+ RUN(0x1f, 12)
+ }
+};
+
+/* Uncompressed codes. */
+const cfe_run cf_uncompressed[6] =
+{
+ RUN(1, 1),
+ RUN(1, 2),
+ RUN(1, 3),
+ RUN(1, 4),
+ RUN(1, 5),
+ RUN(1, 6)
+};
+
+/* Uncompressed exit codes. */
+const cfe_run cf_uncompressed_exit[10] =
+{
+ RUN(2, 8), RUN(3, 8),
+ RUN(2, 9), RUN(3, 9),
+ RUN(2, 10), RUN(3, 10),
+ RUN(2, 11), RUN(3, 11),
+ RUN(2, 12), RUN(3, 12)
+};
+
+/* Some C compilers insist on having executable code in every file.... */
+void
+cfe_dummy(void)
+{
+}
diff --git a/pstoraster/scfparam.c b/pstoraster/scfparam.c
new file mode 100644
index 000000000..c906ae22e
--- /dev/null
+++ b/pstoraster/scfparam.c
@@ -0,0 +1,99 @@
+/* Copyright (C) 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* CCITTFax filter parameter setting and reading */
+#include "std.h"
+#include "gserror.h"
+#include "gserrors.h"
+#include "gstypes.h"
+#include "gsmemory.h"
+#include "gsparam.h"
+#include "scommon.h"
+#include "scf.h" /* for cfe_max_width */
+#include "scfx.h"
+
+/* Define the CCITTFax parameters. */
+private const gs_param_item_t s_CF_param_items[] =
+{
+#define cfp(key, type, memb) { key, type, offset_of(stream_CF_state, memb) }
+ cfp("Uncompressed", gs_param_type_bool, Uncompressed),
+ cfp("K", gs_param_type_int, K),
+ cfp("EndOfLine", gs_param_type_bool, EndOfLine),
+ cfp("EncodedByteAlign", gs_param_type_bool, EncodedByteAlign),
+ cfp("Columns", gs_param_type_int, Columns),
+ cfp("Rows", gs_param_type_int, Rows),
+ cfp("EndOfBlock", gs_param_type_bool, EndOfBlock),
+ cfp("BlackIs1", gs_param_type_bool, BlackIs1),
+ cfp("DamagedRowsBeforeError", gs_param_type_int, DamagedRowsBeforeError),
+ cfp("FirstBitLowOrder", gs_param_type_bool, FirstBitLowOrder),
+ cfp("DecodedByteAlign", gs_param_type_int, DecodedByteAlign),
+#undef cfp
+ gs_param_item_end
+};
+
+/* Define a limit on the Rows parameter, close to max_int. */
+#define cf_max_height 32000
+
+/* Get non-default CCITTFax filter parameters. */
+stream_state_proc_get_params(s_CF_get_params, stream_CF_state); /* check */
+int
+s_CF_get_params(gs_param_list * plist, const stream_CF_state * ss, bool all)
+{
+ stream_CF_state cfs_defaults;
+ const stream_CF_state *defaults;
+
+ if (all)
+ defaults = 0;
+ else {
+ s_CF_set_defaults_inline(&cfs_defaults);
+ defaults = &cfs_defaults;
+ }
+ return gs_param_write_items(plist, ss, defaults, s_CF_param_items);
+}
+
+/* Put CCITTFax filter parameters. */
+stream_state_proc_put_params(s_CF_put_params, stream_CF_state); /* check */
+int
+s_CF_put_params(gs_param_list * plist, stream_CF_state * ss)
+{
+ stream_CF_state state;
+ int code;
+
+ state = *ss;
+ code = gs_param_read_items(plist, (void *)&state, s_CF_param_items);
+ if (code >= 0 &&
+ (state.K < -cf_max_height || state.K > cf_max_height ||
+ state.Columns < 0 || state.Columns > cfe_max_width ||
+ state.Rows < 0 || state.Rows > cf_max_height ||
+ state.DamagedRowsBeforeError < 0 ||
+ state.DamagedRowsBeforeError > cf_max_height ||
+ state.DecodedByteAlign < 1 || state.DecodedByteAlign > 16 ||
+ (state.DecodedByteAlign & (state.DecodedByteAlign - 1)) != 0)
+ )
+ code = gs_note_error(gs_error_rangecheck);
+ if (code >= 0)
+ *ss = state;
+ return code;
+}
diff --git a/pstoraster/scfx.h b/pstoraster/scfx.h
new file mode 100644
index 000000000..96a6b43d4
--- /dev/null
+++ b/pstoraster/scfx.h
@@ -0,0 +1,132 @@
+/* Copyright (C) 1993, 1995, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* CCITTFax filter state definition */
+/* Requires strimpl.h */
+
+#ifndef scfx_DEFINED
+# define scfx_DEFINED
+
+#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) */
+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, /* always set, for s_CF_get_params */\
+ (ss)->FirstBitLowOrder = false,\
+ /* Added by us */\
+ (ss)->DecodedByteAlign = 1)
+
+/* CCITTFaxEncode */
+typedef struct stream_CFE_state_s {
+ stream_CF_state_common;
+ /* The init procedure sets the following. */
+ int max_code_bytes; /* max # of bytes for an encoded line */
+ byte *lcode; /* buffer for encoded output line */
+ /* The following change dynamically. */
+ int read_count; /* # of bytes to copy into lbuf */
+ int write_count; /* # of bytes to copy out of lcode */
+ int code_bytes; /* # of occupied bytes in lcode */
+} stream_CFE_state;
+
+#define private_st_CFE_state() /* in scfe.c */\
+ gs_private_st_ptrs3(st_CFE_state, stream_CFE_state, "CCITTFaxEncode state",\
+ cfe_enum_ptrs, cfe_reloc_ptrs, lbuf, lprev, lcode)
+#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 */
+ 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 */
+ /* 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)
+extern const stream_template s_CFD_template;
+
+#endif /* scfx_INCLUDED */
diff --git a/pstoraster/scommon.h b/pstoraster/scommon.h
new file mode 100644
index 000000000..9f02bc04f
--- /dev/null
+++ b/pstoraster/scommon.h
@@ -0,0 +1,173 @@
+/* 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 supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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);
+
+/*
+ * Some types of streams have the ability to read their parameters from
+ * a parameter list, and to write all (or only the non-default)
+ * parameters to a parameter list. Since these are not virtual
+ * procedures for the stream (they operate on stream_state structures
+ * even if no actual stream has been created), we name them differently.
+ */
+#define stream_state_proc_get_params(proc, state_type)\
+ int proc(P3(gs_param_list *plist, const state_type *ss, bool all))
+#define stream_state_proc_put_params(proc, state_type)\
+ int proc(P2(gs_param_list *plist, state_type *ss))
+
+/*
+ * 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/sdcparam.c b/pstoraster/sdcparam.c
new file mode 100644
index 000000000..247c4666e
--- /dev/null
+++ b/pstoraster/sdcparam.c
@@ -0,0 +1,630 @@
+/*
+ Copyright 1993-2000 by Easy Software Products.
+ Copyright 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+#include <config.h>
+#ifdef HAVE_LIBJPEG
+/*$Id$ */
+/* DCT filter parameter setting and reading */
+#include "memory_.h"
+#include "jpeglib.h"
+#include "gserror.h"
+#include "gserrors.h"
+#include "gstypes.h"
+#include "gsmemory.h"
+#include "gsparam.h"
+#include "strimpl.h" /* sdct.h requires this */
+#include "sdct.h"
+#include "sdcparam.h"
+#include "sjpeg.h"
+
+/* Define the DCT parameters. */
+#define dctp(key, type, stype, memb) { key, type, offset_of(stype, memb) }
+private const gs_param_item_t s_DCT_param_items[] =
+{
+dctp("ColorTransform", gs_param_type_int, stream_DCT_state, ColorTransform),
+ dctp("QFactor", gs_param_type_float, stream_DCT_state, QFactor),
+ gs_param_item_end
+};
+private const gs_param_item_t jsd_param_items[] =
+{
+ dctp("Picky", gs_param_type_int, jpeg_stream_data, Picky),
+ dctp("Relax", gs_param_type_int, jpeg_stream_data, Relax),
+ gs_param_item_end
+};
+
+#undef dctp
+
+/*
+ * 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 byte 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]
+ /* invert natural_order for getting parameters */
+static const byte inverse_natural_order[DCTSIZE2] =
+{
+ 0, 1, 5, 6, 14, 15, 27, 28,
+ 2, 4, 7, 13, 16, 26, 29, 42,
+ 3, 8, 12, 17, 25, 30, 41, 43,
+ 9, 11, 18, 24, 31, 40, 44, 53,
+ 10, 19, 23, 32, 39, 45, 52, 54,
+ 20, 22, 33, 38, 46, 51, 55, 60,
+ 21, 34, 37, 47, 50, 56, 59, 61,
+ 35, 36, 48, 49, 57, 58, 62, 63
+};
+
+#define jpeg_inverse_order(x) inverse_natural_order[x]
+#else
+#define jpeg_order(x) (x)
+#define jpeg_inverse_order(x) (x)
+#endif
+
+/* ================ Get parameters ================ */
+
+private int
+quant_param_string(gs_param_string * pstr, int count, const UINT16 * pvals,
+ floatp QFactor, gs_memory_t * mem)
+{
+ byte *data;
+ int code = 0;
+ int i;
+
+ data = gs_alloc_string(mem, count, "quant_param_string");
+ if (data == 0)
+ return_error(gs_error_VMerror);
+ for (i = 0; i < count; ++i) {
+ floatp val = pvals[jpeg_inverse_order(i)] / QFactor;
+
+ data[i] =
+ (val < 1 ? (code = 1) : val > 255 ? (code = 255) : (byte) val);
+ }
+ pstr->data = data;
+ pstr->size = count;
+ pstr->persistent = true;
+ return code & 1;
+}
+
+private int
+quant_param_array(gs_param_float_array * pfa, int count, const UINT16 * pvals,
+ floatp QFactor, gs_memory_t * mem)
+{
+ float *data;
+ int i;
+
+ data = (float *)gs_alloc_byte_array(mem, count, sizeof(float),
+ "quant_param_array");
+
+ if (data == 0)
+ return_error(gs_error_VMerror);
+ for (i = 0; i < count; ++i)
+ data[i] = pvals[jpeg_inverse_order(i)] / QFactor;
+ pfa->data = data;
+ pfa->size = count;
+ pfa->persistent = true;
+ return 0;
+}
+
+int
+s_DCT_get_quantization_tables(gs_param_list * plist,
+ const stream_DCT_state * pdct, const stream_DCT_state * defaults,
+ bool is_encode)
+{
+ gs_memory_t *mem = pdct->memory;
+ jpeg_component_info d_comp_info[4];
+ int num_in_tables;
+ const jpeg_component_info *comp_info;
+ const jpeg_component_info *default_comp_info;
+ JQUANT_TBL **table_ptrs;
+ JQUANT_TBL **default_table_ptrs;
+ gs_param_array quant_tables;
+ floatp QFactor = pdct->QFactor;
+ int i;
+ int code;
+
+ if (is_encode) {
+ num_in_tables = pdct->data.compress->cinfo.num_components;
+ comp_info = pdct->data.compress->cinfo.comp_info;
+ table_ptrs = pdct->data.compress->cinfo.quant_tbl_ptrs;
+ if (defaults) {
+ default_comp_info = defaults->data.compress->cinfo.comp_info;
+ default_table_ptrs = defaults->data.compress->cinfo.quant_tbl_ptrs;
+ }
+ } else {
+ num_in_tables = quant_tables.size;
+ for (i = 0; i < num_in_tables; ++i)
+ d_comp_info[i].quant_tbl_no = i;
+ comp_info = d_comp_info;
+ table_ptrs = pdct->data.decompress->dinfo.quant_tbl_ptrs;
+ if (defaults) {
+ default_comp_info = d_comp_info;
+ default_table_ptrs =
+ defaults->data.decompress->dinfo.quant_tbl_ptrs;
+ }
+ }
+
+ /* Check whether all tables match defaults. */
+ if (defaults) {
+ bool match = true;
+
+ for (i = 0; i < num_in_tables; ++i) {
+ JQUANT_TBL *tbl = table_ptrs[comp_info[i].quant_tbl_no];
+ JQUANT_TBL *default_tbl =
+ (default_comp_info == 0 || default_table_ptrs == 0 ? 0 :
+ default_table_ptrs[default_comp_info[i].quant_tbl_no]);
+
+ if (tbl == default_tbl)
+ continue;
+ if (tbl == 0 || default_tbl == 0 ||
+ memcmp(tbl->quantval, default_tbl->quantval,
+ DCTSIZE2 * sizeof(UINT16))
+ ) {
+ match = false;
+ break;
+ }
+ }
+ if (match)
+ return 0;
+ }
+ quant_tables.size = num_in_tables;
+ code = param_begin_write_collection(plist, "QuantTables",
+ &quant_tables,
+ gs_param_collection_array);
+ if (code < 0)
+ return code;
+ for (i = 0; i < num_in_tables; ++i) {
+ char key[3];
+ gs_param_string str;
+ gs_param_float_array fa;
+
+ sprintf(key, "%d", i);
+ if (QFactor == 1.0) {
+ code = quant_param_string(&str, DCTSIZE2,
+ table_ptrs[comp_info[i].quant_tbl_no]->quantval,
+ QFactor, mem);
+ switch (code) {
+ case 0:
+ code = param_write_string(quant_tables.list, key, &str);
+ if (code < 0)
+ return code; /* should dealloc */
+ continue;
+ default:
+ return code; /* should dealloc */
+ case 1:
+ break;
+ }
+ /* break const to free the string */
+ gs_free_string(mem, (byte *) str.data, str.size,
+ "quant_param_string");
+ }
+ code = quant_param_array(&fa, DCTSIZE2,
+ table_ptrs[comp_info[i].quant_tbl_no]->quantval,
+ QFactor, mem);
+ if (code < 0)
+ return code; /* should dealloc */
+ code = param_write_float_array(quant_tables.list, key, &fa);
+ if (code < 0)
+ return code; /* should dealloc */
+ }
+ return param_end_write_dict(plist, "QuantTables", &quant_tables);
+}
+
+private int
+pack_huff_table(gs_param_string * pstr, const JHUFF_TBL * table,
+ gs_memory_t * mem)
+{
+ int total;
+ int i;
+ byte *data;
+
+ for (i = 1, total = 0; i <= 16; ++i)
+ total += table->bits[i];
+ data = gs_alloc_string(mem, 16 + total, "pack_huff_table");
+ if (data == 0)
+ return_error(gs_error_VMerror);
+ memcpy(data, table->bits + 1, 16);
+ memcpy(data + 16, table->huffval, total);
+ pstr->data = data;
+ pstr->size = 16 + total;
+ pstr->persistent = true;
+ return 0;
+}
+
+int
+s_DCT_get_huffman_tables(gs_param_list * plist,
+ const stream_DCT_state * pdct, const stream_DCT_state * defaults,
+ bool is_encode)
+{
+ gs_memory_t *mem = pdct->memory;
+ gs_param_string *huff_data;
+ gs_param_string_array hta;
+ int num_in_tables;
+ jpeg_component_info *comp_info;
+ JHUFF_TBL **dc_table_ptrs;
+ JHUFF_TBL **ac_table_ptrs;
+ int i;
+ int code = 0;
+
+ if (is_encode) {
+ dc_table_ptrs = pdct->data.compress->cinfo.dc_huff_tbl_ptrs;
+ ac_table_ptrs = pdct->data.compress->cinfo.ac_huff_tbl_ptrs;
+ num_in_tables = pdct->data.compress->cinfo.input_components * 2;
+ comp_info = pdct->data.compress->cinfo.comp_info;
+ } else {
+ dc_table_ptrs = pdct->data.decompress->dinfo.dc_huff_tbl_ptrs;
+ ac_table_ptrs = pdct->data.decompress->dinfo.ac_huff_tbl_ptrs;
+ for (i = 2; i > 0; --i)
+ if (dc_table_ptrs[i - 1] || ac_table_ptrs[i - 1])
+ break;
+ num_in_tables = i * 2;
+ comp_info = NULL; /* do not set for decompress case */
+ }
+/****** byte_array IS WRONG ******/
+ huff_data = (gs_param_string *)
+ gs_alloc_byte_array(mem, num_in_tables, sizeof(gs_param_string),
+ "get huffman tables");
+ if (huff_data == 0)
+ return_error(gs_error_VMerror);
+ for (i = 0; i < num_in_tables; i += 2) {
+ if ((code = pack_huff_table(huff_data + i, ac_table_ptrs[i >> 1], mem)) < 0 ||
+ (code = pack_huff_table(huff_data + i + 1, dc_table_ptrs[i >> 1], mem))
+ )
+ break;
+ }
+ if (code < 0)
+ return code;
+ hta.data = huff_data;
+ hta.size = num_in_tables;
+ hta.persistent = true;
+ return param_write_string_array(plist, "HuffTables", &hta);
+}
+
+int
+s_DCT_get_params(gs_param_list * plist, const stream_DCT_state * ss,
+ const stream_DCT_state * defaults)
+{
+ int code =
+ gs_param_write_items(plist, ss, defaults, s_DCT_param_items);
+
+ if (code >= 0)
+ code = gs_param_write_items(plist, ss->data.common,
+ (defaults ? defaults->data.common :
+ NULL),
+ jsd_param_items);
+ return code;
+}
+
+/* ================ Put parameters ================ */
+
+stream_state_proc_put_params(s_DCT_put_params, stream_DCT_state); /* check */
+
+/* ---------------- Utilities ---------------- */
+
+/*
+ * Get N byte-size values from an array or a string.
+ * Used for HuffTables, HSamples, VSamples.
+ */
+int
+s_DCT_byte_params(gs_param_list * plist, gs_param_name key, int start,
+ int count, UINT8 * pvals)
+{
+ int i;
+ gs_param_string bytes;
+ gs_param_float_array floats;
+ int code = param_read_string(plist, key, &bytes);
+
+ switch (code) {
+ case 0:
+ if (bytes.size < start + count) {
+ code = gs_note_error(gs_error_rangecheck);
+ break;
+ }
+ for (i = 0; i < count; ++i)
+ pvals[i] = (UINT8) bytes.data[start + i];
+ return 0;
+ default: /* might be a float array */
+ code = param_read_float_array(plist, key, &floats);
+ if (!code) {
+ if (floats.size < start + count) {
+ code = gs_note_error(gs_error_rangecheck);
+ break;
+ }
+ for (i = 0; i < count; ++i) {
+ float v = floats.data[start + i];
+
+ if (v < 0 || v > 255) {
+ code = gs_note_error(gs_error_rangecheck);
+ break;
+ }
+ pvals[i] = (UINT8) (v + 0.5);
+ }
+ }
+ }
+ if (code < 0)
+ param_signal_error(plist, key, code);
+ return code;
+}
+
+/* Get N quantization values from an array or a string. */
+private int
+quant_params(gs_param_list * plist, gs_param_name key, int count,
+ UINT16 * pvals, floatp QFactor)
+{
+ int i;
+ gs_param_string bytes;
+ gs_param_float_array floats;
+ int code = param_read_string(plist, key, &bytes);
+
+ switch (code) {
+ case 0:
+ if (bytes.size != count) {
+ code = gs_note_error(gs_error_rangecheck);
+ break;
+ }
+ for (i = 0; i < count; ++i) {
+ double v = bytes.data[i] * QFactor;
+
+ pvals[jpeg_order(i)] =
+ (UINT16) (v < 1 ? 1 : v > 255 ? 255 : v + 0.5);
+ }
+ return 0;
+ default: /* might be a float array */
+ code = param_read_float_array(plist, key, &floats);
+ if (!code) {
+ if (floats.size != count) {
+ code = gs_note_error(gs_error_rangecheck);
+ break;
+ }
+ for (i = 0; i < count; ++i) {
+ double v = floats.data[i] * QFactor;
+
+ pvals[jpeg_order(i)] =
+ (UINT16) (v < 1 ? 1 : v > 255 ? 255 : v + 0.5);
+ }
+ }
+ }
+ if (code < 0)
+ param_signal_error(plist, key, code);
+ return code;
+#undef jpeg_order
+}
+
+/* ---------------- Main procedures ---------------- */
+
+/* Put common scalars. */
+int
+s_DCT_put_params(gs_param_list * plist, stream_DCT_state * pdct)
+{
+ int code =
+ gs_param_read_items(plist, pdct, s_DCT_param_items);
+
+ if (code < 0)
+ return code;
+ code = gs_param_read_items(plist, pdct->data.common, jsd_param_items);
+ if (code < 0)
+ return code;
+ if (pdct->data.common->Picky < 0 || pdct->data.common->Picky > 1 ||
+ pdct->data.common->Relax < 0 || pdct->data.common->Relax > 1 ||
+ pdct->ColorTransform < -1 || pdct->ColorTransform > 2 ||
+ pdct->QFactor < 0.0 || pdct->QFactor > 1000000.0
+ )
+ return_error(gs_error_rangecheck);
+ return 0;
+}
+
+/* Put quantization tables. */
+int
+s_DCT_put_quantization_tables(gs_param_list * plist, stream_DCT_state * pdct,
+ bool is_encode)
+{
+ int code;
+ int i, j;
+ gs_param_array quant_tables; /* array of strings/arrays */
+ int num_in_tables;
+ int num_out_tables;
+ jpeg_component_info *comp_info;
+ JQUANT_TBL **table_ptrs;
+ JQUANT_TBL *this_table;
+
+ switch ((code = param_begin_read_dict(plist, "QuantTables",
+ &quant_tables, true))
+ ) {
+ case 1:
+ return 0;
+ default:
+ return param_signal_error(plist, "QuantTables", code);
+ case 0:
+ ;
+ }
+ if (is_encode) {
+ num_in_tables = pdct->data.compress->cinfo.num_components;
+ if (quant_tables.size < num_in_tables)
+ return_error(gs_error_rangecheck);
+ comp_info = pdct->data.compress->cinfo.comp_info;
+ table_ptrs = pdct->data.compress->cinfo.quant_tbl_ptrs;
+ } else {
+ num_in_tables = quant_tables.size;
+ 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; i < num_in_tables; ++i) {
+ char istr[5]; /* i converted to string key */
+ UINT16 values[DCTSIZE2];
+
+ sprintf(istr, "%d", i);
+ code = quant_params(quant_tables.list, istr, DCTSIZE2, values,
+ pdct->QFactor);
+ if (code < 0)
+ return code;
+ /* Check for duplicate tables. */
+ for (j = 0; j < num_out_tables; j++) {
+ if (!memcmp(table_ptrs[j]->quantval, values, sizeof(values)))
+ break;
+ }
+ if (comp_info != NULL)
+ comp_info[i].quant_tbl_no = j;
+ if (j < num_out_tables) /* found a duplicate */
+ continue;
+ if (++num_out_tables > NUM_QUANT_TBLS)
+ return_error(gs_error_rangecheck);
+ this_table = table_ptrs[j];
+ if (this_table == NULL) {
+ this_table = gs_jpeg_alloc_quant_table(pdct);
+ if (this_table == NULL)
+ return_error(gs_error_VMerror);
+ table_ptrs[j] = this_table;
+ }
+ memcpy(this_table->quantval, values, sizeof(values));
+ }
+ return 0;
+}
+
+/* Put Huffman tables. */
+private int
+find_huff_values(JHUFF_TBL ** table_ptrs, int num_tables,
+ const UINT8 counts[16], const UINT8 * values, int codes_size)
+{
+ int j;
+
+ for (j = 0; j < num_tables; ++j)
+ if (!memcmp(table_ptrs[j]->bits, counts, sizeof(counts)) &&
+ !memcmp(table_ptrs[j]->huffval, values,
+ codes_size * sizeof(values[0])))
+ break;
+ return j;
+}
+int
+s_DCT_put_huffman_tables(gs_param_list * plist, stream_DCT_state * pdct,
+ bool is_encode)
+{
+ int code;
+ int i, j;
+ gs_param_array huff_tables;
+ 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 */
+
+ switch ((code = param_begin_read_dict(plist, "HuffTables",
+ &huff_tables, true))
+ ) {
+ case 1:
+ return 0;
+ default:
+ return param_signal_error(plist, "HuffTables", code);
+ case 0:
+ ;
+ }
+ if (is_encode) {
+ num_in_tables = pdct->data.compress->cinfo.input_components * 2;
+ if (huff_tables.size < num_in_tables)
+ return_error(gs_error_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 = huff_tables.size;
+ 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; i < num_in_tables; ++i) {
+ char istr[5]; /* i converted to string key */
+ UINT8 counts[16], values[256];
+
+ /* Collect the Huffman parameters. */
+ sprintf(istr, "%d", i);
+ code = s_DCT_byte_params(huff_tables.list, istr, 0, 16, counts);
+ if (code < 0)
+ return code;
+ for (codes_size = 0, j = 0; j < 16; j++)
+ codes_size += counts[j];
+ if (codes_size > 256 /*|| r_size(pa) != codes_size+16 */ )
+ return_error(gs_error_rangecheck);
+ code = s_DCT_byte_params(huff_tables.list, istr, 16, codes_size,
+ values);
+ if (code < 0)
+ return code;
+ if (i & 1) {
+ j = find_huff_values(ac_table_ptrs, nac, counts, values,
+ codes_size);
+ if (comp_info != NULL)
+ comp_info[i >> 1].ac_tbl_no = j;
+ if (j < nac)
+ continue;
+ if (++nac > NUM_HUFF_TBLS)
+ return_error(gs_error_rangecheck);
+ this_table_ptr = ac_table_ptrs + j;
+ } else {
+ j = find_huff_values(dc_table_ptrs, ndc, counts, values,
+ codes_size);
+ if (comp_info != NULL)
+ comp_info[i >> 1].dc_tbl_no = j;
+ if (j < ndc)
+ continue;
+ if (++ndc > NUM_HUFF_TBLS)
+ return_error(gs_error_rangecheck);
+ 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(gs_error_VMerror);
+ *this_table_ptr = this_table;
+ }
+ memcpy(this_table->bits, counts, sizeof(counts));
+ memcpy(this_table->huffval, values, codes_size * sizeof(values[0]));
+ }
+ if (nac > max_tables || ndc > max_tables)
+ return_error(gs_error_rangecheck);
+ return 0;
+}
+#endif /* HAVE_LIBJPEG */
diff --git a/pstoraster/sdcparam.h b/pstoraster/sdcparam.h
new file mode 100644
index 000000000..ae3750310
--- /dev/null
+++ b/pstoraster/sdcparam.h
@@ -0,0 +1,57 @@
+/* Copyright (C) 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* DCT filter parameter setting and reading interface */
+
+#ifndef sdcparam_INCLUDED
+# define sdcparam_INCLUDED
+
+/*
+ * All of these procedures are defined in sdcparam.c and are only for
+ * internal use (by sddparam.c and sdeparam.c), so they are not
+ * documented here.
+ */
+
+int s_DCT_get_params(P3(gs_param_list * plist, const stream_DCT_state * ss,
+ const stream_DCT_state * defaults));
+int s_DCT_get_quantization_tables(P4(gs_param_list * plist,
+ const stream_DCT_state * pdct,
+ const stream_DCT_state * defaults,
+ bool is_encode));
+int s_DCT_get_huffman_tables(P4(gs_param_list * plist,
+ const stream_DCT_state * pdct,
+ const stream_DCT_state * defaults,
+ bool is_encode));
+
+int s_DCT_byte_params(P5(gs_param_list * plist, gs_param_name key, int start,
+ int count, UINT8 * pvals));
+int s_DCT_put_params(P2(gs_param_list * plist, stream_DCT_state * pdct));
+int s_DCT_put_quantization_tables(P3(gs_param_list * plist,
+ stream_DCT_state * pdct,
+ bool is_encode));
+int s_DCT_put_huffman_tables(P3(gs_param_list * plist, stream_DCT_state * pdct,
+ bool is_encode));
+
+#endif /* sdcparam_INCLUDED */
diff --git a/pstoraster/sdct.h b/pstoraster/sdct.h
new file mode 100644
index 000000000..cffc646b8
--- /dev/null
+++ b/pstoraster/sdct.h
@@ -0,0 +1,121 @@
+/* Copyright (C) 1994, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Requires stream.h, strimpl.h, jpeg/jpeglib.h */
+
+#ifndef sdct_INCLUDED
+# define sdct_INCLUDED
+
+#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;\
+ gs_memory_t *memory; /* heap for library allocations */\
+ /* 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;
+
+/* Define initialization for the non-library part of the stream state. */
+#define jpeg_stream_data_common_init(pdata)\
+ ((pdata)->Picky = 0, (pdata)->Relax = 0)
+
+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 */
+ gs_memory_t *jpeg_memory; /* heap for library allocations */
+ /* 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_const_strings1(st_DCT_state, stream_DCT_state,\
+ "DCTEncode/Decode state", dct_enum_ptrs, dct_reloc_ptrs, Markers)
+
+/*
+ * NOTE: the client *must* invoke the set_defaults procedure in the
+ * template before calling the init procedure.
+ */
+extern const stream_template s_DCTD_template;
+extern const stream_template s_DCTE_template;
+
+/* Define an internal procedure for setting stream defaults. */
+/* Clients do not call this. */
+void s_DCT_set_defaults(P1(stream_state * st));
+
+#endif /* sdct_INCLUDED */
diff --git a/pstoraster/sdctc.c b/pstoraster/sdctc.c
new file mode 100644
index 000000000..ec9dfc8f1
--- /dev/null
+++ b/pstoraster/sdctc.c
@@ -0,0 +1,55 @@
+/*
+ Copyright 1993-2000 by Easy Software Products.
+ Copyright 1994, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+#include <config.h>
+#ifdef HAVE_LIBJPEG
+
+/*$Id$ */
+/* Code common to DCT encoding and decoding streams */
+#include "stdio_.h"
+#include "gsmemory.h"
+#include "gsmalloc.h"
+#include "jpeglib.h"
+#include "strimpl.h"
+#include "sdct.h"
+
+public_st_DCT_state();
+
+/* Set the defaults for the DCT filters. */
+void
+s_DCT_set_defaults(stream_state * st)
+{
+ stream_DCT_state *const ss = (stream_DCT_state *) st;
+
+ ss->jpeg_memory = &gs_memory_default;
+ ss->data.common = 0;
+ /****************
+ ss->data.common->Picky = 0;
+ ss->data.common->Relax = 0;
+ ****************/
+ ss->ColorTransform = -1;
+ ss->QFactor = 1.0;
+}
+#endif /* HAVE_LIBJPEG */
diff --git a/pstoraster/sdctd.c b/pstoraster/sdctd.c
new file mode 100644
index 000000000..70fa2324f
--- /dev/null
+++ b/pstoraster/sdctd.c
@@ -0,0 +1,303 @@
+/*
+ Copyright 1993-2000 by Easy Software Products.
+ Copyright 1994, 1995, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+#include <config.h>
+#ifdef HAVE_LIBJPEG
+
+/*$Id$ */
+/* DCT decoding filter stream */
+#include "memory_.h"
+#include "stdio_.h"
+#include "jpeglib.h"
+#include "jerror.h"
+#include "gdebug.h"
+#include "gsmemory.h" /* for gsmalloc.h */
+#include "gsmalloc.h" /* for gs_memory_default */
+#include "strimpl.h"
+#include "sdct.h"
+#include "sjpeg.h"
+
+/* ------ 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)
+{
+}
+
+/* Set the defaults for the DCTDecode filter. */
+private void
+s_DCTD_set_defaults(stream_state * st)
+{
+ s_DCT_set_defaults(st);
+}
+
+/* Initialize DCTDecode filter */
+private int
+s_DCTD_init(stream_state * st)
+{
+ stream_DCT_state *const ss = (stream_DCT_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.common->memory = ss->jpeg_memory;
+ 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)
+{
+ stream_DCT_state *const ss = (stream_DCT_state *) st;
+ 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 =
+ gs_alloc_bytes_immovable(jddp->memory,
+ ss->scan_line_size,
+ "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)
+{
+ stream_DCT_state *const ss = (stream_DCT_state *) st;
+
+ gs_jpeg_destroy(ss);
+#endif /* HAVE_LIBJPEG */
+ if (ss->data.decompress->scanline_buffer != NULL)
+ gs_free_object(ss->data.common->memory,
+ ss->data.decompress->scanline_buffer,
+ "s_DCTD_release(scanline_buffer)");
+ gs_free_object(ss->data.common->memory, ss->data.decompress,
+ "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,
+ s_DCTD_set_defaults
+};
diff --git a/pstoraster/sdcte.c b/pstoraster/sdcte.c
new file mode 100644
index 000000000..388e4574e
--- /dev/null
+++ b/pstoraster/sdcte.c
@@ -0,0 +1,203 @@
+/*
+ Copyright 1993-2000 by Easy Software Products.
+ Copyright 1994, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+#include <config.h>
+#ifdef HAVE_LIBJPEG
+
+/*$Id$ */
+/* DCT encoding filter stream */
+#include "memory_.h"
+#include "stdio_.h"
+#include "jpeglib.h"
+#include "jerror.h"
+#include "gdebug.h"
+#include "gsmemory.h" /* for gsmalloc.h */
+#include "gsmalloc.h" /* for gs_memory_default */
+#include "strimpl.h"
+#include "sdct.h"
+#include "sjpeg.h"
+
+/* ------ 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)
+{
+}
+
+/* Set the defaults for the DCTEncode filter. */
+private void
+s_DCTE_set_defaults(stream_state * st)
+{
+ stream_DCT_state *const ss = (stream_DCT_state *) st;
+
+ s_DCT_set_defaults(st);
+ ss->QFactor = 1.0;
+ ss->ColorTransform = 0;
+ ss->Markers.data = 0;
+ ss->Markers.size = 0;
+ ss->NoMarker = true;
+}
+
+/* Initialize DCTEncode filter */
+private int
+s_DCTE_init(stream_state * st)
+{
+ stream_DCT_state *const ss = (stream_DCT_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.common->memory = ss->jpeg_memory;
+ 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)
+{
+ stream_DCT_state *const ss = (stream_DCT_state *) st;
+ 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 */
+#endif /* HAVE_LIBJPEG */
+private void
+s_DCTE_release(stream_state * st)
+{
+ stream_DCT_state *const ss = (stream_DCT_state *) st;
+
+ gs_jpeg_destroy(ss);
+ gs_free_object(ss->data.common->memory, ss->data.compress,
+ "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,
+ s_DCTE_set_defaults
+};
diff --git a/pstoraster/sddparam.c b/pstoraster/sddparam.c
new file mode 100644
index 000000000..223bf7679
--- /dev/null
+++ b/pstoraster/sddparam.c
@@ -0,0 +1,83 @@
+/*
+ Copyright 1993-2000 by Easy Software Products.
+ Copyright 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+#include <config.h>
+#ifdef HAVE_LIBJPEG
+/*$Id$ */
+/* DCTDecode filter parameter setting and reading */
+#include "std.h"
+#include "jpeglib.h"
+#include "gserror.h"
+#include "gserrors.h"
+#include "gstypes.h"
+#include "gsmemory.h"
+#include "gsparam.h"
+#include "strimpl.h" /* sdct.h requires this */
+#include "sdct.h"
+#include "sdcparam.h"
+#include "sjpeg.h"
+
+/* ================ Get parameters ================ */
+
+stream_state_proc_get_params(s_DCTD_get_params, stream_DCT_state); /* check */
+
+int
+s_DCTD_get_params(gs_param_list * plist, const stream_DCT_state * ss, bool all)
+{
+ stream_DCT_state dcts_defaults;
+ const stream_DCT_state *defaults;
+
+ if (all)
+ defaults = 0;
+ else {
+ (*s_DCTE_template.set_defaults) ((stream_state *) & dcts_defaults);
+ defaults = &dcts_defaults;
+ }
+/****** NYI ******/
+ return s_DCT_get_params(plist, ss, defaults);
+}
+
+/* ================ Put parameters ================ */
+
+stream_state_proc_put_params(s_DCTD_put_params, stream_DCT_state); /* check */
+
+int
+s_DCTD_put_params(gs_param_list * plist, stream_DCT_state * pdct)
+{
+ int code;
+
+ if ((code = s_DCT_put_params(plist, pdct)) < 0 ||
+ /*
+ * DCTDecode accepts quantization and huffman tables
+ * in case these tables have been omitted from the datastream.
+ */
+ (code = s_DCT_put_huffman_tables(plist, pdct, false)) < 0 ||
+ (code = s_DCT_put_quantization_tables(plist, pdct, false)) < 0
+ )
+ DO_NOTHING;
+ return code;
+}
+#endif /* HAVE_LIBJPEG */
diff --git a/pstoraster/sdeparam.c b/pstoraster/sdeparam.c
new file mode 100644
index 000000000..81bbdc87c
--- /dev/null
+++ b/pstoraster/sdeparam.c
@@ -0,0 +1,328 @@
+/*
+ Copyright 1993-2000 by Easy Software Products.
+ Copyright 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+#include <config.h>
+#ifdef HAVE_LIBJPEG
+/*$Id$ */
+/* DCTEncode filter parameter setting and reading */
+#include "memory_.h"
+#include "jpeglib.h"
+#include "gserror.h"
+#include "gserrors.h"
+#include "gstypes.h"
+#include "gsmemory.h"
+#include "gsparam.h"
+#include "strimpl.h" /* sdct.h requires this */
+#include "sdct.h"
+#include "sdcparam.h"
+#include "sjpeg.h"
+
+/* Define a structure for the DCTEncode scalar parameters. */
+typedef struct dcte_scalars_s {
+ int Columns;
+ int Rows;
+ int Colors;
+ gs_param_string Markers;
+ bool NoMarker;
+ int Resync;
+ int Blend;
+} dcte_scalars_t;
+private const dcte_scalars_t dcte_scalars_default =
+{
+ 0, 0, -1,
+ {0, 0}, 0 /*false */ , 0, 0
+};
+private const gs_param_item_t s_DCTE_param_items[] =
+{
+#define dctp(key, type, memb) { key, type, offset_of(dcte_scalars_t, memb) }
+ dctp("Columns", gs_param_type_int, Columns),
+ dctp("Rows", gs_param_type_int, Rows),
+ dctp("Colors", gs_param_type_int, Colors),
+ dctp("Marker", gs_param_type_string, Markers),
+ dctp("NoMarker", gs_param_type_bool, NoMarker),
+ dctp("Resync", gs_param_type_int, Resync),
+ dctp("Blend", gs_param_type_int, Blend),
+#undef dctp
+ gs_param_item_end
+};
+
+/* ================ Get parameters ================ */
+
+stream_state_proc_get_params(s_DCTE_get_params, stream_DCT_state); /* check */
+
+/* Get a set of sampling values. */
+private int
+dcte_get_samples(gs_param_list * plist, gs_param_name key, int num_colors,
+ const jpeg_compress_data * jcdp, gs_memory_t * mem, bool is_vert, bool all)
+{
+ const jpeg_component_info *comp_info = jcdp->cinfo.comp_info;
+ int samples[4];
+ bool write = all;
+ int i;
+
+ for (i = 0; i < num_colors; ++i)
+ write |= (samples[i] = (is_vert ? comp_info[i].v_samp_factor :
+ comp_info[i].h_samp_factor)) != 1;
+ if (write) {
+ int *data = (int *)gs_alloc_byte_array(mem, num_colors, sizeof(int),
+ "dcte_get_samples");
+ gs_param_int_array sa;
+
+ if (data == 0)
+ return_error(gs_error_VMerror);
+ sa.data = data;
+ sa.size = num_colors;
+ sa.persistent = true;
+ memcpy(data, samples, num_colors * sizeof(samples[0]));
+ return param_write_int_array(plist, key, &sa);
+ }
+ return 0;
+}
+
+int
+s_DCTE_get_params(gs_param_list * plist, const stream_DCT_state * ss, bool all)
+{
+ gs_memory_t *mem = ss->memory;
+ stream_DCT_state dcts_defaults;
+ const stream_DCT_state *defaults = 0;
+ dcte_scalars_t params;
+ const jpeg_compress_data *jcdp = ss->data.compress;
+ int code;
+
+ if (!all) {
+ jpeg_compress_data *jcdp_default =
+ (jpeg_compress_data *)
+ gs_alloc_bytes_immovable(mem, sizeof(*jcdp), "s_DCTE_get_params");
+
+ if (jcdp_default == 0)
+ return_error(gs_error_VMerror);
+ defaults = &dcts_defaults;
+ (*s_DCTE_template.set_defaults) ((stream_state *) & dcts_defaults);
+ dcts_defaults.data.compress = jcdp_default;
+ jcdp_default->memory = dcts_defaults.jpeg_memory = mem;
+ if ((code = gs_jpeg_create_compress(&dcts_defaults)) < 0)
+ goto fail; /* correct to do jpeg_destroy here */
+/****** SET DEFAULTS HERE ******/
+ dcts_defaults.data.common->Picky = 0;
+ dcts_defaults.data.common->Relax = 0;
+ }
+ params.Columns = jcdp->cinfo.image_width;
+ params.Rows = jcdp->cinfo.image_height;
+ params.Colors = jcdp->cinfo.input_components;
+ params.Markers.data = ss->Markers.data;
+ params.Markers.size = ss->Markers.size;
+ params.Markers.persistent = false;
+ params.NoMarker = ss->NoMarker;
+ params.Resync = jcdp->cinfo.restart_interval;
+ /* What about Blend?? */
+ if ((code = s_DCT_get_params(plist, ss, defaults)) < 0 ||
+ (code = gs_param_write_items(plist, &params,
+ &dcte_scalars_default,
+ s_DCTE_param_items)) < 0 ||
+ (code = dcte_get_samples(plist, "HSamples", params.Colors,
+ jcdp, mem, false, all)) < 0 ||
+ (code = dcte_get_samples(plist, "VSamples", params.Colors,
+ jcdp, mem, true, all)) < 0 ||
+ (code = s_DCT_get_quantization_tables(plist, ss, defaults, true)) < 0 ||
+ (code = s_DCT_get_huffman_tables(plist, ss, defaults, true)) < 0
+ )
+ DO_NOTHING;
+/****** NYI ******/
+ fail:if (defaults) {
+ gs_jpeg_destroy(&dcts_defaults);
+ gs_free_object(mem, dcts_defaults.data.compress,
+ "s_DCTE_get_params");
+ }
+ return code;
+}
+
+/* ================ Put parameters ================ */
+
+stream_state_proc_put_params(s_DCTE_put_params, stream_DCT_state); /* check */
+
+/* Put a set of sampling values. */
+private int
+dcte_put_samples(gs_param_list * plist, gs_param_name key, int num_colors,
+ jpeg_compress_data * jcdp, bool is_vert)
+{
+ int code;
+ int i;
+ 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.
+ */
+ switch ((code = s_DCT_byte_params(plist, key, 0, num_colors,
+ samples))
+ ) {
+ default: /* error */
+ return code;
+ case 0:
+ break;
+ case 1:
+ 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(gs_error_rangecheck);
+ if (is_vert)
+ comp_info[i].v_samp_factor = samples[i];
+ else
+ comp_info[i].h_samp_factor = samples[i];
+ }
+ return 0;
+}
+
+/* Main procedure */
+int
+s_DCTE_put_params(gs_param_list * plist, stream_DCT_state * pdct)
+{
+ jpeg_compress_data *jcdp = pdct->data.compress;
+ dcte_scalars_t params;
+ int i;
+ int code;
+
+ params = dcte_scalars_default;
+ /*
+ * Required parameters for DCTEncode.
+ * (DCTDecode gets the equivalent info from the SOF marker.)
+ */
+ code = gs_param_read_items(plist, &params, s_DCTE_param_items);
+ if (code < 0)
+ return code;
+ if (params.Columns <= 0 || params.Columns > 0xffff ||
+ params.Rows <= 0 || params.Rows > 0xffff ||
+ params.Colors <= 0 || params.Colors == 2 || params.Colors > 4 ||
+ params.Resync < 0 || params.Resync > 0xffff ||
+ params.Blend < 0 || params.Blend > 1
+ )
+ return_error(gs_error_rangecheck);
+/****** HACK: SET DEFAULTS HERE ******/
+ jcdp->Picky = 0;
+ jcdp->Relax = 0;
+ if ((code = s_DCT_put_params(plist, pdct)) < 0 ||
+ (code = s_DCT_put_huffman_tables(plist, pdct, false)) < 0
+ )
+ return code;
+ switch ((code = s_DCT_put_quantization_tables(plist, pdct, false))) {
+ case 0:
+ break;
+ default:
+ return code;
+ case 1:
+ /* No QuantTables, but maybe a QFactor to apply to default. */
+ if (pdct->QFactor != 1.0) {
+ code = gs_jpeg_set_linear_quality(pdct,
+ (int)(min(pdct->QFactor, 100.0)
+ * 100.0 + 0.5),
+ TRUE);
+ if (code < 0)
+ return code;
+ }
+ }
+ /* Set up minimal image description & call set_defaults */
+ jcdp->cinfo.image_width = params.Columns;
+ jcdp->cinfo.image_height = params.Rows;
+ jcdp->cinfo.input_components = params.Colors;
+ switch (params.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 (params.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 */
+ pdct->Markers.data = params.Markers.data;
+ pdct->Markers.size = params.Markers.size;
+ pdct->NoMarker = params.NoMarker;
+ if ((code = dcte_put_samples(plist, "HSamples", params.Colors,
+ jcdp, false)) < 0 ||
+ (code = dcte_put_samples(plist, "VSamples", params.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 = params.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 < params.Colors; i++)
+ num_samples += comp_info[i].h_samp_factor *
+ comp_info[i].v_samp_factor;
+ if (num_samples > 10)
+ return_error(gs_error_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;
+}
+#endif /* HAVE_LIBJPEG */
diff --git a/pstoraster/seexec.c b/pstoraster/seexec.c
new file mode 100644
index 000000000..df6fb3e0b
--- /dev/null
+++ b/pstoraster/seexec.c
@@ -0,0 +1,192 @@
+/* Copyright (C) 1994, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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();
+
+/* Process a buffer */
+private int
+s_exE_process(stream_state * st, stream_cursor_read * pr,
+ stream_cursor_write * pw, bool last)
+{
+ stream_exE_state *const ss = (stream_exE_state *) st;
+ 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;
+}
+
+/* Stream template */
+const stream_template s_exE_template = {
+ &st_exE_state, NULL, s_exE_process, 1, 2
+};
+
+/* ------ eexecDecode ------ */
+
+private_st_exD_state();
+
+/* Set defaults. */
+private void
+s_exD_set_defaults(stream_state * st)
+{
+ stream_exD_state *const ss = (stream_exD_state *) st;
+
+ ss->lenIV = 4;
+}
+
+/* Initialize the state for reading and decrypting. */
+/* Decrypting streams are not positionable. */
+private int
+s_exD_init(stream_state * st)
+{
+ stream_exD_state *const ss = (stream_exD_state *) st;
+
+ ss->odd = -1;
+ ss->binary = -1; /* unknown */
+ ss->record_left = max_long;
+ ss->skip = ss->lenIV;
+ return 0;
+}
+
+/* Process a buffer. */
+private int
+s_exD_process(stream_state * st, stream_cursor_read * pr,
+ stream_cursor_write * pw, bool last)
+{
+ stream_exD_state *const ss = (stream_exD_state *) st;
+ const byte *p = pr->ptr;
+ 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.
+ */
+ const byte *const 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;
+}
+
+/* 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,
+ NULL, s_exD_set_defaults
+};
diff --git a/pstoraster/sfilter.h b/pstoraster/sfilter.h
new file mode 100644
index 000000000..3daa96783
--- /dev/null
+++ b/pstoraster/sfilter.h
@@ -0,0 +1,139 @@
+/* Copyright (C) 1993, 1995, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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. */
+
+#ifndef sfilter_INCLUDED
+# define sfilter_INCLUDED
+
+#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 *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 */
+ int lenIV; /* # of initial decoded bytes to skip */
+ /* 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_NullE_template;
+extern const stream_template s_NullD_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_const_strings1(st_SFD_state, stream_SFD_state,\
+ "SubFileDecode state", sfd_enum_ptrs, sfd_reloc_ptrs, eod)
+extern const stream_template s_SFD_template;
+
+#endif /* sfilter_INCLUDED */
diff --git a/pstoraster/sfilter1.c b/pstoraster/sfilter1.c
new file mode 100644
index 000000000..0a9181c4e
--- /dev/null
+++ b/pstoraster/sfilter1.c
@@ -0,0 +1,301 @@
+/* Copyright (C) 1993, 1995, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Filters included in Level 1 systems: NullEncode/Decode, PFBDecode, */
+/* SubFileDecode. */
+#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_NullE_template = {
+ &st_stream_state, NULL, s_Null_process, 1, 1
+};
+const stream_template s_NullD_template = {
+ &st_stream_state, NULL, s_Null_process, 1, 1
+};
+
+/* ------ PFBDecode ------ */
+
+private_st_PFBD_state();
+
+/* Initialize the state */
+private int
+s_PFBD_init(stream_state * st)
+{
+ stream_PFBD_state *const ss = (stream_PFBD_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)
+{
+ stream_PFBD_state *const ss = (stream_PFBD_state *) st;
+ 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;
+ const char *const 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;
+}
+
+/* Stream template */
+const stream_template s_PFBD_template = {
+ &st_PFBD_state, s_PFBD_init, s_PFBD_process, 6, 2
+};
+
+/* ------ SubFileDecode ------ */
+
+private_st_SFD_state();
+
+/* Initialize the stream */
+private int
+s_SFD_init(stream_state * st)
+{
+ stream_SFD_state *const ss = (stream_SFD_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)
+{
+ stream_SFD_state *const ss = (stream_SFD_state *) st;
+ 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;
+}
+
+/* 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..ad9728202
--- /dev/null
+++ b/pstoraster/sfilter2.c
@@ -0,0 +1,339 @@
+/* Copyright (C) 1991, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Simple Level 2 filters */
+#include "stdio_.h" /* includes std.h */
+#include "memory_.h"
+#include "strimpl.h"
+#include "sa85x.h"
+#include "sbtx.h"
+#include "scanchar.h"
+
+/* ------ ASCII85Encode ------ */
+
+/* Process a buffer */
+#define LINE_LIMIT 65
+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;
+ byte *qn = q + LINE_LIMIT;
+ 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 */
+
+put: 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) + '!';
+ /*
+ * Two consecutive '%' characters at the beginning
+ * of the line will confuse some document managers:
+ * insert (an) EOL(s) if necessary to prevent this.
+ */
+ if (q[1] == '%') {
+ if (q == pw->ptr) {
+ /*
+ * The very first character written is a %.
+ * Add an EOL before it in case the last
+ * character of the previous batch was a %.
+ */
+ *++q = '\n';
+ qn = q + LINE_LIMIT;
+ goto put;
+ }
+ if (q[2] == '%' && *q == '\n') {
+ /*
+ * We may have to insert more than one EOL if
+ * there are more than two %s in a row.
+ */
+ int extra =
+ (q[3] != '%' ? 1 : q[4] != '%' ? 2 :
+ q[5] != '%' ? 3 : 4);
+
+ if (wlimit - q < 6 + extra) {
+ status = 1;
+ break;
+ }
+ switch (extra) {
+ case 4:
+ q[9] = '%', q[8] = '\n';
+ goto e3;
+ case 3:
+ q[8] = q[5];
+ e3:q[7] = '%', q[6] = '\n';
+ goto e2;
+ case 2:
+ q[7] = q[5], q[6] = q[4];
+ e2:q[5] = '%', q[4] = '\n';
+ goto e1;
+ case 1:
+ q[6] = q[5], q[5] = q[4], q[4] = q[3];
+ e1:q[3] = '%', q[2] = '\n';
+ }
+ q += extra;
+ qn = q + (5 + LINE_LIMIT);
+ }
+ }
+ q += 5;
+ }
+ if (q >= qn) {
+ *++q = '\n';
+ qn = q + LINE_LIMIT;
+ }
+ }
+ /* 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;
+}
+#undef LINE_LIMIT
+
+/* Stream template */
+const stream_template s_A85E_template = {
+ &st_stream_state, NULL, s_A85E_process, 4, 6
+};
+
+/* ------ ASCII85Decode ------ */
+
+private_st_A85D_state();
+
+/* Initialize the state */
+private int
+s_A85D_init(stream_state * st)
+{
+ stream_A85D_state *const ss = (stream_A85D_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)
+{
+ stream_A85D_state *const ss = (stream_A85D_state *) st;
+ 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] = (byte) (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)
+ DO_NOTHING;
+ 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;
+}
+
+/* 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();
+
+/* 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)
+{
+ stream_BT_state *const ss = (stream_BT_state *) st;
+ 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;
+}
+
+/* Stream template */
+const stream_template s_BTE_template = {
+ &st_BT_state, NULL, s_BT_process, 1, 1
+};
+const stream_template s_BTD_template = {
+ &st_BT_state, NULL, s_BT_process, 1, 1
+};
diff --git a/pstoraster/sfxstdio.c b/pstoraster/sfxstdio.c
new file mode 100644
index 000000000..455d80c1d
--- /dev/null
+++ b/pstoraster/sfxstdio.c
@@ -0,0 +1,269 @@
+/* Copyright (C) 1993, 1995, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* File stream implementation using stdio */
+#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);
+}
+
+/*
+ * 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 = ((stream *) st)->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)
+{
+ uint count = pr->limit - pr->ptr;
+
+ /*
+ * The DEC C library on AXP architectures gives an error on
+ * fwrite if the count is zero!
+ */
+ if (count != 0) {
+ FILE *file = ((stream *) st)->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;
+ }
+}
+
+/* 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/shc.c b/pstoraster/shc.c
new file mode 100644
index 000000000..6c745b0f6
--- /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 supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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..81aa300b8
--- /dev/null
+++ b/pstoraster/shc.h
@@ -0,0 +1,254 @@
+/* Copyright (C) 1992, 1995, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Common definitions for filters using Huffman coding */
+
+#ifndef shc_INCLUDED
+# define shc_INCLUDED
+
+#include "gsbittab.h"
+#include "scommon.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') ?\
+ (dlprintf2("[W]0x%x,%d\n", code, clen), 0) : 0)
+# define hc_print_value_then(code, clen) hc_print_value(code, clen),
+#else
+# define hc_print_value(code, clen) 0
+# define hc_print_value_then(code, clen) /* */
+#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_then(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..cdcd957c1
--- /dev/null
+++ b/pstoraster/shcgen.c
@@ -0,0 +1,491 @@
+/* Copyright (C) 1994, 1996, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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;
+
+ dlprintf1("[w]---------------- %s ----------------\n", tag);
+ for (i = 0; i < n; ++i)
+ dlprintf7("[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;
+ dlprintf2("[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 = (uint) ~ 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..247a4cf3d
--- /dev/null
+++ b/pstoraster/shcgen.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 supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Requires shc.h */
+
+#ifndef shcgen_INCLUDED
+# define shcgen_INCLUDED
+
+/* 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));
+
+#endif /* shcgen_INCLUDED */
diff --git a/pstoraster/siscale.c b/pstoraster/siscale.c
new file mode 100644
index 000000000..d23b2e936
--- /dev/null
+++ b/pstoraster/siscale.c
@@ -0,0 +1,498 @@
+/* Copyright (C) 1995, 1996, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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);
+
+ /*
+ * In pathological cases, the limit may be much less
+ * than the support. We do need to deal with this.
+ */
+#define clamp_pixel(j)\
+ (j < 0 ? (-j >= limit ? limit - 1 : -j) :\
+ j >= limit ? (j >> 1 >= limit ? 0 : (limit - j) + limit - 1) :\
+ j)
+ int lmin =
+ (left < 0 ? 0 : left);
+ int lmax =
+ (left < 0 ? (-left >= limit ? limit - 1 : -left) : left);
+ int rmin =
+ (right >= limit ?
+ (right >> 1 >= limit ? 0 : (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;
+ }
+}
+
+/* Initialize the filter. */
+private int
+s_IScale_init(stream_state * st)
+{
+ stream_IScale_state *const ss = (stream_IScale_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)
+{
+ stream_IScale_state *const ss = (stream_IScale_state *) st;
+
+ /* 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);
+ /* Idiotic C coercion rules allow T* and void* to be */
+ /* inter-assigned freely, but not compared! */
+ if ((void *)row != 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. */
+
+ {
+ uint rleft = pr->limit - pr->ptr;
+ uint rcount = ss->src_size - ss->src_offset;
+
+ if (rleft == 0)
+ return 0; /* need more input */
+#ifdef DEBUG
+ assert(ss->src_y < ss->HeightIn);
+#endif
+ 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)
+{
+ stream_IScale_state *const ss = (stream_IScale_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;
+}
+
+/* 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..ca034db12
--- /dev/null
+++ b/pstoraster/siscale.h
@@ -0,0 +1,148 @@
+/* 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 supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Requires strimpl.h */
+
+#ifndef siscale_INCLUDED
+# define siscale_INCLUDED
+
+#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;
+
+#endif /* siscale_INCLUDED */
diff --git a/pstoraster/sjpeg.h b/pstoraster/sjpeg.h
new file mode 100644
index 000000000..92fb684dd
--- /dev/null
+++ b/pstoraster/sjpeg.h
@@ -0,0 +1,80 @@
+/* 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 supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Requires sdct.h, jpeg/jpeglib.h */
+
+#ifndef sjpeg_INCLUDED
+# define sjpeg_INCLUDED
+
+/*
+ * 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_set_quality(P3(stream_DCT_state * st,
+ int quality,
+ 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));
+
+#endif /* sjpeg_INCLUDED */
diff --git a/pstoraster/sjpegc.c b/pstoraster/sjpegc.c
new file mode 100644
index 000000000..f45616b69
--- /dev/null
+++ b/pstoraster/sjpegc.c
@@ -0,0 +1,305 @@
+/*
+ Copyright 1993-2000 by Easy Software Products.
+ Copyright 1994, 1997 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+#include <config.h>
+#ifdef HAVE_LIBJPEG
+/*$Id$ */
+/* 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;
+}
+
+
+/*
+ * 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.
+ */
+
+private gs_memory_t *
+gs_j_common_memory(j_common_ptr cinfo)
+{ /*
+ * We use the offset of cinfo in jpeg_compress data here, but we
+ * could equally well have used jpeg_decompress_data.
+ */
+ const jpeg_stream_data *sd =
+ ((const jpeg_stream_data *)((const byte *)cinfo -
+ offset_of(jpeg_compress_data, cinfo)));
+
+ return sd->memory;
+}
+
+void *
+jpeg_get_small(j_common_ptr cinfo, size_t sizeofobject)
+{
+ gs_memory_t *mem = gs_j_common_memory(cinfo);
+
+ return gs_alloc_bytes_immovable(mem, sizeofobject,
+ "JPEG small internal data allocation");
+}
+
+void
+jpeg_free_small(j_common_ptr cinfo, void *object, size_t sizeofobject)
+{
+ gs_memory_t *mem = gs_j_common_memory(cinfo);
+
+ gs_free_object(mem, object, "Freeing JPEG small internal data");
+}
+
+void FAR *
+jpeg_get_large(j_common_ptr cinfo, size_t sizeofobject)
+{
+ gs_memory_t *mem = gs_j_common_memory(cinfo);
+
+ return gs_alloc_bytes_immovable(mem, sizeofobject,
+ "JPEG large internal data allocation");
+}
+
+void
+jpeg_free_large(j_common_ptr cinfo, void FAR * object, size_t sizeofobject)
+{
+ gs_memory_t *mem = gs_j_common_memory(cinfo);
+
+ gs_free_object(mem, object, "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 /* HAVE_LIBJPEG */
diff --git a/pstoraster/sjpegd.c b/pstoraster/sjpegd.c
new file mode 100644
index 000000000..7c62ba118
--- /dev/null
+++ b/pstoraster/sjpegd.c
@@ -0,0 +1,100 @@
+/*
+ Copyright 1993-2000 by Easy Software Products.
+ Copyright (C) 1994, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+#include <config.h>
+#ifdef HAVE_LIBJPEG
+/*$Id$ */
+/* 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);
+ jpeg_stream_data_common_init(st->data.decompress);
+ 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 /* HAVE_LIBJPEG */
diff --git a/pstoraster/sjpege.c b/pstoraster/sjpege.c
new file mode 100644
index 000000000..dd5cb42a2
--- /dev/null
+++ b/pstoraster/sjpege.c
@@ -0,0 +1,129 @@
+/*
+ Copyright 1993-2000 by Easy Software Products.
+ Copyright 1994, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+#include <config.h>
+#ifdef HAVE_LIBJPEG
+/*$Id$ */
+/* 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);
+ jpeg_stream_data_common_init(st->data.compress);
+ 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_set_quality(stream_DCT_state * st,
+ int quality, boolean force_baseline)
+{
+ if (setjmp(st->data.common->exit_jmpbuf))
+ return_error(gs_jpeg_log_error(st));
+ jpeg_set_quality(&st->data.compress->cinfo,
+ quality, 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 /* HAVE_LIBJPEG */
diff --git a/pstoraster/sjpegerr.c b/pstoraster/sjpegerr.c
new file mode 100644
index 000000000..a4ad86f85
--- /dev/null
+++ b/pstoraster/sjpegerr.c
@@ -0,0 +1,102 @@
+/*
+ Copyright 1993-2000 by Easy Software Products
+ Copyright (C) 1994, 1995, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+#include <config.h>
+#ifdef HAVE_LIBJPEG
+/*$Id$ */
+/* IJG error message table for Ghostscript. */
+#include "stdio_.h"
+#include "jpeglib.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"
+
+/*
+ * 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 code[] = string;
+
+#include "jerror.h"
+
+/* Now build an array of pointers to same. */
+
+#define JMESSAGE(code,string) code ,
+
+static const char *const 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 /* HAVE_LIBJPEG */
diff --git a/pstoraster/slzwc.c b/pstoraster/slzwc.c
new file mode 100644
index 000000000..a9564a785
--- /dev/null
+++ b/pstoraster/slzwc.c
@@ -0,0 +1,50 @@
+/* Copyright (C) 1995, 1996, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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();
+
+/* Set defaults */
+void
+s_LZW_set_defaults(stream_state * st)
+{
+ stream_LZW_state *const ss = (stream_LZW_state *) st;
+
+ s_LZW_set_defaults_inline(ss);
+}
+
+/* Release a LZW filter. */
+void
+s_LZW_release(stream_state * st)
+{
+ stream_LZW_state *const ss = (stream_LZW_state *) st;
+
+ gs_free_object(st->memory, ss->table.decode, "LZW(close)");
+}
diff --git a/pstoraster/slzwce.c b/pstoraster/slzwce.c
new file mode 100644
index 000000000..85a774fad
--- /dev/null
+++ b/pstoraster/slzwce.c
@@ -0,0 +1,167 @@
+/* Copyright (C) 1994, 1995, 1996, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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;
+}
+
+/* Initialize LZW-compatible encoding filter. */
+int
+s_LZWE_reset(stream_state * st)
+{
+ stream_LZW_state *const ss = (stream_LZW_state *) st;
+
+ ss->code_size = ss->InitialCodeLength + 1;
+ ss->bits_left = 8;
+ /* Force the first code emitted to be a reset. */
+ ss->next_code = (1 << ss->code_size) - 2;
+ return 0;
+}
+private int
+s_LZWE_init(stream_state * st)
+{
+ stream_LZW_state *const ss = (stream_LZW_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)
+{
+ stream_LZW_state *const ss = (stream_LZW_state *) st;
+ 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) - 2; /* reset 1 early */
+ 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;
+}
+
+/* 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..8680e8607
--- /dev/null
+++ b/pstoraster/slzwd.c
@@ -0,0 +1,372 @@
+/* Copyright (C) 1993, 1996, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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 */
+
+/* Initialize LZWDecode filter */
+/* We separate out the reset function for some non-stream clients. */
+int
+s_LZWD_reset(stream_state * st)
+{
+ stream_LZW_state *const ss = (stream_LZW_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)
+{
+ stream_LZW_state *const ss = (stream_LZW_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)
+{
+ stream_LZW_state *const ss = (stream_LZW_state *) st;
+ 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;
+}
+
+/* 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..50e0b4258
--- /dev/null
+++ b/pstoraster/slzwx.h
@@ -0,0 +1,80 @@
+/* Copyright (C) 1993, 1995, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Requires strimpl.h */
+
+#ifndef slzwx_INCLUDED
+# define slzwx_INCLUDED
+
+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 */
+ /*
+ * Adobe calls FirstBitLowOrder LowBitFirst. Either one will work
+ * in PostScript code.
+ */
+ 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 *));
+
+#endif /* slzwx_INCLUDED */
diff --git a/pstoraster/smtf.c b/pstoraster/smtf.c
new file mode 100644
index 000000000..93513041d
--- /dev/null
+++ b/pstoraster/smtf.c
@@ -0,0 +1,184 @@
+/* Copyright (C) 1995, 1996, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* MoveToFront filters */
+#include "stdio_.h"
+#include "strimpl.h"
+#include "smtf.h"
+
+/* ------ MoveToFrontEncode/Decode ------ */
+
+private_st_MTF_state();
+
+/* Initialize */
+private int
+s_MTF_init(stream_state * st)
+{
+ stream_MTF_state *const ss = (stream_MTF_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)
+{
+ stream_MTF_state *const ss = (stream_MTF_state *) st;
+ 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)
+{
+ stream_MTF_state *const ss = (stream_MTF_state *) st;
+ 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) |
+ ((ulong) 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
+};
diff --git a/pstoraster/smtf.h b/pstoraster/smtf.h
new file mode 100644
index 000000000..eed6a2db6
--- /dev/null
+++ b/pstoraster/smtf.h
@@ -0,0 +1,49 @@
+/* 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 supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Requires scommon.h; strimpl.h if any templates are referenced */
+
+#ifndef smtf_INCLUDED
+# define smtf_INCLUDED
+
+/* 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;
+
+#endif /* smtf_INCLUDED */
diff --git a/pstoraster/spcxd.c b/pstoraster/spcxd.c
new file mode 100644
index 000000000..4cb2a2f59
--- /dev/null
+++ b/pstoraster/spcxd.c
@@ -0,0 +1,75 @@
+/* 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 supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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;
+}
+
+/* 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..cc156cf8e
--- /dev/null
+++ b/pstoraster/spcxx.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 supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Requires scommon.h; strimpl.h if any templates are referenced */
+
+#ifndef spcxx_INCLUDED
+# define spcxx_INCLUDED
+
+/* PCXDecode */
+/* (no state) */
+extern const stream_template s_PCXD_template;
+
+#endif /* spcxx_INCLUDED */
diff --git a/pstoraster/spdiff.c b/pstoraster/spdiff.c
new file mode 100644
index 000000000..53290596a
--- /dev/null
+++ b/pstoraster/spdiff.c
@@ -0,0 +1,334 @@
+/* Copyright (C) 1994, 1995, 1996, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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 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)
+{
+ stream_PDiff_state *const ss = (stream_PDiff_state *) st;
+
+ s_PDiff_set_defaults_inline(ss);
+}
+
+/* Common (re)initialization. */
+private int
+s_PDiff_reinit(stream_state * st)
+{
+ stream_PDiff_state *const ss = (stream_PDiff_state *) st;
+
+ ss->row_left = 0;
+ return 0;
+}
+
+/* Initialize PixelDifferenceEncode filter. */
+private int
+s_PDiffE_init(stream_state * st)
+{
+ stream_PDiff_state *const ss = (stream_PDiff_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)
+{
+ stream_PDiff_state *const ss = (stream_PDiff_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)
+{
+ stream_PDiff_state *const ss = (stream_PDiff_state *) st;
+ 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..1a4e1da96
--- /dev/null
+++ b/pstoraster/spdiffx.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 supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Requires strimpl.h */
+
+#ifndef spdiffx_INCLUDED
+# define spdiffx_INCLUDED
+
+/* 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;
+
+#endif /* spdiffx_INCLUDED */
diff --git a/pstoraster/spngp.c b/pstoraster/spngp.c
new file mode 100644
index 000000000..cc270a6bb
--- /dev/null
+++ b/pstoraster/spngp.c
@@ -0,0 +1,364 @@
+/* Copyright (C) 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* PNG pixel prediction filters */
+#include "memory_.h"
+#include "strimpl.h"
+#include "spngpx.h"
+
+/* ------ PNGPredictorEncode/Decode ------ */
+
+private_st_PNGP_state();
+
+/* 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)
+{
+ stream_PNGP_state *const ss = (stream_PNGP_state *) st;
+
+ s_PNGP_set_defaults_inline(ss);
+}
+
+/* Common (re)initialization. */
+private int
+s_PNGP_reinit(stream_state * st)
+{
+ stream_PNGP_state *const ss = (stream_PNGP_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)
+{
+ stream_PNGP_state *const ss = (stream_PNGP_state *) st;
+ 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)
+{
+ stream_PNGP_state *const ss = (stream_PNGP_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)
+{
+#undef any_abs /* just in case */
+#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)
+{
+ stream_PNGP_state *const ss = (stream_PNGP_state *) st;
+ 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
+s_pngp_count(const stream_state * st_const, const stream_cursor_read * pr,
+ const stream_cursor_write * pw)
+{
+ const stream_PNGP_state *const ss_const =
+ (const stream_PNGP_state *)st_const;
+ 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)
+{
+ stream_PNGP_state *const ss = (stream_PNGP_state *) st;
+ 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)
+{
+ stream_PNGP_state *const ss = (stream_PNGP_state *) st;
+ 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..303341ad0
--- /dev/null
+++ b/pstoraster/spngpx.h
@@ -0,0 +1,61 @@
+/* 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 supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Requires strimpl.h */
+
+#ifndef spngpx_INCLUDED
+# define spngpx_INCLUDED
+
+/* 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;
+
+#endif /* spngpx_INCLUDED */
diff --git a/pstoraster/srld.c b/pstoraster/srld.c
new file mode 100644
index 000000000..adacef808
--- /dev/null
+++ b/pstoraster/srld.c
@@ -0,0 +1,133 @@
+/* Copyright (C) 1995, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* RunLengthDecode filter */
+#include "stdio_.h" /* includes std.h */
+#include "memory_.h"
+#include "strimpl.h"
+#include "srlx.h"
+
+/* ------ RunLengthDecode ------ */
+
+private_st_RLD_state();
+
+/* Set defaults */
+private void
+s_RLD_set_defaults(stream_state * st)
+{
+ stream_RLD_state *const ss = (stream_RLD_state *) st;
+
+ s_RLD_set_defaults_inline(ss);
+}
+
+/* Initialize */
+private int
+s_RLD_init(stream_state * st)
+{
+ stream_RLD_state *const ss = (stream_RLD_state *) st;
+
+ return s_RLD_init_inline(ss);
+}
+
+/* Refill the buffer */
+private int
+s_RLD_process(stream_state * st, stream_cursor_read * pr,
+ stream_cursor_write * pw, bool last)
+{
+ stream_RLD_state *const ss = (stream_RLD_state *) st;
+ register const byte *p = pr->ptr;
+ register byte *q = pw->ptr;
+ const byte *rlimit = pr->limit;
+ byte *wlimit = pw->limit;
+ int left;
+ int status = 0;
+
+top:
+ if ((left = ss->copy_left) > 0) {
+ /*
+ * We suspended because the output buffer was full:;
+ * try again now.
+ */
+ uint avail = wlimit - q;
+ int copy_status = 1;
+
+ if (left > avail)
+ left = avail;
+ if (ss->copy_data >= 0)
+ memset(q + 1, ss->copy_data, left);
+ else {
+ avail = rlimit - p;
+ if (left >= avail) {
+ copy_status = 0;
+ left = avail;
+ }
+ memcpy(q + 1, p + 1, left);
+ p += left;
+ }
+ q += left;
+ if ((ss->copy_left -= left) > 0) {
+ status = copy_status;
+ goto x;
+ }
+ }
+ while (p < rlimit) {
+ int b = *++p;
+
+ if (b < 128) {
+ if (++b > rlimit - p || b > wlimit - q) {
+ ss->copy_left = b;
+ ss->copy_data = -1;
+ goto top;
+ }
+ 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) {
+ ss->copy_left = b;
+ ss->copy_data = *++p;
+ goto top;
+ } else {
+ memset(q + 1, *++p, b);
+ q += b;
+ }
+ }
+x: pr->ptr = p;
+ pw->ptr = q;
+ return status;
+}
+
+/* Stream template */
+const stream_template s_RLD_template = {
+ &st_RLD_state, s_RLD_init, s_RLD_process, 1, 1, NULL,
+ s_RLD_set_defaults
+};
diff --git a/pstoraster/srle.c b/pstoraster/srle.c
new file mode 100644
index 000000000..58476e091
--- /dev/null
+++ b/pstoraster/srle.c
@@ -0,0 +1,203 @@
+/* Copyright (C) 1995, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* RunLengthEncode filter */
+#include "stdio_.h" /* includes std.h */
+#include "memory_.h"
+#include "strimpl.h"
+#include "srlx.h"
+
+/* ------ RunLengthEncode ------ */
+
+private_st_RLE_state();
+
+/* Set defaults */
+private void
+s_RLE_set_defaults(stream_state * st)
+{
+ stream_RLE_state *const ss = (stream_RLE_state *) st;
+
+ s_RLE_set_defaults_inline(ss);
+}
+
+/* Initialize */
+private int
+s_RLE_init(stream_state * st)
+{
+ stream_RLE_state *const ss = (stream_RLE_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)
+{
+ stream_RLE_state *const ss = (stream_RLE_state *) st;
+ 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;
+
+ /*
+ * We thought that the Genoa CET demands that the output from this
+ * filter be not just legal, but optimal, so we went to the trouble
+ * of ensuring this. It turned out that this wasn't actually
+ * necessary, but we didn't want to change the code back.
+ *
+ * For optimal output, we can't just break runs at buffer
+ * boundaries: unless we hit a record boundary or the end of the
+ * input, we have to look ahead far enough to know that we aren't
+ * breaking a run prematurely.
+ */
+
+ /* Check for leftover output. */
+copy:
+ if (ss->copy_left) {
+ uint rcount = rlimit - p;
+ uint wcount = wlimit - q;
+ uint count = ss->copy_left;
+
+ if (rcount < count)
+ count = rcount;
+ if (wcount < count)
+ count = wcount;
+ if (rleft < count)
+ count = rleft;
+ memcpy(q + 1, p + 1, count);
+ pr->ptr = p += count;
+ pw->ptr = q += count;
+ if ((ss->record_left = rleft -= count) == 0)
+ ss->record_left = rleft = ss->record_size;
+ if ((ss->copy_left -= count) != 0)
+ return (rcount == 0 ? 0 : 1);
+ }
+ while (p < rlimit) {
+ const byte *beg = p;
+ const byte *p1;
+ uint count = rlimit - p;
+ bool end = last;
+ byte next;
+
+ if (count > rleft)
+ count = rleft, end = true;
+ if (count > 128)
+ count = 128, end = true;
+ p1 = p + count - 1;
+ if (count < 3) {
+ if (!end || count == 0)
+ break; /* can't look ahead far enough */
+ if (count == 1) {
+ if (wlimit - q < 2) {
+ status = 1;
+ break;
+ }
+ *++q = 0;
+ } else { /* count == 2 */
+ if (p[1] == p[2]) {
+ if (wlimit - q < 2) {
+ status = 1;
+ break;
+ }
+ *++q = 255;
+ } else {
+ if (wlimit - q < 3) {
+ status = 1;
+ break;
+ }
+ *++q = 1;
+ *++q = p[1];
+ }
+ }
+ *++q = p1[1];
+ p = p1 + 1;
+ } else if ((next = p[1]) == p[2] && next == p[3]) {
+ if (wlimit - q < 2) {
+ status = 1;
+ break;
+ }
+ /* Recognize leading repeated byte */
+ do {
+ p++;
+ }
+ while (p < p1 && p[2] == next);
+ if (p == p1 && !end) {
+ p = beg; /* need to look ahead further */
+ break;
+ }
+ p++;
+ *++q = (byte) (257 - (p - beg));
+ *++q = next;
+ } else {
+ p1--;
+ while (p < p1 && (p[2] != p[1] || p[3] != p[1]))
+ p++;
+ if (p == p1) {
+ if (!end) {
+ p = beg; /* need to look ahead further */
+ break;
+ }
+ p += 2;
+ }
+ count = p - beg;
+ if (wlimit - q < count + 1) {
+ p = beg;
+ if (q >= wlimit) {
+ status = 1;
+ break;
+ }
+ /* Copy some now and some later. */
+ *++q = count - 1;
+ ss->copy_left = count;
+ goto copy;
+ }
+ *++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;
+}
+
+/* Stream template */
+const stream_template s_RLE_template = {
+ &st_RLE_state, s_RLE_init, s_RLE_process, 129, 2, NULL,
+ s_RLE_set_defaults, s_RLE_init
+};
diff --git a/pstoraster/srlx.h b/pstoraster/srlx.h
new file mode 100644
index 000000000..eaf89563a
--- /dev/null
+++ b/pstoraster/srlx.h
@@ -0,0 +1,77 @@
+/* Copyright (C) 1994, 1995, 1996, 1997 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Requires scommon.h; strimpl.h if any templates are referenced */
+
+#ifndef srlx_INCLUDED
+# define srlx_INCLUDED
+
+/* 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 */
+ int copy_left; /* # of bytes waiting to be copied */
+} 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),\
+ (ss)->copy_left = 0)
+extern const stream_template s_RLE_template;
+
+/* RunLengthDecode */
+typedef struct stream_RLD_state_s {
+ stream_RL_state_common;
+ /* The following change dynamically. */
+ int copy_left; /* # of output bytes waiting to be produced */
+ int copy_data; /* -1 if copying, repeated byte if repeating */
+} 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)\
+ ((ss)->copy_left = 0)
+extern const stream_template s_RLD_template;
+
+#endif /* srlx_INCLUDED */
diff --git a/pstoraster/sstring.c b/pstoraster/sstring.c
new file mode 100644
index 000000000..f08d5757e
--- /dev/null
+++ b/pstoraster/sstring.c
@@ -0,0 +1,464 @@
+/* Copyright (C) 1993, 1995, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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 ------ */
+
+private_st_AXE_state();
+
+/* Initialize the state */
+private int
+s_AXE_init(stream_state * st)
+{
+ stream_AXE_state *const ss = (stream_AXE_state *) st;
+
+ return s_AXE_init_inline(ss);
+}
+
+/* Process a buffer */
+private int
+s_AXE_process(stream_state * st, stream_cursor_read * pr,
+ stream_cursor_write * pw, bool last)
+{
+ stream_AXE_state *const ss = (stream_AXE_state *) st;
+ const byte *p = pr->ptr;
+ byte *q = pw->ptr;
+ int rcount = pr->limit - p;
+ int wcount = pw->limit - q;
+ int count;
+ int pos = ss->count;
+ const char *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 (!(++pos & 31) && (count != 0 || !last))
+ *++q = '\n';
+ }
+ if (last && status == 0)
+ *++q = '>';
+ pr->ptr = p;
+ pw->ptr = q;
+ ss->count = pos & 31;
+ return status;
+}
+
+/* Stream template */
+const stream_template s_AXE_template =
+{&st_AXE_state, s_AXE_init, s_AXE_process, 1, 3
+};
+
+/* ------ ASCIIHexDecode ------ */
+
+private_st_AXD_state();
+
+/* Initialize the state */
+private int
+s_AXD_init(stream_state * st)
+{
+ stream_AXD_state *const ss = (stream_AXD_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)
+{
+ stream_AXD_state *const ss = (stream_AXD_state *) st;
+ 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; we must unread it, since the caller might have
+ * invoked the filter with exactly the right count to read all
+ * the available data, and we might be reading past the end.
+ */
+ if (*pr->ptr != '>') { /* EOD */
+ --(pr->ptr);
+ return ERRC;
+ }
+ eod:if (ss->odd >= 0) {
+ if (pw->ptr == pw->limit)
+ return 1;
+ *++(pw->ptr) = ss->odd << 4;
+ }
+ return EOFC;
+}
+
+/* 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)
+{
+ const byte *p = pr->ptr;
+ const byte *rlimit = pr->limit;
+ byte *q = pw->ptr;
+ byte *wlimit = pw->limit;
+ int status = 0;
+
+ /* This doesn't have to be very efficient. */
+ while (p < rlimit) {
+ int c = *++p;
+
+ if (c < 32 || c >= 127) {
+ const char *pesc;
+ const char *const 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();
+
+/* Initialize the state */
+private int
+s_PSSD_init(stream_state * st)
+{
+ stream_PSSD_state *const ss = (stream_PSSD_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)
+{
+ stream_PSSD_state *const ss = (stream_PSSD_state *) st;
+ const byte *p = pr->ptr;
+ const byte *rlimit = pr->limit;
+ byte *q = pw->ptr;
+ byte *wlimit = pw->limit;
+ int status = 0;
+ 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;
+}
+
+/* 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;
+ const byte *const 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..c0618e367
--- /dev/null
+++ b/pstoraster/sstring.h
@@ -0,0 +1,79 @@
+/* Copyright (C) 1996, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* are referenced, but some compilers always require strimpl.h. */
+
+#ifndef sstring_INCLUDED
+# define sstring_INCLUDED
+
+/* ASCIIHexEncode */
+typedef struct stream_AXE_state_s {
+ stream_state_common;
+ int count; /* # of digits since last EOL */
+} stream_AXE_state;
+
+#define private_st_AXE_state() /* in sstring.c */\
+ gs_private_st_simple(st_AXE_state, stream_AXE_state,\
+ "ASCIIHexEncode state")
+#define s_AXE_init_inline(ss)\
+ ((ss)->count = 0)
+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")
+#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;
+
+#endif /* sstring_INCLUDED */
diff --git a/pstoraster/stat_.h b/pstoraster/stat_.h
new file mode 100644
index 000000000..dbc04eef5
--- /dev/null
+++ b/pstoraster/stat_.h
@@ -0,0 +1,62 @@
+/* Copyright (C) 1991, 1995, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Generic substitute for Unix sys/stat.h */
+
+#ifndef stat__INCLUDED
+# define stat__INCLUDED
+
+/* 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__) || defined(__BEOS__) || defined(Plan9)
+# 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
+
+#endif /* stat__INCLUDED */
diff --git a/pstoraster/std.h b/pstoraster/std.h
new file mode 100644
index 000000000..503805252
--- /dev/null
+++ b/pstoraster/std.h
@@ -0,0 +1,261 @@
+/* Copyright (C) 1989, 1992, 1993, 1994, 1995, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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 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;
+ * dlprintf for messages to dsterr with optional with file name (and,
+ * if available, line number);
+ * 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 would prefer not to include stdio.h here, but we need it for
+ * the FILE * argument of the printing procedures.
+ */
+#include <stdio.h>
+
+/* dstderr and estderr may be redefined. */
+#define dstderr stderr
+#define estderr stderr
+
+/* Print the program line # for debugging. */
+#if __LINE__ /* compiler provides it */
+void dprintf_file_and_line(P3(FILE *, const char *, int));
+
+# define _dpl dprintf_file_and_line(estderr, __FILE__, __LINE__),
+#else
+void dprintf_file_only(P2(FILE *, const char *));
+
+# define _dpl dprintf_file_only(estderr, __FILE__),
+#endif
+
+#define dputc(chr) dprintf1("%c", chr)
+#define dlputc(chr) dlprintf1("%c", chr)
+#define dputs(str) dprintf1("%s", str)
+#define dlputs(str) dlprintf1("%s", str)
+#define dprintf(str)\
+ fprintf(dstderr, str)
+#define dlprintf(str)\
+ (_dpl dprintf(str))
+#define dprintf1(str,arg1)\
+ fprintf(dstderr, str, arg1)
+#define dlprintf1(str,arg1)\
+ (_dpl dprintf1(str, arg1))
+#define dprintf2(str,arg1,arg2)\
+ fprintf(dstderr, str, arg1, arg2)
+#define dlprintf2(str,arg1,arg2)\
+ (_dpl dprintf2(str, arg1, arg2))
+#define dprintf3(str,arg1,arg2,arg3)\
+ fprintf(dstderr, str, arg1, arg2, arg3)
+#define dlprintf3(str,arg1,arg2,arg3)\
+ (_dpl dprintf3(str, arg1, arg2, arg3))
+#define dprintf4(str,arg1,arg2,arg3,arg4)\
+ fprintf(dstderr, str, arg1, arg2, arg3, arg4)
+#define dlprintf4(str,arg1,arg2,arg3,arg4)\
+ (_dpl dprintf4(str, arg1, arg2, arg3, arg4))
+#define dprintf5(str,arg1,arg2,arg3,arg4,arg5)\
+ fprintf(dstderr, str, arg1, arg2, arg3, arg4, arg5)
+#define dlprintf5(str,arg1,arg2,arg3,arg4,arg5)\
+ (_dpl dprintf5(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 dlprintf6(str,arg1,arg2,arg3,arg4,arg5,arg6)\
+ (_dpl dprintf6(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 dlprintf7(str,arg1,arg2,arg3,arg4,arg5,arg6,arg7)\
+ (_dpl dprintf7(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 dlprintf8(str,arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8)\
+ (_dpl dprintf8(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 dlprintf9(str,arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8,arg9)\
+ (_dpl dprintf9(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 dlprintf10(str,arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8,arg9,arg10)\
+ (_dpl dprintf10(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 dlprintf11(str,arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8,arg9,arg10,arg11)\
+ (_dpl dprintf11(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)
+#define dlprintf12(str,arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8,arg9,arg10,arg11,arg12)\
+ (_dpl dprintf12(str, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12))
+
+void eprintf_program_name(P2(FILE *, const char *));
+const char *gs_program_name(P0());
+
+#define _epn eprintf_program_name(estderr, gs_program_name()),
+
+#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))
+
+#if __LINE__ /* compiler provides it */
+void lprintf_file_and_line(P3(FILE *, const char *, int));
+
+# define _epl _epn lprintf_file_and_line(estderr, __FILE__, __LINE__),
+#else
+void lprintf_file_only(P2(FILE *, const char *));
+
+# define _epl _epn lprintf_file_only(estderr, __FILE__)
+#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..7b2088bbb
--- /dev/null
+++ b/pstoraster/stdio_.h
@@ -0,0 +1,74 @@
+/* Copyright (C) 1992, 1993, 1994, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Generic substitute for stdio.h */
+
+#ifndef stdio__INCLUDED
+# define stdio__INCLUDED
+
+/*
+ * 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
+
+/*
+ * Plan 9 has a system function called sclose, which interferes with the
+ * procedure defined in stream.h. The following makes the system sclose
+ * inaccessible, but avoids the name clash.
+ */
+#ifdef Plan9
+# undef sclose
+# define sclose(s) Sclose(s)
+#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
+
+#endif /* stdio__INCLUDED */
diff --git a/pstoraster/stdpre.h b/pstoraster/stdpre.h
new file mode 100644
index 000000000..b0e3dc66a
--- /dev/null
+++ b/pstoraster/stdpre.h
@@ -0,0 +1,413 @@
+/* Copyright (C) 1993, 1994, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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__.
+ * gcc defines __GNUC__.
+ * 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.
+ * MetroWerks C defines __MWERKS__.
+ *
+ * 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
+
+/* Disable 'inline' if the compiler can't handle it. */
+#ifdef __DECC
+# undef inline
+# define inline __inline
+#else
+# if !(defined(__GNUC__) || defined(__MWERKS__) || defined(inline))
+# define inline /* */
+# endif
+#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, the simpler
+ * definition works on all compilers except for one broken MIPS compiler
+ * and the IBM RS/6000. Unfortunately, because of these two compilers,
+ * we have to use the more complex definition. Even more unfortunately,
+ * the more complex definition doesn't work on the MetroWerks
+ * CodeWarrior compiler (Macintosh and BeOS).
+ */
+#ifdef __MWERKS__
+#define offset_of(type, memb)\
+ ((int) &((type *) 0)->memb)
+#else
+#define offset_of(type, memb)\
+ ((int) ( (char *)&((type *)0)->memb - (char *)((type *)0) ))
+#endif
+
+/*
+ * Get the alignment of a pointer modulo a given power of 2.
+ * There is no portable way to do this, but the following definition
+ * works on all reasonable systems.
+ */
+#define alignment_mod(ptr, modu)\
+ ((uint)( ((const char *)(ptr) - (const char *)0) & ((modu) - 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
+
+/*
+ * 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).
+ * Unfortunately, at least some C++ compilers have a built-in bool type,
+ * and the MetroWerks C++ compiler insists that bool be equivalent to
+ * unsigned char.
+ */
+#ifndef __cplusplus
+#ifdef __BEOS__
+typedef unsigned char bool;
+
+#else
+typedef int bool;
+
+#endif
+#endif
+/*
+ * MetroWerks CodeWarrior predefines true and false, probably as 1 and 0.
+ * We need to cancel those definitions for our own code.
+ */
+#undef false
+#define false ((bool)0)
+#undef true
+#define true ((bool)1)
+
+/*
+ * 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;
+
+/*
+ * Because of C's strange insistence that ; is a terminator and not a
+ * separator, compound statements {...} are not syntactically equivalent to
+ * single statements. Therefore, we define here a compound-statement
+ * construct that *is* syntactically equivalent to a single statement.
+ * Usage is
+ * BEGIN
+ * ...statements...
+ * END
+ */
+#define BEGIN do {
+#define END } while (0)
+
+/*
+ * Define a handy macro for a statement that does nothing.
+ * We can't just use an empty statement, since this upsets some compilers.
+ */
+#ifndef DO_NOTHING
+# define DO_NOTHING BEGIN END
+#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.
+ * The definition used to have a _ds modifier, so we had to coerce
+ * them when passing them to printf at all; this is no longer needed.
+ */
+typedef const char *client_name_t;
+
+#define client_name_string(cname) (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
+# define P16(t1,t2,t3,t4,t5,t6,t7,t8,t9,t10,t11,t12,t13,t14,t15,t16) t1,t2,t3,t4,t5,t6,t7,t8,t9,t10,t11,t12,t13,t14,t15,t16
+#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) /* */
+# define P16(t1,t2,t3,t4,t5,t6,t7,t8,t9,t10,t11,t12,t13,t14,t15,t16) /* */
+#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
+/*
+ * Define the informational exit status.
+ * We need to distinguish information returns because under MS Windows,
+ * they must return like an error so that the text window stays on the
+ * screen, while on other platforms, they must return successfully.
+ * Note that we define both gs_exit_INFO (before platform-specific
+ * mapping of 0 to exit_OK and 1 to exit_FAILED) and exit_INFO.
+ */
+#if defined(_WINDOWS) || defined(_Windows)
+# define exit_INFO exit_FAILED
+# define gs_exit_INFO 1
+#else
+# define exit_INFO exit_OK
+# define gs_exit_INFO 0
+#endif
+
+#endif /* stdpre_INCLUDED */
diff --git a/pstoraster/store.h b/pstoraster/store.h
new file mode 100644
index 000000000..0488cf974
--- /dev/null
+++ b/pstoraster/store.h
@@ -0,0 +1,250 @@
+/* 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 supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Assignment-related macros */
+
+#ifndef store_INCLUDED
+# define store_INCLUDED
+
+#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_oper_new(pref,opidx,proc)\
+ make_tasv_new(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))
+
+#endif /* store_INCLUDED */
diff --git a/pstoraster/stream.c b/pstoraster/stream.c
new file mode 100644
index 000000000..abe3b103e
--- /dev/null
+++ b/pstoraster/stream.c
@@ -0,0 +1,911 @@
+/* Copyright (C) 1989, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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)
+ ENUM_RETURN(NULL);
+else if (st->cbuf_string.data != 0)
+ ENUM_RETURN_STRING_PTR(stream, cbuf_string);
+else
+ ENUM_RETURN(st->cbuf);
+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_VAR(st->cbuf_string);
+ st->cbuf = st->cbuf_string.data;
+ } else
+ RELOC_VAR(st->cbuf);
+ reloc = cbuf_old - st->cbuf;
+ /* Relocate the other buffer pointers. */
+ st->srptr -= reloc;
+ st->srlimit -= reloc; /* same as swptr */
+ st->swlimit -= reloc;
+ }
+ RELOC_VAR(st->strm);
+ RELOC_VAR(st->prev);
+ RELOC_VAR(st->next);
+ RELOC_VAR(st->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 */
+ s->close_strm = false; /* default */
+ 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;
+ /* The pointers in the next two statements should really be */
+ /* initialized to ([const] byte *)0 - 1, but some very picky */
+ /* compilers complain about this. */
+ s->cursor.r.ptr = s->cursor.r.limit = 0;
+ s->cursor.w.limit = 0;
+ 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;
+}
+
+/* Generic procedure structures for filters. */
+
+const stream_procs s_filter_read_procs =
+{
+ s_std_noavailable, s_std_noseek, s_std_read_reset,
+ s_std_read_flush, s_filter_close
+};
+
+const stream_procs s_filter_write_procs =
+{
+ s_std_noavailable, s_std_noseek, s_std_write_reset,
+ s_std_write_flush, s_filter_close
+};
+
+/* ------ 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 = 0; /* depth of nesting in non-temp streams */
+ int status;
+
+ /*
+ * The handling of EOFC is a little tricky. There are two
+ * invariants that keep it straight:
+ * - We only pass last = true to a stream if either it is
+ * the first stream in the pipeline, or it is a temporary stream
+ * below the first stream and the stream immediately above it has
+ * end_status = EOFC.
+ * - We never unwind the recursion past a stream with
+ * end_status < 0.
+ */
+ 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 &&
+ (prev == 0 ||
+ (depth <= 1 && prev->end_status == EOFC));
+
+ 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->end_status;
+ if (status < 0)
+ break;
+ status = (*curr->procs.process) (curr->state,
+ pr, pw, end);
+ if_debug5('s', "[s]after write 0x%lx, nr=%u, nw=%u, end=%d, status=%d\n",
+ (ulong) curr, (uint) (pr->limit - pr->ptr),
+ (uint) (pw->limit - pw->ptr), end, status);
+ if (status == 0 && end)
+ status = EOFC;
+ if (status == EOFC || status == ERRC)
+ curr->end_status = status;
+ if (strm == 0 || (status < 0 && status != EOFC))
+ break;
+ if (status == 1)
+ end = false;
+ else { /* Keep going if we are closing */
+ /* a filter with a sub-stream. */
+ /* We know status == 0 or EOFC. */
+ if (!end || !strm->is_temp)
+ break;
+ }
+ status = strm->end_status;
+ if (status < 0)
+ break;
+ move_ahead(curr, prev);
+ stream_compact(curr, false);
+ if (!curr->is_temp)
+ ++depth;
+ }
+ /* Unwind from the recursion. */
+ curr->end_status = (status >= 0 ? 0 : status);
+ if (status < 0 || prev == 0) { /*
+ * All streams above here were called with last = true
+ * and returned 0 or EOFC: finish unwinding and then
+ * return.
+ */
+ while (prev) {
+ move_back(curr, prev);
+ curr->end_status = (status >= 0 ? 0 : status);
+ }
+ return status;
+ }
+ move_back(curr, prev);
+ 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);
+}
+
+/* ------ Position-tracking stream ------ */
+
+private int
+ s_write_position_process(P4(stream_state *, stream_cursor_read *,
+ stream_cursor_write *, bool));
+
+/* Set up a write stream that just keeps track of the position. */
+void
+swrite_position_only(stream *s)
+{
+ static byte buf[50]; /* size is arbitrary */
+
+ swrite_string(s, buf, sizeof(buf));
+ s->procs.process = s_write_position_process;
+}
+
+private int
+s_write_position_process(stream_state * st, stream_cursor_read * pr,
+ stream_cursor_write * ignore_pw, bool last)
+{
+ pr->ptr = pr->limit; /* discard data */
+ return 0;
+}
diff --git a/pstoraster/stream.h b/pstoraster/stream.h
new file mode 100644
index 000000000..a0546e6af
--- /dev/null
+++ b/pstoraster/stream.h
@@ -0,0 +1,334 @@
+/* Copyright (C) 1989, 1995, 1996, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Definitions for Ghostscript stream package */
+/* Requires stdio.h */
+
+#ifndef stream_INCLUDED
+# define stream_INCLUDED
+
+#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;
+ * EOFC if a read stream has reached EOD or a write
+ * stream has written the EOD marker;
+ * ERRC if an error terminated the last read or write
+ * operation from or to the underlying data source
+ * or sink;
+ * INTC if the last transfer was interrupted (NOT
+ * USED YET);
+ * CALLC if a callout is required.
+ */
+ 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 and next,
+ * zfilter.c for more information on close_strm.
+ */
+ 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 */
+ bool close_strm; /* CloseSource/CloseTarget */
+ /*
+ * 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));
+
+/* Create a stream on a string or a file. */
+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));
+
+/* Create a stream that tracks the position, */
+/* for calculating how much space to allocate when actually writing. */
+void swrite_position_only(P1(stream *));
+
+/* 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 *));
+
+/* Generic procedure structures for filters. */
+extern const stream_procs s_filter_read_procs, s_filter_write_procs;
+
+#endif /* stream_INCLUDED */
diff --git a/pstoraster/strimpl.h b/pstoraster/strimpl.h
new file mode 100644
index 000000000..6ba9560ff
--- /dev/null
+++ b/pstoraster/strimpl.h
@@ -0,0 +1,154 @@
+/* Copyright (C) 1993, 1995, 1997 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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,
+ * or no more input can be processed for some other reason (e.g.,
+ * the stream was told only to read a certain amount of data).
+ * 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. In this
+ * case:
+ * - If the procedure returns 1, it may be called again (also with
+ * last = 1).
+ * - If the procedure returns any other value other than 1, the
+ * procedure will never be called again for that stream.
+ * - If the procedure returns 0, this is taken as equivalent to
+ * returning EOFC.
+ * - If the procedure returns EOFC (or 0), the stream's end_status
+ * is set to EOFC, meaning no more writing is allowed.
+ *
+ * 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..7771125a3
--- /dev/null
+++ b/pstoraster/string_.h
@@ -0,0 +1,59 @@
+/* Copyright (C) 1989, 1992, 1993, 1994, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Generic substitute for Unix string.h */
+
+#ifndef string__INCLUDED
+# define string__INCLUDED
+
+/* 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
+
+#endif /* string__INCLUDED */
diff --git a/pstoraster/szlibc.c b/pstoraster/szlibc.c
new file mode 100644
index 000000000..72219a2fa
--- /dev/null
+++ b/pstoraster/szlibc.c
@@ -0,0 +1,140 @@
+/*
+ Copyright 1993-2000 by Easy Software Products
+ Copyright 1995, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+#include <config.h>
+#ifdef HAVE_LIBZ
+
+/*$Id$ */
+/* Code common to zlib encoding and decoding streams */
+#include "std.h"
+#include "gserror.h"
+#include "gserrors.h"
+#include "gstypes.h"
+#include "gsmemory.h"
+#include "gsmalloc.h"
+#include "gsstruct.h"
+#include "strimpl.h"
+#include "szlibxx.h"
+#include "zconf.h"
+
+private_st_zlib_block();
+private_st_zlib_dynamic_state();
+public_st_zlib_state();
+
+/* Set defaults for stream parameters. */
+void
+s_zlib_set_defaults(stream_state * st)
+{
+ stream_zlib_state *const ss = (stream_zlib_state *)st;
+#endif
+
+ ss->windowBits = MAX_WBITS;
+ ss->no_wrapper = false;
+ ss->level = Z_DEFAULT_COMPRESSION;
+ ss->method = Z_DEFLATED;
+ /* DEF_MEM_LEVEL should be in zlib.h or zconf.h, but it isn't. */
+ ss->memLevel = min(MAX_MEM_LEVEL, 8);
+ ss->strategy = Z_DEFAULT_STRATEGY;
+}
+
+/* Allocate the dynamic state. */
+int
+s_zlib_alloc_dynamic_state(stream_zlib_state *ss)
+{
+ gs_memory_t *mem = (ss->memory ? ss->memory : &gs_memory_default);
+ zlib_dynamic_state_t *zds =
+ gs_alloc_struct_immovable(mem, zlib_dynamic_state_t,
+ &st_zlib_dynamic_state,
+ "s_zlib_alloc_dynamic_state");
+
+ ss->dynamic = zds;
+ if (zds == 0)
+ return_error(gs_error_VMerror);
+ zds->blocks = 0;
+ zds->memory = mem;
+ zds->zstate.zalloc = (alloc_func)s_zlib_alloc;
+ zds->zstate.zfree = (free_func)s_zlib_free;
+ zds->zstate.opaque = (voidpf)zds;
+ return 0;
+}
+
+/* Free the dynamic state. */
+void
+s_zlib_free_dynamic_state(stream_zlib_state *ss)
+{
+ if (ss->dynamic)
+ gs_free_object(ss->dynamic->memory, ss->dynamic,
+ "s_zlib_free_dynamic_state");
+}
+
+/* Provide zlib-compatible allocation and freeing functions. */
+void *
+s_zlib_alloc(void *zmem, uint items, uint size)
+{
+ zlib_dynamic_state_t *const zds = zmem;
+ gs_memory_t *mem = zds->memory;
+ zlib_block_t *block =
+ gs_alloc_struct(mem, zlib_block_t, &st_zlib_block,
+ "s_zlib_alloc(block)");
+ void *data =
+ gs_alloc_byte_array_immovable(mem, items, size, "s_zlib_alloc(data)");
+
+ if (block == 0 || data == 0) {
+ gs_free_object(mem, data, "s_zlib_alloc(data)");
+ gs_free_object(mem, block, "s_zlib_alloc(block)");
+ return Z_NULL;
+ }
+ block->data = data;
+ block->next = zds->blocks;
+ block->prev = 0;
+ if (zds->blocks)
+ zds->blocks->prev = block;
+ zds->blocks = block;
+ return data;
+}
+void
+s_zlib_free(void *zmem, void *data)
+{
+ zlib_dynamic_state_t *const zds = zmem;
+ gs_memory_t *mem = zds->memory;
+ zlib_block_t *block = zds->blocks;
+
+ gs_free_object(mem, data, "s_zlib_free(data)");
+ for (; ; block = block->next) {
+ if (block == 0) {
+ lprintf1("Freeing unrecorded data 0x%lx!\n", (ulong)data);
+ return;
+ }
+ if (block->data == data)
+ break;
+ }
+ if (block->next)
+ block->next->prev = block->prev;
+ if (block->prev)
+ block->prev->next = block->next;
+ else
+ zds->blocks = block->next;
+ gs_free_object(mem, block, "s_zlib_free(block)");
+}
diff --git a/pstoraster/szlibd.c b/pstoraster/szlibd.c
new file mode 100644
index 000000000..f47833b6e
--- /dev/null
+++ b/pstoraster/szlibd.c
@@ -0,0 +1,115 @@
+/*
+ Copyright 1993-2000 by Easy Software Products.
+ Copyright 1995, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+#include <config.h>
+#ifdef HAVE_LIBZ
+
+/*$Id$ */
+/* zlib decoding (decompression) filter stream */
+#include "std.h"
+#include "gsmemory.h"
+#include "gsmalloc.h" /* for gs_memory_default */
+#include "strimpl.h"
+#include "szlibxx.h"
+
+/* Initialize the filter. */
+private int
+s_zlibD_init(stream_state * st)
+{
+ stream_zlib_state *const ss = (stream_zlib_state *)st;
+ int code = s_zlib_alloc_dynamic_state(ss);
+
+ if (code < 0)
+ return ERRC; /****** WRONG ******/
+ if (inflateInit2(&ss->dynamic->zstate,
+ (ss->no_wrapper ? -ss->windowBits : ss->windowBits))
+ != Z_OK
+ ) {
+ s_zlib_free_dynamic_state(ss);
+ return ERRC; /****** WRONG ******/
+ }
+ return 0;
+}
+
+/* Reinitialize the filter. */
+private int
+s_zlibD_reset(stream_state * st)
+{
+ stream_zlib_state *const ss = (stream_zlib_state *)st;
+
+ if (inflateReset(&ss->dynamic->zstate) != 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)
+{
+ stream_zlib_state *const ss = (stream_zlib_state *)st;
+ z_stream *zs = &ss->dynamic->zstate;
+ 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;
+ zs->next_in = (Bytef *)p + 1;
+ zs->avail_in = pr->limit - p;
+ zs->next_out = pw->ptr + 1;
+ zs->avail_out = pw->limit - pw->ptr;
+ status = inflate(zs, Z_PARTIAL_FLUSH);
+ pr->ptr = zs->next_in - 1;
+ pw->ptr = zs->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)
+{
+ stream_zlib_state *const ss = (stream_zlib_state *)st;
+
+ inflateEnd(&ss->dynamic->zstate);
+ s_zlib_free_dynamic_state(ss);
+}
+
+/* Stream template */
+const stream_template s_zlibD_template = {
+ &st_zlib_state, s_zlibD_init, s_zlibD_process, 1, 1, s_zlibD_release,
+ s_zlib_set_defaults, s_zlibD_reset
+};
+#endif /* HAVE_LIBZ */
diff --git a/pstoraster/szlibe.c b/pstoraster/szlibe.c
new file mode 100644
index 000000000..e5ff7c2af
--- /dev/null
+++ b/pstoraster/szlibe.c
@@ -0,0 +1,112 @@
+/*
+ Copyright 1993-2000 by Easy Software Products
+ Copyright 1995, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+#include <config.h>
+#ifdef HAVE_LIBZ
+
+/*$Id$ */
+/* zlib encoding (compression) filter stream */
+#include "std.h"
+#include "gsmemory.h"
+#include "gsmalloc.h" /* for gs_memory_default */
+#include "strimpl.h"
+#include "szlibxx.h"
+
+/* Initialize the filter. */
+private int
+s_zlibE_init(stream_state * st)
+{
+ stream_zlib_state *const ss = (stream_zlib_state *)st;
+ int code = s_zlib_alloc_dynamic_state(ss);
+
+ if (code < 0)
+ return ERRC; /****** WRONG ******/
+ if (deflateInit2(&ss->dynamic->zstate, ss->level, ss->method,
+ (ss->no_wrapper ? -ss->windowBits : ss->windowBits),
+ ss->memLevel, ss->strategy) != Z_OK)
+ return ERRC; /****** WRONG ******/
+ return 0;
+}
+
+/* Reinitialize the filter. */
+private int
+s_zlibE_reset(stream_state * st)
+{
+ stream_zlib_state *const ss = (stream_zlib_state *)st;
+
+ if (deflateReset(&ss->dynamic->zstate) != 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)
+{
+ stream_zlib_state *const ss = (stream_zlib_state *)st;
+ z_stream *zs = &ss->dynamic->zstate;
+ 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 (p == pr->limit && !last)
+ return 0;
+ zs->next_in = (Bytef *)p + 1;
+ zs->avail_in = pr->limit - p;
+ zs->next_out = pw->ptr + 1;
+ zs->avail_out = pw->limit - pw->ptr;
+ status = deflate(zs, (last ? Z_FINISH : Z_NO_FLUSH));
+ pr->ptr = zs->next_in - 1;
+ pw->ptr = zs->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 (last && pr->ptr == pr->limit ? 0 : ERRC);
+ default:
+ return ERRC;
+ }
+}
+
+/* Release the stream */
+private void
+s_zlibE_release(stream_state * st)
+{
+ stream_zlib_state *const ss = (stream_zlib_state *)st;
+
+ deflateEnd(&ss->dynamic->zstate);
+ s_zlib_free_dynamic_state(ss);
+}
+
+/* Stream template */
+const stream_template s_zlibE_template = {
+ &st_zlib_state, s_zlibE_init, s_zlibE_process, 1, 1, s_zlibE_release,
+ s_zlib_set_defaults, s_zlibE_reset
+};
+#endif
diff --git a/pstoraster/szlibx.h b/pstoraster/szlibx.h
new file mode 100644
index 000000000..f6f09f9bc
--- /dev/null
+++ b/pstoraster/szlibx.h
@@ -0,0 +1,64 @@
+/* Copyright (C) 1995, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* zlib filter state definition */
+
+#ifndef szlibx_INCLUDED
+# define szlibx_INCLUDED
+
+/* Define an opaque type for the dynamic part of the state. */
+typedef struct zlib_dynamic_state_s zlib_dynamic_state_t;
+
+/* Define the stream state structure. */
+typedef struct stream_zlib_state_s {
+ stream_state_common;
+ /* Parameters - compression and decompression */
+ int windowBits;
+ bool no_wrapper; /* omit wrapper and checksum */
+ /* Parameters - compression only */
+ int level; /* effort level */
+ int method;
+ int memLevel;
+ int strategy;
+ /* Dynamic state */
+ zlib_dynamic_state_t *dynamic;
+} stream_zlib_state;
+
+/*
+ * The state descriptor is public only to allow us to split up
+ * the encoding and decoding filters.
+ */
+extern_st(st_zlib_state);
+#define public_st_zlib_state() /* in szlibc.c */\
+ gs_public_st_ptrs1(st_zlib_state, stream_zlib_state,\
+ "zlibEncode/Decode state", zlib_state_enum_ptrs, zlib_state_reloc_ptrs,\
+ dynamic)
+extern const stream_template s_zlibD_template;
+extern const stream_template s_zlibE_template;
+
+/* Shared procedures */
+stream_proc_set_defaults(s_zlib_set_defaults);
+
+#endif /* szlibx_INCLUDED */
diff --git a/pstoraster/szlibxx.h b/pstoraster/szlibxx.h
new file mode 100644
index 000000000..cffffab00
--- /dev/null
+++ b/pstoraster/szlibxx.h
@@ -0,0 +1,73 @@
+/* Copyright (C) 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Implementation definitions for zlib interface */
+/* Must be compiled with -I$(ZSRCDIR) */
+
+#ifndef szlibxx_INCLUDED
+# define szlibxx_INCLUDED
+
+#include "szlibx.h"
+#include "zlib.h"
+
+/*
+ * We don't want to allocate zlib's private data directly from
+ * the C heap, but we must allocate it as immovable; and to avoid
+ * garbage collection issues, we must keep GC-traceable pointers
+ * to every block allocated. Since the stream state itself is movable,
+ * we have to allocate an immovable block for the z_stream state as well.
+ */
+typedef struct zlib_block_s zlib_block_t;
+struct zlib_block_s {
+ void *data;
+ zlib_block_t *next;
+ zlib_block_t *prev;
+};
+#define private_st_zlib_block() /* in szlibc.c */\
+ gs_private_st_ptrs3(st_zlib_block, zlib_block_t, "zlib_block_t",\
+ zlib_block_enum_ptrs, zlib_block_reloc_ptrs, next, prev, data)
+/* The typedef is in szlibx.h */
+/*typedef*/ struct zlib_dynamic_state_s {
+ gs_memory_t *memory;
+ zlib_block_t *blocks;
+ z_stream zstate;
+} /*zlib_dynamic_state_t*/;
+#define private_st_zlib_dynamic_state() /* in szlibc.c */\
+ gs_private_st_ptrs1(st_zlib_dynamic_state, zlib_dynamic_state_t,\
+ "zlib_dynamic_state_t", zlib_dynamic_enum_ptrs, zlib_dynamic_reloc_ptrs,\
+ blocks)
+
+/*
+ * Provide zlib-compatible allocation and freeing functions.
+ * The mem pointer actually points to the dynamic state.
+ */
+void *s_zlib_alloc(P3(void *mem, uint items, uint size));
+void s_zlib_free(P2(void *mem, void *address));
+
+/* Internal procedure to allocate and free the dynamic state. */
+int s_zlib_alloc_dynamic_state(P1(stream_zlib_state *ss));
+void s_zlib_free_dynamic_state(P1(stream_zlib_state *ss));
+
+#endif /* szlibxx_INCLUDED */
diff --git a/pstoraster/time_.h b/pstoraster/time_.h
new file mode 100644
index 000000000..02c647e1f
--- /dev/null
+++ b/pstoraster/time_.h
@@ -0,0 +1,70 @@
+/*
+ Copyright 1993-2000 by Easy Software Products.
+ Copyright 1991, 1995, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Generic substitute for Unix sys/time.h */
+
+#ifndef time__INCLUDED
+# define time__INCLUDED
+
+/* We must include std.h before any file that includes sys/types.h. */
+#include "std.h"
+
+#include <config.h>
+
+/*
+ * Some System V environments don't include sys/time.h.
+ * The HAVE_SYS_TIME_H switch in gconfig_.h reflects this.
+ */
+#include <sys/time.h>
+#include <time.h>
+
+/*
+ * 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>. */
+#ifdef HAVE_SYS_TIMES_H
+# 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
+
+#endif /* time__INCLUDED */
diff --git a/pstoraster/vmsmath.h b/pstoraster/vmsmath.h
new file mode 100644
index 000000000..6e247b5f9
--- /dev/null
+++ b/pstoraster/vmsmath.h
@@ -0,0 +1,49 @@
+/* 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 supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Substitute for math.h on VAX/VMS systems */
+
+#ifndef vmsmath_INCLUDED
+# define vmsmath_INCLUDED
+
+/* 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
+
+#endif /* vmsmath_INCLUDED */
diff --git a/pstoraster/zarith.c b/pstoraster/zarith.c
new file mode 100644
index 000000000..74584ffe2
--- /dev/null
+++ b/pstoraster/zarith.c
@@ -0,0 +1,373 @@
+/* Copyright (C) 1989, 1992, 1993, 1994, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Arithmetic operators */
+#include "math_.h"
+#include "ghost.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)
+
+/* <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)
+{
+ 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 += op->value.realval;
+ break;
+ case t_integer:
+ make_real(op - 1, (double)op[-1].value.intval + 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 += (double)op->value.intval;
+ break;
+ case t_integer: {
+ 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, (double)(op[-1].value.intval - int2) + int2);
+ }
+ }
+ }
+ }
+ 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, (double)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 /= (double)op->value.intval;
+ break;
+ case t_integer:
+ make_real(op1, (double)op1->value.intval / (double)op->value.intval);
+ }
+ }
+ pop(1);
+ return 0;
+}
+
+/* <num1> <num2> mul <product> */
+private int
+zmul(register os_ptr op)
+{
+ 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 *= op->value.realval;
+ break;
+ case t_integer:
+ make_real(op - 1, (double)op[-1].value.intval * 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 *= (double)op->value.intval;
+ break;
+ case t_integer: {
+ 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;
+ }
+ }
+ }
+ 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)
+{
+ 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 -= op->value.realval;
+ break;
+ case t_integer:
+ make_real(op - 1, (double)op[-1].value.intval - 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 -= (double)op->value.intval;
+ break;
+ case t_integer: {
+ 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);
+ }
+ }
+ }
+ }
+ 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)
+{
+ check_type(*op, t_integer);
+ check_type(op[-1], t_integer);
+ if (op->value.intval == 0)
+ return_error(e_undefinedresult);
+ if ((op[-1].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;
+}
+
+/* Non-standard operators */
+
+/* <int1> <int2> .bitadd <sum> */
+private int
+zbitadd(register os_ptr op)
+{
+ check_type(*op, t_integer);
+ check_type(op[-1], t_integer);
+ op[-1].value.intval += op->value.intval;
+ pop(1);
+ return 0;
+}
+
+/* ------ Initialization table ------ */
+
+const op_def zarith_op_defs[] =
+{
+ {"2add", zadd},
+ {"2.bitadd", zbitadd},
+ {"1ceiling", zceiling},
+ {"2div", zdiv},
+ {"2idiv", zidiv},
+ {"1floor", zfloor},
+ {"2mod", zmod},
+ {"2mul", zmul},
+ {"1neg", zneg},
+ {"1round", zround},
+ {"2sub", zsub},
+ {"1truncate", ztruncate},
+ op_def_end(0)
+};
diff --git a/pstoraster/zarray.c b/pstoraster/zarray.c
new file mode 100644
index 000000000..04af4666b
--- /dev/null
+++ b/pstoraster/zarray.c
@@ -0,0 +1,131 @@
+/* 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 supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Array operators */
+#include "memory_.h"
+#include "ghost.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;
+ uint asize;
+
+ ref_assign(&aref, op);
+ if (!r_is_array(&aref))
+ return_op_typecheck(op);
+ check_read(aref);
+ 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(op, aref.value.refs, asize * sizeof(ref));
+ else {
+ uint i;
+ const ref_packed *packed = aref.value.packed;
+ os_ptr pdest = op;
+
+ for (i = 0; i < asize; i++, pdest++, packed = packed_next(packed))
+ packed_get(packed, pdest);
+ }
+ push(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 ------ */
+
+const op_def zarray_op_defs[] =
+{
+ {"1aload", zaload},
+ {"1array", zarray},
+ {"1astore", zastore},
+ op_def_end(0)
+};
diff --git a/pstoraster/zbseq.c b/pstoraster/zbseq.c
new file mode 100644
index 000000000..037a89e3e
--- /dev/null
+++ b/pstoraster/zbseq.c
@@ -0,0 +1,136 @@
+/* Copyright (C) 1990, 1995, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Level 2 binary object sequence operators */
+#include "memory_.h"
+#include "ghost.h"
+#include "oper.h"
+#include "ialloc.h"
+#include "btoken.h"
+#include "store.h"
+
+/* Current binary format (in iscan.c) */
+extern ref ref_binary_object_format;
+
+/* System and user name arrays. */
+ref binary_token_names; /* array of size 2 */
+private ref *const binary_token_names_p = &binary_token_names;
+
+/* 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 *));
+
+/* Initialize the binary token machinery. */
+private void
+zbseq_init(void)
+{
+ /* Initialize fake system and user name tables. */
+ /* PostScript code will install the real system name table. */
+ ialloc_ref_array(&binary_token_names, 0 /*a_noaccess */ , 2,
+ "binary token names");
+ make_empty_array(system_names_p, a_readonly);
+ make_empty_array(user_names_p, a_all);
+ gs_register_ref_root(imemory, NULL, (void **)&binary_token_names_p,
+ "binary token names");
+
+ /* Set up Level 2 scanning constants. */
+ scan_btoken_proc = scan_binary_token;
+}
+
+/* <names> .installsystemnames - */
+private int
+zinstallsystemnames(register os_ptr op)
+{
+ if (r_space(op) != avm_global)
+ return_error(e_invalidaccess);
+ check_read_type(*op, t_shortarray);
+ ref_assign_old(NULL, system_names_p, op, ".installsystemnames");
+ pop(1);
+ 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;
+}
+
+/* <ref_offset> <char_offset> <obj> <string8> .bosobject */
+/* <ref_offset'> <char_offset'> <string8> */
+/*
+ * This converts a single object to its binary object sequence
+ * representation, doing 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.) Note that this may or may not modify the 'unused' field.
+ */
+
+private int
+zbosobject(os_ptr op)
+{
+ int code;
+
+ 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);
+ code = encode_binary_token(op - 1, &op[-3].value.intval,
+ &op[-2].value.intval, op->value.bytes);
+ if (code < 0)
+ return code;
+ op[-1] = *op;
+ r_set_size(op - 1, 8);
+ pop(1);
+ return 0;
+}
+
+/* ------ Initialization procedure ------ */
+
+const op_def zbseq_l2_op_defs[] =
+{
+ op_def_begin_level2(),
+ {"1.installsystemnames", zinstallsystemnames},
+ {"0currentobjectformat", zcurrentobjectformat},
+ {"1setobjectformat", zsetobjectformat},
+ {"4.bosobject", zbosobject},
+ op_def_end(zbseq_init)
+};
diff --git a/pstoraster/zcfont.c b/pstoraster/zcfont.c
new file mode 100644
index 000000000..061a3a15f
--- /dev/null
+++ b/pstoraster/zcfont.c
@@ -0,0 +1,164 @@
+/* Copyright (C) 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Composite font-related character operators */
+#include "ghost.h"
+#include "oper.h"
+#include "gschar.h"
+#include "gsmatrix.h" /* for gxfont.h */
+#include "gxfixed.h" /* for gxfont.h */
+#include "gxfont.h"
+#include "gxchar.h"
+#include "estack.h"
+#include "ichar.h"
+#include "ifont.h"
+#include "igstate.h"
+#include "store.h"
+
+/* Forward references */
+private int cshow_continue(P1(os_ptr));
+private int cshow_restore_font(P1(os_ptr));
+
+/* <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_font *font = gs_show_current_font(penum);
+ gs_font *scaled_font;
+
+ gs_show_current_width(penum, &wpt);
+#if 0
+ /****************
+ * The following code is logically correct (or at least,
+ * probably more correct than the code it replaces),
+ * but because of the issues about creating the scaled
+ * font in the correct VM space that make_font (in zfont.c)
+ * has to deal with, it creates references pointing to
+ * the wrong spaces. Therefore, we've removed it.
+ *****************/
+ {
+ int fdepth = penum->fstack.depth;
+
+ if (fdepth <= 0)
+ scaled_font = font; /* not composite */
+ else {
+ code = gs_makefont(font->dir, font,
+ &penum->fstack.items[fdepth - 1].font->
+ FontMatrix, &scaled_font);
+ if (code < 0)
+ return code;
+ }
+ }
+#else
+ scaled_font = font;
+#endif
+ 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);
+ if (scaled_font != gs_rootfont(igs))
+ push_op_estack(cshow_restore_font);
+ /* cshow does not change rootfont for user procedure */
+ gs_set_currentfont(igs, scaled_font);
+ *++esp = *pslot; /* user procedure */
+ }
+ return o_push_estack;
+}
+private int
+cshow_restore_font(os_ptr op)
+{ /* We have 1 more entry on the e-stack (cshow_continue). */
+ return gs_show_restore_font(esenum(esp - 1));
+}
+
+/* - rootfont <font> */
+private int
+zrootfont(os_ptr op)
+{
+ push(1);
+ *op = *pfont_dict(gs_rootfont(igs));
+ return 0;
+}
+
+/* ------ Initialization procedure ------ */
+
+const op_def zcfont_op_defs[] =
+{
+ {"2cshow", zcshow},
+ {"0rootfont", zrootfont},
+ /* Internal operators */
+ {"0%cshow_continue", cshow_continue},
+ {"0%cshow_restore_font", cshow_restore_font},
+ op_def_end(0)
+};
diff --git a/pstoraster/zchar.c b/pstoraster/zchar.c
new file mode 100644
index 000000000..3911885fb
--- /dev/null
+++ b/pstoraster/zchar.c
@@ -0,0 +1,710 @@
+/* Copyright (C) 1989, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Character operators */
+#include "ghost.h"
+#include "oper.h"
+#include "gsstruct.h"
+#include "gxarith.h"
+#include "gxfixed.h"
+#include "gxmatrix.h" /* for ifont.h */
+#include "gxchar.h" /* for stringwidth_flag */
+#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));
+private int op_show_return_width(P3(os_ptr, uint, double *));
+
+/* <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;
+ double 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;
+ double 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;
+ double 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)
+{
+ double 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_double(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)
+{
+ double 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_double(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)
+{
+ double 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)
+{
+ double 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 ------ */
+
+const op_def 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},
+ op_def_end(0)
+};
+
+/* ------ Subroutines ------ */
+
+/* Most of these are exported for zchar2.c. */
+
+/* Convert a glyph to a ref. */
+private void
+glyph_ref(gs_glyph glyph, ref * gref)
+{
+ if (glyph < gs_min_cid_glyph)
+ name_index_ref(glyph, gref);
+ else
+ make_int(gref, glyph - gs_min_cid_glyph);
+}
+
+/* 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)
+{
+ 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_int(&esgslevel(ep), igs->level);
+ 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 op_show_continue_dispatch 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(code);
+ 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. For other font types
+ * (such as CID fonts), only BuildGlyph will be present.
+ */
+ if (pfont->FontType == ft_user_defined) { /* Type 3 font, prefer BuildGlyph. */
+ if (level2_enabled &&
+ !r_has_type(&pfdata->BuildGlyph, t_null) &&
+ glyph != gs_no_glyph
+ ) {
+ glyph_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;
+
+ glyph_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 {
+ /*
+ * For a Type 1 or Type 4 font, prefer BuildChar: we know
+ * that both BuildChar and BuildGlyph are present. For
+ * other font types, only BuildGlyph is available.
+ */
+ if (chr != gs_no_char && !r_has_type(&pfdata->BuildChar, t_null)) {
+ make_int(op, chr);
+ esp[2] = pfdata->BuildChar;
+ } else {
+ /* We might not have a glyph: substitute 0. **HACK** */
+ if (glyph == gs_no_glyph)
+ make_int(op, 0);
+ else
+ glyph_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:
+ if (code >= 0)
+ code = gs_note_error(e_invalidfont);
+ return op_show_free(code);
+ }
+}
+/* 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)
+{
+ ref_stack_enum_t rsenum;
+ uint count = 0;
+
+ ref_stack_enum_begin(&rsenum, &e_stack);
+ do {
+ es_ptr ep = rsenum.ptr;
+ uint size = rsenum.size;
+
+ for (ep += size - 1; size != 0; size--, ep--, count++)
+ if (r_is_estack_mark(ep) && estack_mark_index(ep) == es_show)
+ return count;
+ } while (ref_stack_enum_next(&rsenum));
+ 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. */
+private int
+op_show_return_width(os_ptr op, uint npop, double *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;
+}
+
+/*
+ * Restore state after finishing, or unwinding from an error within, a show
+ * operation. Note that we assume op == osp, and may reset osp.
+ */
+private int
+op_show_restore(bool for_error)
+{
+ register es_ptr ep = esp + snumpush;
+ gs_show_enum *penum = esenum(ep);
+ int saved_level = esgslevel(ep).value.intval;
+ int code = 0;
+
+ if (for_error) {
+ uint saved_count = esodepth(ep).value.intval;
+ uint count = ref_stack_count(&o_stack);
+
+ if (count > saved_count) /* if <, we're in trouble */
+ ref_stack_pop(&o_stack, count - saved_count);
+ }
+ if (SHOW_IS_STRINGWIDTH(penum)) { /* stringwidth does an extra gsave */
+ --saved_level;
+ }
+ /*
+ * We might have been inside a cshow, in which case currentfont
+ * was reset temporarily, as though we were inside a BuildChar/
+ * BuildGlyph procedure. If this is the case, set currentfont
+ * back to its normal state, the root font.
+ */
+ if (penum->fstack.depth >= 0)
+ gs_set_currentfont(igs, penum->fstack.items[0].font);
+ while (igs->level > saved_level && code >= 0) {
+ if (igs->saved == 0 || igs->saved->saved == 0) {
+ /*
+ * Bad news: we got an error inside a save inside a BuildChar or
+ * BuildGlyph. Don't attempt to recover.
+ */
+ code = gs_note_error(e_Fatal);
+ } else
+ code = gs_grestore(igs);
+ }
+ gs_show_enum_release(penum, (gs_memory_t *) imemory);
+ return code;
+}
+/* Clean up after an error. */
+private int
+op_show_cleanup(os_ptr op)
+{
+ return op_show_restore(true);
+}
+/* Clean up after termination of a show operation. */
+int
+op_show_free(int code)
+{
+ int rcode;
+
+ esp -= snumpush;
+ rcode = op_show_restore(code < 0);
+ return (rcode < 0 ? rcode : code);
+}
+
+/* Get a FontBBox parameter from a font dictionary. */
+int
+font_bbox_param(const ref * pfdict, double 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..bc34e3308
--- /dev/null
+++ b/pstoraster/zchar1.c
@@ -0,0 +1,763 @@
+/* Copyright (C) 1993, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Type 1 character display operator */
+#include "ghost.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 "iname.h"
+#include "store.h"
+
+/* Test whether a font is Type 1 compatible. */
+inline private bool
+font_is_type1_compatible(const gs_font *pfont)
+{
+ return (pfont->FontType == ft_encrypted ||
+ pfont->FontType == ft_disk_based);
+}
+
+/* ---------------- .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.
+ */
+
+/*
+ * Define the state record for this operator, which must save the metrics
+ * separately as well as the Type 1 interpreter state.
+ */
+typedef struct gs_type1exec_state_s {
+ gs_type1_state cis; /* must be first */
+ double sbw[4];
+ int /*metrics_present */ present;
+ gs_rect char_bbox;
+ /*
+ * The following elements are only used locally to make the stack clean
+ * for OtherSubrs: they don't need to be declared for the garbage
+ * collector.
+ */
+ ref save_args[6];
+ int num_args;
+} gs_type1exec_state;
+
+gs_private_st_suffix_add0(st_gs_type1exec_state, gs_type1exec_state,
+ "gs_type1exec_state", gs_type1exec_state_enum_ptrs,
+ gs_type1exec_state_reloc_ptrs, st_gs_type1_state);
+
+/* Forward references */
+private int bbox_continue(P1(os_ptr));
+private int nobbox_continue(P1(os_ptr));
+private int type1_call_OtherSubr(P3(const gs_type1exec_state *,
+ int (*)(P1(os_ptr)), const ref *));
+private int type1_continue_dispatch(P4(gs_type1exec_state *, const ref *,
+ ref *, int));
+private int op_type1_cleanup(P1(os_ptr));
+private void op_type1_free(P1(os_ptr));
+private void
+ type1_cis_get_metrics(P2(const gs_type1_state * pcis, double psbw[4]));
+private int bbox_getsbw_continue(P1(os_ptr));
+private int type1exec_bbox(P3(os_ptr, gs_type1exec_state *, gs_font *));
+private int bbox_fill(P1(os_ptr));
+private int bbox_stroke(P1(os_ptr));
+private int nobbox_finish(P2(os_ptr, gs_type1exec_state *));
+private int nobbox_fill(P1(os_ptr));
+private int nobbox_stroke(P1(os_ptr));
+
+/* <font> <code|name> <name> <charstring> .type1execchar - */
+private int
+ztype1execchar(register os_ptr op)
+{
+ gs_font *pfont;
+ int code = font_param(op - 3, &pfont);
+ gs_font_base *const pbfont = (gs_font_base *) pfont;
+ gs_font_type1 *const pfont1 = (gs_font_type1 *) pfont;
+ const gs_type1_data *pdata;
+ gs_show_enum *penum = op_show_find();
+ gs_type1exec_state cxs;
+ gs_type1_state *const pcis = &cxs.cis;
+
+ 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) <= max(pdata->lenIV, 0))
+ 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.
+ */
+ code = zchar_get_metrics(pbfont, op - 1, cxs.sbw);
+ if (code < 0)
+ return code;
+ cxs.present = code;
+ /* 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
+ ) {
+ /* The FontBBox is valid. */
+ cxs.char_bbox = pfont1->FontBBox;
+ return type1exec_bbox(op, &cxs, pfont);
+ } else {
+ /*
+ * The FontBBox is not valid. In this case,
+ * we create the path first, then do the setcachedevice.
+ * If we are oversampling (in this case, only for anti-
+ * aliasing, not just to improve quality), we have to
+ * create the path twice, since we can't know the
+ * oversampling factor until after setcachedevice.
+ */
+ const ref *opstr = op;
+ ref other_subr;
+
+ if (cxs.present == metricsSideBearingAndWidth) {
+ gs_point sbpt;
+
+ sbpt.x = cxs.sbw[0], sbpt.y = cxs.sbw[1];
+ gs_type1_set_lsb(pcis, &sbpt);
+ }
+ /* Continue interpreting. */
+ icont:
+ code = type1_continue_dispatch(&cxs, opstr, &other_subr, 4);
+ op = osp; /* OtherSubrs might change it */
+ switch (code) {
+ case 0: /* all done */
+ return nobbox_finish(op, &cxs);
+ default: /* code < 0, error */
+ return code;
+ case type1_result_callothersubr: /* unknown OtherSubr */
+ return type1_call_OtherSubr(&cxs, nobbox_continue,
+ &other_subr);
+ case type1_result_sbw: /* [h]sbw, just continue */
+ if (cxs.present != metricsSideBearingAndWidth)
+ type1_cis_get_metrics(pcis, cxs.sbw);
+ opstr = 0;
+ goto icont;
+ }
+ }
+}
+/* Do all the work for the case where we have a bounding box. */
+private int
+type1exec_bbox(os_ptr op, gs_type1exec_state * pcxs, gs_font * pfont)
+{
+ gs_type1_state *const pcis = &pcxs->cis;
+ gs_font_base *const pbfont = (gs_font_base *) pfont;
+
+ /*
+ * 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 (pcxs->present == metricsNone) {
+ /* Get the width from the CharString, */
+ /* then set the cache device. */
+ ref cnref;
+ ref other_subr;
+ int code;
+
+ /* Since an OtherSubr callout might change osp, */
+ /* save the character name now. */
+ ref_assign(&cnref, op - 1);
+ code = type1_continue_dispatch(pcxs, op, &other_subr, 4);
+ op = osp; /* OtherSubrs might change it */
+ 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(pcxs,
+ bbox_getsbw_continue,
+ &other_subr);
+ case type1_result_sbw: /* [h]sbw, done */
+ break;
+ }
+ type1_cis_get_metrics(pcis, pcxs->sbw);
+ return zchar_set_cache(op, pbfont, &cnref,
+ NULL, pcxs->sbw + 2,
+ &pcxs->char_bbox,
+ 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,
+ (pcxs->present ==
+ metricsSideBearingAndWidth ?
+ pcxs->sbw : NULL),
+ pcxs->sbw + 2,
+ &pcxs->char_bbox,
+ bbox_fill, bbox_stroke);
+ }
+}
+
+
+/* Handle the results of gs_type1_interpret. */
+/* pcref points to a t_string ref. */
+private int
+type1_continue_dispatch(gs_type1exec_state *pcxs, const ref * pcref,
+ ref *pos, int num_args)
+{
+ 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;
+ }
+ /*
+ * Since OtherSubrs may push or pop values on the PostScript operand
+ * stack, remove the arguments of .type1execchar before calling the
+ * Type 1 interpreter, and put them back afterwards unless we're
+ * about to execute an OtherSubr procedure.
+ */
+ pcxs->num_args = num_args;
+ memcpy(pcxs->save_args, osp - (num_args - 1), num_args * sizeof(ref));
+ osp -= num_args;
+ code = gs_type1_interpret(&pcxs->cis, pchars, &value);
+ switch (code) {
+ case type1_result_callothersubr: {
+ /*
+ * The Type 1 interpreter handles all known OtherSubrs,
+ * so this must be an unknown one.
+ */
+ const font_data *pfdata = pfont_data(gs_currentfont(igs));
+
+ code = array_get(&pfdata->u.type1.OtherSubrs, (long)value, pos);
+ if (code >= 0)
+ return type1_result_callothersubr;
+ }
+ }
+ /* Put back the arguments removed above. */
+ memcpy(osp + 1, pcxs->save_args, num_args * sizeof(ref));
+ osp += num_args;
+ return code;
+}
+
+/*
+ * Push a continuation, the arguments removed for the OtherSubr, and
+ * the OtherSubr procedure.
+ */
+private int
+type1_push_OtherSubr(const gs_type1exec_state *pcxs, int (*cont)(P1(os_ptr)),
+ const ref *pos)
+{
+ int i, n = pcxs->num_args;
+
+ push_op_estack(cont);
+ /*
+ * Push the saved arguments (in reverse order, so they will get put
+ * back on the operand stack in the correct order) on the e-stack.
+ */
+ for (i = n; --i >= 0; ) {
+ *++esp = pcxs->save_args[i];
+ r_clear_attrs(esp, a_executable); /* just in case */
+ }
+ ++esp;
+ *esp = *pos;
+ return o_push_estack;
+}
+
+/* Do a callout to an OtherSubr implemented in PostScript. */
+/* The caller must have done a check_estack(4 + num_args). */
+private int
+type1_call_OtherSubr(const gs_type1exec_state * pcxs, int (*cont) (P1(os_ptr)),
+ const ref * pos)
+{
+ /* Move the Type 1 interpreter state to the heap. */
+ gs_type1exec_state *hpcxs =
+ ialloc_struct(gs_type1exec_state, &st_gs_type1exec_state,
+ "type1_call_OtherSubr");
+
+ if (hpcxs == 0)
+ return_error(e_VMerror);
+ *hpcxs = *pcxs;
+ push_mark_estack(es_show, op_type1_cleanup);
+ ++esp;
+ make_istruct(esp, 0, hpcxs);
+ return type1_push_OtherSubr(pcxs, cont, pos);
+}
+
+/* Continue from an OtherSubr callout while getting metrics. */
+private int
+bbox_getsbw_continue(os_ptr op)
+{
+ ref other_subr;
+ gs_type1exec_state *pcxs = r_ptr(esp, gs_type1exec_state);
+ gs_type1_state *const pcis = &pcxs->cis;
+ int code;
+
+ code = type1_continue_dispatch(pcxs, NULL, &other_subr, 4);
+ 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 */
+ return type1_push_OtherSubr(pcxs, bbox_getsbw_continue,
+ &other_subr);
+ case type1_result_sbw: { /* [h]sbw, done */
+ double sbw[4];
+ const gs_font_base *const pbfont =
+ (const gs_font_base *)pcis->pfont;
+ gs_rect bbox;
+
+ /* Get the metrics before freeing the state. */
+ type1_cis_get_metrics(pcis, sbw);
+ bbox = pcxs->char_bbox;
+ op_type1_free(op);
+ return zchar_set_cache(op, pbfont, op, sbw, sbw + 2, &bbox,
+ bbox_fill, bbox_stroke);
+ }
+ }
+}
+
+/* <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;
+ int code;
+ gs_show_enum *penum = op_show_find();
+ gs_type1exec_state cxs; /* stack allocate to avoid sandbars */
+ gs_type1_state *const pcis = &cxs.cis;
+ double 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);
+ {
+ gs_font_type1 *const pfont1 = (gs_font_type1 *) pfont;
+ int lenIV = pfont1->data.lenIV;
+
+ if (lenIV > 0 && r_size(opc) <= 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(&cxs, opstr, &other_subr, (psbpt ? 6 : 4));
+ op = osp; /* OtherSubrs might have altered it */
+ switch (code) {
+ case 0: /* all done */
+ /* Call the continuation now. */
+ if (psbpt)
+ pop(2);
+ return (*cont)(osp);
+ case type1_result_callothersubr: /* unknown OtherSubr */
+ push_op_estack(cont); /* call later */
+ return type1_call_OtherSubr(&cxs, bbox_continue, &other_subr);
+ case type1_result_sbw: /* [h]sbw, just continue */
+ opstr = 0;
+ goto icont;
+ default: /* code < 0, error */
+ return code;
+ }
+}
+
+/* Continue from an OtherSubr callout while building the path. */
+private int
+type1_callout_dispatch(os_ptr op, int (*cont)(P1(os_ptr)), int num_args)
+{
+ ref other_subr;
+ gs_type1exec_state *pcxs = r_ptr(esp, gs_type1exec_state);
+ int code;
+
+ icont:
+ code = type1_continue_dispatch(pcxs, NULL, &other_subr, num_args);
+ op = osp; /* in case z1_push/pop_proc was called */
+ switch (code) {
+ case 0: /* callout done, cont is on e-stack */
+ 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 */
+ return type1_push_OtherSubr(pcxs, cont, &other_subr);
+ case type1_result_sbw: /* [h]sbw, just continue */
+ goto icont;
+ }
+}
+private int
+bbox_continue(os_ptr op)
+{
+ int npop = (r_has_type(op, t_string) ? 4 : 6);
+ int code = type1_callout_dispatch(op, bbox_continue, npop);
+
+ if (code == 0) {
+ op = osp; /* OtherSubrs might have altered it */
+ npop -= 4; /* nobbox_fill/stroke handles the rest */
+ pop(npop);
+ op -= npop;
+ op_type1_free(op);
+ }
+ return code;
+}
+private int
+nobbox_continue(os_ptr op)
+{
+ int code = type1_callout_dispatch(op, nobbox_continue, 4);
+
+ if (code)
+ return code;
+ {
+ gs_type1exec_state cxs;
+ gs_type1exec_state *pcxs = r_ptr(esp, gs_type1exec_state);
+
+ op = osp; /* OtherSubrs might have altered it */
+ cxs = *pcxs;
+ op_type1_free(op);
+ return nobbox_finish(op, &cxs);
+ }
+}
+
+/* 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)
+{
+ ifree_object(r_ptr(esp, void), "op_type1_free");
+ /*
+ * 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 (hpcxs 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);
+}
+
+/* Finish the no-FontBBox case after constructing the path. */
+/* If we are oversampling for anti-aliasing, we have to go around again. */
+/* <font> <code|name> <name> <charstring> %nobbox_continue - */
+private int
+nobbox_finish(os_ptr op, gs_type1exec_state * pcxs)
+{
+ int code;
+ gs_show_enum *penum = op_show_find();
+ gs_font *pfont;
+
+ if ((code = gs_pathbbox(igs, &pcxs->char_bbox)) < 0 ||
+ (code = font_param(op - 3, &pfont)) < 0
+ )
+ return code;
+ if (penum == 0 || !font_is_type1_compatible(pfont))
+ return_error(e_undefined);
+ {
+ gs_font_base *const pbfont = (gs_font_base *) pfont;
+ gs_font_type1 *const pfont1 = (gs_font_type1 *) pfont;
+
+ if (pcxs->present == metricsNone) {
+ gs_point endpt;
+
+ if ((code = gs_currentpoint(igs, &endpt)) < 0)
+ return code;
+ pcxs->sbw[2] = endpt.x, pcxs->sbw[3] = endpt.y;
+ pcxs->present = metricsSideBearingAndWidth;
+ }
+ /*
+ * We only need to rebuild the path from scratch if we might
+ * oversample for anti-aliasing.
+ */
+ if ((*dev_proc(igs->device, get_alpha_bits))(igs->device, go_text) > 1
+ ) {
+ gs_newpath(igs);
+ gs_moveto(igs, 0.0, 0.0);
+ code = gs_type1_init(&pcxs->cis, penum, NULL,
+ gs_show_in_charpath(penum) != cpm_show,
+ pfont1->PaintType, pfont1);
+ if (code < 0)
+ return code;
+ return type1exec_bbox(op, pcxs, pfont);
+ }
+ return zchar_set_cache(op, pbfont, op, NULL, pcxs->sbw + 2,
+ &pcxs->char_bbox,
+ nobbox_fill, nobbox_stroke);
+ }
+}
+/* Finish by popping the operands and filling or stroking. */
+private int
+nobbox_fill(os_ptr op)
+{
+ pop(4);
+ /*
+ * Properly designed fonts, which have no self-intersecting outlines
+ * and in which outer and inner outlines are drawn in opposite
+ * directions, aren't affected by choice of filling rule; but some
+ * badly designed fonts in the Genoa test suite seem to require
+ * using the even-odd rule to match Adobe interpreters.
+ */
+ return gs_eofill(igs);
+}
+private int
+nobbox_stroke(os_ptr op)
+{
+ pop(4);
+ 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, double 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 ------ */
+
+const op_def zchar1_op_defs[] =
+{
+ {"4.type1execchar", ztype1execchar},
+ /* Internal operators */
+ {"4%nobbox_continue", nobbox_continue},
+ {"4%nobbox_fill", nobbox_fill},
+ {"4%nobbox_stroke", nobbox_stroke},
+ {"4%bbox_getsbw_continue", bbox_getsbw_continue},
+ {"4%bbox_continue", bbox_continue},
+ {"4%bbox_fill", bbox_fill},
+ {"4%bbox_stroke", bbox_stroke},
+ op_def_end(0)
+};
+
+/* ------ Auxiliary procedures for type 1 fonts ------ */
+
+private int
+z1_charstring_data(gs_font_type1 * pfont, const ref * pgref,
+ gs_const_string * pstr)
+{
+ ref *pcstr;
+
+ if (dict_find(&pfont_data(pfont)->CharStrings, pgref, &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;
+
+}
+
+private int
+z1_glyph_data(gs_font_type1 * pfont, gs_glyph glyph, gs_const_string * pstr)
+{
+ ref gref;
+
+ if (glyph < gs_min_cid_glyph)
+ name_index_ref(glyph, &gref);
+ else
+ make_int(&gref, glyph - gs_min_cid_glyph);
+ return z1_charstring_data(pfont, &gref, pstr);
+}
+
+private int
+z1_subr_data(gs_font_type1 * pfont, int index, bool global,
+ gs_const_string * pstr)
+{
+ const font_data *pfdata = pfont_data(pfont);
+ ref subr;
+ int code;
+
+ code = array_get((global ? &pfdata->u.type1.GlobalSubrs :
+ &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;
+}
+
+private int
+z1_seac_data(gs_font_type1 * pfont, int index, gs_const_string * pstr)
+{
+ ref enc_entry;
+ int code = array_get(&StandardEncoding, (long)index, &enc_entry);
+
+ if (code < 0)
+ return code;
+ return z1_charstring_data(pfont, &enc_entry, pstr);
+}
+
+private int
+z1_next_glyph(gs_font_type1 * pfont, int *pindex, gs_glyph * pglyph)
+{
+ ref *pcsdict = &pfont_data(pfont)->CharStrings;
+ int index = *pindex - 1;
+ ref elt[2];
+
+ if (index < 0)
+ index = dict_first(pcsdict);
+next:
+ index = dict_next(pcsdict, index, elt);
+ *pindex = index + 1;
+ if (index >= 0) {
+ switch (r_type(elt)) {
+ case t_integer:
+ *pglyph = gs_min_cid_glyph + elt[0].value.intval;
+ break;
+ case t_name:
+ *pglyph = name_index(elt);
+ break;
+ default: /* can't handle it */
+ goto next;
+ }
+ }
+ return 0;
+}
+
+private int
+z1_push(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;
+}
+
+private int
+z1_pop(gs_font_type1 * ignore, fixed * pf)
+{
+ double val;
+ int code = real_param(osp, &val);
+
+ if (code < 0)
+ return code;
+ *pf = float2fixed(val);
+ osp--;
+ return 0;
+}
+
+/* Define the Type 1 procedure vector. */
+const gs_type1_data_procs_t z1_data_procs =
+{
+ z1_glyph_data, z1_subr_data, z1_seac_data, z1_next_glyph,
+ z1_push, z1_pop
+};
diff --git a/pstoraster/zchar2.c b/pstoraster/zchar2.c
new file mode 100644
index 000000000..1bb7e348e
--- /dev/null
+++ b/pstoraster/zchar2.c
@@ -0,0 +1,228 @@
+/* Copyright (C) 1992, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Level 2 character operators */
+#include "ghost.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 "gxchar.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 moveshow(P2(os_ptr, int));
+private int moveshow_continue(P2(os_ptr, int));
+
+/* <charname> glyphshow - */
+private int
+zglyphshow(os_ptr op)
+{
+ gs_glyph glyph;
+ gs_show_enum *penum;
+ int code;
+
+ switch (gs_currentfont(igs)->FontType) {
+ case ft_CID_encrypted:
+ case ft_CID_user_defined:
+ case ft_CID_TrueType:
+ case ft_CID_bitmap:
+ check_int_leu(*op, gs_max_glyph - gs_min_cid_glyph);
+ glyph = (gs_glyph) op->value.intval + gs_min_cid_glyph;
+ break;
+ default:
+ check_type(*op, t_name);
+ glyph = name_index(op);
+ }
+ if ((code = op_show_enum_setup(op, &penum)) != 0)
+ return code;
+ if ((code = gs_glyphshow_init(penum, igs, glyph)) < 0) {
+ ifree_object(penum, "op_show_glyph");
+ return code;
+ }
+ op_show_finish_setup(penum, 1, NULL);
+ return op_show_continue(op - 1);
+}
+
+/* <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. */
+ return op_show_free(code);
+}
+
+/* ------ Initialization procedure ------ */
+
+const op_def zchar2_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},
+ op_def_end(0)
+};
diff --git a/pstoraster/zchar32.c b/pstoraster/zchar32.c
new file mode 100644
index 000000000..1a2d532dd
--- /dev/null
+++ b/pstoraster/zchar32.c
@@ -0,0 +1,217 @@
+/* Copyright (C) 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Type 32 font glyph operators */
+#include "ghost.h"
+#include "oper.h"
+#include "gsccode.h" /* for gxfont.h */
+#include "gsmatrix.h"
+#include "gsutil.h"
+#include "gxfixed.h"
+#include "gxchar.h"
+#include "gxfont.h"
+#include "gxfcache.h"
+#include "ifont.h"
+#include "igstate.h"
+#include "store.h"
+
+/* ([wx wy llx lly urx ury] | [w0x w0y llx lly urx ury w1x w1y vx vy]) */
+/* <bitmap> <cid> <type32font> <str22> .makeglyph32 <<same with substr>> */
+private int
+zmakeglyph32(os_ptr op)
+{
+ bool long_form;
+ uint msize;
+ double metrics[10];
+ int wx, llx, lly, urx, ury;
+ int width, height, raster;
+ gs_font *pfont;
+ int code;
+ byte *str;
+
+ check_array(op[-4]);
+ msize = r_size(op - 4);
+ switch (msize) {
+ case 10:
+ long_form = true;
+ break;
+ case 6:
+ long_form = false;
+ break;
+ default:
+ return_error(e_rangecheck);
+ }
+ code = num_params(op[-4].value.refs + msize - 1, msize, metrics);
+ if (code < 0)
+ return code;
+ if (~code & 0x3c) /* check llx .. ury for integers */
+ return_error(e_typecheck);
+ check_read_type(op[-3], t_string);
+ llx = (int)metrics[2];
+ lly = (int)metrics[3];
+ urx = (int)metrics[4];
+ ury = (int)metrics[5];
+ width = urx - llx;
+ height = ury - lly;
+ raster = (width + 7) >> 3;
+ if (width < 0 || height < 0 || r_size(op - 3) != raster * height)
+ return_error(e_rangecheck);
+ check_int_leu(op[-2], 65535);
+ code = font_param(op - 1, &pfont);
+ if (code < 0)
+ return code;
+ if (pfont->FontType != ft_CID_bitmap)
+ return_error(e_invalidfont);
+ check_write_type(*op, t_string);
+ if (r_size(op) < 22)
+ return_error(e_rangecheck);
+ str = op->value.bytes;
+ if (long_form || metrics[0] != (wx = (int)metrics[0]) ||
+ metrics[1] != 0 || height == 0 ||
+ ((wx | width | height | (llx + 128) | (lly + 128)) & ~255) != 0
+ ) {
+ /* Use the long form. */
+ int i, n = (long_form ? 10 : 6);
+
+ str[0] = 0;
+ str[1] = long_form;
+ for (i = 0; i < n; ++i) {
+ int v = (int)metrics[i]; /* no floating point widths yet */
+
+ str[2 + 2 * i] = (byte)(v >> 8);
+ str[2 + 2 * i + 1] = (byte)v;
+ }
+ r_set_size(op, 2 + n * 2);
+ } else {
+ /* Use the short form. */
+ str[0] = (byte)width;
+ str[1] = (byte)height;
+ str[2] = (byte)wx;
+ str[3] = (byte)(llx + 128);
+ str[4] = (byte)(lly + 128);
+ r_set_size(op, 5);
+ }
+ return code;
+}
+
+/* <cid_min> <cid_max> <type32font> .removeglyphs - */
+typedef struct {
+ gs_glyph cid_min, cid_max;
+ gs_font *font;
+} font_cid_range_t;
+private bool
+select_cid_range(cached_char * cc, void *range_ptr)
+{
+ const font_cid_range_t *range = range_ptr;
+
+ return (cc->code >= range->cid_min &&
+ cc->code <= range->cid_max &&
+ cc->pair->font == range->font);
+}
+private int
+zremoveglyphs(os_ptr op)
+{
+ int code;
+ font_cid_range_t range;
+
+ check_int_leu(op[-2], 65535);
+ check_int_leu(op[-1], 65535);
+ code = font_param(op, &range.font);
+ if (code < 0)
+ return code;
+ if (range.font->FontType != ft_CID_bitmap)
+ return_error(e_invalidfont);
+ range.cid_min = gs_min_cid_glyph + op[-2].value.intval;
+ range.cid_max = gs_min_cid_glyph + op[-1].value.intval;
+ gx_purge_selected_cached_chars(range.font->dir, select_cid_range,
+ &range);
+ pop(3);
+ return 0;
+}
+
+/* <str5/14/22> .getmetrics32 <width> <height> <w0x> ... <vy> 5/14 */
+/* <str5/14/22> .getmetrics32 <width> <height> <wx> ... <ury> 22 */
+private int
+zgetmetrics32(os_ptr op)
+{
+ const byte *data;
+ uint size;
+ int i, n = 6;
+ os_ptr wop;
+
+ check_read_type(*op, t_string);
+ data = op->value.const_bytes;
+ size = r_size(op);
+ if (size < 5)
+ return_error(e_rangecheck);
+ if (data[0]) {
+ /* Short form. */
+ int llx = (int)data[3] - 128, lly = (int)data[4] - 128;
+
+ n = 6;
+ size = 5;
+ push(8);
+ make_int(op - 6, data[2]); /* wx */
+ make_int(op - 5, 0); /* wy */
+ make_int(op - 4, llx);
+ make_int(op - 3, lly);
+ make_int(op - 2, llx + data[0]); /* urx */
+ make_int(op - 1, lly + data[1]); /* ury */
+ } else {
+ if (data[1]) {
+ /* Long form, both WModes. */
+ if (size < 22)
+ return_error(e_rangecheck);
+ n = 10;
+ size = 22;
+ } else {
+ /* Long form, WMode = 0 only. */
+ if (size < 14)
+ return_error(e_rangecheck);
+ n = 6;
+ size = 14;
+ }
+ push(2 + n);
+ for (i = 0; i < n; ++i)
+ make_int(op - n + i,
+ ((int)((data[2 * i + 2] << 8) + data[2 * i + 3]) ^ 0x8000)
+ - 0x8000);
+ }
+ wop = op - n;
+ make_int(wop - 2, wop[4].value.intval - wop[2].value.intval);
+ make_int(wop - 1, wop[5].value.intval - wop[3].value.intval);
+ make_int(op, size);
+ return 0;
+}
+
+/* ------ Initialization procedure ------ */
+
+const op_def zchar32_op_defs[] =
+{
+ {"1.getmetrics32", zgetmetrics32},
+ {"4.makeglyph32", zmakeglyph32},
+ {"3.removeglyphs", zremoveglyphs},
+ op_def_end(0)
+};
diff --git a/pstoraster/zchar42.c b/pstoraster/zchar42.c
new file mode 100644
index 000000000..93a78c038
--- /dev/null
+++ b/pstoraster/zchar42.c
@@ -0,0 +1,184 @@
+/* Copyright (C) 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Type 42 character display operator */
+#include "ghost.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;
+ int code = font_param(op - 3, &pfont);
+ gs_font_base *const pbfont = (gs_font_base *) pfont;
+ gs_show_enum *penum = op_show_find();
+ int present;
+ double sbw[4];
+
+ if (code < 0)
+ return code;
+ if (penum == 0 ||
+ (pfont->FontType != ft_TrueType &&
+ pfont->FontType != ft_CID_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) {
+ float sbw42[4];
+ int i;
+
+ code = gs_type42_get_metrics((gs_font_type42 *) pfont,
+ (uint) op->value.intval, sbw42);
+ if (code < 0)
+ return code;
+ for (i = 0; i < 4; ++i)
+ sbw[i] = sbw42[i];
+ }
+ return zchar_set_cache(op, pbfont, op - 1,
+ (present == metricsSideBearingAndWidth ?
+ sbw : NULL),
+ sbw + 2, &pbfont->FontBBox,
+ type42_fill, type42_stroke);
+}
+
+/* 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;
+ int code;
+ gs_show_enum *penum = op_show_find();
+ double 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 &&
+ pfont->FontType != ft_CID_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,
+ (gs_font_type42 *) pfont);
+ if (code < 0)
+ return code;
+ pop((psbpt == 0 ? 4 : 6));
+ return (*cont)(penum->pgs);
+}
+
+/* ------ Initialization procedure ------ */
+
+const op_def zchar42_op_defs[] =
+{
+ {"4.type42execchar", ztype42execchar},
+ op_def_end(0)
+};
diff --git a/pstoraster/zcharout.c b/pstoraster/zcharout.c
new file mode 100644
index 000000000..08d9c2997
--- /dev/null
+++ b/pstoraster/zcharout.c
@@ -0,0 +1,241 @@
+/* Copyright (C) 1996, 1997 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Common code for outline (Type 1 / 4 / 42) fonts */
+#include "ghost.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,
+ double 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 double psb[2], const double 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));
+ double 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_double(penum, igs, w2) :
+ gs_setcachedevice_double(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/zcid.c b/pstoraster/zcid.c
new file mode 100644
index 000000000..1211d249e
--- /dev/null
+++ b/pstoraster/zcid.c
@@ -0,0 +1,102 @@
+/* Copyright (C) 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* CID-keyed font operators */
+#include "ghost.h"
+#include "oper.h"
+#include "gsmatrix.h"
+#include "gsccode.h"
+#include "gxfont.h"
+#include "bfont.h"
+#include "iname.h"
+#include "store.h"
+
+/* Imported from zfont42.c */
+int build_gs_TrueType_font(P5(os_ptr op, font_type ftype,
+ const char *bcstr, const char *bgstr,
+ build_font_options_t options));
+
+/* <string|name> <font_dict> .buildfont9/10 <string|name> <font> */
+/* Build a type 9 or 10 (CID-keyed) font. */
+/* Right now, we treat these like type 3 (with a BuildGlyph procedure). */
+private int
+build_gs_cid_font(os_ptr op, font_type ftype, const build_proc_refs * pbuild)
+{
+ int code;
+ gs_font_base *pfont;
+
+ check_type(*op, t_dictionary);
+ code = build_gs_simple_font(op, &pfont, ftype, &st_gs_font_base,
+ pbuild,
+ bf_Encoding_optional |
+ bf_FontBBox_required |
+ bf_UniqueID_ignored);
+ if (code < 0)
+ return code;
+ return define_gs_font((gs_font *) pfont);
+}
+private int
+zbuildfont9(os_ptr op)
+{
+ build_proc_refs build;
+ int code = build_proc_name_refs(&build, NULL, "%Type9BuildGlyph");
+
+ if (code < 0)
+ return code;
+ return build_gs_cid_font(op, ft_CID_encrypted, &build);
+}
+private int
+zbuildfont10(os_ptr op)
+{
+ build_proc_refs build;
+ int code = build_gs_font_procs(op, &build);
+
+ if (code < 0)
+ return code;
+ make_null(&build.BuildChar); /* only BuildGlyph */
+ return build_gs_cid_font(op, ft_CID_user_defined, &build);
+}
+
+/* <string|name> <font_dict> .buildfont11 <string|name> <font> */
+private int
+zbuildfont11(os_ptr op)
+{
+ return build_gs_TrueType_font(op, ft_CID_TrueType, (const char *)0,
+ "%Type11BuildGlyph",
+ bf_Encoding_optional |
+ bf_FontBBox_required |
+ bf_UniqueID_ignored |
+ bf_CharStrings_optional);
+}
+
+/* ------ Initialization procedure ------ */
+
+const op_def zcid_op_defs[] =
+{
+ {"2.buildfont9", zbuildfont9},
+ {"2.buildfont10", zbuildfont10},
+ {"2.buildfont11", zbuildfont11},
+ op_def_end(0)
+};
diff --git a/pstoraster/zcie.c b/pstoraster/zcie.c
new file mode 100644
index 000000000..f5024e17c
--- /dev/null
+++ b/pstoraster/zcie.c
@@ -0,0 +1,731 @@
+/* Copyright (C) 1992, 1995, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* CIE color operators */
+#include "math_.h"
+#include "memory_.h"
+#include "ghost.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. */
+
+/* Empty procedures */
+static const ref empty_procs[4] =
+{
+ empty_ref_data(t_array, a_readonly | a_executable),
+ empty_ref_data(t_array, a_readonly | a_executable),
+ empty_ref_data(t_array, a_readonly | a_executable),
+ empty_ref_data(t_array, a_readonly | a_executable)
+};
+
+/* Redefined CIE color space installers that load the cache. */
+extern cs_proc_install_cspace(gx_install_CIEDEFG);
+extern cs_proc_install_cspace(gx_install_CIEDEF);
+extern cs_proc_install_cspace(gx_install_CIEABC);
+extern cs_proc_install_cspace(gx_install_CIEA);
+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);
+
+/* ------ 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 *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 *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 *const prt2 = rstrings + i;
+
+ check_read_type(*prt2, t_string);
+ if (r_size(prt2) != nbytes)
+ return_error(e_rangecheck);
+ strings[i].data = prt2->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 * 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);
+ /* Delete the extra reference to the parameter tables. */
+ gs_cspace_release(pcs);
+ /* Free the top-level object, which was copied by gs_setcolorspace. */
+ gs_free_object(gs_state_memory(igs), pcs, "set_cie_finish");
+ 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 */
+}
+
+/* <dict> .setciedefgspace - */
+private int
+zsetciedefgspace(register os_ptr op)
+{
+ gs_memory_t *mem = gs_state_memory(igs);
+ gs_color_space *pcs;
+ ref_color_procs procs;
+ gs_cie_defg *pcie;
+ int code;
+ ref *ptref;
+
+ check_type(*op, t_dictionary);
+ check_dict_read(*op);
+ 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);
+ procs = istate->colorspace.procs;
+ code = gs_cspace_build_CIEDEFG(&pcs, NULL, mem);
+ if (code < 0)
+ return code;
+ pcie = pcs->params.defg;
+ pcie->common.install_cspace = cs_install_zCIEDEFG;
+ 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, (gs_cie_abc *) pcie, &procs.cie)) < 0
+ ) {
+ gs_cspace_release(pcs);
+ gs_free_object(mem, pcs, "setcolorspace(CIEBasedDEFG)");
+ return code;
+ }
+ return set_cie_finish(op, pcs, &procs.cie);
+}
+
+/* <dict> .setciedefspace - */
+private int
+zsetciedefspace(register os_ptr op)
+{
+ gs_memory_t *mem = gs_state_memory(igs);
+ gs_color_space *pcs;
+ ref_color_procs procs;
+ gs_cie_def *pcie;
+ int code;
+ ref *ptref;
+
+ check_type(*op, t_dictionary);
+ check_dict_read(*op);
+ 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);
+ procs = istate->colorspace.procs;
+ code = gs_cspace_build_CIEDEF(&pcs, NULL, mem);
+ if (code < 0)
+ return code;
+ pcie = pcs->params.def;
+ pcie->common.install_cspace = cs_install_zCIEDEF;
+ pcie->Table.n = 3;
+ 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, (gs_cie_abc *) pcie, &procs.cie)) < 0
+ ) {
+ gs_cspace_release(pcs);
+ gs_free_object(mem, pcs, "setcolorspace(CIEBasedDEF)");
+ return code;
+ }
+ return set_cie_finish(op, pcs, &procs.cie);
+}
+
+/* <dict> .setcieabcspace - */
+private int
+zsetcieabcspace(register os_ptr op)
+{
+ gs_memory_t *mem = gs_state_memory(igs);
+ gs_color_space *pcs;
+ ref_color_procs procs;
+ gs_cie_abc *pcie;
+ int code;
+
+ check_type(*op, t_dictionary);
+ check_dict_read(*op);
+ procs = istate->colorspace.procs;
+ code = gs_cspace_build_CIEABC(&pcs, NULL, mem);
+ if (code < 0)
+ return code;
+ pcie = pcs->params.abc;
+ pcie->common.install_cspace = cs_install_zCIEABC;
+ code = cie_abc_param(op, pcie, &procs.cie);
+ if (code < 0) {
+ gs_cspace_release(pcs);
+ gs_free_object(mem, pcs, "setcolorspace(CIEBasedABC)");
+ return code;
+ }
+ return set_cie_finish(op, pcs, &procs.cie);
+}
+
+/* <dict> .setcieaspace - */
+private int
+zsetcieaspace(register os_ptr op)
+{
+ gs_memory_t *mem = gs_state_memory(igs);
+ gs_color_space *pcs;
+ 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;
+ code = gs_cspace_build_CIEA(&pcs, NULL, mem);
+ if (code < 0)
+ return code;
+ pcie = pcs->params.a;
+ pcie->common.install_cspace = cs_install_zCIEA;
+ 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
+ ) {
+ gs_cspace_release(pcs);
+ gs_free_object(mem, pcs, "setcolorspace(CIEBasedA)");
+ return code;
+ }
+ pcie->DecodeA = DecodeA_default;
+ return set_cie_finish(op, pcs, &procs.cie);
+}
+
+/* ------ Install a CIE-based color space. ------ */
+
+/* Forward references */
+private int cache_common(P4(gs_cie_common *, const ref_cie_procs *,
+ void *, gs_ref_memory_t *));
+private int cache_abc_common(P4(gs_cie_abc *, const ref_cie_procs *,
+ void *, gs_ref_memory_t *));
+
+private int cie_defg_finish(P1(os_ptr));
+private int
+cs_install_zCIEDEFG(gs_color_space * pcs, gs_state * pgs)
+{
+ es_ptr ep = esp;
+ gs_cie_defg *pcie = pcs->params.defg;
+ gs_ref_memory_t *imem = (gs_ref_memory_t *) gs_state_memory(pgs);
+ const int_gstate *pigs = gs_int_gstate(pgs);
+ const ref_cie_procs *pcprocs = &pigs->colorspace.procs.cie;
+ int code = gx_install_CIEDEFG(pcs, pgs); /* base routine */
+
+ if (code < 0 ||
+ (code = cie_cache_joint(&pigs->colorrendering.procs, pgs)) < 0 || /* do this last */
+ (code = cie_cache_push_finish(cie_defg_finish, imem, pcie)) < 0 ||
+ (code = cie_prepare_cache4(&pcie->RangeDEFG,
+ pcprocs->PreDecode.DEFG.value.const_refs,
+ &pcie->caches_defg.DecodeDEFG[0],
+ pcie, imem, "Decode.DEFG")) < 0 ||
+ (code = cache_abc_common((gs_cie_abc *)pcie, pcprocs, pcie, imem)) < 0
+ ) {
+ esp = ep;
+ return code;
+ }
+ return o_push_estack;
+}
+private int
+cie_defg_finish(os_ptr op)
+{
+ gs_cie_defg_complete(r_ptr(op, gs_cie_defg));
+ pop(1);
+ return 0;
+}
+
+private int cie_def_finish(P1(os_ptr));
+private int
+cs_install_zCIEDEF(gs_color_space * pcs, gs_state * pgs)
+{
+ es_ptr ep = esp;
+ gs_cie_def *pcie = pcs->params.def;
+ gs_ref_memory_t *imem = (gs_ref_memory_t *) gs_state_memory(pgs);
+ const int_gstate *pigs = gs_int_gstate(pgs);
+ const ref_cie_procs *pcprocs = &pigs->colorspace.procs.cie;
+ int code = gx_install_CIEDEF(pcs, pgs); /* base routine */
+
+ if (code < 0 ||
+ (code = cie_cache_joint(&pigs->colorrendering.procs, pgs)) < 0 || /* do this last */
+ (code = cie_cache_push_finish(cie_def_finish, imem, pcie)) < 0 ||
+ (code = cie_prepare_cache3(&pcie->RangeDEF,
+ pcprocs->PreDecode.DEF.value.const_refs,
+ &pcie->caches_def.DecodeDEF[0],
+ pcie, imem, "Decode.DEF")) < 0 ||
+ (code = cache_abc_common((gs_cie_abc *)pcie, pcprocs, pcie, imem)) < 0
+ ) {
+ esp = ep;
+ return code;
+ }
+ return o_push_estack;
+}
+private int
+cie_def_finish(os_ptr op)
+{
+ gs_cie_def_complete(r_ptr(op, gs_cie_def));
+ pop(1);
+ return 0;
+}
+
+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;
+ gs_ref_memory_t *imem = (gs_ref_memory_t *) gs_state_memory(pgs);
+ const int_gstate *pigs = gs_int_gstate(pgs);
+ const ref_cie_procs *pcprocs = &pigs->colorspace.procs.cie;
+ int code = gx_install_CIEABC(pcs, pgs); /* base routine */
+
+ if (code < 0 ||
+ (code = cie_cache_joint(&pigs->colorrendering.procs, pgs)) < 0 || /* do this last */
+ (code = cie_cache_push_finish(cie_abc_finish, imem, pcie)) < 0 ||
+ (code = cache_abc_common(pcie, pcprocs, pcie, imem)) < 0
+ ) {
+ 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;
+ gs_ref_memory_t *imem = (gs_ref_memory_t *) gs_state_memory(pgs);
+ const int_gstate *pigs = gs_int_gstate(pgs);
+ const ref_cie_procs *pcprocs = &pigs->colorspace.procs.cie;
+ int code = gx_install_CIEA(pcs, pgs); /* base routine */
+
+ if (code < 0 ||
+ (code = cie_cache_joint(&pigs->colorrendering.procs, pgs)) < 0 || /* do this last */
+ (code = cie_cache_push_finish(cie_a_finish, imem, pcie)) < 0 ||
+ (code = cie_prepare_cache(&pcie->RangeA, &pcprocs->Decode.A, &pcie->caches.DecodeA.floats, pcie, imem, "Decode.A")) < 0 ||
+ (code = cache_common(&pcie->common, pcprocs, pcie, imem)) < 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_abc_common(gs_cie_abc * pcie, const ref_cie_procs * pcprocs,
+ void *container, gs_ref_memory_t * imem)
+{
+ int code =
+ cie_prepare_cache3(&pcie->RangeABC,
+ pcprocs->Decode.ABC.value.const_refs,
+ &pcie->caches.DecodeABC[0], pcie, imem,
+ "Decode.ABC");
+
+ return (code < 0 ? code :
+ cache_common(&pcie->common, pcprocs, pcie, imem));
+}
+
+private int
+cache_common(gs_cie_common * pcie, const ref_cie_procs * pcprocs,
+ void *container, gs_ref_memory_t * imem)
+{
+ return cie_prepare_cache3(&pcie->RangeLMN,
+ pcprocs->DecodeLMN.value.const_refs,
+ &pcie->caches.DecodeLMN[0], container, imem,
+ "Decode.LMN");
+}
+
+/* ------ Internal routines ------ */
+
+/* Prepare to cache the values for one or more procedures. */
+private int cie_cache_finish1(P1(os_ptr));
+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,
+ gs_ref_memory_t * imem, client_name_t cname)
+{
+ int space = imemory_space(imem);
+ gs_for_loop_params flp;
+ es_ptr ep;
+
+ gs_cie_cache_init(&pcache->params, &flp, domain, cname);
+ pcache->params.is_identity = r_size(proc) == 0;
+ /*
+ * If a matrix was singular, it is possible that flp.step = 0.
+ * In this case, flp.limit = flp.init as well.
+ * Execute the procedure once, and replicate the result.
+ */
+ if (flp.step == 0) {
+ check_estack(5);
+ ep = esp;
+ make_real(ep + 5, flp.init);
+ ep[4] = *proc;
+ make_op_estack(ep + 3, cie_cache_finish1);
+ esp += 5;
+ } else {
+ check_estack(9);
+ ep = esp;
+ 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);
+ esp += 9;
+ }
+ /*
+ * 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);
+ return o_push_estack;
+}
+/* Note that pc3 may be 0, indicating that there are only 3 caches to load. */
+int
+cie_prepare_caches_4(const gs_range * domains, const ref * procs,
+ cie_cache_floats * pc0, cie_cache_floats * pc1,
+ cie_cache_floats * pc2, cie_cache_floats * pc3,
+ void *container,
+ gs_ref_memory_t * imem, client_name_t cname)
+{
+ cie_cache_floats *pcn[4];
+ int i, n, code = 0;
+
+ pcn[0] = pc0, pcn[1] = pc1, pcn[2] = pc2;
+ if (pc3 == 0)
+ n = 3;
+ else
+ pcn[3] = pc3, n = 4;
+ for (i = 0; i < n && code >= 0; ++i)
+ code = cie_prepare_cache(domains + i, procs + i, pcn[i],
+ container, imem, cname);
+ return code;
+}
+
+/* Store the result of caching one procedure. */
+private int
+cie_cache_finish_store(os_ptr op, bool replicate)
+{
+ 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);
+
+ if_debug3('c', "[c]cache 0x%lx base=%g, factor=%g:\n",
+ (ulong) pcache, pcache->params.base, pcache->params.factor);
+ if (replicate ||
+ (code = float_params(op, gx_cie_cache_size, &pcache->values[0])) < 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 = float_param(ref_stack_index(&o_stack,
+ (replicate ? 0 : 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)
+ dlprintf5("[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, (replicate ? 1 : gx_cie_cache_size));
+ esp -= 2; /* pop pointer to cache */
+ return o_pop_estack;
+}
+private int
+cie_cache_finish(os_ptr op)
+{
+ return cie_cache_finish_store(op, false);
+}
+private int
+cie_cache_finish1(os_ptr op)
+{
+ return cie_cache_finish_store(op, true);
+}
+
+/* Push a finishing procedure on the e-stack. */
+/* ptr will be the top element of the o-stack. */
+int
+cie_cache_push_finish(int (*finish_proc) (P1(os_ptr)), gs_ref_memory_t * imem,
+ void *data)
+{
+ check_estack(2);
+ push_op_estack(finish_proc);
+ ++esp;
+ make_struct(esp, imemory_space(imem), data);
+ return o_push_estack;
+}
+
+/* ------ Initialization procedure ------ */
+
+const op_def zcie_l2_op_defs[] =
+{
+ op_def_begin_level2(),
+ {"1.setcieaspace", zsetcieaspace},
+ {"1.setcieabcspace", zsetcieabcspace},
+ {"1.setciedefspace", zsetciedefspace},
+ {"1.setciedefgspace", zsetciedefgspace},
+ /* Internal operators */
+ {"1%cie_defg_finish", cie_defg_finish},
+ {"1%cie_def_finish", cie_def_finish},
+ {"1%cie_abc_finish", cie_abc_finish},
+ {"1%cie_a_finish", cie_a_finish},
+ {"0%cie_cache_finish", cie_cache_finish},
+ {"1%cie_cache_finish1", cie_cache_finish1},
+ op_def_end(0)
+};
diff --git a/pstoraster/zcolor.c b/pstoraster/zcolor.c
new file mode 100644
index 000000000..27e12d1ad
--- /dev/null
+++ b/pstoraster/zcolor.c
@@ -0,0 +1,241 @@
+/* Copyright (C) 1989, 1992, 1993, 1994, 1996, 1997 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Color operators */
+#include "ghost.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 number of stack slots needed for zcolor_remap_one. */
+const int zcolor_remap_one_ostack = 4;
+const int zcolor_remap_one_estack = 3;
+
+/* - 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_floats(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;
+}
+
+/* <gray> setgray - */
+private int
+zsetgray(register os_ptr op)
+{
+ double 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)
+{
+ double 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++) {
+ double 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 ------ */
+
+const op_def zcolor_op_defs[] =
+{
+ {"0currentgray", zcurrentgray},
+ {"0currentrgbcolor", zcurrentrgbcolor},
+ {"0currenttransfer", zcurrenttransfer},
+ {"0processcolors", zprocesscolors},
+ {"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},
+ op_def_end(0)
+};
diff --git a/pstoraster/zcolor1.c b/pstoraster/zcolor1.c
new file mode 100644
index 000000000..7f6daf786
--- /dev/null
+++ b/pstoraster/zcolor1.c
@@ -0,0 +1,247 @@
+/* Copyright (C) 1993, 1994, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Level 1 extended color operators */
+#include "ghost.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_floats(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)
+{
+ double 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> */
+/* <datasrc_0> ... <datasrc_ncomp-1> true <ncomp> colorimage - */
+/* <datasrc> false <ncomp> colorimage - */
+int zimage_multiple(P2(os_ptr op, bool has_alpha));
+private int
+zcolorimage(register os_ptr op)
+{
+ return zimage_multiple(op, false);
+}
+/* We export zimage_multiple for alphaimage. */
+int
+zimage_multiple(os_ptr op, bool has_alpha)
+{
+ 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_cspace_DeviceGray((const gs_imager_state *)igs);
+ break;
+ case 3:
+ pcs = gs_cspace_DeviceRGB((const gs_imager_state *)igs);
+ goto color;
+ case 4:
+ pcs = gs_cspace_DeviceCMYK((const gs_imager_state *)igs);
+color:
+ if (op[-1].value.boolval) { /* planar format */
+ if (has_alpha)
+ ++spp;
+ npop += spp - 1;
+ procp -= spp - 1;
+ multi = true;
+ }
+ break;
+ default:
+ return_error(e_rangecheck);
+ }
+ return zimage_opaque_setup(procp, multi,
+ (has_alpha ? gs_image_alpha_last : gs_image_alpha_none),
+ pcs, npop);
+}
+
+/* ------ Initialization procedure ------ */
+
+const op_def zcolor1_op_defs[] =
+{
+ {"0currentblackgeneration", zcurrentblackgeneration},
+ {"0currentcmykcolor", zcurrentcmykcolor},
+ {"0currentcolortransfer", zcurrentcolortransfer},
+ {"0currentundercolorremoval", zcurrentundercolorremoval},
+ {"1setblackgeneration", zsetblackgeneration},
+ {"4setcmykcolor", zsetcmykcolor},
+ {"4setcolortransfer", zsetcolortransfer},
+ {"1setundercolorremoval", zsetundercolorremoval},
+ {"7colorimage", zcolorimage},
+ op_def_end(0)
+};
diff --git a/pstoraster/zcolor2.c b/pstoraster/zcolor2.c
new file mode 100644
index 000000000..a3085a9eb
--- /dev/null
+++ b/pstoraster/zcolor2.c
@@ -0,0 +1,191 @@
+/* Copyright (C) 1992, 1995, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Level 2 color operators */
+#include "ghost.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
+ * for the parameterless color spaces.
+ */
+ make_int(op, (int)(gs_currentcolorspace(igs)->type->index));
+ } else
+ *op = istate->colorspace.array;
+ 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;
+}
+
+/* ------ Initialization procedure ------ */
+
+const op_def zcolor2_l2_op_defs[] =
+{
+ op_def_begin_level2(),
+ {"0currentcolor", zcurrentcolor},
+ {"0.currentcolorspace", zcurrentcolorspace},
+ {"1setcolor", zsetcolor},
+ {"1.setcolorspace", zsetcolorspace},
+ op_def_end(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 = cs_num_components(pcs);
+
+ if (pcs->type->index == gs_color_space_index_Indexed)
+ make_int(op + 1, (int)pc->values[0]);
+ else
+ make_floats(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 = cs_num_components(pcs);
+ int code = float_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..2b6ef5d13
--- /dev/null
+++ b/pstoraster/zcontrol.c
@@ -0,0 +1,897 @@
+/*
+ Copyright 1993-2000 by Easy Software Products.
+ Copyright 1989, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Control operators */
+#include "string_.h"
+#include "ghost.h"
+#include "stream.h"
+#include "oper.h"
+#include "estack.h"
+#include "files.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 */
+private int no_cleanup(P1(os_ptr));
+private uint count_exec_stack(P1(bool));
+private uint count_to_stopped(P1(long));
+private int unmatched_exit(P2(os_ptr, op_proc_p));
+
+/* 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;
+}
+
+/* <obj1> ... <objn> <n> .execn - */
+int
+zexecn(register os_ptr op)
+{
+ uint n, i;
+ es_ptr esp_orig;
+
+ check_int_leu(*op, max_uint - 1);
+ n = (uint) op->value.intval;
+ check_op(n + 1);
+ check_estack(n);
+ esp_orig = esp;
+ for (i = 0; i < n; ++i) {
+ const ref *rp = ref_stack_index(&o_stack, (long)(i + 1));
+
+ /* Make sure this object is legal to execute. */
+ if (ref_type_uses_access(r_type(rp))) {
+ if (!r_has_attr(rp, a_execute) &&
+ r_has_attr(rp, a_executable)
+ ) {
+ esp = esp_orig;
+ return_error(e_invalidaccess);
+ }
+ }
+ /* Executable nulls have a special meaning on the e-stack, */
+ /* so since they are no-ops, don't push them. */
+ if (!r_has_type_attrs(rp, t_null, a_executable)) {
+ ++esp;
+ ref_assign(esp, rp);
+ }
+ }
+ esfile_check_cache();
+ pop(n + 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 = float_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)
+{
+ ref_stack_enum_t rsenum;
+ uint scanned = 0;
+
+ ref_stack_enum_begin(&rsenum, &e_stack);
+ do {
+ uint used = rsenum.size;
+ es_ptr ep = rsenum.ptr + used - 1;
+ uint count = used;
+
+ 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;
+ } while (ref_stack_enum_next(&rsenum));
+ /* No mark, quit. (per Adobe documentation) */
+ push(2);
+ return unmatched_exit(op, zexit);
+}
+
+/*
+ * .stopped pushes the following on the e-stack:
+ * - A mark with type = es_stopped and procedure = no_cleanup.
+ * - The result to be pushed on a normal return.
+ * - The signal mask for .stop.
+ * - The procedure %stopped_push, to handle the normal return case.
+ */
+
+/* In the normal (no-error) case, pop the mask from the e-stack, */
+/* and move the result to the o-stack. */
+private int
+stopped_push(register os_ptr op)
+{
+ push(1);
+ *op = esp[-1];
+ esp -= 3;
+ return o_pop_estack;
+}
+
+/* - stop - */
+/* Equivalent to true 1 .stop. */
+/* This is implemented in C because if were a pseudo-operator, */
+/* the stacks would get restored in case of an error. */
+private int
+zstop(register os_ptr op)
+{
+ uint count = count_to_stopped(1L);
+
+ if (count) {
+ /*
+ * If there are any t_oparrays on the e-stack, they will pop
+ * any new items from the o-stack. Wait to push the 'true'
+ * until we have run all the unwind procedures.
+ */
+ check_ostack(2);
+ pop_estack(count);
+ op = osp;
+ push(1);
+ make_true(op);
+ return o_pop_estack;
+ }
+ /* No mark, quit. (per Adobe documentation) */
+ push(2);
+ return unmatched_exit(op, zstop);
+}
+
+/* <result> <mask> .stop - */
+private int
+zzstop(register os_ptr op)
+{
+ uint count;
+
+ check_type(*op, t_integer);
+ count = count_to_stopped(op->value.intval);
+ if (count) {
+ /*
+ * If there are any t_oparrays on the e-stack, they will pop
+ * any new items from the o-stack. Wait to push the result
+ * until we have run all the unwind procedures.
+ */
+ ref save_result;
+
+ check_op(2);
+ save_result = op[-1];
+ pop(2);
+ pop_estack(count);
+ op = osp;
+ push(1);
+ *op = save_result;
+ return o_pop_estack;
+ }
+ /* No mark, quit. (per Adobe documentation) */
+ return unmatched_exit(op, zzstop);
+}
+
+/* <obj> stopped <stopped> */
+/* Equivalent to false 1 .stopped. */
+/* This is implemented in C because if were a pseudo-operator, */
+/* the stacks would get restored in case of an error. */
+private int
+zstopped(register os_ptr op)
+{
+ check_op(1);
+ /* Mark the execution stack, and push the default result */
+ /* in case control returns normally. */
+ check_estack(5);
+ push_mark_estack(es_stopped, no_cleanup);
+ ++esp;
+ make_false(esp); /* save the result */
+ ++esp;
+ make_int(esp, 1); /* save the signal mask */
+ push_op_estack(stopped_push);
+ *++esp = *op; /* execute the operand */
+ esfile_check_cache();
+ pop(1);
+ return o_push_estack;
+}
+
+/* <obj> <result> <mask> .stopped <result> */
+private int
+zzstopped(register os_ptr op)
+{
+ check_type(*op, t_integer);
+ check_op(3);
+ /* Mark the execution stack, and push the default result */
+ /* in case control returns normally. */
+ check_estack(5);
+ push_mark_estack(es_stopped, no_cleanup);
+ *++esp = op[-1]; /* save the result */
+ *++esp = *op; /* save the signal mask */
+ push_op_estack(stopped_push);
+ *++esp = op[-2]; /* execute the operand */
+ esfile_check_cache();
+ pop(3);
+ return o_push_estack;
+}
+
+/* <mask> .instopped false */
+/* <mask> .instopped <result> true */
+private int
+zinstopped(register os_ptr op)
+{
+ uint count;
+
+ check_type(*op, t_integer);
+ count = count_to_stopped(op->value.intval);
+ if (count) {
+ push(1);
+ op[-1] = *ref_stack_index(&e_stack, count - 2); /* default result */
+ make_true(op);
+ } else
+ make_false(op);
+ return 0;
+}
+
+/* <include_marks> .countexecstack <int> */
+/* - countexecstack <int> */
+/* countexecstack is an operator solely for the sake of the Genoa tests. */
+private int
+zcountexecstack(register os_ptr op)
+{
+ push(1);
+ make_int(op, count_exec_stack(false));
+ return 0;
+}
+private int
+zcountexecstack1(register os_ptr op)
+{
+ check_type(*op, t_boolean);
+ make_int(op, count_exec_stack(op->value.boolval));
+ return 0;
+}
+
+/* <array> <include_marks> .execstack <subarray> */
+/* <array> execstack <subarray> */
+/* execstack is an operator solely for the sake of the Genoa tests. */
+private int execstack_continue(P1(os_ptr));
+private int execstack2_continue(P1(os_ptr));
+private int
+push_execstack(os_ptr op1, bool include_marks, int (*cont)(P1(os_ptr)))
+{
+ uint size;
+ /*
+ * 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;
+
+ check_write_type(*op1, t_array);
+ size = r_size(op1);
+ depth = count_exec_stack(include_marks);
+ if (depth > size)
+ return_error(e_rangecheck);
+ {
+ int code = ref_stack_store_check(&e_stack, op1, size, 0);
+
+ if (code < 0)
+ return code;
+ }
+ check_estack(1);
+ r_set_size(op1, depth);
+ push_op_estack(cont);
+ return o_push_estack;
+}
+private int
+zexecstack(register os_ptr op)
+{
+ return push_execstack(op, false, execstack_continue);
+}
+private int
+zexecstack2(register os_ptr op)
+{
+ check_type(*op, t_boolean);
+ return push_execstack(op - 1, op->value.boolval, execstack2_continue);
+}
+/* Continuation operator to do the actual transfer. */
+/* r_size(op1) was set just above. */
+private int
+do_execstack(os_ptr op, bool include_marks, os_ptr op1)
+{
+ ref *arefs = op1->value.refs;
+ uint asize = r_size(op1);
+ uint i;
+ ref *rq;
+
+ /*
+ * Copy elements from the stack to the array,
+ * optionally skipping executable nulls.
+ * 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, rq = arefs + asize; rq != arefs; ++i) {
+ const ref *rp = ref_stack_index(&e_stack, (long)i);
+
+ if (r_has_type_attrs(rp, t_null, a_executable) && !include_marks)
+ continue;
+ --rq;
+ ref_assign_old(op1, rq, rp, "execstack");
+ switch (r_type(rq)) {
+ case t_operator: {
+ uint opidx = op_index(rq);
+
+ if (opidx == 0 || op_def_is_internal(op_def_table[opidx]))
+ r_clear_attrs(rq, a_executable);
+ break;
+ }
+ case t_struct:
+ case t_astruct: {
+ const char *tname =
+ gs_struct_type_name_string(
+ gs_object_type(imemory, rq->value.pstruct));
+
+ make_const_string(rq, a_readonly | avm_foreign,
+ strlen(tname), (const byte *)tname);
+ break;
+ }
+ default:
+ ;
+ }
+ }
+ pop(op - op1);
+ return 0;
+}
+private int
+execstack_continue(os_ptr op)
+{
+ return do_execstack(op, false, op);
+}
+private int
+execstack2_continue(os_ptr op)
+{
+ return do_execstack(op, op->value.boolval, op - 1);
+}
+
+/* - .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_error(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. */
+ 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)
+{
+ ref_stack_enum_t rsenum;
+
+ ref_stack_enum_begin(&rsenum, &e_stack);
+ do {
+ uint count = rsenum.size;
+ es_ptr ep = rsenum.ptr + count - 1;
+
+ for (; count; count--, ep--)
+ if (r_has_type_attrs(ep, t_file, a_executable))
+ return ep;
+ } while (ref_stack_enum_next(&rsenum));
+ return 0;
+}
+
+/* ------ Initialization procedure ------ */
+
+const op_def zcontrol_op_defs[] =
+{
+ {"1.cond", zcond},
+ {"0countexecstack", zcountexecstack},
+ {"1.countexecstack", zcountexecstack1},
+ {"0currentfile", zcurrentfile},
+ {"1exec", zexec},
+ {"1.execn", zexecn},
+ {"1execstack", zexecstack},
+ {"2.execstack", zexecstack2},
+ {"0exit", zexit},
+ {"2if", zif},
+ {"3ifelse", zifelse},
+ {"0.instopped", zinstopped},
+ {"0.needinput", zneedinput},
+ {"4for", zfor},
+ {"1loop", zloop},
+ {"2.quit", zquit},
+ {"2repeat", zrepeat},
+ {"0stop", zstop},
+ {"1.stop", zzstop},
+ {"1stopped", zstopped},
+ {"2.stopped", zzstopped},
+ /* Internal operators */
+ {"1%cond_continue", cond_continue},
+ {"1%execstack_continue", execstack_continue},
+ {"2%execstack2_continue", execstack2_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},
+ {"0%stopped_push", stopped_push},
+ {"1superexec", zsuperexec},
+ op_def_end(0)
+};
+
+/* ------ Internal routines ------ */
+
+/* Vacuous cleanup routine */
+private int
+no_cleanup(os_ptr op)
+{
+ return 0;
+}
+
+/*
+ * Count the number of elements on the exec stack, with or without
+ * the normally invisible elements (*op is a Boolean that indicates this).
+ */
+private uint
+count_exec_stack(bool include_marks)
+{
+ uint count = ref_stack_count(&e_stack);
+
+ if (!include_marks) {
+ uint i;
+
+ for (i = count; i--;)
+ if (r_has_type_attrs(ref_stack_index(&e_stack, (long)i),
+ t_null, a_executable))
+ --count;
+ }
+ return count;
+}
+
+/*
+ * Count the number of elements down to and including the first 'stopped'
+ * mark on the e-stack with a given mask. Return 0 if there is no 'stopped'
+ * mark.
+ */
+private uint
+count_to_stopped(long mask)
+{
+ ref_stack_enum_t rsenum;
+ uint scanned = 0;
+
+ ref_stack_enum_begin(&rsenum, &e_stack);
+ do {
+ uint used = rsenum.size;
+ es_ptr ep = rsenum.ptr + used - 1;
+ uint count = used;
+
+ for (; count; count--, ep--)
+ if (r_is_estack_mark(ep) &&
+ estack_mark_index(ep) == es_stopped &&
+ (ep[2].value.intval & mask) != 0
+ )
+ return scanned + (used - count + 1);
+ scanned += used;
+ } while (ref_stack_enum_next(&rsenum));
+ return 0;
+}
+
+/*
+ * Pop the e-stack, executing cleanup procedures as needed.
+ * We could make this more efficient using ref_stack_enum_*,
+ * 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);
+}
+
+/*
+ * Execute a quit in the case of an exit or stop with no appropriate
+ * enclosing control scope (loop or stopped). The caller has already
+ * ensured two free slots on the top of the o-stack.
+ */
+private int
+unmatched_exit(os_ptr op, op_proc_p opproc)
+{
+ make_oper(op - 1, 0, opproc);
+ make_int(op, e_invalidexit);
+ return_error(e_Quit);
+}
diff --git a/pstoraster/zcrd.c b/pstoraster/zcrd.c
new file mode 100644
index 000000000..aad458be9
--- /dev/null
+++ b/pstoraster/zcrd.c
@@ -0,0 +1,438 @@
+/* Copyright (C) 1995, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* CIE color rendering operators */
+#include "math_.h"
+#include "ghost.h"
+#include "oper.h"
+#include "gsstruct.h"
+#include "gscspace.h"
+#include "gscolor2.h"
+#include "gscrd.h"
+#include "estack.h"
+#include "ialloc.h"
+#include "idict.h"
+#include "idparam.h"
+#include "igstate.h"
+#include "icie.h"
+#include "ivmspace.h"
+#include "store.h" /* for make_null */
+
+/*#define TEST*/
+
+/* Forward references */
+private int zcrd1_proc_params(P2(os_ptr op, ref_cie_render_procs * pcprocs));
+private int zcrd1_params(P4(os_ptr op, gs_cie_render * pcrd,
+ ref_cie_render_procs * pcprocs, gs_memory_t * mem));
+private int cache_colorrendering1(P3(gs_cie_render * pcrd,
+ const ref_cie_render_procs * pcprocs,
+ gs_ref_memory_t * imem));
+
+/* - currentcolorrendering <dict> */
+private int
+zcurrentcolorrendering(os_ptr op)
+{
+ push(1);
+ *op = istate->colorrendering.dict;
+ return 0;
+}
+
+/* <dict> <crd> .setcolorrendering1 - */
+private int
+zsetcolorrendering1(os_ptr op)
+{
+ es_ptr ep = esp;
+ ref_cie_render_procs procs;
+ int code;
+
+ check_type(op[-1], t_dictionary);
+ check_stype(*op, st_cie_render1);
+ code = zcrd1_proc_params(op - 1, &procs);
+ if (code < 0)
+ return code;
+ code = gs_setcolorrendering(igs, r_ptr(op, gs_cie_render));
+ if (code < 0)
+ return code;
+ if (gs_cie_cs_common(igs) != 0 &&
+ (code = cie_cache_joint(&procs, igs)) < 0
+ )
+ return code;
+ istate->colorrendering.dict = op[-1];
+ istate->colorrendering.procs = procs;
+ pop(2);
+ return (esp == ep ? 0 : o_push_estack);
+}
+
+/* <dict> .buildcolorrendering1 <crd> */
+private int
+zbuildcolorrendering1(os_ptr op)
+{
+ gs_memory_t *mem = gs_state_memory(igs);
+ int code;
+ es_ptr ep = esp;
+ gs_cie_render *pcrd;
+ ref_cie_render_procs procs;
+
+ check_read_type(*op, t_dictionary);
+ check_dict_read(*op);
+ code = gs_cie_render1_build(&pcrd, mem, ".setcolorrendering1");
+ if (code < 0)
+ return code;
+ code = zcrd1_params(op, pcrd, &procs, mem);
+ if (code < 0 ||
+ (code = cache_colorrendering1(pcrd, &procs, (gs_ref_memory_t *) mem)) < 0
+ ) {
+ rc_free_struct(pcrd, ".setcolorrendering1");
+ esp = ep;
+ return code;
+ }
+ /****** FIX refct ******/
+ /*rc_decrement(pcrd, ".setcolorrendering1"); *//* build sets rc = 1 */
+ istate->colorrendering.dict = *op;
+ make_istruct_new(op, a_readonly, pcrd);
+ return (esp == ep ? 0 : o_push_estack);
+}
+/* Get ColorRenderingType 1 procedures from the PostScript dictionary. */
+private int
+zcrd1_proc_params(os_ptr op, ref_cie_render_procs * pcprocs)
+{
+ int code;
+ ref *pRT;
+
+ if ((code = dict_proc3_param(op, "EncodeLMN", &pcprocs->EncodeLMN)) < 0 ||
+ (code = dict_proc3_param(op, "EncodeABC", &pcprocs->EncodeABC)) < 0 ||
+ (code = dict_proc3_param(op, "TransformPQR", &pcprocs->TransformPQR)) < 0
+ )
+ return (code < 0 ? code : gs_note_error(e_rangecheck));
+ if (dict_find_string(op, "RenderTable", &pRT) > 0) {
+ const ref *prte;
+ int size;
+ int i;
+
+ check_read_type(*pRT, t_array);
+ size = r_size(pRT);
+ if (size < 5)
+ return_error(e_rangecheck);
+ prte = pRT->value.const_refs;
+ for (i = 5; i < size; i++)
+ check_proc_only(prte[i]);
+ make_const_array(&pcprocs->RenderTableT, a_readonly | r_space(pRT),
+ size - 5, prte + 5);
+ } else
+ make_null(&pcprocs->RenderTableT);
+ return 0;
+}
+/* Get ColorRenderingType 1 parameters from the PostScript dictionary. */
+private int
+zcrd1_params(os_ptr op, gs_cie_render * pcrd,
+ ref_cie_render_procs * pcprocs, gs_memory_t * mem)
+{
+ int code;
+ int ignore;
+ gx_color_lookup_table *const prtl = &pcrd->RenderTable.lookup;
+ ref *pRT;
+
+ if ((code = dict_int_param(op, "ColorRenderingType", 1, 1, 0, &ignore)) < 0 ||
+ (code = zcrd1_proc_params(op, pcprocs)) < 0 ||
+ (code = dict_matrix3_param(op, "MatrixLMN", &pcrd->MatrixLMN)) != matrix3_ok ||
+ (code = dict_range3_param(op, "RangeLMN", &pcrd->RangeLMN)) < 0 ||
+ (code = dict_matrix3_param(op, "MatrixABC", &pcrd->MatrixABC)) != matrix3_ok ||
+ (code = dict_range3_param(op, "RangeABC", &pcrd->RangeABC)) < 0 ||
+ (code = cie_points_param(op, &pcrd->points)) < 0 ||
+ (code = dict_matrix3_param(op, "MatrixPQR", &pcrd->MatrixPQR)) != matrix3_ok ||
+ (code = dict_range3_param(op, "RangePQR", &pcrd->RangePQR)) < 0
+ )
+ return (code < 0 ? code : gs_note_error(e_rangecheck));
+ if (dict_find_string(op, "RenderTable", &pRT) > 0) {
+ const ref *prte = pRT->value.const_refs;
+
+ /* Finish unpacking and checking the RenderTable parameter. */
+ check_type_only(prte[4], t_integer);
+ if (!(prte[4].value.intval == 3 || prte[4].value.intval == 4))
+ return_error(e_rangecheck);
+ prtl->n = 3;
+ prtl->m = prte[4].value.intval;
+ if (r_size(pRT) != prtl->m + 5)
+ return_error(e_rangecheck);
+ code = cie_table_param(pRT, prtl, mem);
+ if (code < 0)
+ return code;
+ } else {
+ prtl->table = 0;
+ }
+ pcrd->EncodeLMN = Encode_default;
+ pcrd->EncodeABC = Encode_default;
+ pcrd->TransformPQR = TransformPQR_default;
+ pcrd->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_colorrendering1(gs_cie_render * pcrd,
+ const ref_cie_render_procs * pcrprocs,
+ gs_ref_memory_t * imem)
+{
+ es_ptr ep = esp;
+ int code = gs_cie_render_init(pcrd); /* sets Domain values */
+ int i;
+
+ if (code < 0 ||
+ (code = cie_cache_push_finish(cie_cache_render_finish, imem, pcrd)) < 0 ||
+ (code = cie_prepare_cache3(&pcrd->DomainLMN, pcrprocs->EncodeLMN.value.const_refs, &pcrd->caches.EncodeLMN[0], pcrd, imem, "Encode.LMN")) < 0 ||
+ (code = cie_prepare_cache3(&pcrd->DomainABC, pcrprocs->EncodeABC.value.const_refs, &pcrd->caches.EncodeABC[0], pcrd, imem, "Encode.ABC")) < 0
+ ) {
+ esp = ep;
+ return code;
+ }
+ if (pcrd->RenderTable.lookup.table != 0) {
+ bool is_identity = true;
+
+ for (i = 0; i < pcrd->RenderTable.lookup.m; i++)
+ if (r_size(pcrprocs->RenderTableT.value.const_refs + i) != 0) {
+ is_identity = false;
+ break;
+ }
+ pcrd->caches.RenderTableT_is_identity = is_identity;
+ if (!is_identity)
+ for (i = 0; i < pcrd->RenderTable.lookup.m; i++)
+ if ((code =
+ cie_prepare_cache(Range4_default.ranges,
+ pcrprocs->RenderTableT.value.const_refs + i,
+ &pcrd->caches.RenderTableT[i].floats,
+ pcrd, imem, "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 *pcrd = r_ptr(op, gs_cie_render);
+ int code;
+
+ if (pcrd->RenderTable.lookup.table != 0 &&
+ !pcrd->caches.RenderTableT_is_identity
+ ) {
+ /* Convert the RenderTableT cache from floats to fracs. */
+ int j;
+
+ for (j = 0; j < pcrd->RenderTable.lookup.m; j++)
+ gs_cie_cache_to_fracs(&pcrd->caches.RenderTableT[j]);
+ }
+ pcrd->status = CIE_RENDER_STATUS_SAMPLED;
+ code = gs_cie_render_complete(pcrd);
+ if (code < 0)
+ return code;
+ /* Note that the cache holds the only record of the values. */
+ pcrd->EncodeLMN = EncodeLMN_from_cache;
+ pcrd->EncodeABC = EncodeABC_from_cache;
+ pop(1);
+ return 0;
+}
+
+/* ------ Internal procedures ------ */
+
+/* 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 *pcrd = 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);
+ gs_ref_memory_t *imem = (gs_ref_memory_t *) gs_state_memory(pgs);
+ ref pqr_procs;
+ uint space;
+ int code;
+ int i;
+
+ if (pcrd == 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, imem, pgs);
+ *++esp = pqr_procs;
+ space = r_space(&pqr_procs);
+ for (i = 0; i < 3; i++) {
+ ref *p = pqr_procs.value.refs + 3 + (4 + 4 * 6) * i;
+ const float *ppt = (float *)&pjc->points_sd;
+ int j;
+
+ make_array(pqr_procs.value.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(&pcrd->RangePQR,
+ pqr_procs.value.const_refs,
+ &pjc->TransformPQR[0],
+ pjc, imem, "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(os_ptr op)
+{
+ const ref *ppt = op[-1].value.const_refs;
+ uint space = r_space(op - 1);
+ int i;
+
+ check_op(3);
+ 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(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(os_ptr op)
+{
+ gs_state *pgs = r_ptr(op, gs_state);
+ int code;
+
+ ifree_ref_array(op - 1, "cie_tpqr_finish");
+ code = gs_cie_cs_complete(pgs, false);
+ pop(2);
+ return code;
+}
+
+/* ------ Operators for testing ------ */
+
+#ifdef TEST
+
+#include "gscrdp.h"
+#include "iparam.h"
+
+/* - .currentcrd <dict> */
+private int
+zcurrentcrd(os_ptr op)
+{
+ stack_param_list list;
+ int code;
+
+ stack_param_list_write(&list, &o_stack, NULL);
+ code = param_write_cie_render1((gs_param_list *) & list, "CRD",
+ gs_currentcolorrendering(igs), imemory);
+ if (code < 0)
+ return code;
+ ref_assign(osp - 1, osp);
+ pop(1);
+ return 0;
+}
+
+/* <dict> .setcrd - */
+private int
+zsetcrd(os_ptr op)
+{
+ gs_memory_t *mem = gs_state_memory(igs);
+ dict_param_list list;
+ gs_cie_render *pcrd = 0;
+ int code;
+
+ check_type(*op, t_dictionary);
+ code = dict_param_list_read(&list, op, NULL, false);
+ if (code < 0)
+ return code;
+ code = gs_cie_render1_build(&pcrd, mem, ".setcrd");
+ if (code >= 0 &&
+ (code = param_get_cie_render1(pcrd, (gs_param_list *) & list,
+ gs_currentdevice(igs))) >= 0
+ ) {
+ code = gs_setcolorrendering(igs, pcrd);
+ }
+ if (pcrd)
+ rc_decrement(pcrd, ".setcrd"); /* build sets rc = 1 */
+ iparam_list_release(&list);
+ if (code < 0)
+ return code;
+ pop(1);
+ return 0;
+}
+
+#endif
+
+/* ------ Initialization procedure ------ */
+
+const op_def zcrd_l2_op_defs[] =
+{
+ op_def_begin_level2(),
+ {"0currentcolorrendering", zcurrentcolorrendering},
+ {"2.setcolorrendering1", zsetcolorrendering1},
+ {"1.buildcolorrendering1", zbuildcolorrendering1},
+ /* 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},
+ /* Testing */
+#ifdef TEST
+ {"0.currentcrd", zcurrentcrd},
+ {"1.setcrd", zsetcrd},
+#endif
+ op_def_end(0)
+};
diff --git a/pstoraster/zcsdevn.c b/pstoraster/zcsdevn.c
new file mode 100644
index 000000000..4a4ffad3d
--- /dev/null
+++ b/pstoraster/zcsdevn.c
@@ -0,0 +1,119 @@
+/* Copyright (C) 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* DeviceN color space support */
+#include "ghost.h"
+#include "oper.h"
+#include "gxcspace.h" /* must precede gscolor2.h */
+#include "gscolor2.h"
+#include "igstate.h"
+#include "ialloc.h"
+#include "iname.h"
+
+/* Imported from gscdevn.c */
+extern const gs_color_space_type gs_color_space_type_DeviceN;
+
+/* <array> .setdevicepixelspace - */
+/* The current color space is the alternate space for the DeviceN space. */
+private int
+zsetdevicenspace(register os_ptr op)
+{
+ const ref *pcsa;
+ gs_separation_name *names;
+ uint num_components;
+ gs_color_space cs;
+ ref_colorspace cspace_old;
+ int code;
+
+ check_read_type(*op, t_array);
+ if (r_size(op) != 4)
+ return_error(e_rangecheck);
+ pcsa = op->value.const_refs + 1;
+ if (!r_is_array(pcsa))
+ return_error(e_typecheck);
+ num_components = r_size(pcsa);
+ if (num_components == 0)
+ return_error(e_rangecheck);
+ check_proc(pcsa[2]);
+ cs = *gs_currentcolorspace(igs);
+ if (!cs.type->can_be_alt_space)
+ return_error(e_rangecheck);
+ names = (gs_separation_name *)
+ ialloc_byte_array(num_components, sizeof(gs_separation_name),
+ ".setdevicenspace");
+ if (names == 0)
+ return_error(e_VMerror);
+ {
+ uint i;
+ ref sname;
+
+ for (i = 0; i < num_components; ++i) {
+ array_get(pcsa, (long)i, &sname);
+ switch (r_type(&sname)) {
+ case t_string:
+ code = name_from_string(&sname, &sname);
+ if (code < 0) {
+ ifree_object(names, ".setdevicenspace");
+ return code;
+ }
+ /* falls through */
+ case t_name:
+ names[i] = name_index(&sname);
+ break;
+ default:
+ ifree_object(names, ".setdevicenspace");
+ return_error(e_typecheck);
+ }
+ }
+ }
+ cs.params.device_n.alt_space = *(gs_base_color_space *) & cs;
+ cspace_old = istate->colorspace;
+ istate->colorspace.procs.special.device_n.layer_names = pcsa[0];
+ istate->colorspace.procs.special.device_n.tint_transform = pcsa[2];
+ cs.type = &gs_color_space_type_DeviceN;
+ cs.params.device_n.names = names;
+ cs.params.device_n.num_components = num_components;
+ cs.params.device_n.tint_transform = 0;
+/****** ? ******/
+ cs.params.device_n.tint_transform_data = 0;
+/****** ? ******/
+ code = gs_setcolorspace(igs, &cs);
+ if (code < 0) {
+ istate->colorspace = cspace_old;
+ ifree_object(names, ".setdevicenspace");
+ return code;
+ }
+ pop(1);
+ return 0;
+}
+
+/* ------ Initialization procedure ------ */
+
+const op_def zcsdevn_op_defs[] =
+{
+ op_def_begin_ll3(),
+ {"1.setdevicenspace", zsetdevicenspace},
+ op_def_end(0)
+};
diff --git a/pstoraster/zcsindex.c b/pstoraster/zcsindex.c
new file mode 100644
index 000000000..afaa03cd0
--- /dev/null
+++ b/pstoraster/zcsindex.c
@@ -0,0 +1,239 @@
+/* Copyright (C) 1993, 1995, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Indexed color space support */
+#include "memory_.h"
+#include "ghost.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));
+
+/* 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 = cs_num_components((const gs_color_space *)&params->base_space);
+ 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_direct_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_direct_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(cs_base));
+ cs.params.indexed.base_space = cs_base;
+ if (r_has_type(&pcsa[2], t_string)) {
+ int num_values = num_entries * cs_num_components(&cs);
+
+ 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 = float_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 ------ */
+
+const op_def zcsindex_l2_op_defs[] =
+{
+ op_def_begin_level2(),
+ {"1.setindexedspace", zsetindexedspace},
+ /* Internal operators */
+ {"1%indexed_map1", indexed_map1},
+ op_def_end(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 =
+ cs_num_components((const gs_color_space *)base_space);
+ 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/zcspixel.c b/pstoraster/zcspixel.c
new file mode 100644
index 000000000..7b427bb6e
--- /dev/null
+++ b/pstoraster/zcspixel.c
@@ -0,0 +1,73 @@
+/* Copyright (C) 1997 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* DevicePixel color space support */
+#include "ghost.h"
+#include "oper.h"
+#include "igstate.h"
+#include "gscspace.h"
+#include "gsmatrix.h" /* for gscolor2.h */
+#include "gscolor2.h"
+#include "gscpixel.h"
+
+/* <array> .setdevicepixelspace - */
+private int
+zsetdevicepixelspace(register os_ptr op)
+{
+ ref depth;
+ gs_color_space cs;
+ int code;
+
+ check_read_type(*op, t_array);
+ if (r_size(op) != 2)
+ return_error(e_rangecheck);
+ array_get(op, 1L, &depth);
+ check_type_only(depth, t_integer);
+ switch (depth.value.intval) {
+ case 1:
+ case 2:
+ case 4:
+ case 8:
+ case 16:
+ case 24:
+ case 32:
+ break;
+ default:
+ return_error(e_rangecheck);
+ }
+ gs_cs_init_DevicePixel(&cs, (int)depth.value.intval);
+ code = gs_setcolorspace(igs, &cs);
+ if (code >= 0)
+ pop(1);
+ return code;
+}
+
+/* ------ Initialization procedure ------ */
+
+const op_def zcspixel_op_defs[] =
+{
+ {"1.setdevicepixelspace", zsetdevicepixelspace},
+ op_def_end(0)
+};
diff --git a/pstoraster/zcssepr.c b/pstoraster/zcssepr.c
new file mode 100644
index 000000000..87f6e349b
--- /dev/null
+++ b/pstoraster/zcssepr.c
@@ -0,0 +1,186 @@
+/* Copyright (C) 1994, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Separation color space support */
+#include "ghost.h"
+#include "oper.h"
+#include "gsstruct.h"
+#include "gscolor.h"
+#include "gsmatrix.h" /* for gxcolor2.h */
+#include "gscsepr.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 = cs_num_components((const gs_color_space *)&params->alt_space);
+ 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_alt_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 = float_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;
+}
+
+/* - currentoverprint <bool> */
+private int
+zcurrentoverprint(register os_ptr op)
+{
+ push(1);
+ make_bool(op, gs_currentoverprint(igs));
+ 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 ------ */
+
+const op_def zcssepr_l2_op_defs[] =
+{
+ op_def_begin_level2(),
+ {"0currentoverprint", zcurrentoverprint},
+ {"1setoverprint", zsetoverprint},
+ {"1.setseparationspace", zsetseparationspace},
+ /* Internal operators */
+ {"1%separation_map1", separation_map1},
+ op_def_end(0)
+};
diff --git a/pstoraster/zdevcal.c b/pstoraster/zdevcal.c
new file mode 100644
index 000000000..7b96e1fc9
--- /dev/null
+++ b/pstoraster/zdevcal.c
@@ -0,0 +1,79 @@
+/* Copyright (C) 1995, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* %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);
+const 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;
+ static const gs_param_item_t items[] = {
+ {"Year", gs_param_type_int, offset_of(struct tm, tm_year)},
+ {"Month", gs_param_type_int, offset_of(struct tm, tm_mon)},
+ {"Day", gs_param_type_int, offset_of(struct tm, tm_mday)},
+ {"Weekday", gs_param_type_int, offset_of(struct tm, tm_wday)},
+ {"Hour", gs_param_type_int, offset_of(struct tm, tm_hour)},
+ {"Minute", gs_param_type_int, offset_of(struct tm, tm_min)},
+ {"Second", gs_param_type_int, offset_of(struct tm, tm_sec)},
+ gs_param_item_end
+ };
+ 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 = gs_param_write_items(plist, &ltime, NULL, items)) < 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..1ec580f7d
--- /dev/null
+++ b/pstoraster/zdevice.c
@@ -0,0 +1,445 @@
+/* Copyright (C) 1989, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Device-related operators */
+#include "string_.h"
+#include "ghost.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 "gxgetbit.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;
+}
+
+/* - 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;
+}
+
+/* <device> .devicename <string> */
+int
+zdevicename(register os_ptr op)
+{
+ const char *dname;
+
+ check_read_type(*op, t_device);
+ dname = op->value.pdevice->dname;
+ make_const_string(op, avm_foreign | a_readonly, strlen(dname),
+ (const byte *)dname);
+ return 0;
+}
+
+/* - .doneshowpage - */
+private int
+zdoneshowpage(register os_ptr op)
+{
+ gx_device *dev = gs_currentdevice(igs);
+ gx_device *tdev = (*dev_proc(dev, get_page_device)) (dev);
+
+ if (tdev != 0)
+ tdev->ShowpageCount++;
+ return 0;
+}
+
+/* - flushpage - */
+int
+zflushpage(register os_ptr op)
+{
+ return gs_flushpage(igs);
+}
+
+/* <device> <x> <y> <width> <max_height> <alpha?> <std_depth> <string> */
+/* .getbitsrect <height> <substring> */
+private int
+zgetbitsrect(register os_ptr op)
+{ /*
+ * alpha? is 0 for no alpha, -1 for alpha first, 1 for alpha last.
+ * std_depth is null for native pixels, depth/component for
+ * standard color space.
+ */
+ gx_device *dev;
+ gs_int_rect rect;
+ gs_get_bits_params_t params;
+ int w, h;
+ gs_get_bits_options_t options =
+ GB_ALIGN_ANY | GB_RETURN_COPY | GB_OFFSET_0 | GB_RASTER_STANDARD |
+ GB_PACKING_CHUNKY;
+ int std_depth;
+ uint raster;
+ int num_rows;
+ int code;
+
+ check_read_type(op[-7], t_device);
+ dev = op[-7].value.pdevice;
+ check_int_leu(op[-6], dev->width);
+ rect.p.x = op[-6].value.intval;
+ check_int_leu(op[-5], dev->height);
+ rect.p.y = op[-5].value.intval;
+ check_int_leu(op[-4], dev->width);
+ w = op[-4].value.intval;
+ check_int_leu(op[-3], dev->height);
+ h = op[-3].value.intval;
+ check_type(op[-2], t_integer);
+ switch (op[-2].value.intval) {
+ case -1:
+ options |= GB_ALPHA_FIRST;
+ break;
+ case 0:
+ options |= GB_ALPHA_NONE;
+ break;
+ case 1:
+ options |= GB_ALPHA_LAST;
+ break;
+ default:
+ return_error(e_rangecheck);
+ }
+ if (r_has_type(op - 1, t_null))
+ options |= GB_COLORS_NATIVE;
+ else {
+ static const gs_get_bits_options_t depths[17] = {
+ 0, GB_DEPTH_1, GB_DEPTH_2, 0, GB_DEPTH_4, 0, 0, 0, GB_DEPTH_8,
+ 0, 0, 0, GB_DEPTH_12, 0, 0, 0, GB_DEPTH_16
+ };
+ gs_get_bits_options_t depth_option;
+
+ check_int_leu(op[-1], 16);
+ std_depth = (int)op[-1].value.intval;
+ depth_option = depths[std_depth];
+ if (depth_option == 0)
+ return_error(e_rangecheck);
+ options |= depth_option | gb_colors_for_device(dev);
+ }
+ check_write_type(*op, t_string);
+ {
+ int depth =
+ (options & GB_COLORS_NATIVE ? dev->color_info.depth :
+ (dev->color_info.num_components +
+ (options & GB_ALPHA_NONE ? 0 : 1)) * std_depth);
+
+ raster = (w * depth + 7) >> 3;
+ }
+ num_rows = r_size(op) / raster;
+ h = min(h, num_rows);
+ if (h == 0)
+ return_error(e_rangecheck);
+ rect.q.x = rect.p.x + w;
+ rect.q.y = rect.p.y + h;
+ params.options = options;
+ params.data[0] = op->value.bytes;
+ code = (*dev_proc(dev, get_bits_rectangle))(dev, &rect, &params, NULL);
+ if (code < 0)
+ return code;
+ make_int(op - 7, h);
+ op[-6] = *op;
+ r_set_size(op - 6, h * raster);
+ pop(6);
+ return 0;
+}
+
+/* <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;
+}
+
+/* Common functionality of zgethardwareparms & zgetdeviceparams */
+private int
+zget_device_params(os_ptr op, bool is_hardware)
+{
+ 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_get_device_or_hardware_params(dev, (gs_param_list *) & list,
+ is_hardware);
+ 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;
+}
+/* <device> <key_dict|null> .getdeviceparams <mark> <name> <value> ... */
+private int
+zgetdeviceparams(os_ptr op)
+{
+ return zget_device_params(op, false);
+}
+/* <device> <key_dict|null> .gethardwareparams <mark> <name> <value> ... */
+private int
+zgethardwareparams(os_ptr op)
+{
+ return zget_device_params(op, true);
+}
+
+/* <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;
+ }
+ iparam_list_release(&list);
+ 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, code != 0); /* erase page if 1 */
+ clear_pagedevice(istate);
+ return code;
+}
+
+/* ------ Initialization procedure ------ */
+
+const op_def zdevice_op_defs[] =
+{
+ {"1copydevice", zcopydevice},
+ {"0currentdevice", zcurrentdevice},
+ {"1.devicename", zdevicename},
+ {"0.doneshowpage", zdoneshowpage},
+ {"0flushpage", zflushpage},
+ {"7.getbitsrect", zgetbitsrect},
+ {"1.getdevice", zgetdevice},
+ {"2.getdeviceparams", zgetdeviceparams},
+ {"2.gethardwareparams", zgethardwareparams},
+ {"5makewordimagedevice", zmakewordimagedevice},
+ {"0nulldevice", znulldevice},
+ {"2.outputpage", zoutputpage},
+ {"3.putdeviceparams", zputdeviceparams},
+ {"1.setdevice", zsetdevice},
+ op_def_end(0)
+};
diff --git a/pstoraster/zdevice2.c b/pstoraster/zdevice2.c
new file mode 100644
index 000000000..098541372
--- /dev/null
+++ b/pstoraster/zdevice2.c
@@ -0,0 +1,373 @@
+/* Copyright (C) 1993, 1995, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Level 2 device operators */
+#include "math_.h"
+#include "memory_.h"
+#include "ghost.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"
+
+/* Forward references */
+private int z2copy_gstate(P1(os_ptr));
+private int push_callout(P1(const char *));
+
+/* Extend the `copy' operator to deal with gstates. */
+/* This is done with a hack -- we know that gstates are the only */
+/* t_astruct subtype that implements copy. */
+private int
+z2copy(register os_ptr op)
+{
+ int code = zcopy(op);
+
+ if (code >= 0)
+ return code;
+ if (!r_has_type(op, t_astruct))
+ return code;
+ return z2copy_gstate(op);
+}
+
+/* - .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 {
+ make_null(op - 1);
+ make_false(op);
+ }
+ return 0;
+}
+
+/* <local_dict|null> .setpagedevice - */
+private int
+zsetpagedevice(register os_ptr op)
+{
+ int code;
+
+/******
+ if ( igs->in_cachedevice )
+ return_error(e_undefined);
+ ******/
+ if (r_has_type(op, t_dictionary)) {
+ check_dict_read(*op);
+#if 0 /****************/
+ /*
+ * In order to avoid invalidaccess errors on setpagedevice,
+ * the dictionary must be allocated in local VM.
+ */
+ if (!(r_is_local(op)))
+ return_error(e_invalidaccess);
+#endif /****************/
+ /* Make the dictionary read-only. */
+ code = zreadonly(op);
+ if (code < 0)
+ return code;
+ } else {
+ check_type(*op, t_null);
+ }
+ 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. */
+private bool
+save_page_device(gs_state *pgs)
+{
+ return
+ (r_has_type(&gs_int_gstate(pgs)->pagedevice, t_null) &&
+ (*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");
+}
+
+/* <gstate> currentgstate <gstate> */
+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
+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 *ppd1 = &gs_int_gstate(pgs_old)->pagedevice;
+ const ref *ppd2 = &gs_int_gstate(pgs_new)->pagedevice;
+
+ return (r_type(ppd1) != r_type(ppd2) ||
+ (r_has_type(ppd1, t_dictionary) &&
+ ppd1->value.pdict != ppd2->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))) {
+ bool done = !gs_state_saved(gs_state_saved(igs));
+
+ gs_grestore(igs);
+ if (done)
+ 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 ------ */
+
+const op_def 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: */
+ {"1copy", z2copy}, /* zdps1.c */
+ {"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},
+ op_def_end(0)
+};
+
+/* ------ Internal routines ------ */
+
+/* Call out to a PostScript procedure. */
+private int
+push_callout(const char *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..fe7685a6e
--- /dev/null
+++ b/pstoraster/zdict.c
@@ -0,0 +1,515 @@
+/* Copyright (C) 1989, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Dictionary operators */
+#include "ghost.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);
+#if arch_sizeof_int < arch_sizeof_long
+ check_int_leu(*op, max_uint);
+#else
+ if (op->value.intval < 0)
+ return_error(e_rangecheck);
+#endif
+ 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)) {
+ check_dict_write(*dsp);
+ /*
+ * If the dictionary is writable, the problem must be
+ * an invalid store.
+ */
+ return_error(e_invalidaccess);
+ }
+ /*
+ * 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)
+{
+ ref_stack_enum_t rsenum;
+
+ check_op(1);
+ ref_stack_enum_begin(&rsenum, &d_stack);
+ do {
+ const ref *const bot = rsenum.ptr;
+ const ref *pdref = bot + rsenum.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;
+ }
+ }
+ } while (ref_stack_enum_next(&rsenum));
+ 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);
+ 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 or the save level is 0) 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 ||
+ !ialloc_is_in_save()
+ ) {
+ 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> .forceundef - */
+/*
+ * This forces an "undef" even if the dictionary is not writable.
+ * Like .forceput, it is meant to be used only in a few special situations,
+ * and should not be accessible by name after initialization.
+ */
+private int
+zforceundef(register os_ptr op)
+{
+ check_type(op[-1], t_dictionary);
+ /* Don't check_dict_write */
+ dict_undef(op - 1, op); /* ignore undefined error */
+ pop(2);
+ 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)
+{
+ os_ptr op1 = op - 1;
+ uint new_size;
+ int code;
+
+ check_type(*op1, t_dictionary);
+ check_dict_write(*op1);
+ check_type(*op, t_integer);
+#if arch_sizeof_int < arch_sizeof_long
+ check_int_leu(*op, max_uint);
+#else
+ if (op->value.intval < 0)
+ return_error(e_rangecheck);
+#endif
+ 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 ------ */
+
+const op_def 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.forceundef", zforceundef},
+ {"2.knownget", zknownget},
+ {"1.knownundef", zknownundef},
+ {"2.setmaxlength", zsetmaxlength},
+ op_def_end(0)
+};
diff --git a/pstoraster/zdps1.c b/pstoraster/zdps1.c
new file mode 100644
index 000000000..c3e891589
--- /dev/null
+++ b/pstoraster/zdps1.c
@@ -0,0 +1,459 @@
+/* Copyright (C) 1990, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Level 2 / Display PostScript graphics extensions */
+#include "ghost.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"
+
+/* Forward references */
+private int gstate_unshare(P1(os_ptr));
+
+/* Structure descriptors */
+public_st_igstate_obj();
+
+/* Extend the `copy' operator to deal with gstates. */
+/* This is done with a hack -- we know that gstates are the only */
+/* t_astruct subtype that implements copy. */
+private int
+z1copy(register os_ptr op)
+{
+ int code = zcopy(op);
+
+ if (code >= 0)
+ return code;
+ if (!r_has_type(op, t_astruct))
+ return code;
+ return zcopy_gstate(op);
+}
+
+/* ------ 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;
+}
+
+/* ------ 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)
+{
+ int code = gstate_check_space(istate, icurrent_space);
+ igstate_obj *pigo;
+ gs_state *pnew;
+ int_gstate *isp;
+
+ if (code < 0)
+ return code;
+ pigo = ialloc_struct(igstate_obj, &st_igstate_obj, "gstate");
+ if (pigo == 0)
+ return_error(e_VMerror);
+ pnew = gs_state_copy(igs, imemory);
+ 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_RECTS 5
+typedef struct local_rects_s {
+ gs_rect *pr;
+ uint count;
+ gs_rect rl[MAX_LOCAL_RECTS];
+} local_rects_t;
+
+/* Forward references */
+private int rect_get(P2(local_rects_t *, os_ptr));
+private void rect_release(P1(local_rects_t *));
+
+/* <x> <y> <width> <height> .rectappend - */
+/* <numarray|numstring> .rectappend - */
+private int
+zrectappend(os_ptr op)
+{
+ local_rects_t 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_t 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_t 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_t 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_t * plr, os_ptr op)
+{
+ int format, code;
+ uint n, count;
+ gs_rect *pr;
+ double rv[4];
+
+ 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;
+ break;
+ default: /* better be 4 numbers */
+ code = num_params(op, 4, rv);
+ if (code < 0)
+ return code;
+ plr->pr = plr->rl;
+ plr->count = 1;
+ plr->rl[0].q.x = (plr->rl[0].p.x = rv[0]) + rv[2];
+ plr->rl[0].q.y = (plr->rl[0].p.y = rv[1]) + rv[3];
+ return 4;
+ }
+ plr->count = count;
+ if (count <= MAX_LOCAL_RECTS)
+ 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;
+ int i;
+
+ for (i = 0; i < 4; i++) {
+ code = num_array_get((const ref *)op, 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 1;
+}
+
+/* Release the rectangle list if needed. */
+private void
+rect_release(local_rects_t * 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)
+{
+ double 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 ------ */
+
+const op_def zdps1_l2_op_defs[] =
+{
+ op_def_begin_level2(),
+ /* Graphics state */
+ {"0currentstrokeadjust", zcurrentstrokeadjust},
+ {"1setstrokeadjust", zsetstrokeadjust},
+ /* Graphics state objects */
+ {"1copy", z1copy},
+ {"1currentgstate", zcurrentgstate},
+ {"0gstate", zgstate},
+ {"1setgstate", zsetgstate},
+ /* Rectangles */
+ {"1.rectappend", zrectappend},
+ {"1rectclip", zrectclip},
+ {"1rectfill", zrectfill},
+ {"1rectstroke", zrectstroke},
+ /* Graphics state components */
+ {"4setbbox", zsetbbox},
+ op_def_end(0)
+};
+
+/* ------ 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..61f9384c7
--- /dev/null
+++ b/pstoraster/zfbcp.c
@@ -0,0 +1,99 @@
+/* Copyright (C) 1994, 1996, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* (T)BCP filter creation */
+#include "memory_.h"
+#include "ghost.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> BCPEncode/filter <file> */
+private int
+zBCPE(os_ptr op)
+{
+ return filter_write_simple(op, &s_BCPE_template);
+}
+
+/* <target> BCPDecode/filter <file> */
+/* <target> <dict> 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, 0, &s_BCPD_template, (stream_state *) & state, 0);
+}
+
+/* <source> TBCPEncode/filter <file> */
+/* <source> <dict> TBCPEncode/filter <file> */
+private int
+zTBCPE(os_ptr op)
+{
+ return filter_write_simple(op, &s_TBCPE_template);
+}
+
+/* <target> TBCPDecode/filter <file> */
+/* <target> <dict> 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, 0, &s_TBCPD_template, (stream_state *)&state, 0);
+}
+
+/* ------ Initialization procedure ------ */
+
+const op_def zfbcp_op_defs[] =
+{
+ op_def_begin_filter(),
+ {"1BCPEncode", zBCPE},
+ {"1BCPDecode", zBCPD},
+ {"1TBCPEncode", zTBCPE},
+ {"1TBCPDecode", zTBCPD},
+ op_def_end(0)
+};
diff --git a/pstoraster/zfcmap.c b/pstoraster/zfcmap.c
new file mode 100644
index 000000000..479881e22
--- /dev/null
+++ b/pstoraster/zfcmap.c
@@ -0,0 +1,355 @@
+/* Copyright (C) 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* CMap creation operator */
+#include "ghost.h"
+#include "oper.h"
+#include "gsmatrix.h" /* for gxfont.h */
+#include "gsstruct.h"
+#include "gsutil.h" /* for bytes_compare */
+#include "gxfcmap.h"
+#include "gxfont.h"
+#include "ialloc.h"
+#include "idict.h"
+#include "idparam.h"
+#include "ifont.h" /* for zfont_mark_glyph_name */
+#include "iname.h"
+#include "store.h"
+
+/* ---------------- Internal procedures ---------------- */
+
+/* Free a code map tree in case of memory overflow. */
+private void
+free_code_map(gx_code_map * pcmap, gs_memory_t * mem)
+{
+ if (pcmap->type == cmap_subtree) {
+ int i;
+
+ for (i = pcmap->byte_data.count1; i >= 0; --i)
+ free_code_map(&pcmap->data.subtree[i], mem);
+ gs_free_object(mem, pcmap->data.subtree, "free_code_map");
+ }
+}
+
+/* Convert a code map to internal form. */
+private int
+acquire_code_map(gx_code_map * pcmap, const ref * pref, int depth,
+ gs_cmap * root, gs_memory_t * mem)
+{
+ pcmap->add_offset = 0;
+ pcmap->cmap = root;
+ pcmap->byte_data.font_index = 0;
+ switch (r_type(pref)) {
+ case t_null:
+ pcmap->type = cmap_glyph;
+ pcmap->data.glyph = gs_no_glyph;
+ return 0;
+ case t_name:
+ pcmap->type = cmap_glyph;
+ pcmap->data.glyph = name_index(pref);
+ return 0;
+ case t_integer:
+ if (pref->value.intval < 0 ||
+ pref->value.intval > gs_max_glyph - gs_min_cid_glyph
+ )
+ break;
+ pcmap->type = cmap_glyph;
+ pcmap->data.glyph = pref->value.intval + gs_min_cid_glyph;
+ return 0;
+ case t_string:
+ if (r_size(pref) < 1 || r_size(pref) > 4)
+ break;
+ pcmap->type = cmap_char_code;
+ pcmap->num_bytes1 = r_size(pref) - 1;
+ {
+ int i;
+ gs_char chr = 0;
+
+ for (i = 0; i < r_size(pref); ++i)
+ chr = (chr << 8) + pref->value.const_bytes[i];
+ pcmap->data.ccode = chr;
+ }
+ return 0;
+ default:
+ if (!r_is_array(pref) || r_size(pref) < 1 || r_size(pref) > 256)
+ break;
+ if (depth >= 4)
+ return_error(e_limitcheck);
+ {
+ uint size = r_size(pref);
+ uint count = 0;
+ ref_type rtype;
+ long prev_value;
+ long diff;
+ uint run_length;
+ ref rsub;
+ gx_code_map *subtree;
+ uint i, j;
+
+ /* Do a first pass to count non-null entries and find runs. */
+
+ for (rtype = t_null, i = 0; i < size; ++i) {
+ ref_type prev_type = rtype;
+
+ array_get(pref, (long)i, &rsub);
+ rtype = r_type(&rsub);
+ switch (rtype) {
+ case t_null:
+ continue;
+ case t_integer:
+ if (prev_type == t_integer) {
+ if (run_length == 1) {
+ diff = rsub.value.intval - prev_value;
+ if (!(diff & ~1L)) {
+ prev_value = rsub.value.intval;
+ run_length = 2;
+ continue;
+ }
+ } else if (rsub.value.intval - prev_value == diff) {
+ prev_value = rsub.value.intval;
+ ++run_length;
+ continue;
+ }
+ }
+ prev_value = rsub.value.intval;
+ run_length = 1;
+ /* falls through */
+ default:
+ ++count;
+ }
+ }
+
+ if (count == 0) /* all nulls */
+ count = 1;
+ subtree =
+ gs_alloc_struct_array(mem, count, gx_code_map,
+ &st_code_map_element,
+ "acquire_code_map");
+ if (subtree == 0)
+ return_error(e_VMerror);
+ pcmap->type = cmap_subtree;
+ pcmap->data.subtree = subtree;
+ /* Initialize a single undefined entry, in case count = 0 */
+ /* or we have to bail out with j = 0. */
+ subtree->first = subtree->last = 255;
+ subtree->type = cmap_glyph;
+ subtree->byte_data.font_index = 0;
+ subtree->data.glyph = gs_no_glyph;
+
+ /* Do the second pass to construct the tree. */
+
+ for (rtype = t_null, i = j = 0; i < size; ++i) {
+ ref_type prev_type = rtype;
+ gx_code_map *submap = &subtree[j];
+ int code;
+
+ array_get(pref, (long)i, &rsub);
+ rtype = r_type(&rsub);
+ switch (rtype) {
+ case t_null:
+ continue;
+ case t_integer:
+ if (prev_type == t_integer) {
+ if (submap[-1].first == submap[-1].last) {
+ diff = rsub.value.intval - prev_value;
+ if (!(diff & ~1L)) {
+ prev_value = rsub.value.intval;
+ submap[-1].add_offset = (uint)diff;
+ submap[-1].last++;
+ continue;
+ }
+ } else if (rsub.value.intval - prev_value == diff) {
+ prev_value = rsub.value.intval;
+ submap[-1].last++;
+ continue;
+ }
+ }
+ prev_value = rsub.value.intval;
+ /* falls through */
+ default:
+ code = acquire_code_map(submap, &rsub, depth + 1,
+ root, mem);
+ if (code < 0) { /* Release allocated elements. */
+ pcmap->byte_data.count1 = (j ? j - 1 : 0);
+ free_code_map(pcmap, mem);
+ return code;
+ }
+ submap->first = submap->last = (byte)i;
+ ++j;
+ }
+ }
+ pcmap->byte_data.count1 = count - 1;
+ }
+ return 0;
+ }
+ return_error(e_rangecheck);
+}
+
+/* Acquire CIDSystemInfo. If missing, set Registry and Ordering to */
+/* empty strings and Supplement to 0, and return 1. */
+/* Note that this currently does not handle the array format. */
+private int
+acquire_cid_system_info(gs_cid_system_info * pcidsi, const ref * op)
+{
+ ref *prcidsi;
+ ref *pregistry;
+ ref *pordering;
+
+ if (dict_find_string(op, "CIDSystemInfo", &prcidsi) <= 0) {
+ pcidsi->Registry.data = 0, pcidsi->Registry.size = 0;
+ pcidsi->Ordering.data = 0, pcidsi->Ordering.size = 0;
+ pcidsi->Supplement = 0;
+ return 1;
+ }
+ if (!r_has_type(prcidsi, t_dictionary))
+ return_error(e_typecheck);
+ if (dict_find_string(prcidsi, "Registry", &pregistry) <= 0 ||
+ dict_find_string(prcidsi, "Ordering", &pordering) <= 0
+ )
+ return_error(e_rangecheck);
+ check_read_type_only(*pregistry, t_string);
+ check_read_type_only(*pordering, t_string);
+ pcidsi->Registry.data = pregistry->value.const_bytes;
+ pcidsi->Registry.size = r_size(pregistry);
+ pcidsi->Ordering.data = pordering->value.const_bytes;
+ pcidsi->Ordering.size = r_size(pordering);
+ return dict_int_param(prcidsi, "Supplement", 0, max_int, -1,
+ &pcidsi->Supplement);
+}
+
+/* Check compatibility of CIDSystemInfo. */
+private bool
+bytes_eq(const gs_const_string *pcs1, const gs_const_string *pcs2)
+{
+ return !bytes_compare(pcs1->data, pcs1->size,
+ pcs2->data, pcs2->size);
+}
+private bool
+cid_system_info_compatible(const gs_cid_system_info * psi1,
+ const gs_cid_system_info * psi2)
+{
+ return bytes_eq(&psi1->Registry, &psi2->Registry) &&
+ bytes_eq(&psi1->Ordering, &psi2->Ordering);
+}
+
+/* ---------------- (Semi-)public procedures ---------------- */
+
+/* Get the CodeMap from a Type 0 font, and check the CIDSystemInfo of */
+/* its subsidiary fonts. */
+int
+ztype0_get_cmap(const gs_cmap ** ppcmap, const ref * pfdepvector, const ref * op)
+{
+ ref *prcmap;
+ ref *pcodemap;
+ const gs_cmap *pcmap;
+ int code;
+ ref rfdep;
+ gs_cid_system_info cidsi;
+
+ if (dict_find_string(op, "CMap", &prcmap) <= 0 ||
+ !r_has_type(prcmap, t_dictionary) ||
+ dict_find_string(prcmap, "CodeMap", &pcodemap) <= 0 ||
+ !r_has_stype(pcodemap, imemory, st_cmap)
+ )
+ return_error(e_invalidfont);
+ pcmap = r_ptr(pcodemap, gs_cmap);
+ /* Currently we only handle 1-element fonts. */
+ if (r_size(pfdepvector) != 1)
+ return_error(e_rangecheck);
+ array_get(pfdepvector, 0L, &rfdep);
+ code = acquire_cid_system_info(&cidsi, &rfdep);
+ if (code < 0)
+ return code;
+ if (code == 0 &&
+ !cid_system_info_compatible(&cidsi, &pcmap->CIDSystemInfo)
+ )
+ return_error(e_rangecheck);
+ *ppcmap = pcmap;
+ return 0;
+}
+
+/* ---------------- Operators ---------------- */
+
+/* <CMap> .buildcmap <CMap> */
+/*
+ * Create the internal form of a CMap. The initial CMap must be read-write
+ * and have an entry with key = CodeMap and value = null; the result is
+ * read-only and has a real CodeMap.
+ */
+private int
+zbuildcmap(os_ptr op)
+{
+ int code;
+ ref *pcodemaps;
+ ref *pcodemap;
+ gs_cmap *pcmap;
+ ref rdef, rnotdef, rcmap;
+
+ check_type(*op, t_dictionary);
+ check_dict_write(*op);
+ pcmap = ialloc_struct(gs_cmap, &st_cmap, "zbuildcmap(cmap)");
+ if (pcmap == 0) {
+ code = gs_note_error(e_VMerror);
+ goto fail;
+ }
+ if ((code = dict_uid_param(op, &pcmap->uid, 0, imemory)) < 0 ||
+ (code = dict_int_param(op, "WMode", 0, 1, 0, &pcmap->WMode)) < 0
+ )
+ goto fail;
+ if (dict_find_string(op, ".CodeMaps", &pcodemaps) <= 0 ||
+ !r_has_type(pcodemaps, t_array) ||
+ r_size(pcodemaps) != 2 ||
+ dict_find_string(op, "CodeMap", &pcodemap) <= 0 ||
+ !r_has_type(pcodemap, t_null)
+ ) {
+ code = gs_note_error(e_rangecheck);
+ goto fail;
+ }
+ if ((code = acquire_cid_system_info(&pcmap->CIDSystemInfo, op)) < 0 ||
+ (array_get(pcodemaps, 0L, &rdef),
+ (code = acquire_code_map(&pcmap->def, &rdef, 0, pcmap, imemory)) < 0) ||
+ (array_get(pcodemaps, 1L, &rnotdef),
+ (code = acquire_code_map(&pcmap->notdef, &rnotdef, 0, pcmap, imemory)) < 0)
+ )
+ goto fail;
+ pcmap->mark_glyph = zfont_mark_glyph_name;
+ pcmap->mark_glyph_data = 0;
+ make_istruct_new(&rcmap, a_readonly, pcmap);
+ code = dict_put_string(op, "CodeMap", &rcmap);
+ if (code < 0)
+ goto fail;
+ return zreadonly(op);
+fail:
+ ifree_object(pcmap, "zbuildcmap(cmap)");
+ return code;
+}
+
+/* ------ Initialization procedure ------ */
+
+const op_def zfcmap_op_defs[] =
+{
+ {"1.buildcmap", zbuildcmap},
+ op_def_end(0)
+};
diff --git a/pstoraster/zfdctd.c b/pstoraster/zfdctd.c
new file mode 100644
index 000000000..80f19b80a
--- /dev/null
+++ b/pstoraster/zfdctd.c
@@ -0,0 +1,110 @@
+/*
+ Copyright 1993-2000 by Easy Software Products.
+ Copyright 1994, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+#include <config.h>
+#ifdef HAVE_LIBJPEG
+
+/*$Id$ */
+/* DCTDecode filter creation */
+#include "memory_.h"
+#include "stdio_.h" /* for jpeglib.h */
+#include "jpeglib.h"
+#include "ghost.h"
+#include "oper.h"
+#include "gsmalloc.h" /* for gs_memory_default */
+#include "strimpl.h"
+#include "sdct.h"
+#include "sjpeg.h"
+#include "ialloc.h"
+#include "ifilter.h"
+#include "iparam.h"
+
+/* Import the parameter processing procedure from sddparam.c */
+stream_state_proc_put_params(s_DCTD_put_params, stream_DCT_state);
+
+/* <source> <dict> DCTDecode/filter <file> */
+/* <source> DCTDecode/filter <file> */
+private int
+zDCTD(os_ptr op)
+{
+ gs_memory_t *mem = &gs_memory_default;
+ stream_DCT_state state;
+ dict_param_list list;
+ jpeg_decompress_data *jddp;
+ int code;
+ int npop;
+ const ref *dop;
+ uint dspace;
+
+ /* First allocate space for IJG parameters. */
+ jddp = (jpeg_decompress_data *)
+ gs_alloc_bytes_immovable(mem, sizeof(*jddp), "zDCTD");
+ if (jddp == 0)
+ return_error(e_VMerror);
+ if (s_DCTD_template.set_defaults)
+ (*s_DCTD_template.set_defaults) ((stream_state *) & state);
+ state.data.decompress = jddp;
+ jddp->memory = state.jpeg_memory = mem; /* set now for allocation */
+ jddp->scanline_buffer = NULL; /* set this early for safe error exit */
+ state.report_error = filter_report_error; /* in case create fails */
+ if ((code = gs_jpeg_create_decompress(&state)) < 0)
+ goto fail; /* correct to do jpeg_destroy here */
+ /* Read parameters from dictionary */
+ if (r_has_type(op, t_dictionary))
+ npop = 1, dop = op, dspace = r_space(op);
+ else
+ npop = 0, dop = 0, dspace = 0;
+ if ((code = dict_param_list_read(&list, dop, NULL, false)) < 0)
+ goto fail;
+ if ((code = s_DCTD_put_params((gs_param_list *) & list, &state)) < 0)
+ goto rel;
+ /* 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.
+ */
+rel:
+ iparam_list_release(&list);
+fail:
+ gs_jpeg_destroy(&state);
+ gs_free_object(mem, jddp, "zDCTD fail");
+ return code;
+}
+
+/* ------ Initialization procedure ------ */
+
+const op_def zfdctd_op_defs[] =
+{
+#endif /* HAVE_LIBJPEG */
+ op_def_begin_filter(),
+ {"2DCTDecode", zDCTD},
+ op_def_end(0)
+};
diff --git a/pstoraster/zfdcte.c b/pstoraster/zfdcte.c
new file mode 100644
index 000000000..14e8f9184
--- /dev/null
+++ b/pstoraster/zfdcte.c
@@ -0,0 +1,157 @@
+/*
+ Copyright 1993-2000 by Easy Software Products.
+ Copyright 1994, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+#include <config.h>
+#ifdef HAVE_LIBJPEG
+
+/*$Id$ */
+/* DCTEncode filter creation */
+#include "memory_.h"
+#include "stdio_.h" /* for jpeglib.h */
+#include "jpeglib.h"
+#include "ghost.h"
+#include "oper.h"
+#include "gsmalloc.h" /* for gs_memory_default */
+#include "ialloc.h"
+#include "idict.h"
+#include "idparam.h"
+#include "strimpl.h"
+#include "sdct.h"
+#include "sjpeg.h"
+#include "ifilter.h"
+#include "iparam.h"
+
+/*#define TEST*/
+
+/* Import the parameter processing procedure from sdeparam.c */
+stream_state_proc_put_params(s_DCTE_put_params, stream_DCT_state);
+#ifdef TEST
+stream_state_proc_get_params(s_DCTE_get_params, stream_DCT_state);
+#endif
+
+/* <target> <dict> DCTEncode/filter <file> */
+private int
+zDCTE(os_ptr op)
+{
+ gs_memory_t *mem = &gs_memory_default;
+ stream_DCT_state state;
+ dict_param_list list;
+ jpeg_compress_data *jcdp;
+ int code;
+ int npop;
+ const ref *dop;
+ uint dspace;
+
+ /* First allocate space for IJG parameters. */
+ jcdp = (jpeg_compress_data *)
+ gs_alloc_bytes_immovable(mem, sizeof(*jcdp), "zDCTE");
+ if (jcdp == 0)
+ return_error(e_VMerror);
+ if (s_DCTE_template.set_defaults)
+ (*s_DCTE_template.set_defaults) ((stream_state *) & state);
+ state.data.compress = jcdp;
+ jcdp->memory = state.jpeg_memory = mem; /* set now for allocation */
+ state.report_error = filter_report_error; /* in case create fails */
+ if ((code = gs_jpeg_create_compress(&state)) < 0)
+ goto fail; /* correct to do jpeg_destroy here */
+ /* Read parameters from dictionary */
+ if (r_has_type(op, t_dictionary))
+ npop = 1, dop = op, dspace = r_space(op);
+ else
+ npop = 0, dop = 0, dspace = 0;
+ if ((code = dict_param_list_read(&list, dop, NULL, false)) < 0)
+ goto fail;
+ if ((code = s_DCTE_put_params((gs_param_list *) & list, &state)) < 0)
+ goto rel;
+ /* 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.
+ */
+rel:
+ iparam_list_release(&list);
+fail:
+ gs_jpeg_destroy(&state);
+ gs_free_object(mem, jcdp, "zDCTE fail");
+ return code;
+}
+
+#ifdef TEST
+#include "stream.h"
+#include "files.h"
+/* <dict> <filter> <bool> .dcteparams <dict> */
+private int
+zdcteparams(os_ptr op)
+{
+ stream *s;
+ dict_param_list list;
+ int code;
+
+ check_type(*op, t_boolean);
+ check_write_file(s, op - 1);
+ check_type(op[-2], t_dictionary);
+ /* The DCT filters copy the template.... */
+ if (s->state->template->process != s_DCTE_template.process)
+ return_error(e_rangecheck);
+ code = dict_param_list_write(&list, op - 2, NULL);
+ if (code < 0)
+ return code;
+ code = s_DCTE_get_params((gs_param_list *) & list,
+ (stream_DCT_state *) s->state,
+ op->value.boolval);
+ iparam_list_release(&list);
+ if (code >= 0)
+ pop(2);
+ return code;
+}
+#endif
+
+/* ------ Initialization procedure ------ */
+
+const op_def zfdcte_op_defs[] =
+{
+#ifdef TEST
+ {"3.dcteparams", zdcteparams},
+#endif
+ op_def_begin_filter(),
+ {"2DCTEncode", zDCTE},
+ op_def_end(0)
+};
+
+#endif /* HAVE_LIBJPEG */
diff --git a/pstoraster/zfdecode.c b/pstoraster/zfdecode.c
new file mode 100644
index 000000000..5baed631c
--- /dev/null
+++ b/pstoraster/zfdecode.c
@@ -0,0 +1,363 @@
+/* Copyright (C) 1994, 1996, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Additional decoding filter creation */
+#include "memory_.h"
+#include "ghost.h"
+#include "oper.h"
+#include "gsparam.h"
+#include "gsstruct.h"
+#include "ialloc.h"
+#include "idict.h"
+#include "idparam.h"
+#include "ilevel.h" /* for LL3 test */
+#include "iparam.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 *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> ASCII85Encode/filter <file> */
+private int
+zA85E(os_ptr op)
+{
+ return filter_write_simple(op, &s_A85E_template);
+}
+
+/* <source> ASCII85Decode/filter <file> */
+/* <source> <dict> ASCII85Decode/filter <file> */
+private int
+zA85D(os_ptr op)
+{
+ return filter_read_simple(op, &s_A85D_template);
+}
+
+/* ------ CCITTFaxDecode filter ------ */
+
+/* Common setup for encoding and decoding filters. */
+extern stream_state_proc_put_params(s_CF_put_params, stream_CF_state);
+int
+zcf_setup(os_ptr op, stream_CF_state * pcfs)
+{
+ dict_param_list list;
+ int code = dict_param_list_read(&list, op, NULL, false);
+
+ if (code < 0)
+ return code;
+ s_CF_set_defaults_inline(pcfs);
+ code = s_CF_put_params((gs_param_list *) & list, pcfs);
+ iparam_list_release(&list);
+ return code;
+}
+
+/* <source> <dict> CCITTFaxDecode/filter <file> */
+/* <source> CCITTFaxDecode/filter <file> */
+private int
+zCFD(os_ptr op)
+{
+ os_ptr dop;
+ stream_CFD_state cfs;
+ int code;
+
+ if (r_has_type(op, t_dictionary)) {
+ check_dict_read(*op);
+ dop = op;
+ } else
+ dop = 0;
+ code = zcf_setup(dop, (stream_CF_state *) & cfs);
+ if (code < 0)
+ return code;
+ return filter_read(op, 0, &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;
+
+ if (r_has_type(op, t_dictionary)) {
+ check_dict_read(*op);
+ dop = op;
+ } else
+ dop = 0;
+ if ( (code = dict_int_param(dop, "EarlyChange", 0, 1, 1,
+ &plzs->EarlyChange)) < 0 ||
+ /*
+ * The following are not PostScript standard, although
+ * LanguageLevel 3 provides the first two under different
+ * names.
+ */
+ (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 0;
+}
+
+/* <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;
+ if (LL3_ENABLED && r_has_type(op, t_dictionary)) {
+ int unit_size;
+
+ if ((code = dict_bool_param(op, "LowBitFirst", lzs.FirstBitLowOrder,
+ &lzs.FirstBitLowOrder)) < 0 ||
+ (code = dict_int_param(op, "UnitSize", 3, 8, 8,
+ &unit_size)) < 0
+ )
+ return code;
+ if (code == 0 /* UnitSize specified */ )
+ lzs.InitialCodeLength = unit_size + 1;
+ }
+ return filter_read_predictor(op, 0, &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, 0, &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, 0, &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, 0, &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, 0, &s_PNGPD_template, (stream_state *) & pps, 0);
+}
+
+/* ---------------- Initialization procedure ---------------- */
+
+const op_def zfdecode_op_defs[] =
+{
+ op_def_begin_filter(),
+ {"1ASCII85Encode", zA85E},
+ {"1ASCII85Decode", zA85D},
+ {"2CCITTFaxDecode", zCFD},
+ {"1LZWDecode", zLZWD},
+ {"2PixelDifferenceDecode", zPDiffD},
+ {"2PixelDifferenceEncode", zPDiffE},
+ {"2PNGPredictorDecode", zPNGPD},
+ {"2PNGPredictorEncode", zPNGPE},
+ op_def_end(zfdecode_init)
+};
diff --git a/pstoraster/zfile.c b/pstoraster/zfile.c
new file mode 100644
index 000000000..640d59a00
--- /dev/null
+++ b/pstoraster/zfile.c
@@ -0,0 +1,915 @@
+/* Copyright (C) 1989, 1995, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Non-I/O file operators */
+#include "memory_.h"
+#include "string_.h"
+#include "ghost.h"
+#include "gscdefs.h" /* for gx_io_device_table */
+#include "gp.h"
+#include "gsstruct.h" /* for registering root */
+#include "gxalloc.h" /* for streams */
+#include "oper.h"
+#include "estack.h" /* for filenameforall, .execfile */
+#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);
+
+/* Import the IODevice table. */
+extern_gx_io_device_table();
+
+/* Forward references: file opening. */
+int file_open(P6(const byte *, uint, const char *, uint, ref *, stream **));
+
+/* Forward references: other. */
+private int execfile_finish(P1(os_ptr));
+private int execfile_cleanup(P1(os_ptr));
+
+/*
+ * 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 allocated at this level, both
+ * open and closed. We store this list in the allocator: this is a hack,
+ * but it simplifies bookkeeping (in particular, it guarantees the list is
+ * restored properly by a restore).
+ *
+ * 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. 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.
+ */
+
+/*
+ * 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 */
+
+/* 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_init_no_id(s);
+ invalid_file_entry = s;
+ gs_register_struct_root(imemory, NULL, (void **)&invalid_file_entry,
+ "invalid_file_entry");
+}
+
+/* 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:
+ check_ostack(4);
+ /*
+ * Check to make sure that the file size fits into
+ * a PostScript integer. (On some systems, long is
+ * 32 bits, but file sizes are 64 bits.)
+ */
+ push(4);
+ make_int(op - 4, stat_blocks(&fstat));
+ make_int(op - 3, fstat.st_size);
+ /*
+ * We can't check the value simply by using ==,
+ * because signed/unsigned == does the wrong thing.
+ * Instead, since integer assignment only keeps the
+ * bottom bits, we convert the values to double
+ * and then test for equality. This handles all
+ * cases of signed/unsigned or width mismatch.
+ */
+ if ((double)op[-4].value.intval !=
+ (double)stat_blocks(&fstat) ||
+ (double)op[-3].value.intval !=
+ (double)fstat.st_size
+ )
+ return_error(e_limitcheck);
+ 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 ------ */
+
+/* <executable_file> .execfile - */
+private int
+zexecfile(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, execfile_cleanup);
+ *++esp = *op;
+ push_op_estack(execfile_finish);
+ return zexec(op);
+}
+/* Finish normally. */
+private int
+execfile_finish(os_ptr op)
+{
+ check_ostack(1);
+ esp -= 2;
+ execfile_cleanup(op);
+ return o_pop_estack;
+}
+/* Clean up by closing the file. */
+private int
+execfile_cleanup(os_ptr op)
+{
+ check_ostack(1);
+ *++osp = esp[2];
+ return zclosefile(osp);
+}
+
+/* <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 MAX_CNAME 200
+ byte cname[MAX_CNAME];
+ 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, MAX_CNAME,
+ &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;
+}
+
+/* ------ Initialization procedure ------ */
+
+const op_def zfile_op_defs[] =
+{
+ {"1deletefile", zdeletefile},
+ {"1.execfile", zexecfile},
+ {"2file", zfile},
+ {"3filenameforall", zfilenameforall},
+ {"2.filenamedirseparator", zfilenamedirseparator},
+ {"0.filenamelistseparator", zfilenamelistseparator},
+ {"1.filenamesplit", zfilenamesplit},
+ {"1findlibfile", zfindlibfile},
+ {"2renamefile", zrenamefile},
+ {"1status", zstatus},
+ /* Internal operators */
+ {"0%file_continue", file_continue},
+ {"0%execfile_finish", execfile_finish},
+ op_def_end(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. */
+ char buffer[gp_file_name_sizeof];
+ /* We can't count on the IODevice table to have been initialized yet. */
+ /* Allocate a copy of the default IODevice. */
+ gx_io_device iodev_default_copy;
+ int code;
+
+ iodev_default_copy = *gx_io_device_table[0];
+ code = lib_file_fopen(&iodev_default_copy, bname, "r", &file,
+ buffer, gp_file_name_sizeof);
+ 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",
+ file_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 = file_default_buffer_size;
+ if (len >= buffer_size) /* we copy the file name into the buffer */
+ return_error(e_limitcheck);
+ /* 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;
+}
+
+/* Report an error by storing it in $error.errorinfo. */
+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);
+}
+
+/* Open a file stream for a filter. */
+int
+filter_open(const char *file_access, uint buffer_size, ref * pfile,
+ const stream_procs * 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;
+}
+
+/* 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;
+ gs_ref_memory_t *imem = 0;
+
+ /*
+ * HACK: Figure out whether this is a gs_ref_memory_t we know
+ * about. Avoiding this hack would require rippling a change
+ * from gs_memory_t to gs_ref_memory_t into the open_file and
+ * open_device procedures of gx_io_device, which in turn would
+ * impact other things we don't want to change.
+ */
+ {
+ int i;
+
+ for (i = 0; i < countof(gs_imemory.spaces.indexed); ++i)
+ if (mem == (gs_memory_t *) gs_imemory.spaces.indexed[i]) {
+ imem = (gs_ref_memory_t *) mem;
+ break;
+ }
+ }
+
+ if (imem) {
+ /* Look first for a free stream allocated at this level. */
+ s = imem->streams;
+ while (s != 0) {
+ if (!s_is_valid(s) && s->read_id != 0 /* i.e. !overflowed */ ) {
+ 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);
+ if (imem) {
+ /* Add s to the list of files. */
+ if (imem->streams != 0)
+ imem->streams->prev = s;
+ s->next = imem->streams;
+ imem->streams = s;
+ } else {
+ s->next = 0;
+ }
+ s->prev = 0;
+ 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)");
+ if (s->close_strm && stemp != 0)
+ return sclose(stemp);
+ 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;
+}
diff --git a/pstoraster/zfileio.c b/pstoraster/zfileio.c
new file mode 100644
index 000000000..c9fd9a78b
--- /dev/null
+++ b/pstoraster/zfileio.c
@@ -0,0 +1,842 @@
+/* Copyright (C) 1989, 1995, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* File I/O operators */
+#include "ghost.h"
+#include "gp.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 *const 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_stdout(&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
+ (s_is_writing(s) ?
+ handle_write_status(status, op, NULL, zflushfile) :
+ handle_read_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_stdout(&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 ------ */
+
+const op_def 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},
+ /* Internal operators */
+ {"3%zreadhexstring_continue", zreadhexstring_continue},
+ {"3%zwritehexstring_continue", zwritehexstring_continue},
+ {"3%zreadstring_continue", zreadstring_continue},
+ {"3%zreadline_continue", zreadline_continue},
+ {"4%zwritecvp_continue", zwritecvp_continue},
+ op_def_end(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, 1 (EOF), 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 EOFC:
+ return 1;
+ 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..2ab5a5c43
--- /dev/null
+++ b/pstoraster/zfilter.c
@@ -0,0 +1,418 @@
+/* Copyright (C) 1993, 1995, 1996, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Filter creation */
+#include "memory_.h"
+#include "ghost.h"
+#include "oper.h"
+#include "gsstruct.h"
+#include "ialloc.h"
+#include "idict.h"
+#include "idparam.h"
+#include "ilevel.h" /* SubFileDecode is different in LL3 */
+#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 */
+
+/* <source> ASCIIHexEncode/filter <file> */
+/* <source> <dict> ASCIIHexEncode/filter <file> */
+private int
+zAXE(os_ptr op)
+{
+ return filter_write_simple(op, &s_AXE_template);
+}
+
+/* <target> ASCIIHexDecode/filter <file> */
+/* <target> <dict> 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> <dict> <bool> PFBDecode/filter <file> */
+private int
+zPFBD(os_ptr op)
+{
+ stream_PFBD_state state;
+ os_ptr sop = op;
+
+ check_type(*sop, t_boolean);
+ state.binary_to_hex = sop->value.boolval;
+ return filter_read(op, 1, &s_PFBD_template, (stream_state *) & state, 0);
+}
+
+/* <target> PSStringEncode/filter <file> */
+/* <target> <dict> 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 dop, bool * eod)
+{
+ if (r_has_type(dop, t_dictionary)) {
+ int code;
+
+ check_dict_read(*dop);
+ if ((code = dict_bool_param(dop, "EndOfData", true, eod)) < 0)
+ return code;
+ return 1;
+ } else {
+ *eod = true;
+ return 0;
+ }
+}
+
+/* <target> <record_size> RunLengthEncode/filter <file> */
+/* <target> <dict> <record_size> RunLengthEncode/filter <file> */
+private int
+zRLE(register os_ptr op)
+{
+ stream_RLE_state state;
+ int code;
+
+ check_op(2);
+ code = rl_setup(op - 1, &state.EndOfData);
+ if (code < 0)
+ return code;
+ check_int_leu(*op, max_uint);
+ state.record_size = op->value.intval;
+ return filter_write(op, 1, &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, 0, &s_RLD_template, (stream_state *) & state, 0);
+}
+
+/* <source> <EODcount> <EODstring> SubFileDecode/filter <file> */
+/* <source> <dict> <EODcount> <EODstring> SubFileDecode/filter <file> */
+/* <source> <dict> SubFileDecode/filter <file> *//* (LL3 only) */
+private int
+zSFD(os_ptr op)
+{
+ stream_SFD_state state;
+ ref *sop = op;
+ int npop;
+
+ if (LL3_ENABLED && r_has_type(op, t_dictionary)) {
+ int count;
+ int code;
+
+ check_dict_read(*op);
+ if ((code = dict_int_param(op, "EODCount", 0, max_int, -1, &count)) < 0)
+ return code;
+ if (dict_find_string(op, "EODString", &sop) <= 0)
+ return_error(e_rangecheck);
+ state.count = count;
+ npop = 0;
+ } else {
+ check_type(sop[-1], t_integer);
+ if (sop[-1].value.intval < 0)
+ return_error(e_rangecheck);
+ state.count = sop[-1].value.intval;
+ npop = 2;
+ }
+ check_read_type(*sop, t_string);
+ 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));
+}
+
+/* ------ Utilities ------ */
+
+/* Forward references */
+private int filter_ensure_buf(P3(stream **, uint, bool));
+
+/* Set up an input filter. */
+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);
+ os_ptr sop = op - npop;
+ stream *s;
+ stream *sstrm;
+ bool close = false;
+ int code;
+
+ /* Skip over an optional dictionary parameter. */
+ if (r_has_type(sop, t_dictionary)) {
+ check_dict_read(*sop);
+ if ((code = dict_bool_param(sop, "CloseSource", false, &close)) < 0)
+ return code;
+ --sop;
+ }
+ /*
+ * 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_filter_read_procs, template, st);
+ if (code < 0)
+ goto out;
+ s = fptr(sop);
+ s->strm = sstrm;
+ s->close_strm = close;
+ pop(op - sop);
+out:
+ ialloc_set_space(idmemory, save_space);
+ return code;
+}
+int
+filter_read_simple(os_ptr op, const stream_template * template)
+{
+ return filter_read(op, 0, template, NULL, 0);
+}
+
+/* Set up an output filter. */
+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;
+ bool close = false;
+ int code;
+
+ /* Skip over an optional dictionary parameter. */
+ if (r_has_type(sop, t_dictionary)) {
+ check_dict_read(*sop);
+ if ((code = dict_bool_param(sop, "CloseTarget", false, &close)) < 0)
+ return code;
+ --sop;
+ }
+ /*
+ * 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_filter_write_procs, template, st);
+ if (code < 0)
+ goto out;
+ s = fptr(sop);
+ s->strm = sstrm;
+ s->close_strm = close;
+ pop(op - sop);
+out:
+ ialloc_set_space(idmemory, save_space);
+ return code;
+}
+int
+filter_write_simple(os_ptr op, const stream_template * template)
+{
+ return filter_write(op, 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_filter_write_procs,
+ &s_NullE_template, NULL);
+ else
+ code = filter_open("r", min_size, &bsop, &s_filter_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 ------ */
+
+const op_def zfilter_op_defs[] =
+{
+ /* We enter PSStringEncode and SubFileDecode (only) */
+ /* as separate operators. */
+ {"1.psstringencode", zPSSE},
+ {"2.subfiledecode", zSFD},
+ op_def_begin_filter(),
+ {"1ASCIIHexEncode", zAXE},
+ {"1ASCIIHexDecode", zAXD},
+ {"1NullEncode", zNullE},
+ {"2PFBDecode", zPFBD},
+ {"1PSStringEncode", zPSSE},
+ {"2RunLengthEncode", zRLE},
+ {"1RunLengthDecode", zRLD},
+ {"3SubFileDecode", zSFD},
+ op_def_end(0)
+};
diff --git a/pstoraster/zfilter2.c b/pstoraster/zfilter2.c
new file mode 100644
index 000000000..fc58762e7
--- /dev/null
+++ b/pstoraster/zfilter2.c
@@ -0,0 +1,165 @@
+/* Copyright (C) 1991, 1995, 1996, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Additional filter creation */
+#include "memory_.h"
+#include "ghost.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, 0, &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_write(op, 0, &s_PDiffE_template, (stream_state *)&pds, 0) :
+ filter_write(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, 0, &s_LZWE_template,
+ (stream_state *) & lzs);
+}
+
+/* ================ Initialization procedure ================ */
+
+const op_def zfilter2_op_defs[] =
+{
+ op_def_begin_filter(),
+ {"2CCITTFaxEncode", zCFE},
+ {"1LZWEncode", zLZWE},
+ op_def_end(0)
+};
diff --git a/pstoraster/zfilterx.c b/pstoraster/zfilterx.c
new file mode 100644
index 000000000..6c3897236
--- /dev/null
+++ b/pstoraster/zfilterx.c
@@ -0,0 +1,336 @@
+/* Copyright (C) 1995, 1996, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Extended (non-standard) filter creation */
+#include "memory_.h"
+#include "ghost.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 = bhc_setup(op, (stream_BHC_state *)&bhcs);
+
+ if (code < 0)
+ return code;
+ return filter_write(op, 0, &s_BHCE_template, (stream_state *)&bhcs, 0);
+}
+
+/* <source> <dict> BoundedHuffmanDecode/filter <file> */
+private int
+zBHCD(os_ptr op)
+{
+ stream_BHCD_state bhcs;
+ int code = bhc_setup(op, (stream_BHC_state *)&bhcs);
+
+ if (code < 0)
+ return code;
+ return filter_read(op, 0, &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 =
+ dict_int_param(op, "BlockSize", 1, max_int / sizeof(int) - 10, 16384,
+ &pbwbss->BlockSize);
+
+ if (code < 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, 0, &s_BWBSE_template, (stream_state *)&bwbss, 0);
+}
+
+/* <source> <dict> BWBlockSortDecode/filter <file> */
+private int
+zBWBSD(os_ptr op)
+{
+ stream_BWBSD_state bwbss;
+ int code = bwbs_setup(op, (stream_BWBS_state *)&bwbss);
+
+ if (code < 0)
+ return code;
+ return filter_read(op, 0, &s_BWBSD_template, (stream_state *)&bwbss, 0);
+}
+
+/* ------ Byte translation filters ------ */
+
+/* Common setup */
+private int
+bt_setup(os_ptr op, stream_BT_state * pbts)
+{
+ check_read_type(*op, t_string);
+ if (r_size(op) != 256)
+ return_error(e_rangecheck);
+ memcpy(pbts->table, op->value.const_bytes, 256);
+ return 0;
+}
+
+/* <target> <table> ByteTranslateEncode/filter <file> */
+/* <target> <table> <dict> 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, 0, &s_BTE_template, (stream_state *)&bts, 0);
+}
+
+/* <target> <table> ByteTranslateDecode/filter <file> */
+/* <target> <table> <dict> 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, 0, &s_BTD_template, (stream_state *)&bts, 0);
+}
+
+/* ------ Move-to-front filters ------ */
+
+/* <target> MoveToFrontEncode/filter <file> */
+/* <target> <dict> MoveToFrontEncode/filter <file> */
+private int
+zMTFE(os_ptr op)
+{
+ return filter_write_simple(op, &s_MTFE_template);
+}
+
+/* <source> MoveToFrontDecode/filter <file> */
+/* <source> <dict> 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> PCXDecode/filter <file> */
+private int
+zPCXD(os_ptr op)
+{
+ return filter_read_simple(op, &s_PCXD_template);
+}
+
+/* ================ Initialization procedure ================ */
+
+const op_def 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},
+ op_def_end(0)
+};
diff --git a/pstoraster/zfname.c b/pstoraster/zfname.c
new file mode 100644
index 000000000..66bfd385e
--- /dev/null
+++ b/pstoraster/zfname.c
@@ -0,0 +1,116 @@
+/* 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 supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* File name utilities */
+#include "memory_.h"
+#include "ghost.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..919d017f6
--- /dev/null
+++ b/pstoraster/zfont.c
@@ -0,0 +1,471 @@
+/* Copyright (C) 1989, 1995, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Generic font operators */
+#include "ghost.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 */
+
+/* Mark a glyph as a PostScript name (if it isn't a CID). */
+bool
+zfont_mark_glyph_name(gs_glyph glyph, void *ignore_data)
+{
+ return (glyph >= gs_min_cid_glyph || glyph == gs_no_glyph ? false :
+ name_mark_index((uint) glyph));
+}
+
+/* Initialize the font operators */
+private void
+zfont_init(void)
+{
+ ifont_dir = gs_font_dir_alloc2(imemory, &gs_memory_default);
+ ifont_dir->ccache.mark_glyph = zfont_mark_glyph_name;
+ gs_register_struct_root(imemory, NULL, (void **)&ifont_dir,
+ "ifont_dir");
+}
+
+/* <font> <scale> scalefont <new_font> */
+private int
+zscalefont(register os_ptr op)
+{
+ int code;
+ double scale;
+ gs_matrix mat;
+
+ if ((code = real_param(op, &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 ------ */
+
+const op_def zfont_op_defs[] =
+{
+ {"0currentfont", zcurrentfont},
+ {"2makefont", zmakefont},
+ {"2scalefont", zscalefont},
+ {"1setfont", zsetfont},
+ {"0cachestatus", zcachestatus},
+ {"1setcachelimit", zsetcachelimit},
+ {"1setcacheparams", zsetcacheparams},
+ {"0currentcacheparams", zcurrentcacheparams},
+ op_def_end(zfont_init)
+};
+
+/* ------ Subroutines ------ */
+
+/* Validate a font parameter. */
+int
+font_param(const ref * pfdict, gs_font ** ppfont)
+{ /*
+ * Check that pfdict is a read-only dictionary, that it has a FID
+ * entry whose value is a fontID, and that the fontID points to a
+ * gs_font structure whose associated PostScript dictionary is
+ * pfdict.
+ */
+ ref *pid;
+ gs_font *pfont;
+ const font_data *pdata;
+
+ 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);
+ pdata = pfont->client_data;
+ if (!obj_eq(&pdata->dict, pfdict))
+ return_error(e_invalidfont);
+ *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 = 0;
+
+ 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 (pencoding != 0 &&
+ !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 ((uid_is_XUID(&pair->UID) &&
+ alloc_is_since_save((char *)pair->UID.xvalues,
+ save))
+ ) {
+ gs_purge_fm_pair(pdir, pair, 0);
+ continue;
+ }
+ if (pair->font != 0 &&
+ alloc_is_since_save((char *)pair->font, save)
+ ) {
+ if (!uid_is_valid(&pair->UID)) {
+ gs_purge_fm_pair(pdir, pair, 0);
+ continue;
+ }
+ /* Don't discard pairs with a surviving UID. */
+ pair->font = 0;
+ }
+ 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..a331047ce
--- /dev/null
+++ b/pstoraster/zfont0.c
@@ -0,0 +1,346 @@
+/* Copyright (C) 1991, 1992, 1993, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Composite font creation operator */
+#include "ghost.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 "gxfcmap.h"
+#include "gxfont.h"
+#include "gxfont0.h"
+#include "bfont.h"
+#include "ialloc.h"
+#include "idict.h"
+#include "idparam.h"
+#include "igstate.h"
+#include "iname.h"
+#include "store.h"
+
+/* Composite font procedures */
+extern font_proc_init_fstack(gs_type0_init_fstack);
+extern font_proc_define_font(gs_type0_define_font);
+extern font_proc_make_font(gs_type0_make_font);
+extern font_proc_next_glyph(gs_type0_next_glyph);
+
+/* Imported from zfcmap.c */
+int ztype0_get_cmap(P3(const gs_cmap ** ppcmap, const ref * pfdepvector,
+ const ref * op));
+
+/* 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 *, byte *, int));
+
+/* <string|name> <font_dict> .buildfont0 <string|name> <font> */
+/* Build a type 0 (composite) font. */
+private int
+zbuildfont0(os_ptr op)
+{
+ gs_type0_data data;
+ ref fdepvector;
+ ref *pprefenc;
+ gs_font_type0 *pfont;
+ font_data *pdata;
+ ref save_FID;
+ int i;
+ int code = 0;
+
+ check_type(*op, t_dictionary);
+ {
+ ref *pfmaptype;
+ ref *pfdepvector;
+
+ 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;
+ /*
+ * Adding elements below could cause the font dictionary to be
+ * resized, which would invalidate pfdepvector.
+ */
+ fdepvector = *pfdepvector;
+ }
+ /* Check that every element of the FDepVector is a font. */
+ data.fdep_size = r_size(&fdepvector);
+ for (i = 0; i < data.fdep_size; i++) {
+ ref fdep;
+ gs_font *psub;
+
+ array_get(&fdepvector, i, &fdep);
+ if ((code = font_param(&fdep, &psub)) < 0)
+ return code;
+ /*
+ * 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
+ */
+ if (psub->FontType == ft_composite) {
+ const gs_font_type0 *const psub0 = (const 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);
+ }
+ }
+ 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 */
+ {
+ ref *psubsvector;
+ 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;
+ } break;
+ case fmap_CMap: /* need CMap */
+ code = ztype0_get_cmap(&data.CMap, (const ref *)&fdepvector,
+ (const ref *)op);
+ break;
+ default:
+ ;
+ }
+ if (code < 0)
+ return code;
+ /*
+ * Save the old FID in case we have to back out.
+ * build_gs_font will return an error if there is a FID entry
+ * but it doesn't reference a valid font.
+ */
+ {
+ ref *pfid;
+
+ if (dict_find_string(op, "FID", &pfid) <= 0)
+ make_null(&save_FID);
+ else
+ save_FID = *pfid;
+ }
+ {
+ 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,
+ bf_options_none);
+ }
+ if (code != 0)
+ return code;
+ /* Fill in the rest of the basic font data. */
+ pfont->procs.init_fstack = gs_type0_init_fstack;
+ pfont->procs.next_char = 0; /* superseded by next_glyph */
+ pfont->procs.define_font = ztype0_define_font;
+ pfont->procs.make_font = ztype0_make_font;
+ pfont->procs.next_glyph = gs_type0_next_glyph;
+ if (dict_find_string(op, "PrefEnc", &pprefenc) <= 0) {
+ ref nul;
+
+ make_null_new(&nul);
+ if ((code = dict_put_string(op, "PrefEnc", &nul)) < 0)
+ goto fail;
+ }
+ /* 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) {
+ code = gs_note_error(e_VMerror);
+ goto fail;
+ }
+ /* 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);
+ if (!r_has_type(&enc, t_integer)) {
+ code = gs_note_error(e_typecheck);
+ goto fail;
+ }
+ if ((ulong) enc.value.intval >= data.fdep_size) {
+ code = gs_note_error(e_rangecheck);
+ goto fail;
+ }
+ 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) {
+ code = gs_note_error(e_VMerror);
+ goto fail;
+ }
+ for (i = 0; i < data.fdep_size; i++) {
+ ref fdep;
+ ref *pfid;
+
+ array_get(&fdepvector, 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;
+ code = define_gs_font((gs_font *) pfont);
+ if (code >= 0)
+ return code;
+fail:
+ /* Undo the insertion of the FID entry in the dictionary. */
+ if (r_has_type(&save_FID, t_null)) {
+ ref rnfid;
+
+ name_enter_string("FID", &rnfid);
+ dict_undef(op, &rnfid);
+ } else
+ dict_put_string(op, "FID", &save_FID);
+ gs_free_object(pfont->memory, pfont, "buildfont0(font)");
+ return code;
+}
+/* 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)
+{
+ gs_font_type0 *const 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);
+}
+private int
+ztype0_make_font(gs_font_dir * pdir, const gs_font * pfont,
+ const gs_matrix * pmat, gs_font ** ppfont)
+{
+ gs_font_type0 **const 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);
+}
+
+/* ------ Internal routines ------ */
+
+/* Find or add a character entry in a font dictionary. */
+private int
+ensure_char_entry(os_ptr op, const char *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 ------ */
+
+const op_def zfont0_op_defs[] =
+{
+ {"2.buildfont0", zbuildfont0},
+ op_def_end(0)
+};
diff --git a/pstoraster/zfont1.c b/pstoraster/zfont1.c
new file mode 100644
index 000000000..525167bc1
--- /dev/null
+++ b/pstoraster/zfont1.c
@@ -0,0 +1,291 @@
+/* Copyright (C) 1991, 1995, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Type 1 and Type 4 font creation operator */
+#include "ghost.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"
+
+/*#define TEST*/
+
+/* Type 1 auxiliary procedures (defined in zchar1.c) */
+extern const gs_type1_data_procs_t z1_data_procs;
+
+/* Default value of lenIV */
+#define DEFAULT_LENIV_1 4
+#define DEFAULT_LENIV_2 (-1)
+
+/* Private utilities */
+private uint
+subr_bias(const ref * psubrs)
+{
+ uint size = r_size(psubrs);
+
+ return (size < 1240 ? 107 : size < 33900 ? 1131 : 32768);
+}
+private void
+find_zone_height(float *pmax_height, int count, const float *values)
+{
+ int i;
+ float zone_height;
+
+ for (i = 0; i < count; i += 2)
+ if ((zone_height = values[i + 1] - values[i]) > *pmax_height)
+ *pmax_height = zone_height;
+}
+
+/* Build a Type 1 or Type 4 font. */
+private int
+buildfont1or4(os_ptr op, build_proc_refs * pbuild, font_type ftype,
+ build_font_options_t options)
+{
+ gs_type1_data data1;
+ ref no_subrs;
+ ref *pothersubrs = &no_subrs;
+ ref *psubrs = &no_subrs;
+ ref *pglobalsubrs = &no_subrs;
+ ref *pprivate;
+ 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))
+ return_error(e_typecheck);
+ }
+ if (dict_find_string(pprivate, "Subrs", &psubrs) > 0) {
+ if (!r_is_array(psubrs))
+ return_error(e_typecheck);
+ }
+ if ((code = dict_int_param(op, "CharstringType", 1, 2, 1,
+ &data1.CharstringType)) < 0
+ )
+ return code;
+ /* Get information specific to Type 2 charstrings. */
+ if (data1.CharstringType == 2) {
+ float dwx, nwx;
+
+ data1.subroutineNumberBias = subr_bias(psubrs);
+ if (dict_find_string(pprivate, "GlobalSubrs", &pglobalsubrs) > 0) {
+ if (!r_is_array(pglobalsubrs))
+ return_error(e_typecheck);
+ }
+ data1.gsubrNumberBias = subr_bias(pglobalsubrs);
+ if ((code = dict_uint_param(pprivate, "gsubrNumberBias",
+ 0, max_uint, data1.gsubrNumberBias,
+ &data1.gsubrNumberBias)) < 0 ||
+ (code = dict_float_param(pprivate, "defaultWidthX", 0.0,
+ &dwx)) < 0 ||
+ (code = dict_float_param(pprivate, "nominalWidthX", 0.0,
+ &nwx)) < 0
+ )
+ return code;
+ data1.defaultWidthX = float2fixed(dwx);
+ data1.nominalWidthX = float2fixed(nwx);
+ {
+ ref *pirs;
+
+ if (dict_find_string(pprivate, "initialRandomSeed", &pirs) <= 0)
+ data1.initialRandomSeed = 0;
+ else if (!r_has_type(pirs, t_integer))
+ return_error(e_typecheck);
+ else
+ data1.initialRandomSeed = pirs->value.intval;
+ }
+ data1.lenIV = DEFAULT_LENIV_2;
+ } else {
+ data1.subroutineNumberBias = 0;
+ data1.gsubrNumberBias = 0;
+ data1.lenIV = DEFAULT_LENIV_1;
+ }
+ /* Get the rest of the information from the Private dictionary. */
+ if ((code = dict_int_param(pprivate, "lenIV", -1, 255, data1.lenIV,
+ &data1.lenIV)) < 0 ||
+ (code = dict_uint_param(pprivate, "subroutineNumberBias",
+ 0, max_uint, data1.subroutineNumberBias,
+ &data1.subroutineNumberBias)) < 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 = data1.WeightVector.count =
+ dict_float_array_param(op, "WeightVector", max_WeightVector,
+ data1.WeightVector.values, 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;
+
+#define SCAN_ZONE(z)\
+ find_zone_height(&max_zone_height, data1.z.count, data1.z.values);
+
+ SCAN_ZONE(BlueValues);
+ SCAN_ZONE(OtherBlues);
+ SCAN_ZONE(FamilyBlues);
+ SCAN_ZONE(FamilyOtherBlues);
+
+#undef SCAN_ZONE
+
+ 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, options);
+ 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);
+ ref_assign(&pdata->u.type1.GlobalSubrs, pglobalsubrs);
+ pfont->data.procs = &z1_data_procs;
+ 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, bf_notdef_required);
+}
+
+/* <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, bf_options_none);
+}
+
+#ifdef TEST
+
+#include "igstate.h"
+#include "stream.h"
+#include "files.h"
+
+/* <file> .printfont1 - */
+private int
+zprintfont1(os_ptr op)
+{
+ const gs_font *pfont = gs_currentfont(igs);
+ stream *s;
+ int code;
+
+ if (pfont->FontType != ft_encrypted)
+ return_error(e_rangecheck);
+ check_write_file(s, op);
+ code = psdf_embed_type1_font(s, (gs_font_type1 *) pfont);
+ if (code >= 0)
+ pop(1);
+ return code;
+}
+
+#endif
+
+/* ------ Initialization procedure ------ */
+
+const op_def zfont1_op_defs[] =
+{
+ {"2.buildfont1", zbuildfont1},
+ {"2.buildfont4", zbuildfont4},
+#ifdef TEST
+ {"2.printfont1", zprintfont1},
+#endif
+ op_def_end(0)
+};
diff --git a/pstoraster/zfont2.c b/pstoraster/zfont2.c
new file mode 100644
index 000000000..0ef13e82b
--- /dev/null
+++ b/pstoraster/zfont2.c
@@ -0,0 +1,553 @@
+/* Copyright (C) 1989, 1995, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Font creation utilities */
+#include "memory_.h"
+#include "string_.h"
+#include "ghost.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 "interp.h" /* for initial_enter_name */
+#include "ipacked.h"
+#include "istruct.h"
+#include "store.h"
+
+/* Registered encodings. See ifont.h for documentation. */
+ref registered_Encodings;
+private ref *const registered_Encodings_p = &registered_Encodings;
+
+/* 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_all,
+ registered_Encodings_countof,
+ "registered_Encodings");
+ for (i = 0; i < registered_Encodings_countof; i++)
+ make_empty_array(&registered_Encoding(i), 0);
+ initial_enter_name("registeredencodings", &registered_Encodings);
+ gs_register_ref_root(imemory, NULL, (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, bf_options_none);
+ if (code < 0)
+ return code;
+ return define_gs_font((gs_font *) pfont);
+}
+
+/* Encode a character. */
+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;
+
+ if (index >= gs_min_cid_glyph) { /* Fabricate a numeric name. */
+ char cid_name[sizeof(gs_glyph) * 3 + 1];
+ int code;
+
+ sprintf(cid_name, "%lu", (ulong) index);
+ code = name_ref((const byte *)cid_name, strlen(cid_name),
+ &nref, 1);
+ if (code < 0)
+ return 0; /* What can we possibly do here? */
+ } else
+ name_index_ref(index, &nref);
+ name_string_ref(&nref, &sref);
+ *plen = r_size(&sref);
+ return (const char *)sref.value.const_bytes;
+}
+
+/* ------ Initialization procedure ------ */
+
+const op_def zfont2_op_defs[] =
+{
+ {"2.buildfont3", zbuildfont3},
+ op_def_end(zfont2_init)
+};
+
+/* ------ Subroutines ------ */
+
+/* Convert strings to executable names for build_proc_refs. */
+int
+build_proc_name_refs(build_proc_refs * pbuild,
+ const char *bcstr, const char *bgstr)
+{
+ int code;
+
+ if (!bcstr)
+ make_null(&pbuild->BuildChar);
+ else {
+ if ((code = name_ref((const byte *)bcstr, strlen(bcstr), &pbuild->BuildChar, 0)) < 0)
+ return code;
+ r_set_attrs(&pbuild->BuildChar, a_executable);
+ }
+ if (!bgstr)
+ make_null(&pbuild->BuildGlyph);
+ else {
+ if ((code = name_ref((const byte *)bgstr, strlen(bgstr), &pbuild->BuildGlyph, 0)) < 0)
+ return code;
+ 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,
+ build_font_options_t options)
+{
+ int painttype;
+ float strokewidth;
+ ref *pcharstrings = 0;
+ 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) {
+ if (!(options & bf_CharStrings_optional))
+ return_error(e_invalidfont);
+ } else {
+ ref *ignore;
+
+ if (!r_has_type(pcharstrings, t_dictionary))
+ return_error(e_invalidfont);
+ if ((options & bf_notdef_required) != 0 &&
+ dict_find_string(pcharstrings, ".notdef", &ignore) <= 0
+ )
+ return_error(e_invalidfont);
+ }
+ code = build_gs_simple_font(op, &pfont, ftype, pstype, pbuild, options);
+ if (code != 0)
+ return code;
+ pfont->PaintType = painttype;
+ pfont->StrokeWidth = strokewidth;
+ pdata = pfont_data(pfont);
+ if (pcharstrings)
+ ref_assign(&pdata->CharStrings, pcharstrings);
+ else
+ make_null(&pdata->CharStrings);
+ /* 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,
+ build_font_options_t options)
+{
+ double bbox[4];
+ gs_uid uid;
+ int code;
+ gs_font_base *pfont;
+
+ code = font_bbox_param(op, bbox);
+ if (code < 0)
+ return code;
+ if ((options & bf_FontBBox_required) &&
+ bbox[0] == 0 && bbox[1] == 0 && bbox[2] == 0 && bbox[3] == 0
+ )
+ return_error(e_invalidfont);
+ code = dict_uid_param(op, &uid, 0, imemory);
+ if (code < 0)
+ return code;
+ if ((options & bf_UniqueID_ignored) && uid_is_UniqueID(&uid))
+ uid_set_invalid(&uid);
+ code = build_gs_font(op, (gs_font **) ppfont, ftype, pstype, pbuild,
+ options);
+ if (code != 0) /* invalid or scaled font */
+ return code;
+ 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,
+ build_font_options_t options)
+{
+ ref kname, fname; /* t_string */
+ ref *pftype;
+ ref *pfontname;
+ ref *pmatrix;
+ gs_matrix mat;
+ ref *pencoding = 0;
+ 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
+ )
+ return_error(e_invalidfont);
+ if (dict_find_string(op, "Encoding", &pencoding) <= 0) {
+ if (!(options & bf_Encoding_optional))
+ return_error(e_invalidfont);
+ } else {
+ if (!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 1;
+ }
+ /*
+ * 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;
+ ref encoding;
+ /*
+ * Make sure that we allocate the font data
+ * in the same VM as the font dictionary.
+ */
+ uint space = ialloc_space(idmemory);
+
+ /*
+ * Since add_FID may resize the dictionary and cause
+ * pencoding to become invalid, save the Encoding.
+ */
+ if (pencoding)
+ encoding = *pencoding;
+ 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);
+ if (pencoding)
+ ref_assign_new(&pdata->Encoding, &encoding);
+ /* Clear the chain pointers so as not to confuse the memory */
+ /* manager if we bail out after returning from here. */
+ pfont->next = pfont->prev = 0;
+ pfont->memory = imemory;
+ pfont->dir = 0;
+ pfont->base = pfont;
+ pfont->client_data = pdata;
+ pfont->FontType = ftype;
+ pfont->FontMatrix = mat;
+ pfont->BitmapWidths = bitmapwidths;
+ pfont->ExactSize = (fbit_type) exactsize;
+ pfont->InBetweenSize = (fbit_type) inbetweensize;
+ pfont->TransformedChar = (fbit_type) 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/zfont32.c b/pstoraster/zfont32.c
new file mode 100644
index 000000000..7fba978aa
--- /dev/null
+++ b/pstoraster/zfont32.c
@@ -0,0 +1,79 @@
+/* Copyright (C) 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Type 32 font operators */
+#include "ghost.h"
+#include "oper.h"
+#include "gsccode.h" /* for gxfont.h */
+#include "gsmatrix.h"
+#include "gsutil.h"
+#include "gxfixed.h" /* for gxchar.h */
+#include "gxchar.h"
+#include "gxfont.h"
+#include "bfont.h"
+#include "store.h"
+
+/* The encode_char procedure of a Type 32 font should never be called. */
+private gs_glyph
+zfont_no_encode_char(gs_show_enum * penum, gs_font * pfont, gs_char * pchr)
+{
+ return gs_no_glyph;
+}
+
+/* <string|name> <font_dict> .buildfont32 <string|name> <font> */
+/* Build a type 32 (bitmap) font. */
+private int
+zbuildfont32(os_ptr op)
+{
+ int code;
+ build_proc_refs build;
+ gs_font_base *pfont;
+
+ check_type(*op, t_dictionary);
+ code = build_proc_name_refs(&build, NULL, "%Type32BuildGlyph");
+ if (code < 0)
+ return code;
+ code = build_gs_simple_font(op, &pfont, ft_CID_bitmap, &st_gs_font_base,
+ &build, bf_Encoding_optional);
+ if (code < 0)
+ return code;
+ /* Always transform cached bitmaps. */
+ pfont->BitmapWidths = true;
+ pfont->ExactSize = fbit_transform_bitmaps;
+ pfont->InBetweenSize = fbit_transform_bitmaps;
+ pfont->TransformedChar = fbit_transform_bitmaps;
+ /* The encode_char procedure of a Type 32 font */
+ /* should never be called. */
+ pfont->procs.encode_char = zfont_no_encode_char;
+ return define_gs_font((gs_font *) pfont);
+}
+
+/* ------ Initialization procedure ------ */
+
+const op_def zfont32_op_defs[] =
+{
+ {"2.buildfont32", zbuildfont32},
+ op_def_end(0)
+};
diff --git a/pstoraster/zfont42.c b/pstoraster/zfont42.c
new file mode 100644
index 000000000..59278a265
--- /dev/null
+++ b/pstoraster/zfont42.c
@@ -0,0 +1,189 @@
+/* Copyright (C) 1996, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Type 42 font creation operator */
+#include "memory_.h"
+#include "ghost.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 **));
+private int z42_gdir_get_outline(P3(gs_font_type42 *, uint, gs_const_string *));
+
+/* <string|name> <font_dict> .buildfont11/42 <string|name> <font> */
+/* Build a type 11 (TrueType CID-keyed) or 42 (TrueType) font. */
+int
+build_gs_TrueType_font(os_ptr op, font_type ftype, const char *bcstr,
+ const char *bgstr, build_font_options_t options)
+{
+ build_proc_refs build;
+ ref sfnts, sfnts0, GlyphDirectory;
+ gs_font_type42 *pfont;
+ font_data *pdata;
+ int code;
+
+ code = build_proc_name_refs(&build, bcstr, bgstr);
+ if (code < 0)
+ return code;
+ check_type(*op, t_dictionary);
+ {
+ ref *psfnts;
+ ref *pGlyphDirectory;
+
+ 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);
+ if (dict_find_string(op, "GlyphDirectory", &pGlyphDirectory) <= 0)
+ make_null(&GlyphDirectory);
+ else if (!r_has_type(pGlyphDirectory, t_dictionary))
+ return_error(e_typecheck);
+ else
+ GlyphDirectory = *pGlyphDirectory;
+ /*
+ * Since build_gs_primitive_font may resize the dictionary and cause
+ * pointers to become invalid, save sfnts.
+ */
+ sfnts = *psfnts;
+ }
+ code = build_gs_primitive_font(op, (gs_font_base **) & pfont, ftype,
+ &st_gs_font_type42, &build, options);
+ if (code != 0)
+ return code;
+ pdata = pfont_data(pfont);
+ ref_assign(&pdata->u.type42.sfnts, &sfnts);
+ ref_assign(&pdata->u.type42.GlyphDirectory, &GlyphDirectory);
+ pfont->data.string_proc = z42_string_proc;
+ pfont->data.proc_data = (char *)pdata;
+ code = gs_type42_font_init(pfont);
+ if (code < 0)
+ return code;
+ /*
+ * Some versions of the Adobe PostScript Windows driver have a bug
+ * that causes them to output the FontBBox for Type 42 fonts in the
+ * 2048- or 4096-unit character space rather than a 1-unit space.
+ * Work around this here.
+ */
+ if (pfont->FontBBox.q.x - pfont->FontBBox.p.x > 100 ||
+ pfont->FontBBox.q.y - pfont->FontBBox.p.y > 100
+ ) {
+ float upem = pfont->data.unitsPerEm;
+
+ pfont->FontBBox.p.x /= upem;
+ pfont->FontBBox.p.y /= upem;
+ pfont->FontBBox.q.x /= upem;
+ pfont->FontBBox.q.y /= upem;
+ }
+ /*
+ * Apparently Adobe versions 2015 and later use an alternate
+ * method of accessing character outlines: instead of loca and glyf,
+ * they use a dictionary called GlyphDirectory. In this case,
+ * we use an alternate get_outline procedure.
+ */
+ if (!r_has_type(&GlyphDirectory, t_null))
+ pfont->data.get_outline = z42_gdir_get_outline;
+ return define_gs_font((gs_font *) pfont);
+}
+private int
+zbuildfont42(os_ptr op)
+{
+ return build_gs_TrueType_font(op, ft_TrueType, "%Type42BuildChar",
+ "%Type42BuildGlyph", bf_options_none);
+}
+
+/* ------ Initialization procedure ------ */
+
+const op_def zfont42_op_defs[] =
+{
+ {"2.buildfont42", zbuildfont42},
+ op_def_end(0)
+};
+
+/* Get an outline from GlyphDirectory instead of loca / glyf. */
+private int
+z42_gdir_get_outline(gs_font_type42 * pfont, uint glyph_index,
+ gs_const_string * pgstr)
+{
+ const font_data *pfdata = pfont_data(pfont);
+ const ref *pgdir = &pfdata->u.type42.GlyphDirectory;
+ ref iglyph;
+ ref *pgdef;
+
+ make_int(&iglyph, glyph_index);
+ if (dict_find(pgdir, &iglyph, &pgdef) <= 0) {
+ pgstr->data = 0;
+ pgstr->size = 0;
+ } else if (!r_has_type(pgdef, t_string)) {
+ return_error(e_typecheck);
+ } else {
+ pgstr->data = pgdef->value.const_bytes;
+ pgstr->size = r_size(pgdef);
+ }
+ return 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);
+ uint size;
+
+ if (code < 0)
+ return code;
+ if (!r_has_type(&rstr, t_string))
+ return_error(e_typecheck);
+ /*
+ * NOTE: According to the Adobe documentation, each sfnts
+ * string should have even length. If the length is odd,
+ * the additional byte is padding and should be ignored.
+ */
+ size = r_size(&rstr) & ~1;
+ if (left < size) {
+ if (left + length > size)
+ return_error(e_rangecheck);
+ *pdata = rstr.value.const_bytes + left;
+ return 0;
+ }
+ left -= size;
+ }
+}
diff --git a/pstoraster/zfproc.c b/pstoraster/zfproc.c
new file mode 100644
index 000000000..46d40ca26
--- /dev/null
+++ b/pstoraster/zfproc.c
@@ -0,0 +1,363 @@
+/* Copyright (C) 1994, 1995, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Procedure-based filter stream support */
+#include "memory_.h"
+#include "ghost.h"
+#include "oper.h" /* for ifilter.h */
+#include "estack.h"
+#include "gsstruct.h"
+#include "ialloc.h"
+#include "istruct.h" /* for RELOC_REF_VAR */
+#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:
+ENUM_RETURN_REF(&pptr->proc);
+case 1:
+ENUM_RETURN_REF(&pptr->data);
+ENUM_PTRS_END
+private RELOC_PTRS_BEGIN(sproc_reloc_ptrs);
+RELOC_REF_VAR(pptr->proc);
+r_clear_attrs(&pptr->proc, l_mark);
+RELOC_REF_VAR(pptr->data);
+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, const stream_procs * procs)
+{
+ stream *sstrm = file_alloc_stream(imemory, "s_proc_init(stream)");
+ stream_proc_state *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
+};
+private const stream_procs s_proc_read_procs = {
+ s_std_noavailable, s_std_noseek, s_std_read_reset,
+ s_std_read_flush, s_std_null, 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,
+ &s_proc_read_procs);
+
+ if (code < 0)
+ return code;
+ (*psstrm)->end_status = CALLC;
+ return code;
+}
+
+/* Handle an input request. */
+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. */
+ stream_proc_state *const ss = (stream_proc_state *) st;
+ 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);
+}
+
+/* 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;
+ 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 = ((stream_proc_state *) ps->state)->proc;
+ 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
+};
+private const stream_procs s_proc_write_procs = {
+ s_std_noavailable, s_std_noseek, s_std_write_reset,
+ s_std_write_flush, s_std_null, 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,
+ &s_proc_write_procs);
+}
+
+/* Handle an output request. */
+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. */
+ stream_proc_state *const ss = (stream_proc_state *) st;
+ 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);
+}
+
+/* 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;
+ stream_proc_state *psst;
+
+ 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;
+ 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);
+ 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 ------ */
+
+const op_def zfproc_op_defs[] =
+{
+ /* Internal operators */
+ {"2%s_proc_read_continue", s_proc_read_continue},
+ {"2%s_proc_write_continue", s_proc_write_continue},
+ op_def_end(0)
+};
diff --git a/pstoraster/zfreuse.c b/pstoraster/zfreuse.c
new file mode 100644
index 000000000..0d2488bef
--- /dev/null
+++ b/pstoraster/zfreuse.c
@@ -0,0 +1,206 @@
+/* Copyright (C) 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* ReusableStreamDecode filter support */
+#include "memory_.h"
+#include "ghost.h"
+#include "oper.h"
+#include "stream.h"
+#include "strimpl.h"
+#include "sfilter.h" /* for SubFileDecode */
+#include "files.h"
+#include "idict.h"
+#include "idparam.h"
+#include "iname.h"
+#include "store.h"
+
+/*
+ * The actual work of constructing the filter is done in PostScript code.
+ * The operators in this file are internal ones that handle the dirty work.
+ */
+
+/* <dict|null> .rsdparams <filters> <decodeparms|null> */
+/* filters is always an array, and decodeparms is always either an array */
+/* of the same length as filters, or null. */
+private int
+zrsdparams(os_ptr op)
+{
+ ref *pFilter;
+ ref *pDecodeParms;
+ int Intent;
+ bool AsyncRead;
+ ref empty_array, filter1_array, parms1_array;
+ uint i;
+ int code;
+
+ make_empty_array(&empty_array, a_readonly);
+ if (dict_find_string(op, "Filter", &pFilter) > 0) {
+ if (!r_is_array(pFilter)) {
+ if (!r_has_type(pFilter, t_name))
+ return_error(e_typecheck);
+ make_array(&filter1_array, a_readonly, 1, pFilter);
+ pFilter = &filter1_array;
+ }
+ } else
+ pFilter = &empty_array;
+ /* If Filter is undefined, ignore DecodeParms. */
+ if (pFilter != &empty_array &&
+ dict_find_string(op, "DecodeParms", &pDecodeParms) > 0
+ ) {
+ if (pFilter == &filter1_array) {
+ make_array(&parms1_array, a_readonly, 1, pDecodeParms);
+ pDecodeParms = &parms1_array;
+ } else if (!r_is_array(pDecodeParms))
+ return_error(e_typecheck);
+ else if (r_size(pFilter) != r_size(pDecodeParms))
+ return_error(e_rangecheck);
+ } else
+ pDecodeParms = 0;
+ for (i = 0; i < r_size(pFilter); ++i) {
+ ref f, fname, dp;
+
+ array_get(pFilter, (long)i, &f);
+ if (!r_has_type(&f, t_name))
+ return_error(e_typecheck);
+ name_string_ref(&f, &fname);
+ if (r_size(&fname) < 6 ||
+ !memcmp(fname.value.bytes + r_size(&fname) - 6, "Decode", 6)
+ )
+ return_error(e_rangecheck);
+ if (pDecodeParms) {
+ array_get(pDecodeParms, (long)i, &dp);
+ if (!(r_has_type(&dp, t_dictionary) || r_has_type(&dp, t_null)))
+ return_error(e_typecheck);
+ }
+ }
+ if ((code = dict_int_param(op, "Intent", 0, 3, 0, &Intent)) < 0 ||
+ (code = dict_bool_param(op, "AsyncRead", false, &AsyncRead)) < 0
+ )
+ return code;
+ push(1);
+ op[-1] = *pFilter;
+ if (pDecodeParms)
+ *op = *pDecodeParms;
+ else
+ make_null(op);
+ return 0;
+}
+
+/* <file|string> <length|null> <CloseSource> .reusablestream <filter> */
+/*
+ * The file|string operand must be a "reusable source", either:
+ * - A string;
+ * - A readable, positionable file stream;
+ * - A SubFileDecode filter with an empty EODString and a reusable
+ * source;
+ * - A reusable stream.
+ */
+private int make_rss(P6(os_ptr op, const byte * data, uint size, long offset,
+ long length, bool close_source));
+private int
+zreusablestream(os_ptr op)
+{
+ os_ptr source_op = op - 2;
+ os_ptr length_op = op - 1;
+ long length;
+ bool close_source;
+ int code;
+
+ if (r_has_type(length_op, t_integer)) {
+ length = length_op->value.intval;
+ if (length < 0)
+ return_error(e_rangecheck);
+ } else
+ length = -1;
+ check_type(*op, t_boolean);
+ close_source = op->value.boolval;
+ if (r_has_type(source_op, t_string)) {
+ check_read(*source_op);
+ code = make_rss(source_op, source_op->value.const_bytes,
+ r_size(source_op), 0L, length, close_source);
+ } else {
+ long offset = 0;
+ stream *source;
+
+ check_read_file(source, source_op);
+rs:
+ if (source->cbuf_string.data != 0) {
+ /* The data source is a string. */
+ long avail;
+
+ offset += stell(source);
+ savailable(source, &avail);
+ if (avail < 0)
+ avail = 0;
+ if (avail > length)
+ avail = length;
+ code = make_rss(source_op, source->cbuf_string.data,
+ source->cbuf_string.size, offset, avail,
+ close_source);
+ } else if (source->file != 0) {
+ /* The data source is a file. */
+/****** NYI ******/
+ } else if (source->state->template == &s_SFD_template) {
+ /* The data source is a SubFileDecode filter. */
+ const stream_SFD_state *const sfd_state =
+ (const stream_SFD_state *)source->state;
+
+ if (sfd_state->eod.size != 0)
+ return_error(e_rangecheck);
+ if (sfd_state->count != 0) {
+ long left = sfd_state->count + sbufavailable(source);
+
+ if (left < length)
+ length = left;
+ }
+ source = source->strm;
+ goto rs;
+ }
+/****** REUSABLE CASE IS NYI ******/
+ else
+ return_error(e_rangecheck);
+ }
+ if (code >= 0)
+ pop(2);
+ return code;
+}
+
+/* Make a reusable string stream. */
+private int
+make_rss(os_ptr op, const byte * data, uint size, long offset,
+ long length, bool close_source)
+{
+/****** NYI ******/
+ return_error(e_rangecheck);
+}
+
+/* ---------------- Initialization procedure ---------------- */
+
+const op_def zfreuse_op_defs[] =
+{
+ {"2.rsdparams", zrsdparams},
+ {"2.reusablestream", zreusablestream},
+ op_def_end(0)
+};
diff --git a/pstoraster/zfunc.c b/pstoraster/zfunc.c
new file mode 100644
index 000000000..d76a25466
--- /dev/null
+++ b/pstoraster/zfunc.c
@@ -0,0 +1,235 @@
+/* Copyright (C) 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Generic PostScript language interface to Functions */
+#include "memory_.h"
+#include "ghost.h"
+#include "oper.h"
+#include "gsfunc.h"
+#include "gsstruct.h"
+#include "ialloc.h"
+#include "idict.h"
+#include "idparam.h"
+#include "ifunc.h"
+#include "store.h"
+
+/* Define the maximum depth of nesting of subsidiary functions. */
+#define MAX_SUB_FUNCTION_DEPTH 3
+
+/* Define the table of build procedures. */
+build_function_proc((*build_function_procs[5])) = {
+ build_function_undefined, build_function_undefined, build_function_undefined,
+ build_function_undefined, build_function_undefined
+};
+
+int
+build_function_undefined(const_os_ptr op, const gs_function_params_t * mnDR,
+ int depth, gs_function_t ** ppfn)
+{
+ return_error(e_rangecheck);
+}
+
+/* GC descriptors */
+gs_private_st_ptr(st_function_ptr, gs_function_t *, "gs_function_t *",
+ function_ptr_enum_ptrs, function_ptr_reloc_ptrs);
+gs_private_st_element(st_function_ptr_element, gs_function_t *,
+ "gs_function_t *[]", function_ptr_element_enum_ptrs,
+ function_ptr_element_reloc_ptrs, st_function_ptr);
+
+/* ------ Operators ------ */
+
+private int zexecfunction(P1(os_ptr op));
+
+/* <dict> .buildfunction <function_struct> */
+private int
+zbuildfunction(os_ptr op)
+{
+ gs_function_t *pfn;
+ ref cref; /* closure */
+ int code;
+
+ code = ialloc_ref_array(&cref, a_executable | a_execute, 2,
+ ".buildfunction");
+ if (code < 0)
+ return code;
+ code = fn_build_function(op, &pfn);
+ if (code < 0) {
+ ifree_ref_array(&cref, ".buildfunction");
+ return code;
+ }
+ make_istruct_new(cref.value.refs, a_executable | a_execute, pfn);
+ make_oper_new(cref.value.refs + 1, 0, zexecfunction);
+ ref_assign(op, &cref);
+ return 0;
+}
+
+/* <in1> ... <function_struct> %execfunction <out1> ... */
+private int
+zexecfunction(os_ptr op)
+{ /*
+ * Since this operator's name begins with %, the name is not defined
+ * in systemdict. The only place this operator can ever appear is
+ * in the execute-only closure created by .buildfunction.
+ * Therefore, in principle it is unnecessary to check the argument.
+ * However, we do a little checking anyway just on general
+ * principles. Note that since the argument may be an instance of
+ * any subclass of gs_function_t, we currently have no way to check
+ * its type.
+ */
+ if (!r_is_struct(op) ||
+ r_has_masked_attrs(op, a_executable | a_execute, a_all)
+ )
+ return_error(e_typecheck);
+ {
+ gs_function_t *pfn = (gs_function_t *) op->value.pstruct;
+ int m = pfn->params.m, n = pfn->params.n;
+ int diff = n - (m + 1);
+
+ if (diff > 0)
+ check_ostack(diff);
+ {
+ float *in = (float *)ialloc_byte_array(m, sizeof(float),
+ "%execfunction(in)");
+ float *out = (float *)ialloc_byte_array(n, sizeof(float),
+ "%execfunction(out)");
+ int code;
+
+ if (in == 0 || out == 0)
+ code = gs_note_error(e_VMerror);
+ else if ((code = float_params(op - 1, m, in)) < 0 ||
+ (code = gs_function_evaluate(pfn, in, out)) < 0
+ )
+ DO_NOTHING;
+ else {
+ if (diff > 0)
+ push(diff); /* can't fail */
+ else if (diff < 0) {
+ pop(-diff);
+ op = osp;
+ }
+ code = make_floats(op + 1 - n, out, n);
+ }
+ ifree_object(out, "%execfunction(out)");
+ ifree_object(in, "%execfunction(in)");
+ return code;
+ }
+ }
+}
+
+/* ------ Procedures ------ */
+
+/* Build a function structure from a PostScript dictionary. */
+int
+fn_build_sub_function(const ref * op, gs_function_t ** ppfn, int depth)
+{
+ int code, type;
+ gs_function_params_t params;
+
+ if (depth > MAX_SUB_FUNCTION_DEPTH)
+ return_error(e_limitcheck);
+ check_type(*op, t_dictionary);
+ code = dict_int_param(op, "FunctionType", 0,
+ countof(build_function_procs) - 1, -1, &type);
+ if (code < 0)
+ return code;
+ /* Collect parameters common to all function types. */
+ params.Domain = 0;
+ params.Range = 0;
+ code = fn_build_float_array(op, "Domain", true, true, &params.Domain);
+ if (code < 0)
+ goto fail;
+ params.m = code >> 1;
+ code = fn_build_float_array(op, "Range", false, true, &params.Range);
+ if (code < 0)
+ goto fail;
+ params.n = code >> 1;
+ /* Finish building the function. */
+ /* If this fails, it will free all the parameters. */
+ return (*build_function_procs[type]) (op, &params, depth + 1, ppfn);
+fail:
+ ifree_object((void *)params.Range, "Range"); /* break const */
+ ifree_object((void *)params.Domain, "Domain"); /* break const */
+ return code;
+}
+
+/* Allocate an array of function objects. */
+int
+ialloc_function_array(uint count, gs_function_t *** pFunctions)
+{
+ gs_function_t **ptr;
+
+ if (count == 0)
+ return_error(e_rangecheck);
+ ptr = ialloc_struct_array(count, gs_function_t *,
+ &st_function_ptr_element, "Functions");
+ if (ptr == 0)
+ return_error(e_VMerror);
+ memset(ptr, 0, sizeof(*ptr) * count);
+ *pFunctions = ptr;
+ return 0;
+}
+
+/*
+ * Collect a heap-allocated array of floats. If the key is missing, set
+ * *pparray = 0 and return 0; otherwise set *pparray and return the number
+ * of elements. Note that 0-length arrays are acceptable, so if the value
+ * returned is 0, the caller must check whether *pparray == 0.
+ */
+int
+fn_build_float_array(const ref * op, const char *kstr, bool required,
+ bool even, const float **pparray)
+{
+ ref *par;
+ int code;
+
+ *pparray = 0;
+ if (dict_find_string(op, kstr, &par) <= 0)
+ return (required ? gs_note_error(e_rangecheck) : 0);
+ if (!r_is_array(par))
+ return_error(e_typecheck);
+ {
+ uint size = r_size(par);
+ float *ptr = (float *)ialloc_byte_array(size, sizeof(float), kstr);
+
+ if (ptr == 0)
+ return_error(e_VMerror);
+ code = dict_float_array_param(op, kstr, size, ptr, NULL);
+ if (code < 0 || (even && (code & 1) != 0)) {
+ ifree_object(ptr, kstr);
+ return(code < 0 ? code : gs_note_error(e_rangecheck));
+ }
+ *pparray = ptr;
+ }
+ return code;
+}
+
+/* ------ Initialization procedure ------ */
+
+const op_def zfunc_op_defs[] =
+{
+ {"1.buildfunction", zbuildfunction},
+ {"1%execfunction", zexecfunction},
+ op_def_end(0)
+};
diff --git a/pstoraster/zfunc0.c b/pstoraster/zfunc0.c
new file mode 100644
index 000000000..56956da51
--- /dev/null
+++ b/pstoraster/zfunc0.c
@@ -0,0 +1,113 @@
+/* Copyright (C) 1997 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* PostScript language interface to FunctionType 0 (Sampled) Functions */
+#include "memory_.h"
+#include "ghost.h"
+#include "oper.h"
+#include "gsdsrc.h"
+#include "gsfunc.h"
+#include "gsfunc0.h"
+#include "stream.h" /* for files.h */
+#include "files.h"
+#include "ialloc.h"
+#include "idict.h"
+#include "idparam.h"
+#include "ifunc.h"
+
+/* Initialization */
+private build_function_proc(build_function_0);
+int
+zfunc0_init(gs_memory_t * mem)
+{
+ build_function_procs[0] = build_function_0;
+ return 0;
+}
+
+const op_def zfunc0_op_defs[] =
+{
+ op_def_end(zfunc0_init)
+};
+
+/* Finish building a FunctionType 0 (Sampled) function. */
+private int
+build_function_0(const_os_ptr op, const gs_function_params_t * mnDR, int depth,
+ gs_function_t ** ppfn)
+{
+ gs_function_Sd_params_t params;
+ ref *pDataSource;
+ int code;
+
+ *(gs_function_params_t *) & params = *mnDR;
+ params.Encode = 0;
+ params.Decode = 0;
+ params.Size = 0;
+ if ((code = dict_find_string(op, "DataSource", &pDataSource)) <= 0)
+ return (code < 0 ? code : gs_note_error(e_rangecheck));
+ switch (r_type(pDataSource)) {
+ case t_string:
+ data_source_init_string2(&params.DataSource,
+ pDataSource->value.const_bytes,
+ r_size(pDataSource));
+ break;
+ case t_file: {
+ stream *s;
+
+ check_read_known_file_else(s, pDataSource, return_error,
+ return_error(e_invalidfileaccess));
+ if (!(s->modes & s_mode_seek))
+ return_error(e_ioerror);
+ data_source_init_stream(&params.DataSource, s);
+ break;
+ }
+ default:
+ return_error(e_rangecheck);
+ }
+ if ((code = dict_int_param(op, "Order", 1, 3, 1, &params.Order)) < 0 ||
+ (code = dict_int_param(op, "BitsPerSample", 1, 32, 0,
+ &params.BitsPerSample)) < 0 ||
+ ((code = fn_build_float_array(op, "Encode", false, true, &params.Encode)) != 2 * params.m && (code != 0 || params.Encode != 0)) ||
+ ((code = fn_build_float_array(op, "Decode", false, true, &params.Decode)) != 2 * params.n && (code != 0 || params.Decode != 0))
+ ) {
+ goto fail;
+ } {
+ int *ptr = (int *)ialloc_byte_array(params.m, sizeof(int), "Size");
+
+ if (ptr == 0) {
+ code = gs_note_error(e_VMerror);
+ goto fail;
+ }
+ params.Size = ptr;
+ code = dict_int_array_param(op, "Size", params.m, ptr);
+ if (code != params.m)
+ goto fail;
+ }
+ code = gs_function_Sd_init(ppfn, &params, imemory);
+ if (code >= 0)
+ return 0;
+fail:
+ gs_function_Sd_free_params(&params, imemory);
+ return (code < 0 ? code : gs_note_error(e_rangecheck));
+}
diff --git a/pstoraster/zfunc3.c b/pstoraster/zfunc3.c
new file mode 100644
index 000000000..20f5d1840
--- /dev/null
+++ b/pstoraster/zfunc3.c
@@ -0,0 +1,136 @@
+/* Copyright (C) 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* PostScript language interface to LL3 Functions */
+#include "memory_.h"
+#include "ghost.h"
+#include "oper.h"
+#include "gsfunc3.h"
+#include "gsstruct.h"
+#include "stream.h" /* for files.h */
+#include "files.h"
+#include "ialloc.h"
+#include "idict.h"
+#include "idparam.h"
+#include "ifunc.h"
+#include "store.h"
+
+/* Initialization */
+private build_function_proc(build_function_2);
+private build_function_proc(build_function_3);
+int
+zfunc3_init(gs_memory_t * mem)
+{
+ build_function_procs[2] = build_function_2;
+ build_function_procs[3] = build_function_3;
+ return 0;
+}
+
+const op_def zfunc3_op_defs[] =
+{
+ op_def_end(zfunc3_init)
+};
+
+/* Define the available Function types. */
+
+/* Finish building a FunctionType 2 (ExponentialInterpolation) function. */
+private int
+build_function_2(const_os_ptr op, const gs_function_params_t * mnDR, int depth,
+ gs_function_t ** ppfn)
+{
+ gs_function_ElIn_params_t params;
+ int code, n0, n1;
+
+ *(gs_function_params_t *)&params = *mnDR;
+ params.C0 = 0;
+ params.C1 = 0;
+ if ((code = dict_float_param(op, "N", 0.0, &params.N)) != 0 ||
+ (code = n0 = fn_build_float_array(op, "C0", false, false, &params.C0)) < 0 ||
+ (code = n1 = fn_build_float_array(op, "C1", false, false, &params.C1)) < 0
+ )
+ goto fail;
+ if (params.C0 == 0)
+ n0 = 1; /* C0 defaulted */
+ if (params.C1 == 0)
+ n1 = 1; /* C1 defaulted */
+ if (params.Range == 0)
+ params.n = n0; /* either one will do */
+ if (n0 != n1 || n0 != params.n)
+ goto fail;
+ code = gs_function_ElIn_init(ppfn, &params, imemory);
+ if (code >= 0)
+ return 0;
+fail:
+ gs_function_ElIn_free_params(&params, imemory);
+ return (code < 0 ? code : gs_note_error(e_rangecheck));
+}
+
+/* Finish building a FunctionType 3 (1-Input Stitching) function. */
+private int
+build_function_3(const_os_ptr op, const gs_function_params_t * mnDR, int depth,
+ gs_function_t ** ppfn)
+{
+ gs_function_1ItSg_params_t params;
+ int code;
+
+ *(gs_function_params_t *) & params = *mnDR;
+ params.Functions = 0;
+ params.Bounds = 0;
+ params.Encode = 0;
+ {
+ ref *pFunctions;
+ gs_function_t **ptr;
+ int i;
+
+ if ((code = dict_find_string(op, "Functions", &pFunctions)) <= 0)
+ return (code < 0 ? code : gs_note_error(e_rangecheck));
+ check_array_only(*pFunctions);
+ params.k = r_size(pFunctions);
+ code = ialloc_function_array(params.k, &ptr);
+ if (code < 0)
+ return code;
+ params.Functions = (const gs_function_t * const *)ptr;
+ for (i = 0; i < params.k; ++i) {
+ ref subfn;
+
+ array_get(pFunctions, (long)i, &subfn);
+ code = fn_build_sub_function(&subfn, &ptr[i], depth);
+ if (code < 0)
+ goto fail;
+ }
+ }
+ if ((code = fn_build_float_array(op, "Bounds", true, false, &params.Bounds)) != params.k - 1 ||
+ (code = fn_build_float_array(op, "Encode", true, true, &params.Encode)) != 2 * params.k
+ )
+ goto fail;
+ if (params.Range == 0)
+ params.n = params.Functions[0]->params.n;
+ code = gs_function_1ItSg_init(ppfn, &params, imemory);
+ if (code >= 0)
+ return 0;
+fail:
+ gs_function_1ItSg_free_params(&params, imemory);
+ return (code < 0 ? code : gs_note_error(e_rangecheck));
+}
diff --git a/pstoraster/zfzlib.c b/pstoraster/zfzlib.c
new file mode 100644
index 000000000..0002671d3
--- /dev/null
+++ b/pstoraster/zfzlib.c
@@ -0,0 +1,105 @@
+/*
+ Copyright 1993-2000 by Easy Software Products
+ Copyright 1995, 1996, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+#include <config.h>
+#ifdef HAVE_LIBZ
+/*$Id$ */
+/* zlib and Flate filter creation */
+#include "ghost.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> zlibEncode/filter <file> */
+private int
+zzlibE(os_ptr op)
+{
+ stream_zlib_state zls;
+
+ (*s_zlibE_template.set_defaults)((stream_state *)&zls);
+ return filter_write(op, 0, &s_zlibE_template, (stream_state *)&zls, 0);
+}
+
+/* <target> zlibDecode/filter <file> */
+/* <target> <dict> zlibDecode/filter <file> */
+private int
+zzlibD(os_ptr op)
+{
+ stream_zlib_state zls;
+
+ (*s_zlibD_template.set_defaults)((stream_state *)&zls);
+ return filter_read(op, 0, &s_zlibD_template, (stream_state *)&zls, 0);
+}
+
+/* <source> FlateEncode/filter <file> */
+/* <source> <dict> FlateEncode/filter <file> */
+private int
+zFlateE(os_ptr op)
+{
+ stream_zlib_state zls;
+
+ (*s_zlibE_template.set_defaults)((stream_state *)&zls);
+ return filter_write_predictor(op, 0, &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;
+
+ (*s_zlibD_template.set_defaults)((stream_state *)&zls);
+ return filter_read_predictor(op, 0, &s_zlibD_template,
+ (stream_state *)&zls);
+}
+
+/* ------ Initialization procedure ------ */
+
+const op_def zfzlib_op_defs[] =
+{
+ op_def_begin_filter(),
+ {"1zlibEncode", zzlibE},
+ {"1zlibDecode", zzlibD},
+ {"1FlateEncode", zFlateE},
+ {"1FlateDecode", zFlateD},
+ op_def_end(0)
+};
+#endif /* HAVE_LIBZ */
diff --git a/pstoraster/zgeneric.c b/pstoraster/zgeneric.c
new file mode 100644
index 000000000..c56c7f220
--- /dev/null
+++ b/pstoraster/zgeneric.c
@@ -0,0 +1,528 @@
+/* Copyright (C) 1989, 1992, 1993, 1994, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Array/string/dictionary generic operators for PostScript */
+#include "memory_.h"
+#include "ghost.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_integer(P1(os_ptr));
+private int zcopy_interval(P1(os_ptr));
+private int copy_interval(P4(os_ptr, uint, os_ptr, client_name_t));
+
+/* <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). */
+int
+zcopy(register os_ptr op)
+{
+ int type = r_type(op);
+
+ if (type == t_integer)
+ return zcopy_integer(op);
+ check_op(2);
+ switch (type) {
+ case t_array:
+ case t_string:
+ return zcopy_interval(op);
+ case t_dictionary:
+ return zcopy_dict(op);
+ default:
+ 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;
+ es_ptr ep = esp;
+ es_ptr cproc = ep + 4;
+
+ check_estack(6);
+ 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_int(cproc, dict_first(obj));
+ ++cproc;
+ make_op_estack(cproc, dict_continue);
+ 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 procedure;
+ * - the iteration index (only for dictionaries, done above);
+ * and invoke the continuation operator.
+ */
+ make_mark_estack(ep + 1, es_for, forall_cleanup);
+ ep[2] = *obj;
+ ep[3] = *op;
+ esp = cproc - 1;
+ pop(2);
+ op -= 2;
+ return (*real_opproc(cproc))(op);
+}
+/* Continuation operator for arrays */
+private int
+array_continue(register os_ptr op)
+{
+ es_ptr obj = esp - 1;
+
+ if (r_size(obj)) { /* continue */
+ push(1);
+ r_dec_size(obj, 1);
+ *op = *obj->value.refs;
+ obj->value.refs++;
+ esp += 2;
+ *esp = obj[1];
+ return o_push_estack;
+ } else { /* done */
+ esp -= 3; /* pop mark, object, 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->value.intval;
+
+ push(2); /* make room for key and value */
+ if ((index = dict_next(obj, index, op - 1)) >= 0) { /* continue */
+ esp->value.intval = index;
+ esp += 2;
+ *esp = obj[1];
+ return o_push_estack;
+ } else { /* done */
+ pop(2); /* undo push */
+ esp -= 4; /* pop mark, object, proc, index */
+ return o_pop_estack;
+ }
+}
+/* Continuation operator for strings */
+private int
+string_continue(register os_ptr op)
+{
+ es_ptr obj = esp - 1;
+
+ 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[1];
+ return o_push_estack;
+ } else { /* done */
+ esp -= 3; /* pop mark, object, proc */
+ return o_pop_estack;
+ }
+}
+/* Continuation operator for packed arrays */
+private int
+packedarray_continue(register os_ptr op)
+{
+ es_ptr obj = esp - 1;
+
+ 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[1];
+ return o_push_estack;
+ } else { /* done */
+ esp -= 3; /* pop mark, object, proc */
+ return o_pop_estack;
+ }
+}
+/* Vacuous cleanup procedure */
+private int
+forall_cleanup(os_ptr op)
+{
+ return 0;
+}
+
+/* ------ Initialization procedure ------ */
+
+const op_def 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},
+ op_def_end(0)
+};
+
+/* ------ 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..b9fabe1a4
--- /dev/null
+++ b/pstoraster/zgstate.c
@@ -0,0 +1,450 @@
+/* Copyright (C) 1989, 1995, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Graphics state operators */
+#include "math_.h"
+#include "ghost.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 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;
+
+/* "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)
+{
+ gs_register_struct_root(imemory, NULL, (void **)&igs, "igs");
+ igs = int_gstate_alloc(iimemory);
+}
+gs_state *
+int_gstate_alloc(gs_ref_memory_t * mem)
+{
+ int_gstate *iigs;
+ ref proc0;
+ gs_state *pgs = gs_state_alloc((gs_memory_t *) mem);
+
+ iigs = gs_alloc_struct((gs_memory_t *) mem, int_gstate, &st_int_gstate,
+ "int_gstate_alloc(int_gstate)");
+ int_gstate_map_refs(iigs, make_null);
+ make_empty_array(&iigs->dash_pattern, a_all);
+ gs_alloc_ref_array(mem, &proc0, a_readonly + a_executable, 2,
+ "int_gstate_alloc(proc0)");
+ make_oper(proc0.value.refs, 0, zpop);
+ make_real(proc0.value.refs + 1, 0.0);
+ iigs->black_generation = proc0;
+ iigs->undercolor_removal = proc0;
+ clear_pagedevice(iigs);
+ gs_state_set_client(pgs, iigs, &istate_procs);
+ /* PostScript code wants limit clamping enabled. */
+ gs_setlimitclamp(pgs, true);
+ /*
+ * 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.
+ */
+ return pgs;
+}
+
+/* - 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)
+{
+ /* gs_initgraphics does a setgray; we must clear the interpreter's */
+ /* cached copy of the color space object. */
+ int code = gs_initgraphics(igs);
+
+ if (code >= 0)
+ make_null(&istate->colorspace.array);
+ return code;
+}
+
+/* ------ Operations on graphics state elements ------ */
+
+/* <num> setlinewidth - */
+private int
+zsetlinewidth(register os_ptr op)
+{ /*
+ * The Red Book doesn't say anything about this, but Adobe
+ * interpreters return (or perhaps store) the absolute value
+ * of the width.
+ */
+ double width;
+ int code = real_param(op, &width);
+
+ if (code < 0)
+ return_op_typecheck(op);
+ code = gs_setlinewidth(igs, fabs(width));
+ if (code >= 0)
+ pop(1);
+ return code;
+}
+
+/* - 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, max_int, &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, max_int, &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;
+ double 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 = float_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 ------ */
+
+/* <bool> .setaccuratecurves - */
+private int
+zsetaccuratecurves(register os_ptr op)
+{
+ check_type(*op, t_boolean);
+ gs_setaccuratecurves(igs, op->value.boolval);
+ pop(1);
+ return 0;
+}
+
+/* - .currentaccuratecurves <bool> */
+private int
+zcurrentaccuratecurves(register os_ptr op)
+{
+ push(1);
+ make_bool(op, gs_currentaccuratecurves(igs));
+ return 0;
+}
+
+/* <adjust.x> <adjust.y> .setfilladjust2 - */
+private int
+zsetfilladjust2(register os_ptr op)
+{
+ double 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;
+}
+
+/* <bool> .setdashadapt - */
+private int
+zsetdashadapt(register os_ptr op)
+{
+ check_type(*op, t_boolean);
+ gs_setdashadapt(igs, op->value.boolval);
+ pop(1);
+ return 0;
+}
+
+/* - .currentdashadapt <bool> */
+private int
+zcurrentdashadapt(register os_ptr op)
+{
+ push(1);
+ make_bool(op, gs_currentdashadapt(igs));
+ return 0;
+}
+
+/* <num> <bool> .setdotlength - */
+private int
+zsetdotlength(register os_ptr op)
+{
+ double length;
+ int code = real_param(op - 1, &length);
+
+ if (code < 0)
+ return code;
+ check_type(*op, t_boolean);
+ code = gs_setdotlength(igs, length, op->value.boolval);
+ if (code < 0)
+ return code;
+ pop(2);
+ return 0;
+}
+
+/* - .currentdotlength <num> <bool> */
+private int
+zcurrentdotlength(register os_ptr op)
+{
+ push(2);
+ make_real(op - 1, gs_currentdotlength(igs));
+ make_bool(op, gs_currentdotlength_absolute(igs));
+ return 0;
+}
+
+/* ------ Initialization procedure ------ */
+
+const op_def zgstate_op_defs[] =
+{
+ {"0.currentaccuratecurves", zcurrentaccuratecurves},
+ {"0currentdash", zcurrentdash},
+ {"0.currentdashadapt", zcurrentdashadapt},
+ {"0.currentdotlength", zcurrentdotlength},
+ {"0.currentfilladjust2", zcurrentfilladjust2},
+ {"0currentflat", zcurrentflat},
+ {"0currentlinecap", zcurrentlinecap},
+ {"0currentlinejoin", zcurrentlinejoin},
+ {"0currentlinewidth", zcurrentlinewidth},
+ {"0currentmiterlimit", zcurrentmiterlimit},
+ {"0grestore", zgrestore},
+ {"0grestoreall", zgrestoreall},
+ {"0gsave", zgsave},
+ {"0initgraphics", zinitgraphics},
+ {"1.setaccuratecurves", zsetaccuratecurves},
+ {"2setdash", zsetdash},
+ {"1.setdashadapt", zsetdashadapt},
+ {"2.setdotlength", zsetdotlength},
+ {"2.setfilladjust2", zsetfilladjust2},
+ {"1setflat", zsetflat},
+ {"1.setlinecap", zsetlinecap},
+ {"1.setlinejoin", zsetlinejoin},
+ {"1setlinewidth", zsetlinewidth},
+ {"1setmiterlimit", zsetmiterlimit},
+ op_def_end(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
+num_param(const_os_ptr op, int (*pproc)(P2(gs_state *, floatp)))
+{
+ double 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..cbc5586a4
--- /dev/null
+++ b/pstoraster/zhsb.c
@@ -0,0 +1,68 @@
+/* Copyright (C) 1994, 1997 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* HSB color operators */
+#include "ghost.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_floats(op - 2, par, 3);
+ return 0;
+}
+
+/* <hue> <saturation> <brightness> sethsbcolor - */
+private int
+zsethsbcolor(register os_ptr op)
+{
+ double 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 ------ */
+
+const op_def zhsb_op_defs[] =
+{
+ {"0currenthsbcolor", zcurrenthsbcolor},
+ {"3sethsbcolor", zsethsbcolor},
+ op_def_end(0)
+};
diff --git a/pstoraster/zht.c b/pstoraster/zht.c
new file mode 100644
index 000000000..006d015fc
--- /dev/null
+++ b/pstoraster/zht.c
@@ -0,0 +1,265 @@
+/* Copyright (C) 1989, 1991, 1993, 1994, 1997 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Halftone definition operators */
+#include "ghost.h"
+#include "memory_.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 "iht.h" /* prototypes */
+#include "store.h"
+
+/* Forward references */
+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(P7(os_ptr, const gx_ht_order *, gs_screen_halftone *,
+ ref *, int, int (*)(P1(os_ptr)), gs_memory_t *));
+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);
+ gs_memory_t *mem;
+
+ if (code < 0)
+ return code;
+ mem = (gs_memory_t *)idmemory->spaces.indexed[r_space_index(op)];
+ /*
+ * Allocate the halftone in the same VM space as the procedure.
+ * This keeps the space relationships consistent.
+ */
+ code = gs_screen_order_init_memory(&order, igs, &screen,
+ gs_currentaccuratescreens(), mem);
+ if (code < 0)
+ return code;
+ return zscreen_enum_init(op, &order, &screen, op, 3,
+ setscreen_finish, mem);
+}
+/* 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_memory_t * mem)
+{
+ 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_memory(penum, porder, igs, psp, mem);
+ 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)
+{
+ double value;
+ int code = real_param(op, &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)
+{
+ double 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 ------ */
+
+const op_def 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},
+ op_def_end(0)
+};
diff --git a/pstoraster/zht1.c b/pstoraster/zht1.c
new file mode 100644
index 000000000..72322093d
--- /dev/null
+++ b/pstoraster/zht1.c
@@ -0,0 +1,156 @@
+/* Copyright (C) 1994, 1997 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* setcolorscreen operator */
+#include "ghost.h"
+#include "memory_.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 "iht.h"
+#include "store.h"
+
+/* 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;
+ int space = 0;
+ gs_memory_t *mem;
+
+ 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;
+ space = max(space, r_space_index(op1));
+ }
+ mem = (gs_memory_t *)idmemory->spaces.indexed[space];
+ check_estack(8); /* for sampling screens */
+ rc_alloc_struct_0(pht, gs_halftone, &st_halftone,
+ mem, pht = 0, "setcolorscreen(halftone)");
+ rc_alloc_struct_0(pdht, gx_device_halftone, &st_device_halftone,
+ mem, pdht = 0, "setcolorscreen(device halftone)");
+ 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, mem);
+ if (code < 0) {
+ esp = esp0;
+ break;
+ }
+ }
+ }
+ if (code < 0) {
+ gs_free_object(mem, pdht, "setcolorscreen(device halftone)");
+ gs_free_object(mem, pht, "setcolorscreen(halftone)");
+ 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)
+{
+ gs_halftone *pht = r_ptr(esp + 6, gs_halftone);
+ gx_device_halftone *pdht = r_ptr(esp + 7, gx_device_halftone);
+
+ gs_free_object(pdht->rc.memory, pdht,
+ "setcolorscreen_cleanup(device halftone)");
+ gs_free_object(pht->rc.memory, pht,
+ "setcolorscreen_cleanup(halftone)");
+ return 0;
+}
+
+/* ------ Initialization procedure ------ */
+
+const op_def zht1_op_defs[] =
+{
+ {"<setcolorscreen", zsetcolorscreen},
+ /* Internal operators */
+ {"0%setcolorscreen_finish", setcolorscreen_finish},
+ op_def_end(0)
+};
diff --git a/pstoraster/zht2.c b/pstoraster/zht2.c
new file mode 100644
index 000000000..9eeee0ed2
--- /dev/null
+++ b/pstoraster/zht2.c
@@ -0,0 +1,357 @@
+/* Copyright (C) 1992, 1995, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Level 2 sethalftone operator */
+#include "ghost.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 "iht.h"
+#include "store.h"
+
+/* 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 *const color_names[] = {
+ gs_ht_separation_name_strings
+ };
+ ref sprocs[countof(color_names)];
+ ref tprocs[countof(color_names)];
+ gs_memory_t *mem;
+ 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);
+ }
+ mem = (gs_memory_t *) idmemory->spaces.indexed[r_space_index(op - 1)];
+ check_estack(5); /* for sampling Type 1 screens */
+ refset_null(sprocs, countof(sprocs));
+ refset_null(tprocs, countof(tprocs));
+ rc_alloc_struct_0(pht, gs_halftone, &st_halftone,
+ imemory, pht = 0, ".sethalftone5");
+ phtc = gs_alloc_struct_array(mem, count, gs_halftone_component,
+ &st_ht_component_element,
+ ".sethalftone5");
+ rc_alloc_struct_0(pdht, gx_device_halftone, &st_device_halftone,
+ imemory, pdht = 0, ".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 = (gs_ht_separation_name) 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, mem);
+ 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) {
+ gs_free_object(mem, pdht, ".sethalftone5");
+ gs_free_object(mem, phtc, ".sethalftone5");
+ gs_free_object(mem, 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)
+{
+ gx_device_halftone *pdht = r_ptr(&esp[4], gx_device_halftone);
+ gs_halftone *pht = r_ptr(&esp[3], gs_halftone);
+
+ gs_free_object(pdht->rc.memory, pdht,
+ "sethalftone_cleanup(device halftone)");
+ gs_free_object(pht->rc.memory, pht,
+ "sethalftone_cleanup(halftone)");
+ return 0;
+}
+
+/* ------ Initialization procedure ------ */
+
+const op_def zht2_l2_op_defs[] =
+{
+ op_def_begin_level2(),
+ {"2.sethalftone5", zsethalftone5},
+ /* Internal operators */
+ {"0%sethalftone_finish", sethalftone_finish},
+ op_def_end(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);
+ psp->transfer_closure.proc = 0;
+ psp->transfer_closure.data = 0;
+ return 0;
+}
+
+/* Set actual frequency and angle in a dictionary. */
+private int
+dict_real_result(ref * pdict, const char *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);
+ ptp->transfer_closure.proc = 0;
+ ptp->transfer_closure.data = 0;
+ return 0;
+}
diff --git a/pstoraster/zimage.c b/pstoraster/zimage.c
new file mode 100644
index 000000000..4a1fe20bf
--- /dev/null
+++ b/pstoraster/zimage.c
@@ -0,0 +1,490 @@
+/* Copyright (C) 1989, 1995, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Image operators */
+#include "ghost.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 "gxiparam.h"
+#include "stream.h"
+#include "ifilter.h" /* for stream exception handling */
+#include "iimage.h"
+
+/* Forward references */
+private int image_setup(P4(gs_image_t * pim, 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));
+
+/* <width> <height> <bits/sample> <matrix> <datasrc> image - */
+int
+zimage(register os_ptr op)
+{
+ return zimage_opaque_setup(op, false, gs_image_alpha_none,
+ gs_cspace_DeviceGray((const gs_imager_state *)igs), 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, op, NULL, 5);
+}
+
+/* Common setup for [color|alpha]image. */
+/* Fills in format, BitsPerComponent, Alpha. */
+int
+zimage_opaque_setup(os_ptr op, bool multi, gs_image_alpha_t alpha,
+ 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(&image, pcs);
+ image.BitsPerComponent = (int)op[-2].value.intval;
+ image.Alpha = alpha;
+ image.format =
+ (multi ? gs_image_format_component_planar : gs_image_format_chunky);
+ return image_setup(&image, op, pcs, npop);
+}
+
+/* Common setup for [color|alpha]image and imagemask. */
+/* Fills in Width, Height, ImageMatrix, ColorSpace. */
+private int
+image_setup(gs_image_t * pim, 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((gs_pixel_image_t *) pim, op,
+ pim->ImageMask | pim->CombineWithColor, npop);
+}
+
+/* Common setup for all Level 1 and 2 images, and ImageType 4 images. */
+int
+zimage_setup(const gs_pixel_image_t * pim, const ref * sources,
+ bool uses_color, int npop)
+{
+ gx_image_enum_common_t *pie;
+ int code =
+ gs_image_begin_typed((const gs_image_common_t *)pim, igs,
+ uses_color, &pie);
+
+ if (code < 0)
+ return code;
+ return zimage_data_setup((const gs_pixel_image_t *)pim, pie,
+ sources, npop);
+}
+
+/* Common setup for all Level 1 and 2 images, and ImageType 3 and 4 images. */
+int
+zimage_data_setup(const gs_pixel_image_t * pim, gx_image_enum_common_t * pie,
+ const ref * sources, int npop)
+{
+ int num_sources = pie->num_planes;
+
+#define NUM_PUSH(nsource) ((nsource) * 2 + 4) /* see below */
+ int inumpush = NUM_PUSH(num_sources);
+ int code;
+ gs_image_enum *penum;
+ int px;
+ const ref *pp;
+ bool must_buffer = false;
+
+ /*
+ * We push the following on the estack.
+ * Control mark,
+ * num_sources times (plane N-1 first, plane 0 last):
+ * row buffers (only if must_buffer, otherwise null),
+ * data sources,
+ * current plane index,
+ * current byte in row (only if must_buffer, otherwise 0),
+ * enumeration structure.
+ */
+ 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. This is OK even if
+ * some of the sources are filters on the same file, since they
+ * have separate 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_common_init(penum, pie, (const gs_data_image_t *)pim,
+ imemory, gs_currentdevice(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 + num_sources - 1;
+ px < num_sources; esp += 2, ++px, --pp
+ ) {
+ make_null(esp); /* buffer */
+ esp[1] = *pp;
+ }
+ esp += 2;
+ 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. */
+ for (px = 0; px < num_sources; ++px) {
+ uint size = gs_image_bytes_per_plane_row(penum, px);
+ byte *sbody = ialloc_string(size, "image_setup");
+
+ if (sbody == 0) {
+ esp -= inumpush;
+ image_cleanup(osp);
+ return_error(e_VMerror);
+ }
+ make_string(esp - 4 - px * 2,
+ 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;
+}
+/* Pop all the control information off the e-stack. */
+private es_ptr
+zimage_pop_estack(es_ptr tep)
+{
+ es_ptr ep = tep - 3;
+
+ while (!r_is_estack_mark(ep))
+ ep -= 2;
+ return ep - 1;
+}
+/* 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 *pp;
+
+ if (!r_has_type_attrs(op, t_string, a_read)) {
+ check_op(1);
+ /* Procedure didn't return a (readable) string. Quit. */
+ esp = zimage_pop_estack(esp);
+ 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 = zimage_pop_estack(esp);
+ pop(1);
+ op = osp;
+ image_cleanup(op);
+ return (code < 0 ? code : o_pop_estack);
+ }
+ pop(1);
+ px = (int)(esp[-2].value.intval) + 1;
+ pp = esp - 3 - px * 2;
+ if (r_is_estack_mark(pp))
+ px = 0, pp = esp - 3;
+ esp[-2].value.intval = px;
+ push_op_estack(image_proc_continue);
+ *++esp = *pp;
+ 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 - 3;
+
+ 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; !r_is_estack_mark(pp);
+ ++pn, pp -= 2
+ ) {
+ 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 */
+ }
+ /* Note that in the EOF case, we can get here with */
+ /* avail < min_left. */
+ if (avail >= min_left) {
+ avail -= min_left;
+ if (avail < size)
+ size = avail;
+ } else
+ size = 0;
+ }
+
+ /* 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 -= 2
+ )
+ 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 -= 2)
+ sbufskip(pp->value.pfile, used);
+ }
+ if (code) {
+ esp = zimage_pop_estack(esp);
+ 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 - 3;
+ int px = esp[-2].value.intval;
+ int dpos = esp[-1].value.intval;
+ int code = 0;
+
+ while (!code) {
+ const ref *pp;
+
+ /****** 0 IS BOGUS ******/
+ uint size = gs_image_bytes_per_plane_row(penum, 0);
+ uint avail = size;
+ uint used;
+ int pi;
+
+ /* Accumulate data until we have a full set of planes. */
+ while (!r_is_estack_mark(pp = pproc - px * 2)) {
+ const ref *pb = pp - 1;
+ 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; pi < px && !code; ++pi, pp -= 2)
+ code = gs_image_next(penum, pp->value.bytes, avail, &used);
+ /* Reinitialize for the next row. */
+ px = dpos = 0;
+ }
+ esp = zimage_pop_estack(esp);
+ 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 - 3 - px * 2;
+ 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 -= NUM_PUSH(num_sources);
+ 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;
+ const ref *pb;
+
+ /* Free any row buffers, in LIFO order as usual. */
+ for (pb = esp + 2; !r_has_type(pb, t_integer); pb += 2)
+ if (r_has_type(pb, t_string))
+ gs_free_string(imemory, pb->value.bytes, r_size(pb),
+ "image_cleanup");
+ penum = r_ptr(pb + 2, gs_image_enum);
+ gs_image_cleanup(penum);
+ ifree_object(penum, "image_cleanup");
+ return 0;
+}
+
+/* ------ Initialization procedure ------ */
+
+const op_def zimage_op_defs[] =
+{
+ {"5image", zimage},
+ {"5imagemask", zimagemask},
+ /* Internal operators */
+ {"1%image_proc_continue", image_proc_continue},
+ {"0%image_file_continue", image_file_continue},
+ op_def_end(0)
+};
diff --git a/pstoraster/zimage2.c b/pstoraster/zimage2.c
new file mode 100644
index 000000000..ca30f01cf
--- /dev/null
+++ b/pstoraster/zimage2.c
@@ -0,0 +1,158 @@
+/* Copyright (C) 1992, 1995, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* image operator extensions for Level 2 PostScript */
+#include "math_.h"
+#include "memory_.h"
+#include "ghost.h"
+#include "oper.h"
+#include "gscolor.h"
+#include "gscspace.h"
+#include "gscolor2.h"
+#include "gsmatrix.h"
+#include "gsimage.h"
+#include "gxfixed.h"
+#include "idict.h"
+#include "idparam.h"
+#include "iimage.h"
+#include "iimage2.h"
+#include "ilevel.h"
+#include "igstate.h" /* for igs */
+
+/* Extract and check the parameters for a gs_data_image_t. */
+int
+data_image_params(const ref * op, gs_data_image_t * pim, image_params * pip,
+ bool require_DataSource, int num_components, int max_bits_per_component)
+{
+ int code;
+ int decode_size;
+ ref *pds;
+
+ check_type(*op, t_dictionary);
+ check_dict_read(*op);
+ if ((code = dict_int_param(op, "Width", 0, max_int_in_fixed / 2,
+ -1, &pim->Width)) < 0 ||
+ (code = dict_int_param(op, "Height", 0, max_int_in_fixed / 2,
+ -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", 1,
+ 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
+ )
+ return code;
+ if (decode_size != num_components * 2)
+ return_error(e_rangecheck);
+ pip->pDecode = &pim->Decode[0];
+ /* Extract and check the data sources. */
+ if ((code = dict_find_string(op, "DataSource", &pds)) <= 0) {
+ if (require_DataSource)
+ return (code < 0 ? code : gs_note_error(e_rangecheck));
+ return 1; /* no data source */
+ }
+ 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;
+ return 0;
+}
+
+/* Extract and check the parameters for a gs_pixel_image_t. */
+int
+pixel_image_params(const ref * op, gs_pixel_image_t * pim, image_params * pip,
+ int max_bits_per_component)
+{
+ int num_components =
+ gs_color_space_num_components(gs_currentcolorspace(igs));
+ int code;
+
+ if (num_components < 1)
+ return_error(e_rangecheck); /* Pattern space not allowed */
+ pim->ColorSpace = gs_currentcolorspace(igs);
+ code = data_image_params(op, (gs_data_image_t *) pim, pip, true,
+ num_components, max_bits_per_component);
+ if (code < 0)
+ return code;
+ pim->format =
+ (pip->MultipleDataSources ? gs_image_format_component_planar :
+ gs_image_format_chunky);
+ return dict_bool_param(op, "CombineWithColor", false,
+ &pim->CombineWithColor);
+}
+
+/* <dict> .image1 - */
+private int
+zimage1(register os_ptr op)
+{
+ gs_image_t image;
+ image_params ip;
+ int code;
+
+ gs_image_t_init(&image, gs_currentcolorspace(igs));
+ code = pixel_image_params(op, (gs_pixel_image_t *) & image, &ip, 12);
+ if (code < 0)
+ return code;
+ return zimage_setup((gs_pixel_image_t *) & image, &ip.DataSource[0],
+ image.CombineWithColor, 1);
+}
+
+/* <dict> .imagemask1 - */
+private int
+zimagemask1(register os_ptr op)
+{
+ gs_image_t image;
+ image_params ip;
+ int code;
+
+ gs_image_t_init_mask(&image, false);
+ code = data_image_params(op, (gs_data_image_t *) & image, &ip, true, 1, 1);
+ if (code < 0)
+ return code;
+ if (ip.MultipleDataSources)
+ return_error(e_rangecheck);
+ return zimage_setup((gs_pixel_image_t *) & image, &ip.DataSource[0],
+ true, 1);
+}
+
+/* ------ Initialization procedure ------ */
+
+const op_def zimage2_l2_op_defs[] =
+{
+ op_def_begin_level2(),
+ {"1.image1", zimage1},
+ {"1.imagemask1", zimagemask1},
+ op_def_end(0)
+};
diff --git a/pstoraster/zimage3.c b/pstoraster/zimage3.c
new file mode 100644
index 000000000..41cb29170
--- /dev/null
+++ b/pstoraster/zimage3.c
@@ -0,0 +1,139 @@
+/* Copyright (C) 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* LanguageLevel 3 ImageTypes (3 & 4 - masked images) */
+#include "memory_.h"
+#include "ghost.h"
+#include "oper.h"
+#include "gscspace.h" /* for gscolor2.h */
+#include "gscolor2.h"
+#include "gsiparm3.h"
+#include "gsiparm4.h"
+#include "gxiparam.h" /* for image enumerator */
+#include "idict.h"
+#include "idparam.h"
+#include "igstate.h"
+#include "iimage.h"
+#include "iimage2.h"
+
+/* <dict> .image3 - */
+private int
+zimage3(register os_ptr op)
+{
+ gs_image3_t image;
+ int interleave_type;
+ ref *pDataDict;
+ ref *pMaskDict;
+ image_params ip_data, ip_mask;
+ int ignored;
+ int code, mcode;
+
+ check_type(*op, t_dictionary);
+ check_dict_read(*op);
+ if ((code = dict_int_param(op, "InterleaveType", 1, 3, -1,
+ &interleave_type)) < 0
+ )
+ return code;
+ gs_image3_t_init(&image, NULL, interleave_type);
+ if (dict_find_string(op, "DataDict", &pDataDict) <= 0 ||
+ dict_find_string(op, "MaskDict", &pMaskDict) <= 0
+ )
+ return_error(e_rangecheck);
+ if ((code = pixel_image_params(pDataDict, (gs_pixel_image_t *)&image,
+ &ip_data, 12)) < 0 ||
+ (mcode = code = data_image_params(pMaskDict, &image.MaskDict, &ip_mask, false, 1, 12)) < 0 ||
+ (code = dict_int_param(pDataDict, "ImageType", 1, 1, 0, &ignored)) < 0 ||
+ (code = dict_int_param(pMaskDict, "ImageType", 1, 1, 0, &ignored)) < 0
+ )
+ return code;
+ /*
+ * MaskDict must have a DataSource iff InterleaveType == 3.
+ */
+ if ((ip_data.MultipleDataSources && interleave_type != 3) ||
+ ip_mask.MultipleDataSources ||
+ mcode != (image.InterleaveType != 3)
+ )
+ return_error(e_rangecheck);
+ if (!mcode) {
+ /* Insert the mask DataSource before the data DataSources. */
+ memmove(&ip_data.DataSource[1], &ip_data.DataSource[0],
+ (countof(ip_data.DataSource) - 1) *
+ sizeof(ip_data.DataSource[0]));
+ ip_data.DataSource[0] = ip_mask.DataSource[0];
+ }
+ return zimage_setup((gs_pixel_image_t *)&image,
+ &ip_data.DataSource[0],
+ image.CombineWithColor, 1);
+}
+
+/* <dict> .image4 - */
+private int
+zimage4(register os_ptr op)
+{
+ gs_image4_t image;
+ image_params ip;
+ int num_components =
+ gs_color_space_num_components(gs_currentcolorspace(igs));
+ int colors[countof(image.MaskColor)];
+ int code;
+ int i;
+
+ gs_image4_t_init(&image, NULL);
+ code = pixel_image_params(op, (gs_pixel_image_t *)&image, &ip, 12);
+ if (code < 0)
+ return code;
+ code = dict_int_array_param(op, "MaskColor", num_components * 2,
+ colors);
+ /* Clamp the color values to the unsigned range. */
+ if (code == num_components) {
+ image.MaskColor_is_range = false;
+ for (i = 0; i < code; ++i)
+ image.MaskColor[i] = (colors[i] < 0 ? ~(uint)0 : colors[i]);
+ }
+ else if (code == num_components * 2) {
+ image.MaskColor_is_range = true;
+ for (i = 0; i < code; i += 2) {
+ if (colors[i+1] < 0) /* no match possible */
+ image.MaskColor[i] = 1, image.MaskColor[i+1] = 0;
+ else {
+ image.MaskColor[i+1] = colors[i+1];
+ image.MaskColor[i] = max(colors[i], 0);
+ }
+ }
+ } else
+ return_error(code < 0 ? code : gs_note_error(e_rangecheck));
+ return zimage_setup((gs_pixel_image_t *)&image, &ip.DataSource[0],
+ image.CombineWithColor, 1);
+}
+
+/* ------ Initialization procedure ------ */
+
+const op_def zimage3_op_defs[] =
+{
+ op_def_begin_ll3(),
+ {"1.image3", zimage3},
+ {"1.image4", zimage4},
+ op_def_end(0)
+};
diff --git a/pstoraster/ziodev.c b/pstoraster/ziodev.c
new file mode 100644
index 000000000..b506d44e1
--- /dev/null
+++ b/pstoraster/ziodev.c
@@ -0,0 +1,475 @@
+/* Copyright (C) 1993, 1995, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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 "oper.h"
+#include "stream.h"
+#include "ialloc.h"
+#include "iscan.h"
+#include "ivmspace.h"
+#include "gxiodev.h" /* must come after stream.h */
+ /* and before files.h */
+#include "files.h"
+#include "scanchar.h" /* for char_EOL */
+#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\
+ }\
+}
+
+/* Export the stdio refs for switching contexts. */
+ref ref_stdio[3];
+
+#define STDIN_BUF_SIZE 128
+/*#define ref_stdin ref_stdio[0] *//* in files.h */
+bool gs_stdin_is_interactive; /* exported for command line only */
+private iodev_proc_init(stdin_init);
+private iodev_proc_open_device(stdin_open);
+const gx_io_device gs_iodev_stdin =
+ iodev_special("%stdin%", stdin_init, stdin_open);
+
+#define STDOUT_BUF_SIZE 128
+/*#define ref_stdout ref_stdio[1] *//* in files.h */
+private iodev_proc_init(stdout_init);
+private iodev_proc_open_device(stdout_open);
+const gx_io_device gs_iodev_stdout =
+ iodev_special("%stdout%", stdout_init, stdout_open);
+
+#define STDERR_BUF_SIZE 128
+/*#define ref_stderr ref_stdio[2] *//* in files.h */
+private iodev_proc_init(stderr_init);
+private iodev_proc_open_device(stderr_open);
+const 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);
+const gx_io_device gs_iodev_lineedit =
+ iodev_special("%lineedit%", iodev_no_init, lineedit_open);
+
+#define STATEMENTEDIT_BUF_SIZE 50 /* initial size, not fixed size */
+private iodev_proc_open_device(statementedit_open);
+const 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 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 void *const 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, NULL, (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_invalid(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;
+ gx_io_device *iodev;
+
+ if (file_is_valid(s, &ref_stdin)) {
+ *ps = s;
+ return 0;
+ }
+ iodev = gs_findiodevice((const byte *)"%stdin", 6);
+ return (*iodev->procs.open_device)(iodev, "r", ps, imemory_system);
+}
+
+private int
+stdout_init(gx_io_device * iodev, gs_memory_t * mem)
+{
+ static void *const pstdout = &ref_stdout;
+
+ make_file(&ref_stdout, a_all | avm_system, 1, invalid_file_entry);
+ gs_register_ref_root(mem, NULL, (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_invalid(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->procs.flush;
+ 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;
+ gx_io_device *iodev;
+
+ if (file_is_valid(s, &ref_stdout)) {
+ *ps = s;
+ return 0;
+ }
+ iodev = gs_findiodevice((const byte *)"%stdout", 7);
+ return (*iodev->procs.open_device)(iodev, "w", ps, imemory_system);
+}
+
+private int
+stderr_init(gx_io_device * iodev, gs_memory_t * mem)
+{
+ static void *const pstderr = &ref_stderr;
+
+ make_file(&ref_stderr, a_all | avm_system, 1, invalid_file_entry);
+ gs_register_ref_root(mem, NULL, (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_invalid(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->procs.flush;
+ 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;
+ gx_io_device *iodev;
+
+ if (file_is_valid(s, &ref_stderr)) {
+ *ps = s;
+ return 0;
+ }
+ iodev = gs_findiodevice((const byte *)"%stderr", 7);
+ return (*iodev->procs.open_device)(iodev, "w", ps, imemory_system);
+}
+
+/* ------ %lineedit and %statementedit ------ */
+
+private int
+line_collect(gx_io_device * iodev, const char *access, stream ** ps,
+ gs_memory_t * mem, uint initial_buf_size, bool statement)
+{
+ uint count = 0;
+ bool in_eol = false;
+ int code;
+ gx_io_device *indev = gs_findiodevice((const byte *)"%stdin", 6);
+ stream *s;
+ stream *ins;
+ byte *buf;
+ uint buf_size = initial_buf_size;
+
+ if (strcmp(access, "r"))
+ return_error(e_invalidfileaccess);
+ s = file_alloc_stream(mem, "line_collect(stream)");
+ if (s == 0)
+ return_error(e_VMerror);
+ code = (indev->procs.open_device)(indev, access, &ins, mem);
+ if (code < 0)
+ return code;
+ buf = gs_alloc_string(mem, buf_size, "line_collect(buffer)");
+ if (buf == 0)
+ return_error(e_VMerror);
+ rd: /* We have to stop 1 character short of the buffer size, */
+ /* because %statementedit must append an EOL. */
+ code = zreadline_from(ins, buf, buf_size - 1, &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,
+ "line_collect(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, "line_collect(buffer)");
+ return code;
+ }
+ if (statement) {
+ /* If we don't have a complete token, keep going. */
+ stream st;
+ stream *ts = &st;
+ scanner_state state;
+ ref ignore_value;
+ uint depth = ref_stack_count(&o_stack);
+ int code;
+
+ /* Add a terminating EOL. */
+ buf[count++] = char_EOL;
+ sread_string(ts, buf, count);
+sc:
+ scanner_state_init_check(&state, false, true);
+ code = scan_token(ts, &ignore_value, &state);
+ ref_stack_pop_to(&o_stack, depth);
+ switch (code) {
+ case 0: /* read a token */
+ case scan_BOS:
+ goto sc; /* keep going until we run out of data */
+ case scan_Refill:
+ goto rd;
+ case scan_EOF:
+ break;
+ default: /* error */
+ gs_free_string(mem, buf, buf_size, "line_collect(buffer)");
+ return code;
+ }
+ }
+ buf = gs_resize_string(mem, buf, buf_size, count,
+ "line_collect(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
+lineedit_open(gx_io_device * iodev, const char *access, stream ** ps,
+ gs_memory_t * mem)
+{
+ return line_collect(iodev, access, ps, mem,
+ LINEEDIT_BUF_SIZE, false);
+}
+
+private int
+statementedit_open(gx_io_device * iodev, const char *access, stream ** ps,
+ gs_memory_t * mem)
+{
+ return line_collect(iodev, access, ps, mem,
+ STATEMENTEDIT_BUF_SIZE, true);
+}
+
+/* ------ Initialization procedure ------ */
+
+const op_def ziodev_op_defs[] =
+{
+ {"1.getiodevice", zgetiodevice},
+ op_def_end(0)
+};
diff --git a/pstoraster/ziodev2.c b/pstoraster/ziodev2.c
new file mode 100644
index 000000000..d7fb6fe7e
--- /dev/null
+++ b/pstoraster/ziodev2.c
@@ -0,0 +1,148 @@
+/* Copyright (C) 1993, 1994, 1996, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* (Level 2) IODevice operators */
+#include "string_.h"
+#include "ghost.h"
+#include "gp.h"
+#include "oper.h"
+#include "stream.h"
+#include "gxiodev.h"
+#include "dstack.h" /* for systemdict */
+#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);
+const 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. */
+const 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;
+ gs_param_list *const plist = (gs_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);
+ if ((code = gs_getdevparams(iodev, plist)) < 0) {
+ ref_stack_pop(&o_stack, list.count * 2);
+ return code;
+ }
+ 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;
+ gs_param_list *const plist = (gs_param_list *) & list;
+ int code;
+ password system_params_password;
+
+ 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;
+ code = dict_read_password(&system_params_password, systemdict,
+ "SystemParamsPassword");
+ if (code < 0)
+ return code;
+ code = param_check_password(plist, &system_params_password);
+ if (code != 0) {
+ iparam_list_release(&list);
+ return_error(code < 0 ? code : e_invalidaccess);
+ }
+ code = gs_putdevparams(iodev, plist);
+ iparam_list_release(&list);
+ if (code < 0)
+ return code;
+ ref_stack_pop(&o_stack, list.count * 2 + 2);
+ return 0;
+}
+
+/* ------ Initialization procedure ------ */
+
+const op_def ziodev2_l2_op_defs[] =
+{
+ op_def_begin_level2(),
+ {"1.getdevparams", zgetdevparams},
+ {"2.putdevparams", zputdevparams},
+ op_def_end(0)
+};
diff --git a/pstoraster/zmath.c b/pstoraster/zmath.c
new file mode 100644
index 000000000..56165e051
--- /dev/null
+++ b/pstoraster/zmath.c
@@ -0,0 +1,277 @@
+/* Copyright (C) 1989, 1992, 1993, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Mathematical operators */
+#include "math_.h"
+#include "ghost.h"
+#include "gxfarith.h"
+#include "oper.h"
+#include "store.h"
+
+/*
+ * Define the 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.
+ *
+ * The state must be public so that context switching can save and
+ * restore it. (Even though the Red Book doesn't mention this,
+ * we verified with Adobe that this is the case.)
+ */
+long zrand_state;
+
+/* Initialize the random number generator. */
+void
+zrand_state_init(long *pstate)
+{
+ *pstate = 1;
+}
+
+/****** NOTE: none of these operators currently ******/
+/****** check for floating over- or underflow. ******/
+
+/* <num> sqrt <real> */
+private int
+zsqrt(register os_ptr op)
+{
+ double num;
+ int code = real_param(op, &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)
+{
+ double num, result;
+ int code = real_param(op, &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)
+{
+ double num, result;
+ int code = real_param(op, &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)
+{
+ double args[2];
+ double 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)
+{
+ double angle;
+ int code = real_param(op, &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)
+{
+ double angle;
+ int code = real_param(op, &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)
+{
+ double args[2];
+ double 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)
+{
+ double num;
+ int code = real_param(op, &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)
+{
+ double num;
+ int code = real_param(op, &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 */
+ zrand_state = A * (zrand_state % Q) - R * (zrand_state / Q);
+ /* Note that zrand_state cannot be 0 here. */
+ if (zrand_state <= 0)
+ zrand_state += M;
+#undef A
+#undef M
+#undef Q
+#undef R
+ push(1);
+ make_int(op, zrand_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;
+ zrand_state = state;
+ pop(1);
+ return 0;
+}
+
+/* - rrand <int> */
+private int
+zrrand(register os_ptr op)
+{
+ push(1);
+ make_int(op, zrand_state);
+ return 0;
+}
+
+/* ------ Initialization procedure ------ */
+
+const op_def 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},
+ op_def_end(0)
+};
diff --git a/pstoraster/zmatrix.c b/pstoraster/zmatrix.c
new file mode 100644
index 000000000..abf6e6515
--- /dev/null
+++ b/pstoraster/zmatrix.c
@@ -0,0 +1,358 @@
+/* Copyright (C) 1989, 1995, 1997 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Matrix operators */
+#include "ghost.h"
+#include "oper.h"
+#include "igstate.h"
+#include "gsmatrix.h"
+#include "gscoord.h"
+#include "store.h"
+
+/* Forward references */
+private int 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);
+}
+
+/* - .currentmatrix <xx> <xy> <yx> <yy> <tx> <ty> */
+private int
+zcurrentmatrix(register os_ptr op)
+{
+ gs_matrix mat;
+ int code = gs_currentmatrix(igs, &mat);
+
+ if (code < 0)
+ return code;
+ push(6);
+ code = make_floats(op - 5, &mat.xx, 6);
+ if (code < 0)
+ pop(6);
+ return code;
+}
+
+/* <xx> <xy> <yx> <yy> <tx> <ty> .setmatrix - */
+private int
+zsetmatrix(register os_ptr op)
+{
+ gs_matrix mat;
+ int code = float_params(op, 6, &mat.xx);
+
+ if (code < 0)
+ return code;
+ if ((code = gs_setmatrix(igs, &mat)) < 0)
+ return code;
+ pop(6);
+ 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;
+ double 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;
+ double 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;
+ double ang;
+
+ if ((code = real_param(op, &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
+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 *)))
+{
+ double 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 ------ */
+
+const op_def zmatrix_op_defs[] =
+{
+ {"1concat", zconcat},
+ {"2dtransform", zdtransform},
+ {"3concatmatrix", zconcatmatrix},
+ {"0.currentmatrix", zcurrentmatrix},
+ {"1defaultmatrix", zdefaultmatrix},
+ {"2idtransform", zidtransform},
+ {"0initmatrix", zinitmatrix},
+ {"2invertmatrix", zinvertmatrix},
+ {"2itransform", zitransform},
+ {"1rotate", zrotate},
+ {"2scale", zscale},
+ {"6.setmatrix", zsetmatrix},
+ {"1.setdefaultmatrix", zsetdefaultmatrix},
+ {"2transform", ztransform},
+ {"2translate", ztranslate},
+ op_def_end(0)
+};
diff --git a/pstoraster/zmedia2.c b/pstoraster/zmedia2.c
new file mode 100644
index 000000000..ca689cff5
--- /dev/null
+++ b/pstoraster/zmedia2.c
@@ -0,0 +1,468 @@
+/* Copyright (C) 1993, 1995, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Media matching for setpagedevice */
+#include "math_.h"
+#include "memory_.h"
+#include "ghost.h"
+#include "gsmatrix.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(P8(const ref * pvreq, const ref * pvmed,
+ int policy, int orient, bool roll,
+ float *best_mismatch, gs_matrix * pmat,
+ gs_point * pmsize));
+typedef struct match_record_s {
+ ref best_key, match_key;
+ uint priority, no_match_priority;
+} match_record_t;
+private void
+reset_match(match_record_t *match)
+{
+ make_null(&match->best_key);
+ make_null(&match->match_key);
+ match->priority = match->no_match_priority;
+}
+private int
+zmatchmedia(register os_ptr op)
+{
+ os_ptr preq = op - 3;
+ os_ptr pattr = op - 2;
+ os_ptr ppol = op - 1;
+ os_ptr pkeys = op; /* *const */
+ int policy_default;
+ float best_mismatch = (float)max_long; /* adhoc */
+ float mbest = best_mismatch;
+ match_record_t match;
+ ref no_priority;
+ ref *ppriority;
+ int mepos, orient;
+ bool roll;
+ int code;
+ int ai;
+ struct mkd_ {
+ ref key, dict;
+ } aelt;
+ 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);
+ 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:;
+ }
+ code = dict_bool_param(preq, "RollFedMedia", false, &roll);
+ if (code < 0)
+ return code;
+ 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;
+ }
+ match.no_match_priority = r_size(ppriority);
+ reset_match(&match);
+ for (ai = dict_first(pattr);
+ (ai = dict_next(pattr, ai, (ref * /*[2]*/)&aelt)) >= 0;
+ ) {
+ if (r_has_type(&aelt.dict, t_dictionary) &&
+ r_has_attr(dict_access_ref(&aelt.dict), a_read) &&
+ r_has_type(&aelt.key, t_integer) &&
+ (mepos < 0 || aelt.key.value.intval == mepos)
+ ) {
+ bool match_all;
+ uint ki, pi;
+
+ code = dict_bool_param(&aelt.dict, "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(&aelt.dict, &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, roll,
+ &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(&match);
+ }
+ /* In case of a tie, see if the new match has */
+ /* priority. */
+ for (pi = match.priority; pi > 0;) {
+ ref pri;
+
+ pi--;
+ array_get(ppriority, pi, &pri);
+ if (obj_eq(&aelt.key, &pri)) { /* Yes, higher priority. */
+ match.best_key = aelt.key;
+ match.priority = pi;
+ break;
+ }
+ }
+ /* Save the match in case no match has priority. */
+ match.match_key = aelt.key;
+no:;
+ }
+ }
+ if (r_has_type(&match.match_key, t_null)) {
+ make_false(op - 3);
+ pop(3);
+ } else {
+ if (r_has_type(&match.best_key, t_null))
+ op[-3] = match.match_key;
+ else
+ op[-3] = match.best_key;
+ make_true(op - 2);
+ pop(2);
+ }
+ return 0;
+}
+
+/* [<req_x> <req_y>] [<med_x0> <med_y0> (<med_x1> <med_y1> | )]
+ * <policy> <orient|null> <roll> <matrix|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;
+ bool roll;
+ int code;
+
+ check_type(op[-3], t_integer);
+ if (r_has_type(op - 2, t_null))
+ orient = -1;
+ else {
+ check_int_leu(op[-2], 3);
+ orient = (int)op[-2].value.intval;
+ }
+ check_type(op[-1], t_boolean);
+ roll = op[-1].value.boolval;
+ code = zmatch_page_size(op - 5, op - 4, (int)op[-3].value.intval,
+ orient, roll,
+ &ignore_mismatch, &mat, &media_size);
+ switch (code) {
+ default:
+ return code;
+ case 0:
+ make_false(op - 5);
+ pop(5);
+ break;
+ case 1:
+ code = write_matrix(op, &mat);
+ if (code < 0 && !r_has_type(op, t_null))
+ return code;
+ op[-5] = *op;
+ make_real(op - 4, media_size.x);
+ make_real(op - 3, media_size.y);
+ make_true(op - 2);
+ pop(2);
+ break;
+ }
+ return 0;
+}
+/* Match the PageSize. See below for details. */
+private bool match_page_size(P8(const gs_point * request,
+ const gs_rect * medium,
+ int policy, int orient, bool roll,
+ float *best_mismatch, gs_matrix * pmat,
+ gs_point * pmsize));
+private int
+zmatch_page_size(const ref * pvreq, const ref * pvmed,
+ int policy, int orient, bool roll,
+ 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;
+ double 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,
+ roll, 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, bool roll, float *best_mismatch, gs_matrix * pmat,
+ gs_point * pmsize)
+{
+ double rx = request->x, ry = request->y;
+
+ if (policy == 7) {
+ /* (Adobe) hack: just impose requested values */
+ *best_mismatch = 0;
+ gs_make_identity(pmat);
+ *pmsize = *request;
+ return true;
+ }
+ 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);
+#undef ADJUST_INTO
+ 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 an unreasonably small medium->p.{x,y}.
+ */
+#define MIN_MEDIA_SIZE 9
+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 < MIN_MEDIA_SIZE && mx > rx)
+ mx = rx;
+ if (medium->p.y < MIN_MEDIA_SIZE && 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);
+}
+#undef MIN_MEDIA_SIZE
+
+/* ------ Initialization procedure ------ */
+
+const op_def zmedia2_l2_op_defs[] =
+{
+ op_def_begin_level2(),
+ {"4.matchmedia", zmatchmedia},
+ {"6.matchpagesize", zmatchpagesize},
+ op_def_end(0)
+};
diff --git a/pstoraster/zmisc.c b/pstoraster/zmisc.c
new file mode 100644
index 000000000..89b1fc69c
--- /dev/null
+++ b/pstoraster/zmisc.c
@@ -0,0 +1,345 @@
+/* Copyright (C) 1989, 1995, 1997 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Miscellaneous operators */
+#include "errno_.h"
+#include "memory_.h"
+#include "string_.h"
+#include "ghost.h"
+#include "gscdefs.h" /* for gs_serialnumber */
+#include "gp.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"
+
+/* <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;
+ byte *value;
+ int code;
+ int len = 0;
+
+ check_read_type(*op, t_string);
+ str = ref_to_string(op, imemory, "getenv key");
+ if (str == 0)
+ return_error(e_VMerror);
+ if (gp_getenv(str, (char *)0, &len) > 0) { /* key missing */
+ ifree_string((byte *) str, r_size(op) + 1, "getenv key");
+ make_false(op);
+ return 0;
+ }
+ value = ialloc_string(len, "getenv value");
+ if (value == 0) {
+ ifree_string((byte *) str, r_size(op) + 1, "getenv key");
+ return_error(e_VMerror);
+ }
+ code = gp_getenv(str, (char *)value, &len); /* can't fail */
+ ifree_string((byte *) str, r_size(op) + 1, "getenv key");
+ /* Delete the stupid C string terminator. */
+ value = iresize_string(value, len, len - 1,
+ "getenv value"); /* can't fail */
+ push(1);
+ make_string(op - 1, a_all | icurrent_space, len - 1, value);
+ make_true(op);
+ 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 ------ */
+
+const op_def 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},
+ op_def_end(0)
+};
diff --git a/pstoraster/zmisc1.c b/pstoraster/zmisc1.c
new file mode 100644
index 000000000..aff17b5ff
--- /dev/null
+++ b/pstoraster/zmisc1.c
@@ -0,0 +1,157 @@
+/* Copyright (C) 1994, 1997 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Miscellaneous Type 1 font operators */
+#include "memory_.h"
+#include "ghost.h"
+#include "oper.h"
+#include "gscrypt1.h"
+#include "stream.h" /* for getting state of PFBD stream */
+#include "strimpl.h"
+#include "sfilter.h"
+#include "idict.h"
+#include "idparam.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> <dict> eexecDecode/filter <file> */
+private int
+zexD(register os_ptr op)
+{
+ stream_exD_state state;
+ int code;
+
+ (*s_exD_template.set_defaults)((stream_state *)&state);
+ if (r_has_type(op, t_dictionary)) {
+ uint cstate;
+
+ check_dict_read(*op);
+ if ((code = dict_uint_param(op, "seed", 0, 0xffff, 0x10000,
+ &cstate)) < 0 ||
+ (code = dict_int_param(op, "lenIV", 0, max_int, 4,
+ &state.lenIV)) < 0
+ )
+ return code;
+ state.cstate = cstate;
+ code = 1;
+ } else {
+ 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 ------ */
+
+const op_def zmisc1_op_defs[] =
+{
+ {"3.type1encrypt", ztype1encrypt},
+ {"3.type1decrypt", ztype1decrypt},
+ op_def_begin_filter(),
+ {"2eexecEncode", zexE},
+ {"2eexecDecode", zexD},
+ op_def_end(0)
+};
diff --git a/pstoraster/zmisc2.c b/pstoraster/zmisc2.c
new file mode 100644
index 000000000..63b367100
--- /dev/null
+++ b/pstoraster/zmisc2.c
@@ -0,0 +1,322 @@
+/* Copyright (C) 1992, 1995, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Miscellaneous Level 2 operators */
+#include "memory_.h"
+#include "string_.h"
+#include "ghost.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 <int> */
+private int
+zlanguagelevel(register os_ptr op)
+{
+ push(1);
+ ref_assign(op, &ref_language_level);
+ return 0;
+}
+
+/* <int> .setlanguagelevel - */
+private int
+zsetlanguagelevel(register os_ptr op)
+{
+ int code = 0;
+
+ check_type(*op, t_integer);
+ 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. */
+const op_def 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},
+ op_def_end(0)
+};
+
+/* ------ Internal procedures ------ */
+
+/*
+ * Adjust the interpreter for a change in language level.
+ * This is used for the .setlanguagelevel operator,
+ * and (perhaps someday) after a restore.
+ */
+private int swap_level_dict(P1(const char *dict_name));
+private int swap_entry(P3(ref elt[2], ref * pdict, ref * pdict2));
+private int
+set_language_level(int new_level)
+{
+ int old_level = ref_language_level.value.intval;
+ ref *pgdict = /* globaldict, if present */
+ ref_stack_index(&d_stack, ref_stack_count(&d_stack) - 2);
+ ref *level2dict;
+ int code = 0;
+
+ if (new_level < 1 ||
+ new_level >
+ (dict_find_string(systemdict, "ll3dict", &level2dict) > 0 ? 3 : 2)
+ )
+ return_error(e_rangecheck);
+ 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.
+ */
+ while (new_level != old_level) {
+ switch (old_level) {
+ case 1: { /* 1 => 2 or 3 */
+ /* Put globaldict in the dictionary stack. */
+ ref *pdict;
+
+ /*
+ * This might be called so early in initialization that
+ * globaldict hasn't been defined yet. If so, just skip
+ * this step.
+ */
+ code = dict_find_string(level2dict, "globaldict", &pdict);
+ if (code > 0) {
+ if (!r_has_type(pdict, t_dictionary))
+ return_error(e_typecheck);
+ *pgdict = *pdict;
+ }
+ /* Set other flags for Level 2 operation. */
+ dict_auto_expand = true;
+ }
+ code = swap_level_dict("level2dict");
+ if (code < 0)
+ return code;
+ ++old_level;
+ continue;
+ case 3: /* 3 => 1 or 2 */
+ code = swap_level_dict("ll3dict");
+ if (code < 0)
+ return code;
+ --old_level;
+ continue;
+ default: /* 2 => 1 or 3 */
+ break;
+ }
+ switch (new_level) {
+ case 1: { /* 2 => 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;
+ }
+ code = swap_level_dict("level2dict");
+ break;
+ case 3: /* 2 => 3 */
+ code = swap_level_dict("ll3dict");
+ break;
+ default: /* not possible */
+ return_error(e_Fatal);
+ }
+ break;
+ }
+ dict_set_top(); /* reload dict stack cache */
+ return code;
+}
+
+/*
+ * Swap the contents of a level dictionary (level2dict or ll3dict) and
+ * systemdict. If a value in the level dictionary is itself 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.)
+ */
+private int
+swap_level_dict(const char *dict_name)
+{
+ ref *leveldict;
+ int index;
+ ref elt[2]; /* key, value */
+ ref *subdict;
+
+ if (dict_find_string(systemdict, dict_name, &leveldict) <= 0)
+ return_error(e_undefined);
+ index = dict_first(leveldict);
+ while ((index = dict_next(leveldict, 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)
+ ) {
+ /* elt[1] is the 2nd-level sub-dictionary */
+ int isub = dict_first(&elt[1]);
+ ref subelt[2];
+ int found = dict_find(systemdict, &elt[0], &subdict);
+
+ if (found <= 0)
+ continue;
+ while ((isub = dict_next(&elt[1], isub, &subelt[0])) >= 0)
+ if (!obj_eq(&subelt[0], &elt[0])) {
+ /* don't swap dict itself */
+ int code = swap_entry(subelt, subdict, &elt[1]);
+
+ if (code < 0)
+ return code;
+ }
+ } else {
+ int code = swap_entry(elt, systemdict, leveldict);
+
+ if (code < 0)
+ return code;
+ }
+ return 0;
+}
+
+/*
+ * Swap an entry from a higher level dictionary into a base dictionary.
+ * elt[0] is the key, elt[1] is the current value in the Level 2 dictionary
+ * (*pdict2).
+ */
+private int
+swap_entry(ref elt[2], ref * pdict, ref * pdict2)
+{
+ ref *pvalue;
+ ref old_value; /* current value in *pdict */
+ 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]);
+ if (code == e_undefined &&
+ r_has_type(&old_value, t_null)
+ )
+ code = 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/zmisc3.c b/pstoraster/zmisc3.c
new file mode 100644
index 000000000..0c27f3cf7
--- /dev/null
+++ b/pstoraster/zmisc3.c
@@ -0,0 +1,129 @@
+/* Copyright (C) 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Miscellaneous LanguageLevel 3 operators */
+#include "ghost.h"
+#include "gsclipsr.h"
+#include "oper.h"
+#include "igstate.h"
+#include "store.h"
+
+/* - clipsave - */
+private int
+zclipsave(os_ptr op)
+{
+ return gs_clipsave(igs);
+}
+
+/* - cliprestore - */
+private int
+zcliprestore(os_ptr op)
+{
+ return gs_cliprestore(igs);
+}
+
+/* <proc1> <proc2> .eqproc <bool> */
+/*
+ * Test whether two procedures are equal to depth 10.
+ * This is the equality test used by idiom recognition in 'bind'.
+ */
+#define MAX_DEPTH 10 /* depth is per Adobe specification */
+typedef struct ref2_s {
+ ref proc1, proc2;
+} ref2_t;
+private int
+zeqproc(register os_ptr op)
+{
+ ref2_t stack[MAX_DEPTH + 1];
+ ref2_t *top = stack;
+
+ make_array(&stack[0].proc1, 0, 1, op - 1);
+ make_array(&stack[0].proc2, 0, 1, op);
+ for (;;) {
+ long i;
+
+ if (r_size(&top->proc1) == 0) {
+ /* Finished these arrays, go up to next level. */
+ if (top == stack) {
+ /* We're done matching: it succeeded. */
+ make_true(op - 1);
+ pop(1);
+ return 0;
+ }
+ --top;
+ continue;
+ }
+ /* Look at the next elements of the arrays. */
+ i = r_size(&top->proc1) - 1;
+ array_get(&top->proc1, i, &top[1].proc1);
+ array_get(&top->proc2, i, &top[1].proc2);
+ r_dec_size(&top->proc1, 1);
+ ++top;
+ /*
+ * Amazingly enough, the objects' executable attributes are not
+ * required to match. This means { x load } will match { /x load },
+ * even though this is clearly wrong.
+ */
+#if 0
+ if (r_has_attr(&top->proc1, a_executable) !=
+ r_has_attr(&top->proc2, a_executable)
+ )
+ break;
+#endif
+ if (obj_eq(&top->proc1, &top->proc2)) {
+ /* Names don't match strings. */
+ if (r_type(&top->proc1) != r_type(&top->proc2) &&
+ (r_type(&top->proc1) == t_name ||
+ r_type(&top->proc2) == t_name)
+ )
+ break;
+ --top; /* no recursion */
+ continue;
+ }
+ if (r_is_array(&top->proc1) && r_is_array(&top->proc2) &&
+ r_size(&top->proc1) == r_size(&top->proc2) &&
+ top < stack + (MAX_DEPTH - 1)
+ ) {
+ /* Descend into the arrays. */
+ continue;
+ }
+ break;
+ }
+ /* An exit from the loop indicates that matching failed. */
+ make_false(op - 1);
+ pop(1);
+ return 0;
+}
+
+/* ------ Initialization procedure ------ */
+
+const op_def zmisc3_op_defs[] =
+{
+ op_def_begin_ll3(),
+ {"0cliprestore", zcliprestore},
+ {"0clipsave", zclipsave},
+ {"2.eqproc", zeqproc},
+ op_def_end(0)
+};
diff --git a/pstoraster/zpacked.c b/pstoraster/zpacked.c
new file mode 100644
index 000000000..7f3947068
--- /dev/null
+++ b/pstoraster/zpacked.c
@@ -0,0 +1,258 @@
+/* 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 supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Packed array operators */
+#include "ghost.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;
+ ref_packed *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 ------ */
+
+const op_def zpacked_op_defs[] =
+{
+ {"0currentpacking", zcurrentpacking},
+ {"1packedarray", zpackedarray},
+ {"1setpacking", zsetpacking},
+ op_def_end(0)
+};
diff --git a/pstoraster/zpaint.c b/pstoraster/zpaint.c
new file mode 100644
index 000000000..895b40852
--- /dev/null
+++ b/pstoraster/zpaint.c
@@ -0,0 +1,92 @@
+/* Copyright (C) 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Painting operators */
+#include "ghost.h"
+#include "oper.h"
+#include "gspaint.h"
+#include "igstate.h"
+
+/* - fill - */
+private int
+zfill(register os_ptr op)
+{
+ return gs_fill(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);
+}
+
+/* ------ Non-standard operators ------ */
+
+/* - .fillpage - */
+private int
+zfillpage(register os_ptr op)
+{
+ return gs_fillpage(igs);
+}
+
+/* <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 ------ */
+
+const op_def zpaint_op_defs[] =
+{
+ {"0eofill", zeofill},
+ {"0fill", zfill},
+ {"0stroke", zstroke},
+ /* Non-standard operators */
+ {"0.fillpage", zfillpage},
+ {"3.imagepath", zimagepath},
+ op_def_end(0)
+};
diff --git a/pstoraster/zpath.c b/pstoraster/zpath.c
new file mode 100644
index 000000000..7b1d77a7e
--- /dev/null
+++ b/pstoraster/zpath.c
@@ -0,0 +1,205 @@
+/* Copyright (C) 1989, 1995, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Basic path operators */
+#include "math_.h"
+#include "ghost.h"
+#include "oper.h"
+#include "igstate.h"
+#include "gsmatrix.h"
+#include "gspath.h"
+#include "store.h"
+
+/* Forward references */
+private int common_to(P2(os_ptr,
+ int (*)(P3(gs_state *, floatp, floatp))));
+private int 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
+common_to(os_ptr op, int (*add_proc)(P3(gs_state *, floatp, floatp)))
+{
+ double 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
+common_curve(os_ptr op,
+ int (*add_proc)(P7(gs_state *, floatp, floatp, floatp, floatp, floatp, floatp)))
+{
+ double 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 ------ */
+
+const op_def 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},
+ op_def_end(0)
+};
diff --git a/pstoraster/zpath1.c b/pstoraster/zpath1.c
new file mode 100644
index 000000000..39dd7d6b2
--- /dev/null
+++ b/pstoraster/zpath1.c
@@ -0,0 +1,279 @@
+/* Copyright (C) 1989, 1995, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* PostScript Level 1 additional path operators */
+#include "memory_.h"
+#include "ghost.h"
+#include "oper.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 common_arc(P2(os_ptr,
+ int (*)(P6(gs_state *, floatp, floatp, floatp, floatp, floatp))));
+private int 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
+common_arc(os_ptr op,
+ int (*aproc)(P6(gs_state *, floatp, floatp, floatp, floatp, floatp)))
+{
+ double xyra[5]; /* x, y, r, ang1, ang2 */
+ int code = num_params(op, 5, xyra);
+
+ if (code < 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
+common_arct(os_ptr op, float *tanxy)
+{
+ double args[5]; /* x1, y1, x2, y2, r */
+ int code = num_params(op, 5, args);
+
+ if (code < 0)
+ return code;
+ return gs_arcto(igs, args[0], args[1], args[2], args[3], args[4], tanxy);
+}
+
+/* - .dashpath - */
+private int
+zdashpath(register os_ptr op)
+{
+ return gs_dashpath(igs);
+}
+
+/* - 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 ------ */
+
+const op_def zpath1_op_defs[] =
+{
+ {"5arc", zarc},
+ {"5arcn", zarcn},
+ {"5arct", zarct},
+ {"5arcto", zarcto},
+ {"0clippath", zclippath},
+ {"0.dashpath", zdashpath},
+ {"0flattenpath", zflattenpath},
+ {"4pathforall", zpathforall},
+ {"0reversepath", zreversepath},
+ {"0strokepath", zstrokepath},
+ {"0.pathbbox", zpathbbox},
+ /* Internal operators */
+ {"0%path_continue", path_continue},
+ op_def_end(0)
+};
diff --git a/pstoraster/zpcolor.c b/pstoraster/zpcolor.c
new file mode 100644
index 000000000..445487d02
--- /dev/null
+++ b/pstoraster/zpcolor.c
@@ -0,0 +1,264 @@
+/* Copyright (C) 1994, 1995, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Pattern color */
+#include "ghost.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> .buildpattern1 <pattern> <instance> */
+private int
+zbuildpattern1(os_ptr op)
+{
+ os_ptr op1 = op - 1;
+ int code;
+ gs_matrix mat;
+ 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);
+ gs_pattern1_init(&template);
+ if ((code = read_matrix(op, &mat)) < 0 ||
+ (code = dict_uid_param(op1, &template.uid, 1, imemory)) != 1 ||
+ (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);
+ switch (r_size(op)) {
+ case 1: /* no base space */
+ cs.params.pattern.has_base_space = false;
+ break;
+ default:
+ return_error(e_rangecheck);
+ case 2:
+ cs = *gs_currentcolorspace(igs);
+ if (cs_num_components(&cs) < 0) /* i.e., Pattern space */
+ return_error(e_rangecheck);
+ /* 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 ------ */
+
+const op_def zpcolor_l2_op_defs[] =
+{
+ op_def_begin_level2(),
+ {"2.buildpattern1", zbuildpattern1},
+ {"1.setpatternspace", zsetpatternspace},
+ /* Internal operators */
+ {"0%pattern_paint_prepare", pattern_paint_prepare},
+ {"0%pattern_paint_finish", pattern_paint_finish},
+ op_def_end(zpcolor_init)
+};
+
+/* ------ Internal procedures ------ */
+
+/* Set up the pattern pointer in a client color for setcolor */
+/* with a Pattern space. */
+/****** ? WHAT WAS THIS FOR ? ******/
+
+/* 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((gs_imager_state *)igs,
+ pdev, &ctile);
+
+ if (code < 0)
+ return code;
+ 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 *const pdev =
+ r_ptr(esp + 2, gx_device_pattern_accum);
+
+ /* grestore will free the device, so close it first. */
+ (*dev_proc(pdev, close_device)) ((gx_device *) pdev);
+ return gs_grestore(igs);
+}
diff --git a/pstoraster/zrelbit.c b/pstoraster/zrelbit.c
new file mode 100644
index 000000000..08e07f3d0
--- /dev/null
+++ b/pstoraster/zrelbit.c
@@ -0,0 +1,342 @@
+/* Copyright (C) 1989, 1995, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Relational, boolean, and bit operators */
+#include "ghost.h"
+#include "oper.h"
+#include "gsutil.h"
+#include "idict.h"
+#include "store.h"
+
+/* ------ Standard operators ------ */
+
+/* Define the type test for eq and its relatives. */
+#define EQ_CHECK_READ(opp, dflt)\
+ switch ( r_type(opp) ) {\
+ case t_string:\
+ check_read(*(opp));\
+ break;\
+ default:\
+ dflt;\
+ }
+
+/* Forward references */
+private int obj_le(P2(os_ptr, os_ptr));
+
+/* <obj1> <obj2> eq <bool> */
+private int
+zeq(register os_ptr op)
+{
+ EQ_CHECK_READ(op - 1, check_op(2));
+ EQ_CHECK_READ(op, DO_NOTHING);
+ make_bool(op - 1, (obj_eq(op - 1, 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)
+{
+ EQ_CHECK_READ(op - 1, check_op(2));
+ EQ_CHECK_READ(op, DO_NOTHING);
+ make_bool(op - 1, (obj_ident_eq(op - 1, 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 ------ */
+
+const op_def 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},
+ op_def_end(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. */
+private int
+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 (op1->value.intval <= op->value.intval);
+ case t_real:
+ return ((double)op1->value.intval <= op->value.realval);
+ default:
+ return_op_typecheck(op);
+ }
+ case t_real:
+ switch (r_type(op)) {
+ case t_real:
+ return (op1->value.realval <= op->value.realval);
+ case t_integer:
+ return (op1->value.realval <= (double)op->value.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);
+ default:
+ return_op_typecheck(op1);
+ }
+}
diff --git a/pstoraster/zshade.c b/pstoraster/zshade.c
new file mode 100644
index 000000000..2486976b8
--- /dev/null
+++ b/pstoraster/zshade.c
@@ -0,0 +1,599 @@
+/* Copyright (C) 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* PostScript language interface to shading */
+#include "memory_.h"
+#include "ghost.h"
+#include "oper.h"
+#include "gscolor3.h"
+#include "gscspace.h"
+#include "gscolor2.h" /* requires gscspace.h */
+#include "gsfunc3.h"
+#include "gsstruct.h" /* must precede gsshade.h */
+#include "gsshade.h"
+#include "gsuid.h"
+#include "stream.h" /* for files.h */
+#include "files.h"
+#include "ialloc.h"
+#include "idict.h"
+#include "idparam.h"
+#include "ifunc.h"
+#include "igstate.h"
+#include "store.h"
+
+/* Forward references */
+private int shading_param(P2(const_os_ptr op, const gs_shading_t ** ppsh));
+
+/* ---------------- Standard operators ---------------- */
+
+/* - currentsmoothness <smoothness> */
+private int
+zcurrentsmoothness(register os_ptr op)
+{
+ push(1);
+ make_real(op, gs_currentsmoothness(igs));
+ return 0;
+}
+
+/* <smoothness> setsmoothness - */
+private int
+zsetsmoothness(register os_ptr op)
+{
+ double smoothness;
+ int code;
+
+ if (real_param(op, &smoothness) < 0)
+ return_op_typecheck(op);
+ if ((code = gs_setsmoothness(igs, smoothness)) < 0)
+ return code;
+ pop(1);
+ return 0;
+}
+
+/* <shading> .shfill - */
+private int
+zshfill(register os_ptr op)
+{
+ const gs_shading_t *psh;
+ int code = shading_param(op, &psh);
+
+ if (code < 0 || (code = gs_shfill(igs, psh)) < 0)
+ return code;
+ pop(1);
+ return 0;
+}
+
+/* ------ Non-standard operators ------ */
+
+/* <pattern> <matrix> <shading> .buildshadingpattern <pattern> <instance> */
+private int
+zbuildshadingpattern(os_ptr op)
+{
+ os_ptr op2 = op - 2;
+ const gs_shading_t *psh;
+ gs_matrix mat;
+ struct {
+ gs_uid uid;
+ } template; /****** WRONG ******/
+ int code;
+
+ check_type(*op2, t_dictionary);
+ check_dict_read(*op2);
+
+ if ((code = shading_param(op, &psh)) < 0 ||
+ (code = read_matrix(op - 1, &mat)) < 0 ||
+ (code = dict_uid_param(op2, &template.uid, 1, imemory)) != 1
+ )
+ return_error((code < 0 ? code : e_rangecheck));
+ /****** NYI ******/
+ return_error(e_undefined);
+}
+
+/* ------ Internal procedures ------ */
+
+/* Get a shading parameter. */
+private int
+shading_param(const_os_ptr op, const gs_shading_t ** ppsh)
+{ /*
+ * Since shadings form a subclass hierarchy, we currently have
+ * no way to check whether a structure is actually a shading.
+ */
+ if (!r_is_struct(op) ||
+ r_has_masked_attrs(op, a_executable | a_execute, a_all)
+ )
+ return_error(e_typecheck);
+ *ppsh = (gs_shading_t *) op->value.pstruct;
+ return 0;
+}
+
+/* ---------------- Shading dictionaries ---------------- */
+
+/* ------ Common code ------ */
+
+extern_st(st_color_space);
+
+typedef int (*build_shading_proc_t)(P3(const ref *op,
+ const gs_shading_params_t *params,
+ gs_shading_t **ppsh));
+
+/* Operators */
+
+/* Common framework for building shadings. */
+private int
+build_shading(ref * op, build_shading_proc_t proc)
+{
+ int code;
+ float box[4];
+ gs_shading_params_t params;
+ gs_shading_t *psh;
+ ref *pvalue;
+
+ check_type(*op, t_dictionary);
+ params.ColorSpace = 0;
+ params.Background = 0;
+ /* Collect parameters common to all shading types. */
+ {
+ const gs_color_space *pcs_orig = gs_currentcolorspace(igs);
+ int num_comp = gs_color_space_num_components(pcs_orig);
+ gs_color_space *pcs;
+
+ if (num_comp < 0) /* Pattern color space */
+ return_error(e_rangecheck);
+ pcs = ialloc_struct(gs_color_space, &st_color_space,
+ "build_shading");
+ if (pcs == 0)
+ return_error(e_VMerror);
+ gs_cspace_init_from(pcs, pcs_orig);
+ params.ColorSpace = pcs;
+ if (dict_find_string(op, "Background", &pvalue) > 0) {
+ gs_client_color *pcc =
+ ialloc_struct(gs_client_color, &st_client_color,
+ "build_shading");
+
+ if (pcc == 0) {
+ code = gs_note_error(e_VMerror);
+ goto fail;
+ }
+ pcc->pattern = 0;
+ params.Background = pcc;
+ code = dict_float_array_param(op, "Background",
+ countof(pcc->paint.values),
+ pcc->paint.values, NULL);
+ if (code != gs_color_space_num_components(pcs))
+ goto fail;
+ }
+ }
+ if (dict_find_string(op, "BBox", &pvalue) <= 0)
+ params.have_BBox = false;
+ else if ((code = dict_float_array_param(op, "BBox", 4, box, NULL)) == 4) {
+ params.BBox.p.x = box[0];
+ params.BBox.p.y = box[1];
+ params.BBox.q.x = box[2];
+ params.BBox.q.y = box[3];
+ params.have_BBox = true;
+ } else
+ goto fail;
+ code = dict_bool_param(op, "AntiAlias", false, &params.AntiAlias);
+ if (code < 0)
+ goto fail;
+ /* Finish building the shading. */
+ code = (*proc) (op, &params, &psh);
+ if (code < 0)
+ goto fail;
+ make_istruct_new(op, 0, psh);
+ return code;
+fail:
+ ifree_object(params.Background, "Background");
+ if (params.ColorSpace) {
+ gs_cspace_release(params.ColorSpace);
+ ifree_object(params.ColorSpace, "ColorSpace");
+ }
+ return (code < 0 ? code : gs_note_error(e_rangecheck));
+}
+
+/* Collect a Function value. */
+private int
+build_shading_function(const ref * op, gs_function_t ** ppfn, int num_inputs)
+{
+ ref *pFunction;
+
+ *ppfn = 0;
+ if (dict_find_string(op, "Function", &pFunction) <= 0)
+ return 0;
+ if (r_is_array(pFunction)) {
+ uint size = r_size(pFunction);
+ gs_function_t **Functions;
+ uint i;
+ gs_function_AdOt_params_t params;
+ int code;
+
+ check_read(*pFunction);
+ if (size == 0)
+ return_error(e_rangecheck);
+ code = ialloc_function_array(size, &Functions);
+ if (code < 0)
+ return code;
+ for (i = 0; i < size; ++i) {
+ ref rsubfn;
+
+ array_get(op, (long)i, &rsubfn);
+ code = fn_build_function(&rsubfn, &Functions[i]);
+ if (code < 0)
+ break;
+ }
+ params.m = 1;
+ params.Domain = 0;
+ params.n = size;
+ params.Range = 0;
+ params.Functions = (const gs_function_t * const *)Functions;
+ if (code >= 0)
+ code = gs_function_AdOt_init(ppfn, &params, imemory);
+ if (code < 0)
+ gs_function_AdOt_free_params(&params, imemory);
+ return code;
+ } else
+ return fn_build_function(pFunction, ppfn);
+}
+
+/* ------ Build shadings ------ */
+
+/* Build a ShadingType 1 (Function-based) shading. */
+private int
+build_shading_1(const ref * op, const gs_shading_params_t * pcommon,
+ gs_shading_t ** ppsh)
+{
+ gs_shading_Fb_params_t params;
+ int code;
+ static const float default_Domain[4] = {0, 1, 0, 1};
+
+ *(gs_shading_params_t *)&params = *pcommon;
+ gs_make_identity(&params.Matrix);
+ params.Function = 0;
+ if ((code = dict_float_array_param(op, "Domain", 4, params.Domain,
+ default_Domain)) != 4 ||
+ (code = dict_matrix_param(op, "Matrix", &params.Matrix)) < 0 ||
+ (code = build_shading_function(op, &params.Function, 2)) < 0 ||
+ (code = gs_shading_Fb_init(ppsh, &params, imemory)) < 0
+ ) {
+ ifree_object(params.Function, "Function");
+ return (code < 0 ? code : gs_note_error(e_rangecheck));
+ }
+ return 0;
+}
+/* <dict> .buildshading1 <shading_struct> */
+private int
+zbuildshading1(os_ptr op)
+{
+ return build_shading(op, build_shading_1);
+}
+
+/* Collect parameters for an Axial or Radial shading. */
+private int
+build_directional_shading(const ref * op, float *Coords, int num_Coords,
+ float Domain[2], gs_function_t ** pFunction, bool Extend[2])
+{
+ int code =
+ dict_float_array_param(op, "Coords", num_Coords, Coords, NULL);
+ static const float default_Domain[2] = {0, 1};
+ ref *pExtend;
+
+ *pFunction = 0;
+ if (code != num_Coords ||
+ (code = dict_float_array_param(op, "Domain", 2, Domain,
+ default_Domain)) != 2 ||
+ (code = build_shading_function(op, pFunction, 1)) < 0
+ )
+ return (code < 0 ? code : gs_note_error(e_rangecheck));
+ if (dict_find_string(op, "Extend", &pExtend) <= 0)
+ Extend[0] = Extend[1] = false;
+ else {
+ ref E0, E1;
+
+ if (!r_is_array(pExtend))
+ return_error(e_typecheck);
+ else if (r_size(pExtend) != 2)
+ return_error(e_rangecheck);
+ else if ((array_get(pExtend, 0L, &E0), !r_has_type(&E0, t_boolean)) ||
+ (array_get(pExtend, 1L, &E1), !r_has_type(&E1, t_boolean))
+ )
+ return_error(e_typecheck);
+ Extend[0] = E0.value.boolval, Extend[1] = E1.value.boolval;
+ }
+ return 0;
+}
+
+/* Build a ShadingType 2 (Axial) shading. */
+private int
+build_shading_2(const ref * op, const gs_shading_params_t * pcommon,
+ gs_shading_t ** ppsh)
+{
+ gs_shading_A_params_t params;
+ int code;
+
+ *(gs_shading_params_t *)&params = *pcommon;
+ if ((code = build_directional_shading(op, params.Coords, 4,
+ params.Domain, &params.Function,
+ params.Extend)) < 0 ||
+ (code = gs_shading_A_init(ppsh, &params, imemory)) < 0
+ ) {
+ ifree_object(params.Function, "Function");
+ }
+ return code;
+}
+/* <dict> .buildshading2 <shading_struct> */
+private int
+zbuildshading2(os_ptr op)
+{
+ return build_shading(op, build_shading_2);
+}
+
+/* Build a ShadingType 3 (Radial) shading. */
+private int
+build_shading_3(const ref * op, const gs_shading_params_t * pcommon,
+ gs_shading_t ** ppsh)
+{
+ gs_shading_R_params_t params;
+ int code;
+
+ *(gs_shading_params_t *)&params = *pcommon;
+ if ((code = build_directional_shading(op, params.Coords, 6,
+ params.Domain, &params.Function,
+ params.Extend)) < 0 ||
+ (code = gs_shading_R_init(ppsh, &params, imemory)) < 0
+ ) {
+ ifree_object(params.Function, "Function");
+ }
+ return code;
+}
+/* <dict> .buildshading3 <shading_struct> */
+private int
+zbuildshading3(os_ptr op)
+{
+ return build_shading(op, build_shading_3);
+}
+
+/* Collect parameters for a mesh shading. */
+private int
+build_mesh_shading(const ref * op, gs_shading_mesh_params_t * params,
+ float **pDecode, gs_function_t ** pFunction)
+{
+ int code;
+ ref *pDataSource;
+
+ *pDecode = 0;
+ *pFunction = 0;
+ if (dict_find_string(op, "DataSource", &pDataSource) <= 0)
+ return_error(e_rangecheck);
+ if (r_is_array(pDataSource)) {
+ uint size = r_size(pDataSource);
+ float *data =
+ (float *)ialloc_byte_array(size, sizeof(float),
+ "build_mesh_shading");
+ int code;
+
+ if (data == 0)
+ return_error(e_VMerror);
+ code = float_params(pDataSource->value.refs + size - 1, size, data);
+ if (code < 0) {
+ ifree_object(data, "build_mesh_shading");
+ return code;
+ }
+ data_source_init_floats(&params->DataSource, data, size);
+ } else
+ switch (r_type(pDataSource)) {
+ case t_file: {
+ stream *s;
+
+ check_read_file(s, pDataSource);
+ data_source_init_stream(&params->DataSource, s);
+ break;
+ }
+ case t_string:
+ check_read(*pDataSource);
+ data_source_init_string2(&params->DataSource,
+ pDataSource->value.bytes,
+ r_size(pDataSource));
+ break;
+ default:
+ return_error(e_typecheck);
+ }
+ if (data_source_is_array(params->DataSource)) {
+ params->BitsPerCoordinate = 0;
+ params->BitsPerComponent = 0;
+ } else {
+ int num_decode =
+ 4 + gs_color_space_num_components(params->ColorSpace) * 2;
+
+/****** FREE FLOAT ARRAY DATA ON ERROR ******/
+ if ((code = dict_int_param(op, "BitsPerCoordinate", 1, 32, 0,
+ &params->BitsPerCoordinate)) < 0 ||
+ (code = dict_int_param(op, "BitsPerComponent", 1, 16, 0,
+ &params->BitsPerComponent)) < 0
+ )
+ return code;
+ *pDecode = (float *)ialloc_byte_array(num_decode, sizeof(float),
+ "build_mesh_shading");
+
+ if (*pDecode == 0)
+ return_error(e_VMerror);
+ code = dict_float_array_param(op, "Decode", num_decode, *pDecode,
+ NULL);
+ if (code != num_decode) {
+ ifree_object(*pDecode, "build_mesh_shading");
+ *pDecode = 0;
+ return (code < 0 ? code : gs_note_error(e_rangecheck));
+ }
+ }
+ code = build_shading_function(op, pFunction, 1);
+ if (code < 0) {
+ ifree_object(*pDecode, "build_mesh_shading");
+ *pDecode = 0;
+ }
+ return code;
+}
+
+/* Collect the BitsPerFlag parameter, if relevant. */
+private int
+flag_bits_param(const ref * op, const gs_shading_mesh_params_t * params,
+ int *pBitsPerFlag)
+{
+ if (data_source_is_array(params->DataSource)) {
+ *pBitsPerFlag = 0;
+ return 0;
+ } else {
+ return dict_int_param(op, "BitsPerFlag", 2, 8, 0, pBitsPerFlag);
+ }
+}
+
+/* Build a ShadingType 4 (Free-form Gouraud triangle mesh) shading. */
+private int
+build_shading_4(const ref * op, const gs_shading_params_t * pcommon,
+ gs_shading_t ** ppsh)
+{
+ gs_shading_FfGt_params_t params;
+ int code;
+
+ *(gs_shading_params_t *)&params = *pcommon;
+ if ((code =
+ build_mesh_shading(op, (gs_shading_mesh_params_t *)&params,
+ &params.Decode, &params.Function)) < 0 ||
+ (code = flag_bits_param(op, (gs_shading_mesh_params_t *)&params,
+ &params.BitsPerFlag)) < 0 ||
+ (code = gs_shading_FfGt_init(ppsh, &params, imemory)) < 0
+ ) {
+ ifree_object(params.Function, "Function");
+ ifree_object(params.Decode, "Decode");
+ }
+ return code;
+}
+/* <dict> .buildshading4 <shading_struct> */
+private int
+zbuildshading4(os_ptr op)
+{
+ return build_shading(op, build_shading_4);
+}
+
+/* Build a ShadingType 5 (Lattice-form Gouraud triangle mesh) shading. */
+private int
+build_shading_5(const ref * op, const gs_shading_params_t * pcommon,
+ gs_shading_t ** ppsh)
+{
+ gs_shading_LfGt_params_t params;
+ int code;
+
+ *(gs_shading_params_t *)&params = *pcommon;
+ if ((code =
+ build_mesh_shading(op, (gs_shading_mesh_params_t *)&params,
+ &params.Decode, &params.Function)) < 0 ||
+ (code = dict_int_param(op, "VerticesPerRow", 2, max_int, 0,
+ &params.VerticesPerRow)) < 0 ||
+ (code = gs_shading_LfGt_init(ppsh, &params, imemory)) < 0
+ ) {
+ ifree_object(params.Function, "Function");
+ ifree_object(params.Decode, "Decode");
+ }
+ return code;
+}
+/* <dict> .buildshading5 <shading_struct> */
+private int
+zbuildshading5(os_ptr op)
+{
+ return build_shading(op, build_shading_5);
+}
+
+/* Build a ShadingType 6 (Coons patch mesh) shading. */
+private int
+build_shading_6(const ref * op, const gs_shading_params_t * pcommon,
+ gs_shading_t ** ppsh)
+{
+ gs_shading_Cp_params_t params;
+ int code;
+
+ *(gs_shading_params_t *)&params = *pcommon;
+ if ((code =
+ build_mesh_shading(op, (gs_shading_mesh_params_t *)&params,
+ &params.Decode, &params.Function)) < 0 ||
+ (code = flag_bits_param(op, (gs_shading_mesh_params_t *)&params,
+ &params.BitsPerFlag)) < 0 ||
+ (code = gs_shading_Cp_init(ppsh, &params, imemory)) < 0
+ ) {
+ ifree_object(params.Function, "Function");
+ ifree_object(params.Decode, "Decode");
+ }
+ return code;
+}
+/* <dict> .buildshading6 <shading_struct> */
+private int
+zbuildshading6(os_ptr op)
+{
+ return build_shading(op, build_shading_6);
+}
+
+/* Build a ShadingType 7 (Tensor product patch mesh) shading. */
+private int
+build_shading_7(const ref * op, const gs_shading_params_t * pcommon,
+ gs_shading_t ** ppsh)
+{
+ gs_shading_Tpp_params_t params;
+ int code;
+
+ *(gs_shading_params_t *)&params = *pcommon;
+ if ((code =
+ build_mesh_shading(op, (gs_shading_mesh_params_t *)&params,
+ &params.Decode, &params.Function)) < 0 ||
+ (code = flag_bits_param(op, (gs_shading_mesh_params_t *)&params,
+ &params.BitsPerFlag)) < 0 ||
+ (code = gs_shading_Tpp_init(ppsh, &params, imemory)) < 0
+ ) {
+ ifree_object(params.Function, "Function");
+ ifree_object(params.Decode, "Decode");
+ }
+ return code;
+}
+/* <dict> .buildshading7 <shading_struct> */
+private int
+zbuildshading7(os_ptr op)
+{
+ return build_shading(op, build_shading_7);
+}
+
+/* ------ Initialization procedure ------ */
+
+const op_def zshade_op_defs[] =
+{
+ op_def_begin_ll3(),
+ {"0currentsmoothness", zcurrentsmoothness},
+ {"1setsmoothness", zsetsmoothness},
+ {"1.shfill", zshfill},
+ {"1.buildshading1", zbuildshading1},
+ {"1.buildshading2", zbuildshading2},
+ {"1.buildshading3", zbuildshading3},
+ {"1.buildshading4", zbuildshading4},
+ {"1.buildshading5", zbuildshading5},
+ {"1.buildshading6", zbuildshading6},
+ {"1.buildshading7", zbuildshading7},
+ {"3.buildshadingpattern", zbuildshadingpattern},
+ op_def_end(0)
+};
diff --git a/pstoraster/zstack.c b/pstoraster/zstack.c
new file mode 100644
index 000000000..5057b1d64
--- /dev/null
+++ b/pstoraster/zstack.c
@@ -0,0 +1,295 @@
+/* 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 supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Operand stack operators */
+#include "memory_.h"
+#include "ghost.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 ------ */
+
+const op_def zstack_op_defs[] =
+{
+ {"0clear", zclear_stack},
+ {"0cleartomark", zcleartomark},
+ {"0count", zcount},
+ {"0counttomark", zcounttomark},
+ {"1dup", zdup},
+ {"2exch", zexch},
+ {"2index", zindex},
+ {"0mark", zmark},
+ {"1pop", zpop},
+ {"2roll", zroll},
+ op_def_end(0)
+};
diff --git a/pstoraster/zstring.c b/pstoraster/zstring.c
new file mode 100644
index 000000000..d009465a8
--- /dev/null
+++ b/pstoraster/zstring.c
@@ -0,0 +1,172 @@
+/* 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 supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* String operators */
+#include "memory_.h"
+#include "ghost.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 ------ */
+
+const op_def zstring_op_defs[] =
+{
+ {"2anchorsearch", zanchorsearch},
+ {"1.namestring", znamestring},
+ {"2search", zsearch},
+ {"1string", zstring},
+ {"2.stringmatch", zstringmatch},
+ op_def_end(0)
+};
diff --git a/pstoraster/zsysvm.c b/pstoraster/zsysvm.c
new file mode 100644
index 000000000..f55d14f12
--- /dev/null
+++ b/pstoraster/zsysvm.c
@@ -0,0 +1,164 @@
+/* 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 supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* 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 ------ */
+
+const op_def 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},
+ op_def_end(0)
+};
diff --git a/pstoraster/ztoken.c b/pstoraster/ztoken.c
new file mode 100644
index 000000000..fe01e18ef
--- /dev/null
+++ b/pstoraster/ztoken.c
@@ -0,0 +1,241 @@
+/* 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 supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Token reading operators */
+#include "ghost.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 ------ */
+
+const op_def ztoken_op_defs[] =
+{
+ {"1token", ztoken},
+ {"1.tokenexec", ztokenexec},
+ /* Internal operators */
+ {"2%ztokenexec_continue", ztokenexec_continue},
+ op_def_end(0)
+};
diff --git a/pstoraster/ztrap.c b/pstoraster/ztrap.c
new file mode 100644
index 000000000..8cb77447e
--- /dev/null
+++ b/pstoraster/ztrap.c
@@ -0,0 +1,72 @@
+/* Copyright (C) 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Operators for setting trapping parameters and zones */
+#include "ghost.h"
+#include "oper.h"
+#include "ialloc.h"
+#include "iparam.h"
+#include "gstrap.h"
+
+/* Define the current trap parameters. */
+/****** THIS IS BOGUS ******/
+gs_trap_params_t i_trap_params;
+
+/* <dict> .settrapparams - */
+private int
+zsettrapparams(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 = gs_settrapparams(&i_trap_params, (gs_param_list *) & list);
+ iparam_list_release(&list);
+ if (code < 0)
+ return code;
+ pop(1);
+ return 0;
+}
+
+/* - settrapzone - */
+private int
+zsettrapzone(os_ptr op)
+{
+/****** NYI ******/
+ return_error(e_undefined);
+}
+
+/* ------ Initialization procedure ------ */
+
+const op_def ztrap_op_defs[] =
+{
+ op_def_begin_ll3(),
+ {"1.settrapparams", zsettrapparams},
+ {"0settrapzone", zsettrapzone},
+ op_def_end(0)
+};
diff --git a/pstoraster/ztype.c b/pstoraster/ztype.c
new file mode 100644
index 000000000..716daee0f
--- /dev/null
+++ b/pstoraster/ztype.c
@@ -0,0 +1,510 @@
+/* Copyright (C) 1989, 1995, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Type, attribute, and conversion operators */
+#include "math_.h"
+#include "memory_.h"
+#include "string_.h"
+#include "gsexit.h"
+#include "ghost.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 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)
+
+/* 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. */
+private void
+ztype_init(void)
+{
+ static const char *const tnames[] = { type_name_strings };
+ ref type_names;
+ int i;
+
+ ialloc_ref_array(&type_names, a_readonly, t_next_index,
+ "type names");
+ for (i = 0; i < t_next_index; i++) {
+ if (i >= countof(tnames) || 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);
+ }
+ }
+ if (dict_put_string(systemdict, "typenames", &type_names) < 0) {
+ lprintf("Entering typenames in systemdict failed.\n");
+ gs_exit(1);
+ }
+}
+
+/* <obj> <typenames> .type <name> */
+private int
+ztype(register os_ptr op)
+{
+ ref tnref;
+ int code = array_get(op, (long)r_btype(op - 1), &tnref);
+
+ if (code < 0)
+ return code;
+ if (!r_has_type(&tnref, t_name)) {
+ /* Must be either a stack underflow or a t_[a]struct. */
+ check_op(2);
+ { /* Get the type name from the structure. */
+ const char *sname =
+ gs_struct_type_name_string(gs_object_type(imemory,
+ op[-1].value.pstruct));
+ int code = name_ref((const byte *)sname, strlen(sname),
+ (ref *) (op - 1), 0);
+
+ if (code < 0)
+ return code;
+ }
+ r_set_attrs(op - 1, a_executable);
+ } else {
+ ref_assign(op - 1, &tnref);
+ }
+ pop(1);
+ 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);
+ if (r_has_type(op, t_dictionary)) {
+ /*
+ * Setting noaccess on a read-only dictionary is an attempt to
+ * change its value, which is forbidden (this is a subtle
+ * point confirmed with Adobe). Also, don't allow removing
+ * read access to permanent dictionaries.
+ */
+ if (dict_is_permanent_on_dstack(op) ||
+ !r_has_attr(dict_access_ref(op), a_write)
+ )
+ 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);
+ }
+ }
+ }
+ if (!REAL_CAN_BE_INT(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 ------ */
+
+const op_def 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},
+ {"2.type", ztype},
+ {"1wcheck", zwcheck},
+ {"1xcheck", zxcheck},
+ op_def_end(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
+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..e05fe6dd3
--- /dev/null
+++ b/pstoraster/zupath.c
@@ -0,0 +1,673 @@
+/* Copyright (C) 1990, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Operators related to user paths */
+#include "ghost.h"
+#include "oper.h"
+#include "idict.h"
+#include "dstack.h"
+#include "igstate.h"
+#include "iname.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 */
+
+/* Imported data */
+extern const gx_device gs_hit_device;
+extern const int gs_hit_detected;
+
+/* Forward references */
+private int upath_append(P2(os_ptr, os_ptr));
+private int upath_stroke(P2(os_ptr, gs_matrix *));
+
+/* ---------------- 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));
+
+/* <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> */
+/* <x> <y> <userpath> <matrix> inustroke <bool> */
+/* <userpath1> <userpath2> inustroke <bool> */
+/* <userpath1> <userpath2> <matrix> 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;
+ gs_matrix mat;
+ gx_device hdev;
+
+ if (code < 0)
+ return code;
+ if ((spop = upath_stroke(op, &mat)) < 0) {
+ gs_grestore(igs);
+ return spop;
+ }
+ if ((npop = in_path(op - spop, op, &hdev)) < 0) {
+ gs_grestore(igs);
+ return npop;
+ }
+ if (npop > 1) /* matrix was supplied */
+ code = gs_concat(igs, &mat);
+ if (code >= 0)
+ code = gs_stroke(igs);
+ return in_upath_result(op, npop + spop, code);
+}
+
+/* ------ Internal routines ------ */
+
+/* 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;
+ double 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 *ipath = igs->path;
+ gx_path save;
+
+ gx_path_init_local(&save, imemory);
+ gx_path_assign_preserve(&save, ipath);
+ gs_newpath(igs);
+ code = upath_append(oppath, op);
+ if (code >= 0)
+ code = gx_clip_to_path(igs);
+ gx_path_assign_free(igs->path, &save);
+ npop = 1;
+ }
+ if (code < 0) {
+ gs_grestore(igs);
+ return code;
+ }
+ /* Install the hit detection device. */
+ gx_set_device_color_1(igs);
+ gx_device_init((gx_device *) phdev, (const gx_device *)&gs_hit_device,
+ NULL, true);
+ phdev->width = phdev->height = max_int;
+ 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)
+{
+ bool result;
+
+ gs_grestore(igs); /* matches gsave in in_path */
+ if (code == gs_hit_detected)
+ result = true;
+ else if (code == 0) /* completed painting without a hit */
+ result = false;
+ else /* 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_op_setbbox = 0,
+ upath_op_moveto = 1,
+ upath_op_rmoveto = 2,
+ upath_op_lineto = 3,
+ upath_op_rlineto = 4,
+ upath_op_curveto = 5,
+ upath_op_rcurveto = 6,
+ upath_op_arc = 7,
+ upath_op_arcn = 8,
+ upath_op_arct = 9,
+ upath_op_closepath = 10,
+ upath_op_ucache = 11
+} upath_op;
+
+#define UPATH_MAX_OP 11
+#define UPATH_REPEAT 32
+static const byte up_nargs[UPATH_MAX_OP + 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 const op_proc_p up_ops[UPATH_MAX_OP + 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, NULL)) >= 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)
+{
+ gx_path save;
+ int code, npop;
+
+ /* Save and reset the path. */
+ gx_path_init_local(&save, imemory);
+ gx_path_assign_preserve(&save, igs->path);
+ if ((code = npop = upath_stroke(op, NULL)) < 0 ||
+ (code = gs_strokepath(igs)) < 0
+ ) {
+ gx_path_assign_free(igs->path, &save);
+ return code;
+ }
+ gx_path_free(&save, "ustrokepath");
+ pop(npop);
+ return 0;
+}
+
+/* <with_ucache> upath <userpath> */
+/* We do all the work in a procedure that is also used to construct */
+/* the UnpaintedPath user path for ImageType 2 images. */
+int make_upath(P4(ref * rupath, gs_state * pgs, gx_path * ppath,
+ bool with_ucache));
+private int
+zupath(register os_ptr op)
+{
+ check_type(*op, t_boolean);
+ return make_upath(op, igs, igs->path, op->value.boolval);
+}
+int
+make_upath(ref * rupath, gs_state * pgs, gx_path * ppath, bool with_ucache)
+{
+ int size = (with_ucache ? 6 : 5);
+ gs_path_enum penum;
+ int op;
+ ref *next;
+ int code;
+
+ /* Compute the size of the user path array. */
+ {
+ gs_fixed_point pts[3];
+
+ gx_path_enum_init(&penum, ppath);
+ while ((op = gx_path_enum_next(&penum, pts)) != 0) {
+ switch (op) {
+ case gs_pe_moveto:
+ case gs_pe_lineto:
+ size += 3;
+ continue;
+ case gs_pe_curveto:
+ size += 7;
+ continue;
+ case gs_pe_closepath:
+ size += 1;
+ continue;
+ default:
+ return_error(e_unregistered);
+ }
+ }
+ }
+ code = ialloc_ref_array(rupath, a_all | a_executable, size,
+ "make_upath");
+ if (code < 0)
+ return code;
+ /* Construct the path. */
+ next = rupath->value.refs;
+ if (with_ucache) {
+ if ((code = name_enter_string("ucache", next)) < 0)
+ return code;
+ r_set_attrs(next, a_executable | l_new);
+ ++next;
+ } {
+ gs_rect bbox;
+
+ gs_upathbbox(pgs, &bbox, true);
+ make_real_new(next, bbox.p.x);
+ make_real_new(next + 1, bbox.p.y);
+ make_real_new(next + 2, bbox.q.x);
+ make_real_new(next + 3, bbox.q.y);
+ next += 4;
+ if ((code = name_enter_string("setbbox", next)) < 0)
+ return code;
+ r_set_attrs(next, a_executable | l_new);
+ ++next;
+ }
+ {
+ gs_point pts[3];
+
+ /* Patch the path in the gstate to set up the enumerator. */
+ gx_path *save_path = pgs->path;
+
+ pgs->path = ppath;
+ gs_path_enum_copy_init(&penum, pgs, false);
+ pgs->path = save_path;
+ while ((op = gs_path_enum_next(&penum, pts)) != 0) {
+ const char *opstr;
+
+ switch (op) {
+ case gs_pe_moveto:
+ opstr = "moveto";
+ goto ml;
+ case gs_pe_lineto:
+ opstr = "lineto";
+ ml:make_real_new(next, pts[0].x);
+ make_real_new(next + 1, pts[0].y);
+ next += 2;
+ break;
+ case gs_pe_curveto:
+ opstr = "curveto";
+ make_real_new(next, pts[0].x);
+ make_real_new(next + 1, pts[0].y);
+ make_real_new(next + 2, pts[1].x);
+ make_real_new(next + 3, pts[1].y);
+ make_real_new(next + 4, pts[2].x);
+ make_real_new(next + 5, pts[2].y);
+ next += 6;
+ break;
+ case gs_pe_closepath:
+ opstr = "closepath";
+ break;
+ default:
+ return_error(e_unregistered);
+ }
+ if ((code = name_enter_string(opstr, next)) < 0)
+ return code;
+ r_set_attrs(next, a_executable);
+ ++next;
+ }
+ }
+ 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 > UPATH_REPEAT)
+ repcount = opx - UPATH_REPEAT;
+ else if (opx > UPATH_MAX_OP)
+ 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_MAX_OP; opx++)
+ if (oproc == up_ops[opx])
+ break;
+ if (opx > UPATH_MAX_OP || 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 or return */
+/* a transformation if one is supplied. */
+private int
+upath_stroke(os_ptr op, gs_matrix *pmat)
+{
+ int code, npop;
+ gs_matrix mat;
+
+ if ((code = read_matrix(op, &mat)) >= 0) {
+ if ((code = upath_append(op - 1, op)) >= 0) {
+ if (pmat)
+ *pmat = mat;
+ else
+ code = gs_concat(igs, &mat);
+ }
+ npop = 2;
+ } else {
+ if ((code = upath_append(op, op)) >= 0)
+ if (pmat)
+ gs_make_identity(pmat);
+ npop = 1;
+ }
+ return (code < 0 ? code : npop);
+}
+
+/* ---------------- Initialization procedure ---------------- */
+
+const op_def 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},
+ {"0ucache", zucache},
+ {"1ueofill", zueofill},
+ {"1ufill", zufill},
+ {"1upath", zupath},
+ {"1ustroke", zustroke},
+ {"1ustrokepath", zustrokepath},
+ op_def_end(0)
+};
diff --git a/pstoraster/zusparam.c b/pstoraster/zusparam.c
new file mode 100644
index 000000000..5cdbd13ca
--- /dev/null
+++ b/pstoraster/zusparam.c
@@ -0,0 +1,655 @@
+/* Copyright (C) 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* User and system parameter operators */
+#include "memory_.h"
+#include "string_.h"
+#include "ghost.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 "icontext.h" /* for set_user_params prototype */
+#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));
+
+/* Define an individual user or system parameter. */
+/* Eventually this will be made public. */
+#define param_def_common\
+ const char *pname
+typedef struct param_def_s {
+ param_def_common;
+} param_def_t;
+typedef struct long_param_def_s {
+ param_def_common;
+ long min_value, max_value;
+ long (*current)(P0());
+ int (*set)(P1(long));
+} long_param_def_t;
+
+#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_t;
+typedef struct string_param_def_s {
+ param_def_common;
+ void (*current)(P1(gs_param_string *));
+ int (*set)(P1(gs_param_string *));
+} string_param_def_t;
+
+/* Define a parameter set (user or system). */
+typedef struct param_set_s {
+ const long_param_def_t *long_defs;
+ uint long_count;
+ const bool_param_def_t *bool_defs;
+ uint bool_count;
+ const string_param_def_t *string_defs;
+ uint string_count;
+} param_set;
+
+/* Forward references */
+private int setparams(P2(gs_param_list *, const param_set *));
+private int currentparams(P2(os_ptr, const param_set *));
+private int currentparam1(P2(os_ptr, const param_set *));
+
+/* ------ Passwords ------ */
+
+/* <string|int> .checkpassword <0|1|2> */
+private int
+zcheckpassword(register os_ptr op)
+{
+ ref params[2];
+ array_param_list list;
+ gs_param_list *const plist = (gs_param_list *)&list;
+ int result = 0;
+ int code = name_ref((const byte *)"Password", 8, &params[0], 0);
+ password pass;
+
+ if (code < 0)
+ return code;
+ params[1] = *op;
+ array_param_list_read(&list, params, 2, NULL, false);
+ if (dict_read_password(&pass, systemdict, "StartJobPassword") >= 0 &&
+ param_check_password(plist, &pass) == 0
+ )
+ result = 1;
+ if (dict_read_password(&pass, systemdict, "SystemParamsPassword") >= 0 &&
+ param_check_password(plist, &pass) == 0
+ )
+ result = 2;
+ iparam_list_release(&list);
+ make_int(op, result);
+ return 0;
+}
+
+/* ------ 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_t system_long_params[] =
+{
+ {"BuildTime", min_long, max_long, current_BuildTime, NULL},
+{"MaxFontCache", 0, MAX_UINT_PARAM, current_MaxFontCache, set_MaxFontCache},
+ {"CurFontCache", 0, MAX_UINT_PARAM, current_CurFontCache, NULL},
+ {"Revision", min_long, max_long, current_Revision, NULL},
+ /* Extensions */
+ {"MaxGlobalVM", 0, max_long, current_MaxGlobalVM, set_MaxGlobalVM}
+};
+
+/* Boolean values */
+private bool
+current_ByteOrder(void)
+{
+ return !arch_is_big_endian;
+}
+private const bool_param_def_t 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 *const rfs = "IEEE";
+
+#else
+ static const char *const rfs = "not IEEE";
+
+#endif
+
+ pval->data = (const byte *)rfs;
+ pval->size = strlen(rfs);
+ pval->persistent = true;
+}
+private const string_param_def_t 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;
+ gs_param_list *const plist = (gs_param_list *)&list;
+ password pass;
+
+ check_type(*op, t_dictionary);
+ code = dict_param_list_read(&list, op, NULL, false);
+ if (code < 0)
+ return code;
+ code = dict_read_password(&pass, systemdict, "SystemParamsPassword");
+ if (code < 0)
+ return code;
+ code = param_check_password(plist, &pass);
+ if (code != 0) {
+ if (code > 0)
+ code = gs_note_error(e_invalidaccess);
+ goto out;
+ }
+ code = param_read_password(plist, "StartJobPassword", &pass);
+ switch (code) {
+ default: /* invalid */
+ goto out;
+ case 1: /* missing */
+ break;
+ case 0:
+ code = dict_write_password(&pass, systemdict,
+ "StartJobPassword");
+ if (code < 0)
+ goto out;
+ }
+ code = param_read_password(plist, "SystemParamsPassword", &pass);
+ switch (code) {
+ default: /* invalid */
+ goto out;
+ case 1: /* missing */
+ break;
+ case 0:
+ code = dict_write_password(&pass, systemdict,
+ "SystemParamsPassword");
+ if (code < 0)
+ goto out;
+ }
+ code = setparams(plist, &system_param_set);
+ out:
+ iparam_list_release(&list);
+ if (code < 0)
+ return code;
+ pop(1);
+ return 0;
+}
+
+/* - .currentsystemparams <name1> <value1> ... */
+private int
+zcurrentsystemparams(os_ptr op)
+{
+ return currentparams(op, &system_param_set);
+}
+
+/* <name> .getsystemparam <value> */
+private int
+zgetsystemparam(os_ptr op)
+{
+ return currentparam1(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;
+}
+private long
+current_WaitTimeout(void)
+{
+ return 0;
+}
+private int
+set_WaitTimeout(long val)
+{
+ return 0;
+}
+private long
+current_MinScreenLevels(void)
+{
+ return gs_currentminscreenlevels();
+}
+private int
+set_MinScreenLevels(long val)
+{
+ gs_setminscreenlevels((uint) val);
+ return 0;
+}
+private const long_param_def_t 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_long,
+ current_MaxLocalVM, set_MaxLocalVM},
+ {"VMReclaim", -2, 0,
+ current_VMReclaim, set_vm_reclaim},
+ {"VMThreshold", -1, max_long,
+ current_VMThreshold, set_vm_threshold},
+ {"WaitTimeout", 0, MAX_UINT_PARAM,
+ current_WaitTimeout, set_WaitTimeout},
+ /* Extensions */
+ {"MinScreenLevels", 0, MAX_UINT_PARAM,
+ current_MinScreenLevels, set_MinScreenLevels}
+};
+
+/* 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_t user_bool_params[] =
+{
+ {"AccurateScreens", current_AccurateScreens, set_AccurateScreens}
+};
+
+/* 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),
+ 0, 0
+};
+
+/* <dict> .setuserparams - */
+/* We break this out for use when switching contexts. */
+int
+set_user_params(const ref * 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);
+ iparam_list_release(&list);
+ return code;
+}
+private int
+zsetuserparams(register os_ptr op)
+{
+ int code = set_user_params(op);
+
+ if (code >= 0)
+ pop(1);
+ return code;
+}
+
+/* - .currentuserparams <name1> <value1> ... */
+private int
+zcurrentuserparams(os_ptr op)
+{
+ return currentparams(op, &user_param_set);
+}
+
+/* <name> .getuserparam <value> */
+private int
+zgetuserparam(os_ptr op)
+{
+ return currentparam1(op, &user_param_set);
+}
+
+/* ------ Initialization procedure ------ */
+
+const op_def zusparam_op_defs[] =
+{
+ /* User and system parameters are accessible even in Level 1 */
+ /* (if this is a Level 2 system). */
+ {"0.currentsystemparams", zcurrentsystemparams},
+ {"0.currentuserparams", zcurrentuserparams},
+ {"1.getsystemparam", zgetsystemparam},
+ {"1.getuserparam", zgetuserparam},
+ {"1.setsystemparams", zsetsystemparams},
+ {"1.setuserparams", zsetuserparams},
+ /* The rest of the operators are defined only in Level 2. */
+ op_def_begin_level2(),
+ {"1.checkpassword", zcheckpassword},
+ op_def_end(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 * pset)
+{
+ int i, code;
+
+ for (i = 0; i < pset->long_count; i++) {
+ const long_param_def_t *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_t *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 bool
+pname_matches(const char *pname, const ref * psref)
+{
+ return
+ (psref == 0 ||
+ !bytes_compare((const byte *)pname, strlen(pname),
+ psref->value.const_bytes, r_size(psref)));
+}
+private int
+current_param_list(os_ptr op, const param_set * pset,
+ const ref * psref /*t_string */ )
+{
+ stack_param_list list;
+ gs_param_list *const plist = (gs_param_list *)&list;
+ int i;
+
+ stack_param_list_write(&list, &o_stack, NULL);
+ for (i = 0; i < pset->long_count; i++) {
+ const char *pname = pset->long_defs[i].pname;
+
+ if (pname_matches(pname, psref)) {
+ long val = (*pset->long_defs[i].current)();
+ int code = param_write_long(plist, pname, &val);
+
+ if (code < 0)
+ return code;
+ }
+ }
+ for (i = 0; i < pset->bool_count; i++) {
+ const char *pname = pset->bool_defs[i].pname;
+
+ if (pname_matches(pname, psref)) {
+ bool val = (*pset->bool_defs[i].current)();
+ int code = param_write_bool(plist, pname, &val);
+
+ if (code < 0)
+ return code;
+ }
+ }
+ for (i = 0; i < pset->string_count; i++) {
+ const char *pname = pset->string_defs[i].pname;
+
+ if (pname_matches(pname, psref)) {
+ gs_param_string val;
+ int code;
+
+ (*pset->string_defs[i].current)(&val);
+ code = param_write_string(plist, pname, &val);
+ if (code < 0)
+ return code;
+ }
+ }
+ return 0;
+}
+
+/* Get the current values of a parameter set to the stack. */
+private int
+currentparams(os_ptr op, const param_set * pset)
+{
+ return current_param_list(op, pset, NULL);
+}
+
+/* Get the value of a single parameter to the stack, or signal an error. */
+private int
+currentparam1(os_ptr op, const param_set * pset)
+{
+ ref sref;
+ int code;
+
+ check_type(*op, t_name);
+ check_ostack(2);
+ name_string_ref((const ref *)op, &sref);
+ code = current_param_list(op, pset, &sref);
+ if (code < 0)
+ return code;
+ if (osp == op)
+ return_error(e_undefined);
+ /* We know osp == op + 2. */
+ ref_assign(op, op + 2);
+ pop(2);
+ return code;
+}
diff --git a/pstoraster/zvmem.c b/pstoraster/zvmem.c
new file mode 100644
index 000000000..109816969
--- /dev/null
+++ b/pstoraster/zvmem.c
@@ -0,0 +1,404 @@
+/* Copyright (C) 1989, 1995, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* "Virtual memory" operators */
+#include "ghost.h"
+#include "gsstruct.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 "stream.h" /* for files.h */
+#include "files.h" /* for e-stack processing */
+#include "store.h"
+#include "gsmalloc.h" /* for gs_memory_default */
+#include "gsmatrix.h" /* for gsstate.h */
+#include "gsstate.h"
+
+/* Define whether we validate memory before/after save/restore. */
+/* Note that we only actually do this if DEBUG is set and -Z? is selected. */
+private bool I_VALIDATE_BEFORE_SAVE = true;
+private bool I_VALIDATE_AFTER_SAVE = true;
+private bool I_VALIDATE_BEFORE_RESTORE = true;
+private bool I_VALIDATE_AFTER_RESTORE = true;
+
+/* 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);
+
+/* Clean up the stacks and validate storage. */
+private void
+ivalidate_clean_spaces(void)
+{
+ if (gs_debug_c('?')) {
+ ref_stack_cleanup(&d_stack);
+ ref_stack_cleanup(&e_stack);
+ ref_stack_cleanup(&o_stack);
+ ivalidate_spaces();
+ }
+}
+
+/* - save <save> */
+int
+zsave(register os_ptr op)
+{
+ uint space = icurrent_space;
+ vm_save_t *vmsave;
+ ulong sid;
+ int code;
+ gs_state *prev;
+
+ if (I_VALIDATE_BEFORE_SAVE)
+ ivalidate_clean_spaces();
+ 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 = gs_gsave_for_save(igs, &prev);
+ if (code < 0)
+ return code;
+ code = gs_gsave(igs);
+ if (code < 0)
+ return code;
+ vmsave->gsave = prev;
+ push(1);
+ make_tav(op, t_save, 0, saveid, sid);
+ if (I_VALIDATE_AFTER_SAVE)
+ ivalidate_clean_spaces();
+ 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;
+ if_debug2('u', "[u]vmrestore 0x%lx, id = %lu\n",
+ (ulong) alloc_save_client_data(asave),
+ (ulong) op->value.saveid);
+ if (I_VALIDATE_BEFORE_RESTORE)
+ ivalidate_clean_spaces();
+ /* 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_for_restore(igs, vmsave->gsave);
+ /*
+ * 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 */
+ if (I_VALIDATE_AFTER_RESTORE)
+ ivalidate_clean_spaces();
+ 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)
+{
+ ref_stack_enum_t rsenum;
+
+ ref_stack_enum_begin(&rsenum, pstack);
+ do {
+ const ref *stkp = rsenum.ptr;
+ uint size = rsenum.size;
+
+ for (; 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 or closed literal */
+ /* files on the e-stack. */
+ {
+ stream *s;
+
+ if (is_estack &&
+ (r_has_attr(stkp, a_executable) ||
+ file_is_invalid(s, stkp))
+ )
+ 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);
+ }
+ } while (ref_stack_enum_next(&rsenum));
+ 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)
+{
+ ref_stack_enum_t rsenum;
+
+ ref_stack_enum_begin(&rsenum, pstack);
+ do {
+ ref *stkp = rsenum.ptr;
+ uint size = rsenum.size;
+
+ for (; 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);
+ }
+ }
+ } while (ref_stack_enum_next(&rsenum));
+}
+
+/* - 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 ------ */
+
+const op_def zvmem_op_defs[] =
+{
+ {"1.forgetsave", zforgetsave},
+ {"1restore", zrestore},
+ {"0save", zsave},
+ {"0vmstatus", zvmstatus},
+ op_def_end(0)
+};
diff --git a/pstoraster/zvmem2.c b/pstoraster/zvmem2.c
new file mode 100644
index 000000000..84f960783
--- /dev/null
+++ b/pstoraster/zvmem2.c
@@ -0,0 +1,153 @@
+/* Copyright (C) 1992, 1993, 1994, 1997, 1998 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given
+ to you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises supports the work of the GNU Project, but is not
+ affiliated with the Free Software Foundation or the GNU Project. GNU
+ Ghostscript, as distributed by Aladdin Enterprises, does not require any
+ GNU software to build or run it.
+*/
+
+/*$Id$ */
+/* Level 2 "Virtual memory" operators */
+#include "ghost.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
+#define MIN_VM_THRESHOLD 1
+#define MAX_VM_THRESHOLD max_long
+
+/* ------ Local/global VM control ------ */
+
+/* <bool> .setglobal - */
+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 - */
+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 -
+ *
+ * This is implemented as a PostScript procedure that calls setuserparams.
+ */
+int
+set_vm_threshold(long val)
+{
+ gs_memory_gc_status_t stat;
+
+ if (val < -1)
+ return_error(e_rangecheck);
+ else if (val == -1)
+ val = (gs_debug_c('.') ? DEFAULT_VM_THRESHOLD_SMALL :
+ DEFAULT_VM_THRESHOLD_LARGE);
+ 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;
+}
+
+/*
+ * <int> .vmreclaim -
+ *
+ * This implements only immediate garbage collection: enabling and
+ * disabling GC is implemented by calling setuserparams.
+ */
+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);
+ }
+ return_error(e_rangecheck);
+}
+
+/* ------ Initialization procedure ------ */
+
+/* The VM operators are defined even if the initial language level is 1, */
+/* because we need them during initialization. */
+const op_def 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(),
+ {"1.vmreclaim", zvmreclaim},
+ op_def_end(0)
+};
diff --git a/scheduler/Makefile b/scheduler/Makefile
new file mode 100644
index 000000000..ad2a5a36e
--- /dev/null
+++ b/scheduler/Makefile
@@ -0,0 +1,143 @@
+#
+# "$Id$"
+#
+# Scheduler Makefile for the Common UNIX Printing System (CUPS).
+#
+# Copyright 1997-2000 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 banners.o cert.o classes.o client.o conf.o devices.o \
+ dirsvc.o main.o ipp.o listen.o job.o log.o ppds.o printers.o
+MIMEOBJS = filter.o mime.o type.o
+OBJS = $(CUPSDOBJS) $(MIMEOBJS) cups-lpd.o cups-polld.o testmime.o \
+ testspeed.o
+
+
+#
+# Make everything...
+#
+
+all: cupsd cups-lpd cups-polld libmime.a testmime testspeed
+
+
+#
+# Clean all object files...
+#
+
+clean:
+ $(RM) $(OBJS) cupsd cups-lpd cups-polld libmime.a testmime testspeed
+
+
+#
+# Install the scheduler...
+#
+
+install:
+ -$(MKDIR) $(SBINDIR)
+ $(INSTALL_BIN) cupsd $(SBINDIR)
+ $(CHMOD) g-rwx,o-rwx $(SBINDIR)/cupsd
+ -$(MKDIR) $(SERVERBIN)/daemon
+ $(INSTALL_BIN) cups-lpd $(SERVERBIN)/daemon
+ $(INSTALL_BIN) cups-polld $(SERVERBIN)/daemon
+ -$(MKDIR) $(SERVERROOT)/certs
+ -$(MKDIR) $(SERVERROOT)/interfaces
+ -$(MKDIR) $(SERVERROOT)/ppd
+ -$(MKDIR) $(LOGDIR)
+ -$(MKDIR) $(REQUESTS)
+
+
+#
+# Make the scheduler executable, "cupsd".
+#
+
+cupsd: $(CUPSDOBJS) libmime.a ../cups/$(LIBCUPS)
+ echo Linking $@...
+ $(CC) $(LDFLAGS) -o cupsd $(CUPSDOBJS) libmime.a $(LIBZ) $(LIBS)
+
+$(CUPSDOBJS): auth.h banners.h cert.h classes.h client.h conf.h \
+ cupsd.h dirsvc.h job.h mime.h printers.h \
+ ../cups/cups.h ../cups/http.h ../cups/ipp.h \
+ ../cups/language.h ../cups/string.h
+
+
+#
+# Make the line printer daemon, "cups-lpd".
+#
+
+cups-lpd: cups-lpd.o ../cups/$(LIBCUPS)
+ echo Linking $@...
+ $(CC) $(LDFLAGS) -o cups-lpd cups-lpd.o $(LIBS)
+
+cups-lpd.o: ../cups/cups.h ../cups/http.h ../cups/ipp.h \
+ ../cups/language.h ../cups/string.h
+
+
+#
+# Make the polling daemon, "cups-polld".
+#
+
+cups-polld: cups-polld.o ../cups/$(LIBCUPS)
+ echo Linking $@...
+ $(CC) $(LDFLAGS) -o cups-polld cups-polld.o $(LIBS)
+
+cups-polld.o: ../cups/cups.h ../cups/http.h ../cups/ipp.h \
+ ../cups/language.h ../cups/string.h
+
+
+#
+# libmime.a
+#
+
+libmime.a: $(MIMEOBJS)
+ echo Archiving $@...
+ $(RM) $@
+ $(AR) $(ARFLAGS) $@ $(MIMEOBJS)
+ $(RANLIB) $@
+
+$(MIMEOBJS): mime.h
+
+
+#
+# testmime
+#
+
+testmime: testmime.o libmime.a
+ echo Linking $@...
+ $(CC) $(LDFLAGS) -o $@ testmime.o libmime.a
+
+testmime.o: mime.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..bbba269ef
--- /dev/null
+++ b/scheduler/auth.c
@@ -0,0 +1,745 @@
+/*
+ * "$Id$"
+ *
+ * Authorization routines for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-2000 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.
+ * pam_func() - PAM conversation function.
+ */
+
+/*
+ * 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 */
+#if HAVE_LIBPAM
+# include <security/pam_appl.h>
+#endif /* HAVE_LIBPAM */
+
+
+/*
+ * Local functions...
+ */
+
+static authmask_t *add_allow(location_t *loc);
+static authmask_t *add_deny(location_t *loc);
+#if HAVE_LIBPAM
+static int pam_func(int, const struct pam_message **,
+ struct pam_response **, void *);
+#endif /* HAVE_LIBPAM */
+
+
+/*
+ * '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(L_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(L_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(L_DEBUG, "AllowIP: %s allow %08x/%08x", loc->location,
+ address, netmask);
+}
+
+
+/*
+ * 'CheckAuth()' - Check authorization masks.
+ */
+
+int /* O - 1 if mask matches, 0 otherwise */
+CheckAuth(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);
+}
+
+
+/*
+ * '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(L_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(L_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 */
+ struct group *grp; /* Group data */
+#if HAVE_LIBPAM
+ pam_handle_t *pamh; /* PAM authentication handle */
+ int pamerr; /* PAM error code */
+ struct pam_conv pamdata; /* PAM conversation data */
+#else
+ char *pass; /* Encrypted password */
+# ifdef HAVE_SHADOW_H
+ struct spwd *spw; /* Shadow password data */
+# endif /* HAVE_SHADOW_H */
+#endif /* HAVE_LIBPAM */
+
+
+ /*
+ * 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 &&
+ loc->location[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...
+ */
+
+ 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 (best->order_type)
+ {
+ default :
+ auth = AUTH_DENY; /* anti-compiler-warning-code */
+ break;
+
+ case AUTH_ALLOW : /* Order Deny,Allow */
+ auth = AUTH_ALLOW;
+
+ if (CheckAuth(address, con->http.hostname, hostlen,
+ best->num_deny, best->deny))
+ auth = AUTH_DENY;
+
+ if (CheckAuth(address, con->http.hostname, hostlen,
+ best->num_allow, best->allow))
+ auth = AUTH_ALLOW;
+ break;
+
+ case AUTH_DENY : /* Order Allow,Deny */
+ auth = AUTH_DENY;
+
+ if (CheckAuth(address, con->http.hostname, hostlen,
+ best->num_allow, best->allow))
+ auth = AUTH_ALLOW;
+
+ if (CheckAuth(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')
+ return (HTTP_UNAUTHORIZED); /* Non-anonymous needs 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(L_WARN, "IsAuthorized: Unknown username \"%s\"; access denied.",
+ con->username);
+ return (HTTP_UNAUTHORIZED);
+ }
+
+ DEBUG_printf(("IsAuthorized: Checking \"%s\", address = %08x, hostname = \"%s\"\n",
+ con->username, address, con->http.hostname));
+
+ if ((address != 0x7f000001 &&
+ strcasecmp(con->http.hostname, "localhost") != 0) ||
+ strncmp(con->http.fields[HTTP_FIELD_AUTHORIZATION], "Local", 5) != 0)
+ {
+ /*
+ * Not doing local certificate-based authentication; check the password...
+ */
+
+ if (!con->password[0])
+ return (HTTP_UNAUTHORIZED);
+
+#if HAVE_LIBPAM
+ /*
+ * Only use PAM to do authentication. This allows MD5 passwords, among
+ * other things...
+ */
+
+ pamdata.conv = pam_func;
+ pamdata.appdata_ptr = con;
+
+ pamerr = pam_start("cups", con->username, &pamdata, &pamh);
+ if (pamerr != PAM_SUCCESS)
+ {
+ LogMessage(L_ERROR, "IsAuthorized: pam_start() returned %d (%s)!\n",
+ pamerr, pam_strerror(pamh, pamerr));
+ pam_end(pamh, 0);
+ return (HTTP_UNAUTHORIZED);
+ }
+
+ pamerr = pam_authenticate(pamh, PAM_SILENT);
+ if (pamerr != PAM_SUCCESS)
+ {
+ LogMessage(L_ERROR, "IsAuthorized: pam_authenticate() returned %d (%s)!\n",
+ pamerr, pam_strerror(pamh, pamerr));
+ pam_end(pamh, 0);
+ return (HTTP_UNAUTHORIZED);
+ }
+
+ pamerr = pam_acct_mgmt(pamh, PAM_SILENT);
+ if (pamerr != PAM_SUCCESS)
+ {
+ LogMessage(L_ERROR, "IsAuthorized: pam_acct_mgmt() returned %d (%s)!\n",
+ pamerr, pam_strerror(pamh, pamerr));
+ pam_end(pamh, 0);
+ return (HTTP_UNAUTHORIZED);
+ }
+
+ pam_end(pamh, PAM_SUCCESS);
+#else
+# ifdef HAVE_SHADOW_H
+ spw = getspnam(con->username);
+ endspent();
+
+ if (spw == NULL && strcmp(pw->pw_passwd, "x") == 0)
+ { /* Don't allow blank passwords! */
+ LogMessage(L_WARN, "IsAuthorized: Username \"%s\" has no shadow password; access denied.",
+ con->username);
+ 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' && pw->pw_passwd[0] == '\0')
+# else
+ if (pw->pw_passwd[0] == '\0') /* Don't allow blank passwords! */
+# endif /* HAVE_SHADOW_H */
+ { /* Don't allow blank passwords! */
+ LogMessage(L_WARN, "IsAuthorized: Username \"%s\" has no password; access denied.",
+ con->username);
+ return (HTTP_UNAUTHORIZED);
+ }
+
+ /*
+ * 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)));
+
+ pass = crypt(con->password, pw->pw_passwd);
+
+ if (pass == NULL ||
+ 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)));
+
+ pass = crypt(con->password, spw->sp_pwdp);
+
+ if (pass == NULL ||
+ strcmp(spw->sp_pwdp, crypt(con->password, spw->sp_pwdp)) != 0)
+ return (HTTP_UNAUTHORIZED);
+ }
+ else
+# endif /* HAVE_SHADOW_H */
+ return (HTTP_UNAUTHORIZED);
+ }
+#endif /* HAVE_LIBPAM */
+ }
+
+ /*
+ * OK, the password is good. See if we need normal user access, or group
+ * access... (root always matches)
+ */
+
+ if (best->level == AUTH_USER || strcmp(con->username, "root") == 0)
+ 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(L_WARN, "IsAuthorized: group name \"%s\" does not exist!",
+ best->group_name);
+ return (HTTP_FORBIDDEN);
+ }
+
+ 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_FORBIDDEN);
+}
+
+
+/*
+ * '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);
+}
+
+
+#if HAVE_LIBPAM
+/*
+ * 'pam_func()' - PAM conversation function.
+ */
+
+static int /* O - Success or failure */
+pam_func(int num_msg, /* I - Number of messages */
+ const struct pam_message **msg, /* I - Messages */
+ struct pam_response **resp, /* O - Responses */
+ void *appdata_ptr) /* I - Pointer to connection */
+{
+ int i; /* Looping var */
+ struct pam_response *replies; /* Replies */
+ client_t *client; /* Pointer client connection */
+
+
+ /*
+ * Allocate memory for the responses...
+ */
+
+ if ((replies = malloc(sizeof(struct pam_response) * num_msg)) == NULL)
+ return (PAM_CONV_ERR);
+
+ /*
+ * Answer all of the messages...
+ */
+
+ client = (client_t *)appdata_ptr;
+
+ for (i = 0; i < num_msg; i ++)
+ switch (msg[i]->msg_style)
+ {
+ case PAM_PROMPT_ECHO_ON:
+ replies[i].resp_retcode = PAM_SUCCESS;
+ replies[i].resp = strdup(client->username);
+ break;
+
+ case PAM_PROMPT_ECHO_OFF:
+ replies[i].resp_retcode = PAM_SUCCESS;
+ replies[i].resp = strdup(client->password);
+ break;
+
+ case PAM_TEXT_INFO:
+ case PAM_ERROR_MSG:
+ replies[i].resp_retcode = PAM_SUCCESS;
+ replies[i].resp = NULL;
+ break;
+
+ default:
+ free(replies);
+ return (PAM_CONV_ERR);
+ }
+
+ /*
+ * Return the responses back to PAM...
+ */
+
+ *resp = replies;
+
+ return (PAM_SUCCESS);
+}
+#endif /* HAVE_LIBPAM */
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/scheduler/auth.h b/scheduler/auth.h
new file mode 100644
index 000000000..07feb4551
--- /dev/null
+++ b/scheduler/auth.h
@@ -0,0 +1,115 @@
+/*
+ * "$Id$"
+ *
+ * Authorization definitions for the Common UNIX Printing System (CUPS)
+ * scheduler.
+ *
+ * Copyright 1997-2000 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_NONE 0 /* No authentication */
+#define AUTH_BASIC 1 /* Basic authentication */
+#define AUTH_DIGEST 2 /* Digest authentication */
+
+#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 access control 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 */
+ type, /* Type of authentication */
+ 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 int CheckAuth(unsigned ip, char *name, int namelen,
+ int num_masks, authmask_t *masks);
+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/banners.c b/scheduler/banners.c
new file mode 100644
index 000000000..9d636cc32
--- /dev/null
+++ b/scheduler/banners.c
@@ -0,0 +1,219 @@
+/*
+ * "$Id$"
+ *
+ * Banner routines for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-2000 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:
+ *
+ * AddBanner() - Add a banner to the array.
+ * FindBanner() - Find a named banner.
+ * LoadBanners() - Load all available banner files...
+ * compare() - Compare two banners.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "cupsd.h"
+
+
+/*
+ * Local functions...
+ */
+
+static int compare(const banner_t *b0, const banner_t *b1);
+
+
+/*
+ * 'AddBanner()' - Add a banner to the array.
+ */
+
+void
+AddBanner(const char *name, /* I - Name of banner */
+ const char *filename) /* I - Filename for banner */
+{
+ mime_type_t *filetype; /* Filetype */
+ banner_t *temp; /* New banner data */
+
+
+ /*
+ * See what the filetype is...
+ */
+
+ if ((filetype = mimeFileType(MimeDatabase, filename)) == NULL)
+ {
+ LogMessage(L_WARN, "AddBanner: Banner \"%s\" is of an unknown file type - skipping!",
+ name);
+ return;
+ }
+
+ /*
+ * Allocate memory...
+ */
+
+ if (NumBanners == 0)
+ temp = malloc(sizeof(banner_t));
+ else
+ temp = realloc(Banners, sizeof(banner_t) * (NumBanners + 1));
+
+ if (temp == NULL)
+ {
+ LogMessage(L_ERROR, "AddBanner: Ran out of memory adding a banner!");
+ return;
+ }
+
+ /*
+ * Copy the new banner data over...
+ */
+
+ Banners = temp;
+ temp += NumBanners;
+ NumBanners ++;
+
+ memset(temp, 0, sizeof(banner_t));
+ strncpy(temp->name, name, sizeof(temp->name) - 1);
+ strncpy(temp->filename, filename, sizeof(temp->filename) - 1);
+ temp->filetype = filetype;
+}
+
+
+/*
+ * 'FindBanner()' - Find a named banner.
+ */
+
+banner_t * /* O - Pointer to banner or NULL */
+FindBanner(const char *name) /* I - Name of banner */
+{
+ banner_t key; /* Search key */
+
+
+ strncpy(key.name, name, sizeof(key.name) - 1);
+ key.name[sizeof(key.name) - 1] = '\0';
+
+ return ((banner_t *)bsearch(&key, Banners, NumBanners, sizeof(banner_t),
+ (int (*)(const void *, const void *))compare));
+}
+
+
+/*
+ * 'LoadBanners()' - Load all available banner files...
+ */
+
+void
+LoadBanners(const char *d) /* I - Directory to search */
+{
+ DIR *dir; /* Directory pointer */
+ DIRENT *dent; /* Directory entry */
+ char filename[1024], /* Name of banner */
+ *ext; /* Pointer to extension */
+ struct stat fileinfo; /* File information */
+
+
+ /*
+ * Free old banner info...
+ */
+
+ if (NumBanners)
+ {
+ free(Banners);
+ NumBanners = 0;
+ }
+
+ /*
+ * Try opening the banner directory...
+ */
+
+ if ((dir = opendir(d)) == NULL)
+ {
+ LogMessage(L_ERROR, "LoadBanners: Unable to open banner directory \"%s\": %s",
+ d, strerror(errno));
+ return;
+ }
+
+ /*
+ * Read entries, skipping directories and backup files.
+ */
+
+ while ((dent = readdir(dir)) != NULL)
+ {
+ /*
+ * Check the file to make sure it isn't a directory or a backup
+ * file of some sort...
+ */
+
+ snprintf(filename, sizeof(filename), "%s/%s", d, dent->d_name);
+
+ if (stat(filename, &fileinfo))
+ {
+ LogMessage(L_WARN, "LoadBanners: Unable to state \"%s\" banner: %s",
+ dent->d_name, strerror(errno));
+ continue;
+ }
+
+ if (S_ISDIR(fileinfo.st_mode))
+ continue;
+
+ if (dent->d_name[0] == '~')
+ continue;
+
+ if ((ext = strrchr(dent->d_name, '.')) != NULL)
+ if (strcmp(ext, ".bck") == 0 ||
+ strcmp(ext, ".bak") == 0 ||
+ strcmp(ext, ".sav") == 0)
+ continue;
+
+ /*
+ * Must be a valid file; add it!
+ */
+
+ AddBanner(dent->d_name, filename);
+ }
+
+ /*
+ * Close the directory and sort as needed...
+ */
+
+ closedir(dir);
+
+ if (NumBanners > 1)
+ qsort(Banners, NumBanners, sizeof(banner_t),
+ (int (*)(const void *, const void *))compare);
+}
+
+
+/*
+ * 'compare()' - Compare two banners.
+ */
+
+static int /* O - -1 if name0 < name1, etc. */
+compare(const banner_t *b0, /* I - First banner */
+ const banner_t *b1) /* I - Second banner */
+{
+ return (strcasecmp(b0->name, b1->name));
+}
+
+
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/scheduler/banners.h b/scheduler/banners.h
new file mode 100644
index 000000000..99ec807a9
--- /dev/null
+++ b/scheduler/banners.h
@@ -0,0 +1,58 @@
+/*
+ * "$Id$"
+ *
+ * Banner definitions for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-2000 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
+ */
+
+/*
+ * Banner information structure...
+ */
+
+typedef struct
+{
+ char name[256]; /* Name of banner */
+ char filename[1024]; /* Full filename */
+ mime_type_t *filetype; /* Filetype for banner */
+} banner_t;
+
+
+/*
+ * Globals...
+ */
+
+VAR int NumBanners VALUE(0);
+ /* Number of banner files available */
+VAR banner_t *Banners VALUE(NULL);
+ /* Available banner files */
+
+
+/*
+ * Prototypes...
+ */
+
+extern void AddBanner(const char *name, const char *filename);
+extern banner_t *FindBanner(const char *name);
+extern void LoadBanners(const char *d);
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/scheduler/cert.c b/scheduler/cert.c
new file mode 100644
index 000000000..5616511ab
--- /dev/null
+++ b/scheduler/cert.c
@@ -0,0 +1,250 @@
+/*
+ * "$Id$"
+ *
+ * Authentication certificate routines for the Common UNIX
+ * Printing System (CUPS).
+ *
+ * Copyright 1997-2000 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:
+ *
+ * AddCert() - Add a certificate.
+ * DeleteCert() - Delete a single certificate.
+ * DeleteAllCerts() - Delete all certificates...
+ * FindCert() - Find a certificate.
+ * InitCerts() - Initialize the certificate "system" and root
+ * certificate.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "cupsd.h"
+#include <grp.h>
+
+
+/*
+ * 'AddCert()' - Add a certificate.
+ */
+
+void
+AddCert(int pid, /* I - Process ID */
+ const char *username) /* I - Username */
+{
+ int i; /* Looping var */
+ cert_t *cert; /* Current certificate */
+ FILE *fp; /* Certificate file */
+ char filename[1024]; /* Certificate filename */
+ struct group *grp; /* System group */
+ static const char *hex = "0123456789ABCDEF";
+ /* Hex constants... */
+
+
+ /*
+ * Allocate memory for the certificate...
+ */
+
+ if ((cert = calloc(sizeof(cert_t), 1)) == NULL)
+ return;
+
+ /*
+ * Fill in the certificate information...
+ */
+
+ cert->pid = pid;
+ strncpy(cert->username, username, sizeof(cert->username) - 1);
+
+ for (i = 0; i < 32; i ++)
+ cert->certificate[i] = hex[random() & 15];
+
+ /*
+ * Save the certificate to a file readable only by the User and Group
+ * (or root and SystemGroup for PID == 0)...
+ */
+
+ sprintf(filename, "%s/certs/%d", ServerRoot, pid);
+
+ if ((fp = fopen(filename, "w")) == NULL)
+ {
+ free(cert);
+ return;
+ }
+
+ if (pid == 0)
+ {
+ /*
+ * Root certificate...
+ */
+
+ fchmod(fileno(fp), 0440);
+
+ if ((grp = getgrnam(SystemGroup)) == NULL)
+ fchown(fileno(fp), 0, 0);
+ else
+ fchown(fileno(fp), 0, grp->gr_gid);
+
+ endgrent();
+
+ RootCertTime = time(NULL);
+ }
+ else
+ {
+ /*
+ * CGI certificate...
+ */
+
+ fchmod(fileno(fp), 0400);
+ fchown(fileno(fp), User, Group);
+ }
+
+ fputs(cert->certificate, fp);
+ fclose(fp);
+
+ /*
+ * Insert the certificate at the front of the list...
+ */
+
+ cert->next = Certs;
+ Certs = cert;
+}
+
+
+/*
+ * 'DeleteCert()' - Delete a single certificate.
+ */
+
+void
+DeleteCert(int pid) /* I - Process ID */
+{
+ cert_t *cert, /* Current certificate */
+ *prev; /* Previous certificate */
+ char filename[1024]; /* Certificate file */
+
+
+ for (prev = NULL, cert = Certs; cert != NULL; prev = cert, cert = cert->next)
+ if (cert->pid == pid)
+ {
+ /*
+ * Remove this certificate from the list...
+ */
+
+ if (prev == NULL)
+ Certs = cert->next;
+ else
+ prev->next = cert->next;
+
+ free(cert);
+
+ /*
+ * Delete the file and return...
+ */
+
+ sprintf(filename, "%s/certs/%d", ServerRoot, pid);
+ unlink(filename);
+ return;
+ }
+}
+
+
+/*
+ * 'DeleteAllCerts()' - Delete all certificates...
+ */
+
+void
+DeleteAllCerts(void)
+{
+ cert_t *cert, /* Current certificate */
+ *next; /* Next certificate */
+ char filename[1024]; /* Certificate file */
+
+
+ /*
+ * Loop through each certificate, deleting them...
+ */
+
+ for (cert = Certs; cert != NULL; cert = next)
+ {
+ /*
+ * Delete the file...
+ */
+
+ sprintf(filename, "%s/certs/%d", ServerRoot, cert->pid);
+ unlink(filename);
+
+ /*
+ * Free memory...
+ */
+
+ next = cert->next;
+ free(cert);
+ }
+
+ Certs = NULL;
+}
+
+
+/*
+ * 'FindCert()' - Find a certificate.
+ */
+
+const char * /* O - Matching username or NULL */
+FindCert(const char *certificate) /* I - Certificate */
+{
+ cert_t *cert; /* Current certificate */
+
+
+ for (cert = Certs; cert != NULL; cert = cert->next)
+ if (strcasecmp(certificate, cert->certificate) == 0)
+ return (cert->username);
+
+ return (NULL);
+}
+
+
+/*
+ * 'InitCerts()' - Initialize the certificate "system" and root certificate.
+ */
+
+void
+InitCerts(void)
+{
+ struct timeval tod; /* Time of day */
+
+
+ /*
+ * Initialize the random number generator using the current time,
+ * including milliseconds...
+ */
+
+ gettimeofday(&tod, NULL);
+
+ srandom(tod.tv_sec + tod.tv_usec);
+
+ /*
+ * Create a root certificate and return...
+ */
+
+ AddCert(0, "root");
+}
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/scheduler/cert.h b/scheduler/cert.h
new file mode 100644
index 000000000..d5dd7004f
--- /dev/null
+++ b/scheduler/cert.h
@@ -0,0 +1,60 @@
+/*
+ * "$Id$"
+ *
+ * Authentication certificate definitions for the Common UNIX
+ * Printing System (CUPS).
+ *
+ * Copyright 1997-2000 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
+ */
+
+/*
+ * Certificate structure...
+ */
+
+typedef struct cert_str
+{
+ struct cert_str *next; /* Next certificate in list */
+ int pid; /* Process ID (0 for root certificate) */
+ char certificate[33];/* 32 hex characters, or 128 bits */
+ char username[16]; /* Authenticated username */
+} cert_t;
+
+
+/*
+ * Globals...
+ */
+
+VAR cert_t *Certs; /* List of certificates */
+VAR time_t RootCertTime; /* Root certificate update time */
+
+
+/*
+ * Prototypes...
+ */
+
+extern void AddCert(int pid, const char *username);
+extern void DeleteCert(int pid);
+extern void DeleteAllCerts(void);
+extern const char *FindCert(const char *certificate);
+extern void InitCerts(void);
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/scheduler/classes.c b/scheduler/classes.c
new file mode 100644
index 000000000..497086d96
--- /dev/null
+++ b/scheduler/classes.c
@@ -0,0 +1,553 @@
+/*
+ * "$Id$"
+ *
+ * Printer class routines for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-2000 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(const 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);
+ strcpy(c->more_info, c->uri);
+ 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(L_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(const 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(const 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 linenum; /* Current line number */
+ int len; /* Length of line */
+ char line[1024], /* Line from file */
+ name[256], /* Parameter name */
+ *nameptr, /* Pointer into name */
+ *value; /* Pointer to value */
+ printer_t *p, /* Current printer class */
+ *temp; /* Temporary pointer to printer */
+
+
+ /*
+ * Open the classes.conf file...
+ */
+
+ sprintf(line, "%s/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(L_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(L_ERROR, "Syntax error on line %d of classes.conf.",
+ linenum);
+ return;
+ }
+ }
+ else if (p == NULL)
+ {
+ LogMessage(L_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, "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(L_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;
+ }
+ else if (strcmp(name, "StateMessage") == 0)
+ {
+ /*
+ * Set the initial queue state message...
+ */
+
+ while (isspace(*value))
+ value ++;
+
+ strcpy(p->state_message, value);
+ }
+ else if (strcmp(name, "Accepting") == 0)
+ {
+ /*
+ * Set the initial accepting state...
+ */
+
+ if (strcasecmp(value, "yes") == 0)
+ p->accepting = 1;
+ else
+ p->accepting = 0;
+ }
+ else
+ {
+ /*
+ * Something else we don't understand...
+ */
+
+ LogMessage(L_ERROR, "Unknown configuration directive %s on line %d of classes.conf.",
+ name, linenum);
+ }
+ }
+
+ 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/classes.conf", ServerRoot);
+ if ((fp = fopen(temp, "w")) == NULL)
+ {
+ LogMessage(L_ERROR, "Unable to save classes.conf - %s", strerror(errno));
+ return;
+ }
+ else
+ LogMessage(L_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, "Location %s\n", pclass->location);
+ if (pclass->state == IPP_PRINTER_STOPPED)
+ {
+ fputs("State Stopped\n", fp);
+ fprintf(fp, "StateMessage %s\n", pclass->state_message);
+ }
+ else
+ fputs("State Idle\n", fp);
+ if (pclass->accepting)
+ fputs("Accepting Yes\n", fp);
+ else
+ fputs("Accepting No\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..db42d9b98
--- /dev/null
+++ b/scheduler/classes.h
@@ -0,0 +1,43 @@
+/*
+ * "$Id$"
+ *
+ * Printer class definitions for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-2000 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(const 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(const char *name);
+extern printer_t *FindClass(const 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..e4a1cebc8
--- /dev/null
+++ b/scheduler/client.c
@@ -0,0 +1,1697 @@
+/*
+ * "$Id$"
+ *
+ * Client routines for the Common UNIX Printing System (CUPS) scheduler.
+ *
+ * Copyright 1997-2000 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_auth() - Decode an 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_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(L_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 *)&(con->http.hostaddr.sin_addr),
+ sizeof(struct in_addr), AF_INET);
+#else
+ host = gethostbyaddr(&(con->http.hostaddr.sin_addr),
+ sizeof(struct in_addr), 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(L_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(L_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 */
+ printer_t *p; /* Printer */
+ static unsigned request_id = 0;/* Request ID for temp files */
+
+
+ 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->command[0] = '\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%63s", 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(L_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;
+
+ default :
+ break; /* Anti-compiler-warning-code */
+ }
+
+ /*
+ * Handle new transfers...
+ */
+
+ if (status == HTTP_OK)
+ {
+ con->language = cupsLangGet(con->http.fields[HTTP_FIELD_ACCEPT_LANGUAGE]);
+
+ decode_auth(con);
+
+ if (con->http.fields[HTTP_FIELD_HOST][0] == '\0' &&
+ con->http.version >= HTTP_1_1)
+ {
+ /*
+ * HTTP/1.1 and higher require the "Host:" field...
+ */
+
+ if (!SendError(con, HTTP_BAD_REQUEST))
+ {
+ CloseClient(con);
+ return (0);
+ }
+ }
+ else if (strstr(con->uri, "..") != NULL)
+ {
+ /*
+ * 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, "/admin", 6) == 0 ||
+ 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, "/admin", 6) == 0)
+ {
+ snprintf(con->command, sizeof(con->command),
+ "%s/cgi-bin/admin.cgi", ServerBin);
+ con->options = con->uri + 6;
+ }
+ else if (strncmp(con->uri, "/printers", 9) == 0)
+ {
+ snprintf(con->command, sizeof(con->command),
+ "%s/cgi-bin/printers.cgi", ServerBin);
+ con->options = con->uri + 9;
+ }
+ else if (strncmp(con->uri, "/classes", 8) == 0)
+ {
+ snprintf(con->command, sizeof(con->command),
+ "%s/cgi-bin/classes.cgi", ServerBin);
+ con->options = con->uri + 8;
+ }
+ else
+ {
+ snprintf(con->command, sizeof(con->command),
+ "%s/cgi-bin/jobs.cgi", ServerBin);
+ con->options = con->uri + 5;
+ }
+
+ if (con->options[0] == '/')
+ con->options ++;
+
+ if (!SendCommand(con, con->command, con->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...
+ */
+
+ LogMessage(L_DEBUG, "POST %s", con->uri);
+ LogMessage(L_DEBUG, "CONTENT_TYPE = %s", con->http.fields[HTTP_FIELD_CONTENT_TYPE]);
+
+ 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 (strncmp(con->uri, "/admin", 6) == 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, "/admin", 6) == 0)
+ {
+ snprintf(con->command, sizeof(con->command),
+ "%s/cgi-bin/admin.cgi", ServerBin);
+ con->options = con->uri + 6;
+ }
+ else if (strncmp(con->uri, "/printers", 9) == 0)
+ {
+ snprintf(con->command, sizeof(con->command),
+ "%s/cgi-bin/printers.cgi", ServerBin);
+ con->options = con->uri + 9;
+ }
+ else if (strncmp(con->uri, "/classes", 8) == 0)
+ {
+ snprintf(con->command, sizeof(con->command),
+ "%s/cgi-bin/classes.cgi", ServerBin);
+ con->options = con->uri + 8;
+ }
+ else
+ {
+ snprintf(con->command, sizeof(con->command),
+ "%s/cgi-bin/jobs.cgi", ServerBin);
+ con->options = con->uri + 5;
+ }
+
+ if (con->options[0] == '/')
+ con->options ++;
+
+ LogMessage(L_DEBUG, "ReadClient() %d command=\"%s\", options = \"%s\"",
+ con->http.fd, con->command, con->options);
+
+ 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...
+ */
+
+ snprintf(con->command, sizeof(con->command),
+ "/ppd/%s", con->uri + 10);
+ strcpy(con->uri, con->command);
+ con->command[0] = '\0';
+ }
+
+ if (strncmp(con->uri, "/admin/", 7) == 0 ||
+ 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 (httpPrintf(HTTP(con), "\r\n") < 0)
+ {
+ CloseClient(con);
+ return (0);
+ }
+
+ con->http.state = HTTP_WAITING;
+ break;
+
+ default :
+ break; /* Anti-compiler-warning-code */
+ }
+ }
+
+ /*
+ * Handle any incoming data...
+ */
+
+ switch (con->http.state)
+ {
+ case HTTP_PUT_RECV :
+ break;
+
+ case HTTP_POST_RECV :
+ LogMessage(L_DEBUG, "ReadClient() %d con->data_encoding = %s, con->data_remaining = %d, con->file = %d",
+ con->http.fd,
+ con->http.data_encoding == HTTP_ENCODE_CHUNKED ? "chunked" : "length",
+ con->http.data_remaining, con->file);
+
+ if (con->request != NULL)
+ {
+ /*
+ * Grab any request data from the connection...
+ */
+
+ if ((ipp_state = ippRead(&(con->http), con->request)) == IPP_ERROR)
+ {
+ LogMessage(L_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...
+ */
+
+ snprintf(con->filename, sizeof(con->filename), "%s/%08x",
+ RequestRoot, request_id ++);
+ con->file = open(con->filename, O_WRONLY | O_CREAT | O_TRUNC, 0640);
+ fchmod(con->file, 0640);
+ fchown(con->file, User, Group);
+
+ LogMessage(L_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;
+
+ LogMessage(L_DEBUG, "ReadClient() %d writing %d bytes",
+ con->http.fd, 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->command[0])
+ {
+ if (!SendCommand(con, con->command, con->options))
+ {
+ if (!SendError(con, HTTP_NOT_FOUND))
+ {
+ CloseClient(con);
+ return (0);
+ }
+ }
+ else
+ LogRequest(con, HTTP_OK);
+ }
+ }
+
+ if (con->request)
+ ProcessIPPRequest(con);
+ }
+ break;
+
+ default :
+ break; /* Anti-compiler-warning-code */
+ }
+
+ 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)
+{
+ int fd;
+
+
+ if (con->filename[0])
+ fd = open(con->filename, O_RDONLY);
+ else
+ fd = open("/dev/null", O_RDONLY);
+
+ con->pipe_pid = pipe_command(con, fd, &(con->file), command, options);
+
+ close(fd);
+
+ LogMessage(L_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);
+ }
+
+ con->got_fields = 0;
+ con->field_col = 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]; /* Message for user */
+
+
+ /*
+ * 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.
+ */
+
+ snprintf(message, sizeof(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 (httpPrintf(HTTP(con), "%s", message) < 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(L_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.1\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);
+ }
+ /**** MRS - Need to store which kind of authentication to perform in the
+ client structure! ****/
+ if (code == HTTP_UNAUTHORIZED)
+ httpPrintf(HTTP(con), "WWW-Authenticate: Basic realm=\"CUPS\"\r\n");
+ 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 + 1];/* Data buffer */
+ char *bufptr; /* Pointer into 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, HTTP_MAX_BUFFER)) > 0)
+ {
+ if (con->pipe_pid && !con->got_fields)
+ {
+ /*
+ * Inspect the data for Content-Type and other fields.
+ */
+
+ buf[bytes] = '\0';
+
+ for (bufptr = buf; !con->got_fields && *bufptr; bufptr ++)
+ if (*bufptr == '\n')
+ {
+ /*
+ * Send line to client...
+ */
+
+ if (bufptr > buf && bufptr[-1] == '\r')
+ bufptr[-1] = '\0';
+ *bufptr++ = '\0';
+
+ httpPrintf(HTTP(con), "%s\r\n", buf);
+ LogMessage(L_DEBUG, "WriteClient() %d %s", con->http.fd, buf);
+
+ /*
+ * Update buffer...
+ */
+
+ bytes -= (bufptr - buf);
+ memcpy(buf, bufptr, bytes + 1);
+ bufptr = buf - 1;
+
+ /*
+ * See if the line was empty...
+ */
+
+ if (con->field_col == 0)
+ con->got_fields = 1;
+ else
+ con->field_col = 0;
+ }
+ else if (*bufptr != '\r')
+ con->field_col ++;
+
+ if (bytes > 0 && !con->got_fields)
+ {
+ /*
+ * Remaining text needs to go out...
+ */
+
+ httpPrintf(HTTP(con), "%s", buf);
+
+ con->http.activity = time(NULL);
+ return (1);
+ }
+ else if (bytes == 0)
+ {
+ con->http.activity = time(NULL);
+ return (1);
+ }
+ }
+
+ 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->filename[0])
+ unlink(con->filename);
+
+ 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(L_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(L_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(L_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_auth()' - Decode an authorization string.
+ */
+
+static void
+decode_auth(client_t *con) /* I - Client to decode to */
+{
+ char *s, /* Authorization string */
+ value[1024]; /* Value string */
+ const char *username; /* Certificate username */
+
+
+ /*
+ * Decode the string...
+ */
+
+ s = con->http.fields[HTTP_FIELD_AUTHORIZATION];
+
+ DEBUG_printf(("decode_auth(%08x): Authorization string = \"%s\"\n",
+ con, s));
+
+ if (strncmp(s, "Basic", 5) == 0)
+ {
+ s += 5;
+ while (isspace(*s))
+ s ++;
+
+ httpDecode64(value, s);
+
+ /*
+ * Pull the username and password out...
+ */
+
+ if ((s = strchr(value, ':')) == NULL)
+ {
+ LogMessage(L_DEBUG, "decode_auth() %d no colon in auth string \"%s\"",
+ con->http.fd, value);
+ return;
+ }
+
+ *s++ = '\0';
+
+ strncpy(con->username, value, sizeof(con->username) - 1);
+ con->username[sizeof(con->username) - 1] = '\0';
+
+ strncpy(con->password, s, sizeof(con->password) - 1);
+ con->password[sizeof(con->password) - 1] = '\0';
+ }
+ else if (strncmp(s, "Local", 5) == 0)
+ {
+ s += 5;
+ while (isspace(*s))
+ s ++;
+
+ if ((username = FindCert(s)) != NULL)
+ strcpy(con->username, username);
+ }
+
+ LogMessage(L_DEBUG, "decode_auth() %d username=\"%s\"",
+ con->http.fd, con->username);
+ DEBUG_printf(("decode_auth() %d username=\"%s\" password=\"%s\"\n",
+ con->http.fd, con->username, con->password));
+}
+
+
+/*
+ * '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)
+ snprintf(filename, sizeof(filename), "%s%s", ServerRoot, con->uri);
+ else if (con->language != NULL)
+ snprintf(filename, sizeof(filename), "%s/%s%s", DocumentRoot, con->language->language,
+ con->uri);
+ else
+ snprintf(filename, sizeof(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)
+ {
+ snprintf(filename, sizeof(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(L_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[10240], /* 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 */
+ static char query_string[10240]; /* QUERY_STRING 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 + and are terminated
+ * by ?...
+ */
+
+ argv[0] = argbuf;
+
+ for (commptr = argbuf, argc = 1; *commptr != '\0' && argc < 99; commptr ++)
+ if (*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);
+ }
+ else if (*commptr == '?')
+ break;
+
+ 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.1";
+ 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;
+ envp[11] = tmpdir;
+
+ if (con->operation == HTTP_GET)
+ {
+ envp[12] = "REQUEST_METHOD=GET";
+
+ if (*commptr)
+ {
+ /*
+ * Add GET form variables after ?...
+ */
+
+ *commptr++ = '\0';
+
+ snprintf(query_string, sizeof(query_string), "QUERY_STRING=%s", commptr);
+ envp[13] = query_string;
+ envp[14] = NULL;
+ }
+ else
+ envp[13] = NULL;
+ }
+ else
+ {
+ sprintf(content_length, "CONTENT_LENGTH=%d", con->bytes);
+ snprintf(content_type, sizeof(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))
+ {
+ LogMessage(L_ERROR, "Unable to create pipes for CGI %s - %s",
+ argv[0], strerror(errno));
+ return (0);
+ }
+
+ /*
+ * Then execute the command...
+ */
+
+ if ((pid = fork()) == 0)
+ {
+ /*
+ * Child comes here... Close stdin if necessary and dup the pipe to stdout.
+ */
+
+ setgid(Group);
+ setuid(User);
+
+ 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!
+ */
+
+ LogMessage(L_ERROR, "Unable to fork for CGI %s - %s", argv[0],
+ strerror(errno));
+
+ close(fds[0]);
+ close(fds[1]);
+ return (0);
+ }
+ else
+ {
+ /*
+ * Fork successful - return the PID...
+ */
+
+ AddCert(pid, con->username);
+
+ LogMessage(L_DEBUG, "CGI %s started - PID = %d", argv[0], 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..6caff035e
--- /dev/null
+++ b/scheduler/client.h
@@ -0,0 +1,98 @@
+/*
+ * "$Id$"
+ *
+ * Client definitions for the Common UNIX Printing System (CUPS) scheduler.
+ *
+ * Copyright 1997-2000 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 */
+ command[HTTP_MAX_URI], /* Command to run */
+ *options; /* Options for command */
+ int file; /* Input/output file */
+ int pipe_pid; /* Pipe process ID (or 0 if not a pipe) */
+ int got_fields, /* Non-zero if all fields seen */
+ field_col; /* Column within line */
+ 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 server */
+ 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..799e6542d
--- /dev/null
+++ b/scheduler/conf.c
@@ -0,0 +1,1391 @@
+/*
+ * "$Id$"
+ *
+ * Configuration routines for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-2000 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.
+ * 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>
+
+#ifdef HAVE_VSYSLOG
+# include <syslog.h>
+#endif /* HAVE_VSYSLOG */
+
+
+/*
+ * 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) },
+ { "ServerBin", ServerBin, VAR_STRING, sizeof(ServerBin) },
+ { "DocumentRoot", DocumentRoot, VAR_STRING, sizeof(DocumentRoot) },
+ { "RequestRoot", RequestRoot, VAR_STRING, sizeof(RequestRoot) },
+ { "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) },
+ { "Printcap", Printcap, VAR_STRING, sizeof(Printcap) },
+ { "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 },
+ { "PreserveJobHistory", &JobHistory, VAR_BOOLEAN, 0 },
+ { "PreserveJobFiles", &JobFiles, VAR_BOOLEAN, 0 }
+};
+#define NUM_VARS (sizeof(variables) / sizeof(variables[0]))
+
+
+/*
+ * 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)
+{
+ int i; /* Looping var */
+ FILE *fp; /* Configuration file */
+ int status; /* Return status */
+ char directory[1024];/* Configuration directory */
+ struct rlimit limit; /* Runtime limit */
+ char *language; /* Language string */
+ struct passwd *user; /* Default user */
+ struct group *group; /* Default group */
+
+
+ /*
+ * Close all network clients and stop all jobs...
+ */
+
+ CloseAllClients();
+ StopListening();
+ StopPolling();
+ StopBrowsing();
+
+ if (Clients != NULL)
+ {
+ free(Clients);
+ Clients = NULL;
+ }
+
+ StopAllJobs();
+
+ if (AccessFile != NULL)
+ {
+ fclose(AccessFile);
+
+ AccessFile = NULL;
+ }
+
+ if (ErrorFile != NULL)
+ {
+ fclose(ErrorFile);
+
+ ErrorFile = NULL;
+ }
+
+ if (PageFile != NULL)
+ {
+ fclose(PageFile);
+
+ PageFile = 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(ServerBin, CUPS_SERVERBIN);
+ strcpy(RequestRoot, CUPS_REQUESTS);
+ strcpy(DocumentRoot, CUPS_DOCROOT);
+ strcpy(AccessLog, CUPS_LOGDIR "/access_log");
+ strcpy(ErrorLog, CUPS_LOGDIR "/error_log");
+ strcpy(PageLog, CUPS_LOGDIR "/page_log");
+ strcpy(Printcap, "");
+
+ if ((language = DEFAULT_LANGUAGE) == NULL)
+ language = "en";
+ else if (strcmp(language, "C") == 0 || strcmp(language, "POSIX") == 0)
+ language = "en";
+
+ strncpy(DefaultLanguage, language, sizeof(DefaultLanguage) - 1);
+ DefaultLanguage[sizeof(DefaultLanguage) - 1] = '\0';
+
+ strcpy(DefaultCharset, DEFAULT_CHARSET);
+ strcpy(RIPCache, "8m");
+ if (getenv("TMPDIR") == NULL)
+ strcpy(TempDir, "/var/tmp");
+ else
+ {
+ strncpy(TempDir, getenv("TMPDIR"), sizeof(TempDir) - 1);
+ TempDir[sizeof(TempDir) - 1] = '\0';
+ }
+
+ if (Devices)
+ {
+ ippDelete(Devices);
+ Devices = NULL;
+ }
+
+ if (PPDs)
+ {
+ ippDelete(PPDs);
+ PPDs = NULL;
+ }
+
+ /*
+ * Find the default system group: "sys", "system", or "root"...
+ */
+
+ group = getgrnam("sys");
+ endgrent();
+
+ if (group != NULL)
+ {
+ strcpy(SystemGroup, "sys");
+ Group = group->gr_gid;
+ }
+ else
+ {
+ group = getgrnam("system");
+ endgrent();
+
+ if (group != NULL)
+ {
+ strcpy(SystemGroup, "system");
+ Group = group->gr_gid;
+ }
+ else
+ {
+ group = getgrnam("root");
+ endgrent();
+
+ if (group != NULL)
+ {
+ strcpy(SystemGroup, "root");
+ Group = group->gr_gid;
+ }
+ else
+ {
+ strcpy(SystemGroup, "unknown");
+ Group = 0;
+ }
+ }
+ }
+
+ /*
+ * Find the default user...
+ */
+
+ if ((user = getpwnam("lp")) == NULL)
+ User = 1; /* Force to a non-priviledged account */
+ else
+ User = user->pw_uid;
+
+ endpwent();
+
+ LogLevel = L_ERROR;
+ HostNameLookups = FALSE;
+ Timeout = DEFAULT_TIMEOUT;
+ KeepAlive = TRUE;
+ KeepAliveTimeout = DEFAULT_KEEPALIVE;
+ ImplicitClasses = TRUE;
+
+ MaxClients = 100;
+
+ MaxLogSize = 1024 * 1024;
+ MaxRequestSize = 0;
+
+ for (i = 0; i < NumRelays; i ++)
+ if (Relays[i].from.type == AUTH_NAME)
+ free(Relays[i].from.mask.name.name);
+
+ Browsing = TRUE;
+ BrowsePort = ippPort();
+ BrowseInterval = DEFAULT_INTERVAL;
+ BrowseTimeout = DEFAULT_TIMEOUT;
+ BrowseACL = NULL;
+ NumBrowsers = 0;
+ NumRelays = 0;
+ NumPolled = 0;
+
+ NumListeners = 0;
+
+ DefaultPrinter = NULL;
+
+ DeleteAllPrinters();
+
+ if (MimeDatabase != NULL)
+ mimeDelete(MimeDatabase);
+
+ JobHistory = DEFAULT_HISTORY;
+ JobFiles = DEFAULT_FILES;
+
+ if ((fp = fopen(ConfigurationFile, "r")) == NULL)
+ return (0);
+
+ status = read_configuration(fp);
+
+ fclose(fp);
+
+ if (!status)
+ return (0);
+
+ if (DocumentRoot[0] != '/')
+ {
+ snprintf(directory, sizeof(directory), "%s/%s", ServerRoot, DocumentRoot);
+ strcpy(DocumentRoot, directory);
+ }
+
+ if (RequestRoot[0] != '/')
+ {
+ snprintf(directory, sizeof(directory), "%s/%s", ServerRoot, RequestRoot);
+ strcpy(RequestRoot, directory);
+ }
+
+ if (ServerBin[0] != '/')
+ {
+ snprintf(directory, sizeof(directory), "%s/%s", ServerRoot, ServerBin);
+ strcpy(ServerBin, directory);
+ }
+
+#ifdef HAVE_VSYSLOG
+ if (strcmp(AccessLog, "syslog") == 0 ||
+ strcmp(ErrorLog, "syslog") == 0 ||
+ strcmp(PageLog, "syslog") == 0)
+ openlog("cupsd", LOG_PID | LOG_NOWAIT | LOG_NDELAY, LOG_LPR);
+#endif /* HAVE_VSYSLOG */
+
+ LogMessage(L_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(L_ERROR, "ReadConfiguration() FATAL: unable to allocate memory for %d clients!",
+ MaxClients);
+ exit(1);
+ }
+ else
+ LogMessage(L_INFO, "ReadConfiguration() Configured for up to %d clients.",
+ MaxClients);
+
+ /*
+ * Read the MIME type and conversion database...
+ */
+
+ MimeDatabase = mimeNew();
+ mimeMerge(MimeDatabase, ServerRoot);
+
+ /*
+ * Load banners...
+ */
+
+ LoadBanners(CUPS_DATADIR "/banners");
+
+ /*
+ * Load printers and classes...
+ */
+
+ LoadAllPrinters();
+ LoadAllClasses();
+
+ /*
+ * Load devices and PPDs...
+ */
+
+ LoadPPDs(CUPS_DATADIR "/model");
+
+ sprintf(directory, "%s/backend", ServerBin);
+ LoadDevices(directory);
+
+ /*
+ * 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();
+ StartPolling();
+
+ /*
+ * Check for queued jobs...
+ */
+
+ CheckJobs();
+
+ 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 */
+ unsigned address, /* Address value */
+ netmask; /* Netmask value */
+ int ip[4], /* IP address components */
+ ipcount, /* Number of components provided */
+ mask[4]; /* IP netmask components */
+ dirsvc_relay_t *relay; /* Relay data */
+ dirsvc_poll_t *poll; /* Polling data */
+ struct sockaddr_in polladdr; /* Polling address */
+ static unsigned netmasks[4] = /* Standard netmasks... */
+ {
+ 0xff000000,
+ 0xffff0000,
+ 0xffffff00,
+ 0xffffffff
+ };
+
+
+ /*
+ * 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(L_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)))
+ {
+ LogMessage(L_INFO, "Listening to %x:%d",
+ ntohl(Listeners[NumListeners].address.sin_addr.s_addr),
+ ntohs(Listeners[NumListeners].address.sin_port));
+ NumListeners ++;
+ }
+ else
+ LogMessage(L_ERROR, "Bad %s address %s at line %d.", name,
+ value, linenum);
+ }
+ else
+ LogMessage(L_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))
+ {
+ LogMessage(L_INFO, "Sending browsing info to %x:%d",
+ ntohl(Browsers[NumBrowsers].sin_addr.s_addr),
+ ntohs(Browsers[NumBrowsers].sin_port));
+ NumBrowsers ++;
+ }
+ else
+ LogMessage(L_ERROR, "Bad BrowseAddress %s at line %d.", value,
+ linenum);
+ }
+ else
+ LogMessage(L_WARN, "Too many BrowseAddress directives at line %d.",
+ linenum);
+ }
+ else if (strcmp(name, "BrowseOrder") == 0)
+ {
+ /*
+ * "BrowseOrder Deny,Allow" or "BrowseOrder Allow,Deny"...
+ */
+
+ if (BrowseACL == NULL)
+ BrowseACL = AddLocation("CUPS_INTERNAL_BROWSE_ACL");
+
+ if (BrowseACL == NULL)
+ LogMessage(L_ERROR, "Unable to initialize browse access control list!");
+ else if (strncasecmp(value, "deny", 4) == 0)
+ BrowseACL->order_type = AUTH_ALLOW;
+ else if (strncasecmp(value, "allow", 5) == 0)
+ BrowseACL->order_type = AUTH_DENY;
+ else
+ LogMessage(L_ERROR, "Unknown BrowseOrder value %s on line %d.",
+ value, linenum);
+ }
+ else if (strcmp(name, "BrowseAllow") == 0 ||
+ strcmp(name, "BrowseDeny") == 0)
+ {
+ /*
+ * BrowseAllow [From] host/ip...
+ * BrowseDeny [From] host/ip...
+ */
+
+ if (BrowseACL == NULL)
+ BrowseACL = AddLocation("CUPS_INTERNAL_BROWSE_ACL");
+
+ if (BrowseACL == NULL)
+ LogMessage(L_ERROR, "Unable to initialize browse access control list!");
+ else
+ {
+ if (strncasecmp(value, "from ", 5) == 0)
+ {
+ /*
+ * Strip leading "from"...
+ */
+
+ value += 5;
+
+ 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, "BrowseAllow") == 0)
+ AllowIP(BrowseACL, 0, 0);
+ else
+ DenyIP(BrowseACL, 0, 0);
+ }
+ else if (strcasecmp(value, "none") == 0)
+ {
+ /*
+ * No hosts...
+ */
+
+ if (strcmp(name, "BrowseAllow") == 0)
+ AllowIP(BrowseACL, ~0, 0);
+ else
+ DenyIP(BrowseACL, ~0, 0);
+ }
+ else if (value[0] == '*' || value[0] == '.' || !isdigit(value[0]))
+ {
+ /*
+ * Host or domain name...
+ */
+
+ if (value[0] == '*')
+ value ++;
+
+ if (strcmp(name, "BrowseAllow") == 0)
+ AllowHost(BrowseACL, value);
+ else
+ DenyHost(BrowseACL, 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(L_ERROR, "Bad netmask value %s on line %d.",
+ value, linenum);
+ netmask = 0xffffffff;
+ break;
+ }
+ }
+ else
+ netmask = netmasks[ipcount - 1];
+
+ if (strcmp(name, "BrowseAllow") == 0)
+ AllowIP(BrowseACL, address, netmask);
+ else
+ DenyIP(BrowseACL, address, netmask);
+ }
+ }
+ }
+ else if (strcmp(name, "BrowseRelay") == 0)
+ {
+ /*
+ * BrowseRelay [from] source [to] destination
+ */
+
+ if (NumRelays >= MAX_BROWSERS)
+ {
+ LogMessage(L_WARN, "Too many BrowseRelay directives at line %d.",
+ linenum);
+ continue;
+ }
+
+ relay = Relays + NumRelays;
+
+ memset(relay, 0, sizeof(dirsvc_relay_t));
+
+ if (strncasecmp(value, "from ", 5) == 0)
+ {
+ /*
+ * Strip leading "from"...
+ */
+
+ value += 5;
+
+ while (isspace(*value))
+ value ++;
+ }
+
+ /*
+ * Figure out what form the from address takes:
+ *
+ * *.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 (value[0] == '*' || value[0] == '.' || !isdigit(value[0]))
+ {
+ /*
+ * Host or domain name...
+ */
+
+ if (value[0] == '*')
+ value ++;
+
+ relay->from.type = AUTH_NAME;
+ relay->from.mask.name.name = strdup(value);
+ relay->from.mask.name.length = strlen(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];
+
+ for (; *value; value ++)
+ if (*value == '/' || isspace(*value))
+ break;
+
+ if (*value == '/')
+ {
+ 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(L_ERROR, "Bad netmask value %s on line %d.",
+ value, linenum);
+ netmask = 0xffffffff;
+ break;
+ }
+ }
+ else
+ netmask = netmasks[ipcount - 1];
+
+ relay->from.type = AUTH_IP;
+ relay->from.mask.ip.address = address;
+ relay->from.mask.ip.netmask = netmask;
+ }
+
+ /*
+ * Skip value and trailing whitespace...
+ */
+
+ for (; *value; value ++)
+ if (isspace(*value))
+ break;
+
+ while (isspace(*value))
+ value ++;
+
+ if (strncasecmp(value, "to ", 3) == 0)
+ {
+ /*
+ * Strip leading "to"...
+ */
+
+ value += 3;
+
+ while (isspace(*value))
+ value ++;
+ }
+
+ /*
+ * Get "to" address and port...
+ */
+
+ if (get_address(value, INADDR_BROADCAST, BrowsePort, &(relay->to)))
+ {
+ if (relay->from.type == AUTH_NAME)
+ LogMessage(L_INFO, "Relaying from %s to %x:%d",
+ ntohl(relay->to.sin_addr.s_addr),
+ ntohs(relay->to.sin_port));
+ else
+ LogMessage(L_INFO, "Relaying from %x/%x to %x:%d",
+ relay->from.mask.ip.address, relay->from.mask.ip.netmask,
+ ntohl(relay->to.sin_addr.s_addr),
+ ntohs(relay->to.sin_port));
+
+ NumRelays ++;
+ }
+ else
+ {
+ if (relay->from.type == AUTH_NAME)
+ free(relay->from.mask.name.name);
+
+ LogMessage(L_ERROR, "Bad relay address %s at line %d.", value, linenum);
+ }
+ }
+ else if (strcmp(name, "BrowsePoll") == 0)
+ {
+ /*
+ * BrowsePoll address[:port]
+ */
+
+ if (NumPolled >= MAX_BROWSERS)
+ {
+ LogMessage(L_WARN, "Too many BrowsePoll directives at line %d.",
+ linenum);
+ continue;
+ }
+
+ /*
+ * Get poll address and port...
+ */
+
+ if (get_address(value, INADDR_NONE, ippPort(), &polladdr))
+ {
+ LogMessage(L_INFO, "Polling %x:%d", ntohl(polladdr.sin_addr.s_addr),
+ ntohs(polladdr.sin_port));
+
+ poll = Polled + NumPolled;
+ NumPolled ++;
+ memset(poll, 0, sizeof(dirsvc_poll_t));
+
+ address = ntohl(polladdr.sin_addr.s_addr);
+
+ sprintf(poll->hostname, "%d.%d.%d.%d", address >> 24,
+ (address >> 16) & 255, (address >> 8) & 255, address & 255);
+ poll->port = ntohs(polladdr.sin_port);
+ }
+ else
+ LogMessage(L_ERROR, "Bad poll address %s at line %d.", value, 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(L_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(L_WARN, "ReadConfiguration() Unknown groupname \"%s\"",
+ value);
+ }
+ }
+ else if (strcmp(name, "LogLevel") == 0)
+ {
+ /*
+ * Amount of logging to do...
+ */
+
+ if (strcmp(value, "debug") == 0)
+ LogLevel = L_DEBUG;
+ else if (strcmp(value, "info") == 0)
+ LogLevel = L_INFO;
+ else if (strcmp(value, "warn") == 0)
+ LogLevel = L_WARN;
+ else if (strcmp(value, "error") == 0)
+ LogLevel = L_ERROR;
+ else if (strcmp(value, "none") == 0)
+ LogLevel = L_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(L_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 ||
+ strcasecmp(value, "yes") == 0 ||
+ atoi(value) != 0)
+ *((int *)var->ptr) = TRUE;
+ else if (strcasecmp(value, "false") == 0 ||
+ strcasecmp(value, "off") == 0 ||
+ strcasecmp(value, "disabled") == 0 ||
+ strcasecmp(value, "no") == 0 ||
+ strcasecmp(value, "0") == 0)
+ *((int *)var->ptr) = FALSE;
+ else
+ LogMessage(L_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(L_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(L_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 {none,basic,digest}
+ */
+
+ if (strcasecmp(value, "none") == 0)
+ {
+ loc->type = AUTH_NONE;
+ loc->level = AUTH_ANON;
+ }
+ else if (strcasecmp(value, "basic") == 0)
+ {
+ loc->type = AUTH_BASIC;
+
+ if (loc->level == AUTH_ANON)
+ loc->level = AUTH_USER;
+ }
+ else if (strcasecmp(value, "digest") == 0)
+ {
+ loc->type = AUTH_DIGEST;
+
+ if (loc->level == AUTH_ANON)
+ loc->level = AUTH_USER;
+ }
+ else
+ LogMessage(L_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->type = AUTH_NONE;
+ 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(L_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(L_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, "%255[^:]:%255s", hostname, portname))
+ {
+ case 1 :
+ if (strchr(hostname, '.') == NULL && defaddress == INADDR_ANY)
+ {
+ /*
+ * Hostname is a port number...
+ */
+
+ strcpy(portname, hostname);
+ hostname[0] = '\0';
+ }
+ else
+ portname[0] = '\0';
+ break;
+ case 2 :
+ break;
+ default :
+ LogMessage(L_ERROR, "Unable to decode address \"%s\"!", value);
+ return (0);
+ }
+
+ /*
+ * Decode the hostname and port number as needed...
+ */
+
+ if (hostname[0] != '\0')
+ {
+ if (isdigit(hostname[0]))
+ address->sin_addr.s_addr = inet_addr(hostname);
+ else
+ {
+ if ((host = gethostbyname(hostname)) == NULL)
+ {
+ LogMessage(L_ERROR, "gethostbyname(\"%s\") failed - %s!", hostname,
+ strerror(errno));
+ 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)
+ {
+ LogMessage(L_ERROR, "getservbyname(\"%s\") failed - %s!", portname,
+ strerror(errno));
+ 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..b68f4ef96
--- /dev/null
+++ b/scheduler/conf.h
@@ -0,0 +1,118 @@
+/*
+ * "$Id$"
+ *
+ * Configuration file definitions for the Common UNIX Printing System (CUPS)
+ * scheduler.
+ *
+ * Copyright 1997-2000 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 L_PAGE -1 /* Used internally for page logging */
+#define L_NONE 0
+#define L_ERROR 1
+#define L_WARN 2
+#define L_INFO 3
+#define L_DEBUG 4
+
+
+/*
+ * Globals...
+ */
+
+VAR char ConfigurationFile[256] VALUE(CUPS_SERVERROOT "/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 */
+ ServerBin[1024] VALUE(CUPS_SERVERBIN),
+ /* Root directory for binaries */
+ RequestRoot[1024] VALUE(CUPS_REQUESTS),
+ /* Directory for request files */
+ DocumentRoot[1024] VALUE(CUPS_DOCROOT),
+ /* Root directory for documents */
+ SystemGroup[32],
+ /* System group name */
+ AccessLog[1024] VALUE(CUPS_LOGDIR "/access_log"),
+ /* Access log filename */
+ ErrorLog[1024] VALUE(CUPS_LOGDIR "/error_log"),
+ /* Error log filename */
+ PageLog[1024] VALUE(CUPS_LOGDIR "/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[1024] VALUE("/var/tmp"),
+ /* Temporary directory */
+ Printcap[1024] VALUE("");
+ /* Printcap file */
+VAR int User VALUE(1),
+ /* User ID for server */
+ Group VALUE(0),
+ /* Group ID for server */
+ LogLevel VALUE(L_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, const char *message, ...);
+extern int LogPage(job_t *job, const char *page);
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/scheduler/cups-lpd.c b/scheduler/cups-lpd.c
new file mode 100644
index 000000000..f9a172e64
--- /dev/null
+++ b/scheduler/cups-lpd.c
@@ -0,0 +1,526 @@
+/*
+ * "$Id$"
+ *
+ * Line Printer Daemon interface for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-2000 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:
+ *
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include <cups/cups.h>
+#include <cups/string.h>
+#include <cups/language.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+
+/*
+ * LPD "mini-daemon" for CUPS. This program must be used in conjunction
+ * with inetd or another similar program that monitors ports and starts
+ * daemons for each client connection. A typical configuration is:
+ *
+ * printer stream tcp nowait lp /usr/lib/cups/daemon/cups-lpd cups-lpd
+ *
+ * This daemon implements most of RFC 1179 (the unofficial LPD specification)
+ * except for:
+ *
+ * - This daemon does not check to make sure that the source port is
+ * between 721 and 731, since it isn't necessary for proper
+ * functioning, and port-based security is no security at all!
+ *
+ * - The "Print any waiting jobs" command is a no-op.
+ *
+ * The LPD-to-IPP mapping is as defined in RFC 2569.
+ */
+
+/*
+ * Prototypes...
+ */
+
+int recv_print_job(const char *dest);
+int send_short_state(const char *dest, const char *list);
+int send_long_state(const char *dest, const char *list);
+int remove_jobs(const char *dest, const char *agent, const char *list);
+
+
+/*
+ * 'main()' - Process an incoming LPD request...
+ */
+
+int /* O - Exit status */
+main(int argc, /* I - Number of command-line arguments */
+ char *argv[]) /* I - Command-line arguments */
+{
+ char line[1024], /* Command string */
+ command, /* Command code */
+ *dest, /* Pointer to destination */
+ *list, /* Pointer to list */
+ *agent, /* Pointer to user */
+ status; /* Status for client */
+
+
+ /*
+ * Don't buffer the output...
+ */
+
+ setbuf(stdout, NULL);
+
+ /*
+ * RFC1179 specifies that only 1 daemon command can be received for
+ * every connection.
+ */
+
+ if (fgets(line, sizeof(line), stdin) == NULL)
+ {
+ /*
+ * Unable to get command from client! Send an error status and return.
+ */
+
+ putchar(1);
+ return (1);
+ }
+
+ /*
+ * The first byte is the command byte. After that will be the queue name,
+ * resource list, and/or user name.
+ */
+
+ command = line[0];
+ dest = line + 1;
+
+ for (list = dest + 1; *list && !isspace(*list); list ++);
+
+ while (isspace(*list))
+ *list++ = '\0';
+
+ /*
+ * Do the command...
+ */
+
+ switch (command)
+ {
+ default : /* Unknown command */
+ putchar(1);
+
+ status = 1;
+ break;
+
+ case 0x01 : /* Print any waiting jobs */
+ putchar(0);
+
+ status = 0;
+ break;
+
+ case 0x02 : /* Receive a printer job */
+ putchar(0);
+
+ status = recv_print_job(dest);
+ break;
+
+ case 0x03 : /* Send queue state (short) */
+ putchar(0);
+
+ status = send_short_state(dest, list);
+ break;
+
+ case 0x04 : /* Send queue state (long) */
+ putchar(0);
+
+ status = send_long_state(dest, list);
+ break;
+
+ case 0x05 : /* Remove jobs */
+ putchar(0);
+
+ /*
+ * Grab the agent and skip to the list of users and/or jobs.
+ */
+
+ agent = list;
+
+ for (; *list && !isspace(*list); list ++);
+ while (isspace(*list))
+ *list++ = '\0';
+
+ status = remove_jobs(dest, agent, list);
+ break;
+ }
+
+ return (status);
+}
+
+
+/*
+ * 'recv_print_job()' - Receive a print job from the client.
+ */
+
+int /* O - Command status */
+recv_print_job(const char *dest) /* I - Destination */
+{
+ int i; /* Looping var */
+ int status; /* Command status */
+ FILE *fp; /* Temporary file */
+ char filename[1024]; /* Filename */
+ int bytes; /* Bytes received */
+ char line[1024], /* Line from file/stdin */
+ command, /* Command from line */
+ *count, /* Number of bytes */
+ *name; /* Name of file */
+ int num_data; /* Number of data files */
+ char data[32][256]; /* Data files */
+ const char *tmpdir; /* Temporary directory */
+ char user[1024], /* User name */
+ title[1024], /* Job title */
+ docname[1024]; /* Document name */
+ http_t *http; /* HTTP server connection */
+ cups_lang_t *language; /* Language information */
+ ipp_t *request, /* IPP request */
+ *response; /* IPP response */
+ char uri[HTTP_MAX_URI]; /* Printer URI */
+
+
+ status = 0;
+ num_data = 0;
+ if ((tmpdir = getenv("TMP")) == NULL)
+ tmpdir = "/var/tmp";
+
+ while (fgets(line, sizeof(line), stdin) != NULL)
+ {
+ if (strlen(line) < 2)
+ {
+ status = 1;
+ break;
+ }
+
+ command = line[0];
+ count = line + 1;
+
+ for (name = count + 1; *name && !isspace(*name); name ++);
+ while (isspace(*name))
+ *name++ = '\0';
+
+ switch (command)
+ {
+ default :
+ case 0x01 : /* Abort */
+ status = 1;
+ break;
+ case 0x02 : /* Receive control file */
+ if (strlen(name) < 2)
+ {
+ putchar(1);
+ status = 1;
+ break;
+ }
+
+ snprintf(filename, sizeof(filename), "%s/%06d-0", tmpdir, getpid());
+ break;
+ case 0x03 : /* Receive data file */
+ if (strlen(name) < 2)
+ {
+ putchar(1);
+ status = 1;
+ break;
+ }
+
+ name[strlen(name) - 1] = '\0'; /* Strip LF */
+ strncpy(data[num_data], name, sizeof(data[0]) - 1);
+ data[num_data][sizeof(data[0]) - 1] = '\0';
+
+ num_data ++;
+ snprintf(filename, sizeof(filename), "%s/%06d-%d", tmpdir, getpid(),
+ num_data);
+ break;
+ }
+
+ putchar(status);
+
+ if (status)
+ break;
+
+ /*
+ * Try opening the temp file...
+ */
+
+ if ((fp = fopen(filename, "wb")) == NULL)
+ {
+ putchar(1);
+ status = 1;
+ break;
+ }
+
+ /*
+ * Copy the data or control file from the client...
+ */
+
+ for (i = atoi(count); i > 0; i -= bytes)
+ {
+ if (i > sizeof(line))
+ bytes = sizeof(line);
+ else
+ bytes = i;
+
+ if ((bytes = fread(line, 1, bytes, stdin)) > 0)
+ bytes = fwrite(line, 1, bytes, fp);
+
+ if (bytes < 1)
+ {
+ status = 1;
+ break;
+ }
+ }
+
+ /*
+ * Read trailing nul...
+ */
+
+ if (!status)
+ {
+ fread(line, 1, 1, stdin);
+ status = line[0];
+ }
+
+ /*
+ * Close the file and send an acknowledgement...
+ */
+
+ fclose(fp);
+
+ putchar(status);
+
+ if (status)
+ break;
+ }
+
+ if (!status)
+ {
+ /*
+ * Process the control file and print stuff...
+ */
+
+ snprintf(filename, sizeof(filename), "%s/%06d-0", tmpdir, getpid());
+ if ((fp = fopen(filename, "rb")) == NULL)
+ status = 1;
+ else if ((http = httpConnect(cupsServer(), ippPort())) == NULL)
+ status = 1;
+ else
+ {
+ language = cupsLangDefault();
+ title[0] = '\0';
+ user[0] = '\0';
+ docname[0] = '\0';
+
+ while (fgets(line, sizeof(line), fp) != NULL)
+ {
+ /*
+ * Strip the trailing newline...
+ */
+
+ if (line[0])
+ line[strlen(line) - 1] = '\0';
+
+ /*
+ * Process control lines...
+ */
+
+ switch (line[0])
+ {
+ case 'J' : /* Job name */
+ strcpy(title, line + 1);
+ break;
+ case 'N' : /* Document name */
+ strcpy(docname, line + 1);
+ break;
+ case 'P' : /* User identification */
+ strcpy(user, line + 1);
+ break;
+ case 'c' : /* Plot CIF file */
+ case 'd' : /* Print DVI file */
+ case 'f' : /* Print formatted file */
+ case 'g' : /* Plot file */
+ case 'l' : /* Print file leaving control characters (raw) */
+ case 'n' : /* Print ditroff output file */
+ case 'o' : /* Print PostScript output file */
+ case 'p' : /* Print file with 'pr' format (prettyprint) */
+ case 'r' : /* File to print with FORTRAN carriage control */
+ case 't' : /* Print troff output file */
+ case 'v' : /* Print raster file */
+ /*
+ * Verify that we have a username...
+ */
+
+ if (!user[0])
+ {
+ status = 1;
+ break;
+ }
+
+ /*
+ * Figure out which file we are printing...
+ */
+
+ for (i = 0; i < num_data; i ++)
+ if (strcmp(data[i], line + 1) == 0)
+ break;
+
+ if (i >= num_data)
+ {
+ status = 1;
+ break;
+ }
+
+ /*
+ * Build an IPP_PRINT_JOB request...
+ */
+
+ if ((request = ippNew()) == NULL)
+ {
+ status = 1;
+ break;
+ }
+
+ request->request.op.operation_id = IPP_PRINT_JOB;
+ request->request.op.request_id = 1;
+
+ snprintf(uri, sizeof(uri), "ipp://%s:%d/printers/%s",
+ cupsServer(), ippPort(), dest);
+
+ 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);
+
+ if (line[0] == 'l')
+ 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, user);
+
+ if (title[0])
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
+ "job-name", NULL, title);
+
+ if (docname[0])
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
+ "document-name", NULL, docname);
+
+ if (line[0] == 'p')
+ ippAddBoolean(request, IPP_TAG_JOB, "prettyprint", 1);
+
+ snprintf(filename, sizeof(filename), "%s/%06d-%d", tmpdir,
+ getpid(), i + 1);
+
+ /*
+ * Do the request...
+ */
+
+ snprintf(uri, sizeof(uri), "/printers/%s", dest);
+
+ if ((response = cupsDoFileRequest(http, request, uri, filename)) == NULL)
+ status = 1;
+ else if (response->request.status.status_code > IPP_OK_CONFLICT)
+ status = 1;
+
+ if (response != NULL)
+ ippDelete(response);
+ break;
+ }
+
+ if (status)
+ break;
+ }
+
+ fclose(fp);
+ httpClose(http);
+ cupsLangFree(language);
+ }
+ }
+
+ /*
+ * Clean up all temporary files and return...
+ */
+
+ for (i = -1; i < num_data; i ++)
+ {
+ snprintf(filename, sizeof(filename), "%s/%06d-%d", tmpdir, getpid(), i + 1);
+ unlink(filename);
+ }
+
+ return (status);
+}
+
+
+/*
+ * 'send_short_state()' - Send the short queue state.
+ */
+
+int /* O - Command status */
+send_short_state(const char *dest, /* I - Destination */
+ const char *list) /* I - List of jobs or users */
+{
+ puts("Sorry, not yet implemented!");
+ return (1);
+}
+
+
+/*
+ * 'send_long_state()' - Send the long queue state.
+ */
+
+int /* O - Command status */
+send_long_state(const char *dest, /* I - Destination */
+ const char *list) /* I - List of jobs or users */
+{
+ puts("Sorry, not yet implemented!");
+ return (1);
+}
+
+
+/*
+ * 'remove_jobs()' - Cancel one or more jobs.
+ */
+
+int /* O - Command status */
+remove_jobs(const char *dest, /* I - Destination */
+ const char *agent, /* I - User agent */
+ const char *list) /* I - List of jobs or users */
+{
+ return (1);
+}
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/scheduler/cups-polld.c b/scheduler/cups-polld.c
new file mode 100644
index 000000000..213bd4f8c
--- /dev/null
+++ b/scheduler/cups-polld.c
@@ -0,0 +1,308 @@
+/*
+ * "$Id$"
+ *
+ * Polling daemon for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-2000 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:
+ *
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include <cups/cups.h>
+#include <stdlib.h>
+#include <cups/language.h>
+#include <cups/string.h>
+
+
+/*
+ * Local functions...
+ */
+
+int poll_server(http_t *http, cups_lang_t *language, ipp_op_t op,
+ int sock, int port);
+
+
+/*
+ * 'main()' - Open socks and poll until we are killed...
+ */
+
+int /* O - Exit status */
+main(int argc, /* I - Number of command-line arguments */
+ char *argv[]) /* I - Command-line arguments */
+{
+ http_t *http; /* HTTP connection */
+ cups_lang_t *language; /* Language info */
+ int interval; /* Polling interval */
+ int sock; /* Browser sock */
+ int port; /* Browser port */
+ int val; /* Socket option value */
+
+
+ /*
+ * The command-line must contain the following:
+ *
+ * cups-polld server server-port interval port
+ */
+
+ if (argc != 5)
+ {
+ fputs("Usage: cups-polld server server-port interval port\n", stderr);
+ return (1);
+ }
+
+ interval = atoi(argv[3]);
+ port = atoi(argv[4]);
+
+ /*
+ * Open a connection to the server...
+ */
+
+ if ((http = httpConnect(argv[1], atoi(argv[2]))) == NULL)
+ {
+ perror("cups-polld");
+ return (1);
+ }
+
+ /*
+ * Open a broadcast sock...
+ */
+
+ if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
+ {
+ perror("cups-polld");
+ httpClose(http);
+ return (1);
+ }
+
+ /*
+ * Set the "broadcast" flag...
+ */
+
+ val = 1;
+ if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &val, sizeof(val)))
+ {
+ perror("cups-polld");
+
+ close(sock);
+ httpClose(http);
+ return (1);
+ }
+
+ /*
+ * Loop forever, asking for available printers and classes...
+ */
+
+ language = cupsLangDefault();
+
+ for (;;)
+ {
+ if (poll_server(http, language, CUPS_GET_PRINTERS, sock, port))
+ continue;
+
+ if (poll_server(http, language, CUPS_GET_CLASSES, sock, port))
+ continue;
+
+ sleep(interval);
+ }
+}
+
+
+/*
+ * 'poll_server()' - Poll the server for the given set of printers or classes.
+ */
+
+int /* O - 0 for success, -1 on error */
+poll_server(http_t *http, /* I - HTTP connection */
+ cups_lang_t *language, /* I - Language */
+ ipp_op_t op, /* I - Operation code */
+ int sock, /* I - Broadcast sock */
+ int port) /* I - Broadcast port */
+{
+ ipp_t *request, /* Request data */
+ *response; /* Response data */
+ ipp_attribute_t *attr; /* Current attribute */
+ const char *uri, /* printer-uri */
+ *info, /* printer-info */
+ *location, /* printer-location */
+ *make_model; /* printer-make-and-model */
+ cups_ptype_t type; /* printer-type */
+ ipp_pstate_t state; /* printer-state */
+ struct sockaddr_in addr; /* Broadcast address */
+ char packet[1540]; /* Data packet */
+
+
+ /*
+ * Broadcast to 127.0.0.1 (localhost)
+ */
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_addr.s_addr = htonl(0x7f000001);
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(port);
+
+ /*
+ * Build a CUPS_GET_PRINTERS or CUPS_GET_CLASSES request, which requires
+ * only the attributes-charset and attributes-natural-language attributes.
+ */
+
+ 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);
+
+ /*
+ * Do the request and get back a response...
+ */
+
+ if ((response = cupsDoRequest(http, request, "/")) != NULL)
+ {
+ if (response->request.status.status_code > IPP_OK_CONFLICT)
+ {
+ fprintf(stderr, "cups-polld: get-%s failed: %s\n",
+ op == CUPS_GET_PRINTERS ? "printers" : "classes",
+ ippErrorString(response->request.status.status_code));
+ ippDelete(response);
+ return (-1);
+ }
+
+ /*
+ * Loop through the printers or classes returned in the list...
+ */
+
+ for (attr = response->attrs; attr != NULL; attr = attr->next)
+ {
+ /*
+ * Skip leading attributes until we hit a printer...
+ */
+
+ while (attr != NULL && attr->group_tag != IPP_TAG_PRINTER)
+ attr = attr->next;
+
+ if (attr == NULL)
+ break;
+
+ /*
+ * Pull the needed attributes from this printer...
+ */
+
+ uri = NULL;
+ info = "";
+ location = "";
+ make_model = "";
+ type = CUPS_PRINTER_REMOTE;
+ state = IPP_PRINTER_IDLE;
+
+ while (attr != NULL && attr->group_tag == IPP_TAG_PRINTER)
+ {
+ if (strcmp(attr->name, "printer-uri-supported") == 0 &&
+ attr->value_tag == IPP_TAG_URI)
+ uri = attr->values[0].string.text;
+
+ if (strcmp(attr->name, "printer-info") == 0 &&
+ attr->value_tag == IPP_TAG_TEXT)
+ info = attr->values[0].string.text;
+
+ if (strcmp(attr->name, "printer-location") == 0 &&
+ attr->value_tag == IPP_TAG_TEXT)
+ location = attr->values[0].string.text;
+
+ if (strcmp(attr->name, "printer-make-and-model") == 0 &&
+ attr->value_tag == IPP_TAG_TEXT)
+ make_model = attr->values[0].string.text;
+
+ if (strcmp(attr->name, "printer-state") == 0 &&
+ attr->value_tag == IPP_TAG_ENUM)
+ state = (ipp_pstate_t)attr->values[0].integer;
+
+ if (strcmp(attr->name, "printer-type") == 0 &&
+ attr->value_tag == IPP_TAG_ENUM)
+ type = (cups_ptype_t)attr->values[0].integer;
+
+ attr = attr->next;
+ }
+
+ /*
+ * See if we have everything needed...
+ */
+
+ if (uri == NULL)
+ {
+ if (attr == NULL)
+ break;
+ else
+ continue;
+ }
+
+ /*
+ * See if this is a local printer or class...
+ */
+
+ if (!(type & CUPS_PRINTER_REMOTE))
+ {
+ /*
+ * Send the printer information...
+ */
+
+ snprintf(packet, sizeof(packet), "%x %x %s \"%s\" \"%s\" \"%s\"\n",
+ type | CUPS_PRINTER_REMOTE, state, uri,
+ location, info, make_model);
+ puts(packet);
+
+ if (sendto(sock, packet, strlen(packet), 0,
+ (struct sockaddr *)&addr, sizeof(addr)) <= 0)
+ {
+ perror("cups-polld");
+ return (-1);
+ }
+ }
+
+ if (attr == NULL)
+ break;
+ }
+
+ ippDelete(response);
+ }
+ else
+ {
+ fprintf(stderr, "cups-polld: get-%s failed: %s\n",
+ op == CUPS_GET_PRINTERS ? "printers" : "classes",
+ ippErrorString(cupsLastError()));
+ return (-1);
+ }
+
+ return (0);
+}
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/scheduler/cups.pam b/scheduler/cups.pam
new file mode 100644
index 000000000..f38e70184
--- /dev/null
+++ b/scheduler/cups.pam
@@ -0,0 +1,2 @@
+auth required /lib/security/pam_pwdb.so nullok shadow
+account required /lib/security/pam_pwdb.so
diff --git a/scheduler/cupsd.dsp b/scheduler/cupsd.dsp
new file mode 100644
index 000000000..60b2cb927
--- /dev/null
+++ b/scheduler/cupsd.dsp
@@ -0,0 +1,173 @@
+# Microsoft Developer Studio Project File - Name="cupsd" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Application" 0x0101
+
+CFG=cupsd - 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 "cupsd.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 "cupsd.mak" CFG="cupsd - Win32 Debug"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "cupsd - Win32 Release" (based on "Win32 (x86) Application")
+!MESSAGE "cupsd - Win32 Debug" (based on "Win32 (x86) Application")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "cupsd - 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 "_WINDOWS" /D "_MBCS" /YX /FD /c
+# ADD CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# 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 /nologo /subsystem:windows /machine:I386
+# ADD 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 /nologo /subsystem:windows /machine:I386
+
+!ELSEIF "$(CFG)" == "cupsd - 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 "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c
+# ADD CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /I "../visualc" /I ".." /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# 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 /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept
+# ADD 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 /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept
+
+!ENDIF
+
+# Begin Target
+
+# Name "cupsd - Win32 Release"
+# Name "cupsd - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=.\auth.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\classes.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\client.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\conf.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\dirsvc.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\ipp.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\job.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\listen.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\log.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\main.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\printers.c
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=.\auth.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\classes.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\client.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\conf.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\cupsd.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\dirsvc.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\job.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\printers.h
+# End Source File
+# End Group
+# End Target
+# End Project
diff --git a/scheduler/cupsd.h b/scheduler/cupsd.h
new file mode 100644
index 000000000..24d207616
--- /dev/null
+++ b/scheduler/cupsd.h
@@ -0,0 +1,176 @@
+/*
+ * "$Id$"
+ *
+ * Main header file for the Common UNIX Printing System (CUPS) scheduler.
+ *
+ * Copyright 1997-2000 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 <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 "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_HISTORY 1 /* Preserve job history? */
+#define DEFAULT_FILES 0 /* Preserve job files? */
+#define DEFAULT_TIMEOUT 300 /* Timeout during requests/updates */
+#define DEFAULT_KEEPALIVE 60 /* Timeout between requests */
+#define DEFAULT_INTERVAL 30 /* Interval between browse updates */
+#ifdef WIN32 /* Fix for broken Linux setlocale() */
+# define DEFAULT_LANGUAGE setlocale(LC_ALL,"")
+ /* Default language encoding */
+#else
+# define DEFAULT_LANGUAGE getenv("LANG")
+ /* Default language encoding */
+#endif /* !WIN32 */
+#define DEFAULT_CHARSET "utf-8"
+ /* Default charset */
+
+
+/*
+ * 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 "cert.h"
+#include "client.h"
+#include "auth.h"
+#include "dirsvc.h"
+#include "printers.h"
+#include "classes.h"
+#include "job.h"
+#include "conf.h"
+#include "banners.h"
+
+
+/*
+ * Directory handling functions...
+ */
+
+#if 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
+
+
+/*
+ * 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? */
+VAR char TZ[1024] VALUE("TZ=GMT");
+ /* Timezone configuration */
+
+VAR ipp_t *Devices VALUE(NULL),
+ /* Available devices */
+ *PPDs VALUE(NULL);
+ /* Available PPDs */
+
+
+/*
+ * Prototypes...
+ */
+
+extern void LoadDevices(const char *d);
+extern void LoadPPDs(const char *d);
+
+/*
+ * End of "$Id$".
+ */
diff --git a/scheduler/devices.c b/scheduler/devices.c
new file mode 100644
index 000000000..c518a204c
--- /dev/null
+++ b/scheduler/devices.c
@@ -0,0 +1,372 @@
+/*
+ * "$Id$"
+ *
+ * Device scanning routines for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-2000 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:
+ *
+ * LoadDevices() - Load all available devices.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "cupsd.h"
+
+
+/*
+ * Device information structure...
+ */
+
+typedef struct
+{
+ char device_class[128], /* Device class */
+ device_make_and_model[128], /* Make and model, if known */
+ device_info[128], /* Device info/description */
+ device_uri[1024]; /* Device URI */
+} dev_info_t;
+
+
+/*
+ * Local globals...
+ */
+
+static int num_devs, /* Number of devices */
+ alloc_devs; /* Number of allocated entries */
+static dev_info_t *devs; /* Device info */
+
+
+/*
+ * Local functions...
+ */
+
+static int compare_devs(const dev_info_t *p0, const dev_info_t *p1);
+
+
+/*
+ * 'LoadDevices()' - Load all available devices.
+ */
+
+void
+LoadDevices(const char *d) /* I - Directory to scan */
+{
+ int i; /* Looping var */
+ FILE *fp; /* Pipe to device backend */
+ DIR *dir; /* Directory pointer */
+ DIRENT *dent; /* Directory entry */
+ char filename[1024], /* Name of backend */
+ line[2048], /* Line from backend */
+ dclass[64], /* Device class */
+ uri[1024], /* Device URI */
+ info[128], /* Device info */
+ make_model[256];/* Make and model */
+ dev_info_t *dev; /* Current device */
+
+
+ /*
+ * We always support the "file" device...
+ */
+
+ Devices = ippNew();
+
+ ippAddString(Devices, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
+ "device-class", NULL, "file");
+ ippAddString(Devices, IPP_TAG_PRINTER, IPP_TAG_TEXT,
+ "device-info", NULL, "Disk File");
+ ippAddString(Devices, IPP_TAG_PRINTER, IPP_TAG_TEXT,
+ "device-make-and-model", NULL, "Unknown");
+ ippAddString(Devices, IPP_TAG_PRINTER, IPP_TAG_URI,
+ "device-uri", NULL, "file");
+
+ /*
+ * Try opening the backend directory...
+ */
+
+ if ((dir = opendir(d)) == NULL)
+ {
+ LogMessage(L_ERROR, "LoadDevices: Unable to open backend directory \"%s\": %s",
+ d, strerror(errno));
+ return;
+ }
+
+ /*
+ * Setup the devices array...
+ */
+
+ alloc_devs = 0;
+ num_devs = 0;
+ devs = (dev_info_t *)0;
+
+ /*
+ * Loop through all of the device backends...
+ */
+
+ while ((dent = readdir(dir)) != NULL)
+ {
+ /*
+ * Skip "." and ".."...
+ */
+
+ if (dent->d_name[0] == '.')
+ continue;
+
+ /*
+ * Run the backend with no arguments and collect the output...
+ */
+
+ snprintf(filename, sizeof(filename), "%s/%s", d, dent->d_name);
+ if ((fp = popen(filename, "r")) != NULL)
+ {
+ while (fgets(line, sizeof(line), fp) != NULL)
+ {
+ /*
+ * Each line is of the form:
+ *
+ * class URI "make model" "name"
+ */
+
+ if (sscanf(line, "%63s%1023s%*[ \t]\"%127[^\"]\"%*[ \t]\"%255[^\"]",
+ dclass, uri, make_model, info) != 4)
+ {
+ /*
+ * Bad format; strip trailing newline and write an error message.
+ */
+
+ line[strlen(line) - 1] = '\0';
+ LogMessage(L_ERROR, "LoadDevices: Bad line from \"%s\": %s",
+ dent->d_name, line);
+ }
+ else
+ {
+ /*
+ * Add the device to the array of available devices...
+ */
+
+ if (num_devs >= alloc_devs)
+ {
+ /*
+ * Allocate (more) memory for the PPD files...
+ */
+
+ if (alloc_devs == 0)
+ dev = malloc(sizeof(dev_info_t) * 16);
+ else
+ dev = realloc(devs, sizeof(dev_info_t) * (alloc_devs + 16));
+
+ if (dev == NULL)
+ {
+ LogMessage(L_ERROR, "load_devs: Ran out of memory for %d devices!",
+ alloc_devs + 16);
+ closedir(dir);
+ return;
+ }
+
+ devs = dev;
+ alloc_devs += 16;
+ }
+
+ dev = devs + num_devs;
+ num_devs ++;
+
+ memset(dev, 0, sizeof(dev_info_t));
+ strncpy(dev->device_class, dclass, sizeof(dev->device_class) - 1);
+ strncpy(dev->device_info, info, sizeof(dev->device_info) - 1);
+ strncpy(dev->device_make_and_model, make_model,
+ sizeof(dev->device_make_and_model) - 1);
+ strncpy(dev->device_uri, uri, sizeof(dev->device_uri) - 1);
+
+ LogMessage(L_DEBUG, "LoadDevices: Added device \"%s\"...", uri);
+ }
+ }
+
+ pclose(fp);
+ }
+ else
+ LogMessage(L_WARN, "LoadDevices: Unable to execute \"%s\" backend: %s",
+ dent->d_name, strerror(errno));
+ }
+
+ closedir(dir);
+
+ /*
+ * Sort the available devices...
+ */
+
+ if (num_devs > 1)
+ qsort(devs, num_devs, sizeof(dev_info_t),
+ (int (*)(const void *, const void *))compare_devs);
+
+ /*
+ * Create the list of devices...
+ */
+
+ for (i = num_devs, dev = devs; i > 0; i --, dev ++)
+ {
+ /*
+ * Add strings to attributes...
+ */
+
+ ippAddSeparator(Devices);
+ ippAddString(Devices, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
+ "device-class", NULL, dev->device_class);
+ ippAddString(Devices, IPP_TAG_PRINTER, IPP_TAG_TEXT,
+ "device-info", NULL, dev->device_info);
+ ippAddString(Devices, IPP_TAG_PRINTER, IPP_TAG_TEXT,
+ "device-make-and-model", NULL, dev->device_make_and_model);
+ ippAddString(Devices, IPP_TAG_PRINTER, IPP_TAG_URI,
+ "device-uri", NULL, dev->device_uri);
+ }
+
+ /*
+ * Free the devices array...
+ */
+
+ if (alloc_devs)
+ free(devs);
+}
+
+
+/*
+ * 'compare_devs()' - Compare PPD file make and model names for sorting.
+ */
+
+static int /* O - Result of comparison */
+compare_devs(const dev_info_t *d0, /* I - First PPD file */
+ const dev_info_t *d1) /* I - Second PPD file */
+{
+ const char *s, /* First name */
+ *t; /* Second name */
+ int diff, /* Difference between digits */
+ digits; /* Number of digits */
+
+
+ /*
+ * First compare names...
+ */
+
+ s = d0->device_info;
+ t = d1->device_info;
+
+ /*
+ * Loop through both nicknames, 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;
+ s ++;
+ t ++;
+
+ 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 if (diff)
+ 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 if ((diff = strcasecmp(d0->device_class, d1->device_class)) != 0)
+ return (diff);
+ else
+ return (strcasecmp(d0->device_uri, d1->device_uri));
+}
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/scheduler/dirsvc.c b/scheduler/dirsvc.c
new file mode 100644
index 000000000..52a0121f7
--- /dev/null
+++ b/scheduler/dirsvc.c
@@ -0,0 +1,727 @@
+/*
+ * "$Id$"
+ *
+ * Directory services routines for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-2000 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.
+ * StartPolling() - Start polling servers as needed.
+ * StopPolling() - Stop polling servers as needed.
+ */
+
+/*
+ * 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(L_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(L_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(L_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);
+ BrowseSocket = 0;
+ }
+}
+
+
+/*
+ * 'UpdateBrowseList()' - Update the browse lists for any new browse data.
+ */
+
+void
+UpdateBrowseList(void)
+{
+ int i; /* Looping var */
+ int auth; /* Authorization status */
+ int len, /* Length of name string */
+ offset; /* Offset in name string */
+ int bytes; /* Number of bytes left */
+ char packet[1540]; /* Broadcast packet */
+ struct sockaddr_in srcaddr; /* Source address */
+ char srcname[1024]; /* Source hostname */
+ unsigned address; /* Source address (host order) */
+ struct hostent *srchost; /* Host entry for source address */
+ 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 */
+ info[IPP_MAX_NAME], /* Information string */
+ location[IPP_MAX_NAME], /* Location string */
+ make_model[IPP_MAX_NAME];/* Make and model string */
+ 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...
+ */
+
+ len = sizeof(srcaddr);
+ if ((bytes = recvfrom(BrowseSocket, packet, sizeof(packet), 0,
+ (struct sockaddr *)&srcaddr, &len)) <= 0)
+ {
+ LogMessage(L_ERROR, "Browse recv failed - %s.",
+ strerror(errno));
+ LogMessage(L_ERROR, "Browsing turned off.");
+
+ StopBrowsing();
+ Browsing = 0;
+ return;
+ }
+
+ packet[bytes] = '\0';
+
+ /*
+ * Figure out where it came from...
+ */
+
+ address = ntohl(srcaddr.sin_addr.s_addr);
+
+ if (HostNameLookups)
+#ifndef __sgi
+ srchost = gethostbyaddr((char *)&(srcaddr.sin_addr), sizeof(struct in_addr),
+ AF_INET);
+#else
+ srchost = gethostbyaddr(&(srcaddr.sin_addr), sizeof(struct in_addr),
+ AF_INET);
+#endif /* !__sgi */
+ else
+ srchost = NULL;
+
+ if (srchost == NULL)
+ sprintf(srcname, "%d.%d.%d.%d", address >> 24, (address >> 16) & 255,
+ (address >> 8) & 255, address & 255);
+ else
+ {
+ strncpy(srcname, srchost->h_name, sizeof(srcname) - 1);
+ srcname[sizeof(srcname) - 1] = '\0';
+ }
+
+ len = strlen(srcname);
+
+ /*
+ * Do ACL stuff...
+ */
+
+ if (BrowseACL)
+ {
+ if (address == 0x7f000001 || strcasecmp(srcname, "localhost") == 0)
+ {
+ /*
+ * Access from localhost (127.0.0.1) is always allowed...
+ */
+
+ auth = AUTH_ALLOW;
+ }
+ else
+ {
+ /*
+ * Do authorization checks on the domain/address...
+ */
+
+ switch (BrowseACL->order_type)
+ {
+ default :
+ auth = AUTH_DENY; /* anti-compiler-warning-code */
+ break;
+
+ case AUTH_ALLOW : /* Order Deny,Allow */
+ auth = AUTH_ALLOW;
+
+ if (CheckAuth(address, srcname, len,
+ BrowseACL->num_deny, BrowseACL->deny))
+ auth = AUTH_DENY;
+
+ if (CheckAuth(address, srcname, len,
+ BrowseACL->num_allow, BrowseACL->allow))
+ auth = AUTH_ALLOW;
+ break;
+
+ case AUTH_DENY : /* Order Allow,Deny */
+ auth = AUTH_DENY;
+
+ if (CheckAuth(address, srcname, len,
+ BrowseACL->num_allow, BrowseACL->allow))
+ auth = AUTH_ALLOW;
+
+ if (CheckAuth(address, srcname, len,
+ BrowseACL->num_deny, BrowseACL->deny))
+ auth = AUTH_DENY;
+ break;
+ }
+ }
+ }
+ else
+ auth = AUTH_ALLOW;
+
+ if (auth == AUTH_DENY)
+ {
+ LogMessage(L_DEBUG, "UpdateBrowseList: Refused %d bytes from %s", bytes,
+ srcname);
+ return;
+ }
+
+ LogMessage(L_DEBUG, "UpdateBrowseList: (%d bytes from %s) %s", bytes, srcname,
+ packet);
+
+ /*
+ * Parse packet...
+ */
+
+ location[0] = '\0';
+ info[0] = '\0';
+ make_model[0] = '\0';
+
+ if (sscanf(packet,
+ "%x%x%1023s%*[^\"]\"%127[^\"]%*[^\"]\"%127[^\"]%*[^\"]\"%127[^\"]",
+ (unsigned *)&type, (unsigned *)&state, uri, location, info,
+ make_model) < 3)
+ {
+ LogMessage(L_WARN, "UpdateBrowseList: Garbled browse packet - %s",
+ packet);
+ return;
+ }
+
+ DEBUG_printf(("type=%x, state=%x, uri=\"%s\"\n"
+ "location=\"%s\", info=\"%s\", make_model=\"%s\"\n",
+ type, state, uri, location, info, make_model));
+
+ /*
+ * 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;
+
+ /*
+ * Do relaying...
+ */
+
+ for (i = 0; i < NumRelays; i ++)
+ if (CheckAuth(address, srcname, len, 1, &(Relays[i].from)))
+ if (sendto(BrowseSocket, packet, bytes, 0,
+ (struct sockaddr *)&(Relays[i].to),
+ sizeof(struct sockaddr_in)) <= 0)
+ {
+ LogMessage(L_ERROR, "UpdateBrowseList: sendto failed for relay %d - %s.",
+ i + 1, strerror(errno));
+ 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)
+ snprintf(name, sizeof(name), "%s@%s", resource + 9, host);
+ else
+ return;
+
+ if ((p = FindClass(name)) == 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->more_info, uri);
+ strcpy(p->device_uri, uri);
+ strcpy(p->hostname, host);
+
+ strcpy(p->location, "Location Unknown");
+ strcpy(p->info, "No Information Available");
+ snprintf(p->make_model, sizeof(p->make_model), "Remote Class on %s",
+ host);
+
+ SetPrinterAttrs(p);
+ }
+ }
+ else
+ {
+ /*
+ * Remote destination is a printer...
+ */
+
+ if (strncmp(resource, "/printers/", 10) == 0)
+ snprintf(name, sizeof(name), "%s@%s", resource + 10, host);
+ else
+ return;
+
+ if ((p = FindPrinter(name)) == 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->more_info, uri);
+ strcpy(p->device_uri, uri);
+ strcpy(p->hostname, host);
+
+ strcpy(p->location, "Location Unknown");
+ strcpy(p->info, "No Information Available");
+ snprintf(p->make_model, sizeof(p->make_model), "Remote Printer on %s",
+ host);
+
+ SetPrinterAttrs(p);
+ }
+ }
+
+ /*
+ * Update the state...
+ */
+
+ p->type = type;
+ p->state = state;
+ p->accepting = state != IPP_PRINTER_STOPPED;
+ p->browse_time = time(NULL);
+
+ if (location[0])
+ strcpy(p->location, location);
+ if (info[0])
+ strcpy(p->info, info);
+ if (make_model[0])
+ strcpy(p->make_model, make_model);
+
+ /*
+ * If the remote printer is idle, check for local jobs that might need to
+ * go to it...
+ */
+
+ if (p->state == IPP_PRINTER_IDLE)
+ CheckJobs();
+
+ /*
+ * 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, first = NULL;
+ 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 = FindPrinter(name)) == NULL)
+ {
+ /*
+ * Need to add the class...
+ */
+
+ pclass = AddPrinter(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 ((pclass = FindPrinter(name)) != NULL &&
+ !(pclass->type & CUPS_PRINTER_IMPLICIT))
+ {
+ /*
+ * 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[1453];
+ /* Browse data packet */
+
+
+ if (!Browsing || BrowseInterval == 0)
+ return;
+
+ /*
+ * 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)
+ {
+ LogMessage(L_INFO, "Remote destination \"%s\" has timed out; deleting it...",
+ p->name);
+ DeletePrinter(p);
+ }
+ }
+ else if (p->browse_time < ut && !(p->type & CUPS_PRINTER_IMPLICIT))
+ {
+ /*
+ * Need to send an update...
+ */
+
+ p->browse_time = time(NULL);
+
+ snprintf(packet, sizeof(packet), "%x %x %s \"%s\" \"%s\" \"%s\"\n",
+ p->type | CUPS_PRINTER_REMOTE, p->state, p->uri,
+ p->location, p->info, p->make_model);
+
+ 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(L_ERROR, "SendBrowseList: sendto failed for browser %d - %s.",
+ i + 1, strerror(errno));
+ LogMessage(L_ERROR, "Browsing turned off.");
+
+ StopBrowsing();
+ Browsing = 0;
+ return;
+ }
+ }
+ }
+}
+
+
+/*
+ * 'StartPolling()' - Start polling servers as needed.
+ */
+
+void
+StartPolling(void)
+{
+ int i; /* Looping var */
+ dirsvc_poll_t *poll; /* Current polling server */
+ int pid; /* New process ID */
+ char sport[10]; /* Server port */
+ char bport[10]; /* Browser port */
+ char interval[10]; /* Poll interval */
+
+
+ sprintf(bport, "%d", BrowsePort);
+ sprintf(interval, "%d", BrowseInterval);
+
+ for (i = 0, poll = Polled; i < NumPolled; i ++, poll ++)
+ {
+ sprintf(sport, "%d", poll->port);
+
+ if ((pid = fork()) == 0)
+ {
+ /*
+ * Child...
+ */
+
+ setgid(Group);
+ setuid(User);
+
+ execl(CUPS_SERVERBIN "/daemon/cups-polld", "cups-polld", poll->hostname,
+ sport, interval, bport, NULL);
+ exit(errno);
+ }
+ else if (pid < 0)
+ {
+ LogMessage(L_ERROR, "StartPolling: Unable to fork polling daemon - %s",
+ strerror(errno));
+ poll->pid = 0;
+ break;
+ }
+ else
+ {
+ poll->pid = pid;
+ LogMessage(L_DEBUG, "StartPolling: Started polling daemon for %s:%d, pid = %d",
+ poll->hostname, poll->port, pid);
+ }
+ }
+}
+
+
+/*
+ * 'StopPolling()' - Stop polling servers as needed.
+ */
+
+void
+StopPolling(void)
+{
+ int i; /* Looping var */
+ dirsvc_poll_t *poll; /* Current polling server */
+
+
+ for (i = 0, poll = Polled; i < NumPolled; i ++, poll ++)
+ if (poll->pid)
+ kill(poll->pid, SIGTERM);
+}
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/scheduler/dirsvc.h b/scheduler/dirsvc.h
new file mode 100644
index 000000000..10cb3d78c
--- /dev/null
+++ b/scheduler/dirsvc.h
@@ -0,0 +1,94 @@
+/*
+ * "$Id$"
+ *
+ * Directory services definitions for the Common UNIX Printing System
+ * (CUPS) scheduler.
+ *
+ * Copyright 1997-2000 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
+ */
+
+/*
+ * Relay structure...
+ */
+
+typedef struct
+{
+ authmask_t from; /* Source address/name mask */
+ struct sockaddr_in to; /* Destination address */
+} dirsvc_relay_t;
+
+
+/*
+ * Polling structure...
+ */
+
+typedef struct
+{
+ char hostname[16]; /* Hostname (actually, IP address) */
+ int port; /* Port number */
+ int pid; /* Current poll server PID */
+} dirsvc_poll_t;
+
+
+/*
+ * 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 */
+VAR location_t *BrowseACL VALUE(NULL);
+ /* Browser access control list */
+VAR int NumRelays VALUE(0);
+ /* Number of broadcast relays */
+VAR dirsvc_relay_t Relays[MAX_BROWSERS];
+ /* Broadcast relays */
+VAR int NumPolled VALUE(0);
+ /* Number of polled servers */
+VAR dirsvc_poll_t Polled[MAX_BROWSERS];
+ /* Polled servers */
+
+
+/*
+ * Prototypes...
+ */
+
+extern void StartBrowsing(void);
+extern void StopBrowsing(void);
+extern void UpdateBrowseList(void);
+extern void SendBrowseList(void);
+
+extern void StartPolling(void);
+extern void StopPolling(void);
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/scheduler/filter.c b/scheduler/filter.c
new file mode 100644
index 000000000..f1f4d0452
--- /dev/null
+++ b/scheduler/filter.c
@@ -0,0 +1,301 @@
+/*
+ * "$Id$"
+ *
+ * File type conversion routines for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-2000 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 <cups/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;
+ strncpy(temp->filter, filter, sizeof(temp->filter) - 1);
+ temp->filter[sizeof(temp->filter) - 1] = '\0';
+ }
+ }
+ 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;
+ strncpy(temp->filter, filter, sizeof(temp->filter) - 1);
+ temp->filter[sizeof(temp->filter) - 1] = '\0';
+
+ 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;
+ num_mintemp = 0;
+ mincurrent = 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/scheduler/ipp.c b/scheduler/ipp.c
new file mode 100644
index 000000000..d9042da3f
--- /dev/null
+++ b/scheduler/ipp.c
@@ -0,0 +1,4676 @@
+/*
+ * "$Id$"
+ *
+ * IPP routines for the Common UNIX Printing System (CUPS) scheduler.
+ *
+ * Copyright 1997-2000 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_file() - Add a file to a job.
+ * add_job_state_reasons() - Add the "job-state-reasons" attribute based
+ * upon the job and printer state...
+ * add_printer() - Add a printer to the system.
+ * add_printer_state_reasons() - Add the "printer-state-reasons" attribute
+ * based upon the printer state...
+ * add_queued_job_count() - Add the "queued-job-count" attribute for
+ * cancel_all_jobs() - Cancel all print jobs.
+ * cancel_job() - Cancel a print job.
+ * copy_attrs() - Copy attributes from one request to another.
+ * create_job() - Print a file to a printer or class.
+ * copy_file() - Copy a PPD file or interface script...
+ * delete_printer() - Remove a printer or class from the system.
+ * get_default() - Get the default destination.
+ * get_devices() - Get the list of available devices on the
+ * local system.
+ * get_jobs() - Get a list of jobs for the specified printer.
+ * get_job_attrs() - Get job attributes.
+ * get_ppds() - Get the list of PPD files on the local
+ * system.
+ * get_printer_attrs() - Get printer attributes.
+ * get_printers() - Get a list of printers.
+ * hold_job() - Hold a print job.
+ * move_job() - Move a job.
+ * print_job() - Print a file to a printer or class.
+ * reject_jobs() - Reject print jobs to a printer.
+ * release_job() - Release a held print job.
+ * restart_job() - Restart an old print job.
+ * send_document() - Send a file to a printer or class.
+ * send_ipp_error() - Send an error status back to the IPP client.
+ * set_default() - Set the default destination...
+ * set_job_attrs() - Set job attributes.
+ * start_printer() - Start a printer.
+ * stop_printer() - Stop a printer.
+ * validate_job() - Validate printer options and destination.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "cupsd.h"
+#include <pwd.h>
+#include <grp.h>
+#ifdef HAVE_LIBZ
+# include <zlib.h>
+#endif /* HAVE_LIBZ */
+
+
+/*
+ * 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 int add_file(client_t *con, job_t *job, mime_type_t *filetype);
+static void add_job_state_reasons(client_t *con, job_t *job);
+static void add_printer(client_t *con, ipp_attribute_t *uri);
+static void add_printer_state_reasons(client_t *con, printer_t *p);
+static void add_queued_job_count(client_t *con, printer_t *p);
+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,
+ ipp_tag_t group);
+static int copy_file(const char *from, const char *to);
+static void create_job(client_t *con, ipp_attribute_t *uri);
+static void delete_printer(client_t *con, ipp_attribute_t *uri);
+static void get_default(client_t *con);
+static void get_devices(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_ppds(client_t *con);
+static void get_printers(client_t *con, int type);
+static void get_printer_attrs(client_t *con, ipp_attribute_t *uri);
+static void hold_job(client_t *con, ipp_attribute_t *uri);
+static void move_job(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 release_job(client_t *con, ipp_attribute_t *uri);
+static void restart_job(client_t *con, ipp_attribute_t *uri);
+static void send_document(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 set_job_attrs(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 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] = con->request->request.op.version[0];
+ con->response->request.status.version[1] = con->request->request.op.version[1];
+ 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...
+ */
+
+ LogMessage(L_ERROR, "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/job-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;
+
+ if (attr)
+ attr = attr->next;
+ if (attr != NULL && strcmp(attr->name, "attributes-natural-language") == 0 &&
+ attr->value_tag == IPP_TAG_LANGUAGE)
+ language = attr;
+ else
+ language = NULL;
+
+ if ((attr = ippFindAttribute(con->request, "printer-uri", IPP_TAG_URI)) != NULL)
+ uri = attr;
+ else if ((attr = ippFindAttribute(con->request, "job-uri", IPP_TAG_URI)) != NULL)
+ uri = attr;
+ else
+ uri = NULL;
+
+ if (charset)
+ ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
+ "attributes-charset", NULL, charset->values[0].string.text);
+ else
+ ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
+ "attributes-charset", NULL, DefaultCharset);
+
+ if (language)
+ ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
+ "attributes-natural-language", NULL,
+ language->values[0].string.text);
+ else
+ ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
+ "attributes-natural-language", NULL, DefaultLanguage);
+
+ 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.
+ */
+
+ if (charset == NULL)
+ LogMessage(L_ERROR, "ProcessIPPRequest: missing attributes-charset attribute!");
+
+ if (language == NULL)
+ LogMessage(L_ERROR, "ProcessIPPRequest: missing attributes-natural-language attribute!");
+
+ if (uri == NULL)
+ LogMessage(L_ERROR, "ProcessIPPRequest: missing printer-uri or job-uri attribute!");
+
+ send_ipp_error(con, IPP_BAD_REQUEST);
+ }
+ else
+ {
+ /*
+ * 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_CREATE_JOB :
+ create_job(con, uri);
+ break;
+
+ case IPP_SEND_DOCUMENT :
+ send_document(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_HOLD_JOB :
+ hold_job(con, uri);
+ break;
+
+ case IPP_RELEASE_JOB :
+ release_job(con, uri);
+ break;
+
+ case IPP_RESTART_JOB :
+ restart_job(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 IPP_SET_JOB_ATTRIBUTES :
+ set_job_attrs(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;
+
+ case CUPS_GET_DEVICES :
+ get_devices(con);
+ break;
+
+ case CUPS_GET_PPDS :
+ get_ppds(con);
+ break;
+
+ case CUPS_MOVE_JOB :
+ move_job(con, uri);
+ break;
+
+ default :
+ send_ipp_error(con, IPP_OPERATION_NOT_SUPPORTED);
+ }
+ }
+ }
+ }
+
+ SendHeader(con, HTTP_OK, "application/ipp");
+
+ con->http.data_encoding = HTTP_ENCODE_LENGTH;
+ con->http.data_remaining = ippLength(con->response);
+
+ httpPrintf(HTTP(con), "Content-Length: %d\r\n\r\n",
+ con->http.data_remaining);
+
+ 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 */
+ const 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)
+ {
+ LogMessage(L_ERROR, "accept_jobs: admin request on bad resource \'%s\'!",
+ con->uri);
+ 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 = ValidateDest(resource, &dtype)) == NULL)
+ {
+ /*
+ * Bad URI...
+ */
+
+ LogMessage(L_ERROR, "accept_jobs: resource name \'%s\' no good!", 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';
+
+ if (dtype == CUPS_PRINTER_CLASS)
+ SaveAllClasses();
+ else
+ SaveAllPrinters();
+
+ LogMessage(L_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 */
+ const 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)
+ {
+ LogMessage(L_ERROR, "add_class: admin request on bad resource \'%s\'!",
+ con->uri);
+ 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 ((pclass = FindPrinter(resource + 9)) != NULL &&
+ !(pclass->type & CUPS_PRINTER_REMOTE))
+ {
+ /*
+ * Yes, return an error...
+ */
+
+ send_ipp_error(con, IPP_NOT_POSSIBLE);
+ return;
+ }
+
+ /*
+ * No, add the pclass...
+ */
+
+ pclass = AddClass(resource + 9);
+ }
+
+ /*
+ * Look for attributes and copy them over as needed...
+ */
+
+ if ((attr = ippFindAttribute(con->request, "printer-location", IPP_TAG_TEXT)) != NULL)
+ {
+ strncpy(pclass->location, attr->values[0].string.text, sizeof(pclass->location) - 1);
+ pclass->location[sizeof(pclass->location) - 1] = '\0';
+ }
+
+ if ((attr = ippFindAttribute(con->request, "printer-info", IPP_TAG_TEXT)) != NULL)
+ {
+ strncpy(pclass->info, attr->values[0].string.text, sizeof(pclass->info) - 1);
+ pclass->info[sizeof(pclass->info) - 1] = '\0';
+ }
+
+ if ((attr = ippFindAttribute(con->request, "printer-more-info", IPP_TAG_URI)) != NULL)
+ {
+ strncpy(pclass->more_info, attr->values[0].string.text, sizeof(pclass->more_info) - 1);
+ pclass->more_info[sizeof(pclass->more_info) - 1] = '\0';
+ }
+
+ if ((attr = ippFindAttribute(con->request, "printer-is-accepting-jobs", IPP_TAG_BOOLEAN)) != NULL)
+ {
+ LogMessage(L_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(L_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, "job-sheets-default", IPP_TAG_NAME)) == NULL)
+ attr = ippFindAttribute(con->request, "job-sheets-default", IPP_TAG_KEYWORD);
+ if (attr != NULL)
+ {
+ strncpy(pclass->job_sheets[0], attr->values[0].string.text,
+ sizeof(pclass->job_sheets[0]) - 1);
+ if (attr->num_values > 1)
+ strncpy(pclass->job_sheets[1], attr->values[1].string.text,
+ sizeof(pclass->job_sheets[1]) - 1);
+ else
+ strcpy(pclass->job_sheets[1], "none");
+ }
+
+ 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 = ValidateDest(resource, &dtype)) == NULL)
+ {
+ /*
+ * Bad URI...
+ */
+
+ LogMessage(L_ERROR, "add_class: resource name \'%s\' no good!", 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();
+ CheckJobs();
+
+ LogMessage(L_INFO, "New class \'%s\' added by \'%s\'.", pclass->name,
+ con->username);
+
+ con->response->request.status.status_code = IPP_OK;
+}
+
+
+/*
+ * 'add_file()' - Add a file to a job.
+ */
+
+static int
+add_file(client_t *con, /* I - Connection to client */
+ job_t *job, /* I - Job to add to */
+ mime_type_t *filetype) /* I - Type of file */
+{
+ mime_type_t **filetypes; /* New filetypes array... */
+
+
+ /*
+ * Add the file to the job...
+ */
+
+ if (job->num_files == 0)
+ filetypes = (mime_type_t **)malloc(sizeof(mime_type_t *));
+ else
+ filetypes = (mime_type_t **)realloc(job->filetypes,
+ (job->num_files + 1) *
+ sizeof(mime_type_t));
+
+ if (filetypes == NULL)
+ {
+ CancelJob(job->id);
+ LogMessage(L_ERROR, "add_file: unable to allocate memory for file types!");
+ send_ipp_error(con, IPP_INTERNAL_ERROR);
+ return (-1);
+ }
+
+ job->filetypes = filetypes;
+ job->filetypes[job->num_files] = filetype;
+
+ job->num_files ++;
+
+ return (0);
+}
+
+
+/*
+ * 'add_job_state_reasons()' - Add the "job-state-reasons" attribute based
+ * upon the job and printer state...
+ */
+
+static void
+add_job_state_reasons(client_t *con, /* I - Client connection */
+ job_t *job) /* I - Job info */
+{
+ printer_t *dest; /* Destination printer */
+
+
+ switch (job->state->values[0].integer)
+ {
+ case IPP_JOB_PENDING :
+ if (job->dtype & CUPS_PRINTER_CLASS)
+ dest = FindClass(job->dest);
+ else
+ dest = FindPrinter(job->dest);
+
+ if (dest != NULL && dest->state == IPP_PRINTER_STOPPED)
+ ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD,
+ "job-state-reasons", NULL, "printer-stopped");
+ else
+ ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD,
+ "job-state-reasons", NULL, "none");
+ break;
+
+ case IPP_JOB_HELD :
+ ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD,
+ "job-state-reasons", NULL, "job-hold-until-specified");
+ break;
+
+ case IPP_JOB_PROCESSING :
+ ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD,
+ "job-state-reasons", NULL, "job-printing");
+ break;
+
+ case IPP_JOB_STOPPED :
+ ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD,
+ "job-state-reasons", NULL, "job-stopped");
+ break;
+
+ case IPP_JOB_CANCELLED :
+ ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD,
+ "job-state-reasons", NULL, "job-canceled-by-user");
+ break;
+
+ case IPP_JOB_ABORTED :
+ ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD,
+ "job-state-reasons", NULL, "aborted-by-system");
+ break;
+
+ case IPP_JOB_COMPLETED :
+ ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD,
+ "job-state-reasons", NULL, "job-completed-successfully");
+ break;
+ }
+}
+
+
+/*
+ * '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 */
+#ifdef HAVE_LIBZ
+ gzFile fp; /* Script/PPD file */
+#else
+ FILE *fp; /* Script/PPD file */
+#endif /* HAVE_LIBZ */
+ char line[1024]; /* Line from file... */
+ char srcfile[1024], /* Source Script/PPD file */
+ dstfile[1024]; /* Destination Script/PPD file */
+
+
+ /*
+ * Was this operation called from the correct URI?
+ */
+
+ if (strncmp(con->uri, "/admin/", 7) != 0)
+ {
+ LogMessage(L_ERROR, "add_printer: admin request on bad resource \'%s\'!",
+ con->uri);
+ 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 ((printer = FindClass(resource + 10)) != NULL &&
+ !(printer->type & CUPS_PRINTER_REMOTE))
+ {
+ /*
+ * Yes, return an error...
+ */
+
+ send_ipp_error(con, IPP_NOT_POSSIBLE);
+ return;
+ }
+
+ /*
+ * No, add the printer...
+ */
+
+ printer = AddPrinter(resource + 10);
+ }
+
+ /*
+ * Look for attributes and copy them over as needed...
+ */
+
+ if ((attr = ippFindAttribute(con->request, "printer-location", IPP_TAG_TEXT)) != NULL)
+ {
+ strncpy(printer->location, attr->values[0].string.text, sizeof(printer->location) - 1);
+ printer->location[sizeof(printer->location) - 1] = '\0';
+ }
+
+ if ((attr = ippFindAttribute(con->request, "printer-info", IPP_TAG_TEXT)) != NULL)
+ {
+ strncpy(printer->info, attr->values[0].string.text, sizeof(printer->info) - 1);
+ printer->info[sizeof(printer->info) - 1] = '\0';
+ }
+
+ if ((attr = ippFindAttribute(con->request, "printer-more-info", IPP_TAG_URI)) != NULL)
+ {
+ strncpy(printer->more_info, attr->values[0].string.text, sizeof(printer->more_info) - 1);
+ printer->more_info[sizeof(printer->more_info) - 1] = '\0';
+ }
+
+ if ((attr = ippFindAttribute(con->request, "device-uri", IPP_TAG_URI)) != NULL)
+ {
+ LogMessage(L_INFO, "Setting %s device-uri to \"%s\" (was \"%s\".)",
+ printer->name, attr->values[0].string.text, printer->device_uri);
+
+ strncpy(printer->device_uri, attr->values[0].string.text,
+ sizeof(printer->device_uri) - 1);
+ printer->device_uri[sizeof(printer->device_uri) - 1] = '\0';
+ }
+
+ if ((attr = ippFindAttribute(con->request, "printer-is-accepting-jobs", IPP_TAG_BOOLEAN)) != NULL)
+ {
+ LogMessage(L_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(L_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';
+ }
+ if ((attr = ippFindAttribute(con->request, "job-sheets-default", IPP_TAG_NAME)) == NULL)
+ attr = ippFindAttribute(con->request, "job-sheets-default", IPP_TAG_KEYWORD);
+ if (attr != NULL)
+ {
+ strncpy(printer->job_sheets[0], attr->values[0].string.text,
+ sizeof(printer->job_sheets[0]) - 1);
+ if (attr->num_values > 1)
+ strncpy(printer->job_sheets[1], attr->values[1].string.text,
+ sizeof(printer->job_sheets[1]) - 1);
+ else
+ strcpy(printer->job_sheets[1], "none");
+ }
+
+ /*
+ * 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])
+ strcpy(srcfile, con->filename);
+ else if ((attr = ippFindAttribute(con->request, "ppd-name", IPP_TAG_NAME)) != NULL)
+ snprintf(srcfile, sizeof(srcfile), CUPS_DATADIR "/model/%s",
+ attr->values[0].string.text);
+ else
+ srcfile[0] = '\0';
+
+ LogMessage(L_DEBUG, "add_printer: srcfile = \"%s\"", srcfile);
+
+#ifdef HAVE_LIBZ
+ if (srcfile[0] && (fp = gzopen(srcfile, "rb")) != NULL)
+#else
+ if (srcfile[0] && (fp = fopen(srcfile, "rb")) != NULL)
+#endif /* HAVE_LIBZ */
+ {
+ /*
+ * Yes; get the first line from it...
+ */
+
+ line[0] = '\0';
+#ifdef HAVE_LIBZ
+ gzgets(fp, line, sizeof(line));
+ gzclose(fp);
+#else
+ fgets(line, sizeof(line), fp);
+ fclose(fp);
+#endif /* HAVE_LIBZ */
+
+ /*
+ * Then see what kind of file it is...
+ */
+
+ snprintf(dstfile, sizeof(dstfile), "%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(dstfile);
+ }
+ else
+ {
+ /*
+ * This must be an interface script, so move the file over to the
+ * interfaces directory and make it executable...
+ */
+
+ if (copy_file(srcfile, dstfile))
+ {
+ LogMessage(L_ERROR, "add_printer: Unable to copy interface script - %s!",
+ strerror(errno));
+ send_ipp_error(con, IPP_INTERNAL_ERROR);
+ return;
+ }
+ else
+ {
+ LogMessage(L_DEBUG, "add_printer: Copied interface script successfully!");
+ chmod(dstfile, 0755);
+ }
+ }
+
+ snprintf(dstfile, sizeof(dstfile), "%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...
+ */
+
+ if (copy_file(srcfile, dstfile))
+ {
+ LogMessage(L_ERROR, "add_printer: Unable to copy PPD file - %s!",
+ strerror(errno));
+ send_ipp_error(con, IPP_INTERNAL_ERROR);
+ return;
+ }
+ else
+ {
+ LogMessage(L_DEBUG, "add_printer: Copied PPD file successfully!");
+ chmod(dstfile, 0644);
+ }
+ }
+ else
+ {
+ /*
+ * This must be an interface script, so remove any old PPD file that
+ * may be lying around...
+ */
+
+ unlink(dstfile);
+ }
+ }
+
+ /*
+ * Make this printer the default if there is none...
+ */
+
+ if (DefaultPrinter == NULL)
+ DefaultPrinter = printer;
+
+ /*
+ * Update the printer attributes and return...
+ */
+
+ SetPrinterAttrs(printer);
+ SaveAllPrinters();
+
+ if (printer->job != NULL)
+ {
+ /*
+ * Stop the current job and then restart it below...
+ */
+
+ StopJob(((job_t *)printer->job)->id);
+ }
+
+ CheckJobs();
+
+ LogMessage(L_INFO, "New printer \'%s\' added by \'%s\'.", printer->name,
+ con->username);
+
+ con->response->request.status.status_code = IPP_OK;
+}
+
+
+/*
+ * 'add_printer_state_reasons()' - Add the "printer-state-reasons" attribute
+ * based upon the printer state...
+ */
+
+static void
+add_printer_state_reasons(client_t *con, /* I - Client connection */
+ printer_t *p) /* I - Printer info */
+{
+ switch (p->state)
+ {
+ case IPP_PRINTER_STOPPED :
+ ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
+ "printer-state-reasons", NULL, "paused");
+ break;
+
+ default :
+ ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
+ "printer-state-reasons", NULL, "none");
+ break;
+ }
+}
+
+
+/*
+ * 'add_queued_job_count()' - Add the "queued-job-count" attribute for
+ * the specified printer or class.
+ */
+
+static void
+add_queued_job_count(client_t *con, /* I - Client connection */
+ printer_t *p) /* I - Printer or class */
+{
+ job_t *job; /* Current job */
+ cups_ptype_t dtype; /* Destination type */
+ int count; /* Number of jobs on destination */
+
+
+ dtype = p->type & CUPS_PRINTER_CLASS;
+
+ for (count = 0, job = Jobs; job != NULL; job = job->next)
+ if (strcmp(job->dest, p->name) == 0 && job->dtype == dtype &&
+ job->state->values[0].integer < IPP_JOB_STOPPED)
+ count ++;
+
+ ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
+ "queued-job-count", count);
+}
+
+
+/*
+ * '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 */
+{
+ const 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)
+ {
+ LogMessage(L_ERROR, "cancel_all_jobs: admin request on bad resource \'%s\'!",
+ con->uri);
+ send_ipp_error(con, IPP_NOT_AUTHORIZED);
+ return;
+ }
+
+ /*
+ * See if we have a printer URI...
+ */
+
+ if (strcmp(uri->name, "printer-uri") != 0)
+ {
+ LogMessage(L_ERROR, "cancel_all_jobs: bad %s attribute \'%s\'!",
+ 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 = ValidateDest(resource, &dtype)) == NULL)
+ {
+ /*
+ * Bad URI...
+ */
+
+ LogMessage(L_ERROR, "cancel_all_jobs: resource name \'%s\' no good!", resource);
+ send_ipp_error(con, IPP_NOT_FOUND);
+ return;
+ }
+
+ /*
+ * Cancel all of the jobs and return...
+ */
+
+ CancelJobs(dest);
+ LogMessage(L_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 */
+{
+ int i; /* Looping var */
+ 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 */
+ job_t *job; /* Job information */
+ struct passwd *user; /* User info */
+ struct group *group; /* System group info */
+
+
+ DEBUG_printf(("cancel_job(%08x, %08x)\n", con, uri));
+
+ /*
+ * Verify that the POST operation was done to a valid URI.
+ */
+
+ if (strncmp(con->uri, "/classes/", 9) != 0 &&
+ strncmp(con->uri, "/jobs/", 5) != 0 &&
+ strncmp(con->uri, "/printers/", 10) != 0)
+ {
+ LogMessage(L_ERROR, "cancel_job: cancel request on bad resource \'%s\'!",
+ con->uri);
+ send_ipp_error(con, IPP_NOT_AUTHORIZED);
+ return;
+ }
+
+ /*
+ * 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)
+ {
+ LogMessage(L_ERROR, "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!
+ */
+
+ LogMessage(L_ERROR, "cancel_job: bad job-uri attribute \'%s\'!",
+ 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...
+ */
+
+ LogMessage(L_ERROR, "cancel_job: job #%d doesn't exist!", jobid);
+ send_ipp_error(con, IPP_NOT_FOUND);
+ return;
+ }
+
+ /*
+ * See if the job is owned by the requesting user...
+ */
+
+ if (con->username[0])
+ strcpy(username, con->username);
+ else 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
+ strcpy(username, "anonymous");
+
+ if (strcmp(username, job->username) != 0 && strcmp(username, "root") != 0)
+ {
+ /*
+ * Not the owner or root; check to see if the user is a member of the
+ * system group...
+ */
+
+ user = getpwnam(username);
+ endpwent();
+
+ group = getgrnam(SystemGroup);
+ endgrent();
+
+ if (group != NULL)
+ {
+ for (i = 0; group->gr_mem[i]; i ++)
+ if (strcmp(username, group->gr_mem[i]) == 0)
+ break;
+ }
+ else
+ i = 0;
+
+ if (user == NULL || group == NULL ||
+ (group->gr_mem[i] == NULL && group->gr_gid != user->pw_gid))
+ {
+ /*
+ * Username not found, group not found, or user is not part of the
+ * system group...
+ */
+
+ LogMessage(L_ERROR, "cancel_job: \"%s\" not authorized to delete job id %d owned by \"%s\"!",
+ username, jobid, job->username);
+ send_ipp_error(con, IPP_FORBIDDEN);
+ return;
+ }
+ }
+
+ /*
+ * Cancel the job and return...
+ */
+
+ CancelJob(jobid);
+ CheckJobs();
+
+ LogMessage(L_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 */
+ ipp_tag_t group) /* I - Group to copy */
+{
+ 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 (group != IPP_TAG_ZERO && fromattr->group_tag != group &&
+ fromattr->group_tag != IPP_TAG_ZERO)
+ continue;
+
+ if (req != NULL && fromattr->name != 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_ZERO :
+ ippAddSeparator(to);
+ break;
+
+ 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;
+
+ default :
+ break; /* anti-compiler-warning-code */
+ }
+ }
+}
+
+
+/*
+ * 'create_job()' - Print a file to a printer or class.
+ */
+
+static void
+create_job(client_t *con, /* I - Client connection */
+ ipp_attribute_t *uri) /* I - Printer URI */
+{
+ ipp_attribute_t *attr; /* Current attribute */
+ const 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 */
+ printer_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 */
+ printer_t *printer; /* Printer data */
+ char filename[1024]; /* Banner filename */
+ banner_t *banner; /* Banner file */
+
+
+ DEBUG_printf(("create_job(%08x, %08x)\n", con, uri));
+
+ /*
+ * Verify that the POST operation was done to a valid URI.
+ */
+
+ if (strncmp(con->uri, "/classes/", 9) != 0 &&
+ strncmp(con->uri, "/printers/", 10) != 0)
+ {
+ LogMessage(L_ERROR, "create_job: cancel request on bad resource \'%s\'!",
+ con->uri);
+ send_ipp_error(con, IPP_NOT_AUTHORIZED);
+ return;
+ }
+
+ /*
+ * Is the destination valid?
+ */
+
+ httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
+
+ if ((dest = ValidateDest(resource, &dtype)) == NULL)
+ {
+ /*
+ * Bad URI...
+ */
+
+ LogMessage(L_ERROR, "create_job: resource name \'%s\' no good!", resource);
+ send_ipp_error(con, IPP_NOT_FOUND);
+ return;
+ }
+
+ /*
+ * See if the printer is accepting jobs...
+ */
+
+ if (dtype == CUPS_PRINTER_CLASS)
+ {
+ printer = FindClass(dest);
+ sprintf(printer_uri, "http://%s:%d/classes/%s", ServerName,
+ ntohs(con->http.hostaddr.sin_port), dest);
+ }
+ else
+ {
+ printer = FindPrinter(dest);
+
+ sprintf(printer_uri, "http://%s:%d/printers/%s", ServerName,
+ ntohs(con->http.hostaddr.sin_port), dest);
+ }
+
+ if (!printer->accepting)
+ {
+ LogMessage(L_INFO, "create_job: destination \'%s\' is not accepting jobs.",
+ dest);
+ 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
+ ippAddInteger(con->request, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-priority",
+ priority = 50);
+
+ if ((attr = ippFindAttribute(con->request, "job-name", IPP_TAG_NAME)) != NULL)
+ title = attr->values[0].string.text;
+ else
+ ippAddString(con->request, IPP_TAG_JOB, IPP_TAG_NAME, "job-name", NULL,
+ title = "Untitled");
+
+ if ((job = AddJob(priority, printer->name)) == NULL)
+ {
+ LogMessage(L_ERROR, "create_job: unable to add job for destination \'%s\'!",
+ dest);
+ send_ipp_error(con, IPP_INTERNAL_ERROR);
+ return;
+ }
+
+ job->dtype = dtype;
+ job->attrs = con->request;
+ con->request = NULL;
+
+ strncpy(job->title, title, sizeof(job->title) - 1);
+
+ attr = ippFindAttribute(job->attrs, "requesting-user-name", IPP_TAG_NAME);
+
+ if (con->username[0])
+ strcpy(job->username, con->username);
+ else if (attr != NULL)
+ {
+ LogMessage(L_DEBUG, "create_job: requesting-user-name = \'%s\'",
+ attr->values[0].string.text);
+
+ strncpy(job->username, attr->values[0].string.text, sizeof(job->username) - 1);
+ job->username[sizeof(job->username) - 1] = '\0';
+ }
+ else
+ strcpy(job->username, "anonymous");
+
+ if (attr == NULL)
+ ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_NAME, "job-originating-user-name",
+ NULL, job->username);
+ else
+ {
+ attr->group_tag = IPP_TAG_JOB;
+ free(attr->name);
+ attr->name = strdup("job-originating-user-name");
+ }
+
+ ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER, "time-at-creation",
+ time(NULL) - StartTime);
+ attr = ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER,
+ "time-at-processing", 0);
+ attr->value_tag = IPP_TAG_NOVALUE;
+ attr = ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER,
+ "time-at-completed", 0);
+ attr->value_tag = IPP_TAG_NOVALUE;
+
+ if (!(printer->type & CUPS_PRINTER_REMOTE))
+ {
+ /*
+ * Add job sheets options...
+ */
+
+ if ((attr = ippFindAttribute(job->attrs, "job-sheets", IPP_TAG_NAME)) == NULL)
+ if ((attr = ippFindAttribute(job->attrs, "job-sheets", IPP_TAG_KEYWORD)) != NULL)
+ attr->value_tag = IPP_TAG_NAME;
+
+ if (attr == NULL)
+ {
+ attr = ippAddStrings(job->attrs, IPP_TAG_JOB, IPP_TAG_NAME, "job->sheets",
+ 2, NULL, NULL);
+ attr->values[0].string.text = strdup(printer->job_sheets[0]);
+ attr->values[1].string.text = strdup(printer->job_sheets[1]);
+ }
+
+ /*
+ * See if we need to add the starting sheet...
+ */
+
+ if (strcasecmp(attr->values[0].string.text, "none") != 0 &&
+ (banner = FindBanner(attr->values[0].string.text)) != NULL)
+ {
+ /*
+ * Yes...
+ */
+
+ if (add_file(con, job, banner->filetype))
+ return;
+
+ sprintf(filename, "%s/d%05d-%03d", RequestRoot, job->id, job->num_files);
+ symlink(banner->filename, filename);
+ }
+ }
+
+ SaveJob(job->id);
+
+ LogMessage(L_INFO, "Job %d created on \'%s\' by \'%s\'.", job->id,
+ job->dest, job->username);
+
+ /*
+ * Add remaining job attributes...
+ */
+
+ ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-id", job->id);
+ job->state = ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_ENUM,
+ "job-state", IPP_JOB_STOPPED);
+ ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_URI, "job-printer-uri", NULL,
+ printer_uri);
+ ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_NAME, "job-name", NULL,
+ title);
+
+ /*
+ * Fill in the response info...
+ */
+
+ sprintf(job_uri, "http://%s:%d/jobs/%d", ServerName,
+ ntohs(con->http.hostaddr.sin_port), job->id);
+ ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_URI, "job-uri", NULL, job_uri);
+
+ ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-id", job->id);
+
+ ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_ENUM, "job-state",
+ job->state->values[0].integer);
+
+ con->response->request.status.status_code = IPP_OK;
+}
+
+
+/*
+ * 'copy_file()' - Copy a PPD file or interface script...
+ */
+
+static int /* O - 0 = success, -1 = error */
+copy_file(const char *from, /* I - Source file */
+ const char *to) /* I - Destination file */
+{
+#ifdef HAVE_LIBZ
+ gzFile src; /* Source file */
+#else
+ int src; /* Source file */
+#endif /* HAVE_LIBZ */
+ int dst, /* Destination file */
+ bytes; /* Bytes to read/write */
+ char buffer[8192]; /* Copy buffer */
+
+
+#ifdef HAVE_LIBZ
+ if ((src = gzopen(from, "rb")) == NULL)
+ return (-1);
+#else
+ if ((src = open(from, O_RDONLY)) < 0)
+ return (-1);
+#endif /* HAVE_LIBZ */
+
+ if ((dst = open(to, O_WRONLY | O_CREAT | O_TRUNC, 0644)) < 0)
+ {
+#ifdef HAVE_LIBZ
+ gzclose(src);
+#else
+ close(src);
+#endif /* HAVE_LIBZ */
+ return (-1);
+ }
+
+#ifdef HAVE_LIBZ
+ while ((bytes = gzread(src, buffer, sizeof(buffer))) > 0)
+#else
+ while ((bytes = read(src, buffer, sizeof(buffer))) > 0)
+#endif /* HAVE_LIBZ */
+ if (write(dst, buffer, bytes) < bytes)
+ {
+#ifdef HAVE_LIBZ
+ gzclose(src);
+#else
+ close(src);
+#endif /* HAVE_LIBZ */
+ close(dst);
+ return (-1);
+ }
+
+#ifdef HAVE_LIBZ
+ gzclose(src);
+#else
+ close(src);
+#endif /* HAVE_LIBZ */
+ close(dst);
+
+ return (0);
+}
+
+
+/*
+ * '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 */
+{
+ const 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)
+ {
+ LogMessage(L_ERROR, "delete_printer: admin request on bad resource \'%s\'!",
+ con->uri);
+ 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 = ValidateDest(resource, &dtype)) == NULL)
+ {
+ /*
+ * Bad URI...
+ */
+
+ LogMessage(L_ERROR, "delete_printer: resource name \'%s\' no good!", 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(L_INFO, "Class \'%s\' deleted by \'%s\'.", dest,
+ con->username);
+ else
+ LogMessage(L_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), IPP_TAG_ZERO);
+
+ con->response->request.status.status_code = IPP_OK;
+ }
+ else
+ con->response->request.status.status_code = IPP_NOT_FOUND;
+}
+
+
+/*
+ * 'get_devices()' - Get the list of available devices on the local system.
+ */
+
+static void
+get_devices(client_t *con) /* I - Client connection */
+{
+ /*
+ * Copy the device attributes to the response using the requested-attributes
+ * attribute that may be provided by the client.
+ */
+
+ copy_attrs(con->response, Devices,
+ ippFindAttribute(con->request, "requested-attributes",
+ IPP_TAG_KEYWORD), IPP_TAG_ZERO);
+
+ con->response->request.status.status_code = IPP_OK;
+}
+
+
+/*
+ * '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 */
+{
+ int i; /* Looping var */
+ ipp_attribute_t *attr; /* Current attribute */
+ const 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 completed; /* Completed jobs? */
+ 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... */
+ char filename[1024]; /* Job filename */
+ struct stat filestats; /* Print file information */
+ size_t jobsize; /* Total job sizes */
+
+
+ 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 = ValidateDest(resource, &dtype)) == NULL)
+ {
+ /*
+ * Bad URI...
+ */
+
+ LogMessage(L_ERROR, "get_jobs: resource name \'%s\' no good!", 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)
+ completed = 1;
+ else
+ completed = 0;
+
+ /*
+ * See if they want to limit the number of jobs reported...
+ */
+
+ if ((attr = ippFindAttribute(con->request, "limit", IPP_TAG_INTEGER)) != NULL)
+ limit = attr->values[0].integer;
+ else
+ limit = 1000000;
+
+ /*
+ * 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)
+ {
+ if (con->username[0])
+ strcpy(username, con->username);
+ else 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
+ strcpy(username, "anonymous");
+ }
+ 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;
+
+ if (completed && job->state->values[0].integer <= IPP_JOB_STOPPED)
+ continue;
+ if (!completed && job->state->values[0].integer > IPP_JOB_STOPPED)
+ 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
+ * job-printer-up-time
+ *
+ * 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);
+
+ for (i = 0, jobsize = 0; i < job->num_files; i ++)
+ {
+ sprintf(filename, "%s/d%05d-%03d", RequestRoot, job->id, i + 1);
+ stat(filename, &filestats);
+ jobsize += filestats.st_size;
+ }
+
+ ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER,
+ "job-k-octets", (jobsize + 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_URI,
+ "job-uri", NULL, job_uri);
+
+ ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER,
+ "job-printer-up-time", time(NULL) - StartTime);
+
+ /*
+ * 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), IPP_TAG_JOB);
+
+ add_job_state_reasons(con, job);
+
+ 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 */
+{
+ int i; /* Looping var */
+ 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... */
+ char filename[1024]; /* Job filename */
+ struct stat filestats; /* Print file information */
+ size_t jobsize; /* Total job sizes */
+
+
+ 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)
+ {
+ LogMessage(L_ERROR, "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!
+ */
+
+ LogMessage(L_ERROR, "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...
+ */
+
+ LogMessage(L_ERROR, "get_job_attrs: job #%d doesn't exist!", 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);
+
+ ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-id", job->id);
+
+ for (i = 0, jobsize = 0; i < job->num_files; i ++)
+ {
+ sprintf(filename, "%s/d%05d-%03d", RequestRoot, job->id, i + 1);
+ stat(filename, &filestats);
+ jobsize += filestats.st_size;
+ }
+
+ ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER,
+ "job-k-octets", (jobsize + 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_URI,
+ "job-uri", NULL, job_uri);
+
+ ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER,
+ "job-printer-up-time", time(NULL) - StartTime);
+
+ /*
+ * 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), IPP_TAG_JOB);
+
+ add_job_state_reasons(con, job);
+
+ 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_ppds()' - Get the list of PPD files on the local system.
+ */
+
+static void
+get_ppds(client_t *con) /* I - Client connection */
+{
+ /*
+ * Copy the PPD attributes to the response using the requested-attributes
+ * attribute that may be provided by the client.
+ */
+
+ copy_attrs(con->response, PPDs,
+ ippFindAttribute(con->request, "requested-attributes",
+ IPP_TAG_KEYWORD), IPP_TAG_ZERO);
+
+ 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 */
+{
+ const 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));
+ LogMessage(L_DEBUG, "get_printer_attrs(%08x, \"%s\")\n", con,
+ uri->values[0].string.text);
+
+ /*
+ * Is the destination valid?
+ */
+
+ httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
+
+ if ((dest = ValidateDest(resource, &dtype)) == NULL)
+ {
+ /*
+ * Bad URI...
+ */
+
+ LogMessage(L_ERROR, "get_printer_attrs: resource name \'%s\' no good!", 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.
+ */
+
+ ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_ENUM, "printer-state",
+ printer->state);
+
+ add_printer_state_reasons(con, printer);
+
+ 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);
+ ippAddDate(con->response, IPP_TAG_PRINTER, "printer-current-time",
+ ippTimeToDate(curtime));
+
+ add_queued_job_count(con, printer);
+
+ copy_attrs(con->response, printer->attrs,
+ ippFindAttribute(con->request, "requested-attributes",
+ IPP_TAG_KEYWORD), IPP_TAG_ZERO);
+
+ 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 */
+ int printer_type, /* printer-type attribute */
+ printer_mask; /* printer-type-mask attribute */
+ char *location; /* Location string */
+
+
+ DEBUG_printf(("get_printers(%08x)\n", con));
+
+ /*
+ * See if they want to limit the number of printers reported...
+ */
+
+ if ((attr = ippFindAttribute(con->request, "limit", IPP_TAG_INTEGER)) != NULL)
+ limit = attr->values[0].integer;
+ else
+ limit = 10000000;
+
+ /*
+ * Support filtering...
+ */
+
+ if ((attr = ippFindAttribute(con->request, "printer-type", IPP_TAG_ENUM)) != NULL)
+ printer_type = attr->values[0].integer;
+ else
+ printer_type = 0;
+
+ if ((attr = ippFindAttribute(con->request, "printer-type-mask", IPP_TAG_ENUM)) != NULL)
+ printer_mask = attr->values[0].integer;
+ else
+ printer_mask = 0;
+
+ if ((attr = ippFindAttribute(con->request, "location", IPP_TAG_TEXT)) != NULL)
+ location = attr->values[0].string.text;
+ else
+ location = NULL;
+
+ /*
+ * 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 &&
+ (printer->type & printer_mask) == printer_type &&
+ (location == NULL || strcasecmp(printer->location, location) == 0))
+ {
+ if (count > 0)
+ ippAddSeparator(con->response);
+
+ count ++;
+
+ /*
+ * 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);
+
+ add_printer_state_reasons(con, printer);
+
+ 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);
+ ippAddDate(con->response, IPP_TAG_PRINTER, "printer-current-time",
+ ippTimeToDate(curtime));
+
+ add_queued_job_count(con, printer);
+
+ copy_attrs(con->response, printer->attrs,
+ ippFindAttribute(con->request, "requested-attributes",
+ IPP_TAG_KEYWORD), IPP_TAG_ZERO);
+ }
+
+ con->response->request.status.status_code = IPP_OK;
+}
+
+
+/*
+ * 'hold_job()' - Hold a print job.
+ */
+
+static void
+hold_job(client_t *con, /* I - Client connection */
+ ipp_attribute_t *uri) /* I - Job or Printer URI */
+{
+ int i; /* Looping var */
+ 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 */
+ job_t *job; /* Job information */
+ struct passwd *user; /* User info */
+ struct group *group; /* System group info */
+
+
+ DEBUG_printf(("hold_job(%08x, %08x)\n", con, uri));
+
+ /*
+ * Verify that the POST operation was done to a valid URI.
+ */
+
+ if (strncmp(con->uri, "/classes/", 9) != 0 &&
+ strncmp(con->uri, "/jobs/", 5) != 0 &&
+ strncmp(con->uri, "/printers/", 10) != 0)
+ {
+ LogMessage(L_ERROR, "hold_job: hold request on bad resource \'%s\'!",
+ con->uri);
+ send_ipp_error(con, IPP_NOT_AUTHORIZED);
+ return;
+ }
+
+ /*
+ * 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)
+ {
+ LogMessage(L_ERROR, "hold_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!
+ */
+
+ LogMessage(L_ERROR, "hold_job: bad job-uri attribute \'%s\'!",
+ 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...
+ */
+
+ LogMessage(L_ERROR, "hold_job: job #%d doesn't exist!", jobid);
+ send_ipp_error(con, IPP_NOT_FOUND);
+ return;
+ }
+
+ /*
+ * See if the job is owned by the requesting user...
+ */
+
+ if (con->username[0])
+ strcpy(username, con->username);
+ else 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
+ strcpy(username, "anonymous");
+
+ if (strcmp(username, job->username) != 0 && strcmp(username, "root") != 0)
+ {
+ /*
+ * Not the owner or root; check to see if the user is a member of the
+ * system group...
+ */
+
+ user = getpwnam(username);
+ endpwent();
+
+ group = getgrnam(SystemGroup);
+ endgrent();
+
+ if (group != NULL)
+ {
+ for (i = 0; group->gr_mem[i]; i ++)
+ if (strcmp(username, group->gr_mem[i]) == 0)
+ break;
+ }
+ else
+ i = 0;
+
+ if (user == NULL || group == NULL ||
+ (group->gr_mem[i] == NULL && group->gr_gid != user->pw_gid))
+ {
+ /*
+ * Username not found, group not found, or user is not part of the
+ * system group...
+ */
+
+ LogMessage(L_ERROR, "hold_job: \"%s\" not authorized to hold job id %d owned by \"%s\"!",
+ username, jobid, job->username);
+ send_ipp_error(con, IPP_FORBIDDEN);
+ return;
+ }
+ }
+
+ /*
+ * Hold the job and return...
+ */
+
+ HoldJob(jobid);
+
+ LogMessage(L_INFO, "Job %d was held by \'%s\'.", jobid,
+ con->username[0] ? con->username : "unknown");
+
+ con->response->request.status.status_code = IPP_OK;
+}
+
+
+/*
+ * 'move_job()' - Set job attributes.
+ */
+
+static void
+move_job(client_t *con, /* I - Client connection */
+ ipp_attribute_t *uri) /* I - Job URI */
+{
+ int i; /* Looping var */
+ ipp_attribute_t *attr; /* Current attribute */
+ int jobid; /* Job ID */
+ job_t *job; /* Current job */
+ const 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 */
+ struct passwd *user; /* User info */
+ struct group *group; /* System group info */
+
+
+ DEBUG_printf(("move_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)
+ {
+ LogMessage(L_ERROR, "move_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!
+ */
+
+ LogMessage(L_ERROR, "move_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 ((job = FindJob(jobid)) == NULL)
+ {
+ /*
+ * Nope - return a "not found" error...
+ */
+
+ LogMessage(L_ERROR, "move_job: job #%d doesn't exist!", jobid);
+ send_ipp_error(con, IPP_NOT_FOUND);
+ return;
+ }
+
+ /*
+ * See if the job has been completed...
+ */
+
+ if (job->state->values[0].integer > IPP_JOB_STOPPED)
+ {
+ /*
+ * Return a "not-possible" error...
+ */
+
+ LogMessage(L_ERROR, "move_job: job #%d is finished and cannot be altered!", jobid);
+ send_ipp_error(con, IPP_NOT_POSSIBLE);
+ return;
+ }
+
+ /*
+ * See if the job is owned by the requesting user...
+ */
+
+ if (con->username[0])
+ strcpy(username, con->username);
+ else 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
+ strcpy(username, "anonymous");
+
+ if (strcmp(username, job->username) != 0 && strcmp(username, "root") != 0)
+ {
+ /*
+ * Not the owner or root; check to see if the user is a member of the
+ * system group...
+ */
+
+ user = getpwnam(username);
+ endpwent();
+
+ group = getgrnam(SystemGroup);
+ endgrent();
+
+ if (group != NULL)
+ {
+ for (i = 0; group->gr_mem[i]; i ++)
+ if (strcmp(username, group->gr_mem[i]) == 0)
+ break;
+ }
+ else
+ i = 0;
+
+ if (user == NULL || group == NULL ||
+ (group->gr_mem[i] == NULL && group->gr_gid != user->pw_gid))
+ {
+ /*
+ * Username not found, group not found, or user is not part of the
+ * system group...
+ */
+
+ LogMessage(L_ERROR, "move_job: \"%s\" not authorized to delete job id %d owned by \"%s\"!",
+ username, jobid, job->username);
+ send_ipp_error(con, IPP_FORBIDDEN);
+ return;
+ }
+ }
+
+ if ((attr = ippFindAttribute(con->request, "job-printer-uri", IPP_TAG_URI)) == NULL)
+ {
+ /*
+ * Need job-printer-uri...
+ */
+
+ LogMessage(L_ERROR, "move_job: job-printer-uri attribute missing!");
+ send_ipp_error(con, IPP_BAD_REQUEST);
+ return;
+ }
+
+ /*
+ * Move the job to a different printer or class...
+ */
+
+ httpSeparate(attr->values[0].string.text, method, username, host, &port,
+ resource);
+ if ((dest = ValidateDest(resource, &dtype)) == NULL)
+ {
+ /*
+ * Bad URI...
+ */
+
+ LogMessage(L_ERROR, "move_job: resource name \'%s\' no good!", resource);
+ send_ipp_error(con, IPP_NOT_FOUND);
+ return;
+ }
+
+ MoveJob(jobid, dest);
+
+ /*
+ * Start jobs if possible...
+ */
+
+ CheckJobs();
+
+ /*
+ * Return with "everything is OK" status...
+ */
+
+ 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 */
+ const 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 */
+ printer_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 */
+ filename[1024]; /* Job filename */
+ 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 */
+ banner_t *banner; /* Current banner */
+
+
+ DEBUG_printf(("print_job(%08x, %08x)\n", con, uri));
+
+ /*
+ * Verify that the POST operation was done to a valid URI.
+ */
+
+ if (strncmp(con->uri, "/classes/", 9) != 0 &&
+ strncmp(con->uri, "/printers/", 10) != 0)
+ {
+ LogMessage(L_ERROR, "print_job: cancel request on bad resource \'%s\'!",
+ con->uri);
+ send_ipp_error(con, IPP_NOT_AUTHORIZED);
+ return;
+ }
+
+ /*
+ * 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 &&
+ strcmp(attr->values[0].string.text, "none") == 0)
+ {
+ LogMessage(L_ERROR, "print_job: Unsupported compression attribute %s!",
+ attr->values[0].string.text);
+ send_ipp_error(con, IPP_ATTRIBUTES);
+ ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_KEYWORD,
+ "compression", NULL, attr->values[0].string.text);
+ return;
+ }
+
+ /*
+ * Do we have a file to print?
+ */
+
+ if (con->filename[0] == '\0')
+ {
+ LogMessage(L_ERROR, "print_job: No file!?!");
+ 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)
+ {
+ /*
+ * Grab format from client...
+ */
+
+ if (sscanf(format->values[0].string.text, "%15[^/]/%31[^;]", super, type) != 2)
+ {
+ LogMessage(L_ERROR, "print_job: could not scan type \'%s\'!",
+ format->values[0].string.text);
+ send_ipp_error(con, IPP_BAD_REQUEST);
+ return;
+ }
+ }
+ else
+ {
+ /*
+ * No document format attribute? Auto-type it!
+ */
+
+ strcpy(super, "application");
+ strcpy(type, "octet-stream");
+ }
+
+ if (strcmp(super, "application") == 0 &&
+ strcmp(type, "octet-stream") == 0)
+ {
+ /*
+ * Auto-type the file...
+ */
+
+ LogMessage(L_DEBUG, "print_job: auto-typing file...");
+
+ filetype = mimeFileType(MimeDatabase, con->filename);
+
+ if (filetype != NULL)
+ {
+ /*
+ * Replace the document-format attribute value with the auto-typed one.
+ */
+
+ sprintf(mimetype, "%s/%s", filetype->super, filetype->type);
+
+ if (format != NULL)
+ {
+ free(format->values[0].string.text);
+ format->values[0].string.text = strdup(mimetype);
+ }
+ else
+ ippAddString(con->request, IPP_TAG_JOB, IPP_TAG_MIMETYPE,
+ "document-format", NULL, mimetype);
+ }
+ }
+ else
+ filetype = mimeType(MimeDatabase, super, type);
+
+ if (filetype == NULL)
+ {
+ LogMessage(L_ERROR, "print_job: Unsupported format \'%s\'!",
+ format->values[0].string.text);
+ send_ipp_error(con, IPP_DOCUMENT_FORMAT);
+ ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_MIMETYPE,
+ "document-format", NULL, format->values[0].string.text);
+ return;
+ }
+
+ LogMessage(L_DEBUG, "print_job: request file type is %s/%s.",
+ filetype->super, filetype->type);
+
+ /*
+ * Is the destination valid?
+ */
+
+ httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
+
+ if ((dest = ValidateDest(resource, &dtype)) == NULL)
+ {
+ /*
+ * Bad URI...
+ */
+
+ LogMessage(L_ERROR, "print_job: resource name \'%s\' no good!", resource);
+ send_ipp_error(con, IPP_NOT_FOUND);
+ return;
+ }
+
+ /*
+ * See if the printer is accepting jobs...
+ */
+
+ if (dtype == CUPS_PRINTER_CLASS)
+ {
+ printer = FindClass(dest);
+ sprintf(printer_uri, "http://%s:%d/classes/%s", ServerName,
+ ntohs(con->http.hostaddr.sin_port), dest);
+ }
+ else
+ {
+ printer = FindPrinter(dest);
+
+ sprintf(printer_uri, "http://%s:%d/printers/%s", ServerName,
+ ntohs(con->http.hostaddr.sin_port), dest);
+ }
+
+ if (!printer->accepting)
+ {
+ LogMessage(L_INFO, "print_job: destination \'%s\' is not accepting jobs.",
+ dest);
+ 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
+ ippAddInteger(con->request, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-priority",
+ priority = 50);
+
+ if ((attr = ippFindAttribute(con->request, "job-name", IPP_TAG_NAME)) != NULL)
+ title = attr->values[0].string.text;
+ else
+ ippAddString(con->request, IPP_TAG_JOB, IPP_TAG_NAME, "job-name", NULL,
+ title = "Untitled");
+
+
+ if ((job = AddJob(priority, printer->name)) == NULL)
+ {
+ LogMessage(L_ERROR, "print_job: unable to add job for destination \'%s\'!",
+ dest);
+ send_ipp_error(con, IPP_INTERNAL_ERROR);
+ return;
+ }
+
+ job->dtype = dtype;
+ job->attrs = con->request;
+ con->request = NULL;
+
+ if (!(printer->type & CUPS_PRINTER_REMOTE))
+ {
+ /*
+ * Add job sheets options...
+ */
+
+ if ((attr = ippFindAttribute(job->attrs, "job-sheets", IPP_TAG_NAME)) == NULL)
+ if ((attr = ippFindAttribute(job->attrs, "job-sheets", IPP_TAG_KEYWORD)) != NULL)
+ attr->value_tag = IPP_TAG_NAME;
+
+ if (attr == NULL)
+ {
+ attr = ippAddStrings(job->attrs, IPP_TAG_JOB, IPP_TAG_NAME, "job-sheets",
+ 2, NULL, NULL);
+ attr->values[0].string.text = strdup(printer->job_sheets[0]);
+ attr->values[1].string.text = strdup(printer->job_sheets[1]);
+ }
+
+ /*
+ * See if we need to add the starting sheet...
+ */
+
+ if (strcasecmp(attr->values[0].string.text, "none") != 0 &&
+ (banner = FindBanner(attr->values[0].string.text)) != NULL)
+ {
+ /*
+ * Yes...
+ */
+
+ if (add_file(con, job, banner->filetype))
+ return;
+
+ sprintf(filename, "%s/d%05d-%03d", RequestRoot, job->id, job->num_files);
+ symlink(banner->filename, filename);
+ }
+ }
+
+ /*
+ * See if we need to add the starting sheet...
+ */
+
+ if (strcasecmp(attr->values[0].string.text, "none") != 0 &&
+ (banner = FindBanner(attr->values[0].string.text)) != NULL)
+ {
+ /*
+ * Yes...
+ */
+
+ if (add_file(con, job, banner->filetype))
+ return;
+
+ sprintf(filename, "%s/d%05d-%03d", RequestRoot, job->id, job->num_files);
+ symlink(banner->filename, filename);
+ }
+
+ /*
+ * Add the job file...
+ */
+
+ if (add_file(con, job, filetype))
+ return;
+
+ sprintf(filename, "%s/d%05d-%03d", RequestRoot, job->id, job->num_files);
+ rename(con->filename, filename);
+
+ /*
+ * See if we need to add the ending sheet...
+ */
+
+ if (!(printer->type & CUPS_PRINTER_REMOTE))
+ {
+ if (attr->num_values > 1 &&
+ strcasecmp(attr->values[1].string.text, "none") != 0 &&
+ (banner = FindBanner(attr->values[1].string.text)) != NULL)
+ {
+ /*
+ * Yes...
+ */
+
+ if (add_file(con, job, banner->filetype))
+ return;
+
+ sprintf(filename, "%s/d%05d-%03d", RequestRoot, job->id, job->num_files);
+ symlink(banner->filename, filename);
+ }
+ }
+
+ /*
+ * Copy the rest of the job info...
+ */
+
+ strncpy(job->title, title, sizeof(job->title) - 1);
+
+ con->filename[0] = '\0';
+
+ attr = ippFindAttribute(job->attrs, "requesting-user-name", IPP_TAG_NAME);
+
+ if (con->username[0])
+ strcpy(job->username, con->username);
+ if (attr != NULL)
+ {
+ LogMessage(L_DEBUG, "print_job: requesting-user-name = \'%s\'",
+ attr->values[0].string.text);
+
+ strncpy(job->username, attr->values[0].string.text, sizeof(job->username) - 1);
+ job->username[sizeof(job->username) - 1] = '\0';
+ }
+ else
+ strcpy(job->username, "anonymous");
+
+ if (attr == NULL)
+ ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_NAME, "job-originating-user-name",
+ NULL, job->username);
+ else
+ {
+ attr->group_tag = IPP_TAG_JOB;
+ free(attr->name);
+ attr->name = strdup("job-originating-user-name");
+ }
+
+ LogMessage(L_INFO, "Job %d queued on \'%s\' by \'%s\'.", job->id,
+ job->dest, job->username);
+
+ /*
+ * Add remaining job attributes...
+ */
+
+ ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-id", job->id);
+ job->state = ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_ENUM,
+ "job-state", IPP_JOB_PENDING);
+ ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_URI, "job-printer-uri", NULL,
+ printer_uri);
+ ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_NAME, "job-name", NULL,
+ title);
+
+ ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER, "time-at-creation",
+ time(NULL) - StartTime);
+ attr = ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER,
+ "time-at-processing", 0);
+ attr->value_tag = IPP_TAG_NOVALUE;
+ attr = ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER,
+ "time-at-completed", 0);
+ attr->value_tag = IPP_TAG_NOVALUE;
+
+ SaveJob(job->id);
+
+ /*
+ * Start the job if possible...
+ */
+
+ CheckJobs();
+
+ /*
+ * Fill in the response info...
+ */
+
+ sprintf(job_uri, "http://%s:%d/jobs/%d", ServerName,
+ ntohs(con->http.hostaddr.sin_port), job->id);
+ ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_URI, "job-uri", NULL, job_uri);
+
+ ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-id", job->id);
+
+ ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_ENUM, "job-state",
+ job->state->values[0].integer);
+ add_job_state_reasons(con, job);
+
+ 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 */
+ const 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)
+ {
+ LogMessage(L_ERROR, "reject_jobs: admin request on bad resource \'%s\'!",
+ con->uri);
+ 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 = ValidateDest(resource, &dtype)) == NULL)
+ {
+ /*
+ * Bad URI...
+ */
+
+ LogMessage(L_ERROR, "reject_jobs: resource name \'%s\' no good!", 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
+ {
+ strncpy(printer->state_message, attr->values[0].string.text,
+ sizeof(printer->state_message) - 1);
+ printer->state_message[sizeof(printer->state_message) - 1] = '\0';
+ }
+
+ if (dtype == CUPS_PRINTER_CLASS)
+ SaveAllClasses();
+ else
+ SaveAllPrinters();
+
+ if (dtype == CUPS_PRINTER_CLASS)
+ LogMessage(L_INFO, "Class \'%s\' rejecting jobs (\'%s\').", name,
+ con->username);
+ else
+ LogMessage(L_INFO, "Printer \'%s\' rejecting jobs (\'%s\').", name,
+ con->username);
+
+ /*
+ * Everything was ok, so return OK status...
+ */
+
+ con->response->request.status.status_code = IPP_OK;
+}
+
+
+/*
+ * 'release_job()' - Release a held print job.
+ */
+
+static void
+release_job(client_t *con, /* I - Client connection */
+ ipp_attribute_t *uri) /* I - Job or Printer URI */
+{
+ int i; /* Looping var */
+ 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 */
+ job_t *job; /* Job information */
+ struct passwd *user; /* User info */
+ struct group *group; /* System group info */
+
+
+ DEBUG_printf(("release_job(%08x, %08x)\n", con, uri));
+
+ /*
+ * Verify that the POST operation was done to a valid URI.
+ */
+
+ if (strncmp(con->uri, "/classes/", 9) != 0 &&
+ strncmp(con->uri, "/jobs/", 5) != 0 &&
+ strncmp(con->uri, "/printers/", 10) != 0)
+ {
+ LogMessage(L_ERROR, "release_job: release request on bad resource \'%s\'!",
+ con->uri);
+ send_ipp_error(con, IPP_NOT_AUTHORIZED);
+ return;
+ }
+
+ /*
+ * 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)
+ {
+ LogMessage(L_ERROR, "release_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!
+ */
+
+ LogMessage(L_ERROR, "release_job: bad job-uri attribute \'%s\'!",
+ 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...
+ */
+
+ LogMessage(L_ERROR, "release_job: job #%d doesn't exist!", jobid);
+ send_ipp_error(con, IPP_NOT_FOUND);
+ return;
+ }
+
+ /*
+ * See if job is "held"...
+ */
+
+ if (job->state->values[0].integer != IPP_JOB_HELD)
+ {
+ /*
+ * Nope - return a "not possible" error...
+ */
+
+ LogMessage(L_ERROR, "release_job: job #%d is not held!", jobid);
+ send_ipp_error(con, IPP_NOT_POSSIBLE);
+ return;
+ }
+
+ /*
+ * See if the job is owned by the requesting user...
+ */
+
+ if (con->username[0])
+ strcpy(username, con->username);
+ else 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
+ strcpy(username, "anonymous");
+
+ if (strcmp(username, job->username) != 0 && strcmp(username, "root") != 0)
+ {
+ /*
+ * Not the owner or root; check to see if the user is a member of the
+ * system group...
+ */
+
+ user = getpwnam(username);
+ endpwent();
+
+ group = getgrnam(SystemGroup);
+ endgrent();
+
+ if (group != NULL)
+ {
+ for (i = 0; group->gr_mem[i]; i ++)
+ if (strcmp(username, group->gr_mem[i]) == 0)
+ break;
+ }
+ else
+ i = 0;
+
+ if (user == NULL || group == NULL ||
+ (group->gr_mem[i] == NULL && group->gr_gid != user->pw_gid))
+ {
+ /*
+ * Username not found, group not found, or user is not part of the
+ * system group...
+ */
+
+ LogMessage(L_ERROR, "release_job: \"%s\" not authorized to release job id %d owned by \"%s\"!",
+ username, jobid, job->username);
+ send_ipp_error(con, IPP_FORBIDDEN);
+ return;
+ }
+ }
+
+ /*
+ * Release the job and return...
+ */
+
+ ReleaseJob(jobid);
+
+ LogMessage(L_INFO, "Job %d was released by \'%s\'.", jobid,
+ con->username[0] ? con->username : "unknown");
+
+ con->response->request.status.status_code = IPP_OK;
+}
+
+
+/*
+ * 'restart_job()' - Restart an old print job.
+ */
+
+static void
+restart_job(client_t *con, /* I - Client connection */
+ ipp_attribute_t *uri) /* I - Job or Printer URI */
+{
+ int i; /* Looping var */
+ 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 */
+ job_t *job; /* Job information */
+ struct passwd *user; /* User info */
+ struct group *group; /* System group info */
+
+
+ DEBUG_printf(("restart_job(%08x, %08x)\n", con, uri));
+
+ /*
+ * Verify that the POST operation was done to a valid URI.
+ */
+
+ if (strncmp(con->uri, "/classes/", 9) != 0 &&
+ strncmp(con->uri, "/jobs/", 5) != 0 &&
+ strncmp(con->uri, "/printers/", 10) != 0)
+ {
+ LogMessage(L_ERROR, "restart_job: restart request on bad resource \'%s\'!",
+ con->uri);
+ send_ipp_error(con, IPP_NOT_AUTHORIZED);
+ return;
+ }
+
+ /*
+ * 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)
+ {
+ LogMessage(L_ERROR, "restart_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!
+ */
+
+ LogMessage(L_ERROR, "restart_job: bad job-uri attribute \'%s\'!",
+ 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...
+ */
+
+ LogMessage(L_ERROR, "restart_job: job #%d doesn't exist!", jobid);
+ send_ipp_error(con, IPP_NOT_FOUND);
+ return;
+ }
+
+ /*
+ * See if job is in any of the "completed" states...
+ */
+
+ if (job->state->values[0].integer <= IPP_JOB_PROCESSING)
+ {
+ /*
+ * Nope - return a "not possible" error...
+ */
+
+ LogMessage(L_ERROR, "restart_job: job #%d is not complete!", jobid);
+ send_ipp_error(con, IPP_NOT_POSSIBLE);
+ return;
+ }
+
+ /*
+ * See if we have retained the job files...
+ */
+
+ if (!JobFiles && job->state->values[0].integer > IPP_JOB_STOPPED)
+ {
+ /*
+ * Nope - return a "not possible" error...
+ */
+
+ LogMessage(L_ERROR, "restart_job: job #%d cannot be restarted - no files!", jobid);
+ send_ipp_error(con, IPP_NOT_POSSIBLE);
+ return;
+ }
+
+ /*
+ * See if the job is owned by the requesting user...
+ */
+
+ if (con->username[0])
+ strcpy(username, con->username);
+ else 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
+ strcpy(username, "anonymous");
+
+ if (strcmp(username, job->username) != 0 && strcmp(username, "root") != 0)
+ {
+ /*
+ * Not the owner or root; check to see if the user is a member of the
+ * system group...
+ */
+
+ user = getpwnam(username);
+ endpwent();
+
+ group = getgrnam(SystemGroup);
+ endgrent();
+
+ if (group != NULL)
+ {
+ for (i = 0; group->gr_mem[i]; i ++)
+ if (strcmp(username, group->gr_mem[i]) == 0)
+ break;
+ }
+ else
+ i = 0;
+
+ if (user == NULL || group == NULL ||
+ (group->gr_mem[i] == NULL && group->gr_gid != user->pw_gid))
+ {
+ /*
+ * Username not found, group not found, or user is not part of the
+ * system group...
+ */
+
+ LogMessage(L_ERROR, "restart_job: \"%s\" not authorized to restart job id %d owned by \"%s\"!",
+ username, jobid, job->username);
+ send_ipp_error(con, IPP_FORBIDDEN);
+ return;
+ }
+ }
+
+ /*
+ * Restart the job and return...
+ */
+
+ RestartJob(jobid);
+
+ LogMessage(L_INFO, "Job %d was restarted by \'%s\'.", jobid,
+ con->username[0] ? con->username : "unknown");
+
+ con->response->request.status.status_code = IPP_OK;
+}
+
+
+/*
+ * 'send_document()' - Send a file to a printer or class.
+ */
+
+static void
+send_document(client_t *con, /* I - Client connection */
+ ipp_attribute_t *uri) /* I - Printer URI */
+{
+ int i; /* Looping var */
+ ipp_attribute_t *attr; /* Current attribute */
+ ipp_attribute_t *format; /* Document-format attribute */
+ int jobid; /* Job ID number */
+ 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 */
+ struct passwd *user; /* User info */
+ struct group *group; /* System group info */
+ char filename[1024]; /* Job filename */
+ printer_t *printer; /* Current printer */
+ banner_t *banner; /* Current banner */
+
+
+ DEBUG_printf(("send_document(%08x, %08x)\n", con, uri));
+
+ /*
+ * Verify that the POST operation was done to a valid URI.
+ */
+
+ if (strncmp(con->uri, "/classes/", 9) != 0 &&
+ strncmp(con->uri, "/jobs/", 6) != 0 &&
+ strncmp(con->uri, "/printers/", 10) != 0)
+ {
+ LogMessage(L_ERROR, "send_document: print request on bad resource \'%s\'!",
+ con->uri);
+ send_ipp_error(con, IPP_NOT_AUTHORIZED);
+ return;
+ }
+
+ /*
+ * 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)
+ {
+ LogMessage(L_ERROR, "send_document: 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!
+ */
+
+ LogMessage(L_ERROR, "send_document: bad job-uri attribute \'%s\'!",
+ 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...
+ */
+
+ LogMessage(L_ERROR, "send_document: job #%d doesn't exist!", jobid);
+ send_ipp_error(con, IPP_NOT_FOUND);
+ return;
+ }
+
+ /*
+ * See if the job is owned by the requesting user...
+ */
+
+ if (con->username[0])
+ strcpy(username, con->username);
+ else 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
+ strcpy(username, "anonymous");
+
+ if (strcmp(username, job->username) != 0 && strcmp(username, "root") != 0)
+ {
+ /*
+ * Not the owner or root; check to see if the user is a member of the
+ * system group...
+ */
+
+ user = getpwnam(username);
+ endpwent();
+
+ group = getgrnam(SystemGroup);
+ endgrent();
+
+ if (group != NULL)
+ {
+ for (i = 0; group->gr_mem[i]; i ++)
+ if (strcmp(username, group->gr_mem[i]) == 0)
+ break;
+ }
+ else
+ i = 0;
+
+ if (user == NULL || group == NULL ||
+ (group->gr_mem[i] == NULL && group->gr_gid != user->pw_gid))
+ {
+ /*
+ * Username not found, group not found, or user is not part of the
+ * system group...
+ */
+
+ LogMessage(L_ERROR, "send_document: \"%s\" not authorized to send document for job id %d owned by \"%s\"!",
+ username, jobid, job->username);
+ send_ipp_error(con, IPP_FORBIDDEN);
+ return;
+ }
+ }
+
+ /*
+ * 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 &&
+ strcmp(attr->values[0].string.text, "none") == 0)
+ {
+ LogMessage(L_ERROR, "send_document: Unsupported compression attribute %s!",
+ attr->values[0].string.text);
+ send_ipp_error(con, IPP_ATTRIBUTES);
+ ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_KEYWORD,
+ "compression", NULL, attr->values[0].string.text);
+ return;
+ }
+
+ /*
+ * Do we have a file to print?
+ */
+
+ if (con->filename[0] == '\0')
+ {
+ LogMessage(L_ERROR, "send_document: No file!?!");
+ 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)
+ {
+ /*
+ * Grab format from client...
+ */
+
+ if (sscanf(format->values[0].string.text, "%15[^/]/%31[^;]", super, type) != 2)
+ {
+ LogMessage(L_ERROR, "send_document: could not scan type \'%s\'!",
+ format->values[0].string.text);
+ send_ipp_error(con, IPP_BAD_REQUEST);
+ return;
+ }
+ }
+ else
+ {
+ /*
+ * No document format attribute? Auto-type it!
+ */
+
+ strcpy(super, "application");
+ strcpy(type, "octet-stream");
+ }
+
+ if (strcmp(super, "application") == 0 &&
+ strcmp(type, "octet-stream") == 0)
+ {
+ /*
+ * Auto-type the file...
+ */
+
+ LogMessage(L_DEBUG, "send_document: auto-typing file...");
+
+ filetype = mimeFileType(MimeDatabase, con->filename);
+
+ if (filetype != NULL)
+ {
+ /*
+ * Replace the document-format attribute value with the auto-typed one.
+ */
+
+ sprintf(mimetype, "%s/%s", filetype->super, filetype->type);
+
+ if (format != NULL)
+ {
+ free(format->values[0].string.text);
+ format->values[0].string.text = strdup(mimetype);
+ }
+ else
+ ippAddString(con->request, IPP_TAG_JOB, IPP_TAG_MIMETYPE,
+ "document-format", NULL, mimetype);
+ }
+ }
+ else
+ filetype = mimeType(MimeDatabase, super, type);
+
+ if (filetype == NULL)
+ {
+ LogMessage(L_ERROR, "send_document: Unsupported format \'%s\'!",
+ format->values[0].string.text);
+ send_ipp_error(con, IPP_DOCUMENT_FORMAT);
+ ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_MIMETYPE,
+ "document-format", NULL, format->values[0].string.text);
+ return;
+ }
+
+ LogMessage(L_DEBUG, "send_document: request file type is %s/%s.",
+ filetype->super, filetype->type);
+
+ /*
+ * Add the file to the job...
+ */
+
+ if (add_file(con, job, filetype))
+ return;
+
+ sprintf(filename, "%s/d%05d-%03d", RequestRoot, job->id, job->num_files);
+ rename(con->filename, filename);
+
+ con->filename[0] = '\0';
+
+ LogMessage(L_INFO, "File queued in job #%d by \'%s\'.", job->id,
+ job->username);
+
+ /*
+ * Start the job if this is the last document...
+ */
+
+ if ((attr = ippFindAttribute(con->request, "last-document", IPP_TAG_BOOLEAN)) != NULL &&
+ attr->values[0].boolean)
+ {
+ /*
+ * See if we need to add the ending sheet...
+ */
+
+ if (job->dtype & CUPS_PRINTER_CLASS)
+ printer = FindClass(job->dest);
+ else
+ printer = FindPrinter(job->dest);
+
+ if (printer != NULL && !(printer->type & CUPS_PRINTER_REMOTE) &&
+ (attr = ippFindAttribute(job->attrs, "job-sheets", IPP_TAG_NAME)) != NULL &&
+ attr->num_values > 1 &&
+ strcasecmp(attr->values[1].string.text, "none") != 0 &&
+ (banner = FindBanner(attr->values[1].string.text)) != NULL)
+ {
+ /*
+ * Yes...
+ */
+
+ if (add_file(con, job, banner->filetype))
+ return;
+
+ sprintf(filename, "%s/d%05d-%03d", RequestRoot, job->id, job->num_files);
+ symlink(banner->filename, filename);
+ }
+
+ job->state->values[0].integer = IPP_JOB_PENDING;
+ CheckJobs();
+ }
+
+ /*
+ * Fill in the response info...
+ */
+
+ sprintf(job_uri, "http://%s:%d/jobs/%d", ServerName,
+ ntohs(con->http.hostaddr.sin_port), job->id);
+ ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_URI, "job-uri", NULL,
+ job_uri);
+
+ ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-id", job->id);
+
+ ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_ENUM, "job-state",
+ job->state->values[0].integer);
+ add_job_state_reasons(con, job);
+
+ 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));
+
+ LogMessage(L_DEBUG, "Sending IPP error code %x.", status);
+ if (con->filename[0])
+ unlink(con->filename);
+
+ con->response->request.status.status_code = status;
+}
+
+
+/*
+ * '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 */
+ const 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)
+ {
+ LogMessage(L_ERROR, "set_default: admin request on bad resource \'%s\'!",
+ con->uri);
+ 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 = ValidateDest(resource, &dtype)) == NULL)
+ {
+ /*
+ * Bad URI...
+ */
+
+ LogMessage(L_ERROR, "set_default: resource name \'%s\' no good!", 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(L_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;
+}
+
+
+/*
+ * 'set_job_attrs()' - Set job attributes.
+ */
+
+static void
+set_job_attrs(client_t *con, /* I - Client connection */
+ ipp_attribute_t *uri) /* I - Job URI */
+{
+ int i; /* Looping var */
+ ipp_attribute_t *attr, /* Current attribute */
+ *prev, /* Previous attribute */
+ *attr2, /* Job attribute */
+ *prev2; /* Previous job 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 */
+ struct passwd *user; /* User info */
+ struct group *group; /* System group info */
+
+
+ DEBUG_printf(("set_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)
+ {
+ LogMessage(L_ERROR, "set_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!
+ */
+
+ LogMessage(L_ERROR, "set_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...
+ */
+
+ LogMessage(L_ERROR, "set_job_attrs: job #%d doesn't exist!", jobid);
+ send_ipp_error(con, IPP_NOT_FOUND);
+ return;
+ }
+
+ /*
+ * See if the job has been completed...
+ */
+
+ if (job->state->values[0].integer > IPP_JOB_STOPPED)
+ {
+ /*
+ * Return a "not-possible" error...
+ */
+
+ LogMessage(L_ERROR, "set_job_attrs: job #%d is finished and cannot be altered!", jobid);
+ send_ipp_error(con, IPP_NOT_POSSIBLE);
+ return;
+ }
+
+ /*
+ * See if the job is owned by the requesting user...
+ */
+
+ if (con->username[0])
+ strcpy(username, con->username);
+ else 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
+ strcpy(username, "anonymous");
+
+ if (strcmp(username, job->username) != 0 && strcmp(username, "root") != 0)
+ {
+ /*
+ * Not the owner or root; check to see if the user is a member of the
+ * system group...
+ */
+
+ user = getpwnam(username);
+ endpwent();
+
+ group = getgrnam(SystemGroup);
+ endgrent();
+
+ if (group != NULL)
+ {
+ for (i = 0; group->gr_mem[i]; i ++)
+ if (strcmp(username, group->gr_mem[i]) == 0)
+ break;
+ }
+ else
+ i = 0;
+
+ if (user == NULL || group == NULL ||
+ (group->gr_mem[i] == NULL && group->gr_gid != user->pw_gid))
+ {
+ /*
+ * Username not found, group not found, or user is not part of the
+ * system group...
+ */
+
+ LogMessage(L_ERROR, "set_job_attrs: \"%s\" not authorized to delete job id %d owned by \"%s\"!",
+ username, jobid, job->username);
+ send_ipp_error(con, IPP_FORBIDDEN);
+ return;
+ }
+ }
+
+ /*
+ * See what the user wants to change.
+ */
+
+ for (attr = con->request->attrs, prev = NULL;
+ attr != NULL;
+ prev = attr, attr = attr->next)
+ {
+ if (attr->group_tag != IPP_TAG_JOB || !attr->name)
+ continue;
+
+ if (strcmp(attr->name, "job-priority") == 0 &&
+ attr->value_tag == IPP_TAG_INTEGER &&
+ job->state->values[0].integer != IPP_JOB_PROCESSING)
+ {
+ /*
+ * Change the job priority
+ */
+
+ SetJobPriority(jobid, attr->values[0].integer);
+ }
+ else if ((attr2 = ippFindAttribute(job->attrs, attr->name, attr->value_tag)) != NULL)
+ {
+ /*
+ * Some other value...
+ */
+
+ for (prev2 = job->attrs->attrs; prev2 != NULL; prev2 = prev2->next)
+ if (prev2->next == attr2)
+ break;
+
+ if (prev)
+ prev->next = attr->next;
+ else
+ con->request->attrs = attr->next;
+
+ if (prev2)
+ prev2->next = attr;
+ else
+ job->attrs->attrs = attr;
+
+ attr->next = attr2->next;
+ attr = prev;
+
+ _ipp_free_attr(attr2);
+
+ /*
+ * See if the job-name is being changed.
+ */
+
+ if (strcmp(attr->name, "job-name") == 0)
+ strncpy(job->title, attr->values[0].string.text, sizeof(job->title) - 1);
+ }
+ else if (attr->value_tag == IPP_TAG_DELETEATTR)
+ {
+ /*
+ * Delete the attribute...
+ */
+
+ for (attr2 = job->attrs->attrs, prev2 = NULL;
+ attr2 != NULL;
+ prev2 = attr2, attr2 = attr2->next)
+ if (attr2->name && strcmp(attr2->name, attr->name) == 0)
+ break;
+
+ if (attr2)
+ {
+ if (prev2)
+ prev2->next = attr2->next;
+ else
+ job->attrs->attrs = attr2->next;
+
+ _ipp_free_attr(attr2);
+ }
+ }
+ else
+ {
+ /*
+ * Add new option by moving it from one request to another...
+ */
+
+ for (attr2 = job->attrs->attrs; attr2 != NULL; attr2 = attr2->next)
+ if (!attr2->next)
+ break;
+
+ if (attr2)
+ attr2->next = attr;
+ else
+ job->attrs->attrs = attr;
+
+ if (prev)
+ prev->next = attr->next;
+ else
+ con->request->attrs = attr->next;
+
+ attr->next = NULL;
+ attr = prev;
+ }
+ }
+
+ /*
+ * Start jobs if possible...
+ */
+
+ CheckJobs();
+
+ /*
+ * Return with "everything is 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 */
+ const 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)
+ {
+ LogMessage(L_ERROR, "start_printer: admin request on bad resource \'%s\'!",
+ con->uri);
+ 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 = ValidateDest(resource, &dtype)) == NULL)
+ {
+ /*
+ * Bad URI...
+ */
+
+ LogMessage(L_ERROR, "start_printer: resource name \'%s\' no good!", 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(L_INFO, "Class \'%s\' started by \'%s\'.", name,
+ con->username);
+ else
+ LogMessage(L_INFO, "Printer \'%s\' started by \'%s\'.", name,
+ con->username);
+
+ printer->state_message[0] = '\0';
+
+ CheckJobs();
+
+ /*
+ * 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 */
+ const 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)
+ {
+ LogMessage(L_ERROR, "stop_printer: admin request on bad resource \'%s\'!",
+ con->uri);
+ 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 = ValidateDest(resource, &dtype)) == NULL)
+ {
+ /*
+ * Bad URI...
+ */
+
+ LogMessage(L_ERROR, "stop_printer: resource name \'%s\' no good!", 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
+ {
+ strncpy(printer->state_message, attr->values[0].string.text,
+ sizeof(printer->state_message) - 1);
+ printer->state_message[sizeof(printer->state_message) - 1] = '\0';
+ }
+
+ if (dtype == CUPS_PRINTER_CLASS)
+ LogMessage(L_INFO, "Class \'%s\' stopped by \'%s\'.", name,
+ con->username);
+ else
+ LogMessage(L_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_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));
+
+ /*
+ * Verify that the POST operation was done to a valid URI.
+ */
+
+ if (strncmp(con->uri, "/classes/", 9) != 0 &&
+ strncmp(con->uri, "/printers/", 10) != 0)
+ {
+ LogMessage(L_ERROR, "validate_job: request on bad resource \'%s\'!",
+ con->uri);
+ send_ipp_error(con, IPP_NOT_AUTHORIZED);
+ return;
+ }
+
+ /*
+ * 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 &&
+ strcmp(attr->values[0].string.text, "none") == 0)
+ {
+ LogMessage(L_ERROR, "validate_job: Unsupported compression attribute %s!",
+ attr->values[0].string.text);
+ send_ipp_error(con, IPP_ATTRIBUTES);
+ ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, 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)
+ {
+ if (sscanf(format->values[0].string.text, "%15[^/]/%31[^;]", super, type) != 2)
+ {
+ LogMessage(L_ERROR, "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)
+ {
+ LogMessage(L_ERROR, "validate_job: Unsupported format \'%s\'!\n",
+ format->values[0].string.text);
+ send_ipp_error(con, IPP_DOCUMENT_FORMAT);
+ ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, 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 (ValidateDest(resource, &dtype) == NULL)
+ {
+ /*
+ * Bad URI...
+ */
+
+ LogMessage(L_ERROR, "validate_job: resource name \'%s\' no good!", 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..c7212a5d8
--- /dev/null
+++ b/scheduler/job.c
@@ -0,0 +1,2317 @@
+/*
+ * "$Id$"
+ *
+ * Job management routines for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-2000 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.
+ * HoldJob() - Hold the specified job.
+ * LoadAllJobs() - Load all jobs from disk.
+ * LoadJob() - Load a job from disk.
+ * MoveJob() - Move the specified job to a different destination.
+ * ReleaseJob() - Release the specified job.
+ * RestartJob() - Restart the specified job.
+ * SaveJob() - Save a job to disk.
+ * SetJobPriority() - Set the priority of a job, moving it up/down in the
+ * list as needed.
+ * StartJob() - Start a print job.
+ * StopAllJobs() - Stop all print jobs.
+ * StopJob() - Stop a print job.
+ * UpdateJob() - Read a status update from a job's filters.
+ * ValidateDest() - Validate a printer/class destination.
+ * ipp_read_file() - Read an IPP request from a file.
+ * ipp_write_file() - Write an IPP request to a file.
+ * start_process() - Start a background process.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "cupsd.h"
+
+
+/*
+ * Local functions...
+ */
+
+static ipp_state_t ipp_read_file(const char *filename, ipp_t *ipp);
+static ipp_state_t ipp_write_file(const char *filename, ipp_t *ipp);
+static void set_time(job_t *job, const char *name);
+static int start_process(const char *command, char *argv[],
+ char *envp[], int in, int out, int err,
+ int root);
+
+
+/*
+ * 'AddJob()' - Add a new job to the job queue...
+ */
+
+job_t * /* O - New job record */
+AddJob(int priority, /* I - Job priority */
+ const 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);
+
+ 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 */
+{
+ int i; /* Looping var */
+ job_t *current, /* Current job */
+ *prev; /* Previous job in list */
+ char filename[1024]; /* Job filename */
+
+
+ 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->values[0].integer == IPP_JOB_PROCESSING)
+ StopJob(current->id);
+
+ current->state->values[0].integer = IPP_JOB_CANCELLED;
+
+ set_time(current, "job-at-completion");
+
+ /*
+ * Remove the print file for good if we aren't preserving jobs or
+ * files...
+ */
+
+ current->current_file = 0;
+
+ if (!JobHistory || !JobFiles)
+ for (i = 1; i <= current->num_files; i ++)
+ {
+ snprintf(filename, sizeof(filename), "%s/d%05d-%03d", RequestRoot,
+ current->id, i);
+ unlink(filename);
+ }
+
+ if (JobHistory)
+ {
+ /*
+ * Save job state info...
+ */
+
+ SaveJob(current->id);
+ }
+ else
+ {
+ /*
+ * Remove the job info file...
+ */
+
+ snprintf(filename, sizeof(filename), "%s/c%05d", RequestRoot,
+ current->id);
+ unlink(filename);
+
+ /*
+ * Update pointers if we aren't preserving jobs...
+ */
+
+ if (prev == NULL)
+ Jobs = current->next;
+ else
+ prev->next = current->next;
+
+ /*
+ * Free all memory used...
+ */
+
+ if (current->attrs != NULL)
+ ippDelete(current->attrs);
+
+ free(current->filetypes);
+
+ free(current);
+ }
+
+ return;
+ }
+}
+
+
+/*
+ * 'CancelJobs()' - Cancel all jobs on the given printer or class.
+ */
+
+void
+CancelJobs(const 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, /* Current job in queue */
+ *prev; /* Previous job in queue */
+ printer_t *printer, /* Printer destination */
+ *pclass; /* Printer class destination */
+
+
+ DEBUG_puts("CheckJobs()");
+
+ for (current = Jobs, prev = NULL; current != NULL; prev = current)
+ {
+ DEBUG_printf(("CheckJobs: current->state->values[0].integer = %d\n",
+ current->state->values[0].integer));
+
+ /*
+ * Start pending jobs if the destination is available...
+ */
+
+ if (current->state->values[0].integer == IPP_JOB_PENDING)
+ {
+ DEBUG_printf(("CheckJobs: current->dest = \'%s\'\n", current->dest));
+
+ if ((pclass = FindClass(current->dest)) != NULL)
+ printer = FindAvailablePrinter(current->dest);
+ else
+ printer = FindPrinter(current->dest);
+
+ if (printer != NULL && (printer->type & CUPS_PRINTER_IMPLICIT))
+ {
+ /*
+ * Handle implicit classes...
+ */
+
+ pclass = printer;
+ printer = FindAvailablePrinter(current->dest);
+ }
+
+ if (printer == NULL && pclass == NULL)
+ {
+ /*
+ * Whoa, the printer and/or class for this destination went away;
+ * cancel the job...
+ */
+
+ LogMessage(L_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 or remote and not printing a job;
+ * if so, start the job...
+ */
+
+ DEBUG_printf(("CheckJobs: printer->state = %d\n", printer->state));
+
+ if (printer->state == IPP_PRINTER_IDLE || /* Printer is idle */
+ ((printer->type & CUPS_PRINTER_REMOTE) && /* Printer is remote */
+ !printer->job)) /* and not printing a job */
+ 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);
+}
+
+
+/*
+ * 'HoldJob()' - Hold the specified job.
+ */
+
+void
+HoldJob(int id) /* I - Job ID */
+{
+ job_t *job; /* Job data */
+
+
+ if ((job = FindJob(id)) == NULL)
+ return;
+
+ if (job->state->values[0].integer == IPP_JOB_PROCESSING)
+ StopJob(id);
+
+ job->state->values[0].integer = IPP_JOB_HELD;
+
+ SaveJob(id);
+
+ CheckJobs();
+}
+
+
+/*
+ * 'LoadAllJobs()' - Load all jobs from disk.
+ */
+
+void
+LoadAllJobs(void)
+{
+ DIR *dir; /* Directory */
+ DIRENT *dent; /* Directory entry */
+ char filename[1024]; /* Full filename of job file */
+ job_t *job, /* New job */
+ *current, /* Current job */
+ *prev; /* Previous job */
+ int jobid, /* Current job ID */
+ fileid; /* Current file ID */
+ ipp_attribute_t *attr; /* Job attribute */
+ 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 */
+ const char *dest; /* Destination */
+ mime_type_t **filetypes; /* New filetypes array */
+
+
+ /*
+ * First open the requests directory...
+ */
+
+ if ((dir = opendir(RequestRoot)) == NULL)
+ return;
+
+ /*
+ * Read all the c##### files...
+ */
+
+ while ((dent = readdir(dir)) != NULL)
+ if (NAMLEN(dent) == 6 && dent->d_name[0] == 'c')
+ {
+ /*
+ * Allocate memory for the job...
+ */
+
+ if ((job = calloc(sizeof(job_t), 1)) == NULL)
+ {
+ LogMessage(L_ERROR, "LoadAddJobs: Ran out of memory for jobs!");
+ closedir(dir);
+ return;
+ }
+
+ if ((job->attrs = ippNew()) == NULL)
+ {
+ free(job);
+ LogMessage(L_ERROR, "LoadAddJobs: Ran out of memory for job attributes!");
+ closedir(dir);
+ return;
+ }
+
+ /*
+ * Assign the job ID...
+ */
+
+ job->id = atoi(dent->d_name + 1);
+
+ if (job->id >= NextJobId)
+ NextJobId = job->id + 1;
+
+ /*
+ * Load the job control file...
+ */
+
+ snprintf(filename, sizeof(filename), "%s/%s", RequestRoot, dent->d_name);
+ if (ipp_read_file(filename, job->attrs) != IPP_DATA)
+ {
+ LogMessage(L_ERROR, "LoadAllJobs: Unable to read job control file \"%s\"!",
+ filename);
+ ippDelete(job->attrs);
+ free(job);
+ continue;
+ }
+
+ if ((attr = ippFindAttribute(job->attrs, "job-printer-uri", IPP_TAG_URI)) == NULL)
+ {
+ LogMessage(L_ERROR, "LoadAllJobs: No job-printer-uri attribute in control file \"%s\"!",
+ filename);
+ ippDelete(job->attrs);
+ free(job);
+ continue;
+ }
+
+ httpSeparate(attr->values[0].string.text, method, username, host,
+ &port, resource);
+
+ if ((dest = ValidateDest(resource, &(job->dtype))) == NULL)
+ {
+ LogMessage(L_ERROR, "LoadAllJobs: Unable to queue job for destination \"%s\"!",
+ attr->values[0].string.text);
+ ippDelete(job->attrs);
+ free(job);
+ continue;
+ }
+
+ strncpy(job->dest, dest, sizeof(job->dest) - 1);
+
+ job->state = ippFindAttribute(job->attrs, "job-state", IPP_TAG_ENUM);
+ job->job_sheets = ippFindAttribute(job->attrs, "job-sheets", IPP_TAG_NAME);
+
+ attr = ippFindAttribute(job->attrs, "job-priority", IPP_TAG_INTEGER);
+ job->priority = attr->values[0].integer;
+
+ attr = ippFindAttribute(job->attrs, "job-name", IPP_TAG_NAME);
+ strncpy(job->title, attr->values[0].string.text,
+ sizeof(job->title) - 1);
+
+ attr = ippFindAttribute(job->attrs, "job-originating-user-name", IPP_TAG_NAME);
+ strncpy(job->username, attr->values[0].string.text,
+ sizeof(job->username) - 1);
+
+ /*
+ * Insert the job into the array, sorting by job priority and ID...
+ */
+
+ for (current = Jobs, prev = NULL;
+ current != NULL;
+ prev = current, current = current->next)
+ if (job->priority > current->priority)
+ break;
+ else if (job->priority == current->priority && job->id < current->id)
+ break;
+
+ job->next = current;
+ if (prev != NULL)
+ prev->next = job;
+ else
+ Jobs = job;
+ }
+
+ /*
+ * Read all the d##### files...
+ */
+
+ rewinddir(dir);
+
+ while ((dent = readdir(dir)) != NULL)
+ if (NAMLEN(dent) > 7 && dent->d_name[0] == 'd')
+ {
+ /*
+ * Find the job...
+ */
+
+ jobid = atoi(dent->d_name + 1);
+ fileid = atoi(dent->d_name + 7);
+
+ snprintf(filename, sizeof(filename), "%s/%s", RequestRoot, dent->d_name);
+
+ if ((job = FindJob(jobid)) == NULL)
+ {
+ LogMessage(L_ERROR, "LoadAddJobs: Orphaned print file \"%s\"!",
+ filename);
+ continue;
+ }
+
+ if (fileid > job->num_files)
+ {
+ if (job->num_files == 0)
+ filetypes = (mime_type_t **)calloc(sizeof(mime_type_t *), fileid);
+ else
+ filetypes = (mime_type_t **)realloc(job->filetypes,
+ sizeof(mime_type_t *) * fileid);
+
+ if (filetypes == NULL)
+ {
+ LogMessage(L_ERROR, "LoadAddJobs: Ran out of memory for job file types!");
+ continue;
+ }
+
+ job->filetypes = filetypes;
+ job->num_files = fileid;
+ }
+
+ job->filetypes[fileid - 1] = mimeFileType(MimeDatabase, filename);
+ }
+
+ /*
+ * Check to see if we need to start any jobs...
+ */
+
+ CheckJobs();
+}
+
+
+/*
+ * 'MoveJob()' - Move the specified job to a different destination.
+ */
+
+void
+MoveJob(int id, /* I - Job ID */
+ const char *dest) /* I - Destination */
+{
+ job_t *current; /* Current job */
+
+
+ for (current = Jobs; current != NULL; current = current->next)
+ if (current->id == id)
+ {
+ if (current->state->values[0].integer == IPP_JOB_PENDING)
+ strncpy(current->dest, dest, sizeof(current->dest) - 1);
+
+ SaveJob(current->id);
+
+ return;
+ }
+}
+
+
+/*
+ * 'ReleaseJob()' - Release the specified job.
+ */
+
+void
+ReleaseJob(int id) /* I - Job ID */
+{
+ job_t *job; /* Job data */
+
+
+ if ((job = FindJob(id)) == NULL)
+ return;
+
+ if (job->state->values[0].integer == IPP_JOB_HELD)
+ {
+ job->state->values[0].integer = IPP_JOB_PENDING;
+ SaveJob(id);
+ CheckJobs();
+ }
+}
+
+
+/*
+ * 'RestartJob()' - Restart the specified job.
+ */
+
+void
+RestartJob(int id) /* I - Job ID */
+{
+ job_t *job; /* Job data */
+
+
+ if ((job = FindJob(id)) == NULL)
+ return;
+
+ if (job->state->values[0].integer > IPP_JOB_PROCESSING && JobFiles)
+ {
+ job->state->values[0].integer = IPP_JOB_PENDING;
+ SaveJob(id);
+ CheckJobs();
+ }
+}
+
+
+/*
+ * 'SaveJob()' - Save a job to disk.
+ */
+
+void
+SaveJob(int id) /* I - Job ID */
+{
+ job_t *job; /* Pointer to job */
+ char filename[1024]; /* Job control filename */
+
+
+ if ((job = FindJob(id)) == NULL)
+ return;
+
+ snprintf(filename, sizeof(filename), "%s/c%05d", RequestRoot, id);
+ ipp_write_file(filename, job->attrs);
+}
+
+
+/*
+ * 'SetJobPriority()' - Set the priority of a job, moving it up/down in the
+ * list as needed.
+ */
+
+void
+SetJobPriority(int id, /* I - Job ID */
+ int priority) /* I - New priority (0 to 100) */
+{
+ job_t *job, /* Job to change */
+ *current, /* Current job */
+ *prev; /* Previous job */
+ ipp_attribute_t *attr; /* Job attribute */
+
+
+ /*
+ * Find the job...
+ */
+
+ for (current = Jobs, prev = NULL;
+ current != NULL;
+ prev = current, current = current->next)
+ if (current->id == id)
+ break;
+
+ if (current == NULL)
+ return;
+
+ /*
+ * Set the new priority...
+ */
+
+ job = current;
+ job->priority = priority;
+
+ if ((attr = ippFindAttribute(job->attrs, "job-priority", IPP_TAG_INTEGER)) != NULL)
+ attr->values[0].integer = priority;
+ else
+ ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-priority",
+ priority);
+
+ SaveJob(job->id);
+
+ /*
+ * See if we need to do any sorting...
+ */
+
+ if ((prev == NULL || job->priority < prev->priority) &&
+ (job->next == NULL || job->next->priority < job->priority))
+ return;
+
+ /*
+ * Remove the job from the list, and then insert it where it belongs...
+ */
+
+ if (prev == NULL)
+ Jobs = job->next;
+ else
+ prev->next = job->next;
+
+ 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;
+}
+
+
+/*
+ * '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 */
+ filename[1024], /* Job filename */
+ 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[15], /* Environment variables */
+ language[255], /* LANG environment variable */
+ charset[255], /* CHARSET environment variable */
+ content_type[255],/* CONTENT_TYPE environment variable */
+ device_uri[1024],/* DEVICE_URI environment variable */
+ ppd[1024], /* PPD environment variable */
+ printer_name[255],/* PRINTER 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->values[0].integer = IPP_JOB_PROCESSING;
+ current->status = 0;
+ current->printer = printer;
+ printer->job = current;
+ SetPrinterState(printer, IPP_PRINTER_PROCESSING);
+
+ set_time(current, "job-at-processing");
+
+ /*
+ * Figure out what filters are required to convert from
+ * the source to the destination type...
+ */
+
+ num_filters = 0;
+
+ 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->filetypes[current->current_file],
+ printer->filetype, &num_filters);
+
+ if (num_filters == 0)
+ {
+ LogMessage(L_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)
+ {
+ /*
+ * Don't use the # copies attribute if we are printing the job sheets...
+ */
+
+ if (current->job_sheets == NULL ||
+ ((strcasecmp(current->job_sheets->values[0].string.text, "none") == 0 ||
+ current->current_file != 0) &&
+ (current->job_sheets->num_values == 1 ||
+ strcasecmp(current->job_sheets->values[1].string.text, "none") == 0 ||
+ current->current_file != (current->num_files - 1))))
+ 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 &&
+ (optptr - options) < (sizeof(options) - 128))
+ {
+ /*
+ * Filter out other unwanted attributes...
+ */
+
+ 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 (strncmp(attr->name, "job-", 4) == 0 ||
+ strncmp(attr->name, "time-", 5) == 0)
+ continue;
+
+ /*
+ * Otherwise add them to the list...
+ */
+
+ 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;
+
+ default :
+ break; /* anti-compiler-warning-code */
+ }
+ }
+
+ 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);
+ sprintf(filename, "%s/d%05d-%03d", RequestRoot, current->id,
+ current->current_file + 1);
+
+ argv[0] = printer->name;
+ argv[1] = jobid;
+ argv[2] = current->username;
+ argv[3] = title;
+ argv[4] = copies;
+ argv[5] = options;
+ argv[6] = 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(content_type, "CONTENT_TYPE=%s/%s",
+ current->filetypes[current->current_file]->super,
+ current->filetypes[current->current_file]->type);
+ sprintf(device_uri, "DEVICE_URI=%s", printer->device_uri);
+ sprintf(ppd, "PPD=%s/ppd/%s.ppd", ServerRoot, printer->name);
+ sprintf(printer_name, "PRINTER=%s", printer->name);
+ sprintf(cache, "RIP_MAX_CACHE=%s", RIPCache);
+ sprintf(root, "SERVER_ROOT=%s", ServerRoot);
+ sprintf(tmpdir, "TMPDIR=%s", TempDir);
+
+ envp[0] = "PATH=/bin:/usr/bin";
+ envp[1] = "SOFTWARE=CUPS/1.1";
+ envp[2] = "TZ=GMT";
+ envp[3] = "USER=root";
+ envp[4] = charset;
+ envp[5] = language;
+ envp[6] = TZ;
+ envp[7] = ppd;
+ envp[8] = root;
+ envp[9] = cache;
+ envp[10] = tmpdir;
+ envp[11] = content_type;
+ envp[12] = device_uri;
+ envp[13] = printer_name;
+ envp[14] = 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]);
+ DEBUG_puts(envp[12]);
+ DEBUG_puts(envp[13]);
+
+ current->current_file ++;
+
+ /*
+ * Now create processes for all of the filters...
+ */
+
+ if (pipe(statusfds))
+ {
+ LogMessage(L_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", ServerBin, 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, 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], 0);
+
+ close(filterfds[!(i & 1)][0]);
+ close(filterfds[!(i & 1)][1]);
+
+ if (pid == 0)
+ {
+ LogMessage(L_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;
+
+ LogMessage(L_INFO, "Started %s (PID %d) for job %d.", command, pid,
+ current->id);
+ }
+ }
+
+ 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, "%254[^:]", method);
+ sprintf(command, "%s/backend/%s", ServerBin, 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], 1);
+
+ close(filterfds[!(i & 1)][0]);
+ close(filterfds[!(i & 1)][1]);
+
+ if (pid == 0)
+ {
+ LogMessage(L_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;
+
+ LogMessage(L_INFO, "Started %s (PID %d) for job %d.", command, pid,
+ current->id);
+ }
+ }
+ 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);
+}
+
+
+/*
+ * 'StopAllJobs()' - Stop all print jobs.
+ */
+
+void
+StopAllJobs(void)
+{
+ job_t *current; /* Current job */
+
+
+ DEBUG_puts("StopAllJobs()");
+
+ for (current = Jobs; current != NULL; current = current->next)
+ if (current->state->values[0].integer == IPP_JOB_PROCESSING)
+ {
+ StopJob(current->id);
+ current->state->values[0].integer = IPP_JOB_PENDING;
+ }
+}
+
+
+/*
+ * 'StopJob()' - Stop a print job.
+ */
+
+void
+StopJob(int id) /* I - Job 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->values[0].integer == IPP_JOB_PROCESSING)
+ {
+ DEBUG_puts("StopJob: job state is \'processing\'.");
+
+ if (current->status < 0)
+ SetPrinterState(current->printer, IPP_PRINTER_STOPPED);
+ else
+ SetPrinterState(current->printer, IPP_PRINTER_IDLE);
+
+ DEBUG_printf(("StopJob: printer state is %d\n", current->printer->state));
+
+ current->state->values[0].integer = IPP_JOB_STOPPED;
+ current->printer->job = NULL;
+ current->printer = NULL;
+
+ current->current_file --;
+
+ for (i = 0; current->procs[i]; i ++)
+ if (current->procs[i] > 0 && current->procs[i + 1])
+ {
+ kill(current->procs[i], SIGTERM);
+ current->procs[i] = 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 = L_ERROR;
+ message = buffer + 6;
+ }
+ else if (strncmp(buffer, "WARNING:", 8) == 0)
+ {
+ loglevel = L_WARN;
+ message = buffer + 8;
+ }
+ else if (strncmp(buffer, "INFO:", 5) == 0)
+ {
+ loglevel = L_INFO;
+ message = buffer + 5;
+ }
+ else if (strncmp(buffer, "DEBUG:", 6) == 0)
+ {
+ loglevel = L_DEBUG;
+ message = buffer + 6;
+ }
+ else if (strncmp(buffer, "PAGE:", 5) == 0)
+ {
+ loglevel = L_PAGE;
+ message = buffer + 5;
+ }
+ else
+ {
+ loglevel = L_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 == L_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 != L_INFO)
+ LogMessage(loglevel, "%s", message);
+
+ if ((loglevel == L_INFO && !job->status) ||
+ loglevel < L_INFO)
+ 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 < 0)
+ {
+ /*
+ * Backend had errors; stop it...
+ */
+
+ StopJob(job->id);
+ job->state->values[0].integer = IPP_JOB_PENDING;
+ }
+ else if (job->status > 0)
+ {
+ /*
+ * Filter had errors; cancel it...
+ */
+
+ if (job->current_file < job->num_files)
+ StartJob(job->id, job->printer);
+ else
+ {
+ CancelJob(job->id);
+
+ if (JobHistory)
+ job->state->values[0].integer = IPP_JOB_ABORTED;
+
+ CheckJobs();
+ }
+ }
+ else
+ {
+ /*
+ * Job printed successfully; cancel it...
+ */
+
+ if (job->current_file < job->num_files)
+ StartJob(job->id, job->printer);
+ else
+ {
+ CancelJob(job->id);
+
+ if (JobHistory)
+ job->state->values[0].integer = IPP_JOB_COMPLETED;
+
+ CheckJobs();
+ }
+ }
+ }
+}
+
+
+/*
+ * 'ValidateDest()' - Validate a printer/class destination.
+ */
+
+const char * /* O - Printer or class name */
+ValidateDest(const 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);
+}
+
+
+/*
+ * 'ipp_read_file()' - Read an IPP request from a file.
+ */
+
+static ipp_state_t /* O - State */
+ipp_read_file(const char *filename, /* I - File to read from */
+ ipp_t *ipp) /* I - Request to read into */
+{
+ int fd; /* File descriptor for file */
+ int n; /* Length of data */
+ unsigned char buffer[8192], /* Data buffer */
+ *bufptr; /* Pointer into buffer */
+ ipp_attribute_t *attr; /* Current attribute */
+ ipp_tag_t tag; /* Current tag */
+
+
+ /*
+ * Open the file if possible...
+ */
+
+ if (filename == NULL || ipp == NULL)
+ return (IPP_ERROR);
+
+ if ((fd = open(filename, O_RDONLY)) == -1)
+ return (IPP_ERROR);
+
+ /*
+ * Read the IPP request...
+ */
+
+ ipp->state = IPP_IDLE;
+
+ switch (ipp->state)
+ {
+ default :
+ break; /* anti-compiler-warning-code */
+
+ case IPP_IDLE :
+ ipp->state ++; /* Avoid common problem... */
+
+ case IPP_HEADER :
+ /*
+ * Get the request header...
+ */
+
+ if ((n = read(fd, buffer, 8)) < 8)
+ {
+ DEBUG_printf(("ipp_read_file: Unable to read header (%d bytes read)!\n", n));
+ close(fd);
+ return (n == 0 ? IPP_IDLE : IPP_ERROR);
+ }
+
+ /*
+ * Verify the major version number...
+ */
+
+ if (buffer[0] != 1)
+ {
+ DEBUG_printf(("ipp_read_file: version number (%d.%d) is bad.\n", buffer[0],
+ buffer[1]));
+ close(fd);
+ 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;
+
+ case IPP_ATTRIBUTE :
+ while (read(fd, buffer, 1) > 0)
+ {
+ /*
+ * Read this attribute...
+ */
+
+ tag = (ipp_tag_t)buffer[0];
+
+ if (tag == IPP_TAG_END)
+ {
+ /*
+ * No more attributes left...
+ */
+
+ DEBUG_puts("ipp_read_file: IPP_TAG_END!");
+
+ ipp->state = IPP_DATA;
+ break;
+ }
+ else if (tag < IPP_TAG_UNSUPPORTED_VALUE)
+ {
+ /*
+ * Group tag... Set the current group and continue...
+ */
+
+ if (ipp->curtag == tag)
+ ippAddSeparator(ipp);
+
+ ipp->curtag = tag;
+ ipp->current = NULL;
+ DEBUG_printf(("ipp_read_file: group tag = %x\n", tag));
+ continue;
+ }
+
+ DEBUG_printf(("ipp_read_file: value tag = %x\n", tag));
+
+ /*
+ * Get the name...
+ */
+
+ if (read(fd, buffer, 2) < 2)
+ {
+ DEBUG_puts("ipp_read_file: unable to read name length!");
+ close(fd);
+ return (IPP_ERROR);
+ }
+
+ n = (buffer[0] << 8) | buffer[1];
+
+ DEBUG_printf(("ipp_read_file: name length = %d\n", n));
+
+ if (n == 0)
+ {
+ /*
+ * More values for current attribute...
+ */
+
+ if (ipp->current == NULL)
+ {
+ close(fd);
+ return (IPP_ERROR);
+ }
+
+ attr = ipp->current;
+
+ if (attr->num_values >= IPP_MAX_VALUES)
+ {
+ close(fd);
+ return (IPP_ERROR);
+ }
+ }
+ else
+ {
+ /*
+ * New attribute; read the name and add it...
+ */
+
+ if (read(fd, buffer, n) < n)
+ {
+ DEBUG_puts("ipp_read_file: unable to read name!");
+ close(fd);
+ return (IPP_ERROR);
+ }
+
+ buffer[n] = '\0';
+ DEBUG_printf(("ipp_read_file: name = \'%s\'\n", buffer));
+
+ attr = ipp->current = _ipp_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 (read(fd, buffer, 2) < 2)
+ {
+ DEBUG_puts("ipp_read_file: unable to read value length!");
+ close(fd);
+ return (IPP_ERROR);
+ }
+
+ n = (buffer[0] << 8) | buffer[1];
+ DEBUG_printf(("ipp_read_file: value length = %d\n", n));
+
+ switch (tag)
+ {
+ case IPP_TAG_INTEGER :
+ case IPP_TAG_ENUM :
+ if (read(fd, buffer, 4) < 4)
+ {
+ close(fd);
+ 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 (read(fd, buffer, 1) < 1)
+ {
+ close(fd);
+ 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 (read(fd, buffer, n) < n)
+ {
+ close(fd);
+ return (IPP_ERROR);
+ }
+
+ buffer[n] = '\0';
+ DEBUG_printf(("ipp_read_file: value = \'%s\'\n", buffer));
+
+ attr->values[attr->num_values].string.text = strdup((char *)buffer);
+ break;
+ case IPP_TAG_DATE :
+ if (read(fd, buffer, 11) < 11)
+ {
+ close(fd);
+ return (IPP_ERROR);
+ }
+
+ memcpy(attr->values[attr->num_values].date, buffer, 11);
+ break;
+ case IPP_TAG_RESOLUTION :
+ if (read(fd, buffer, 9) < 9)
+ {
+ close(fd);
+ 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 (read(fd, buffer, 8) < 8)
+ {
+ close(fd);
+ 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 (read(fd, buffer, n) < n)
+ return (IPP_ERROR);
+
+ bufptr = buffer;
+
+ /*
+ * text-with-language and name-with-language are composite
+ * values:
+ *
+ * charset-length
+ * charset
+ * text-length
+ * text
+ */
+
+ n = (bufptr[0] << 8) | bufptr[1];
+
+ attr->values[attr->num_values].string.charset = calloc(n + 1, 1);
+
+ memcpy(attr->values[attr->num_values].string.charset,
+ bufptr + 2, n);
+
+ bufptr += 2 + n;
+ n = (bufptr[0] << 8) | bufptr[1];
+
+ attr->values[attr->num_values].string.text = calloc(n + 1, 1);
+
+ memcpy(attr->values[attr->num_values].string.text,
+ bufptr + 2, n);
+
+ break;
+
+ default : /* Other unsupported values */
+ attr->values[attr->num_values].unknown.length = n;
+ if (n > 0)
+ {
+ attr->values[attr->num_values].unknown.data = malloc(n);
+ if (read(fd, attr->values[attr->num_values].unknown.data, n) < n)
+ return (IPP_ERROR);
+ }
+ else
+ attr->values[attr->num_values].unknown.data = NULL;
+ break;
+ }
+
+ attr->num_values ++;
+ }
+ break;
+
+ case IPP_DATA :
+ break;
+ }
+
+ /*
+ * Close the file and return...
+ */
+
+ close(fd);
+
+ return (ipp->state);
+}
+
+
+/*
+ * 'ipp_write_file()' - Write an IPP request to a file.
+ */
+
+static ipp_state_t /* O - State */
+ipp_write_file(const char *filename, /* I - File to write to */
+ ipp_t *ipp) /* I - Request to write */
+{
+ int fd; /* File descriptor */
+ 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 */
+
+
+ /*
+ * Open the file if possible...
+ */
+
+ if (filename == NULL || ipp == NULL)
+ return (IPP_ERROR);
+
+ if ((fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0640)) == -1)
+ return (IPP_ERROR);
+
+ fchmod(fd, 0640);
+ fchown(fd, User, Group);
+
+ /*
+ * Write the IPP request...
+ */
+
+ ipp->state = IPP_IDLE;
+
+ switch (ipp->state)
+ {
+ default :
+ break; /* anti-compiler-warning-code */
+
+ case IPP_IDLE :
+ ipp->state ++; /* Avoid common problem... */
+
+ case IPP_HEADER :
+ /*
+ * Send the request header...
+ */
+
+ bufptr = buffer;
+
+ *bufptr++ = ipp->request.any.version[0];
+ *bufptr++ = ipp->request.any.version[1];
+ *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 (write(fd, (char *)buffer, bufptr - buffer) < 0)
+ {
+ DEBUG_puts("ipp_write_file: Could not write IPP header...");
+ close(fd);
+ return (IPP_ERROR);
+ }
+
+ ipp->state = IPP_ATTRIBUTE;
+ ipp->current = ipp->attrs;
+ ipp->curtag = IPP_TAG_ZERO;
+
+ 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(("ipp_write_file: wrote group tag = %x\n", attr->group_tag));
+ *bufptr++ = attr->group_tag;
+ }
+
+ n = strlen(attr->name);
+
+ DEBUG_printf(("ipp_write_file: writing value tag = %x\n", attr->value_tag));
+ DEBUG_printf(("ipp_write_file: 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(("ipp_write_file: writing value tag = %x\n",
+ attr->value_tag));
+ DEBUG_printf(("ipp_write_file: writing name = 0, \'\'\n"));
+
+ *bufptr++ = attr->value_tag;
+ *bufptr++ = 0;
+ *bufptr++ = 0;
+ }
+
+ n = strlen(attr->values[i].string.text);
+
+ DEBUG_printf(("ipp_write_file: writing string = %d, \'%s\'\n", n,
+ attr->values[i].string.text));
+
+ if ((sizeof(buffer) - (bufptr - buffer)) < (n + 2))
+ {
+ if (write(fd, (char *)buffer, bufptr - buffer) < 0)
+ {
+ DEBUG_puts("ipp_write_file: Could not write IPP attribute...");
+ close(fd);
+ return (IPP_ERROR);
+ }
+
+ bufptr = buffer;
+ }
+
+ *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) +
+ strlen(attr->values[i].string.text) +
+ 2;
+
+ if ((sizeof(buffer) - (bufptr - buffer)) < (n + 2))
+ {
+ if (write(fd, (char *)buffer, bufptr - buffer) < 0)
+ {
+ DEBUG_puts("ipp_write_file: Could not write IPP attribute...");
+ return (IPP_ERROR);
+ }
+
+ bufptr = buffer;
+ }
+
+ /* Length of entire value */
+ *bufptr++ = n >> 8;
+ *bufptr++ = n;
+
+ /* Length of charset */
+ n = strlen(attr->values[i].string.charset);
+ *bufptr++ = n >> 8;
+ *bufptr++ = n;
+
+ /* Charset */
+ memcpy(bufptr, attr->values[i].string.charset, n);
+ bufptr += n;
+
+ /* Length of text */
+ n = strlen(attr->values[i].string.text);
+ *bufptr++ = n >> 8;
+ *bufptr++ = n;
+
+ /* Text */
+ memcpy(bufptr, attr->values[i].string.text, n);
+ bufptr += n;
+ }
+ break;
+
+ default :
+ 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 = attr->values[i].unknown.length;
+
+ if ((sizeof(buffer) - (bufptr - buffer)) < (n + 2))
+ {
+ if (write(fd, (char *)buffer, bufptr - buffer) < 0)
+ {
+ DEBUG_puts("ipp_write_file: Could not write IPP attribute...");
+ return (IPP_ERROR);
+ }
+
+ bufptr = buffer;
+ }
+
+ /* Length of unknown value */
+ *bufptr++ = n >> 8;
+ *bufptr++ = n;
+
+ /* Value */
+ if (n > 0)
+ {
+ memcpy(bufptr, attr->values[i].unknown.data, n);
+ bufptr += n;
+ }
+ }
+ break;
+ }
+
+ /*
+ * Write the data out...
+ */
+
+ if (write(fd, (char *)buffer, bufptr - buffer) < 0)
+ {
+ DEBUG_puts("ipp_write_file: Could not write IPP attribute...");
+ close(fd);
+ return (IPP_ERROR);
+ }
+
+ DEBUG_printf(("ipp_write_file: wrote %d bytes\n", bufptr - buffer));
+ }
+
+ if (ipp->current == NULL)
+ {
+ /*
+ * Done with all of the attributes; add the end-of-attributes tag...
+ */
+
+ buffer[0] = IPP_TAG_END;
+ if (write(fd, (char *)buffer, 1) < 0)
+ {
+ DEBUG_puts("ipp_write_file: Could not write IPP end-tag...");
+ close(fd);
+ return (IPP_ERROR);
+ }
+
+ ipp->state = IPP_DATA;
+ }
+ break;
+
+ case IPP_DATA :
+ break;
+ }
+
+ /*
+ * Close the file and return...
+ */
+
+ close(fd);
+
+ return (ipp->state);
+}
+
+
+/*
+ * 'set_time()' - Set one of the "job-time-at-xyz" attributes...
+ */
+
+static void
+set_time(job_t *job, /* I - Job to update */
+ const char *name) /* I - Name of attribute */
+{
+ ipp_attribute_t *attr; /* Time attribute */
+
+
+ if ((attr = ippFindAttribute(job->attrs, name, IPP_TAG_NOVALUE)) != NULL)
+ {
+ attr->value_tag = IPP_TAG_INTEGER;
+ attr->values[0].integer = time(NULL) - StartTime;
+ }
+ else if ((attr = ippFindAttribute(job->attrs, name, IPP_TAG_INTEGER)) != NULL)
+ attr->values[0].integer = time(NULL) - StartTime;
+}
+
+
+/*
+ * 'start_process()' - Start a background process.
+ */
+
+static int /* O - Process ID or 0 */
+start_process(const 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 root) /* I - Run as root? */
+{
+ 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"...
+ */
+
+ if (!root)
+ {
+ setgid(Group);
+ setuid(User);
+ }
+
+ /*
+ * Execute the command; if for some reason this doesn't work,
+ * return the error code...
+ */
+
+ execve(command, argv, envp);
+
+ perror(command);
+
+ 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..7ae3606c2
--- /dev/null
+++ b/scheduler/job.h
@@ -0,0 +1,88 @@
+/*
+ * "$Id$"
+ *
+ * Print job definitions for the Common UNIX Printing System (CUPS) scheduler.
+ *
+ * Copyright 1997-2000 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_attribute_t *state; /* Job state */
+ char username[16]; /* Printing user */
+ char dest[IPP_MAX_NAME]; /* Destination printer or class */
+ cups_ptype_t dtype; /* Destination type (class/remote bits) */
+ char title[IPP_MAX_NAME]; /* Job name/title */
+ ipp_attribute_t *job_sheets; /* Job sheets (NULL if none) */
+ int num_files; /* Number of files in job */
+ int current_file; /* Current file in job */
+ mime_type_t **filetypes; /* File types */
+ 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 JobHistory VALUE(1); /* Preserve job history? */
+VAR int JobFiles VALUE(0); /* Preserve job files? */
+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, const char *dest);
+extern void CancelJob(int id);
+extern void CancelJobs(const char *dest);
+extern void CheckJobs(void);
+extern void DeleteJob(int id);
+extern job_t *FindJob(int id);
+extern void HoldJob(int id);
+extern void LoadAllJobs(void);
+extern void MoveJob(int id, const char *dest);
+extern void ReleaseJob(int id);
+extern void RestartJob(int id);
+extern void SaveJob(int id);
+extern void SetJobPriority(int id, int priority);
+extern void StartJob(int id, printer_t *printer);
+extern void StopAllJobs(void);
+extern void StopJob(int id);
+extern void UpdateJob(job_t *job);
+extern const char *ValidateDest(const char *resource, cups_ptype_t *dtype);
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/scheduler/listen.c b/scheduler/listen.c
new file mode 100644
index 000000000..409c65aae
--- /dev/null
+++ b/scheduler/listen.c
@@ -0,0 +1,142 @@
+/*
+ * "$Id$"
+ *
+ * Server listening routines for the Common UNIX Printing System (CUPS)
+ * scheduler.
+ *
+ * Copyright 1997-2000 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(L_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(L_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(L_ERROR, "StartListening() Unable to bind socket - %s.", strerror(errno));
+ exit(errno);
+ }
+
+ /*
+ * Listen for new clients.
+ */
+
+ if (listen(lis->fd, SOMAXCONN) < 0)
+ {
+ LogMessage(L_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(L_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(L_DEBUG, "StopListening()");
+}
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/scheduler/log.c b/scheduler/log.c
new file mode 100644
index 000000000..d9a1ae02c
--- /dev/null
+++ b/scheduler/log.c
@@ -0,0 +1,436 @@
+/*
+ * "$Id$"
+ *
+ * Log file routines for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-2000 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:
+ *
+ * LogMessage() - Log a message to the error log file.
+ * LogPage() - Log a page to the page log file.
+ * LogRequest() - Log an HTTP request in Common Log Format.
+ * get_datetime() - Returns a pointer to a date/time string.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "cupsd.h"
+#include <stdarg.h>
+
+#ifdef HAVE_VSYSLOG
+# include <syslog.h>
+#endif /* HAVE_VSYSLOG */
+
+
+/*
+ * Local functions...
+ */
+
+static char *get_datetime(time_t t);
+
+
+/*
+ * 'LogMessage()' - Log a message to the error log file.
+ */
+
+int /* O - 1 on success, 0 on error */
+LogMessage(int level, /* I - Log level */
+ const char *message, /* I - printf-style message string */
+ ...) /* I - Additional args as needed */
+{
+ char filename[1024], /* Name of error log file */
+ backname[1024], /* Backup filename */
+ line[1024]; /* Line for output file */
+ va_list ap; /* Argument pointer */
+ static char levels[] = /* Log levels... */
+ {
+ 'N',
+ 'E',
+ 'W',
+ 'I',
+ 'D'
+ };
+#ifdef HAVE_VSYSLOG
+ static int syslevels[] = /* SYSLOG levels... */
+ {
+ LOG_NOTICE,
+ LOG_ERR,
+ LOG_WARNING,
+ LOG_INFO,
+ LOG_DEBUG
+ };
+#endif /* HAVE_VSYSLOG */
+
+
+ /*
+ * See if we want to log this message...
+ */
+
+ if (level > LogLevel)
+ return (1);
+
+#ifdef HAVE_VSYSLOG
+ /*
+ * See if we are logging errors via syslog...
+ */
+
+ if (strcmp(ErrorLog, "syslog") == 0)
+ {
+ va_start(ap, message);
+ vsyslog(syslevels[level], message, ap);
+ va_end(ap);
+
+ return (1);
+ }
+#endif /* HAVE_VSYSLOG */
+
+ /*
+ * Not using syslog; 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)
+ return (0);
+ }
+
+ /*
+ * 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)
+ return (0);
+ }
+
+ /*
+ * Print the log level and date/time...
+ */
+
+ fprintf(ErrorFile, "%c %s ", levels[level], get_datetime(time(NULL)));
+
+ /*
+ * Then the log message...
+ */
+
+ va_start(ap, message);
+ vsnprintf(line, sizeof(line), message, ap);
+ va_end(ap);
+
+ /*
+ * Then a newline...
+ */
+
+ if (line[strlen(line) - 1] != '\n')
+ strcat(line, "\n");
+
+ fputs(line, ErrorFile);
+ fflush(ErrorFile);
+
+ return (1);
+}
+
+
+/*
+ * 'LogPage()' - Log a page to the page log file.
+ */
+
+int /* O - 1 on success, 0 on error */
+LogPage(job_t *job, /* I - Job being printed */
+ const char *page) /* I - Page being printed */
+{
+ char filename[1024], /* Name of error log file */
+ backname[1024]; /* Backup filename */
+
+
+#ifdef HAVE_VSYSLOG
+ /*
+ * See if we are logging pages via syslog...
+ */
+
+ if (strcmp(PageLog, "syslog") == 0)
+ {
+ syslog(LOG_INFO, "PAGE %s %s %d %s", job->printer->name, job->username,
+ job->id, page);
+
+ return (1);
+ }
+#endif /* HAVE_VSYSLOG */
+
+ /*
+ * 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)
+ return (0);
+ }
+
+ /*
+ * 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)
+ return (0);
+ }
+
+ /*
+ * Print a page log entry of the form:
+ *
+ * printer job-id user [DD/MON/YYYY:HH:MM:SS +TTTT] page num-copies
+ */
+
+ fprintf(PageFile, "%s %s %d %s %s\n", job->printer->name, job->username,
+ job->id, get_datetime(time(NULL)), page);
+ fflush(PageFile);
+
+ 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 */
+ static const char *states[] = /* HTTP client states... */
+ {
+ "WAITING",
+ "OPTIONS",
+ "GET",
+ "GET",
+ "HEAD",
+ "POST",
+ "POST",
+ "POST",
+ "PUT",
+ "PUT",
+ "DELETE",
+ "TRACE",
+ "CLOSE",
+ "STATUS"
+ };
+
+
+#ifdef HAVE_VSYSLOG
+ /*
+ * See if we are logging accesses via syslog...
+ */
+
+ if (strcmp(AccessLog, "syslog") == 0)
+ {
+ syslog(LOG_INFO, "REQUEST %s - %s \"%s %s HTTP/%d.%d\" %d %d\n",
+ con->http.hostname, con->username[0] != '\0' ? con->username : "-",
+ states[con->operation], con->uri,
+ con->http.version / 100, con->http.version % 100,
+ code, con->bytes);
+
+ return (1);
+ }
+#endif /* HAVE_VSYSLOG */
+
+ /*
+ * 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)
+ return (0);
+ }
+
+ /*
+ * 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)
+ return (0);
+ }
+
+ /*
+ * Write a log of the request in "common log format"...
+ */
+
+ fprintf(AccessFile, "%s - %s %s \"%s %s HTTP/%d.%d\" %d %d\n",
+ con->http.hostname, con->username[0] != '\0' ? con->username : "-",
+ get_datetime(con->start), states[con->operation], con->uri,
+ con->http.version / 100, con->http.version % 100,
+ code, con->bytes);
+ fflush(AccessFile);
+
+ return (1);
+}
+
+
+/*
+ * 'get_datetime()' - Returns a pointer to a date/time string.
+ */
+
+static char * /* O - Date/time string */
+get_datetime(time_t t) /* I - Time value */
+{
+ struct tm *date; /* Date/time value */
+ static char s[1024]; /* Date/time string */
+ static const char *months[12] =/* Months */
+ {
+ "Jan",
+ "Feb",
+ "Mar",
+ "Apr",
+ "May",
+ "Jun",
+ "Jul",
+ "Aug",
+ "Sep",
+ "Oct",
+ "Nov",
+ "Dec"
+ };
+
+
+ /*
+ * Get the date and time from the UNIX time value, and then format it
+ * into a string. Note that we *can't* use the strftime() function since
+ * it is localized and will seriously confuse automatic programs if the
+ * month names are in the wrong language!
+ *
+ * Also, we use the "timezone" variable that contains the current timezone
+ * offset from GMT in seconds so that we are reporting local time in the
+ * log files. If you want GMT, set the TZ environment variable accordingly
+ * before starting the scheduler.
+ *
+ * (*BSD stores the timezone offset in the tm structure)
+ */
+
+ date = localtime(&t);
+
+ sprintf(s, "[%02d/%s/%04d:%02d:%02d:%02d %+03ld%02ld]",
+ date->tm_mday, months[date->tm_mon], 1900 + date->tm_year,
+ date->tm_hour, date->tm_min, date->tm_sec,
+#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__)
+ -date->tm_gmtoff / 3600, (date->tm_gmtoff / 60) % 60);
+#else
+ -timezone / 3600, (timezone / 60) % 60);
+#endif /* __*BSD__ */
+
+ return (s);
+}
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/scheduler/main.c b/scheduler/main.c
new file mode 100644
index 000000000..b8455559b
--- /dev/null
+++ b/scheduler/main.c
@@ -0,0 +1,604 @@
+/*
+ * "$Id$"
+ *
+ * Scheduler main loop for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-2000 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.
+ * sigterm_handler() - Handle 'terminate' signals that stop the scheduler.
+ * usage() - Show scheduler usage.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#define _MAIN_C_
+#include "cupsd.h"
+#include <sys/resource.h>
+#include <syslog.h>
+
+
+/*
+ * Local functions...
+ */
+
+static void sigchld_handler(int sig);
+static void sighup_handler(int sig);
+static void sigterm_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 */
+ int fg; /* Run in the foreground */
+ 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 */
+#if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
+ struct sigaction action; /* Actions for POSIX signals */
+#endif /* HAVE_SIGACTION && !HAVE_SIGSET */
+
+
+ /*
+ * Check for command-line arguments...
+ */
+
+ fg = 0;
+
+ 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();
+
+ if (argv[i][0] == '/')
+ {
+ /*
+ * Absolute directory...
+ */
+
+ strncpy(ConfigurationFile, argv[i], sizeof(ConfigurationFile) - 1);
+ ConfigurationFile[sizeof(ConfigurationFile) - 1] = '\0';
+ }
+ else
+ {
+ /*
+ * Relative directory...
+ */
+
+ getcwd(ConfigurationFile, sizeof(ConfigurationFile));
+ strncat(ConfigurationFile, "/", sizeof(ConfigurationFile) - 1);
+ strncat(ConfigurationFile, argv[i], sizeof(ConfigurationFile) - 1);
+ ConfigurationFile[sizeof(ConfigurationFile) - 1] = '\0';
+ }
+ break;
+
+ case 'f' : /* Run in foreground... */
+ fg = 1;
+ 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();
+ }
+
+ /*
+ * If the user hasn't specified "-f", run in the background...
+ */
+
+ if (!fg)
+ {
+ if (fork() > 0)
+ return (0);
+
+ /*
+ * Make sure we aren't tying up any filesystems...
+ */
+
+ chdir("/");
+
+#ifndef DEBUG
+ /*
+ * Disable core dumps...
+ */
+
+ getrlimit(RLIMIT_CORE, &limit);
+ limit.rlim_cur = 0;
+ setrlimit(RLIMIT_CORE, &limit);
+
+ /*
+ * Disconnect from the controlling terminal...
+ */
+
+ close(0);
+ close(1);
+ close(2);
+
+ setsid();
+#endif /* DEBUG */
+ }
+
+ /*
+ * Set the timezone info...
+ */
+
+ if (getenv("TZ") != NULL)
+ sprintf(TZ, "TZ=%s", getenv("TZ"));
+
+ tzset();
+
+ /*
+ * 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);
+ sigset(SIGTERM, sigterm_handler);
+#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);
+
+ sigemptyset(&action.sa_mask);
+ sigaddset(&action.sa_mask, SIGTERM);
+ action.sa_handler = sigterm_handler;
+ sigaction(SIGTERM, &action, NULL);
+#else
+ signal(SIGHUP, sighup_handler);
+ signal(SIGCLD, sigchld_handler); /* No, SIGCLD isn't a typo... */
+ signal(SIGPIPE, SIG_IGN);
+ signal(SIGTERM, sigterm_handler);
+#endif /* HAVE_SIGSET */
+
+ /*
+ * Read configuration...
+ */
+
+ InitCerts();
+
+ if (!ReadConfiguration())
+ {
+ syslog(LOG_LPR, "Unable to read configuration file \'%s\' - exiting!",
+ ConfigurationFile);
+ return (1);
+ }
+
+ LoadAllJobs();
+
+ /*
+ * 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())
+ {
+ syslog(LOG_LPR, "Unable to read configuration file \'%s\' - exiting!",
+ ConfigurationFile);
+ break;
+ }
+ }
+
+ /*
+ * 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;
+
+#ifdef DEBUG
+ /*
+ * 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);
+#endif /* DEBUG */
+
+ 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 (Browsing)
+ {
+ if (FD_ISSET(BrowseSocket, &input))
+ UpdateBrowseList();
+
+ SendBrowseList();
+ }
+
+ /*
+ * Update the root certificate once every 5 minutes...
+ */
+
+ if ((time(NULL) - RootCertTime) >= 300)
+ {
+ DeleteCert(0);
+ AddCert(0, "root");
+ }
+ }
+
+ /*
+ * If we get here something very bad happened and we need to exit
+ * immediately.
+ */
+
+ DeleteAllCerts();
+ 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));
+
+ /*
+ * Delete certificates for CGI processes...
+ */
+
+ if (pid)
+ DeleteCert(pid);
+
+ /*
+ * Ignore SIGTERM errors - that comes when a job is cancelled...
+ */
+
+ if (status == SIGTERM)
+ status = 0;
+
+ if (status)
+ {
+ if (status < 256)
+ LogMessage(L_ERROR, "PID %d crashed on signal %d!", pid, status);
+ else
+ LogMessage(L_ERROR, "PID %d crashed with status %d!", pid,
+ status / 256);
+ }
+
+ for (job = Jobs; job != NULL; job = job->next)
+ if (job->state->values[0].integer == 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 && job->status >= 0)
+ {
+ /*
+ * An error occurred; save the exit status so we know to stop
+ * the printer or cancel the job when all of the filters finish...
+ *
+ * A negative status indicates that the backend failed and the
+ * printer needs to be stopped.
+ */
+
+ if (!job->procs[i + 1])
+ job->status = -status; /* Backend failed */
+ else
+ job->status = status; /* Filter failed */
+ }
+ 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;
+
+#ifdef HAVE_SIGSET
+ sigset(SIGHUP, sighup_handler);
+#elif !defined(HAVE_SIGACTION)
+ signal(SIGHUP, sighup_handler);
+#endif /* HAVE_SIGSET */
+}
+
+
+/*
+ * 'sigterm_handler()' - Handle 'terminate' signals that stop the scheduler.
+ */
+
+static void
+sigterm_handler(int sig)
+{
+ /*
+ * Log an error...
+ */
+
+ LogMessage(L_ERROR, "Scheduler shutting down due to SIGTERM.");
+
+ /*
+ * Close all network clients and stop all jobs...
+ */
+
+ CloseAllClients();
+ StopListening();
+ StopPolling();
+ StopBrowsing();
+
+ if (Clients != NULL)
+ free(Clients);
+
+ StopAllJobs();
+
+ if (AccessFile != NULL)
+ fclose(AccessFile);
+
+ if (ErrorFile != NULL)
+ fclose(ErrorFile);
+
+ if (PageFile != NULL)
+ fclose(PageFile);
+
+ DeleteAllLocations();
+
+ DeleteAllClasses();
+
+ if (Devices)
+ ippDelete(Devices);
+
+ if (PPDs)
+ ippDelete(PPDs);
+
+ DeleteAllPrinters();
+
+ if (MimeDatabase != NULL)
+ mimeDelete(MimeDatabase);
+
+ exit(1);
+}
+
+
+/*
+ * 'usage()' - Show scheduler usage.
+ */
+
+static void
+usage(void)
+{
+ fputs("Usage: cupsd [-c config-file] [-f]\n", stderr);
+ exit(1);
+}
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/scheduler/mime.c b/scheduler/mime.c
new file mode 100644
index 000000000..9ae8ec993
--- /dev/null
+++ b/scheduler/mime.c
@@ -0,0 +1,553 @@
+/*
+ * "$Id$"
+ *
+ * MIME database file routines for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-2000 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...
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+#include <cups/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);
+
+ strncpy(filename, pathname, sizeof(filename) - 1);
+ filename[sizeof(filename) - 1] = '\0';
+
+ 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...
+ */
+
+ snprintf(filename, sizeof(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...
+ */
+
+ snprintf(filename, sizeof(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);
+ }
+
+ fclose(fp);
+}
+
+
+/*
+ * '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);
+ }
+
+ fclose(fp);
+}
+
+
+/*
+ * '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/scheduler/mime.h b/scheduler/mime.h
new file mode 100644
index 000000000..14526ee8a
--- /dev/null
+++ b/scheduler/mime.h
@@ -0,0 +1,139 @@
+/*
+ * "$Id$"
+ *
+ * MIME type/conversion database definitions for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-2000 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_MAGIC_CONTAINS /* File contains a 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 */
+ region, /* Region length */
+ 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/scheduler/ppds.c b/scheduler/ppds.c
new file mode 100644
index 000000000..ef63947e8
--- /dev/null
+++ b/scheduler/ppds.c
@@ -0,0 +1,641 @@
+/*
+ * "$Id$"
+ *
+ * PPD scanning routines for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-2000 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:
+ *
+ * LoadPPDs() - Load PPD files from the specified directory...
+ * load_ppds() - Load PPD files recursively.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "cupsd.h"
+#include <ctype.h>
+
+#ifdef HAVE_LIBZ
+# include <zlib.h>
+#endif /* HAVE_LIBZ */
+
+
+/*
+ * PPD information structure...
+ */
+
+typedef struct
+{
+ char ppd_make[128], /* Manufacturer */
+ ppd_make_and_model[256], /* Make and model */
+ ppd_name[256], /* PPD filename */
+ ppd_natural_language[16]; /* Natural language */
+} ppd_info_t;
+
+
+/*
+ * Local globals...
+ */
+
+static int num_ppds, /* Number of PPD files */
+ alloc_ppds; /* Number of allocated entries */
+static ppd_info_t *ppds; /* PPD file info */
+
+
+/*
+ * Local functions...
+ */
+
+static int check_ppds(const char *d, time_t mtime);
+static int compare_ppds(const ppd_info_t *p0, const ppd_info_t *p1);
+static void load_ppds(const char *d, const char *p);
+
+
+/*
+ * 'LoadPPDs()' - Load PPD files from the specified directory...
+ */
+
+void
+LoadPPDs(const char *d) /* I - Directory to scan... */
+{
+ int i; /* Looping var */
+ ppd_info_t *ppd; /* Current PPD file */
+ FILE *fp; /* ppds.dat file */
+ struct stat fileinfo; /* ppds.dat information */
+ char filename[1024]; /* ppds.dat filename */
+
+
+ /*
+ * See if we need to reload the PPD files...
+ */
+
+ snprintf(filename, sizeof(filename), "%s/ppds.dat", ServerRoot);
+ if (stat(filename, &fileinfo))
+ i = 1;
+ else
+ i = check_ppds(d, fileinfo.st_mtime);
+
+ if (i)
+ {
+ /*
+ * Load all PPDs in the specified directory and below...
+ */
+
+ num_ppds = 0;
+ alloc_ppds = 0;
+ ppds = (ppd_info_t *)0;
+
+ load_ppds(d, "");
+
+ /*
+ * Sort the PPDs...
+ */
+
+ if (num_ppds > 1)
+ qsort(ppds, num_ppds, sizeof(ppd_info_t),
+ (int (*)(const void *, const void *))compare_ppds);
+
+ /*
+ * Write the new ppds.dat file...
+ */
+
+ if ((fp = fopen(filename, "wb")) != NULL)
+ {
+ fwrite(ppds, num_ppds, sizeof(ppd_info_t), fp);
+ fclose(fp);
+ LogMessage(L_INFO, "LoadPPDs: Wrote %s...", filename);
+ }
+ else
+ LogMessage(L_ERROR, "LoadPPDs: Unable to write %s...", filename);
+ }
+ else
+ {
+ /*
+ * Load the ppds.dat file instead...
+ */
+
+ num_ppds = fileinfo.st_size / sizeof(ppd_info_t);
+ if ((ppds = malloc(sizeof(ppd_info_t) * num_ppds)) == NULL)
+ {
+ LogMessage(L_ERROR, "LoadPPDs: Unable to allocate memory for %d PPD files!",
+ num_ppds);
+ num_ppds = 0;
+ }
+ else if ((fp = fopen(filename, "rb")) != NULL)
+ {
+ fread(ppds, num_ppds, sizeof(ppd_info_t), fp);
+ fclose(fp);
+ LogMessage(L_INFO, "LoadPPDs: Read %s...", filename);
+ }
+ else
+ LogMessage(L_ERROR, "LoadPPDs: Unable to read %s...", filename);
+ }
+
+ /*
+ * Create the list of PPDs...
+ */
+
+ PPDs = ippNew();
+
+ for (i = num_ppds, ppd = ppds; i > 0; i --, ppd ++)
+ {
+ if (i)
+ ippAddSeparator(PPDs);
+
+ ippAddString(PPDs, IPP_TAG_PRINTER, IPP_TAG_NAME,
+ "ppd-name", NULL, ppd->ppd_name);
+ ippAddString(PPDs, IPP_TAG_PRINTER, IPP_TAG_TEXT,
+ "ppd-make", NULL, ppd->ppd_make);
+ ippAddString(PPDs, IPP_TAG_PRINTER, IPP_TAG_TEXT,
+ "ppd-make-and-model", NULL, ppd->ppd_make_and_model);
+ ippAddString(PPDs, IPP_TAG_PRINTER, IPP_TAG_LANGUAGE,
+ "ppd-natural-language", NULL, ppd->ppd_natural_language);
+ }
+
+ /*
+ * Free the memory used...
+ */
+
+ if (alloc_ppds)
+ free(ppds);
+}
+
+
+/*
+ * 'check_ppds()' - Check to see if we need to regenerate the PPD file
+ * list...
+ */
+
+static int /* O - 1 if reload needed, 0 otherwise */
+check_ppds(const char *d, /* I - Directory to scan */
+ time_t mtime) /* I - Modification time of ppds.dat */
+{
+ DIR *dir; /* Directory pointer */
+ DIRENT *dent; /* Directory entry */
+ struct stat fileinfo; /* File information */
+ char filename[1024]; /* Name of file */
+
+
+ if ((dir = opendir(d)) == NULL)
+ {
+ LogMessage(L_ERROR, "LoadPPDs: Unable to open PPD directory \"%s\": %s",
+ d, strerror(errno));
+ return (1);
+ }
+
+ while ((dent = readdir(dir)) != NULL)
+ {
+ /*
+ * Skip "." and ".."...
+ */
+
+ if (dent->d_name[0] == '.')
+ continue;
+
+ /*
+ * Check the modification time of the file or directory...
+ */
+
+ snprintf(filename, sizeof(filename), "%s/%s", d, dent->d_name);
+
+ if (stat(filename, &fileinfo))
+ continue;
+
+ if (fileinfo.st_mtime >= mtime)
+ {
+ closedir(dir);
+ return (1);
+ }
+
+ if (S_ISDIR(fileinfo.st_mode))
+ {
+ /*
+ * Do subdirectory...
+ */
+
+ if (check_ppds(filename, mtime))
+ {
+ closedir(dir);
+ return (1);
+ }
+ }
+ }
+
+ closedir(dir);
+
+ return (0);
+}
+
+
+/*
+ * 'compare_ppds()' - Compare PPD file make and model names for sorting.
+ */
+
+static int /* O - Result of comparison */
+compare_ppds(const ppd_info_t *p0, /* I - First PPD file */
+ const ppd_info_t *p1) /* I - Second PPD file */
+{
+ const char *s, /* First name */
+ *t; /* Second name */
+ int diff, /* Difference between digits */
+ digits; /* Number of digits */
+
+
+ /*
+ * First compare manufacturers...
+ */
+
+ if ((diff = strcasecmp(p0->ppd_make, p1->ppd_make)) != 0)
+ return (diff);
+
+ /*
+ * Then compare names...
+ */
+
+ s = p0->ppd_make_and_model;
+ t = p1->ppd_make_and_model;
+
+ /*
+ * Loop through both nicknames, 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;
+ s ++;
+ t ++;
+
+ 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 if (diff)
+ 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 (strcasecmp(p0->ppd_natural_language, p1->ppd_natural_language));
+}
+
+
+/*
+ * 'load_ppds()' - Load PPD files recursively.
+ */
+
+static void
+load_ppds(const char *d, /* I - Actual directory */
+ const char *p) /* I - Virtual path in name */
+{
+#ifdef HAVE_LIBZ
+ gzFile fp; /* Pointer to file */
+#else
+ FILE *fp; /* Pointer to file */
+#endif /* HAVE_LIBZ */
+ DIR *dir; /* Directory pointer */
+ DIRENT *dent; /* Directory entry */
+ struct stat fileinfo; /* File information */
+ char filename[1024], /* Name of PPD or directory */
+ line[1024], /* Line from backend */
+ *ptr, /* Pointer into name */
+ name[128], /* Name of PPD file */
+ language[64], /* Device class */
+ manufacturer[1024], /* Manufacturer */
+ make_model[256]; /* Make and model */
+ ppd_info_t *ppd; /* New PPD file */
+
+
+ if ((dir = opendir(d)) == NULL)
+ {
+ LogMessage(L_ERROR, "LoadPPDs: Unable to open PPD directory \"%s\": %s",
+ d, strerror(errno));
+ return;
+ }
+
+ while ((dent = readdir(dir)) != NULL)
+ {
+ /*
+ * Skip "." and ".."...
+ */
+
+ if (dent->d_name[0] == '.')
+ continue;
+
+ /*
+ * See if this is a file...
+ */
+
+ snprintf(filename, sizeof(filename), "%s/%s", d, dent->d_name);
+
+ if (p[0])
+ snprintf(name, sizeof(name), "%s/%s", p, dent->d_name);
+ else
+ strcpy(name, dent->d_name);
+
+ if (stat(filename, &fileinfo))
+ continue;
+
+ if (S_ISDIR(fileinfo.st_mode))
+ {
+ /*
+ * Do subdirectory...
+ */
+
+ load_ppds(filename, name);
+ continue;
+ }
+
+#ifdef HAVE_LIBZ
+ if ((fp = gzopen(filename, "rb")) == NULL)
+#else
+ if ((fp = fopen(filename, "rb")) == NULL)
+#endif /* HAVE_LIBZ */
+ continue;
+
+ /*
+ * Yup, now see if this is a PPD file...
+ */
+
+ line[0] = '\0';
+#ifdef HAVE_LIBZ
+ gzgets(fp, line, sizeof(line));
+#else
+ fgets(line, sizeof(line), fp);
+#endif /* HAVE_LIBZ */
+
+ if (strncmp(line, "*PPD-Adobe:", 11) != 0)
+ {
+ /*
+ * Nope, close the file and continue...
+ */
+
+#ifdef HAVE_LIBZ
+ gzclose(fp);
+#else
+ fclose(fp);
+#endif /* HAVE_LIBZ */
+
+ continue;
+ }
+
+ /*
+ * Now read until we get the NickName field...
+ */
+
+ make_model[0] = '\0';
+ manufacturer[0] = '\0';
+ strcpy(language, "en");
+
+#ifdef HAVE_LIBZ
+ while (gzgets(fp, line, sizeof(line)) != NULL)
+#else
+ while (fgets(line, sizeof(line), fp) != NULL)
+#endif /* HAVE_LIBZ */
+ {
+ if (strncmp(line, "*Manufacturer:", 14) == 0)
+ sscanf(line, "%*[^\"]\"%255[^\"]", manufacturer);
+ else if (strncmp(line, "*ModelName:", 11) == 0)
+ sscanf(line, "%*[^\"]\"%127[^\"]", make_model);
+ else if (strncmp(line, "*LanguageVersion:", 17) == 0)
+ sscanf(line, "%*[^:]:%63s", language);
+ else if (strncmp(line, "*NickName:", 10) == 0)
+ {
+ sscanf(line, "%*[^\"]\"%255[^\"]", make_model);
+ break;
+ }
+ }
+
+ /*
+ * Close the file...
+ */
+
+#ifdef HAVE_LIBZ
+ gzclose(fp);
+#else
+ fclose(fp);
+#endif /* HAVE_LIBZ */
+
+ /*
+ * See if we got all of the required info...
+ */
+
+ while (isspace(make_model[0]))
+ strcpy(make_model, make_model + 1);
+
+ if (!make_model[0])
+ continue; /* Nope... */
+
+ /*
+ * See if we got a manufacturer...
+ */
+
+ while (isspace(manufacturer[0]))
+ strcpy(manufacturer, manufacturer + 1);
+
+ if (!manufacturer[0] || strcmp(manufacturer, "ESP") == 0)
+ {
+ /*
+ * Nope, copy the first part of the make and model then...
+ */
+
+ strncpy(manufacturer, make_model, sizeof(manufacturer) - 1);
+
+ /*
+ * Truncate at the first space, dash, or slash, or make the
+ * manufacturer "Other"...
+ */
+
+ for (ptr = manufacturer; *ptr; ptr ++)
+ if (*ptr == ' ' || *ptr == '-' || *ptr == '/')
+ break;
+
+ if (*ptr && ptr > manufacturer)
+ *ptr = '\0';
+ else if (strncasecmp(manufacturer, "agfa", 4) == 0)
+ strcpy(manufacturer, "AGFA");
+ else if (strncasecmp(manufacturer, "ps-ipu", 6) == 0)
+ strcpy(manufacturer, "Canon");
+ else if (strncasecmp(manufacturer, "herk", 4) == 0)
+ strcpy(manufacturer, "Linotype");
+ else
+ strcpy(manufacturer, "Other");
+
+ /*
+ * Hack for various vendors...
+ */
+
+ if (strcasecmp(manufacturer, "XPrint") == 0)
+ strcpy(manufacturer, "Xerox");
+ else if (strcasecmp(manufacturer, "Eastman") == 0)
+ strcpy(manufacturer, "Kodak");
+ else if (strcasecmp(manufacturer, "laserwriter") == 0)
+ strcpy(manufacturer, "Apple");
+ else if (strcasecmp(manufacturer, "colorpoint") == 0)
+ strcpy(manufacturer, "Seiko");
+ else if (strcasecmp(manufacturer, "fiery") == 0)
+ strcpy(manufacturer, "EFI");
+ else if (strncasecmp(manufacturer, "primera", 7) == 0)
+ strcpy(manufacturer, "Fargo");
+ }
+
+ /*
+ * Fix the language as needed...
+ */
+
+ if (strcasecmp(language, "german") == 0)
+ strcpy(language, "de");
+ else if (strcasecmp(language, "spanish") == 0)
+ strcpy(language, "es");
+ else if (strlen(language) > 2)
+ {
+ /*
+ * en, fr, it, etc.
+ */
+
+ language[0] = tolower(language[0]);
+ language[1] = tolower(language[1]);
+ language[2] = '\0';
+ }
+
+ /*
+ * Add the PPD file...
+ */
+
+ if (num_ppds >= alloc_ppds)
+ {
+ /*
+ * Allocate (more) memory for the PPD files...
+ */
+
+ if (alloc_ppds == 0)
+ ppd = malloc(sizeof(ppd_info_t) * 32);
+ else
+ ppd = realloc(ppds, sizeof(ppd_info_t) * (alloc_ppds + 32));
+
+ if (ppd == NULL)
+ {
+ LogMessage(L_ERROR, "load_ppds: Ran out of memory for %d PPD files!",
+ alloc_ppds + 32);
+ closedir(dir);
+ return;
+ }
+
+ ppds = ppd;
+ alloc_ppds += 32;
+ }
+
+ ppd = ppds + num_ppds;
+ num_ppds ++;
+
+ memset(ppd, 0, sizeof(ppd_info_t));
+ strncpy(ppd->ppd_name, name, sizeof(ppd->ppd_name) - 1);
+ strncpy(ppd->ppd_make, manufacturer, sizeof(ppd->ppd_make) - 1);
+ strncpy(ppd->ppd_make_and_model, make_model,
+ sizeof(ppd->ppd_make_and_model) - 1);
+ strncpy(ppd->ppd_natural_language, language,
+ sizeof(ppd->ppd_natural_language) - 1);
+
+ LogMessage(L_DEBUG, "LoadPPDs: Added ppd \"%s\"...", name);
+ }
+
+ closedir(dir);
+}
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/scheduler/printers.c b/scheduler/printers.c
new file mode 100644
index 000000000..d22f7e25b
--- /dev/null
+++ b/scheduler/printers.c
@@ -0,0 +1,1380 @@
+/*
+ * "$Id$"
+ *
+ * Printer routines for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-2000 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...
+ * write_printcap() - Write a pseudo-printcap file for older applications
+ * that need it...
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "cupsd.h"
+
+
+/*
+ * Local functions...
+ */
+
+static void write_printcap(void);
+
+
+/*
+ * 'AddPrinter()' - Add a printer to the system.
+ */
+
+printer_t * /* O - New printer */
+AddPrinter(const char *name) /* I - Name of printer */
+{
+ printer_t *p, /* New printer */
+ *current, /* Current printer in list */
+ *prev; /* Previous printer in list */
+
+
+ DEBUG_printf(("AddPrinter(\"%s\")\n", name));
+
+ /*
+ * 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);
+ strcpy(p->more_info, p->uri);
+
+ p->state = IPP_PRINTER_STOPPED;
+ p->accepting = 0;
+ p->filetype = mimeAddType(MimeDatabase, "printer", name);
+
+ strcpy(p->job_sheets[0], "none");
+ strcpy(p->job_sheets[1], "none");
+
+ /*
+ * 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;
+
+ /*
+ * Write a new /etc/printcap or /var/spool/lp/pstatus file.
+ */
+
+ write_printcap();
+
+ 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, "%15[^/]/%31s%d%1023s", super, type, &cost, program) != 4)
+ {
+ LogMessage(L_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(L_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 */
+{
+ printer_t *current, /* Current printer in list */
+ *prev; /* Previous printer in list */
+#ifdef __sgi
+ char filename[1024]; /* Interface script filename */
+#endif /* __sgi */
+
+
+ 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)
+ {
+ LogMessage(L_ERROR, "Tried to delete a non-existent printer %s!\n",
+ p->name);
+ 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;
+
+ /*
+ * Write a new /etc/printcap file, and delete the dummy interface and GUI
+ * scripts to fool SGI's stupid printing tools.
+ */
+
+ write_printcap();
+
+#ifdef __sgi
+ sprintf(filename, "/var/spool/lp/interface/%s", p->name);
+ unlink(filename);
+
+ sprintf(filename, "/var/spool/lp/gui_interface/ELF/%s.gui", p->name);
+ unlink(filename);
+
+ sprintf(filename, "/var/spool/lp/activeicons/%s", p->name);
+ unlink(filename);
+#endif /* __sgi */
+}
+
+
+/*
+ * '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(const 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 linenum; /* Current line number */
+ int len; /* Length of line */
+ char line[1024], /* Line from file */
+ name[256], /* Parameter name */
+ *nameptr, /* Pointer into name */
+ *value, /* Pointer to value */
+ *valueptr; /* Pointer into value */
+ printer_t *p; /* Current printer */
+
+
+ /*
+ * Open the printer.conf file...
+ */
+
+ sprintf(line, "%s/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(L_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(L_ERROR, "Syntax error on line %d of printers.conf.",
+ linenum);
+ return;
+ }
+ }
+ else if (p == NULL)
+ {
+ LogMessage(L_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, "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;
+ else if (strcasecmp(value, "stopped") == 0)
+ p->state = IPP_PRINTER_STOPPED;
+ }
+ else if (strcmp(name, "StateMessage") == 0)
+ {
+ /*
+ * Set the initial queue state message...
+ */
+
+ while (isspace(*value))
+ value ++;
+
+ strcpy(p->state_message, value);
+ }
+ else if (strcmp(name, "Accepting") == 0)
+ {
+ /*
+ * Set the initial accepting state...
+ */
+
+ if (strcasecmp(value, "yes") == 0)
+ p->accepting = 1;
+ else
+ p->accepting = 0;
+ }
+ else if (strcmp(name, "JobSheets") == 0)
+ {
+ /*
+ * Set the initial job sheets...
+ */
+
+ for (valueptr = value; *valueptr && !isspace(*valueptr); valueptr ++);
+
+ if (*valueptr)
+ *valueptr++ = '\0';
+
+ strncpy(p->job_sheets[0], value, sizeof(p->job_sheets[0]) - 1);
+
+ while (isspace(*valueptr))
+ valueptr ++;
+
+ if (*valueptr)
+ {
+ for (value = valueptr; *valueptr && !isspace(*valueptr); valueptr ++);
+
+ if (*valueptr)
+ *valueptr++ = '\0';
+
+ strncpy(p->job_sheets[1], value, sizeof(p->job_sheets[1]) - 1);
+ }
+ }
+ else
+ {
+ /*
+ * Something else we don't understand...
+ */
+
+ LogMessage(L_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 */
+ time_t curtime; /* Current time */
+ struct tm *curdate; /* Current date */
+
+
+ /*
+ * Create the printers.conf file...
+ */
+
+ sprintf(temp, "%s/printers.conf", ServerRoot);
+ if ((fp = fopen(temp, "w")) == NULL)
+ {
+ LogMessage(L_ERROR, "Unable to save printers.conf - %s", strerror(errno));
+ return;
+ }
+ else
+ LogMessage(L_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, "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);
+ fprintf(fp, "StateMessage %s\n", printer->state_message);
+ }
+ else
+ fputs("State Idle\n", fp);
+ if (printer->accepting)
+ fputs("Accepting Yes\n", fp);
+ else
+ fputs("Accepting No\n", fp);
+ fprintf(fp, "JobSheets %s %s\n", printer->job_sheets[0],
+ printer->job_sheets[1]);
+
+ 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 */
+ 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 i; /* Looping var */
+ char filename[1024]; /* Name of PPD file */
+ int num_media; /* Number of media options */
+ location_t *auth; /* Pointer to authentication element */
+ int auth_len; /* Length of class or printer resource */
+ const char *auth_supported; /* Authentication supported */
+ 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"
+ };
+ const char *versions[] = /* ipp-versions-supported values */
+ {
+ "1.0",
+ "1.1"
+ };
+ ipp_op_t ops[] = /* operations-supported values */
+ {
+ IPP_PRINT_JOB,
+ IPP_VALIDATE_JOB,
+ IPP_CREATE_JOB,
+ IPP_SEND_DOCUMENT,
+ IPP_CANCEL_JOB,
+ IPP_GET_JOB_ATTRIBUTES,
+ IPP_GET_JOBS,
+ IPP_GET_PRINTER_ATTRIBUTES,
+ IPP_HOLD_JOB,
+ IPP_RELEASE_JOB,
+ IPP_PAUSE_PRINTER,
+ IPP_RESUME_PRINTER,
+ IPP_PURGE_JOBS,
+ IPP_SET_JOB_ATTRIBUTES,
+ 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_GET_DEVICES,
+ CUPS_GET_PPDS,
+ IPP_RESTART_JOB
+ };
+ 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];
+ const char *multiple_document_handling[] =
+ {
+ "single-document",
+ "separate-documents-uncollated-copies",
+ "separate-documents-collated-copies",
+ "single-document-new-sheet"
+ };
+#ifdef __sgi
+ FILE *fp; /* Interface script file */
+#endif /* __sgi */
+
+
+ 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 -");
+
+ /*
+ * Figure out the authentication that is required for the printer.
+ */
+
+ auth_supported = "requesting-user-name";
+ if (!(p->type & CUPS_PRINTER_REMOTE))
+ {
+ if (p->type & CUPS_PRINTER_CLASS)
+ {
+ auth_len = 8;
+ sprintf(resource, "/classes/%s", p->name);
+ }
+ else
+ {
+ auth_len = 9;
+ sprintf(resource, "/printers/%s", p->name);
+ }
+
+ for (i = NumLocations, auth = Locations; i > 0; i --, auth ++)
+ if (strcmp(auth->location, resource) == 0)
+ {
+ /*
+ * Exact match...
+ */
+
+ if (auth->type == AUTH_BASIC)
+ auth_supported = "basic";
+ else if (auth->type == AUTH_DIGEST)
+ auth_supported = "digest";
+ break;
+ }
+ else if (strcmp(auth->location, "/") == 0 ||
+ (strncmp(auth->location, resource, auth_len) == 0 &&
+ (strlen(auth->location) == auth_len ||
+ strlen(auth->location) == (auth_len + 1))))
+ {
+ /*
+ * Matches base printer or class resources...
+ */
+
+ if (auth->type == AUTH_BASIC)
+ auth_supported = "basic";
+ else if (auth->type == AUTH_DIGEST)
+ auth_supported = "digest";
+ }
+ }
+
+ /*
+ * 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-authentication-supported", NULL, auth_supported);
+ 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");
+ ippAddStrings(p->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
+ "ipp-versions-supported", sizeof(versions) / sizeof(versions[0]),
+ NULL, versions);
+ ippAddIntegers(p->attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "operations-supported",
+ sizeof(ops) / sizeof(ops[0]) + JobFiles - 1, (int *)ops);
+ ippAddBoolean(p->attrs, IPP_TAG_PRINTER, "multiple-document-jobs-supported", 1);
+ ippAddStrings(p->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
+ "multiple-document-handling-supported",
+ sizeof(multiple_document_handling) /
+ sizeof(multiple_document_handling[0]), NULL,
+ multiple_document_handling);
+ 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");
+ ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
+ "compression-supported", NULL, "none");
+ 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, 65535);
+ 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 (NumBanners > 0)
+ {
+ /*
+ * Setup the job-sheets-supported and job-sheets-default attributes...
+ */
+
+ attr = ippAddStrings(p->attrs, IPP_TAG_PRINTER, IPP_TAG_NAME,
+ "job-sheets-supported", NumBanners + 1, NULL, NULL);
+ attr->values[0].string.text = strdup("none");
+ for (i = 0; i < NumBanners; i ++)
+ attr->values[i + 1].string.text = strdup(Banners[i].name);
+
+ attr = ippAddStrings(p->attrs, IPP_TAG_PRINTER, IPP_TAG_NAME,
+ "job-sheets-default", 2, NULL, NULL);
+ attr->values[0].string.text = strdup(p->job_sheets[0]);
+ attr->values[1].string.text = strdup(p->job_sheets[1]);
+ }
+
+ if (p->type & CUPS_PRINTER_REMOTE)
+ {
+ /*
+ * Tell the client this is a remote printer of some type...
+ */
+
+ ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT,
+ "printer-make-and-model", NULL, p->make_model);
+ }
+ else
+ {
+ /*
+ * Assign additional attributes depending on whether this is a printer
+ * or class...
+ */
+
+ p->type &= ~CUPS_PRINTER_OPTIONS;
+
+ if (p->type & (CUPS_PRINTER_CLASS | CUPS_PRINTER_IMPLICIT))
+ {
+ /*
+ * Add class-specific attributes...
+ */
+
+ if ((p->type & CUPS_PRINTER_IMPLICIT) && p->num_printers > 0)
+ ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT,
+ "printer-make-and-model", NULL, p->printers[0]->make_model);
+ else
+ ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT,
+ "printer-make-and-model", NULL, "Local Printer Class");
+
+ 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... Start by sanitizing the device
+ * URI so it doesn't have a username or password in it...
+ */
+
+ if (strstr(p->device_uri, "://") != NULL)
+ {
+ /*
+ * http://..., ipp://..., etc.
+ */
+
+ httpSeparate(p->device_uri, method, username, host, &port, resource);
+ if (port)
+ sprintf(uri, "%s://%s:%d%s", method, host, port, resource);
+ else
+ sprintf(uri, "%s://%s%s", method, host, resource);
+ }
+ else
+ {
+ /*
+ * file:..., serial:..., etc.
+ */
+
+ strcpy(uri, p->device_uri);
+ }
+
+ ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_URI, "device-uri", NULL,
+ 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);
+ if (ppd->throughput)
+ ippAddInteger(p->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
+ "pages-per-minute", ppd->throughput);
+ ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT,
+ "printer-make-and-model", NULL, ppd->nickname);
+
+ strncpy(p->make_model, ppd->nickname, sizeof(p->make_model) - 1);
+
+ /*
+ * 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(L_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!
+ */
+
+ ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT,
+ "printer-make-and-model", NULL, "Local System V Printer");
+
+ sprintf(filename, "*/* 0 %s/interfaces/%s", ServerRoot, p->name);
+ AddPrinterFilter(p, filename);
+ }
+ else
+ {
+ /*
+ * Otherwise we have neither - treat this as a "dumb" printer
+ * with no PPD file...
+ */
+
+ ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT,
+ "printer-make-and-model", NULL, "Local Raw Printer");
+
+ AddPrinterFilter(p, "*/* 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));
+
+#ifdef __sgi
+ /*
+ * Add dummy interface and GUI scripts to fool SGI's "challenged" printing
+ * tools.
+ */
+
+ sprintf(filename, "/var/spool/lp/interface/%s", p->name);
+ if ((fp = fopen(filename, "w")) != NULL)
+ {
+ fputs("#!/bin/sh\n", fp);
+
+ if ((attr = ippFindAttribute(p->attrs, "printer-make-and-model",
+ IPP_TAG_TEXT)) != NULL)
+ fprintf(fp, "NAME=\"%s\"\n", attr->values[0].string.text);
+ else if (p->type & CUPS_PRINTER_CLASS)
+ fputs("NAME=\"Printer Class\"\n", fp);
+ else
+ fputs("NAME=\"Remote Destination\"\n", fp);
+
+ if (p->type & CUPS_PRINTER_COLOR)
+ fputs("TYPE=ColorPostScript\n", fp);
+ else
+ fputs("TYPE=PostScript\n", fp);
+
+ fclose(fp);
+ chmod(filename, 0755);
+ }
+
+ sprintf(filename, "/var/spool/lp/member/%s", p->name);
+ if ((fp = fopen(filename, "w")) != NULL)
+ {
+ fputs("/dev/null\n", fp);
+ fclose(fp);
+ chmod(filename, 0644);
+ }
+
+ sprintf(filename, "/var/spool/lp/gui_interface/ELF/%s.gui", p->name);
+ if ((fp = fopen(filename, "w")) != NULL)
+ {
+ fputs("#!/bin/sh\n", fp);
+ fprintf(fp, "/usr/bin/glpoptions -d %s -o \"$3\"\n", p->name);
+ fclose(fp);
+ chmod(filename, 0755);
+ }
+
+ sprintf(filename, "/var/spool/lp/activeicons/%s", p->name);
+ if ((fp = fopen(filename, "w")) != NULL)
+ {
+ fputs("#!/bin/sh\n", fp);
+ if (p->type & CUPS_PRINTER_COLOR)
+ fputs("#Tag 66240\n", fp);
+ else
+ fputs("#Tag 66208\n", fp);
+ fclose(fp);
+ chmod(filename, 0755);
+ }
+#endif /* __sgi */
+}
+
+
+/*
+ * '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 */
+ *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;
+}
+
+
+/*
+ * 'write_printcap()' - Write a pseudo-printcap file for older applications
+ * that need it...
+ */
+
+static void
+write_printcap(void)
+{
+ FILE *fp; /* printcap file */
+ printer_t *p; /* Current printer */
+
+
+ /*
+ * See if we have a printcap file; if not, don't bother writing it.
+ */
+
+ if (!Printcap[0])
+ return;
+
+ /*
+ * Write a new printcap with the current list of printers. Each printer
+ * is put in the file as:
+ *
+ * Printer1:
+ * Printer2:
+ * Printer3:
+ * ...
+ * PrinterN:
+ */
+
+ if ((fp = fopen(Printcap, "w")) == NULL)
+ return;
+
+ for (p = Printers; p != NULL; p = p->next)
+ fprintf(fp, "%s:\n", p->name);
+
+ /*
+ * Close the file...
+ */
+
+ fclose(fp);
+}
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/scheduler/printers.h b/scheduler/printers.h
new file mode 100644
index 000000000..e0062f4e3
--- /dev/null
+++ b/scheduler/printers.h
@@ -0,0 +1,84 @@
+/*
+ * "$Id$"
+ *
+ * Printer definitions for the Common UNIX Printing System (CUPS) scheduler.
+ *
+ * Copyright 1997-2000 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 */
+ make_model[IPP_MAX_NAME],/* Make and model */
+ 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 */
+ char job_sheets[2][IPP_MAX_NAME];
+ /* Banners/job sheets */
+ 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(const char *name);
+extern void DeleteAllPrinters(void);
+extern void DeletePrinter(printer_t *p);
+extern printer_t *FindPrinter(const 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/testmime.c b/scheduler/testmime.c
new file mode 100644
index 000000000..610bb4e64
--- /dev/null
+++ b/scheduler/testmime.c
@@ -0,0 +1,199 @@
+/*
+ * "$Id$"
+ *
+ * MIME test program for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-2000 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], "%15[^/]/%31s", 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/scheduler/testspeed.c b/scheduler/testspeed.c
new file mode 100644
index 000000000..aff28f390
--- /dev/null
+++ b/scheduler/testspeed.c
@@ -0,0 +1,126 @@
+/*
+ * "$Id$"
+ *
+ * Scheduler speed test for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-2000 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/scheduler/type.c b/scheduler/type.c
new file mode 100644
index 000000000..31ce97157
--- /dev/null
+++ b/scheduler/type.c
@@ -0,0 +1,1065 @@
+/*
+ * "$Id$"
+ *
+ * MIME typing routines for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-2000 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 <cups/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;
+ strncpy(temp->super, super, sizeof(temp->super) - 1);
+ strncpy(temp->type, type, sizeof(temp->type) - 1);
+
+ 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 if (strcmp(name, "contains") == 0)
+ op = MIME_MAGIC_CONTAINS;
+ else
+ return (-1);
+ }
+ else
+ {
+ /*
+ * This is just a filename match on the extension...
+ */
+
+ snprintf(value[0], sizeof(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;
+ case MIME_MAGIC_CONTAINS :
+ temp->offset = atoi(value[0]);
+ temp->region = atoi(value[1]);
+ if (length[2] > sizeof(temp->value.stringv))
+ return (-1);
+ temp->length = length[2];
+ memcpy(temp->value.stringv, value[2], length[2]);
+ 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...
+ */
+
+ strncpy(key.super, super, sizeof(key.super) - 1);
+ key.super[sizeof(key.super) - 1] = '\0';
+ strncpy(key.type, type, sizeof(key.type) - 1);
+ key.type[sizeof(key.type) - 1] = '\0';
+
+ 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 region; /* Region to look at */
+ 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;
+ buflength = 0;
+ result = 0;
+
+ 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 8-bit 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 >= 128 ||
+ (*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;
+
+ case MIME_MAGIC_CONTAINS :
+ /*
+ * Load the buffer if necessary...
+ */
+
+ if (bufoffset < 0 || rules->offset < bufoffset ||
+ (rules->offset + rules->region) > (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
+ {
+ if (buflength > rules->region)
+ region = rules->region - rules->length;
+ else
+ region = buflength - rules->length;
+
+ for (n = 0; n < region; n ++)
+ if ((result = (memcmp(buffer + rules->offset - bufoffset + n,
+ rules->value.stringv, rules->length) == 0)) != 0)
+ break;
+ }
+ 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/standards/draft-ietf-ipp-model-v11-04.txt b/standards/draft-ietf-ipp-model-v11-04.txt
new file mode 100644
index 000000000..ee35addd8
--- /dev/null
+++ b/standards/draft-ietf-ipp-model-v11-04.txt
@@ -0,0 +1,11876 @@
+INTERNET-DRAFT
+draft-ietf-ipp-model-v11-04.txt
+ R. deBry
+ Utah Valley State College
+ T. Hastings (editor)
+ Xerox Corporation
+ R. Herriot
+ Xerox Corporation
+ S. Isaacson
+ Novell, Inc.
+ P. Powell
+ Astart Technologies
+ June 23, 1999
+
+ Internet Printing Protocol/1.1: Model and Semantics
+
+ Copyright (C) The Internet Society (1999). All Rights Reserved.
+
+Status of this Memo
+
+This document is an Internet-Draft and is in full conformance with all
+provisions of Section 10 of [RFC2026]. Internet-Drafts are working
+documents of the Internet Engineering Task Force (IETF), its areas, and
+its working groups. Note that other groups may also distribute working
+documents as Internet-Drafts.
+
+Internet-Drafts are draft documents valid for a maximum of six months
+and may be updated, replaced, or obsoleted by other documents at any
+time. It is inappropriate to use Internet-Drafts as reference material
+or to cite them other than as "work in progress".
+
+The list of current Internet-Drafts can be accessed at
+http://www.ietf.org/ietf/1id-abstracts.txt
+
+The list of Internet-Draft Shadow Directories can be accessed as
+http://www.ietf.org/shadow.html.
+
+ Abstract
+
+This document is one of a set of documents, which together describe all
+aspects of a new Internet Printing Protocol (IPP). IPP is an
+application level protocol that can be used for distributed printing
+using Internet tools and technologies. This document describes a
+simplified model consisting of abstract objects, their attributes, and
+their operations that is independent of encoding and transport. The
+model consists of a Printer and a Job object. A Job optionally supports
+multiple documents. IPP 1.1 semantics allow end-users and operators to
+query printer capabilities, submit print jobs, inquire about the status
+of print jobs and printers, cancel, hold, release, and restart print
+jobs. IPP 1.1 semantics allow operators to pause, resume, and purge
+(jobs from) Printer objects. This document also addresses security,
+internationalization, and directory issues.
+The full set of IPP documents includes:
+ Design Goals for an Internet Printing Protocol [RFC2567]
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 1]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+ Rationale for the Structure and Model and Protocol for the Internet
+ Printing Protocol [RFC2568]
+ Internet Printing Protocol/1.1: Model and Semantics (this document)
+ Internet Printing Protocol/1.1: Encoding and Transport [IPP-PRO]
+ Internet Printing Protocol/1.1: Implementer's Guide [IPP-IIG]
+ Mapping between LPD and IPP Protocols [RFC2569]
+
+
+The "Design Goals for an Internet Printing Protocol" document takes a
+broad look at distributed printing functionality, and it enumerates
+real-life scenarios that help to clarify the features that need to be
+included in a printing protocol for the Internet. It identifies
+requirements for three types of users: end users, operators, and
+administrators. It calls out a subset of end user requirements that are
+satisfied in IPP/1.0. A few OPTIONAL operator operations have been
+added to IPP/1.1.
+
+The "Rationale for the Structure and Model and Protocol for the Internet
+Printing Protocol" document describes IPP from a high level view,
+defines a roadmap for the various documents that form the suite of IPP
+specification documents, and gives background and rationale for the IETF
+working group's major decisions.
+
+The "Internet Printing Protocol/1.1: Encoding and Transport" document is
+a formal mapping of the abstract operations and attributes defined in
+the model document onto HTTP/1.1 [RFC2616]. It defines the encoding
+rules for a new Internet MIME media type called "application/ipp". This
+document also defines the rules for transporting over HTTP a message
+body whose Content-Type is "application/ipp". This document defines a
+new scheme named 'ipp' for identifying IPP printers and jobs.
+
+The "Internet Printing Protocol/1.1: Implementer's Guide" document gives
+insight and advice to implementers of IPP clients and IPP objects. It
+is intended to help them understand IPP/1.1 and some of the
+considerations that may assist them in the design of their client and/or
+IPP object implementations. For example, a typical order of processing
+requests is given, including error checking. Motivation for some of the
+specification decisions is also included.
+
+The "Mapping between LPD and IPP Protocols" document gives some advice
+to implementers of gateways between IPP and LPD (Line Printer Daemon)
+implementations.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 2]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+ Table of Contents
+
+
+1. Introduction.....................................................9
+ 1.1 Simplified Printing Model....................................10
+
+2. IPP Objects.....................................................12
+ 2.1 Printer Object...............................................13
+ 2.2 Job Object...................................................15
+ 2.3 Object Relationships.........................................16
+ 2.4 Object Identity..............................................17
+
+3. IPP Operations..................................................20
+ 3.1 Common Semantics.............................................20
+ 3.1.1 Required Parameters......................................21
+ 3.1.2 Operation IDs and Request IDs............................21
+ 3.1.3 Attributes...............................................21
+ 3.1.4 Character Set and Natural Language Operation Attributes..23
+ 3.1.4.1 Request Operation Attributes..........................23
+ 3.1.4.2 Response Operation Attributes.........................27
+ 3.1.5 Operation Targets........................................28
+ 3.1.6 Operation Response Status Codes and Status Messages......29
+ 3.1.6.1 "status-code" (type2 enum)............................29
+ 3.1.6.2 "status-message" (text(255))..........................30
+ 3.1.6.3 "detailed-status-message" (text(MAX)) ...............31
+ 3.1.6.4 "document-access-error" (text(MAX)) .................31
+ 3.1.7 Unsupported Attributes...................................31
+ 3.1.8 Versions.................................................33
+ 3.1.9 Job Creation Operations..................................35
+ 3.2 Printer Operations...........................................37
+ 3.2.1 Print-Job Operation......................................37
+ 3.2.1.1 Print-Job Request.....................................37
+ 3.2.1.2 Print-Job Response....................................41
+ 3.2.2 Print-URI Operation......................................43
+ 3.2.3 Validate-Job Operation...................................44
+ 3.2.4 Create-Job Operation.....................................44
+ 3.2.5 Get-Printer-Attributes Operation.........................45
+ 3.2.5.1 Get-Printer-Attributes Request........................46
+ 3.2.5.2 Get-Printer-Attributes Response.......................47
+ 3.2.6 Get-Jobs Operation.......................................48
+ 3.2.6.1 Get-Jobs Request......................................49
+ 3.2.6.2 Get-Jobs Response.....................................50
+ 3.2.7 Pause-Printer Operation..................................51
+ 3.2.7.1 Pause-Printer Request.................................53
+ 3.2.7.2 Pause-Printer Response................................53
+ 3.2.8 Resume-Printer Operation.................................53
+ 3.2.9 Purge-Jobs Operation.....................................54
+ 3.3 Job Operations...............................................55
+ 3.3.1 Send-Document Operation..................................55
+ 3.3.1.1 Send-Document Request.................................56
+ 3.3.1.2 Send-Document Response................................57
+ 3.3.2 Send-URI Operation.......................................58
+ 3.3.3 Cancel-Job Operation.....................................58
+ 3.3.3.1 Cancel-Job Request....................................59
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 3]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+ 3.3.3.2 Cancel-Job Response...................................60
+ 3.3.4 Get-Job-Attributes Operation.............................60
+ 3.3.4.1 Get-Job-Attributes Request............................61
+ 3.3.4.2 Get-Job-Attributes Response...........................62
+ 3.3.5 Hold-Job Operation.......................................62
+ 3.3.5.1 Hold-Job Request......................................63
+ 3.3.5.2 Hold-Job Response.....................................64
+ 3.3.6 Release-Job Operation....................................64
+ 3.3.7 Restart-Job Operation....................................65
+ 3.3.7.1 Restart-Job Request...................................66
+ 3.3.7.2 Restart-Job Response..................................67
+
+4. Object Attributes...............................................68
+ 4.1 Attribute Syntaxes...........................................68
+ 4.1.1 'text'...................................................69
+ 4.1.1.1 'textWithoutLanguage'.................................70
+ 4.1.1.2 'textWithLanguage'....................................70
+ 4.1.2 'name'...................................................71
+ 4.1.2.1 'nameWithoutLanguage'.................................71
+ 4.1.2.2 'nameWithLanguage'....................................71
+ 4.1.2.3 Matching 'name' attribute values......................72
+ 4.1.3 'keyword'................................................72
+ 4.1.4 'enum'...................................................73
+ 4.1.5 'uri'....................................................73
+ 4.1.6 'uriScheme'..............................................74
+ 4.1.7 'charset'................................................74
+ 4.1.8 'naturalLanguage'........................................75
+ 4.1.9 'mimeMediaType'..........................................75
+ 4.1.9.1 Application/octet-stream -- Auto-Sensing the document
+ format 76
+ 4.1.10 'octetString'............................................77
+ 4.1.11 'boolean'................................................77
+ 4.1.12 'integer'................................................77
+ 4.1.13 'rangeOfInteger'.........................................77
+ 4.1.14 'dateTime'...............................................77
+ 4.1.15 'resolution'.............................................78
+ 4.1.16 '1setOf X'..............................................78
+ 4.2 Job Template Attributes......................................78
+ 4.2.1 job-priority (integer(1:100))............................82
+ 4.2.2 job-hold-until (type3 keyword | name (MAX))..............83
+ 4.2.3 job-sheets (type3 keyword | name(MAX))...................83
+ 4.2.4 multiple-document-handling (type2 keyword)...............84
+ 4.2.5 copies (integer(1:MAX))..................................85
+ 4.2.6 finishings (1setOf type2 enum)...........................85
+ 4.2.7 page-ranges (1setOf rangeOfInteger (1:MAX))..............87
+ 4.2.8 sides (type2 keyword)....................................88
+ 4.2.9 number-up (integer(1:MAX))...............................89
+ 4.2.10 orientation-requested (type2 enum).......................89
+ 4.2.11 media (type3 keyword | name(MAX))........................91
+ 4.2.12 printer-resolution (resolution)..........................91
+ 4.2.13 print-quality (type2 enum)...............................91
+ 4.3 Job Description Attributes...................................92
+ 4.3.1 job-uri (uri)............................................94
+ 4.3.2 job-id (integer(1:MAX))..................................94
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 4]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+ 4.3.3 job-printer-uri (uri)....................................94
+ 4.3.4 job-more-info (uri)......................................95
+ 4.3.5 job-name (name(MAX)).....................................95
+ 4.3.6 job-originating-user-name (name(MAX))....................95
+ 4.3.7 job-state (type1 enum)...................................95
+ 4.3.7.1 Forwarding Servers ..................................98
+ 4.3.7.2 Partitioning of Job States............................98
+ 4.3.8 job-state-reasons (1setOf type2 keyword)................99
+ 4.3.9 job-state-message (text(MAX))...........................103
+ 4.3.10 job-detailed-status-messages (1setOf text(MAX)) ........103
+ 4.3.11 job-document-access-errors (1setOf text(MAX)) ..........103
+ 4.3.12 number-of-documents (integer(0:MAX))....................103
+ 4.3.13 output-device-assigned (name(127))......................104
+ 4.3.14 Event Time Job Description Attributes .................104
+ 4.3.14.1 time-at-creation (integer(MIN:MAX))..................105
+ 4.3.14.2 time-at-processing (integer(MIN:MAX))................105
+ 4.3.14.3 time-at-completed (integer(MIN:MAX)).................105
+ 4.3.14.4 job-printer-up-time (integer(1:MAX)) ................105
+ 4.3.14.5 date-time-at-creation (dateTime) ...................105
+ 4.3.14.6 date-time-at-processing (dateTime) .................105
+ 4.3.14.7 date-time-at-completed (dateTime) ..................105
+ 4.3.15 number-of-intervening-jobs (integer(0:MAX)).............106
+ 4.3.16 job-message-from-operator (text(127))...................106
+ 4.3.17 Job Size Attributes.....................................106
+ 4.3.17.1 job-k-octets (integer(0:MAX))........................106
+ 4.3.17.2 job-impressions (integer(0:MAX)).....................107
+ 4.3.17.3 job-media-sheets (integer(0:MAX))....................107
+ 4.3.18 Job Progress Attributes.................................107
+ 4.3.18.1 job-k-octets-processed (integer(0:MAX))..............107
+ 4.3.18.2 job-impressions-completed (integer(0:MAX))...........108
+ 4.3.18.3 job-media-sheets-completed (integer(0:MAX))..........108
+ 4.3.19 attributes-charset (charset)............................108
+ 4.3.20 attributes-natural-language (naturalLanguage)...........108
+ 4.4 Printer Description Attributes..............................109
+ 4.4.1 printer-uri-supported (1setOf uri)......................111
+ 4.4.2 uri-authentication-supported (1setOf type2 keyword) ...111
+ 4.4.3 uri-security-supported (1setOf type2 keyword)...........112
+ 4.4.4 printer-name (name(127))................................114
+ 4.4.5 printer-location (text(127))............................114
+ 4.4.6 printer-info (text(127))................................114
+ 4.4.7 printer-more-info (uri).................................114
+ 4.4.8 printer-driver-installer (uri)..........................114
+ 4.4.9 printer-make-and-model (text(127))......................114
+ 4.4.10 printer-more-info-manufacturer (uri)....................115
+ 4.4.11 printer-state (type1 enum)..............................115
+ 4.4.12 printer-state-reasons (1setOf type2 keyword)............115
+ 4.4.13 printer-state-message (text(MAX)).......................118
+ 4.4.14 ipp-versions-supported (1setOf type2 keyword) .........118
+ 4.4.15 operations-supported (1setOf type2 enum)................118
+ 4.4.16 multiple-document-jobs-supported (boolean) ............119
+ 4.4.17 charset-configured (charset)............................119
+ 4.4.18 charset-supported (1setOf charset)......................120
+ 4.4.19 natural-language-configured (naturalLanguage)...........120
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 5]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+ 4.4.20 generated-natural-language-supported (1setOf naturalLanguage)
+ 120
+ 4.4.21 document-format-default (mimeMediaType).................121
+ 4.4.22 document-format-supported (1setOf mimeMediaType)........121
+ 4.4.23 printer-is-accepting-jobs (boolean).....................121
+ 4.4.24 queued-job-count (integer(0:MAX)).......................121
+ 4.4.25 printer-message-from-operator (text(127))...............121
+ 4.4.26 color-supported (boolean)...............................122
+ 4.4.27 reference-uri-schemes-supported (1setOf uriScheme)......122
+ 4.4.28 pdl-override-supported (type2 keyword)..................122
+ 4.4.29 printer-up-time (integer(1:MAX))........................122
+ 4.4.30 printer-current-time (dateTime).........................123
+ 4.4.31 multiple-operation-time-out (integer(1:MAX))............123
+ 4.4.32 compression-supported (1setOf type3 keyword)............124
+ 4.4.33 job-k-octets-supported (rangeOfInteger(0:MAX))..........124
+ 4.4.34 job-impressions-supported (rangeOfInteger(0:MAX)).......124
+ 4.4.35 job-media-sheets-supported (rangeOfInteger(0:MAX))......124
+ 4.4.36 pages-per-minute (integer(0:MAX)).......................125
+ 4.4.37 pages-per-minute-color (integer(0:MAX)).................125
+
+5. Conformance....................................................125
+ 5.1 Client Conformance Requirements.............................126
+ 5.2 IPP Object Conformance Requirements.........................127
+ 5.2.1 Objects.................................................127
+ 5.2.2 Operations..............................................127
+ 5.2.3 IPP Object Attributes...................................128
+ 5.2.4 Versions................................................128
+ 5.2.5 Extensions..............................................129
+ 5.2.6 Attribute Syntaxes......................................129
+ 5.2.7 Security ...............................................129
+ 5.3 Charset and Natural Language Requirements...................130
+
+6. IANA Considerations (registered and private extensions)........130
+ 6.1 Typed 'keyword' and 'enum' Extensions.......................131
+ 6.2 Attribute Extensibility.....................................133
+ 6.3 Attribute Syntax Extensibility..............................133
+ 6.4 Operation Extensibility.....................................133
+ 6.5 Attribute Groups............................................134
+ 6.6 Status Code Extensibility...................................134
+ 6.7 Registration of MIME types/sub-types for document-formats...135
+ 6.8 Registration of charsets for use in 'charset' attribute values135
+
+7. Internationalization Considerations............................135
+
+8. Security Considerations........................................139
+ 8.1 Security Scenarios..........................................140
+ 8.1.1 Client and Server in the Same Security Domain...........140
+ 8.1.2 Client and Server in Different Security Domains.........140
+ 8.1.3 Print by Reference......................................140
+ 8.2 URIs in Operation, Job, and Printer attributes..............140
+ 8.3 URIs for each authentication mechanisms.....................141
+ 8.4 Restricted Queries..........................................142
+ 8.5 Operations performed by operators and system administrators.142
+ 8.6 Queries on jobs submitted using non-IPP protocols...........142
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 6]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+9. References.....................................................143
+
+10.Author's Address..............................................146
+
+11.Formats for IPP Registration Proposals........................149
+ 11.1Type2 keyword attribute values registration.................149
+ 11.2Type3 keyword attribute values registration.................149
+ 11.3Type2 enum attribute values registration....................149
+ 11.4Type3 enum attribute values registration....................150
+ 11.5Attribute registration......................................150
+ 11.6Attribute Syntax registration...............................151
+ 11.7Operation registration......................................151
+ 11.8Attribute Group registration................................151
+ 11.9Status code registration....................................152
+
+12.APPENDIX A: Terminology.......................................152
+ 12.1Conformance Terminology.....................................152
+ 12.1.1 NEED NOT................................................152
+ 12.2Model Terminology...........................................152
+ 12.2.1 Keyword.................................................152
+ 12.2.2 Attributes..............................................152
+ 12.2.2.1 Attribute Name.......................................153
+ 12.2.2.2 Attribute Group Name.................................153
+ 12.2.2.3 Attribute Value......................................153
+ 12.2.2.4 Attribute Syntax.....................................153
+ 12.2.3 Supports................................................153
+ 12.2.4 print-stream page.......................................155
+ 12.2.5 impression..............................................155
+
+13.APPENDIX B: Status Codes and Suggested Status Code Messages..155
+ 13.1Status Codes................................................156
+ 13.1.1 Informational...........................................157
+ 13.1.2 Successful Status Codes.................................157
+ 13.1.2.1 successful-ok (0x0000)...............................157
+ 13.1.2.2 successful-ok-ignored-or-substituted-attributes (0x0001)157
+ 13.1.2.3 successful-ok-conflicting-attributes (0x0002)........157
+ 13.1.3 Redirection Status Codes................................158
+ 13.1.4 Client Error Status Codes...............................158
+ 13.1.4.1 client-error-bad-request (0x0400)....................158
+ 13.1.4.2 client-error-forbidden (0x0401)......................158
+ 13.1.4.3 client-error-not-authenticated (0x0402)..............158
+ 13.1.4.4 client-error-not-authorized (0x0403).................158
+ 13.1.4.5 client-error-not-possible (0x0404)...................159
+ 13.1.4.6 client-error-timeout (0x0405)........................159
+ 13.1.4.7 client-error-not-found (0x0406)......................159
+ 13.1.4.8 client-error-gone (0x0407)...........................159
+ 13.1.4.9 client-error-request-entity-too-large (0x0408).......160
+ 13.1.4.10client-error-request-value-too-long (0x0409).........160
+ 13.1.4.11client-error-document-format-not-supported (0x040A)..160
+ 13.1.4.12client-error-attributes-or-values-not-supported (0x040B)160
+ 13.1.4.13client-error-uri-scheme-not-supported (0x040C).......161
+ 13.1.4.14client-error-charset-not-supported (0x040D)..........161
+ 13.1.4.15client-error-conflicting-attributes (0x040E).........161
+ 13.1.4.16client-error-compression-not-supported (0x040F) .....161
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 7]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+ 13.1.4.17client-error-compression-error (0x0410) ............162
+ 13.1.4.18client-error-document-format-error (0x0411) ........162
+ 13.1.4.19client-error-document-access-error (0x0412) ........162
+ 13.1.5 Server Error Status Codes...............................162
+ 13.1.5.1 server-error-internal-error (0x0500).................162
+ 13.1.5.2 server-error-operation-not-supported (0x0501)........163
+ 13.1.5.3 server-error-service-unavailable (0x0502)............163
+ 13.1.5.4 server-error-version-not-supported (0x0503)..........163
+ 13.1.5.5 server-error-device-error (0x0504)...................163
+ 13.1.5.6 server-error-temporary-error (0x0505)................164
+ 13.1.5.7 server-error-not-accepting-jobs (0x0506).............164
+ 13.1.5.8 server-error-busy (0x0507)...........................164
+ 13.1.5.9 server-error-job-canceled (0x0508)...................164
+ 13.1.5.10server-error-multiple-document-jobs-not-supported (0x0509)
+ 164
+ 13.2Status Codes for IPP Operations.............................165
+
+14.APPENDIX C: "media" keyword values...........................167
+
+15.APPENDIX D: Processing IPP Attributes.........................171
+ 15.1Fidelity....................................................171
+ 15.2Page Description Language (PDL) Override....................172
+ 15.3Using Job Template Attributes During Document Processing....174
+
+16.APPENDIX E: Generic Directory Schema..........................175
+
+17.APPENDIX F: Differences between the IPP/1.0 and IPP/1.1 "Model and
+Semantics" Documents..............................................178
+
+18.Full Copyright Statement......................................184
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 8]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+1. Introduction
+
+
+The Internet Printing Protocol (IPP) is an application level protocol
+that can be used for distributed printing using Internet tools and
+technologies. IPP version 1.1 (IPP/1.1) focuses only on end user
+functionality. This document is just one of a suite of documents that
+fully define IPP. The full set of IPP documents includes:
+
+ Design Goals for an Internet Printing Protocol [RFC2567]
+ Rationale for the Structure and Model and Protocol for the Internet
+ Printing Protocol [RFC2568]
+ Internet Printing Protocol/1.1: Model and Semantics (this document)
+ Internet Printing Protocol/1.1: Encoding and Transport [IPP-PRO]
+ Internet Printing Protocol/1.1: Implementer's Guide [IPP-IIG]
+ Mapping between LPD and IPP Protocols [RFC2569]
+
+
+Anyone reading these documents for the first time is strongly encouraged
+to read the IPP documents in the above order.
+
+This document is laid out as follows:
+
+ - The rest of Section 1 is an introduction to the IPP simplified
+ model for distributed printing.
+ - Section 2 introduces the object types covered in the model with
+ their basic behaviors, attributes, and interactions.
+ - Section 3 defines the operations included in IPP/1.1. IPP
+ operations are synchronous, therefore, for each operation, there is
+ a both request and a response.
+ - Section 4 defines the attributes (and their syntaxes) that are used
+ in the model.
+ - Sections 5 - 6 summarizes the implementation conformance
+ requirements for objects that support the protocol and IANA
+ considerations, respectively.
+ - Sections 7 - 11 cover the Internationalization and Security
+ considerations as well as References, Author contact information,
+ and Formats for Registration Proposals.
+ - Sections 12 - 14 are appendices that cover Terminology, Status
+ Codes and Messages, and "media" keyword values.
+
+ Note: This document uses terms such as "attributes",
+ "keywords", and "support". These terms have special meaning
+ and are defined in the model terminology section 12.2.
+ Capitalized terms, such as MUST, MUST NOT, REQUIRED, SHOULD,
+ SHOULD NOT, MAY, NEED NOT, and OPTIONAL, have special meaning
+ relating to conformance. These terms are defined in section
+ 12.1 on conformance terminology, most of which is taken from
+ RFC 2119 [RFC2119].
+
+ - Section 15 is an appendix that helps to clarify the effects of
+ interactions between related attributes and their values.
+ - Section 16 is an appendix that enumerates the subset of Printer
+ attributes that form a generic directory schema. These attributes
+ are useful when registering a Printer so that a client can find the
+ Printer not just by name, but by filtered searches as well.
+
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 9]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+ - Section 17 is an appendix summarizing the additions and changes
+ from the IPP/1.0 "Model and Semantics" document [RFC2566] to make
+ this IPP/1.1 document.
+ - Section 18 is the full copyright notice.
+
+1.1 Simplified Printing Model
+
+
+In order to achieve its goal of realizing a workable printing protocol
+for the Internet, the Internet Printing Protocol (IPP) is based on a
+simplified printing model that abstracts the many components of real
+world printing solutions. The Internet is a distributed computing
+environment where requesters of print services (clients, applications,
+printer drivers, etc.) cooperate and interact with print service
+providers. This model and semantics document describes a simple,
+abstract model for IPP even though the underlying configurations may be
+complex "n-tier" client/server systems. An important simplifying step
+in the IPP model is to expose only the key objects and interfaces
+required for printing. The model described in this model document does
+not include features, interfaces, and relationships that are beyond the
+scope of the first version of IPP (IPP/1.1). IPP/1.1 incorporates many
+of the relevant ideas and lessons learned from other specification and
+development efforts [HTPP] [ISO10175] [LDPA] [P1387.4] [PSIS] [RFC1179]
+[SWP]. IPP is heavily influenced by the printing model introduced in
+the Document Printing Application (DPA) [ISO10175] standard. Although
+DPA specifies both end user and administrative features, IPP version 1.1
+(IPP/1.1) focuses primarily on end user functionality with a few
+additional OPTIONAL operator operations.
+
+The IPP/1.1 model encapsulates the important components of distributed
+printing into two object types:
+
+ - Printer (Section 2.1)
+ - Job (Section 2.2)
+
+
+Each object type has an associated set of operations (see section 3) and
+attributes (see section 4).
+
+It is important, however, to understand that in real system
+implementations (which lie underneath the abstracted IPP/1.1 model),
+there are other components of a print service which are not explicitly
+defined in the IPP/1.1 model. The following figure illustrates where
+IPP/1.1 fits with respect to these other components.
+
+
+
+
+
+
+
+
+
+
+
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 10]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+
+ +--------------+
+ | Application |
+ o +. . . . . . . |
+ \|/ | Spooler |
+ / \ +. . . . . . . | +---------+
+ End-User | Print Driver |---| File |
+ +-----------+ +-----+ +------+-------+ +----+----+
+ | Browser | | GUI | | |
+ +-----+-----+ +--+--+ | |
+ | | | |
+ | +---+------------+---+ |
+N D S | | IPP Client |------------+
+O I E | +---------+----------+
+T R C | |
+I E U |
+F C R -------------- Transport ------------------
+I T I
+C O T | --+
+A R Y +--------+--------+ |
+T Y | IPP Server | |
+I +--------+--------+ |
+O | |
+N +-----------------+ | IPP Printer
+ | Print Service | |
+ +-----------------+ |
+ | --+
+ +-----------------+
+ | Output Device(s)|
+ +-----------------+
+
+
+An IPP Printer object encapsulates the functions normally associated
+with physical output devices along with the spooling, scheduling and
+multiple device management functions often associated with a print
+server. Printer objects are optionally registered as entries in a
+directory where end users find and select them based on some sort of
+filtered and context based searching mechanism (see section 16). The
+directory is used to store relatively static information about the
+Printer, allowing end users to search for and find Printers that match
+their search criteria, for example: name, context, printer capabilities,
+etc. The more dynamic information, such as state, currently loaded and
+ready media, number of jobs at the Printer, errors, warnings, and so
+forth, is directly associated with the Printer object itself rather than
+with the entry in the directory which only represents the Printer
+object.
+
+IPP clients implement the IPP protocol on the client side and give end
+users (or programs running on behalf of end users) the ability to query
+Printer objects and submit and manage print jobs. An IPP server is just
+that part of the Printer object that implements the server-side
+protocol. The rest of the Printer object implements (or gateways into)
+the application semantics of the print service itself. The Printer
+
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 11]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+objects may be embedded in an output device or may be implemented on a
+host on the network that communicates with an output device.
+
+When a job is submitted to the Printer object and the Printer object
+validates the attributes in the submission request, the Printer object
+creates a new Job object. The end user then interacts with this new Job
+object to query its status and monitor the progress of the job. An end
+user can also cancel their print jobs by using the Job object's Cancel-
+Job operation. An end-user can also hold, release, and restart their
+print jobs using the Job object's OPTIONAL Hold-Job, Release-Job, and
+Restart-Job operations, if implemented.
+
+A privileged operator or administrator of a Printer object can cancel,
+hold, release, and restart any user's job using the REQUIRED Cancel-Job
+and the OPTIONAL Hold-Job, Release-Job, and Restart-Job operations. In
+additional privileged operator or administrator of a Printer object can
+pause, resume, or purge (jobs from) a Printer object using the OPTIONAL
+Pause-Printer, Resume-Printer, and Purge-Jobs operations, if
+implemented.
+
+The notification service is out of scope for this IPP/1.1 document, but
+using such a notification service, the end user is able to register for
+and receive Printer specific and Job specific events. An end user can
+query the status of Printer objects and can follow the progress of Job
+objects by polling using the Get-Printer-Attributes, Get-Jobs, and Get-
+Job-Attributes operations.
+
+
+
+2. IPP Objects
+
+
+The IPP/1.1 model introduces objects of type Printer and Job. Each type
+of object models relevant aspects of a real-world entity such as a real
+printer or real print job. Each object type is defined as a set of
+possible attributes that may be supported by instances of that object
+type. For each object (instance), the actual set of supported
+attributes and values describe a specific implementation. The object's
+attributes and values describe its state, capabilities, realizable
+features, job processing functions, and default behaviors and
+characteristics. For example, the Printer object type is defined as a
+set of attributes that each Printer object potentially supports. In the
+same manner, the Job object type is defined as a set of attributes that
+are potentially supported by each Job object.
+
+Each attribute included in the set of attributes defining an object type
+is labeled as:
+
+ - "REQUIRED": each object MUST support the attribute.
+ - "RECOMMENDED": each object SHOULD support the attribute.
+ - "OPTIONAL": each object MAY support the attribute.
+
+
+Some definitions of attribute values indicate that an object MUST or
+SHOULD support the value; otherwise, support of the value is OPTIONAL.
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 12]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+However, if an implementation supports an attribute, it MUST support at
+least one of the possible values for that attribute.
+
+
+2.1 Printer Object
+
+
+The major component of the IPP/1.1 model is the Printer object. A
+Printer object implements the server-side of the IPP/1.1 protocol.
+Using the protocol, end users may query the attributes of the Printer
+object and submit print jobs to the Printer object. The actual
+implementation components behind the Printer abstraction may take on
+different forms and different configurations. However, the model
+abstraction allows the details of the configuration of real components
+to remain opaque to the end user. Section 3 describes each of the
+Printer operations in detail.
+
+The capabilities and state of a Printer object are described by its
+attributes. Printer attributes are divided into two groups:
+
+ - "job-template" attributes: These attributes describe supported job
+ processing capabilities and defaults for the Printer object. (See
+ section 4.2)
+ - "printer-description" attributes: These attributes describe the
+ Printer object's identification, state, location, references to
+ other sources of information about the Printer object, etc. (see
+ section 4.4)
+
+
+Since a Printer object is an abstraction of a generic document output
+device and print service provider, a Printer object could be used to
+represent any real or virtual device with semantics consistent with the
+Printer object, such as a fax device, an imager, or even a CD writer.
+
+Some examples of configurations supporting a Printer object include:
+
+ 1) An output device with no spooling capabilities
+ 2) An output device with a built-in spooler
+ 3) A print server supporting IPP with one or more associated output
+ devices
+ 3a) The associated output devices may or may not be capable of
+ spooling jobs
+ 3b) The associated output devices may or may not support IPP
+
+
+The following figures show some examples of how Printer objects can be
+realized on top of various distributed printing configurations. The
+embedded case below represents configurations 1 and 2. The hosted and
+fan-out figures below represent configurations 3a and 3b.
+
+In this document the term "client" refers to a software entity that
+sends IPP operation requests to an IPP Printer object and accepts IPP
+operation responses. A client MAY be:
+
+ 1. contained within software controlled by an end user, e.g. activated
+ by the "Print" menu item in an application or
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 13]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+ 2. the print server component that sends IPP requests to either an
+ output device or another "downstream" print server.
+
+The term "IPP Printer" is a network entity that accepts IPP operation
+requests and returns IPP operation responses. As such, an IPP object
+MAY be:
+
+ 1. an (embedded) device component that accepts IPP requests and
+ controls the device or
+
+ 2. a component of a print server that accepts IPP requests (where the
+ print server controls one or more networked devices using IPP or
+ other protocols).
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 14]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+Legend:
+
+##### indicates a Printer object which is
+ either embedded in an output device or is
+ hosted in a server. The Printer object
+ might or might not be capable of queuing/spooling.
+
+any indicates any network protocol or direct
+ connect, including IPP
+
+
+embedded printer:
+ output device
+ +---------------+
+ O +--------+ | ########### |
+/|\ | client |------------IPP------------># Printer # |
+/ \ +--------+ | # Object # |
+ | ########### |
+ +---------------+
+
+
+hosted printer:
+ +---------------+
+ O +--------+ ########### | |
+/|\ | client |--IPP--># Printer #-any->| output device |
+/ \ +--------+ # Object # | |
+ ########### +---------------+
+
+
+
+ +---------------+
+fan out: | |
+ +-->| output device |
+ any/ | |
+ O +--------+ ########### / +---------------+
+/|\ | client |-IPP-># Printer #--*
+/ \ +--------+ # Object # \ +---------------+
+ ########### any\ | |
+ +-->| output device |
+ | |
+ +---------------+
+
+
+
+2.2 Job Object
+
+
+A Job object is used to model a print job. A Job object contains
+documents. The information required to create a Job object is sent in a
+create request from the end user via an IPP Client to the Printer
+object. The Printer object validates the create request, and if the
+Printer object accepts the request, the Printer object creates the new
+Job object. Section 3 describes each of the Job operations in detail.
+
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 15]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+The characteristics and state of a Job object are described by its
+attributes. Job attributes are grouped into two groups as follows:
+
+ - "job-template" attributes: These attributes can be supplied by the
+ client or end user and include job processing instructions which
+ are intended to override any Printer object defaults and/or
+ instructions embedded within the document data. (See section 4.2)
+ - "job-description" attributes: These attributes describe the Job
+ object's identification, state, size, etc. The client supplies some
+ of these attributes, and the Printer object generates others. (See
+ section 4.3)
+
+
+An implementation MUST support at least one document per Job object. An
+implementation MAY support multiple documents per Job object. A
+document is either:
+
+ - a stream of document data in a format supported by the Printer
+ object (typically a Page Description Language - PDL), or
+ - a reference to such a stream of document data
+
+
+In IPP/1.1, a document is not modeled as an IPP object, therefore it has
+no object identifier or associated attributes. All job processing
+instructions are modeled as Job object attributes. These attributes are
+called Job Template attributes and they apply equally to all documents
+within a Job object.
+
+
+2.3 Object Relationships
+
+
+IPP objects have relationships that are maintained persistently along
+with the persistent storage of the object attributes.
+
+A Printer object can represent either one or more physical output
+devices or a logical device which "processes" jobs but never actually
+uses a physical output device to put marks on paper. Examples of
+logical devices include a Web page publisher or a gateway into an online
+document archive or repository. A Printer object contains zero or more
+Job objects.
+
+A Job object is contained by exactly one Printer object, however the
+identical document data associated with a Job object could be sent to
+either the same or a different Printer object. In this case, a second
+Job object would be created which would be almost identical to the first
+Job object, however it would have new (different) Job object identifiers
+(see section 2.4).
+
+A Job object is either empty (before any documents have been added) or
+contains one or more documents. If the contained document is a stream
+of document data, that stream can be contained in only one document.
+However, there can be identical copies of the stream in other documents
+in the same or different Job objects. If the contained document is just
+a reference to a stream of document data, other documents (in the same
+or different Job object(s)) may contain the same reference.
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 16]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+2.4 Object Identity
+
+
+All Printer and Job objects are identified by a Uniform Resource
+Identifier (URI) [RFC2396] so that they can be persistently and
+unambiguously referenced. The notion of a URI is a useful concept,
+however, until the notion of URI is more stable (i.e., defined more
+completely and deployed more widely), it is expected that the URIs used
+for IPP objects will actually be URLs [RFC2396]. Since every URL is a
+specialized form of a URI, even though the more generic term URI is used
+throughout the rest of this document, its usage is intended to cover the
+more specific notion of URL as well.
+
+An administrator configures Printer objects to either support or not
+support authentication and/or message privacy using Transport Layer
+Security (TLS) [RFC2246] (the mechanism for security configuration is
+outside the scope of this IPP/1.1 document). In some situations, both
+types of connections (both authenticated and unauthenticated) can be
+established using a single communication channel that has some sort of
+negotiation mechanism. In other situations, multiple communication
+channels are used, one for each type of security configuration. Section
+8 provides a full description of all security considerations and
+configurations.
+
+If a Printer object supports more than one communication channel, some
+or all of those channels might support and/or require different security
+mechanisms. In such cases, an administrator could expose the
+simultaneous support for these multiple communication channels as
+multiple URIs for a single Printer object where each URI represents one
+of the communication channels to the Printer object. To support this
+flexibility, the IPP Printer object type defines a multi-valued
+identification attribute called the "printer-uri-supported" attribute.
+It MUST contain at least one URI. It MAY contain more than one URI.
+That is, every Printer object will have at least one URI that identifies
+at least one communication channel to the Printer object, but it may
+have more than one URI where each URI identifies a different
+communication channel to the Printer object. The "printer-uri-
+supported" attribute has two companion attributes, the "uri-security-
+supported" attribute and the "uri-authentication-supported". Both have
+the same cardinality as "printer-uri-supported". The purpose of the
+"uri-security-supported" attribute is to indicate the security
+mechanisms (if any) used for each URI listed in "printer-uri-supported".
+The purpose of the "uri-authentication-supported" attribute is to
+indicate the authentication mechanisms (if any) used for each URI listed
+in "printer-uri-supported". These three attributes are fully described
+in sections 4.4.1, 4.4.2, and 4.4.3.
+
+When a job is submitted to the Printer object via a create request, the
+client supplies only a single Printer object URI. The client supplied
+Printer object URI MUST be one of the values in the "printer-uri-
+supported" Printer attribute.
+
+IPP/1.1 does not specify how the client obtains the client supplied URI,
+but it is RECOMMENDED that a Printer object be registered as an entry in
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 17]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+a directory service. End-users and programs can then interrogate the
+directory searching for Printers. Section 16 defines a generic schema
+for Printer object entries in the directory service and describes how
+the entry acts as a bridge to the actual IPP Printer object. The entry
+in the directory that represents the IPP Printer object includes the
+possibly many URIs for that Printer object as values in one its
+attributes.
+
+When a client submits a create request to the Printer object, the
+Printer object validates the request and creates a new Job object. The
+Printer object assigns the new Job object a URI which is stored in the
+"job-uri" Job attribute. This URI is then used by clients as the target
+for subsequent Job operations. The Printer object generates a Job URI
+based on its configured security policy and the URI used by the client
+in the create request.
+
+For example, consider a Printer object that supports both a
+communication channel secured by the use of SSL3 (using HTTP over SSL3
+with an "https" schemed URI) and another open communication channel that
+is not secured with SSL3 (using a simple "http" schemed URI). If a
+client were to submit a job using the secure URI, the Printer object
+would assign the new Job object a secure URI as well. If a client were
+to submit a job using the open-channel URI, the Printer would assign the
+new Job object an open-channel URI.
+
+In addition, the Printer object also populates the Job object's "job-
+printer-uri" attribute. This is a reference back to the Printer object
+that created the Job object. If a client only has access to a Job
+object's "job-uri" identifier, the client can query the Job's "job-
+printer-uri" attribute in order to determine which Printer object
+created the Job object. If the Printer object supports more than one
+URI, the Printer object picks the one URI supplied by the client when
+creating the job to build the value for and to populate the Job's "job-
+printer-uri" attribute.
+
+Allowing Job objects to have URIs allows for flexibility and
+scalability. For example, in some implementations, the Printer object
+might create Jobs that are processed in the same local environment as
+the Printer object itself. In this case, the Job URI might just be a
+composition of the Printer's URI and some unique component for the Job
+object, such as the unique 32-bit positive integer mentioned later in
+this paragraph. In other implementations, the Printer object might be a
+central clearing-house for validating all Job object creation requests,
+but the Job object itself might be created in some environment that is
+remote from the Printer object. In this case, the Job object's URI may
+have no physical-location relationship at all to the Printer object's
+URI. Again, the fact that Job objects have URIs allows for flexibility
+and scalability, however, many existing printing systems have local
+models or interface constraints that force print jobs to be identified
+using only a 32-bit positive integer rather than an independent URI.
+This numeric Job ID is only unique within the context of the Printer
+object to which the create request was originally submitted. Therefore,
+in order to allow both types of client access to IPP Job objects (either
+by Job URI or by numeric Job ID), when the Printer object successfully
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 18]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+processes a create request and creates a new Job object, the Printer
+object MUST generate both a Job URI and a Job ID. The Job ID (stored in
+the "job-id" attribute) only has meaning in the context of the Printer
+object to which the create request was originally submitted. This
+requirement to support both Job URIs and Job IDs allows all types of
+clients to access Printer objects and Job objects no matter the local
+constraints imposed on the client implementation.
+
+In addition to identifiers, Printer objects and Job objects have names
+("printer-name" and "job-name"). An object name NEED NOT be unique
+across all instances of all objects. A Printer object's name is chosen
+and set by an administrator through some mechanism outside the scope of
+this IPP/1.1 document. A Job object's name is optionally chosen and
+supplied by the IPP client submitting the job. If the client does not
+supply a Job object name, the Printer object generates a name for the
+new Job object. In all cases, the name only has local meaning.
+
+To summarize:
+
+ - Each Printer object is identified with one or more URIs. The
+ Printer's "printer-uri-supported" attribute contains the URI(s).
+ - The Printer object's "uri-security-supported" attribute identifies
+ the communication channel security protocols that may or may not
+ have been configured for the various Printer object URIs (e.g.,
+ 'tls' or 'none').
+ - The Printer object's "uri-authentication-supported" attribute
+ identifies the authentication mechanisms that may or may not have
+ been configured for the various Printer object URIs (e.g., 'digest'
+ or 'none').
+ - Each Job object is identified with a Job URI. The Job's "job-uri"
+ attribute contains the URI.
+ - Each Job object is also identified with Job ID which is a 32-bit,
+ positive integer. The Job's "job-id" attribute contains the Job
+ ID. The Job ID is only unique within the context of the Printer
+ object which created the Job object.
+ - Each Job object has a "job-printer-uri" attribute which contains
+ the URI of the Printer object that was used to create the Job
+ object. This attribute is used to determine the Printer object
+ that created a Job object when given only the URI for the Job
+ object. This linkage is necessary to determine the languages,
+ charsets, and operations which are supported on that Job (the basis
+ for such support comes from the creating Printer object).
+ - Each Printer object has a name (which is not necessarily unique).
+ The administrator chooses and sets this name through some mechanism
+ outside the scope of this IPP/1.1 document. The Printer object's
+ "printer-name" attribute contains the name.
+ - Each Job object has a name (which is not necessarily unique). The
+ client optionally supplies this name in the create request. If the
+ client does not supply this name, the Printer object generates a
+ name for the Job object. The Job object's "job-name" attribute
+ contains the name.
+
+
+
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 19]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+3. IPP Operations
+
+
+IPP objects support operations. An operation consists of a request and
+a response. When a client communicates with an IPP object, the client
+issues an operation request to the URI for that object. Operation
+requests and responses have parameters that identify the operation.
+Operations also have attributes that affect the run-time characteristics
+of the operation (the intended target, localization information, etc.).
+These operation-specific attributes are called operation attributes (as
+compared to object attributes such as Printer object attributes or Job
+object attributes). Each request carries along with it any operation
+attributes, object attributes, and/or document data required to perform
+the operation. Each request requires a response from the object. Each
+response indicates success or failure of the operation with a status
+code as a response parameter. The response contains any operation
+attributes, object attributes, and/or status messages generated during
+the execution of the operation request.
+
+This section describes the semantics of the IPP operations, both
+requests and responses, in terms of the parameters, attributes, and
+other data associated with each operation.
+
+The IPP/1.1 Printer operations are:
+
+ Print-Job (section 3.2.1)
+ Print-URI (section 3.2.2)
+ Validate-Job (section 3.2.3)
+ Create-Job (section 3.2.4)
+ Get-Printer-Attributes (section 3.2.5)
+ Get-Jobs (section 3.2.6)
+ Pause-Printer (section 3.3.5)
+ Resume-Printer (section 3.3.6)
+ Purge-Jobs (section 3.3.7)
+
+
+The Job operations are:
+
+ Send-Document (section 3.3.1)
+ Send-URI (section 3.3.2)
+ Cancel-Job (section 3.3.3)
+ Get-Job-Attributes (section 3.3.4)
+ Hold-Job (section 3.3.5)
+ Release-Job (section 3.3.6)
+ Restart-Job (section 3.3.7)
+
+
+The Send-Document and Send-URI Job operations are used to add a new
+document to an existing multi-document Job object created using the
+Create-Job operation.
+
+
+3.1 Common Semantics
+
+All IPP operations require some common parameters and operation
+attributes. These common elements and their semantic characteristics
+are defined and described in more detail in the following sections.
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 20]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+3.1.1 Required Parameters
+
+Every operation request contains the following REQUIRED parameters:
+
+ - a "version-number",
+ - an "operation-id",
+ - a "request-id", and
+ - the attributes that are REQUIRED for that type of request.
+
+
+Every operation response contains the following REQUIRED parameters:
+
+ - a "version-number",
+ - a "status-code",
+ - the "request-id" that was supplied in the corresponding request,
+ and
+ - the attributes that are REQUIRED for that type of response.
+
+The "Encoding and Transport document [IPP-PRO] defines special rules for
+the encoding of these parameters. All other operation elements are
+represented using the more generic encoding rules for attributes and
+groups of attributes.
+
+3.1.2 Operation IDs and Request IDs
+
+Each IPP operation request includes an identifying "operation-id" value.
+Valid values are defined in the "operations-supported" Printer attribute
+section (see section 4.4.15). The client specifies which operation is
+being requested by supplying the correct "operation-id" value.
+
+In addition, every invocation of an operation is identified by a
+"request-id" value. For each request, the client chooses the "request-
+id" which MUST be an integer (possibly unique depending on client
+requirements) in the range from 1 to 2**31 - 1 (inclusive). This
+"request-id" allows clients to manage multiple outstanding requests. The
+receiving IPP object copies all 32-bits of the client-supplied "request-
+id" attribute into the response so that the client can match the
+response with the correct outstanding request, even if the "request-id"
+is out of range. If the request is terminated before the complete
+"request-id" is received, the IPP object rejects the request and returns
+a response with a "request-id" of 0.
+
+Note: In some cases, the transport protocol underneath IPP might be a
+connection oriented protocol that would make it impossible for a client
+to receive responses in any order other than the order in which the
+corresponding requests were sent. In such cases, the "request-id"
+attribute would not be essential for correct protocol operation.
+However, in other mappings, the operation responses can come back in any
+order. In these cases, the "request-id" would be essential.
+
+
+3.1.3 Attributes
+
+Operation requests and responses are both composed of groups of
+attributes and/or document data. The attributes groups are:
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 21]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+ - Operation Attributes: These attributes are passed in the operation
+ and affect the IPP object's behavior while processing the operation
+ request and may affect other attributes or groups of attributes.
+ Some operation attributes describe the document data associated
+ with the print job and are associated with new Job objects, however
+ most operation attributes do not persist beyond the life of the
+ operation. The description of each operation attribute includes
+ conformance statements indicating which operation attributes are
+ REQUIRED and which are OPTIONAL for an IPP object to support and
+ which attributes a client MUST supply in a request and an IPP
+ object MUST supply in a response.
+ - Job Template Attributes: These attributes affect the processing of
+ a job. A client OPTIONALLY supplies Job Template Attributes in a
+ create request, and the receiving object MUST be prepared to
+ receive all supported attributes. The Job object can later be
+ queried to find out what Job Template attributes were originally
+ requested in the create request, and such attributes are returned
+ in the response as Job Object Attributes. The Printer object can
+ be queried about its Job Template attributes to find out what type
+ of job processing capabilities are supported and/or what the
+ default job processing behaviors are, though such attributes are
+ returned in the response as Printer Object Attributes. The "ipp-
+ attribute-fidelity" operation attribute affects processing of all
+ client-supplied Job Template attributes (see sections 3.2.1.2 and
+ 15 for a full description of "ipp-attribute-fidelity" and its
+ relationship to other attributes).
+ - Job Object Attributes: These attributes are returned in response to
+ a query operation directed at a Job object.
+ - Printer Object Attributes: These attributes are returned in
+ response to a query operation directed at a Printer object.
+ - Unsupported Attributes: In a create request, the client supplies a
+ set of Operation and Job Template attributes. If any of these
+ attributes or their values is unsupported by the Printer object,
+ the Printer object returns the set of unsupported attributes in the
+ response. Sections 3.1.7, 3.2.1.2, and 15 give a full description
+ of how Job Template attributes supplied by the client in a create
+ request are processed by the Printer object and how unsupported
+ attributes are returned to the client. Because of extensibility,
+ any IPP object might receive a request that contains new or unknown
+ attributes or values for which it has no support. In such cases,
+ the IPP object processes what it can and returns the unsupported
+ attributes in the response. The Unsupported Attribute group is
+ defined for all operation responses for returning unsupported
+ attributes that the client supplied in the request.
+
+
+Later in this section, each operation is formally defined by identifying
+the allowed and expected groups of attributes for each request and
+response. The model identifies a specific order for each group in each
+request or response, but the attributes within each group may be in any
+order, unless specified otherwise.
+
+Each attribute definition includes the attribute's name followed by the
+name of its attribute syntax(es) in parenthesizes. In addition, each
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 22]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+'integer' attribute is followed by the allowed range in parentheses,
+(m:n), for values of that attribute. Each 'text' or 'name' attribute is
+followed by the maximum size in octets in parentheses, (size), for
+values of that attribute. For more details on attribute syntax notation,
+see the descriptions of these attributes syntaxes in section 4.1.
+
+Note: Document data included in the operation is not strictly an
+attribute, but it is treated as a special attribute group for ordering
+purposes. The only operations that support supplying the document data
+within an operation request are Print-Job and Send-Document. There are
+no operation responses that include document data.
+
+Some operations are REQUIRED for IPP objects to support; the others are
+OPTIONAL (see section 5.2.2). Therefore, before using an OPTIONAL
+operation, a client SHOULD first use the REQUIRED Get-Printer-Attributes
+operation to query the Printer's "operations-supported" attribute in
+order to determine which OPTIONAL Printer and Job operations are
+actually supported. The client SHOULD NOT use an OPTIONAL operation
+that is not supported. When an IPP object receives a request to perform
+an operation it does not support, it returns the 'server-error-
+operation-not-supported' status code (see section 13.1.5.2). An IPP
+object is non-conformant if it does not support a REQUIRED operation.
+
+
+3.1.4 Character Set and Natural Language Operation Attributes
+
+Some Job and Printer attributes have values that are text strings and
+names intended for human understanding rather than machine understanding
+(see the 'text' and 'name' attribute syntax descriptions in section
+4.1). The following sections describe two special Operation Attributes
+called "attributes-charset" and "attributes-natural-language". These
+attributes are always part of the Operation Attributes group. For most
+attribute groups, the order of the attributes within the group is not
+important. However, for these two attributes within the Operation
+Attributes group, the order is critical. The "attributes-charset"
+attribute MUST be the first attribute in the group and the "attributes-
+natural-language" attribute MUST be the second attribute in the group.
+In other words, these attributes MUST be supplied in every IPP request
+and response, they MUST come first in the group, and MUST come in the
+specified order. For job creation operations, the IPP Printer
+implementation saves these two attributes with the new Job object as Job
+Description attributes. For the sake of brevity in this document, these
+operation attribute descriptions are not repeated with every operation
+request and response, but have a reference back to this section instead.
+
+
+3.1.4.1 Request Operation Attributes
+
+The client MUST supply and the Printer object MUST support the following
+REQUIRED operation attributes in every IPP/1.1 operation request:
+
+ "attributes-charset" (charset):
+ This operation attribute identifies the charset (coded character
+ set and encoding method) used by any 'text' and 'name' attributes
+ that the client is supplying in this request. It also identifies
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 23]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+ the charset that the Printer object MUST use (if supported) for all
+ 'text' and 'name' attributes and status messages that the Printer
+ object returns in the response to this request. See Sections 4.1.1
+ and 4.1.2 for the definition of the 'text' and 'name' attribute
+ syntaxes.
+
+ All clients and IPP objects MUST support the 'utf-8' charset
+ [RFC2279] and MAY support additional charsets provided that they
+ are registered with IANA [IANA-CS]. If the Printer object does not
+ support the client supplied charset value, the Printer object MUST
+ reject the request, set the "attributes-charset" to 'utf-8' in the
+ response, and return the 'client-error-charset-not-supported'
+ status code and any 'text' or 'name' attributes using the 'utf-8'
+ charset. The Printer NEED NOT return any attributes in the
+ Unsupported Attributes Group (See sections 3.1.7 and 3.2.1.2). The
+ Printer object MUST indicate the charset(s) supported as the values
+ of the "charset-supported" Printer attribute (see Section 4.4.18),
+ so that the client can query to determine which charset(s) are
+ supported.
+
+ Note to client implementers: Since IPP objects are only required to
+ support the 'utf-8' charset, in order to maximize interoperability
+ with multiple IPP object implementations, a client may want to
+ supply 'utf-8' in the "attributes-charset" operation attribute,
+ even though the client is only passing and able to present a
+ simpler charset, such as US-ASCII or ISO-8859-1. Then the client
+ will have to filter out (or charset convert) those characters that
+ are returned in the response that it cannot present to its user.
+ On the other hand, if both the client and the IPP objects also
+ support a charset in common besides utf-8, the client may want to
+ use that charset in order to avoid charset conversion or data loss.
+
+ See the 'charset' attribute syntax description in Section 4.1.7 for
+ the syntax and semantic interpretation of the values of this
+ attribute and for example values.
+
+ "attributes-natural-language" (naturalLanguage):
+ This operation attribute identifies the natural language used by
+ any 'text' and 'name' attributes that the client is supplying in
+ this request. This attribute also identifies the natural language
+ that the Printer object SHOULD use for all 'text' and 'name'
+ attributes and status messages that the Printer object returns in
+ the response to this request.
+
+ There are no REQUIRED natural languages required for the Printer
+ object to support. However, the Printer object's "generated-
+ natural-language-supported" attribute identifies the natural
+ languages supported by the Printer object and any contained Job
+ objects for all text strings generated by the IPP object. A client
+ MAY query this attribute to determine which natural language(s) are
+ supported for generated messages.
+
+ For any of the attributes for which the Printer object generates
+ text, i.e., for the "job-state-message", "printer-state-message",
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 24]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+ and status messages (see Section 3.1.6), the Printer object MUST be
+ able to generate these text strings in any of its supported natural
+ languages. If the client requests a natural language that is not
+ supported, the Printer object MUST return these generated messages
+ in the Printer's configured natural language as specified by the
+ Printer's "natural-language-configured" attribute" (see Section
+ 4.4.19).
+
+ For other 'text' and 'name' attributes supplied by the client,
+ authentication system, operator, system administrator, or
+ manufacturer (i.e., for "job-originating-user-name", "printer-name"
+ (name), "printer-location" (text), "printer-info" (text), and
+ "printer-make-and-model" (text)), the Printer object is only
+ required to support the configured natural language of the Printer
+ identified by the Printer object's "natural-language-configured"
+ attribute, though support of additional natural languages for these
+ attributes is permitted.
+
+ For any 'text' or 'name' attribute in the request that is in a
+ different natural language than the value supplied in the
+ "attributes-natural-language" operation attribute, the client MUST
+ use the Natural Language Override mechanism (see sections 4.1.1.2
+ and 4.1.2.2) for each such attribute value supplied. The client
+ MAY use the Natural Language Override mechanism redundantly, i.e.,
+ use it even when the value is in the same natural language as the
+ value supplied in the "attributes-natural-language" operation
+ attribute of the request.
+
+ The IPP object MUST accept any natural language and any Natural
+ Language Override, whether the IPP object supports that natural
+ language or not (and independent of the value of the "ipp-
+ attribute-fidelity" Operation attribute). That is the IPP object
+ accepts all client supplied values no matter what the values are in
+ the Printer object's "generated-natural-language-supported"
+ attribute. That attribute, "generated-natural-language-supported",
+ only applies to generated messages, not client supplied messages.
+ The IPP object MUST remember that natural language for all client-
+ supplied attributes, and when returning those attributes in
+ response to a query, the IPP object MUST indicate that natural
+ language.
+
+ Each value whose attribute syntax type is 'text' or 'name' (see
+ sections 4.1.1 and 4.1.2) has an Associated Natural-Language. This
+ document does not specify how this association is stored in a
+ Printer or Job object. When such a value is encoded in a request
+ or response, the natural language is either implicit or explicit:
+
+ @ In the implicit case, the value contains only the text/name
+ value, and the language is specified by the "attributes-
+ natural-language" operation attribute in the request or
+ response (see sections 4.1.1.1 textWithoutLanguage and 4.1.2.1
+ nameWithoutLanguage).
+
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 25]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+ @ In the explicit case (also known as the Natural-Language
+ Override case), the value contains both the language and the
+ text/name value (see sections 4.1.1.2 textWithLanguage and
+ 4.1.2.2 nameWithLanguage).
+
+ For example, the "job-name" attribute MAY be supplied by the client
+ in a create request. The text value for this attribute will be in
+ the natural language identified by the "attribute-natural-language"
+ attribute, or if different, as identified by the Natural Language
+ Override mechanism. If supplied, the IPP object will use the value
+ of the "job-name" attribute to populate the Job object's "job-name"
+ attribute. Whenever any client queries the Job object's "job-name"
+ attribute, the IPP object returns the attribute as stored and uses
+ the Natural Language Override mechanism to specify the natural
+ language, if it is different from that reported in the "attributes-
+ natural-language" operation attribute of the response. The IPP
+ object MAY use the Natural Language Override mechanism redundantly,
+ i.e., use it even when the value is in the same natural language
+ as the value supplied in the "attributes-natural-language"
+ operation attribute of the response.
+
+ An IPP object MUST NOT reject a request based on a supplied natural
+ language in an "attributes-natural-language" Operation attribute or
+ in any attribute that uses the Natural Language Override.
+
+ See the 'naturalLanguage' attribute syntax description in section
+ 4.1.8 for the syntax and semantic interpretation of the values of
+ this attribute and for example values.
+
+
+Clients SHOULD NOT supply 'text' or 'name' attributes that use an
+illegal combination of natural language and charset. For example,
+suppose a Printer object supports charsets 'utf-8', 'iso-8859-1', and
+'iso-8859-7'. Suppose also, that it supports natural languages 'en'
+(English), 'fr' (French), and 'el' (Greek). Although the Printer object
+supports the charset 'iso-8859-1' and natural language 'el', it probably
+does not support the combination of Greek text strings using the 'iso-
+8859-1' charset. The Printer object handles this apparent
+incompatibility differently depending on the context in which it occurs:
+
+ - In a create request: If the client supplies a text or name
+ attribute (for example, the "job-name" operation attribute) that
+ uses an apparently incompatible combination, it is a client choice
+ that does not affect the Printer object or its correct operation.
+ Therefore, the Printer object simply accepts the client supplied
+ value, stores it with the Job object, and responds back with the
+ same combination whenever the client (or any client) queries for
+ that attribute.
+ - In a query-type operation, like Get-Printer-Attributes: If the
+ client requests an apparently incompatible combination, the Printer
+ object responds (as described in section 3.1.4.2) using the
+ Printer's configured natural language rather than the natural
+ language requested by the client.
+
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 26]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+In either case, the Printer object does not reject the request because
+of the apparent incompatibility. The potential incompatible combination
+of charset and natural language can occur either at the global operation
+level or at the Natural Language Override attribute-by-attribute level.
+In addition, since the response always includes explicit charset and
+natural language information, there is never any question or ambiguity
+in how the client interprets the response.
+
+
+3.1.4.2 Response Operation Attributes
+
+The Printer object MUST supply and the client MUST support the following
+REQUIRED operation attributes in every IPP/1.1 operation response:
+
+ "attributes-charset" (charset):
+ This operation attribute identifies the charset used by any 'text'
+ and 'name' attributes that the Printer object is returning in this
+ response. The value in this response MUST be the same value as the
+ "attributes-charset" operation attribute supplied by the client in
+ the request. If this is not possible (i.e., the charset requested
+ is not supported), the request would have been rejected. See
+ "attributes-charset" described in Section 3.1.4.1 above.
+
+ If the Printer object supports more than just the 'utf-8' charset,
+ the Printer object MUST be able to code convert between each of the
+ charsets supported on a highest fidelity possible basis in order to
+ return the 'text' and 'name' attributes in the charset requested by
+ the client. However, some information loss MAY occur during the
+ charset conversion depending on the charsets involved. For
+ example, the Printer object may convert from a UTF-8 'a' to a US-
+ ASCII 'a' (with no loss of information), from an ISO Latin 1
+ CAPITAL LETTER A WITH ACUTE ACCENT to US-ASCII 'A' (losing the
+ accent), or from a UTF-8 Japanese Kanji character to some ISO Latin
+ 1 error character indication such as '?', decimal code equivalent,
+ or to the absence of a character, depending on implementation.
+
+ Whether an implementation that supports more than one charset
+ stores the data in the charset supplied by the client or code
+ converts to one of the other supported charsets, depends on
+ implementation. The strategy should try to minimize loss of
+ information during code conversion. On each response, such an
+ implementation converts from its internal charset to that
+ requested.
+
+ "attributes-natural-language" (naturalLanguage):
+ This operation attribute identifies the natural language used by
+ any 'text' and 'name' attributes that the IPP object is returning
+ in this response. Unlike the "attributes-charset" operation
+ attribute, the IPP object NEED NOT return the same value as that
+ supplied by the client in the request. The IPP object MAY return
+ the natural language of the Job object or the Printer's configured
+ natural language as identified by the Printer object's "natural-
+ language-configured" attribute, rather than the natural language
+ supplied by the client. For any 'text' or 'name' attribute or
+ status message in the response that is in a different natural
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 27]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+ language than the value returned in the "attributes-natural-
+ language" operation attribute, the IPP object MUST use the Natural
+ Language Override mechanism (see sections 4.1.1.2 and 4.1.2.2) on
+ each attribute value returned. The IPP object MAY use the Natural
+ Language Override mechanism redundantly, i.e., use it even when the
+ value is in the same natural language as the value supplied in the
+ "attributes-natural-language" operation attribute of the response.
+
+3.1.5 Operation Targets
+
+All IPP operations are directed at IPP objects. For Printer operations,
+the operation is always directed at a Printer object using one of its
+URIs (i.e., one of the values in the Printer object's "printer-uri-
+supported" attribute). Even if the Printer object supports more than
+one URI, the client supplies only one URI as the target of the
+operation. The client identifies the target object by supplying the
+correct URI in the "printer-uri (uri)" operation attribute.
+
+For Job operations, the operation is directed at either:
+
+ - The Job object itself using the Job object's URI. In this case,
+ the client identifies the target object by supplying the correct
+ URI in the "job-uri (uri)" operation attribute.
+ - The Printer object that created the Job object using both the
+ Printer objects URI and the Job object's Job ID. Since the Printer
+ object that created the Job object generated the Job ID, it MUST be
+ able to correctly associate the client supplied Job ID with the
+ correct Job object. The client supplies the Printer object's URI
+ in the "printer-uri (uri)" operation attribute and the Job object's
+ Job ID in the "job-id (integer(1:MAX))" operation attribute.
+
+
+If the operation is directed at the Job object directly using the Job
+object's URI, the client MUST NOT include the redundant "job-id"
+operation attribute.
+
+The operation target attributes are REQUIRED operation attributes that
+MUST be included in every operation request. Like the charset and
+natural language attributes (see section 3.1.4), the operation target
+attributes are specially ordered operation attributes. In all cases,
+the operation target attributes immediately follow the "attributes-
+charset" and "attributes-natural-language" attributes within the
+operation attribute group, however the specific ordering rules are:
+
+ - In the case where there is only one operation target attribute
+ (i.e., either only the "printer-uri" attribute or only the "job-
+ uri" attribute), that attribute MUST be the third attribute in the
+ operation attributes group.
+ - In the case where Job operations use two operation target
+ attributes (i.e., the "printer-uri" and "job-id" attributes), the
+ "printer-uri" attribute MUST be the third attribute and the "job-
+ id" attribute MUST be the fourth attribute.
+
+
+In all cases, the target URIs contained within the body of IPP operation
+requests and responses must be in absolute format rather than relative
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 28]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+format (a relative URL identifies a resource with the scope of the HTTP
+server, but does not include scheme, host or port).
+
+The following rules apply to the use of port numbers in URIs that
+identify IPP objects:
+
+ 1. If the URI scheme allows the port number to be explicitly included
+ in the URI string, and a port number is specified within the URI,
+ then that port number MUST be used by the client to contact the IPP
+ object.
+
+ 2. If the URI scheme allows the port number to be explicitly included
+ in the URI string, and a port number is not specified within the
+ URI, then default port number implied by that URI scheme MUST be
+ used by the client to contact the IPP object.
+
+ 3. If the URI scheme does not allow an explicit port number to be
+ specified within the URI, then the default port number implied by
+ that URI MUST be used by the client to contact the IPP object.
+
+
+Note: The IPP "Encoding and Transport document [IPP-PRO] shows a mapping
+of IPP onto HTTP/1.1 [RFC2616] and defines a new default port number for
+using IPP over HTTP/1.1.
+
+
+3.1.6 Operation Response Status Codes and Status Messages
+
+Every operation response includes a REQUIRED "status-code" parameter and
+an OPTIONAL "status-message" operation attribute, and an OPTIONAL
+"detailed-status-message" operation attribute. The Print-URI and Send-
+URI response MAY include an OPTIONAL "document-access-error" operation
+attribute.
+
+
+3.1.6.1 "status-code" (type2 enum)
+
+The REQUIRED "status-code" parameter provides information on the
+processing of a request.
+
+The status code is intended for use by automata. A client
+implementation of IPP SHOULD convert status code values into any
+localized message that has semantic meaning to the end user.
+
+The "status-code" value is a numeric value that has semantic meaning.
+The "status-code" syntax is similar to a "type2 enum" (see section 4.1
+on "Attribute Syntaxes") except that values can range only from 0x0000
+to 0x7FFF. Section 13 describes the status codes, assigns the numeric
+values, and suggests a corresponding status message for each status code
+for use by the client when the user's natural language is English.
+
+If the Printer performs an operation with no errors and it encounters no
+problems, it MUST return the status code 'successful-ok' in the
+response. See section 13.
+
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 29]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+If the client supplies unsupported values for the following parameters
+or Operation attributes, the Printer object MUST reject the operation,
+NEED NOT return the unsupported attribute value in the Unsupported
+Attributes group, and MUST return the indicated status code:
+
+ Parameter/Attribute Status code
+
+ version-number server-error-version-not-supported
+ operation-id server-error-operation-not-supported
+ attributes-charset client-error-charset-not-supported
+ compression client-error-compression-not-supported
+ document-format client-error-document-format-not-supported
+ document-uri client-error-uri-scheme-not-supported,
+ client-error-document-access-error
+
+
+If the client supplies unsupported values for other attributes, or
+unsupported attributes, the Printer returns the status code defined in
+section 3.1.7 on Unsupported Attributes.
+
+
+3.1.6.2 "status-message" (text(255))
+
+The OPTIONAL "status-message" operation attribute provides a short
+textual description of the status of the operation. The "status-
+message" attribute's syntax is "text(255)", so the maximum length is 255
+octets (see section 4.1.1). The status message is intended for the
+human end user. If a response does include a "status-message"
+attribute, an IPP client NEED NOT examine or display the messages,
+however it SHOULD do so in some implementation specific manner. The
+"status-message" is especially useful for a later version of a Printer
+object to return as supplemental information for the human user to
+accompany a status code that an earlier version of a client might not
+understand.
+
+If the Printer object supports the "status-message" operation attribute,
+the Printer object MUST be able to generate this message in any of the
+natural languages identified by the Printer object's "generated-natural-
+language-supported" attribute (see the "attributes-natural-language"
+operation attribute specified in section 3.1.4.1. Section 13 suggests
+the text for the status message returned by the Printer for use with the
+English natural language.
+
+As described in section 3.1.4.1 for any returned 'text' attribute, if
+there is a choice for generating this message, the Printer object uses
+the natural language indicated by the value of the "attributes-natural-
+language" in the client request if supported, otherwise the Printer
+object uses the value in the Printer object's own "natural-language-
+configured" attribute.
+
+If the Printer object supports the "status-message" operation attribute,
+it SHOULD use the REQUIRED 'utf-8' charset to return a status message
+for the following error status codes (see section 13): 'client-error-
+bad-request', 'client-error-charset-not-supported', 'server-error-
+internal-error', 'server-error-operation-not-supported', and 'server-
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 30]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+error-version-not-supported'. In this case, it MUST set the value of
+the "attributes-charset" operation attribute to 'utf-8' in the error
+response.
+
+
+3.1.6.3 "detailed-status-message" (text(MAX))
+
+The OPTIONAL "detailed-status-message" operation attribute provides
+additional more detailed technical and implementation-specific
+information about the operation. The "detailed-status-message"
+attribute's syntax is "text(MAX)", so the maximum length is 1023 octets
+(see section 4.1.1). If the Printer objects supports the "detailed-
+status-message" operation attribute, neither the Printer nor the client
+localizes the message, since it is intended for use by the system
+administrator or other experienced technical persons. Clients MUST NOT
+attempt to parse the value of this attribute. See the "document-access-
+error" operation attribute (section 3.1.6.4) for additional errors that
+a program can process.
+
+
+3.1.6.4 "document-access-error" (text(MAX))
+
+This OPTIONAL operation attribute provides additional information about
+any document access errors encountered by the Printer before it returned
+a response to the Print-URI (section 3.2.2) or Send-URI (section 3.3.1)
+operation. For errors in the protocol identified by the URI scheme in
+the "document-uri" operation attribute, such as 'http:' or 'ftp:', the
+error code is returned in parentheses, followed by the URI. For
+example:
+
+ (404) http://ftp.pwg.org/pub/pwg/ipp/new_MOD/ipp-model-v11-
+ 990510.pdf
+
+Most Internet protocols use decimal error codes (unlike IPP), so the
+ASCII error code representation is in decimal.
+
+3.1.7 Unsupported Attributes
+
+The Unsupported Attributes group contains attributes that are not
+supported by the operation. This group is primarily for the job creation
+operations, but all operations can return this group.
+
+A Printer object MUST include an Unsupported Attributes group in a
+response if the status code is one of the following: 'successful-ok-
+ignored-or-substituted-attributes', 'successful-ok-conflicting-
+attributes', 'client-error-attributes-or-values-not-supported' or
+'client-error-conflicting-attributes'.
+
+If the status code is one of the four specified in the preceding
+paragraph, the Unsupported Attributes group MUST contain all of those
+attributes and only those attributes that are:
+
+ a. an Operation or Job Template attribute supplied in the request, and
+
+
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 31]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+ b. unsupported by the printer. See below for details on the three
+ categories .unsupported. attributes.
+
+If the Printer object is not returning any Unsupported Attributes in the
+response, the Printer object SHOULD omit Group 2 rather than sending an
+empty group. However, a client MUST be able to accept an empty group.
+
+Unsupported attributes fall into three categories:
+
+ 1. The Printer object does not support the supplied attribute (no
+ matter what the attribute syntax or value).
+
+ 2. The Printer object does support the attribute, but does not support
+ some or all of the particular attribute syntaxes or values supplied
+ by the client (i.e., the Printer object does not have those
+ attribute syntaxes or values in its corresponding "xxx-supported"
+ attribute).
+
+ 3. The Printer object does support the attributes and values supplied,
+ but the particular values are in conflict with one another, because
+ they violate a constraint, such as not being able to staple
+ transparencies.
+
+In the case of an unsupported attribute name, the Printer object returns
+the client-supplied attribute with a substituted value of 'unsupported'.
+This value's syntax type is "out-of-band" and its encoding is defined by
+special rules for "out-of-band" values in the "Encoding and Transport"
+document [IPP-PRO]. Its value indicates no support for the attribute
+itself (see the beginning of section 4.1).
+
+In the case of a supported attribute with one or more unsupported
+attribute syntaxes or values, the Printer object simply returns the
+client-supplied attribute with the unsupported attribute syntaxes or
+values as supplied by the client. This indicates support for the
+attribute, but no support for that particular attribute syntax or value.
+If the client supplies a multi-valued attribute with more than one value
+and the Printer object supports the attribute but only supports a subset
+of the client-supplied attribute syntaxes or values, the Printer object
+MUST return only those attribute syntaxes or values that are
+unsupported.
+
+In the case of two (or more) supported attribute values that are in
+conflict with one another (although each is supported independently, the
+values conflict when requested together within the same job), the
+Printer object MUST return all the values that it ignores or substitutes
+to resolve the conflict, but not any of the values that it is still
+using. The choice for exactly how to resolve the conflict is
+implementation dependent. See sections 3.2.1.2 and 15. See The
+Implementer's Guide [IPP-IIG] for an example.
+
+
+
+
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 32]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+3.1.8 Versions
+
+Each operation request and response carries with it a "version-number"
+parameter. Each value of the "version-number" is in the form "X.Y"
+where X is the major version number and Y is the minor version number.
+By including a version number in the client request, it allows the
+client to identify which version of IPP it is interested in using,
+i.e., the version whose conformance requirements the client may be
+depending upon the Printer to meet.
+
+If the IPP object does not support that major version number supplied by
+the client, i.e., the major version field of the "version-number"
+parameter does not match any of the values of the Printer's "ipp-
+versions-supported" (see section 4.4.14), the object MUST respond with a
+status code of 'server-error-version-not-supported' along with the
+closest version number that is supported (see section 13.1.5.4). If the
+major version number is supported, but the minor version number is not,
+the IPP object SHOULD accept and attempt to perform the request (or
+reject the request if the operation is not supported), else it rejects
+the request and returns the 'server-error-version-not-supported' status
+code. In all cases, the IPP object MUST return the "version-number"
+that it supports that is closest to the version number supplied by the
+client in the request.
+
+There is no version negotiation per se. However, if after receiving a
+'server-error-version-not-supported' status code from an IPP object, a
+client SHOULD try again with a different version number. A client MAY
+also determine the versions supported either from a directory that
+conforms to Appendix E (see section 16) or by querying the Printer
+object's "ipp-versions-supported" attribute (see section 4.4.14) to
+determine which versions are supported.
+
+An IPP object implementation MUST support version '1.1', i.e., meet the
+conformance requirements for IPP/1.1 as specified in this document and
+[IPP-PRO]. It is recommended that IPP object implementations accept any
+request with the major version '1' (or reject the request if the
+operation is not supported).
+
+There is only one notion of "version number" that covers both IPP Model
+and IPP Protocol changes. Thus the version number MUST change when
+introducing a new version of the Model and Semantics document (this
+document) or a new version of the "Encoding and Transport" document
+[IPP-PRO].
+
+Changes to the major version number of the Model and Semantics document
+indicate structural or syntactic changes that make it impossible for
+older version of IPP clients and Printer objects to correctly parse and
+correctly process the new or changed attributes, operations and
+responses. If the major version number changes, the minor version
+numbers is set to zero. As an example, adding the REQUIRED "ipp-
+attribute-fidelity" attribute to version '1.1' (if it had not been part
+of version '1.0'), would have required a change to the major version
+number, since an IPP/1.0 Printer would not have processed a request with
+the correct semantics that contained the "ipp-attribute-fidelity"
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 33]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+attribute that it did not know about. Items that might affect the
+changing of the major version number include any changes to the Model
+and Semantics document (this document) or the "Encoding and Transport"
+document [IPP-PRO] itself, such as:
+
+ - reordering of ordered attributes or attribute sets
+ - changes to the syntax of existing attributes
+ - adding REQUIRED (for an IPP object to support) operation attribute
+ groups
+ - adding values to existing REQUIRED operation attributes
+ - adding REQUIRED operations
+
+
+Changes to the minor version number indicate the addition of new
+features, attributes and attribute values that may not be understood by
+all IPP objects, but which can be ignored if not understood. Items that
+might affect the changing of the minor version number include any
+changes to the model objects and attributes but not the encoding and
+transport rules [IPP-PRO] (except adding attribute syntaxes). Examples
+of such changes are:
+
+ - grouping all extensions not included in a previous version into a
+ new version
+ - adding new attribute values
+ - adding new object attributes
+ - adding OPTIONAL (for an IPP object to support) operation attributes
+ (i.e., those attributes that an IPP object can ignore without
+ confusing clients)
+ - adding OPTIONAL (for an IPP object to support) operation attribute
+ groups (i.e., those attributes that an IPP object can ignore
+ without confusing clients)
+ - adding new attribute syntaxes
+ - adding OPTIONAL operations
+ - changing Job Description attributes or Printer Description
+ attributes from OPTIONAL to REQUIRED or vice versa.
+ - adding OPTIONAL attribute syntaxes to an existing attribute.
+
+The encoding of the "version-number" MUST NOT change over any version
+number (either major or minor). This rule guarantees that all future
+versions will be backwards compatible with all previous versions (at
+least for checking the "version-number"). In addition, any protocol
+elements (attributes, error codes, tags, etc.) that are not carried
+forward from one version to the next are deprecated so that they can
+never be reused with new semantics.
+
+Implementations that support a certain version NEED NOT support ALL
+previous versions. As each new version is defined (through the release
+of a new IPP specification document), that version will specify which
+previous versions MUST and which versions SHOULD be supported in
+compliant implementations.
+
+
+
+
+
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 34]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+3.1.9 Job Creation Operations
+
+In order to "submit a print job" and create a new Job object, a client
+issues a create request. A create request is any one of following three
+operation requests:
+
+ - The Print-Job Request: A client that wants to submit a print job
+ with only a single document uses the Print-Job operation. The
+ operation allows for the client to "push" the document data to the
+ Printer object by including the document data in the request
+ itself.
+
+ - The Print-URI Request: A client that wants to submit a print job
+ with only a single document (where the Printer object "pulls" the
+ document data instead of the client "pushing" the data to the
+ Printer object) uses the Print-URI operation. In this case, the
+ client includes in the request only a URI reference to the document
+ data (not the document data itself).
+
+ - The Create-Job Request: A client that wants to submit a print job
+ with multiple documents uses the Create-Job operation. This
+ operation is followed by an arbitrary number of Send-Document
+ and/or Send-URI operations (each creating another document for the
+ newly create Job object). The Send-Document operation includes the
+ document data in the request (the client "pushes" the document data
+ to the printer), and the Send-URI operation includes only a URI
+ reference to the document data in the request (the Printer "pulls"
+ the document data from the referenced location). The last Send-
+ Document or Send-URI request for a given Job object includes a
+ "last-document" operation attribute set to 'true' indicating that
+ this is the last request.
+
+
+Throughout this model document, the term "create request" is used to
+refer to any of these three operation requests.
+
+A Create-Job operation followed by only one Send-Document operation is
+semantically equivalent to a Print-Job operation, however, for
+performance reasons, the client SHOULD use the Print-Job operation for
+all single document jobs. Also, Print-Job is a REQUIRED operation (all
+implementations MUST support it) whereas Create-Job is an OPTIONAL
+operation, hence some implementations might not support it.
+
+Job submission time is the point in time when a client issues a create
+request. The initial state of every Job object is the 'pending',
+'pending-held', or 'processing' state (see section 4.3.7). When the
+Printer object begins processing the print job, the Job object's state
+moves to 'processing'. This is known as job processing time. There are
+validation checks that must be done at job submission time and others
+that must be performed at job processing time.
+
+At job submission time and at the time a Validate-Job operation is
+received, the Printer MUST do the following:
+
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 35]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+ 1. Process the client supplied attributes and either accept or reject
+ the request
+ 2. Validate the syntax of and support for the scheme of any client
+ supplied URI
+
+
+At job submission time the Printer object MUST validate whether or not
+the supplied attributes, attribute syntaxes, and values are supported by
+matching them with the Printer object's corresponding "xxx-supported"
+attributes. See section 3.1.7 for details. [IPP-IIG] presents
+suggested steps for an IPP object to either accept or reject any request
+and additional steps for processing create requests.
+
+At job submission time the Printer object NEED NOT perform the
+validation checks reserved for job processing time such as:
+
+ 1. Validating the document data
+ 2. Validating the actual contents of any client supplied URI (resolve
+ the reference and follow the link to the document data)
+
+
+At job submission time, these additional job processing time validation
+checks are essentially useless, since they require actually parsing and
+interpreting the document data, are not guaranteed to be 100% accurate,
+and MUST be done, yet again, at job processing time. Also, in the case
+of a URI, checking for availability at job submission time does not
+guarantee availability at job processing time. In addition, at job
+processing time, the Printer object might discover any of the following
+conditions that were not detectable at job submission time:
+
+ - runtime errors in the document data,
+ - nested document data that is in an unsupported format,
+ - the URI reference is no longer valid (i.e., the server hosting the
+ document might be down), or
+ - any other job processing error
+
+
+At job submission time, a Printer object, especially a non-spooling
+Printer, MAY accept jobs that it does not have enough space for. In
+such a situation, a Printer object MAY stop reading data from a client
+for an indefinite period of time. A client MUST be prepared for a write
+operation to block for an indefinite period of time (see section 5.1 on
+client conformance).
+
+When a Printer object has too little space for starting a new job, it
+MAY reject a new create request. In this case, a Printer object MUST
+return a response (in reply to the rejected request) with a status-code
+of 'server-error-busy' (see section 14.1.5.8) and it MAY close the
+connection before receiving all bytes of the operation. A Printer
+SHOULD indicate that it is temporarily unable to accept jobs by setting
+the 'spool-space-full' value in its "printer-state-reasons" attribute
+and removing the value when it can accept another job (see section
+4.4.12).
+
+When receiving a 'server-error-busy' status-code in an operation
+response, a client MUST be prepared for the Printer object to close the
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 36]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+connection before the client has sent all of the data (especially for
+the Print-Job operation). A client MUST be prepared to keep submitting a
+create request until the IPP Printer object accepts the create request.
+
+At job processing time, since the Printer object has already responded
+with a successful status code in the response to the create request, if
+the Printer object detects an error, the Printer object is unable to
+inform the end user of the error with an operation status code. In
+this case, the Printer, depending on the error, can set the job object's
+"job-state", "job-state-reasons", or "job-state-message" attributes to
+the appropriate value(s) so that later queries can report the correct
+job status.
+
+Note: Asynchronous notification of events is outside the scope of this
+IPP/1.1 document.
+
+
+
+
+3.2 Printer Operations
+
+
+All Printer operations are directed at Printer objects. A client MUST
+always supply the "printer-uri" operation attribute in order to identify
+the correct target of the operation.
+
+
+3.2.1 Print-Job Operation
+
+This REQUIRED operation allows a client to submit a print job with only
+one document and supply the document data (rather than just a reference
+to the data). See Section 15 for the suggested steps for processing
+create operations and their Operation and Job Template attributes.
+
+
+3.2.1.1 Print-Job Request
+
+The following groups of attributes are supplied as part of the Print-Job
+Request:
+
+Group 1: Operation Attributes
+
+ Natural Language and Character Set:
+ The "attributes-charset" and "attributes-natural-language"
+ attributes as described in section 3.1.4.1. The Printer object
+ MUST copy these values to the corresponding Job Description
+ attributes described in sections 4.3.19 and 4.3.20.
+
+ Target:
+ The "printer-uri" (uri) operation attribute which is the target for
+ this operation as described in section 3.1.5.
+
+ Requesting User Name:
+ The "requesting-user-name" (name(MAX)) attribute SHOULD be supplied
+ by the client as described in section 8.3.
+
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 37]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+ "job-name" (name(MAX)):
+ The client OPTIONALLY supplies this attribute. The Printer object
+ MUST support this attribute. It contains the client supplied Job
+ name. If this attribute is supplied by the client, its value is
+ used for the "job-name" attribute of the newly created Job object.
+ The client MAY automatically include any information that will help
+ the end-user distinguish amongst his/her jobs, such as the name of
+ the application program along with information from the document,
+ such as the document name, document subject, or source file name.
+ If this attribute is not supplied by the client, the Printer
+ generates a name to use in the "job-name" attribute of the newly
+ created Job object (see Section 4.3.5).
+
+ "ipp-attribute-fidelity" (boolean):
+ The client OPTIONALLY supplies this attribute. The Printer object
+ MUST support this attribute. The value 'true' indicates that total
+ fidelity to client supplied Job Template attributes and values is
+ required, else the Printer object MUST reject the Print-Job
+ request. The value 'false' indicates that a reasonable attempt to
+ print the Job object is acceptable and the Printer object MUST
+ accept the Print-job request. If not supplied, the Printer object
+ assumes the value is 'false'. All Printer objects MUST support
+ both types of job processing. See section 15 for a full
+ description of "ipp-attribute-fidelity" and its relationship to
+ other attributes, especially the Printer object's "pdl-override-
+ supported" attribute.
+
+ "document-name" (name(MAX)):
+ The client OPTIONALLY supplies this attribute. The Printer object
+ MUST support this attribute. It contains the client supplied
+ document name. The document name MAY be different than the Job
+ name. Typically, the client software automatically supplies the
+ document name on behalf of the end user by using a file name or an
+ application generated name. If this attribute is supplied, its
+ value can be used in a manner defined by each implementation.
+ Examples include: printed along with the Job (job start sheet, page
+ adornments, etc.), used by accounting or resource tracking
+ management tools, or even stored along with the document as a
+ document level attribute. IPP/1.1 does not support the concept of
+ document level attributes.
+
+ "compression" (type3 keyword)
+ The client OPTIONALLY supplies this attribute. The Printer object
+ MUST support this attribute and the "compression-supported"
+ attribute (see section 4.4.32). The client supplied "compression"
+ operation attribute identifies the compression algorithm used on
+ the document data. The following cases exist:
+ a)If the client omits this attribute, the Printer object MUST
+ assume that the data is not compressed (i.e. the Printer
+ follows the rules below as if the client supplied the
+ "compression" attribute with a value of 'none').
+ b)If the client supplies this attribute, but the value is not
+ supported by the Printer object, i.e., the value is not one
+ of the values of the Printer object's "compression-
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 38]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+ supported" attribute, the Printer object MUST reject the
+ request, and return the 'client-error-compression-not-
+ supported' status code. See section 3.1.7 for returning
+ unsupported attributes and values.
+ c)If the client supplies the attribute and the Printer object
+ supports the attribute value, the Printer object uses the
+ corresponding decompression algorithm on the document data.
+ d)If the decompression algorithm fails before the Printer
+ returns an operation response, the Printer object MUST
+ reject the request and return the 'client-error-
+ compression-error' status code.
+ e)If the decompression algorithm fails after the Printer
+ returns an operation response, the Printer object MUST
+ abort the job and add the 'compression-error' value to the
+ job's "job-state-reasons" attribute.
+ f)If the decompression algorithm succeeds, the document data
+ MUST then have the format specified by the job's "document-
+ format" attribute, if supplied (see "document-format"
+ operation attribute definition below).
+
+ "document-format" (mimeMediaType) :
+ The client OPTIONALLY supplies this attribute. The Printer object
+ MUST support this attribute. The value of this attribute
+ identifies the format of the supplied document data. The following
+ cases exist:
+ a)If the client does not supply this attribute, the Printer
+ object assumes that the document data is in the format
+ defined by the Printer object's "document-format-default"
+ attribute. (i.e. the Printer follows the rules below as if
+ the client supplied the "document-format" attribute with a
+ value equal to the printer's default value).
+ b)If the client supplies this attribute, but the value is not
+ supported by the Printer object, i.e., the value is not one
+ of the values of the Printer object's "document-format-
+ supported" attribute, the Printer object MUST reject the
+ request and return the 'client-error-document-format-not-
+ supported' status code.
+ c)If the client supplies this attribute and its value is
+ 'application/octet-stream' (i.e. to be auto-sensed, see
+ Section 4.1.9.1), and the format is not one of the
+ document-formats that the Printer can auto-sense, and this
+ check occurs before the Printer returns an operation
+ response, then the Printer MUST reject the request and
+ return the 'client-error-document-format-not-supported'
+ status code.
+ d)If the client supplies this attribute, and the value is
+ supported by the Printer object, the document data, the
+ Printer is capable of interpreting the document data.
+ e)If interpreting of the document data fails before the
+ Printer returns an operation response, the Printer object
+ MUST reject the request and return the 'client-error-
+ document-format-error' status code.
+ f)If interpreting of the document data fails after the
+ Printer returns an operation response, the Printer object
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 39]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+ MUST abort the job and add the 'document-format-error'
+ value to the job's "job-state-reasons" attribute.
+
+ "document-natural-language" (naturalLanguage):
+ The client OPTIONALLY supplies this attribute. The Printer object
+ OPTIONALLY supports this attribute. This attribute specifies the
+ natural language of the document for those document-formats that
+ require a specification of the natural language in order to image
+ the document unambiguously. There are no particular values required
+ for the Printer object to support.
+
+
+ "job-k-octets" (integer(0:MAX))
+ The client OPTIONALLY supplies this attribute. The Printer object
+ OPTIONALLY supports this attribute and the "job-k-octets-supported"
+ attribute (see section 4.4.33). The client supplied "job-k-octets"
+ operation attribute identifies the total size of the document(s) in
+ K octets being submitted (see section 4.3.17.1 for the complete
+ semantics). If the client supplies the attribute and the Printer
+ object supports the attribute, the value of the attribute is used
+ to populate the Job object's "job-k-octets" Job Description
+ attribute.
+
+ For this attribute and the following two attributes ("job-
+ impressions", and "job-media-sheets"), if the client supplies the
+ attribute, but the Printer object does not support the attribute,
+ the Printer object ignores the client-supplied value. If the
+ client supplies the attribute and the Printer supports the
+ attribute, and the value is within the range of the corresponding
+ Printer object's "xxx-supported" attribute, the Printer object MUST
+ use the value to populate the Job object's "xxx" attribute. If the
+ client supplies the attribute and the Printer supports the
+ attribute, but the value is outside the range of the corresponding
+ Printer object's "xxx-supported" attribute, the Printer object MUST
+ copy the attribute and its value to the Unsupported Attributes
+ response group, reject the request, and return the 'client-error-
+ attributes-or-values-not-supported' status code. If the client
+ does not supply the attribute, the Printer object MAY choose to
+ populate the corresponding Job object attribute depending on
+ whether the Printer object supports the attribute and is able to
+ calculate or discern the correct value.
+
+ "job-impressions" (integer(0:MAX))
+ The client OPTIONALLY supplies this attribute. The Printer object
+ OPTIONALLY supports this attribute and the "job-impressions-
+ supported" attribute (see section 4.4.34). The client supplied
+ "job-impressions" operation attribute identifies the total size in
+ number of impressions of the document(s) being submitted (see
+ section 4.3.17.2 for the complete semantics).
+
+ See last paragraph under "job-k-octets".
+
+
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 40]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+ "job-media-sheets" (integer(0:MAX))
+ The client OPTIONALLY supplies this attribute. The Printer object
+ OPTIONALLY supports this attribute and the "job-media-sheets-
+ supported" attribute (see section 4.4.35). The client supplied
+ "job-media-sheets" operation attribute identifies the total number
+ of media sheets to be produced for this job (see section 4.3.17.3
+ for the complete semantics).
+
+ See last paragraph under "job-k-octets".
+
+
+Group 2: Job Template Attributes
+
+ The client OPTIONALLY supplies a set of Job Template attributes as
+ defined in section 4.2. If the client is not supplying any Job
+ Template attributes in the request, the client SHOULD omit Group 2
+ rather than sending an empty group. However, a Printer object MUST
+ be able to accept an empty group.
+
+
+Group 3: Document Content
+
+ The client MUST supply the document data to be processed.
+
+
+In addition to the MANDATORY parameters required for every operation
+request, the simplest Print-Job Request consists of just the
+"attributes-charset" and "attributes-natural-language" operation
+attributes; the "printer-uri" target operation attribute; the Document
+Content and nothing else. In this simple case, the Printer object:
+
+ - creates a new Job object (the Job object contains a single
+ document),
+ - stores a generated Job name in the "job-name" attribute in the
+ natural language and charset requested (see Section 3.1.4.1) (if
+ those are supported, otherwise using the Printer object's default
+ natural language and charset), and
+ - at job processing time, uses its corresponding default value
+ attributes for the supported Job Template attributes that were not
+ supplied by the client as IPP attribute or embedded instructions in
+ the document data.
+
+
+
+3.2.1.2 Print-Job Response
+
+The Printer object MUST return to the client the following sets of
+attributes as part of the Print-Job Response:
+
+Group 1: Operation Attributes
+
+ Status Message:
+ In addition to the REQUIRED status code returned in every response,
+ the response OPTIONALLY includes a "status-message" (text(255))
+ and/or a "detailed-status-message" (text(MAX)) operation attribute
+ as described in sections 13 and 3.1.6. If the client supplies
+ unsupported or conflicting Job Template attributes or values, the
+ Printer object MUST reject or accept the Print-Job request
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 41]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+ depending on the whether the client supplied a 'true' or 'false'
+ value for the "ipp-attribute-fidelity" operation attribute. See
+ the Implementer's Guide [IPP-IIG] for a complete description of the
+ suggested steps for processing a create request.
+
+ Natural Language and Character Set:
+ The "attributes-charset" and "attributes-natural-language"
+ attributes as described in section 3.1.4.2.
+
+
+Group 2: Unsupported Attributes
+
+ See section 3.1.7 for details on returning Unsupported Attributes.
+
+ The value of the "ipp-attribute-fidelity" supplied by the client
+ does not affect what attributes the Printer object returns in this
+ group. The value of "ipp-attribute-fidelity" only affects whether
+ the Print-Job operation is accepted or rejected. If the job is
+ accepted, the client may query the job using the Get-Job-Attributes
+ operation requesting the unsupported attributes that were returned
+ in the create response to see which attributes were ignored (not
+ stored on the Job object) and which attributes were stored with
+ other (substituted) values.
+
+
+Group 3: Job Object Attributes
+
+ "job-uri" (uri):
+ The Printer object MUST return the Job object's URI by returning
+ the contents of the REQUIRED "job-uri" Job object attribute. The
+ client uses the Job object's URI when directing operations at the
+ Job object. The Printer object always uses its configured security
+ policy when creating the new URI. However, if the Printer object
+ supports more than one URI, the Printer object also uses
+ information about which URI was used in the Print-Job Request to
+ generated the new URI so that the new URI references the correct
+ access channel. In other words, if the Print-Job Request comes in
+ over a secure channel, the Printer object MUST generate a Job URI
+ that uses the secure channel as well.
+
+ "job-id" (integer(1:MAX)):
+ The Printer object MUST return the Job object's Job ID by returning
+ the REQUIRED "job-id" Job object attribute. The client uses this
+ "job-id" attribute in conjunction with the "printer-uri" attribute
+ used in the Print-Job Request when directing Job operations at the
+ Printer object.
+
+ "job-state":
+ The Printer object MUST return the Job object's REQUIRED "job-
+ state" attribute. The value of this attribute (along with the value
+ of the next attribute: "job-state-reasons") is taken from a
+ "snapshot" of the new Job object at some meaningful point in time
+ (implementation defined) between when the Printer object receives
+ the Print-Job Request and when the Printer object returns the
+ response.
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 42]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+ "job-state-reasons":
+ The Printer object MUST return the Job object's REQUIRED "job-
+ state-reasons" attribute.
+
+ "job-state-message":
+ The Printer object OPTIONALLY returns the Job object's OPTIONAL
+ "job-state-message" attribute. If the Printer object supports this
+ attribute then it MUST be returned in the response. If this
+ attribute is not returned in the response, the client can assume
+ that the "job-state-message" attribute is not supported and will
+ not be returned in a subsequent Job object query.
+
+ "number-of-intervening-jobs":
+ The Printer object OPTIONALLY returns the Job object's OPTIONAL
+ "number-of-intervening-jobs" attribute. If the Printer object
+ supports this attribute then it MUST be returned in the response.
+ If this attribute is not returned in the response, the client can
+ assume that the "number-of-intervening-jobs" attribute is not
+ supported and will not be returned in a subsequent Job object
+ query.
+
+ Note: Since any printer state information which affects a job's
+ state is reflected in the "job-state" and "job-state-reasons"
+ attributes, it is sufficient to return only these attributes and no
+ specific printer status attributes.
+
+
+Note: In addition to the MANDATORY parameters required for every
+operation response, the simplest response consists of the just the
+"attributes-charset" and "attributes-natural-language" operation
+attributes and the "job-uri", "job-id", and "job-state" Job Object
+Attributes. In this simplest case, the status code is 'successful-ok'
+and there is no "status-message" or "detailed-status-message" operation
+attribute.
+
+
+3.2.2 Print-URI Operation
+
+This OPTIONAL operation is identical to the Print-Job operation (section
+3.2.1) except that a client supplies a URI reference to the document
+data using the "document-uri" (uri) operation attribute (in Group 1)
+rather than including the document data itself. Before returning the
+response, the Printer MUST validate that the Printer supports the
+retrieval method (e.g., http, ftp, etc.) implied by the URI, and MUST
+check for valid URI syntax. If the client-supplied URI scheme is not
+supported, i.e. the value is not in the Printer object's "referenced-
+uri-scheme-supported" attribute, the Printer object MUST reject the
+request and return the 'client-error-uri-scheme-not-supported' status
+code.
+
+The IPP Printer MAY validate the accessibility of the document as part
+of the operation or subsequently. If the Printer determines an
+accessibility problem before returning an operation response, it rejects
+the request and returns the 'client-error-document-access-error' status
+code. The Printer MAY also return a specific document access error code
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 43]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+using the "document-access-error" operation attribute (see section
+3.1.6.4).
+
+If the Printer determines this document accessibility problem after
+accepting the request and returning an operation response with one of
+the successful status codes, the Printer adds the 'document-access-
+error' value to the job's "job-state-reasons" attribute and MAY populate
+the job's "job-document-access-errors" Job Description attribute (see
+section 4.3.11). See The Implementer's Guide [IPP-IIG] for suggested
+additional checks.
+
+If the Printer object supports this operation, it MUST support the
+"reference-uri-schemes-supported" Printer attribute (see section
+4.4.27).
+
+It is up to the IPP object to interpret the URI and subsequently "pull"
+the document from the source referenced by the URI string.
+
+
+3.2.3 Validate-Job Operation
+
+This REQUIRED operation is similar to the Print-Job operation (section
+3.2.1) except that a client supplies no document data and the Printer
+allocates no resources (i.e., it does not create a new Job object).
+This operation is used only to verify capabilities of a printer object
+against whatever attributes are supplied by the client in the Validate-
+Job request. By using the Validate-Job operation a client can validate
+that an identical Print-Job operation (with the document data) would be
+accepted. The Validate-Job operation also performs the same security
+negotiation as the Print-Job operation (see section 8), so that a client
+can check that the client and Printer object security requirements can
+be met before performing a Print-Job operation.
+
+The Validate-Job operation does not accept a "document-uri" attribute in
+order to allow a client to check that the same Print-URI operation will
+be accepted, since the client doesn't send the data with the Print-URI
+operation. The client SHOULD just issue the Print-URI request.
+
+The Printer object returns the same status codes, Operation Attributes
+(Group 1) and Unsupported Attributes (Group 2) as the Print-Job
+operation. However, no Job Object Attributes (Group 3) are returned,
+since no Job object is created.
+
+
+3.2.4 Create-Job Operation
+
+This OPTIONAL operation is similar to the Print-Job operation (section
+3.2.1) except that in the Create-Job request, a client does not supply
+document data or any reference to document data. Also, the client does
+not supply any of the "document-name", "document-format", "compression",
+or "document-natural-language" operation attributes. This operation is
+followed by one or more Send-Document or Send-URI operations. In each
+of those operation requests, the client OPTIONALLY supplies the
+"document-name", "document-format", and "document-natural-language"
+attributes for each document in the multi-document Job object.
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 44]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+If a Printer object supports the Create-Job operation, it MUST also
+support the Send-Document operation and also MAY support the Send-URI
+operation.
+
+If the Printer object supports this operation, it MUST support the
+"multiple-operation-time-out" Printer attribute (see section 4.4.31).
+
+If the Printer object supports this operation, then it MUST support the
+"multiple-document-jobs-supported" Printer Description attribute (see
+section 4.4.16) and indicate whether or not it supports multiple-
+document jobs.
+
+If the Printer object supports this operation and supports multiple
+documents in a job, then it MUST support the "multiple-document-
+handling" Job Template job attribute with at least one value (see
+section 4.2.4) and the associated "multiple-document-handling-default"
+and "multiple-document-handling-supported" Job Template Printer
+attributes (see section 4.2).
+
+After the Create-Job operation has completed, the value of the "job-
+state" attribute is similar to the "job-state" after a Print-Job, even
+though no document-data has arrived. A Printer MAY set the 'job-data-
+insufficient' value of the job's "job-state-reason" attribute to
+indicate that processing cannot begin until sufficient data has arrived
+and set the "job-state" to either 'pending' or 'pending-held'. A non-
+spooling printer that doesn't implement the 'pending' job state may even
+set the "job-state" to 'processing', even though there is not yet any
+data to process. See sections 4.3.7 and 4.3.8.
+
+
+3.2.5 Get-Printer-Attributes Operation
+
+This REQUIRED operation allows a client to request the values of the
+attributes of a Printer object. In the request, the client supplies
+the set of Printer attribute names and/or attribute group names in which
+the requester is interested. In the response, the Printer object
+returns a corresponding attribute set with the appropriate attribute
+values filled in.
+
+For Printer objects, the possible names of attribute groups are:
+
+ - 'job-template': the subset of the Job Template attributes that
+ apply to a Printer object (the last two columns of the table in
+ Section 4.2) that the implementation supports for Printer objects.
+ - 'printer-description': the subset of the attributes specified in
+ Section 4.4 that the implementation supports for Printer objects.
+ - 'all': the special group 'all' that includes all attributes that
+ the implementation supports for Printer objects.
+
+
+Since a client MAY request specific attributes or named groups, there is
+a potential that there is some overlap. For example, if a client
+requests, 'printer-name' and 'all', the client is actually requesting
+the "printer-name" attribute twice: once by naming it explicitly, and
+once by inclusion in the 'all' group. In such cases, the Printer object
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 45]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+NEED NOT return each attribute only once in the response even if it is
+requested multiple times. The client SHOULD NOT request the same
+attribute in multiple ways.
+
+It is NOT REQUIRED that a Printer object support all attributes
+belonging to a group (since some attributes are OPTIONAL). However, it
+is REQUIRED that each Printer object support all group names.
+
+
+3.2.5.1 Get-Printer-Attributes Request
+
+The following sets of attributes are part of the Get-Printer-Attributes
+Request:
+
+Group 1: Operation Attributes
+
+ Natural Language and Character Set:
+ The "attributes-charset" and "attributes-natural-language"
+ attributes as described in section 3.1.4.1.
+
+ Target:
+ The "printer-uri" (uri) operation attribute which is the target for
+ this operation as described in section 3.1.5.
+
+ Requesting User Name:
+ The "requesting-user-name" (name(MAX)) attribute SHOULD be supplied
+ by the client as described in section 8.3.
+
+ "requested-attributes" (1setOf keyword) :
+ The client OPTIONALLY supplies a set of attribute names and/or
+ attribute group names in whose values the requester is interested.
+ The Printer object MUST support this attribute. If the client
+ omits this attribute, the Printer MUST respond as if this attribute
+ had been supplied with a value of 'all'.
+
+ "document-format" (mimeMediaType) :
+ The client OPTIONALLY supplies this attribute. The Printer object
+ MUST support this attribute. This attribute is useful for a
+ Printer object to determine the set of supported attribute values
+ that relate to the requested document format. The Printer object
+ MUST return the attributes and values that it uses to validate a
+ job on a create or Validate-Job operation in which this document
+ format is supplied. The Printer object SHOULD return only (1) those
+ attributes that are supported for the specified format and (2) the
+ attribute values that are supported for the specified document
+ format. By specifying the document format, the client can get the
+ Printer object to eliminate the attributes and values that are not
+ supported for a specific document format. For example, a Printer
+ object might have multiple interpreters to support both
+ 'application/postscript' (for PostScript) and 'text/plain' (for
+ text) documents. However, for only one of those interpreters might
+ the Printer object be able to support "number-up" with values of
+ '1', '2', and '4'. For the other interpreter it might be able to
+ only support "number-up" with a value of '1'. Thus a client can use
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 46]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+ the Get-Printer-Attributes operation to obtain the attributes and
+ values that will be used to accept/reject a create job operation.
+
+ If the Printer object does not distinguish between different sets
+ of supported values for each different document format when
+ validating jobs in the create and Validate-Job operations, it MUST
+ NOT distinguish between different document formats in the Get-
+ Printer-Attributes operation. If the Printer object does
+ distinguish between different sets of supported values for each
+ different document format specified by the client, this
+ specialization applies only to the following Printer object
+ attributes:
+
+ - Printer attributes that are Job Template attributes ("xxx-
+ default" "xxx-supported", and "xxx-ready" in the Table in
+ Section 4.2),
+ - "pdl-override-supported",
+ - "compression-supported",
+ - "job-k-octets-supported",
+ - "job-impressions-supported,
+ - "job-media-sheets-supported"
+ - "printer-driver-installer",
+ - "color-supported", and
+ - "reference-uri-schemes-supported"
+
+ The values of all other Printer object attributes (including
+ "document-format-supported") remain invariant with respect to the
+ client supplied document format (except for new Printer description
+ attribute as registered according to section 6.2).
+
+ If the client omits this "document-format" operation attribute, the
+ Printer object MUST respond as if the attribute had been supplied
+ with the value of the Printer object's "document-format-default"
+ attribute. It is recommended that the client always supply a value
+ for "document-format", since the Printer object's "document-format-
+ default" may be 'application/octet-stream', in which case the
+ returned attributes and values are for the union of the document
+ formats that the Printer can automatically sense. For more
+ details, see the description of the 'mimeMediaType' attribute
+ syntax in section 4.1.9.
+
+ If the client supplies a value for the "document-format" Operation
+ attribute that is not supported by the Printer, i.e., is not among
+ the values of the Printer object's "document-format-supported"
+ attribute, the Printer object MUST reject the operation and return
+ the 'client-error-document-format-not-supported' status code.
+
+
+3.2.5.2 Get-Printer-Attributes Response
+
+The Printer object returns the following sets of attributes as part of
+the Get-Printer-Attributes Response:
+
+Group 1: Operation Attributes
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 47]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+ Status Message:
+ In addition to the REQUIRED status code returned in every response,
+ the response OPTIONALLY includes a "status-message" (text(255))
+ and/or a "detailed-status-message" (text(MAX)) operation attribute
+ as described in sections 13 and 3.1.6.
+
+ Natural Language and Character Set:
+ The "attributes-charset" and "attributes-natural-language"
+ attributes as described in section 3.1.4.2.
+
+
+Group 2: Unsupported Attributes
+
+ See section 3.1.7 for details on returning Unsupported Attributes.
+
+ The response NEED NOT contain the "requested-attributes" operation
+ attribute with any supplied values (attribute keywords) that were
+ requested by the client but are not supported by the IPP object. If
+ the Printer object does include unsupported attributes referenced
+ in "requested-attributes" and such attributes include group names,
+ such as 'all', the unsupported attributes MUST NOT include
+ attributes described in the standard but not supported by the
+ implementation.
+
+
+Group 3: Printer Object Attributes
+
+ This is the set of requested attributes and their current values.
+ The Printer object ignores (does not respond with) any requested
+ attribute which is not supported. The Printer object MAY respond
+ with a subset of the supported attributes and values, depending on
+ the security policy in force. However, the Printer object MUST
+ respond with the 'unknown' value for any supported attribute
+ (including all REQUIRED attributes) for which the Printer object
+ does not know the value. Also the Printer object MUST respond with
+ the 'no-value' for any supported attribute (including all REQUIRED
+ attributes) for which the system administrator has not configured a
+ value. See the description of the "out-of-band" values in the
+ beginning of Section 4.1.
+
+
+3.2.6 Get-Jobs Operation
+
+This REQUIRED operation allows a client to retrieve the list of Job
+objects belonging to the target Printer object. The client may also
+supply a list of Job attribute names and/or attribute group names. A
+group of Job object attributes will be returned for each Job object that
+is returned.
+
+This operation is similar to the Get-Job-Attributes operation, except
+that this Get-Jobs operation returns attributes from possibly more than
+one object (see the description of Job attribute group names in section
+3.3.4).
+
+
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 48]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+3.2.6.1 Get-Jobs Request
+
+The client submits the Get-Jobs request to a Printer object.
+
+The following groups of attributes are part of the Get-Jobs Request:
+
+Group 1: Operation Attributes
+
+ Natural Language and Character Set:
+ The "attributes-charset" and "attributes-natural-language"
+ attributes as described in section 3.1.4.1.
+
+ Target:
+ The "printer-uri" (uri) operation attribute which is the target for
+ this operation as described in section 3.1.5.
+
+ Requesting User Name:
+ The "requesting-user-name" (name(MAX)) attribute SHOULD be supplied
+ by the client as described in section 8.3.
+
+ "limit" (integer(1:MAX)):
+ The client OPTIONALLY supplies this attribute. The Printer object
+ MUST support this attribute. It is an integer value that determines
+ the maximum number of jobs that a client will receive from the
+ Printer even if "which-jobs" or "my-jobs" constrain which jobs are
+ returned. The limit is a "stateless limit" in that if the value
+ supplied by the client is 'N', then only the first 'N' jobs are
+ returned in the Get-Jobs Response. There is no mechanism to allow
+ for the next 'M' jobs after the first 'N' jobs. If the client does
+ not supply this attribute, the Printer object responds with all
+ applicable jobs.
+
+ "requested-attributes" (1setOf keyword):
+ The client OPTIONALLY supplies this attribute. The Printer object
+ MUST support this attribute. It is a set of Job attribute names
+ and/or attribute groups names in whose values the requester is
+ interested. This set of attributes is returned for each Job object
+ that is returned. The allowed attribute group names are the same
+ as those defined in the Get-Job-Attributes operation in section
+ 3.3.4. If the client does not supply this attribute, the Printer
+ MUST respond as if the client had supplied this attribute with two
+ values: 'job-uri' and 'job-id'.
+
+ "which-jobs" (keyword):
+ The client OPTIONALLY supplies this attribute. The Printer object
+ MUST support this attribute. It indicates which Job objects MUST
+ be returned by the Printer object. The values for this attribute
+ are:
+
+ 'completed': This includes any Job object whose state is
+ 'completed', 'canceled', or 'aborted'.
+ 'not-completed': This includes any Job object whose state is
+ 'pending', 'processing', 'processing-stopped', or 'pending-
+ held'.
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 49]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+
+ A Printer object MUST support both values. However, if the
+ implementation does not keep jobs in the 'completed', 'canceled',
+ and 'aborted' states, then it returns no jobs when the 'completed'
+ value is supplied.
+
+ If a client supplies some other value, the Printer object MUST copy
+ the attribute and the unsupported value to the Unsupported
+ Attributes response group, reject the request, and return the
+ 'client-error-attributes-or-values-not-supported' status code.
+
+ If the client does not supply this attribute, the Printer object
+ MUST respond as if the client had supplied the attribute with a
+ value of 'not-completed'.
+
+ "my-jobs" (boolean):
+ The client OPTIONALLY supplies this attribute. The Printer object
+ MUST support this attribute. It indicates whether jobs from all
+ users or just the jobs submitted by the requesting user of this
+ request MUST be returned by the Printer object. If the client does
+ not supply this attribute, the Printer object MUST respond as if
+ the client had supplied the attribute with a value of 'false',
+ i.e., jobs from all users. The means for authenticating the
+ requesting user and matching the jobs is described in section 8.
+
+3.2.6.2 Get-Jobs Response
+
+The Printer object returns all of the Job objects up to the number
+specified by the "limit" attribute that match the criteria as defined by
+the attribute values supplied by the client in the request. It is
+possible that no Job objects are returned since there may literally be
+no Job objects at the Printer, or there may be no Job objects that match
+the criteria supplied by the client. If the client requests any Job
+attributes at all, there is a set of Job Object Attributes returned for
+each Job object.
+
+It is not an error for the Printer to return 0 jobs. If the response
+returns 0 jobs because there are no jobs matching the criteria, and the
+request would have returned 1 or more jobs with a status code of
+'successful-ok' if there had been jobs matching the criteria, then the
+status code for 0 jobs MUST be 'successful-ok'.
+
+Group 1: Operation Attributes
+
+ Status Message:
+ In addition to the REQUIRED status code returned in every response,
+ the response OPTIONALLY includes a "status-message" (text(255))
+ and/or a "detailed-status-message" (text(MAX)) operation attribute
+ as described in sections 13 and 3.1.6.
+
+ Natural Language and Character Set:
+ The "attributes-charset" and "attributes-natural-language"
+ attributes as described in section 3.1.4.2.
+
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 50]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+Group 2: Unsupported Attributes
+
+ See section 3.1.7 for details on returning Unsupported Attributes.
+
+ The response NEED NOT contain the "requested-attributes" operation
+ attribute with any supplied values (attribute keywords) that were
+ requested by the client but are not supported by the IPP object. If
+ the Printer object does include unsupported attributes referenced
+ in "requested-attributes" and such attributes include group names,
+ such as 'all', the unsupported attributes MUST NOT include
+ attributes described in the standard but not supported by the
+ implementation.
+
+
+Groups 3 to N: Job Object Attributes
+
+ The Printer object responds with one set of Job Object Attributes
+ for each returned Job object. The Printer object ignores (does not
+ respond with) any requested attribute or value which is not
+ supported or which is restricted by the security policy in force,
+ including whether the requesting user is the user that submitted
+ the job (job originating user) or not (see section 8). However,
+ the Printer object MUST respond with the 'unknown' value for any
+ supported attribute (including all REQUIRED attributes) for which
+ the Printer object does not know the value, unless it would violate
+ the security policy. See the description of the "out-of-band"
+ values in the beginning of Section 4.1.
+
+
+ Jobs are returned in the following order:
+
+ - If the client requests all 'completed' Jobs (Jobs in the
+ 'completed', 'aborted', or 'canceled' states), then the Jobs
+ are returned newest to oldest (with respect to actual
+ completion time)
+ - If the client requests all 'not-completed' Jobs (Jobs in the
+ 'pending', 'processing', 'pending-held', and 'processing-
+ stopped' states), then Jobs are returned in relative
+ chronological order of expected time to complete (based on
+ whatever scheduling algorithm is configured for the Printer
+ object).
+
+3.2.7 Pause-Printer Operation
+
+This OPTIONAL operation allows a client to stop the Printer object from
+scheduling jobs on all its devices. Depending on implementation, the
+Pause-Printer operation MAY also stop the Printer from processing the
+current job or jobs. Any job that is currently being printed is either
+stopped as soon as the implementation permits or is completed, depending
+on implementation. The Printer object MUST still accept create
+operations to create new jobs, but MUST prevent any jobs from entering
+the 'processing' state.
+
+If the Pause-Printer operation is supported, then the Resume-Printer
+operation MUST be supported, and vice-versa.
+
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 51]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+The IPP Printer stops the current job(s) on its device(s) that were in
+the 'processing' or 'processing-stopped' states as soon as the
+implementation permits. If the implementation will take appreciable
+time to stop, the IPP Printer adds the 'moving-to-paused' value to the
+Printer object's "printer-state-reasons" attribute (see section 4.4.12).
+When the device(s) have all stopped, the IPP Printer transitions the
+Printer object to the 'stopped' state, removes the 'moving-to-paused'
+value, if present, and adds the 'paused' value to the Printer object's
+"printer-state-reasons" attribute.
+
+When the current job(s) complete that were in the 'processing' state,
+the IPP Printer transitions them to the 'completed' state. When the
+current job(s) stop in mid processing that were in the 'processing'
+state, the IPP Printer transitions them to the 'processing-stopped'
+state and adds the 'printer-stopped' value to the job's "job-state-
+reasons" attribute.
+
+For any jobs that are 'pending' or 'pending-held', the 'printer-stopped'
+value of the jobs' "job-state-reasons" attribute also applies. However,
+the IPP Printer NEED NOT update those jobs' "job-state-reasons"
+attributes and only need return the 'printer-stopped' value when those
+jobs are queried (so-called "lazy evaluation").
+
+Whether the Pause-Printer operation affects jobs that were submitted to
+the device from other sources than the IPP Printer object in the same
+way that the Pause-Printer operation affects jobs that were submitted to
+the IPP Printer object using IPP, depends on implementation, i.e., on
+whether the IPP protocol is being used as a universal management
+protocol or just to manage IPP jobs, respectively.
+
+The IPP Printer MUST accept the request in any state and transition the
+Printer to the indicated new "printer-state" before returning as
+follows:
+
+ Current New "printer IPP Printer's response status
+ "printer- "printer- -state- code and action:
+ state" state" reasons"
+
+ 'idle' 'stopped' 'paused' 'successful-ok'
+ 'processing' 'processing' 'moving- OPTION 1: 'successful-ok';
+ to- Later, when all output has
+ paused' stopped, the "printer-state"
+ becomes 'stopped', and the
+ 'paused' value replaces the
+ 'moving-to-paused' value in the
+ "printer-state-reasons"
+ attribute
+ 'processing' 'stopped' 'paused' OPTION 2: 'successful-ok';
+ all device output stopped
+ immediately
+ 'stopped' 'stopped' 'paused' 'successful-ok'
+
+Access Rights: The authenticated user (see section 8.3) performing this
+operation must be an operator or administrator of the Printer object
+(see Sections 1 and 8.5). Otherwise, the IPP Printer MUST reject the
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 52]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+operation and return: 'client-error-forbidden', 'client-error-not-
+authenticated', or 'client-error-not-authorized' as appropriate.
+
+
+3.2.7.1 Pause-Printer Request
+
+The following groups of attributes are part of the Pause-Printer
+Request:
+
+Group 1: Operation Attributes
+
+ Natural Language and Character Set:
+ The "attributes-charset" and "attributes-natural-language"
+ attributes as described in section 3.1.4.1.
+
+ Target:
+ The "printer-uri" (uri) operation attribute which is the target for
+ this operation as described in section 3.1.5.
+
+ Requesting User Name:
+ The "requesting-user-name" (name(MAX)) attribute SHOULD be supplied
+ by the client as described in section 8.3.
+
+3.2.7.2 Pause-Printer Response
+
+The following groups of attributes are part of the Pause-Printer
+Response:
+
+Group 1: Operation Attributes
+
+ Status Message:
+ In addition to the REQUIRED status code returned in every response,
+ the response OPTIONALLY includes a "status-message" (text(255))
+ and/or a "detailed-status-message" (text(MAX)) operation attribute
+ as described in sections 13 and 3.1.6.
+
+ Natural Language and Character Set:
+ The "attributes-charset" and "attributes-natural-language"
+ attributes as described in section 3.1.4.2.
+
+
+Group 2: Unsupported Attributes
+
+ See section 3.1.7 for details on returning Unsupported Attributes.
+
+
+3.2.8 Resume-Printer Operation
+
+This operation allows a client to resume the Printer object scheduling
+jobs on all its devices. The Printer object MUST remove the 'paused'
+and 'moving-to-paused' values from the Printer object's "printer-state-
+reasons" attribute, if present. If there are no other reasons to keep a
+device paused (such as media-jam), the IPP Printer transitions itself to
+the 'processing' or 'idle' states, depending on whether there are jobs
+to be processed or not, respectively, and the device(s) resume
+processing jobs.
+
+
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 53]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+If the Pause-Printer operation is supported, then the Resume-Printer
+operation MUST be supported, and vice-versa.
+
+The IPP Printer removes the 'printer-stopped' value from any job's "job-
+state-reasons" attributes contained in that Printer.
+
+The IPP Printer MUST accept the request in any state, transition the
+Printer object to the indicated new state as follows:
+
+ Current New "printer- IPP Printer's response status code and
+ "printer- state" action:
+ state"
+
+ 'idle' 'idle' 'successful-ok'
+ 'processing 'processing' 'successful-ok'
+ '
+ 'stopped' 'processing' 'successful-ok';
+ when there are jobs to be processed
+ 'stopped' 'idle' 'successful-ok';
+ when there are no jobs to be processed.
+
+Access Rights: The authenticated user (see section 8.3) performing this
+operation must be an operator or administrator of the Printer object
+(see Sections 1 and 8.5). Otherwise, the IPP Printer MUST reject the
+operation and return: 'client-error-forbidden', 'client-error-not-
+authenticated', or 'client-error-not-authorized' as appropriate.
+
+The Resume-Printer Request and Resume-Printer Response have the same
+attribute groups and attributes as the Pause-Printer operation (see
+sections 3.2.7.1 and 3.2.7.2).
+
+
+3.2.9 Purge-Jobs Operation
+
+This OPTIONAL operation allows a client to remove all jobs from an IPP
+Printer object, regardless of their job states, including jobs in the
+Printer object's Job History (see Section 4.3.7.2). After a Purge-Jobs
+operation has been performed, a Printer object MUST return no jobs in
+subsequent Get-Job-Attributes and Get-Jobs responses (until new jobs are
+submitted).
+
+Whether the Purge-Jobs (and Get-Jobs) operation affects jobs that were
+submitted to the device from other sources than the IPP Printer object
+in the same way that the Purge-Jobs operation affects jobs that were
+submitted to the IPP Printer object using IPP, depends on
+implementation, i.e., on whether the IPP protocol is being used as a
+universal management protocol or just to manage IPP jobs, respectively.
+
+Note: if an operator wants to cancel all jobs without clearing out the
+Job History, the operator uses the Cancel-Job operation on each job
+instead of using the Purge-Job operation.
+
+The Printer object MUST accept this operation in any state and
+transition the Printer object to the 'idle' state.
+
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 54]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+Access Rights: The authenticated user (see section 8.3) performing this
+operation must be an operator or administrator of the Printer object
+(see Sections 1 and 8.5). Otherwise, the IPP object MUST reject the
+operation and return: client-error-forbidden, client-error-not-
+authenticated, and client-error-not-authorized as appropriate.
+
+The Purge-Jobs Request and Purge-Jobs Response have the same attribute
+groups and attributes as the Pause-Printer operation (see sections
+3.2.7.1 and 3.2.7.2).
+
+
+
+3.3 Job Operations
+
+
+All Job operations are directed at Job objects. A client MUST always
+supply some means of identifying the Job object in order to identify the
+correct target of the operation. That job identification MAY either be
+a single Job URI or a combination of a Printer URI with a Job ID. The
+IPP object implementation MUST support both forms of identification for
+every job.
+
+
+3.3.1 Send-Document Operation
+
+This OPTIONAL operation allows a client to create a multi-document Job
+object that is initially "empty" (contains no documents). In the
+Create-Job response, the Printer object returns the Job object's URI
+(the "job-uri" attribute) and the Job object's 32-bit identifier (the
+"job-id" attribute). For each new document that the client desires to
+add, the client uses a Send-Document operation. Each Send-Document
+Request contains the entire stream of document data for one document.
+
+If the Printer supports this operation but does not support multiple
+documents per job, the Printer MUST reject subsequent Send-Document
+operations supplied with data and return the 'server-error-multiple-
+document-jobs-not-supported'. However, the Printer MUST accept the
+first document with a 'true' or 'false' value for the "last-document"
+operation attribute (see below), so that clients MAY always submit one
+document jobs with a 'false' value for "last-document" in the first
+Send-Document and a 'true' for "last-document" in the second Send-
+Document (with no data).
+
+Since the Create-Job and the send operations (Send-Document or Send-URI
+operations) that follow could occur over an arbitrarily long period of
+time for a particular job, a client MUST send another send operation
+within an IPP Printer defined minimum time interval after the receipt of
+the previous request for the job. If a Printer object supports multiple
+document jobs, the Printer object MUST support the "multiple-operation-
+time-out" attribute (see section 4.4.31). This attribute indicates the
+minimum number of seconds the Printer object will wait for the next send
+operation before taking some recovery action.
+
+An IPP object MUST recover from an errant client that does not supply a
+send operation, sometime after the minimum time interval specified by
+
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 55]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+the Printer object's "multiple-operation-time-out" attribute. Such
+recovery MAY include any of the following or other recovery actions:
+
+ 1. Assume that the Job is an invalid job, start the process of
+ changing the job state to 'aborted', add the 'aborted-by-system'
+ value to the job's "job-state-reasons" attribute (see section
+ 4.3.8), and clean up all resources associated with the Job. In
+ this case, if another send operation is finally received, the
+ Printer responds with an "client-error-not-possible" or "client-
+ error-not-found" depending on whether or not the Job object is
+ still around when the send operation finally arrives.
+ 2. Assume that the last send operation received was in fact the last
+ document (as if the "last-document" flag had been set to 'true'),
+ close the Job object, and proceed to process it (i.e., move the
+ Job's state to 'pending').
+ 3. Assume that the last send operation received was in fact the last
+ document, close the Job, but move it to the 'pending-held' and add
+ the 'submission-interrupted' value to the job's "job-state-reasons"
+ attribute (see section 4.3.8). This action allows the user or an
+ operator to determine whether to continue processing the Job by
+ moving it back to the 'pending' state using the Release-Job
+ operation (see section 3.3.6) or to cancel the job using the
+ Cancel-Job operation (see section 3.3.3).
+
+
+Each implementation is free to decide the "best" action to take
+depending on local policy, whether any documents have been added,
+whether the implementation spools jobs or not, and/or any other piece
+of information available to it. If the choice is to abort the Job
+object, it is possible that the Job object may already have been
+processed to the point that some media sheet pages have been printed.
+
+Access Rights: The authenticated user (see section 8.3) performing this
+operation must either be the job owner (as determined in the Create-Job
+operation) or an operator or administrator of the Printer object (see
+Sections 1 and 8.5). Otherwise, the IPP object MUST reject the
+operation and return: 'client-error-forbidden', 'client-error-not-
+authenticated', or 'client-error-not-authorized' as appropriate.
+
+
+3.3.1.1 Send-Document Request
+
+The following attribute sets are part of the Send-Document Request:
+
+Group 1: Operation Attributes
+
+ Natural Language and Character Set:
+ The "attributes-charset" and "attributes-natural-language"
+ attributes as described in section 3.1.4.1.
+
+ Target:
+ Either (1) the "printer-uri" (uri) plus "job-id" (integer(1:MAX))or
+ (2) the "job-uri" (uri) operation attribute(s) which define the
+ target for this operation as described in section 3.1.5.
+
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 56]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+ Requesting User Name:
+ The "requesting-user-name" (name(MAX)) attribute SHOULD be supplied
+ by the client as described in section 8.3.
+
+ "document-name" (name(MAX)):
+ The client OPTIONALLY supplies this attribute. The Printer object
+ MUST support this attribute. It contains the client supplied
+ document name. The document name MAY be different than the Job
+ name. It might be helpful, but NEED NOT be unique across multiple
+ documents in the same Job. Typically, the client software
+ automatically supplies the document name on behalf of the end user
+ by using a file name or an application generated name. See the
+ description of the "document-name" operation attribute in the
+ Print-Job Request (section 3.2.1.1) for more information about this
+ attribute.
+
+ "compression" (type3 keyword)
+ See the description of "compression" for the Print-Job operation in
+ Section 3.2.1.1.
+
+ "document-format" (mimeMediaType) :
+ See the description of "document-format" for the Print-Job
+ operation in Section 3.2.1.1.
+
+ "document-natural-language" (naturalLanguage):
+ The client OPTIONALLY supplies this attribute. The Printer object
+ OPTIONALLY supports this attribute. This attribute specifies the
+ natural language of the document for those document-formats that
+ require a specification of the natural language in order to image
+ the document unambiguously. There are no particular values
+ required for the Printer object to support.
+
+
+ "last-document" (boolean):
+ The client MUST supply this attribute. The Printer object MUST
+ support this attribute. It is a boolean flag that is set to 'true'
+ if this is the last document for the Job, 'false' otherwise.
+
+
+Group 2: Document Content
+
+ The client MUST supply the document data if the "last-document"
+ flag is set to 'false'. However, since a client might not know
+ that the previous document sent with a Send-Document (or Send-URI)
+ operation was the last document (i.e., the "last-document"
+ attribute was set to 'false'), it is legal to send a Send-Document
+ request with no document data where the "last-document" flag is set
+ to 'true'. Such a request MUST NOT increment the value of the Job
+ object's "number-of-documents" attribute, since no real document
+ was added to the job.
+
+3.3.1.2 Send-Document Response
+
+The following sets of attributes are part of the Send-Document Response:
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 57]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+Group 1: Operation Attributes
+
+ Status Message:
+ In addition to the REQUIRED status code returned in every response,
+ the response OPTIONALLY includes a "status-message" (text(255))
+ and/or a "detailed-status-message" (text(MAX)) operation attribute
+ as described in sections 13 and 3.1.6.
+
+ Natural Language and Character Set:
+ The "attributes-charset" and "attributes-natural-language"
+ attributes as described in section 3.1.4.2.
+
+
+Group 2: Unsupported Attributes
+
+ See section 3.1.7 for details on returning Unsupported Attributes.
+
+Group 3: Job Object Attributes
+
+ This is the same set of attributes as described in the Print-Job
+ response (see section 3.2.1.2).
+
+
+3.3.2 Send-URI Operation
+
+This OPTIONAL operation is identical to the Send-Document operation (see
+section 3.3.1) except that a client MUST supply a URI reference
+("document-uri" operation attribute) rather than the document data
+itself. If a Printer object supports this operation, clients can use
+both Send-URI or Send-Document operations to add new documents to an
+existing multi-document Job object. However, if a client needs to
+indicate that the previous Send-URI or Send-Document was the last
+document, the client MUST use the Send-Document operation with no
+document data and the "last-document" flag set to 'true' (rather than
+using a Send-URI operation with no "document-uri" operation attribute).
+
+If a Printer object supports this operation, it MUST also support the
+Print-URI operation (see section 3.2.2).
+
+The Printer object MUST validate the syntax and URI scheme of the
+supplied URI before returning a response, just as in the Print-URI
+operation. The IPP Printer MAY validate the accessibility of the
+document as part of the operation or subsequently (see section 3.2.2).
+
+
+3.3.3 Cancel-Job Operation
+
+This REQUIRED operation allows a client to cancel a Print Job from the
+time the job is created up to the time it is completed, canceled, or
+aborted. Since a Job might already be printing by the time a Cancel-Job
+is received, some media sheet pages might be printed before the job is
+actually terminated.
+
+The IPP object MUST accept or reject the request based on the job's
+current state and transition the job to the indicated new state as
+follows:
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 58]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+
+ Current "job- New "job- IPP object's response status
+ state" state" code and action:
+
+ 'pending' 'canceled' 'successful-ok'
+ 'pending-held' 'canceled' 'successful-ok'
+ 'processing' 'canceled' 'successful-ok'
+ 'processing' 'processing' 'successful-ok' See Rule 1
+ 'processing' 'processing' 'client-error-not-possible'
+ See Rule 2
+ 'processing- 'canceled' 'successful-ok'
+ stopped'
+ 'processing- 'processing- 'successful-ok' See Rule 1
+ stopped' stopped'
+ 'processing- 'processing- 'client-error-not-possible'
+ stopped' stopped' See Rule 2
+ 'completed' 'completed' 'client-error-not-possible'
+ 'canceled' 'canceled' 'client-error-not-possible'
+ 'aborted' 'aborted' 'client-error-not-possible'
+
+Rule 1: If the implementation requires some measurable time to cancel
+the job in the 'processing' or 'processing-stopped' job states, the IPP
+object MUST add the 'processing-to-stop-point' value to the job's "job-
+state-reasons" attribute and then transition the job to the 'canceled'
+state when the processing ceases (see section 4.3.8).
+
+Rule 2: If the Job object already has the 'processing-to-stop-point'
+value in its "job-state-reasons" attribute, then the Printer object MUST
+reject a Cancel-Job operation.
+
+Access Rights: The authenticated user (see section 8.3) performing this
+operation must either be the job owner or an operator or administrator
+of the Printer object (see Sections 1 and 8.5). Otherwise, the IPP
+object MUST reject the operation and return: 'client-error-forbidden',
+'client-error-not-authenticated', or 'client-error-not-authorized' as
+appropriate.
+
+
+3.3.3.1 Cancel-Job Request
+
+The following groups of attributes are part of the Cancel-Job Request:
+
+Group 1: Operation Attributes
+
+ Natural Language and Character Set:
+ The "attributes-charset" and "attributes-natural-language"
+ attributes as described in section 3.1.4.1.
+
+ Target:
+ Either (1) the "printer-uri" (uri) plus "job-id"
+ (integer(1:MAX))or (2) the "job-uri" (uri) operation attribute(s)
+ which define the target for this operation as described in section
+ 3.1.5.
+
+
+
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 59]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+ Requesting User Name:
+ The "requesting-user-name" (name(MAX)) attribute SHOULD be supplied
+ by the client as described in section 8.3.
+
+ "message" (text(127)):
+ The client OPTIONALLY supplies this attribute. The Printer object
+ OPTIONALLY supports this attribute. It is a message to the
+ operator. This "message" attribute is not the same as the "job-
+ message-from-operator" attribute. That attribute is used to report
+ a message from the operator to the end user that queries that
+ attribute. This "message" operation attribute is used to send a
+ message from the client to the operator along with the operation
+ request. It is an implementation decision of how or where to
+ display this message to the operator (if at all).
+
+
+3.3.3.2 Cancel-Job Response
+
+The following sets of attributes are part of the Cancel-Job Response:
+
+Group 1: Operation Attributes
+
+ Status Message:
+ In addition to the REQUIRED status code returned in every response,
+ the response OPTIONALLY includes a "status-message" (text(255))
+ and/or a "detailed-status-message" (text(MAX)) operation attribute
+ as described in sections 13 and 3.1.6.
+
+ Natural Language and Character Set:
+ The "attributes-charset" and "attributes-natural-language"
+ attributes as described in section 3.1.4.2.
+
+
+Group 2: Unsupported Attributes
+
+ See section 3.1.7 for details on returning Unsupported Attributes.
+
+
+
+Once a successful response has been sent, the implementation guarantees
+that the Job will eventually end up in the 'canceled' state. Between the
+time of the Cancel-Job operation is accepted and when the job enters the
+'canceled' job-state (see section 4.3.7), the "job-state-reasons"
+attribute SHOULD contain the 'processing-to-stop-point' value which
+indicates to later queries that although the Job might still be
+'processing', it will eventually end up in the 'canceled' state, not the
+'completed' state.
+
+
+3.3.4 Get-Job-Attributes Operation
+
+This REQUIRED operation allows a client to request the values of
+attributes of a Job object and it is almost identical to the Get-
+Printer-Attributes operation (see section 3.2.5). The only differences
+are that the operation is directed at a Job object rather than a Printer
+object, there is no "document-format" operation attribute used when
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 60]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+querying a Job object, and the returned attribute group is a set of Job
+object attributes rather than a set of Printer object attributes.
+
+For Jobs, the possible names of attribute groups are:
+
+ - 'job-template': the subset of the Job Template attributes that
+ apply to a Job object (the first column of the table in Section
+ 4.2) that the implementation supports for Job objects.
+ - 'job-description': the subset of the Job Description attributes
+ specified in Section 4.3 that the implementation supports for Job
+ objects.
+ - 'all': the special group 'all' that includes all attributes that
+ the implementation supports for Job objects.
+
+
+Since a client MAY request specific attributes or named groups, there is
+a potential that there is some overlap. For example, if a client
+requests, 'job-name' and 'job-description', the client is actually
+requesting the "job-name" attribute once by naming it explicitly, and
+once by inclusion in the 'job-description' group. In such cases, the
+Printer object NEED NOT return the attribute only once in the response
+even if it is requested multiple times. The client SHOULD NOT request
+the same attribute in multiple ways.
+
+It is NOT REQUIRED that a Job object support all attributes belonging to
+a group (since some attributes are OPTIONAL). However it is REQUIRED
+that each Job object support all group names.
+
+
+3.3.4.1 Get-Job-Attributes Request
+
+The following groups of attributes are part of the Get-Job-Attributes
+Request when the request is directed at a Job object:
+
+Group 1: Operation Attributes
+
+ Natural Language and Character Set:
+ The "attributes-charset" and "attributes-natural-language"
+ attributes as described in section 3.1.4.1.
+
+ Target:
+ Either (1) the "printer-uri" (uri) plus "job-id" (integer(1:MAX))
+ or (2) the "job-uri" (uri) operation attribute(s) which define the
+ target for this operation as described in section 3.1.5.
+
+ Requesting User Name:
+ The "requesting-user-name" (name(MAX)) attribute SHOULD be supplied
+ by the client as described in section 8.3.
+
+ "requested-attributes" (1setOf keyword) :
+ The client OPTIONALLY supplies this attribute. The IPP object MUST
+ support this attribute. It is a set of attribute names and/or
+ attribute group names in whose values the requester is interested.
+ If the client omits this attribute, the IPP object MUST respond as
+ if this attribute had been supplied with a value of 'all'.
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 61]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+3.3.4.2 Get-Job-Attributes Response
+
+The Printer object returns the following sets of attributes as part of
+the Get-Job-Attributes Response:
+
+Group 1: Operation Attributes
+
+ Status Message:
+ In addition to the REQUIRED status code returned in every response,
+ the response OPTIONALLY includes a "status-message" (text(255))
+ and/or a "detailed-status-message" (text(MAX)) operation attribute
+ as described in sections 13 and 3.1.6.
+
+ Natural Language and Character Set:
+ The "attributes-charset" and "attributes-natural-language"
+ attributes as described in section 3.1.4.2. The "attributes-
+ natural-language" MAY be the natural language of the Job object,
+ rather than the one requested.
+
+
+
+Group 2: Unsupported Attributes
+
+ See section 3.1.7 for details on returning Unsupported Attributes.
+
+ The response NEED NOT contain the "requested-attributes" operation
+ attribute with any supplied values (attribute keywords) that were
+ requested by the client but are not supported by the IPP object. If
+ the Printer object does include unsupported attributes referenced
+ in "requested-attributes" and such attributes include group names,
+ such as 'all', the unsupported attributes MUST NOT include
+ attributes described in the standard but not supported by the
+ implementation.
+
+
+Group 3: Job Object Attributes
+
+ This is the set of requested attributes and their current values.
+ The IPP object ignores (does not respond with) any requested
+ attribute or value which is not supported or which is restricted by
+ the security policy in force, including whether the requesting user
+ is the user that submitted the job (job originating user) or not
+ (see section 8). However, the IPP object MUST respond with the
+ 'unknown' value for any supported attribute (including all REQUIRED
+ attributes) for which the IPP object does not know the value,
+ unless it would violate the security policy. See the description
+ of the "out-of-band" values in the beginning of Section 4.1.
+
+3.3.5 Hold-Job Operation
+
+This OPTIONAL operation allows a client to hold a pending job in the
+queue so that it is not eligible for scheduling. If the Hold-Job
+operation is supported, then the Release-Job operation MUST be
+supported, and vice-versa. The OPTIONAL "job-hold-until" operation
+attribute allows a client to specify whether to hold the job
+indefinitely or until a specified time period, if supported.
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 62]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+The IPP object MUST accept or reject the request based on the job's
+current state and transition the job to the indicated new state as
+follows:
+
+ Current "job- New "job-state" IPP object's response status
+ state" code and action:
+
+ 'pending' 'pending-held' 'successful-ok' See Rule 1
+ 'pending' 'pending' 'successful-ok' See Rule 2
+ 'pending-held' 'pending-held' 'successful-ok' See Rule 1
+ 'pending-held' 'pending' 'successful-ok' See Rule 2
+ 'processing' 'processing' 'client-error-not-possible'
+ 'processing- 'processing- 'client-error-not-possible'
+ stopped' stopped'
+ 'completed' 'completed' 'client-error-not-possible'
+ 'canceled' 'canceled' 'client-error-not-possible'
+ 'aborted' 'aborted' 'client-error-not-possible'
+
+Rule 1: If the implementation supports multiple reasons for a job to be
+in the 'pending-held' state, the IPP object MUST add the 'job-hold-
+until-specified' value to the job's "job-state-reasons" attribute.
+
+Rule 2: If the IPP object supports the "job-hold-until" operation
+attribute, but the specified time period has already started (or is the
+'no-hold' value) and there are no other reasons to hold the job, the IPP
+object MUST make the job be a candidate for processing immediately (see
+Section 4.2.2) by putting the job in the 'pending' state.
+
+Note: In order to keep the Hold-Job operation simple, such a request is
+rejected when the job is in the 'processing' or 'processing-stopped'
+states. If an operation is needed to hold jobs while in these states,
+it will be added as an additional operation, rather than overloading the
+Hold-Job operation. Then it is clear to clients by querying the Printer
+object's "operations-supported" (see Section 4.4.15) and the Job
+object's "job-state" (see Section 4.3.7) attributes which operations are
+possible.
+
+Access Rights: The authenticated user (see section 8.3) performing this
+operation must either be the job owner or an operator or administrator
+of the Printer object (see Sections 1 and 8.5). Otherwise, the IPP
+object MUST reject the operation and return: 'client-error-forbidden',
+'client-error-not-authenticated', or 'client-error-not-authorized' as
+appropriate.
+
+
+3.3.5.1 Hold-Job Request
+
+The groups and operation attributes are the same as for a Cancel-Job
+request (see section 3.3.3.1), with the addition of the following Group
+1 Operation attribute:
+
+ "job-hold-until" (type3 keyword | name(MAX)):
+ The client OPTIONALLY supplies this Operation attribute. The IPP
+ object MUST support this operation attribute in a Hold-Job request,
+ if it supports the "job-hold-until" Job template attribute in
+ create operations. See section 4.2.2. The IPP object SHOULD
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 63]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+ support the "job-hold-until" Job Template attribute for use in job
+ create operations with at least the 'indefinite' value, if it
+ supports the Hold-Job operation. Otherwise, a client cannot create
+ a job and hold it immediately (without picking some supported time
+ period in the future).
+
+ If supplied and supported as specified in the Printer's "job-hold-
+ until-supported" attribute, the IPP object copies the supplied
+ operation attribute to the Job object, replacing the job's previous
+ "job-hold-until" attribute, if present, and makes the job a
+ candidate for scheduling during the supplied named time period.
+
+ If supplied, but either the "job-hold-until" Operation attribute
+ itself or the value supplied is not supported, the IPP object
+ accepts the request, returns the unsupported attribute or value in
+ the Unsupported Attributes Group according to section 3.1.7,
+ returns the 'successful-ok-ignored-or-substituted-attributes, and
+ holds the job indefinitely until a client performs a subsequent
+ Release-Job operation.
+
+ If the client (1) supplies a value that specifies a time period
+ that has already started or the 'no-hold' value (meaning don't hold
+ the job) and (2) the IPP object supports the "job-hold-until"
+ operation attribute and there are no other reasons to hold the job,
+ the IPP object MUST accept the operation and make the job be a
+ candidate for processing immediately (see Section 4.2.2).
+
+ If the client does not supply a "job-hold-until" Operation
+ attribute in the request, the IPP object MUST populate the job
+ object with a "job-hold-until" attribute with the 'indefinite'
+ value (if IPP object supports the "job-hold-until" attribute) and
+ hold the job indefinitely, until a client performs a Release-Job
+ operation.
+
+
+3.3.5.2 Hold-Job Response
+
+The groups and attributes are the same as for a Cancel-Job response (see
+section 3.3.3.2).
+
+
+3.3.6 Release-Job Operation
+
+This OPTIONAL operation allows a client to release a previously held job
+so that it is again eligible for scheduling. If the Hold-Job operation
+is supported, then the Release-Job operation MUST be supported, and
+vice-versa.
+
+This operation removes the "job-hold-until" job attribute, if present,
+from the job object that had been supplied in the create or most recent
+Hold-Job or Restart-Job operation and removes its effect on the job.
+The IPP object MUST remove the 'job-hold-until-specified' value from the
+job's "job-state-reasons" attribute, if present. See section 4.3.8.
+
+
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 64]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+The IPP object MUST accept or reject the request based on the job's
+current state and transition the job to the indicated new state as
+follows:
+
+ Current "job- New "job-state" IPP object's response status
+ state" code and action:
+
+ 'pending' 'pending' 'successful-ok'
+ No effect on the job.
+ 'pending-held' 'pending-held' 'successful-ok' See Rule 1
+ 'pending-held' 'pending' 'successful-ok'
+ 'processing' 'processing' 'successful-ok'
+ No effect on the job.
+ 'processing- 'processing- 'successful-ok'
+ stopped' stopped' No effect on the job.
+ 'completed' 'completed' 'client-error-not-possible'
+ 'canceled' 'canceled' 'client-error-not-possible'
+ 'aborted' 'aborted' 'client-error-not-possible'
+
+Rule 1: If there are other reasons to keep the job in the 'pending-
+held' state, such as 'resources-are-not-ready', the job remains in the
+'pending-held' state. Thus the 'pending-held' state is not just for
+jobs that have the 'job-hold-until' applied to them, but are for any
+reason to keep the job from being a candidate for scheduling and
+processing, such as 'resources-are-not-ready'. See the "job-hold-until"
+attribute (section 4.2.2).
+
+Access Rights: The authenticated user (see section 8.3) performing this
+operation must either be the job owner or an operator or administrator
+of the Printer object (see Sections 1 and 8.5). Otherwise, the IPP
+object MUST reject the operation and return: 'client-error-forbidden',
+'client-error-not-authenticated', or 'client-error-not-authorized' as
+appropriate.
+
+The Release-Job Request and Release-Job Response have the same attribute
+groups and attributes as the Cancel-Job operation (see section 3.3.3.1
+and 3.3.3.2).
+
+
+3.3.7 Restart-Job Operation
+
+This OPTIONAL operation allows a client to restart a job that is
+retained in the queue after processing has completed (see section
+4.3.7.2).
+
+The job is moved to the 'pending' job state and restarts at the
+beginning on the same IPP Printer object with the same attribute values.
+The Job Description attributes that accumulate job progress, such as
+"job-impressions-completed", "job-media-sheets-completed", and "job-k-
+octets-processed", MUST be reset to 0 so that they give an accurate
+record of the job from its restart point. The job object MUST continue
+to use the same "job-uri" and "job-id" attribute values.
+
+Note: If in the future an operation is needed that does not reset the
+job progress attributes, then a new operation will be defined which
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 65]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+makes a copy of the job, assigns a new "job-uri" and "job-id" to the
+copy and resets the job progress attributes in the new copy only.
+
+The IPP object MUST accept or reject the request based on the job's
+current state, transition the job to the indicated new state as follows:
+
+ Current "job- New "job-state" IPP object's response status
+ state" code and action:
+
+ 'pending' 'pending' 'client-error-not-possible'
+ 'pending-held' 'pending-held' 'client-error-not-possible'
+ 'processing' 'processing' 'client-error-not-possible'
+ 'processing- 'processing- 'client-error-not-possible'
+ stopped' stopped'
+ 'completed' 'pending' 'successful-ok' - job is started
+ over.
+ 'completed' 'completed' 'client-error-not-possible' -
+ see Rule 1
+ 'canceled' 'pending' 'successful-ok' - job is started
+ over.
+ 'canceled' 'canceled' 'client-error-not-possible' -
+ see Rule 1
+ 'aborted' 'pending' 'successful-ok' - job is started
+ over.
+ 'aborted' 'aborted' 'client-error-not-possible' -
+ see Rule 1
+
+
+Rule 1: If the Job Retention Period has expired for the job in this
+state, then the IPP object rejects the operation. See section 4.3.7.2.
+
+Note: In order to prevent a user from inadvertently restarting a job in
+the middle, the Restart-Job request is rejected when the job is in the
+'processing' or 'processing-stopped' states. If in the future an
+operation is needed to hold or restart jobs while in these states, it
+will be added as an additional operation, rather than overloading the
+Restart-Job operation, so that it is clear that the user intended that
+the current job not be completed.
+
+Access Rights: The authenticated user (see section 8.3) performing this
+operation must either be the job owner or an operator or administrator
+of the Printer object (see Sections 1 and 8.5). Otherwise, the IPP
+object MUST reject the operation and return: 'client-error-forbidden',
+'client-error-not-authenticated', or 'client-error-not-authorized' as
+appropriate.
+
+
+3.3.7.1 Restart-Job Request
+
+The groups and attributes are the same as for a Cancel-Job request (see
+section 3.3.3.1), with the addition of the following Group 1 Operation
+attribute:
+
+ "job-hold-until" (type3 keyword | name(MAX)):
+ The client OPTIONALLY supplies this attribute. The IPP object MUST
+ support this Operation attribute in a Restart-Job request, if it
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 66]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+ supports the "job-hold-until" Job Template attribute in create
+ operations. See section 4.2.2. Otherwise, the IPP object NEED NOT
+ support the "job-hold-until" Operation attribute in a Restart-Job
+ request.
+
+ If supplied and supported as specified in the Printer's "job-hold-
+ until-supported" attribute, the IPP object copies the supplied
+ Operation attribute to the Job object, replacing the job's previous
+ "job-hold-until" attribute, if present, and makes the job a
+ candidate for scheduling during the supplied named time period.
+ See section 4.2.2.
+
+ If supplied, but the value is not supported, the IPP object accepts
+ the request, returns the unsupported attribute or value in the
+ Unsupported Attributes Group according to section 3.1.7, returns
+ the 'successful-ok-ignored-or-substituted-attributes' status code,
+ and holds the job indefinitely until a client performs a subsequent
+ Release-Job operation.
+
+ If supplied, but the "job-hold-until" Operation attribute itself is
+ not supported, the IPP object accepts the request, returns the
+ unsupported attribute with the out-of-band 'unsupported' value in
+ the Unsupported Attributes Group according to section 3.1.7,
+ returns the 'successful-ok-ignored-or-substituted-attributes'
+ status code, and restarts the job, i.e., ignores the "job-hold-
+ until" attribute.
+
+ If the client (1) supplies a value that specifies a time period
+ that has already started or the 'no-hold' value (meaning don't hold
+ the job) and (2) the IPP object supports the "job-hold-until"
+ operation attribute and there are no other reasons to hold the job,
+ the IPP object makes the job a candidate for processing immediately
+ (see Section 4.2.2).
+
+ If the client does not supply a "job-hold-until" operation
+ attribute in the request, the IPP object removes the "job-hold-
+ until" attribute, if present, from the job. If there are no other
+ reasons to hold the job, the Restart-Job operation makes the job a
+ candidate for processing immediately (see Section 4.2.2).
+
+
+3.3.7.2 Restart-Job Response
+
+The groups and attributes are the same as for a Cancel-Job response (see
+section 3.3.3.2).
+
+Note: In the future an OPTIONAL Modify-Job or Set-Job-Attributes
+operation may be specified that allows the client to modify other
+attributes before releasing the restarted job.
+
+
+
+
+
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 67]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+4. Object Attributes
+
+
+This section describes the attributes with their corresponding attribute
+syntaxes and values that are part of the IPP model. The sections below
+show the objects and their associated attributes which are included
+within the scope of this protocol. Many of these attributes are derived
+from other relevant documents:
+
+ - Document Printing Application (DPA) [ISO10175]
+ - RFC 1759 Printer MIB [RFC1759]
+
+
+Each attribute is uniquely identified in this document using a "keyword"
+(see section 12.2.1) which is the name of the attribute. The keyword is
+included in the section header describing that attribute.
+
+Note: Not only are keywords used to identify attributes, but one of the
+attribute syntaxes described below is "keyword" so that some attributes
+have keyword values. Therefore, these attributes are defined as having
+an attribute syntax that is a set of keywords.
+
+
+4.1 Attribute Syntaxes
+
+
+This section defines the basic attribute syntax types that all clients
+and IPP objects MUST be able to accept in responses and accept in
+requests, respectively. Each attribute description in sections 3 and 4
+includes the name of attribute syntax(es) in the heading (in
+parentheses). A conforming implementation of an attribute MUST include
+the semantics of the attribute syntax(es) so identified. Section 6.3
+describes how the protocol can be extended with new attribute syntaxes.
+
+The attribute syntaxes are specified in the following sub-sections,
+where the sub-section heading is the keyword name of the attribute
+syntax inside the single quotes. In operation requests and responses
+each attribute value MUST be represented as one of the attribute
+syntaxes specified in the sub-section heading for the attribute. In
+addition, the value of an attribute in a response (but not in a request)
+MAY be one of the "out-of-band" values whose special encoding rules are
+defined in the "Encoding and Transport" document [IPP-PRO]. Standard
+"out-of-band" values are:
+
+ 'unknown': The attribute is supported by the IPP object, but the
+ value is unknown to the IPP object for some reason.
+ 'unsupported': The attribute is unsupported by the IPP object. This
+ value MUST be returned only as the value of an attribute in the
+ Unsupported Attributes Group.
+ 'no-value': The attribute is supported by the Printer object, but the
+ administrator has not yet configured a value.
+
+
+
+All attributes in a request MUST have one or more values as defined in
+Sections 4.2 to 4.4. Thus clients MUST NOT supply attributes with "out-
+of-band" values. All attributes in a response MUST have one or more
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 68]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+values as defined in Sections 4.2 to 4.4 or a single "out-of-band"
+value.
+
+Most attributes are defined to have a single attribute syntax. However,
+a few attributes (e.g., "job-sheet", "media", "job-hold-until") are
+defined to have several attribute syntaxes, depending on the value.
+These multiple attribute syntaxes are separated by the "|" character in
+the sub-section heading to indicate the choice. Since each value MUST
+be tagged as to its attribute syntax in the protocol, a single-valued
+attribute instance may have any one of its attribute syntaxes and a
+multi-valued attribute instance may have a mixture of its defined
+attribute syntaxes.
+
+
+4.1.1 'text'
+
+A text attribute is an attribute whose value is a sequence of zero or
+more characters encoded in a maximum of 1023 ('MAX') octets. MAX is the
+maximum length for each value of any text attribute. However, if an
+attribute will always contain values whose maximum length is much less
+than MAX, the definition of that attribute will include a qualifier that
+defines the maximum length for values of that attribute. For example:
+the "printer-location" attribute is specified as "printer-location
+(text(127))". In this case, text values for "printer-location" MUST NOT
+exceed 127 octets; if supplied with a longer text string via some
+external interface (other than the protocol), implementations are free
+to truncate to this shorter length limitation.
+
+In this document, all text attributes are defined using the 'text'
+syntax. However, 'text' is used only for brevity; the formal
+interpretation of 'text' is: 'textWithoutLanguage | textWithLanguage'.
+That is, for any attribute defined in this document using the 'text'
+attribute syntax, all IPP objects and clients MUST support both the
+'textWithoutLanguage' and 'textWithLanguage' attribute syntaxes.
+However, in actual usage and protocol execution, objects and clients
+accept and return only one of the two syntax per attribute. The syntax
+'text' never appears "on-the-wire".
+
+Both 'textWithoutLanguage' and 'textWithLanguage' are needed to support
+the real world needs of interoperability between sites and systems that
+use different natural languages as the basis for human communication.
+Generally, one natural language applies to all text attributes in a
+given request or response. The language is indicated by the "attributes-
+natural-language" operation attribute defined in section 3.1.4 or
+"attributes-natural-language" job attribute defined in section 4.3.20,
+and there is no need to identify the natural language for each text
+string on a value-by-value basis. In these cases, the attribute syntax
+'textWithoutLanguage' is used for text attributes. In other cases, the
+client needs to supply or the Printer object needs to return a text
+value in a natural language that is different from the rest of the text
+values in the request or response. In these cases, the client or
+Printer object uses the attribute syntax 'textWithLanguage' for text
+attributes (this is the Natural Language Override mechanism described in
+section 3.1.4).
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 69]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+The 'textWithoutLanguage' and 'textWithLanguage' attribute syntaxes are
+described in more detail in the following sections.
+
+
+4.1.1.1 'textWithoutLanguage'
+
+The 'textWithoutLanguage' syntax indicates a value that is sequence of
+zero or more characters. Text strings are encoded using the rules of
+some charset. The Printer object MUST support the UTF-8 charset
+[RFC2279] and MAY support additional charsets to represent 'text'
+values, provided that the charsets are registered with IANA [IANA-CS].
+See Section 4.1.7 for the definition of the 'charset' attribute syntax,
+including restricted semantics and examples of charsets.
+
+
+4.1.1.2 'textWithLanguage'
+
+The 'textWithLanguage' attribute syntax is a compound attribute syntax
+consisting of two parts: a 'textWithoutLanguage' part plus an additional
+'naturalLanguage' (see section 4.1.8) part that overrides the natural
+language in force. The 'naturalLanguage' part explicitly identifies the
+natural language that applies to the text part of that value and that
+value alone. For any give text attribute, the 'textWithoutLanguage'
+part is limited to the maximum length defined for that attribute, but
+the 'naturalLanguage' part is always limited to 63 octets. Using the
+'textWithLanguage' attribute syntax rather than the normal
+'textWithoutLanguage' syntax is the so-called Natural Language Override
+mechanism and MUST be supported by all IPP objects and clients.
+
+If the attribute is multi-valued (1setOf text), then the
+'textWithLanguage' attribute syntax MUST be used to explicitly specify
+each attribute value whose natural language needs to be overridden.
+Other values in a multi-valued 'text' attribute in a request or a
+response revert to the natural language of the operation attribute.
+
+In a create request, the Printer object MUST accept and store with the
+Job object any natural language in the "attributes-natural-language"
+operation attribute, whether the Printer object supports that natural
+language or not. Furthermore, the Printer object MUST accept and store
+any 'textWithLanguage' attribute value, whether the Printer object
+supports that natural language or not. These requirements are
+independent of the value of the "ipp-attribute-fidelity" operation
+attribute that the client MAY supply.
+
+Example: If the client supplies the "attributes-natural-language"
+operation attribute with the value: 'en' indicating English, but the
+value of the "job-name" attribute is in French, the client MUST use the
+'textWithLanguage' attribute syntax with the following two values:
+
+ 'fr': Natural Language Override indicating French
+ 'Rapport Mensuel': the job name in French
+
+
+See the "Encoding and Transport" document [IPP-PRO] for a detailed
+example of the 'textWithLanguage' attribute syntax.
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 70]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+4.1.2 'name'
+
+This syntax type is used for user-friendly strings, such as a Printer
+name, that, for humans, are more meaningful than identifiers. Names are
+never translated from one natural language to another. The 'name'
+attribute syntax is essentially the same as 'text', including the
+REQUIRED support of UTF-8 except that the sequence of characters is
+limited so that its encoded form MUST NOT exceed 255 (MAX) octets.
+
+Also like 'text', 'name' is really an abbreviated notation for either
+'nameWithoutLanguage' or 'nameWithLanguage'. That is, all IPP objects
+and clients MUST support both the 'nameWithoutLanguage' and
+'nameWithLanguage' attribute syntaxes. However, in actual usage and
+protocol execution, objects and clients accept and return only one of
+the two syntax per attribute. The syntax 'name' never appears "on-the-
+wire".
+
+Only the 'text' and 'name' attribute syntaxes permit the Natural
+Language Override mechanism.
+
+Some attributes are defined as 'type3 keyword | name'. These attributes
+support values that are either type3 keywords or names. This dual-
+syntax mechanism enables a site administrator to extend these attributes
+to legally include values that are locally defined by the site
+administrator. Such names are not registered with IANA.
+
+
+4.1.2.1 'nameWithoutLanguage'
+
+The nameWithoutLanguage' syntax indicates a value that is sequence of
+zero or more characters so that its encoded form does not exceed MAX
+octets.
+
+
+4.1.2.2 'nameWithLanguage'
+
+The 'nameWithLanguage' attribute syntax is a compound attribute syntax
+consisting of two parts: a 'nameWithoutLanguage' part plus an additional
+'naturalLanguage' (see section 4.1.8) part that overrides the natural
+language in force. The 'naturalLanguage' part explicitly identifies the
+natural language that applies to that name value and that name value
+alone.
+
+The 'nameWithLanguage' attribute syntax behaves the same as the
+'textWithLanguage' syntax. If a name is in a language that is different
+than the rest of the object or operation, then this 'nameWithLanguage'
+syntax is used rather than the generic 'nameWithoutLanguage' syntax.
+
+Example: If the client supplies the "attributes-natural-language"
+operation attribute with the value: 'en' indicating English, but the
+"printer-name" attribute is in German, the client MUST use the
+'nameWithLanguage' attribute syntax as follows:
+
+ 'de': Natural Language Override indicating German
+ 'Farbdrucker': the Printer name in German
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 71]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+
+
+4.1.2.3 Matching 'name' attribute values
+
+For purposes of matching two 'name' attribute values for equality, such
+as in job validation (where a client-supplied value for attribute "xxx"
+is checked to see if the value is among the values of the Printer
+object's corresponding "xxx-supported" attribute), the following match
+rules apply:
+
+ 1. 'keyword' values never match 'name' values.
+
+ 2. 'name' (nameWithoutLanguage and nameWithLanguage) values match
+ if (1) the name parts match and (2) the Associated Natural-Language
+ parts (see section 3.1.4.1) match. The matching rules are:
+
+ a. the name parts match if the two names are identical
+ character by character, except it is RECOMMENDED that case be
+ ignored. For example: 'Ajax-letter-head-white' MUST match
+ 'Ajax-letter-head-white' and SHOULD match 'ajax-letter-head-
+ white' and 'AJAX-LETTER-HEAD-WHITE'.
+
+ b. the Associated Natural-Language parts match if the shorter
+ of the two meets the syntactic requirements of RFC 1766
+ [RFC1766] and matches byte for byte with the longer. For
+ example, 'en' matches 'en', 'en-us' and 'en-gb', but matches
+ neither 'fr' nor 'e'.
+
+
+4.1.3 'keyword'
+
+The 'keyword' attribute syntax is a sequence of characters, length: 1 to
+255, containing only the US-ASCII [ASCII] encoded values for lowercase
+letters ("a" - "z"), digits ("0" - "9"), hyphen ("-"), dot ("."), and
+underscore ("_"). The first character MUST be a lowercase letter.
+Furthermore, keywords MUST be in U.S. English.
+
+This syntax type is used for enumerating semantic identifiers of
+entities in the abstract protocol, i.e., entities identified in this
+document. Keywords are used as attribute names or values of attributes.
+Unlike 'text' and 'name' attribute values, 'keyword' values MUST NOT use
+the Natural Language Override mechanism, since they MUST always be US-
+ASCII and U.S. English.
+
+Keywords are for use in the protocol. A user interface will likely
+provide a mapping between protocol keywords and displayable user-
+friendly words and phrases which are localized to the natural language
+of the user. While the keywords specified in this document MAY be
+displayed to users whose natural language is U.S. English, they MAY be
+mapped to other U.S. English words for U.S. English users, since the
+user interface is outside the scope of this document.
+
+In the definition for each attribute of this syntax type, the full set
+of defined keyword values for that attribute are listed.
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 72]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+When a keyword is used to represent an attribute (its name), it MUST be
+unique within the full scope of all IPP objects and attributes. When a
+keyword is used to represent a value of an attribute, it MUST be unique
+just within the scope of that attribute. That is, the same keyword MUST
+NOT be used for two different values within the same attribute to mean
+two different semantic ideas. However, the same keyword MAY be used
+across two or more attributes, representing different semantic ideas for
+each attribute. Section 6.1 describes how the protocol can be extended
+with new keyword values. Examples of attribute name keywords:
+
+ "job-name"
+ "attributes-charset"
+
+
+Note: This document uses "type1", "type2", and "type3" prefixes to the
+"keyword" basic syntax to indicate different levels of review for
+extensions (see section 6.1).
+
+
+4.1.4 'enum'
+
+The 'enum' attribute syntax is an enumerated integer value that is in
+the range from 1 to 2**31 - 1 (MAX). Each value has an associated
+'keyword' name. In the definition for each attribute of this syntax
+type, the full set of possible values for that attribute are listed.
+This syntax type is used for attributes for which there are enum values
+assigned by other standards, such as SNMP MIBs. A number of attribute
+enum values in this document are also used for corresponding attributes
+in other standards [RFC1759]. This syntax type is not used for
+attributes to which the administrator may assign values. Section 6.1
+describes how the protocol can be extended with new enum values.
+
+Enum values are for use in the protocol. A user interface will provide
+a mapping between protocol enum values and displayable user-friendly
+words and phrases which are localized to the natural language of the
+user. While the enum symbols specified in this document MAY be
+displayed to users whose natural language is U.S. English, they MAY be
+mapped to other U.S. English words for U.S. English users, since the
+user interface is outside the scope of this document.
+
+Note: SNMP MIBs use '2' for 'unknown' which corresponds to the IPP "out-
+of-band" value 'unknown'. See the description of the "out-of-band"
+values at the beginning of Section 4.1. Therefore, attributes of type
+'enum' start at '3'.
+
+Note: This document uses "type1", "type2", and "type3" prefixes to the
+"enum" basic syntax to indicate different levels of review for
+extensions (see section 6.1).
+
+
+4.1.5 'uri'
+
+The 'uri' attribute syntax is any valid Uniform Resource Identifier or
+URI [RFC2396]. Most often, URIs are simply Uniform Resource Locators or
+URLs. The maximum length of URIs used as values of IPP attributes is
+1023 octets. Although most other IPP attribute syntax types allow for
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 73]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+only lower-cased values, this attribute syntax type conforms to the
+case-sensitive and case-insensitive rules specified in [RFC2396]. See
+also [IPP-IIG] for a discussion of case in URIs.
+
+
+4.1.6 'uriScheme'
+
+The 'uriScheme' attribute syntax is a sequence of characters
+representing a URI scheme according to RFC 2396 [RFC2396]. Though RFC
+2396 requires that the values be case-insensitive, IPP requires all
+lower case values in IPP attributes to simplify comparing by IPP clients
+and Printer objects.
+
+Standard values for this syntax type are the following keywords:
+
+ 'ipp': for IPP schemed URIs (e.g., "ipp:...")
+ 'http': for HTTP schemed URIs (e.g., "http:...")
+ 'https': for use with HTTPS schemed URIs (e.g., "https:...") (not on
+ IETF standards track)
+ 'ftp': for FTP schemed URIs (e.g., "ftp:...")
+ 'mailto': for SMTP schemed URIs (e.g., "mailto:...")
+ 'file': for file schemed URIs (e.g., "file:...")
+
+
+A Printer object MAY support any URI 'scheme' that has been registered
+with IANA [IANA-MT]. The maximum length of URI 'scheme' values used to
+represent IPP attribute values is 63 octets.
+
+
+4.1.7 'charset'
+
+The 'charset' attribute syntax is a standard identifier for a charset.
+A charset is a coded character set and encoding scheme. Charsets are
+used for labeling certain document contents and 'text' and 'name'
+attribute values. The syntax and semantics of this attribute syntax are
+specified in RFC 2046 [RFC2046] and contained in the IANA character-set
+Registry [IANA-CS] according to the IANA procedures [RFC2278]. Though
+RFC 2046 requires that the values be case-insensitive US-ASCII, IPP
+requires all lower case values in IPP attributes to simplify comparing
+by IPP clients and Printer objects. When a character-set in the IANA
+registry has more than one name (alias), the name labeled as "(preferred
+MIME name)", if present, MUST be used.
+
+The maximum length of 'charset' values used to represent IPP attribute
+values is 63 octets.
+
+Some examples are:
+
+ 'utf-8': ISO 10646 Universal Multiple-Octet Coded Character Set
+ (UCS) represented as the UTF-8 [RFC2279] transfer encoding scheme
+ in which US-ASCII is a subset charset.
+ 'us-ascii': 7-bit American Standard Code for Information Interchange
+ (ASCII), ANSI X3.4-1986 [ASCII]. That standard defines US-ASCII,
+ but RFC 2045 [RFC2045] eliminates most of the control characters
+ from conformant usage in MIME and IPP.
+
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 74]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+ 'iso-8859-1': 8-bit One-Byte Coded Character Set, Latin Alphabet Nr
+ 1 [ISO8859-1]. That standard defines a coded character set that is
+ used by Latin languages in the Western Hemisphere and Western
+ Europe. US-ASCII is a subset charset.
+ 'iso-10646-ucs-2': ISO 10646 Universal Multiple-Octet Coded
+ Character Set (UCS) represented as two octets (UCS-2), with the
+ high order octet of each pair coming first (so-called Big Endian
+ integer).
+
+
+Some attribute descriptions MAY place additional requirements on charset
+values that may be used, such as REQUIRED values that MUST be supported
+or additional restrictions, such as requiring that the charset have US-
+ASCII as a subset charset.
+
+
+4.1.8 'naturalLanguage'
+
+The 'naturalLanguage' attribute syntax is a standard identifier for a
+natural language and optionally a country. The values for this syntax
+type are defined by RFC 1766 [RFC1766]. Though RFC 1766 requires that
+the values be case-insensitive US-ASCII, IPP requires all lower case to
+simplify comparing by IPP clients and Printer objects. Examples
+include:
+
+ 'en': for English
+ 'en-us': for US English
+ 'fr': for French
+ 'de': for German
+
+
+The maximum length of 'naturalLanguage' values used to represent IPP
+attribute values is 63 octets.
+
+
+4.1.9 'mimeMediaType'
+
+The 'mimeMediaType' attribute syntax is the Internet Media Type
+(sometimes called MIME type) as defined by RFC 2046 [RFC2046] and
+registered according to the procedures of RFC 2048 [RFC2048] for
+identifying a document format. The value MAY include a charset
+parameter, depending on the specification of the Media Type in the IANA
+Registry [IANA-MT]. Although most other IPP syntax types allow for only
+lower-cased values, this syntax type allows for mixed-case values which
+are case-insensitive.
+
+Examples are:
+
+ 'text/html': An HTML document
+ 'text/plain': A plain text document in US-ASCII (RFC 2046 indicates
+ that in the absence of the charset parameter MUST mean US-ASCII
+ rather than simply unspecified) [RFC2046].
+ 'text/plain; charset=US-ASCII': A plain text document in US-ASCII
+ [52, 56].
+ 'text/plain; charset=ISO-8859-1': A plain text document in ISO 8859-
+ 1 (Latin 1) [ISO8859-1].
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 75]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+ 'text/plain; charset=utf-8': A plain text document in ISO 10646
+ represented as UTF-8 [RFC2279]
+ 'application/postscript': A PostScript document [RFC2046]
+ 'application/vnd.hp-PCL': A PCL document [IANA-MT] (charset escape
+ sequence embedded in the document data)
+ 'application/pdf': Portable Document Format - see IANA MIME Media
+ Type registry
+ 'application/octet-stream': Auto-sense - see section 4.1.9.1
+
+
+4.1.9.1 Application/octet-stream -- Auto-Sensing the document format
+
+One special type is 'application/octet-stream'. If the Printer object
+supports this value, the Printer object MUST be capable of auto-sensing
+the format of the document data, either as part of the create operation
+and/or at document processing time. During auto-sensing, a Printer may
+determine that the document-data has a format that the Printer doesn't
+recognize. If the Printer determines this problem before returning an
+operation response, it rejects the request and returns the 'client-
+error-document-format-not-supported' status code. If the Printer
+determines this problem after accepting the request and returning an
+operation response with one of the successful status codes, the Printer
+adds the 'unsupported-document-format' value to the job's "job-state-
+reasons" attribute.
+
+If the Printer object's default value attribute "document-format-
+default" is set to 'application/octet-stream', the Printer object not
+only supports auto-sensing of the document format, but will depend on
+the result of applying its auto-sensing when the client does not supply
+the "document-format" attribute. If the client supplies a document
+format value, the Printer MUST rely on the supplied attribute, rather
+than trust its auto-sensing algorithm. To summarize:
+
+ 1. If the client does not supply a document format value, the Printer
+ MUST rely on its default value setting (which may be
+ 'application/octet-stream' indicating an auto-sensing mechanism).
+ 2. If the client supplies a value other than 'application/octet-
+ stream', the client is supplying valid information about the format
+ of the document data and the Printer object MUST trust the client
+ supplied value more than the outcome of applying an automatic
+ format detection mechanism. For example, the client may be
+ requesting the printing of a PostScript file as a 'text/plain'
+ document. The Printer object MUST print a text representation of
+ the PostScript commands rather than interpret the stream of
+ PostScript commands and print the result.
+ 3. If the client supplies a value of 'application/octet-stream', the
+ client is indicating that the Printer object MUST use its auto-
+ sensing mechanism on the client supplied document data whether
+ auto-sensing is the Printer object's default or not.
+
+
+Note: Since the auto-sensing algorithm is probabilistic, if the client
+requests both auto-sensing ("document-format" set to 'application/octet-
+stream') and true fidelity ("ipp-attribute-fidelity" set to 'true'), the
+Printer object might not be able to guarantee exactly what the end user
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 76]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+intended (the auto-sensing algorithm might mistake one document format
+for another), but it is able to guarantee that its auto-sensing
+mechanism be used.
+
+The maximum length of a 'mimeMediaType' value to represent IPP attribute
+values is 255 octets.
+
+
+4.1.10 'octetString'
+
+The 'octetString' attribute syntax is a sequence of octets encoded in a
+maximum of 1023 octets which is indicated in sub-section headers using
+the notation: octetString(MAX). This syntax type is used for opaque
+data.
+
+
+4.1.11 'boolean'
+
+The 'boolean' attribute syntax has only two values: 'true' and 'false'.
+
+
+4.1.12 'integer'
+
+The 'integer' attribute syntax is an integer value that is in the range
+from -2**31 (MIN) to 2**31 - 1 (MAX). Each individual attribute may
+specify the range constraint explicitly in sub-section headers if the
+range is different from the full range of possible integer values. For
+example: job-priority (integer(1:100)) for the "job-priority"
+attribute. However, the enforcement of that additional constraint is up
+to the IPP objects, not the protocol.
+
+
+4.1.13 'rangeOfInteger'
+
+The 'rangeOfInteger' attribute syntax is an ordered pair of integers
+that defines an inclusive range of integer values. The first integer
+specifies the lower bound and the second specifies the upper bound. If
+a range constraint is specified in the header description for an
+attribute in this document whose attribute syntax is 'rangeOfInteger'
+(i.e., 'X:Y' indicating X as a minimum value and Y as a maximum value),
+then the constraint applies to both integers.
+
+
+4.1.14 'dateTime'
+
+The 'dateTime' attribute syntax is a standard, fixed length, 11 octet
+representation of the "DateAndTime" syntax as defined in RFC 2579
+[RFC2579]. RFC 2579 also identifies an 8 octet representation of a
+"DateAndTime" value, but IPP objects MUST use the 11 octet
+representation. A user interface will provide a mapping between
+protocol dateTime values and displayable user-friendly words or
+presentation values and phrases which are localized to the natural
+language and date format of the user, including time zone.
+
+
+
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 77]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+4.1.15 'resolution'
+
+The 'resolution' attribute syntax specifies a two-dimensional resolution
+in the indicated units. It consists of 3 values: a cross feed direction
+resolution (positive integer value), a feed direction resolution
+(positive integer value), and a units value. The semantics of these
+three components are taken from the Printer MIB [RFC1759] suggested
+values. That is, the cross feed direction component resolution
+component is the same as the prtMarkerAddressabilityXFeedDir object in
+the Printer MIB, the feed direction component resolution component is
+the same as the prtMarkerAddressabilityFeedDir in the Printer MIB, and
+the units component is the same as the prtMarkerAddressabilityUnit
+object in the Printer MIB (namely, '3' indicates dots per inch and '4'
+indicates dots per centimeter). All three values MUST be present even
+if the first two values are the same. Example: '300', '600', '3'
+indicates a 300 dpi cross-feed direction resolution, a 600 dpi feed
+direction resolution, since a '3' indicates dots per inch (dpi).
+
+
+4.1.16 '1setOf X'
+
+The '1setOf X' attribute syntax is 1 or more values of attribute syntax
+type X. This syntax type is used for multi-valued attributes. The
+syntax type is called '1setOf' rather than just 'setOf' as a reminder
+that the set of values MUST NOT be empty (i.e., a set of size 0). Sets
+are normally unordered. However each attribute description of this type
+may specify that the values MUST be in a certain order for that
+attribute.
+
+
+4.2 Job Template Attributes
+
+
+Job Template attributes describe job processing behavior. Support for
+Job Template attributes by a Printer object is OPTIONAL (see section
+12.2.3 for a description of support for OPTIONAL attributes). Also,
+clients OPTIONALLY supply Job Template attributes in create requests.
+
+Job Template attributes conform to the following rules. For each Job
+Template attribute called "xxx":
+
+ 1. If the Printer object supports "xxx" then it MUST support both a
+ "xxx-default" attribute (unless there is a "No" in the table below)
+ and a "xxx-supported" attribute. If the Printer object doesn't
+ support "xxx", then it MUST support neither an "xxx-default"
+ attribute nor an "xxx-supported" attribute, and it MUST treat an
+ attribute "xxx" supplied by a client as unsupported. An attribute
+ "xxx" may be supported for some document formats and not supported
+ for other document formats. For example, it is expected that a
+ Printer object would only support "orientation-requested" for some
+ document formats (such as 'text/plain' or 'text/html') but not
+ others (such as 'application/postscript').
+
+ 2. "xxx" is OPTIONALLY supplied by the client in a create request.
+ If "xxx" is supplied, the client is indicating a desired job
+ processing behavior for this Job. When "xxx" is not supplied, the
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 78]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+ client is indicating that the Printer object apply its default job
+ processing behavior at job processing time if the document content
+ does not contain an embedded instruction indicating an xxx-related
+ behavior.
+
+ Since an administrator MAY change the default value attribute after
+ a Job object has been submitted but before it has been processed,
+ the default value used by the Printer object at job processing time
+ may be different that the default value in effect at job submission
+ time.
+
+ 3. The "xxx-supported" attribute is a Printer object attribute that
+ describes which job processing behaviors are supported by that
+ Printer object. A client can query the Printer object to find out
+ what xxx-related behaviors are supported by inspecting the returned
+ values of the "xxx-supported" attribute.
+
+ Note: The "xxx" in each "xxx-supported" attribute name is singular,
+ even though an "xxx-supported" attribute usually has more than one
+ value, such as "job-sheet-supported", unless the "xxx" Job Template
+ attribute is plural, such as "finishings" or "sides". In such
+ cases the "xxx-supported" attribute names are: "finishings-
+ supported" and "sides-supported".
+
+ 4. The "xxx-default" default value attribute describes what will be
+ done at job processing time when no other job processing
+ information is supplied by the client (either explicitly as an IPP
+ attribute in the create request or implicitly as an embedded
+ instruction within the document data).
+
+
+If an application wishes to present an end user with a list of supported
+values from which to choose, the application SHOULD query the Printer
+object for its supported value attributes. The application SHOULD also
+query the default value attributes. If the application then limits
+selectable values to only those value that are supported, the
+application can guarantee that the values supplied by the client in the
+create request all fall within the set of supported values at the
+Printer. When querying the Printer, the client MAY enumerate each
+attribute by name in the Get-Printer-Attributes Request, or the client
+MAY just name the "job-template" group in order to get the complete set
+of supported attributes (both supported and default attributes).
+
+The "finishings" attribute is an example of a Job Template attribute.
+It can take on a set of values such as 'staple', 'punch', and/or
+'cover'. A client can query the Printer object for the "finishings-
+supported" attribute and the "finishings-default" attribute. The
+supported attribute contains a set of supported values. The default
+value attribute contains the finishing value(s) that will be used for a
+new Job if the client does not supply a "finishings" attribute in the
+create request and the document data does not contain any corresponding
+finishing instructions. If the client does supply the "finishings"
+attribute in the create request, the IPP object validates the value or
+values to make sure that they are a subset of the supported values
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 79]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+identified in the Printer object's "finishings-supported" attribute.
+See section 3.1.7.
+
+The table below summarizes the names and relationships for all Job
+Template attributes. The first column of the table (labeled "Job
+Attribute") shows the name and syntax for each Job Template attribute in
+the Job object. These are the attributes that can optionally be supplied
+by the client in a create request. The last two columns (labeled
+"Printer: Default Value Attribute" and "Printer: Supported Values
+Attribute") shows the name and syntax for each Job Template attribute in
+the Printer object (the default value attribute and the supported values
+attribute). A "No" in the table means the Printer MUST NOT support the
+attribute (that is, the attribute is simply not applicable). For
+brevity in the table, the 'text' and 'name' entries do not show the
+maximum length for each attribute.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 80]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+ +===================+======================+======================+
+ | Job Attribute |Printer: Default Value| Printer: Supported |
+ | | Attribute | Values Attribute |
+ +===================+======================+======================+
+ | job-priority | job-priority-default |job-priority-supported|
+ | (integer 1:100) | (integer 1:100) |(integer 1:100) |
+ +-------------------+----------------------+----------------------+
+ | job-hold-until | job-hold-until- |job-hold-until- |
+ | (type3 keyword | | default | supported |
+ | name) | (type3 keyword | |(1setOf ( |
+ | | name) |type3 keyword | name))|
+ +-------------------+----------------------+----------------------+
+ | job-sheets | job-sheets-default |job-sheets-supported |
+ | (type3 keyword | | (type3 keyword | |(1setOf ( |
+ | name) | name) |type3 keyword | name))|
+ +-------------------+----------------------+----------------------+
+ |multiple-document- |multiple-document- |multiple-document- |
+ | handling | handling-default |handling-supported |
+ | (type2 keyword) | (type2 keyword) |(1setOf type2 keyword)|
+ +-------------------+----------------------+----------------------+
+ | copies | copies-default | copies-supported |
+ | (integer (1:MAX)) | (integer (1:MAX)) | (rangeOfInteger |
+ | | | (1:MAX)) |
+ +-------------------+----------------------+----------------------+
+ | finishings | finishings-default | finishings-supported |
+ |(1setOf type2 enum)|(1setOf type2 enum) |(1setOf type2 enum) |
+ +-------------------+----------------------+----------------------+
+ | page-ranges | No | page-ranges- |
+ | (1setOf | | supported (boolean) |
+ | rangeOfInteger | | |
+ | (1:MAX)) | | |
+ +-------------------+----------------------+----------------------+
+ | sides | sides-default | sides-supported |
+ | (type2 keyword) | (type2 keyword) |(1setOf type2 keyword)|
+ +-------------------+----------------------+----------------------+
+ | number-up | number-up-default | number-up-supported |
+ | (integer (1:MAX)) | (integer (1:MAX)) |(1setOf integer |
+ | | | (1:MAX) | |
+ | | | rangeOfInteger |
+ | | | (1:MAX)) |
+ +-------------------+----------------------+----------------------+
+ | orientation- |orientation-requested-|orientation-requested-|
+ | requested | default | supported |
+ | (type2 enum) | (type2 enum) | (1setOf type2 enum) |
+ +-------------------+----------------------+----------------------+
+ | media | media-default | media-supported |
+ | (type3 keyword | | (type3 keyword | |(1setOf ( |
+ | name) | name) |type3 keyword | name))|
+ | | | |
+ | | | media-ready |
+ | | |(1setOf ( |
+ | | |type3 keyword | name))|
+ +-------------------+----------------------+----------------------+
+ | printer-resolution| printer-resolution- | printer-resolution- |
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 81]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+ | (resolution) | default | supported |
+ | | (resolution) |(1setOf resolution) |
+ +-------------------+----------------------+----------------------+
+ | print-quality | print-quality-default| print-quality- |
+ | (type2 enum) | (type2 enum) | supported |
+ | | |(1setOf type2 enum) |
+ +-------------------+----------------------+----------------------+
+
+
+
+4.2.1 job-priority (integer(1:100))
+
+This attribute specifies a priority for scheduling the Job. A higher
+value specifies a higher priority. The value 1 indicates the lowest
+possible priority. The value 100 indicates the highest possible
+priority. Among those jobs that are ready to print, a Printer MUST
+print all jobs with a priority value of n before printing those with a
+priority value of n-1 for all n.
+
+If the Printer object supports this attribute, it MUST always support
+the full range from 1 to 100. No administrative restrictions are
+permitted. This way an end-user can always make full use of the entire
+range with any Printer object. If privileged jobs are implemented
+outside IPP/1.1, they MUST have priorities higher than 100, rather than
+restricting the range available to end-users.
+
+If the client does not supply this attribute and this attribute is
+supported by the Printer object, the Printer object MUST use the value
+of the Printer object's "job-priority-default" at job submission time
+(unlike most Job Template attributes that are used if necessary at job
+processing time).
+
+The syntax for the "job-priority-supported" is also integer(1:100).
+This single integer value indicates the number of priority levels
+supported. The Printer object MUST take the value supplied by the
+client and map it to the closest integer in a sequence of n integers
+values that are evenly distributed over the range from 1 to 100 using
+the formula:
+
+ roundToNearestInt((100x+50)/n)
+
+where n is the value of "job-priority-supported" and x ranges from 0
+through n-1.
+
+For example, if n=1 the sequence of values is 50; if n=2, the sequence
+of values is: 25 and 75; if n = 3, the sequence of values is: 17, 50
+and 83; if n = 10, the sequence of values is: 5, 15, 25, 35, 45, 55,
+65, 75, 85, and 95; if n = 100, the sequence of values is: 1, 2, 3, .
+100.
+
+If the value of the Printer object's "job-priority-supported" is 10 and
+the client supplies values in the range 1 to 10, the Printer object maps
+them to 5, in the range 11 to 20, the Printer object maps them to 15,
+etc.
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 82]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+4.2.2 job-hold-until (type3 keyword | name (MAX))
+
+This attribute specifies the named time period during which the Job MUST
+become a candidate for printing.
+
+Standard keyword values for named time periods are:
+
+ 'no-hold': immediately, if there are not other reasons to hold the
+ job
+ 'indefinite': - the job is held indefinitely, until a client
+ performs a Release-Job (section 3.3.6)
+ 'day-time': during the day
+ 'evening': evening
+ 'night': night
+ 'weekend': weekend
+ 'second-shift': second-shift (after close of business)
+ 'third-shift': third-shift (after midnight)
+
+
+An administrator MUST associate allowable print times with a named time
+period (by means outside the scope of this IPP/1.1 document). An
+administrator is encouraged to pick names that suggest the type of time
+period. An administrator MAY define additional values using the 'name'
+or 'keyword' attribute syntax, depending on implementation.
+
+If the value of this attribute specifies a time period that is in the
+future, the Printer SHOULD add the 'job-hold-until-specified' value to
+the job's "job-state-reasons" attribute, MUST move the job to the
+'pending-held' state, and MUST NOT schedule the job for printing until
+the specified time-period arrives.
+
+When the specified time period arrives, the Printer MUST remove the
+'job-hold-until-specified' value from the job's "job-state-reason"
+attribute, if present. If there are no other job state reasons that
+keep the job in the 'pending-held' state, the Printer MUST consider the
+job as a candidate for processing by moving the job to the 'pending'
+state.
+
+If this job attribute value is the named value 'no-hold', or the
+specified time period has already started, the job MUST be a candidate
+for processing immediately.
+
+If the client does not supply this attribute and this attribute is
+supported by the Printer object, the Printer object MUST use the value
+of the Printer object's "job-hold-until-default" at job submission time
+(unlike most Job Template attributes that are used if necessary at job
+processing time).
+
+
+4.2.3 job-sheets (type3 keyword | name(MAX))
+
+This attribute determines which job start/end sheet(s), if any, MUST be
+printed with a job.
+
+Standard keyword values are:
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 83]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+ 'none': no job sheet is printed
+ 'standard': one or more site specific standard job sheets are
+ printed, e.g. a single start sheet or both start and end sheet is
+ printed
+
+
+An administrator MAY define additional values using the 'name' or
+'keyword' attribute syntax, depending on implementation.
+
+The effect of this attribute on jobs with multiple documents MAY be
+affected by the "multiple-document-handling" job attribute (section
+4.2.4), depending on the job sheet semantics.
+
+
+4.2.4 multiple-document-handling (type2 keyword)
+
+This attribute is relevant only if a job consists of two or more
+documents. This attribute MUST be supported if the Printer supports
+multiple documents per job (see sections 3.2.4 and 3.3.1). The
+attribute controls finishing operations and the placement of one or more
+print-stream pages into impressions and onto media sheets. When the
+value of the "copies" attribute exceeds 1, it also controls the order in
+which the copies that result from processing the documents are produced.
+For the purposes of this explanations, if "a" represents an instance of
+document data, then the result of processing the data in document "a" is
+a sequence of media sheets represented by "a(*)".
+
+Standard keyword values are:
+
+ 'single-document': If a Job object has multiple documents, say, the
+ document data is called a and b, then the result of processing all
+ the document data (a and then b) MUST be treated as a single
+ sequence of media sheets for finishing operations; that is,
+ finishing would be performed on the concatenation of the sequences
+ a(*),b(*). The Printer object MUST NOT force the data in each
+ document instance to be formatted onto a new print-stream page, nor
+ to start a new impression on a new media sheet. If more than one
+ copy is made, the ordering of the sets of media sheets resulting
+ from processing the document data MUST be a(*), b(*), a(*), b(*),
+ ..., and the Printer object MUST force each copy (a(*),b(*)) to
+ start on a new media sheet.
+ 'separate-documents-uncollated-copies': If a Job object has multiple
+ documents, say, the document data is called a and b, then the
+ result of processing the data in each document instance MUST be
+ treated as a single sequence of media sheets for finishing
+ operations; that is, the sets a(*) and b(*) would each be finished
+ separately. The Printer object MUST force each copy of the result
+ of processing the data in a single document to start on a new media
+ sheet. If more than one copy is made, the ordering of the sets of
+ media sheets resulting from processing the document data MUST be
+ a(*), a(*), ..., b(*), b(*) ... .
+ 'separate-documents-collated-copies': If a Job object has multiple
+ documents, say, the document data is called a and b, then the
+ result of processing the data in each document instance MUST be
+ treated as a single sequence of media sheets for finishing
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 84]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+ operations; that is, the sets a(*) and b(*) would each be finished
+ separately. The Printer object MUST force each copy of the result
+ of processing the data in a single document to start on a new media
+ sheet. If more than one copy is made, the ordering of the sets of
+ media sheets resulting from processing the document data MUST be
+ a(*), b(*), a(*), b(*), ... .
+ 'single-document-new-sheet': Same as 'single-document', except that
+ the Printer object MUST ensure that the first impression of each
+ document instance in the job is placed on a new media sheet. This
+ value allows multiple documents to be stapled together with a
+ single staple where each document starts on a new sheet.
+
+
+The 'single-document' value is the same as 'separate-documents-collated-
+copies' with respect to ordering of print-stream pages, but not media
+sheet generation, since 'single-document' will put the first page of the
+next document on the back side of a sheet if an odd number of pages have
+been produced so far for the job, while 'separate-documents-collated-
+copies' always forces the next document or document copy on to a new
+sheet. In addition, if the "finishings" attribute specifies 'staple',
+then with 'single-document', documents a and b are stapled together as a
+single document with no regard to new sheets, with 'single-document-new-
+sheet', documents a and b are stapled together as a single document, but
+document b starts on a new sheet, but with 'separate-documents-
+uncollated-copies' and 'separate-documents-collated-copies', documents a
+and b are stapled separately.
+
+Note: None of these values provide means to produce uncollated sheets
+within a document, i.e., where multiple copies of sheet n are produced
+before sheet n+1 of the same document.
+
+The relationship of this attribute and the other attributes that control
+document processing is described in section 15.3.
+
+
+4.2.5 copies (integer(1:MAX))
+
+This attribute specifies the number of copies to be printed.
+
+On many devices the supported number of collated copies will be limited
+by the number of physical output bins on the device, and may be
+different from the number of uncollated copies which can be supported.
+
+Note: The effect of this attribute on jobs with multiple documents is
+controlled by the "multiple-document-handling" job attribute (section
+4.2.4) and the relationship of this attribute and the other attributes
+that control document processing is described in section 15.3.
+
+
+4.2.6 finishings (1setOf type2 enum)
+
+This attribute identifies the finishing operations that the Printer uses
+for each copy of each printed document in the Job. For Jobs with
+multiple documents, the "multiple-document-handling" attribute
+determines what constitutes a "copy" for purposes of finishing.
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 85]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+Standard enum values are:
+
+ Value Symbolic Name and Description
+
+ '3' 'none': Perform no finishing
+ '4' 'staple': Bind the document(s) with one or more staples. The
+ exact number and placement of the staples is site-
+ defined.
+ '5' 'punch': This value indicates that holes are required in the
+ finished document. The exact number and placement of the
+ holes is site-defined The punch specification MAY be
+ satisfied (in a site- and implementation-specific manner)
+ either by drilling/punching, or by substituting pre-
+ drilled media.
+ '6' 'cover': This value is specified when it is desired to select
+ a non-printed (or pre-printed) cover for the document.
+ This does not supplant the specification of a printed
+ cover (on cover stock medium) by the document itself.
+ '7' 'bind': This value indicates that a binding is to be applied
+ to the document; the type and placement of the binding is
+ site-defined.
+
+ '8' 'saddle-stitch': Bind the document(s) with one or more
+ staples (wire stitches) along the middle fold. The exact
+ number and placement of the staples and the middle fold
+ is implementation and/or site-defined.
+ '9' 'edge-stitch': Bind the document(s) with one or more staples
+ (wire stitches) along one edge. The exact number and
+ placement of the staples is implementation and/or site-
+ defined.
+ '10'-'19' reserved for future generic finishing enum values.
+
+The following values are more specific; they indicate a corner or an
+edge as if the document were a portrait document (see below):
+
+ '20' 'staple-top-left': Bind the document(s) with one or more
+ staples in the top left corner.
+ '21' 'staple-bottom-left': Bind the document(s) with one or more
+ staples in the bottom left corner.
+ '22' 'staple-top-right': Bind the document(s) with one or more
+ staples in the top right corner.
+ '23' 'staple-bottom-right': Bind the document(s) with one or more
+ staples in the bottom right corner.
+ '24' 'edge-stitch-left': Bind the document(s) with one or more
+ staples (wire stitches) along the left edge. The exact
+ number and placement of the staples is implementation
+ and/or site-defined.
+ '25' 'edge-stitch-top': Bind the document(s) with one or more
+ staples (wire stitches) along the top edge. The exact
+ number and placement of the staples is implementation
+ and/or site-defined.
+ '26' 'edge-stitch-right': Bind the document(s) with one or more
+ staples (wire stitches) along the right edge. The exact
+ number and placement of the staples is implementation
+ and/or site-defined.
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 86]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+ '27' 'edge-stitch-bottom': Bind the document(s) with one or more
+ staples (wire stitches) along the bottom edge. The exact
+ number and placement of the staples is implementation
+ and/or site-defined.
+ '28' 'staple-dual-left': Bind the document(s) with two staples
+ (wire stitches) along the left edge assuming a portrait
+ document (see above).
+ '29' 'staple-dual-top': Bind the document(s) with two staples
+ (wire stitches) along the top edge assuming a portrait
+ document (see above).
+ '30' 'staple-dual-right': Bind the document(s) with two staples
+ (wire stitches) along the right edge assuming a portrait
+ document (see above).
+ '31' 'staple-dual-bottom': Bind the document(s) with two staples
+ (wire stitches) along the bottom edge assuming a portrait
+ document (see above).
+
+The 'staple-xxx' values are specified with respect to the document as if
+the document were a portrait document. If the document is actually a
+landscape or a reverse-landscape document, the client supplies the
+appropriate transformed value. For example, to position a staple in the
+upper left hand corner of a landscape document when held for reading,
+the client supplies the 'staple-bottom-left' value (since landscape is
+defined as a +90 degree rotation from portrait, i.e., anti-clockwise).
+On the other hand, to position a staple in the upper left hand corner of
+a reverse-landscape document when held for reading, the client supplies
+the 'staple-top-right' value (since reverse-landscape is defined as a -
+90 degree rotation from portrait, i.e., clockwise).
+
+The angle (vertical, horizontal, angled) of each staple with respect to
+the document depends on the implementation which may in turn depend on
+the value of the attribute.
+
+Note: The effect of this attribute on jobs with multiple documents is
+controlled by the "multiple-document-handling" job attribute (section
+4.2.4) and the relationship of this attribute and the other attributes
+that control document processing is described in section 15.3.
+
+If the client supplies a value of 'none' along with any other
+combination of values, it is the same as if only that other combination
+of values had been supplied (that is the 'none' value has no effect).
+
+
+4.2.7 page-ranges (1setOf rangeOfInteger (1:MAX))
+
+This attribute identifies the range(s) of print-stream pages that the
+Printer object uses for each copy of each document which are to be
+printed. Nothing is printed for any pages identified that do not exist
+in the document(s). Ranges MUST be in ascending order, for example: 1-
+3, 5-7, 15-19 and MUST NOT overlap, so that a non-spooling Printer
+object can process the job in a single pass. If the ranges are not
+ascending or are overlapping, the IPP object MUST reject the request and
+return the 'client-error-bad-request' status code. The attribute is
+associated with print-stream pages not application-numbered pages (for
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 87]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+example, the page numbers found in the headers and or footers for
+certain word processing applications).
+
+For Jobs with multiple documents, the "multiple-document-handling"
+attribute determines what constitutes a "copy" for purposes of the
+specified page range(s). When "multiple-document-handling" is 'single-
+document', the Printer object MUST apply each supplied page range once
+to the concatenation of the print-stream pages. For example, if there
+are 8 documents of 10 pages each, the page-range '41:60' prints the
+pages in the 5th and 6th documents as a single document and none of the
+pages of the other documents are printed. When "multiple-document-
+handling" is 'separate-documents-uncollated-copies' or 'separate-
+documents-collated-copies', the Printer object MUST apply each supplied
+page range repeatedly to each document copy. For the same job, the
+page-range '1:3, 10:10' would print the first 3 pages and the 10th page
+of each of the 8 documents in the Job, as 8 separate documents.
+
+In most cases, the exact pages to be printed will be generated by a
+device driver and this attribute would not be required. However, when
+printing an archived document which has already been formatted, the end
+user may elect to print just a subset of the pages contained in the
+document. In this case, if page-range = n.m is specified, the first
+page to be printed will be page n. All subsequent pages of the document
+will be printed through and including page m.
+
+"page-ranges-supported" is a boolean value indicating whether or not the
+printer is capable of supporting the printing of page ranges. This
+capability may differ from one PDL to another. There is no "page-ranges-
+default" attribute. If the "page-ranges" attribute is not supplied by
+the client, all pages of the document will be printed.
+
+Note: The effect of this attribute on jobs with multiple documents is
+controlled by the "multiple-document-handling" job attribute (section
+4.2.4) and the relationship of this attribute and the other attributes
+that control document processing is described in section 15.3.
+
+
+4.2.8 sides (type2 keyword)
+
+This attribute specifies how print-stream pages are to be imposed upon
+the sides of an instance of a selected medium, i.e., an impression.
+
+The standard keyword values are:
+
+ 'one-sided': imposes each consecutive print-stream page upon the same
+ side of consecutive media sheets.
+ 'two-sided-long-edge': imposes each consecutive pair of print-stream
+ pages upon front and back sides of consecutive media sheets, such
+ that the orientation of each pair of print-stream pages on the
+ medium would be correct for the reader as if for binding on the
+ long edge. This imposition is sometimes called 'duplex' or 'head-
+ to-head'.
+ 'two-sided-short-edge': imposes each consecutive pair of print-stream
+ pages upon front and back sides of consecutive media sheets, such
+ that the orientation of each pair of print-stream pages on the
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 88]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+ medium would be correct for the reader as if for binding on the
+ short edge. This imposition is sometimes called 'tumble' or 'head-
+ to-toe'.
+
+
+'two-sided-long-edge', 'two-sided-short-edge', 'tumble', and 'duplex'
+all work the same for portrait or landscape. However 'head-to-toe' is
+'tumble' in portrait but 'duplex' in landscape. 'head-to-head' also
+switches between 'duplex' and 'tumble' when using portrait and landscape
+modes.
+
+Note: The effect of this attribute on jobs with multiple documents is
+controlled by the "multiple-document-handling" job attribute (section
+4.2.4) and the relationship of this attribute and the other attributes
+that control document processing is described in section 15.3.
+
+
+4.2.9 number-up (integer(1:MAX))
+
+This attribute specifies the number of print-stream pages to impose upon
+a single side of an instance of a selected medium. For example, if the
+value is:
+
+ Value Description
+
+ '1' the Printer MUST place one print-stream page on a single side
+ of an instance of the selected medium (MAY add some sort
+ of translation, scaling, or rotation).
+ '2' the Printer MUST place two print-stream pages on a single side
+ of an instance of the selected medium (MAY add some sort
+ of translation, scaling, or rotation).
+ '4' the Printer MUST place four print-stream pages on a single
+ side of an instance of the selected medium (MAY add some
+ sort of translation, scaling, or rotation).
+
+
+This attribute primarily controls the translation, scaling and rotation
+of print-stream pages.
+
+Note: The effect of this attribute on jobs with multiple documents is
+controlled by the "multiple-document-handling" job attribute (section
+4.2.4) and the relationship of this attribute and the other attributes
+that control document processing is described in section 15.3.
+
+
+4.2.10 orientation-requested (type2 enum)
+
+This attribute indicates the desired orientation for printed print-
+stream pages; it does not describe the orientation of the client-
+supplied print-stream pages.
+
+For some document formats (such as 'application/postscript'), the
+desired orientation of the print-stream pages is specified within the
+document data. This information is generated by a device driver prior
+to the submission of the print job. Other document formats (such as
+'text/plain') do not include the notion of desired orientation within
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 89]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+the document data. In the latter case it is possible for the Printer
+object to bind the desired orientation to the document data after it has
+been submitted. It is expected that a Printer object would only support
+"orientations-requested" for some document formats (e.g., 'text/plain'
+or 'text/html') but not others (e.g., 'application/postscript'). This
+is no different than any other Job Template attribute since section 4.2,
+item 1, points out that a Printer object may support or not support any
+Job Template attribute based on the document format supplied by the
+client. However, a special mention is made here since it is very likely
+that a Printer object will support "orientation-requested" for only a
+subset of the supported document formats.
+
+Standard enum values are:
+
+ Value Symbolic Name and Description
+
+ '3' 'portrait': The content will be imaged across the short edge
+ of the medium.
+ '4' 'landscape': The content will be imaged across the long edge
+ of the medium. Landscape is defined to be a rotation of
+ the print-stream page to be imaged by +90 degrees with
+ respect to the medium (i.e. anti-clockwise) from the
+ portrait orientation. Note: The +90 direction was
+ chosen because simple finishing on the long edge is the
+ same edge whether portrait or landscape
+ '5' 'reverse-landscape': The content will be imaged across the
+ long edge of the medium. Reverse-landscape is defined to
+ be a rotation of the print-stream page to be imaged by -
+ 90 degrees with respect to the medium (i.e. clockwise)
+ from the portrait orientation. Note: The 'reverse-
+ landscape' value was added because some applications
+ rotate landscape -90 degrees from portrait, rather than
+ +90 degrees.
+ '6' 'reverse-portrait': The content will be imaged across the
+ short edge of the medium. Reverse-portrait is defined to
+ be a rotation of the print-stream page to be imaged by
+ 180 degrees with respect to the medium from the portrait
+ orientation. Note: The 'reverse-portrait' value was
+ added for use with the "finishings" attribute in cases
+ where the opposite edge is desired for finishing a
+ portrait document on simple finishing devices that have
+ only one finishing position. Thus a 'text'/plain'
+ portrait document can be stapled "on the right" by a
+ simple finishing device as is common use with some middle
+ eastern languages such as Hebrew.
+
+
+Note: The effect of this attribute on jobs with multiple documents is
+controlled by the "multiple-document-handling" job attribute (section
+4.2.4) and the relationship of this attribute and the other attributes
+that control document processing is described in section 15.3.
+
+
+
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 90]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+4.2.11 media (type3 keyword | name(MAX))
+
+This attribute identifies the medium that the Printer uses for all
+impressions of the Job.
+
+The values for "media" include medium-names, medium-sizes, input-trays
+and electronic forms so that one attribute specifies the media. If a
+Printer object supports a medium name as a value of this attribute, such
+a medium name implicitly selects an input-tray that contains the
+specified medium. If a Printer object supports a medium size as a value
+of this attribute, such a medium size implicitly selects a medium name
+that in turn implicitly selects an input-tray that contains the medium
+with the specified size. If a Printer object supports an input-tray as
+the value of this attribute, such an input-tray implicitly selects the
+medium that is in that input-tray at the time the job prints. This case
+includes manual-feed input-trays. If a Printer object supports an
+electronic form as the value of this attribute, such an electronic form
+implicitly selects a medium-name that in turn implicitly selects an
+input-tray that contains the medium specified by the electronic form.
+The electronic form also implicitly selects an image that the Printer
+MUST merge with the document data as its prints each page.
+
+Standard keyword values are (taken from ISO DPA and the Printer MIB) and
+are listed in section 14. An administrator MAY define additional values
+using the 'name' or 'keyword' attribute syntax, depending on
+implementation.
+
+There is also an additional Printer attribute named "media-ready" which
+differs from "media-supported" in that legal values only include the
+subset of "media-supported" values that are physically loaded and ready
+for printing with no operator intervention required. If an IPP object
+supports "media-supported", it NEED NOT support "media-ready".
+
+The relationship of this attribute and the other attributes that control
+document processing is described in section 15.3.
+
+
+4.2.12 printer-resolution (resolution)
+
+This attribute identifies the resolution that Printer uses for the Job.
+
+
+4.2.13 print-quality (type2 enum)
+
+This attribute specifies the print quality that the Printer uses for the
+Job.
+
+The standard enum values are:
+
+ Value Symbolic Name and Description
+
+ '3' 'draft': lowest quality available on the printer
+ '4' 'normal': normal or intermediate quality on the printer
+ '5' 'high': highest quality available on the printer
+
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 91]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+4.3 Job Description Attributes
+
+
+The attributes in this section form the attribute group called "job-
+description". The following table summarizes these attributes. The
+third column indicates whether the attribute is a REQUIRED attribute
+that MUST be supported by Printer objects. If it is not indicated as
+REQUIRED, then it is OPTIONAL. The maximum size in octets for 'text'
+and 'name' attributes is indicated in parenthesizes.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 92]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
++----------------------------+----------------------+----------------+
+| Attribute | Syntax | REQUIRED? |
++----------------------------+----------------------+----------------+
+| job-uri | uri | REQUIRED |
++----------------------------+----------------------+----------------+
+| job-id | integer(1:MAX) | REQUIRED |
++----------------------------+----------------------+----------------+
+| job-printer-uri | uri | REQUIRED |
++----------------------------+----------------------+----------------+
+| job-more-info | uri | |
++----------------------------+----------------------+----------------+
+| job-name | name (MAX) | REQUIRED |
++----------------------------+----------------------+----------------+
+| job-originating-user-name | name (MAX) | REQUIRED |
++----------------------------+----------------------+----------------+
+| job-state | type1 enum | REQUIRED |
++----------------------------+----------------------+----------------+
+| job-state-reasons | 1setOf type2 keyword | REQUIRED |
++----------------------------+----------------------+----------------+
+| job-state-message | text (MAX) | |
++----------------------------+----------------------+----------------+
+| number-of-documents | integer (0:MAX) | |
++----------------------------+----------------------+----------------+
+| output-device-assigned | name (127) | |
++----------------------------+----------------------+----------------+
+| time-at-creation | integer (MIN:MAX) | REQUIRED |
++----------------------------+----------------------+----------------+
+| time-at-processing | integer (MIN:MAX) | REQUIRED |
++----------------------------+----------------------+----------------+
+| time-at-completed | integer (MIN:MAX) | REQUIRED |
++----------------------------+----------------------+----------------+
+| job-printer-up-time | integer (1:MAX) | REQUIRED |
++----------------------------+----------------------+----------------+
+| date-time-at-creation | dateTime | OPTIONAL |
++----------------------------+----------------------+----------------+
+| date-time-at-processing | dateTime | OPTIONAL |
++----------------------------+----------------------+----------------+
+| date-time-at-completed | dateTime | OPTIONAL |
++----------------------------+----------------------+----------------+
+| number-of-intervening-jobs | integer (0:MAX) | |
++----------------------------+----------------------+----------------+
+| job-message-from-operator | text (127) | |
++----------------------------+----------------------+----------------+
+| job-k-octets | integer (0:MAX) | |
++----------------------------+----------------------+----------------+
+| job-impressions | integer (0:MAX) | |
++----------------------------+----------------------+----------------+
+| job-media-sheets | integer (0:MAX) | |
++----------------------------+----------------------+----------------+
+| job-k-octets-processed | integer (0:MAX) | |
++----------------------------+----------------------+----------------+
+| job-impressions-completed | integer (0:MAX) | |
++----------------------------+----------------------+----------------+
+| job-media-sheets-completed | integer (0:MAX) | |
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 93]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
++----------------------------+----------------------+----------------+
+| attributes-charset | charset | REQUIRED |
++----------------------------+----------------------+----------------+
+| attributes-natural-language| naturalLanguage | REQUIRED |
++----------------------------+----------------------+----------------+
+
+
+
+4.3.1 job-uri (uri)
+
+This REQUIRED attribute contains the URI for the job. The Printer
+object, on receipt of a new job, generates a URI which identifies the
+new Job. The Printer object returns the value of the "job-uri"
+attribute as part of the response to a create request. The precise
+format of a Job URI is implementation dependent. If the Printer object
+supports more than one URI and there is some relationship between the
+newly formed Job URI and the Printer object's URI, the Printer object
+uses the Printer URI supplied by the client in the create request. For
+example, if the create request comes in over a secure channel, the new
+Job URI MUST use the same secure channel. This can be guaranteed
+because the Printer object is responsible for generating the Job URI and
+the Printer object is aware of its security configuration and policy as
+well as the Printer URI used in the create request.
+
+For a description of this attribute and its relationship to "job-id" and
+"job-printer-uri" attribute, see the discussion in section 2.4 on
+"Object Identity".
+
+
+4.3.2 job-id (integer(1:MAX))
+
+This REQUIRED attribute contains the ID of the job. The Printer, on
+receipt of a new job, generates an ID which identifies the new Job on
+that Printer. The Printer returns the value of the "job-id" attribute
+as part of the response to a create request. The 0 value is not
+included to allow for compatibility with SNMP index values which also
+cannot be 0.
+
+For a description of this attribute and its relationship to "job-uri"
+and "job-printer-uri" attribute, see the discussion in section 2.4 on
+"Object Identity".
+
+
+4.3.3 job-printer-uri (uri)
+
+This REQUIRED attribute identifies the Printer object that created this
+Job object. When a Printer object creates a Job object, it populates
+this attribute with the Printer object URI that was used in the create
+request. This attribute permits a client to identify the Printer object
+that created this Job object when only the Job object's URI is available
+to the client. The client queries the creating Printer object to
+determine which languages, charsets, operations, are supported for this
+Job.
+
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 94]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+For a description of this attribute and its relationship to "job-uri"
+and "job-id" attribute, see the discussion in section 2.4 on "Object
+Identity".
+
+
+4.3.4 job-more-info (uri)
+
+Similar to "printer-more-info", this attribute contains the URI
+referencing some resource with more information about this Job object,
+perhaps an HTML page containing information about the Job.
+
+
+4.3.5 job-name (name(MAX))
+
+This REQUIRED attribute is the name of the job. It is a name that is
+more user friendly than the "job-uri" attribute value. It does not need
+to be unique between Jobs. The Job's "job-name" attribute is set to the
+value supplied by the client in the "job-name" operation attribute in
+the create request (see Section 3.2.1.1). If, however, the "job-name"
+operation attribute is not supplied by the client in the create request,
+the Printer object, on creation of the Job, MUST generate a name. The
+printer SHOULD generate the value of the Job's "job-name" attribute from
+the first of the following sources that produces a value: 1) the
+"document-name" operation attribute of the first (or only) document, 2)
+the "document-URI" attribute of the first (or only) document, or 3) any
+other piece of Job specific and/or Document Content information.
+
+
+4.3.6 job-originating-user-name (name(MAX))
+
+This REQUIRED attribute contains the name of the end user that submitted
+the print job. The Printer object sets this attribute to the most
+authenticated printable name that it can obtain from the authentication
+service over which the IPP operation was received. Only if such is not
+available, does the Printer object use the value supplied by the client
+in the "requesting-user-name" operation attribute of the create
+operation (see Section 8).
+
+Note: The Printer object needs to keep an internal originating user id
+of some form, typically as a credential of a principal, with the Job
+object. Since such an internal attribute is implementation-dependent
+and not of interest to clients, it is not specified as a Job Description
+attribute. This originating user id is used for authorization checks
+(if any) on all subsequent operation.
+
+
+4.3.7 job-state (type1 enum)
+
+This REQUIRED attribute identifies the current state of the job. Even
+though the IPP protocol defines seven values for job states (plus the
+out-of-band 'unknown' value - see Section 4.1), implementations only
+need to support those states which are appropriate for the particular
+implementation. In other words, a Printer supports only those job
+states implemented by the output device and available to the Printer
+object implementation.
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 95]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+Standard enum values are:
+
+ Values Symbolic Name and Description
+
+ '3' 'pending': The job is a candidate to start processing, but is
+ not yet processing.
+
+ '4' 'pending-held': The job is not a candidate for processing for
+ any number of reasons but will return to the 'pending'
+ state as soon as the reasons are no longer present. The
+ job's "job-state-reason" attribute MUST indicate why the
+ job is no longer a candidate for processing.
+
+ '5' 'processing': One or more of:
+
+ 1. the job is using, or is attempting to use, one or
+ more purely software processes that are analyzing,
+ creating, or interpreting a PDL, etc.,
+ 2. the job is using, or is attempting to use, one or
+ more hardware devices that are interpreting a PDL, making
+ marks on a medium, and/or performing finishing, such as
+ stapling, etc.,
+ 3. the Printer object has made the job ready for
+ printing, but the output device is not yet printing it,
+ either because the job hasn't reached the output device
+ or because the job is queued in the output device or some
+ other spooler, awaiting the output device to print it.
+
+
+ When the job is in the 'processing' state, the entire job
+ state includes the detailed status represented in the
+ Printer object's "printer-state", "printer-state-
+ reasons", and "printer-state-message" attributes.
+
+ Implementations MAY, though they NEED NOT, include
+ additional values in the job's "job-state-reasons"
+ attribute to indicate the progress of the job, such as
+ adding the 'job-printing' value to indicate when the
+ output device is actually making marks on paper and/or
+ the 'processing-to-stop-point' value to indicate that the
+ IPP object is in the process of canceling or aborting the
+ job. Most implementations won't bother with this nuance.
+
+
+ '6' 'processing-stopped': The job has stopped while processing
+ for any number of reasons and will return to the
+ 'processing' state as soon as the reasons are no longer
+ present.
+
+
+ The job's "job-state-reason" attribute MAY indicate why
+ the job has stopped processing. For example, if the
+ output device is stopped, the 'printer-stopped' value MAY
+ be included in the job's "job-state-reasons" attribute.
+
+
+
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 96]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+ Note: When an output device is stopped, the device
+ usually indicates its condition in human readable form
+ locally at the device. A client can obtain more complete
+ device status remotely by querying the Printer object's
+ "printer-state", "printer-state-reasons" and "printer-
+ state-message" attributes.
+
+
+ '7' 'canceled': The job has been canceled by a Cancel-Job
+ operation and the Printer object has completed canceling
+ the job and all job status attributes have reached their
+ final values for the job. While the Printer object is
+ canceling the job, the job remains in its current state,
+ but the job's "job-state-reasons" attribute SHOULD
+ contain the 'processing-to-stop-point' value and one of
+ the 'canceled-by-user', 'canceled-by-operator', or
+ 'canceled-at-device' value. When the job moves to the
+ 'canceled' state, the 'processing-to-stop-point' value,
+ if present, MUST be removed, but the 'canceled-by-xxx',
+ if present, MUST remain.
+
+ '8' 'aborted': The job has been aborted by the system, usually
+ while the job was in the 'processing' or 'processing-
+ stopped' state and the Printer has completed aborting the
+ job and all job status attributes have reached their
+ final values for the job. While the Printer object is
+ aborting the job, the job remains in its current state,
+ but the job's "job-state-reasons" attribute SHOULD
+ contain the 'processing-to-stop-point' and 'aborted-by-
+ system' values. When the job moves to the 'aborted'
+ state, the 'processing-to-stop-point' value, if present,
+ MUST be removed, but the 'aborted-by-system' value, if
+ present, MUST remain.
+
+ '9' 'completed': The job has completed successfully or with
+ warnings or errors after processing and all of the job
+ media sheets have been successfully stacked in the
+ appropriate output bin(s) and all job status attributes
+ have reached their final values for the job. The job's
+ "job-state-reasons" attribute SHOULD contain one of:
+ 'completed-successfully', 'completed-with-warnings', or
+ 'completed-with-errors' values.
+
+
+The final value for this attribute MUST be one of: 'completed',
+'canceled', or 'aborted' before the Printer removes the job altogether.
+The length of time that jobs remain in the 'canceled', 'aborted', and
+'completed' states depends on implementation. See section 4.3.7.2.
+
+The following figure shows the normal job state transitions.
+
+
+
+
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 97]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+ +----> canceled
+ /
+ +----> pending --------> processing ---------+------> completed
+ | ^ ^ \
+--->+ | | +----> aborted
+ | v v /
+ +----> pending-held processing-stopped ---+
+
+
+Normally a job progresses from left to right. Other state transitions
+are unlikely, but are not forbidden. Not shown are the transitions to
+the 'canceled' state from the 'pending', 'pending-held', and
+'processing-stopped' states.
+
+Jobs reach one of the three terminal states: 'completed', 'canceled', or
+'aborted', after the jobs have completed all activity, including
+stacking output media, after the jobs have completed all activity, and
+all job status attributes have reached their final values for the job.
+
+
+4.3.7.1 Forwarding Servers
+
+As with all other IPP attributes, if the implementation cannot determine
+the correct value for this attribute, it SHOULD respond with the out-of-
+band value 'unknown' (see section 4.1) rather than try to guess at some
+possibly incorrect value and give the end user the wrong impression
+about the state of the Job object. For example, if the implementation
+is just a gateway into some printing system from which it can normally
+get status, but temporarily is unable, then the implementation should
+return the 'unknown' value. However, if the implementation is a gateway
+to a printing system that never provides detailed status about the print
+job, the implementation MAY set the IPP Job object's state to
+'completed', provided that it also sets the 'queued-in-device' value in
+the job's "job-state-reasons" attribute (see section 4.3.8).
+
+
+4.3.7.2 Partitioning of Job States
+
+This section partitions the 7 job states into phases: Job Not
+Completed, Job Retention, Job History, and Job Removal. This section
+also explains the 'job-restartable' value of the "job-state-reasons" Job
+Description attribute for use with the Restart-Job operation.
+
+Job Not Completed: When a job is in the 'pending', 'pending-held',
+'processing', or 'processing-stopped' states, the job is not completed.
+
+Job Retention: When a job enters one of the three terminal job states:
+'completed', 'canceled', or 'aborted', the IPP Printer object MAY
+"retain" the job in a restartable condition for an implementation-
+defined time period. This time period MAY be zero seconds and MAY
+depend on the terminal job state. This phase is called Job Retention.
+While in the Job Retention phase, the job's document data is retained
+and a client may restart the job using the Restart-Job operation. If
+the IPP object supports the Restart-Job operation, then it SHOULD
+indicate that the job is restartable by adding the 'job-restartable'
+
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 98]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+value to the job's "job-state-reasons" attribute (see Section 4.3.8)
+during the Job Retention phase.
+
+Job History: After the Job Retention phase expires for a job, the
+Printer object deletes the document data for the job and the job becomes
+part of the Job History. The Printer object MAY also delete any number
+of the job attributes. Since the job is no longer restartable, the
+Printer object MUST remove the 'job-restartable' value from the job's
+"job-state-reasons" attribute, if present.
+
+Job Removal: After the job has remained in the Job History for an
+implementation-defined time, such as when the number of jobs exceeds a
+fixed number or after a fixed time period (which MAY be zero seconds),
+the IPP Printer removes the job from the system.
+
+Using the Get-Jobs operation and supplying the 'not-completed' value for
+the "which-jobs" operation attribute, a client is requesting jobs in the
+Job Not Completed phase. Using the Get-Jobs operation and supplying the
+'completed' value for the "which-jobs" operation attribute, a client is
+requesting jobs in the Job Retention and Job History phases. Using the
+Get-Job-Attributes operation, a client is requesting a job in any phase
+except Job Removal. After Job Removal, the Get-Job-Attributes and Get-
+Jobs operations no longer are capable of returning any information about
+a job.
+
+
+4.3.8 job-state-reasons (1setOf type2 keyword)
+
+This REQUIRED attribute provides additional information about the job's
+current state, i.e., information that augments the value of the job's
+"job-state" attribute.
+
+These values MAY be used with any job state or states for which the
+reason makes sense. Some of these value definitions indicate
+conformance requirements; the rest are OPTIONAL. Furthermore, when
+implemented, the Printer MUST return these values when the reason
+applies and MUST NOT return them when the reason no longer applies
+whether the value of the Job's "job-state" attribute changed or not.
+When the Job does not have any reasons for being in its current state,
+the value of the Job's "job-state-reasons" attribute MUST be 'none'.
+
+Note: While values cannot be added to the 'job-state' attribute without
+impacting deployed clients that take actions upon receiving "job-state"
+values, it is the intent that additional "job-state-reasons" values can
+be defined and registered without impacting such deployed clients. In
+other words, the "job-state-reasons" attribute is intended to be
+extensible.
+
+The following standard keyword values are defined. For ease of
+understanding, the values are presented in the order in which the
+reasons are likely to occur (if implemented), starting with the 'job-
+incoming' value:
+
+ 'none': There are no reasons for the job's current state. This
+ state reason is semantically equivalent to "job-state-reasons"
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 99]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+ without any value and MUST be used when there is no other value,
+ since the 1setOf attribute syntax requires at least one value.
+ 'job-incoming': The Create-Job operation has been accepted by the
+ Printer, but the Printer is expecting additional Send-Document
+ and/or Send-URI operations and/or is accessing/accepting document
+ data.
+ 'job-data-insufficient': The Create-Job operation has been accepted
+ by the Printer, but the Printer is expecting additional document
+ data before it can move the job into the 'processing' state. If a
+ Printer starts processing before it has received all data, the
+ Printer removes the 'job-data-insufficient' reason, but the 'job-
+ incoming' remains. If a Printer starts processing after it has
+ received all data, the Printer removes the 'job-data-insufficient'
+ reason and the 'job-incoming' at the same time.
+ 'document-access-error': After accepting a Print-URI or Send-URI
+ request, the Printer could not access one or more documents passed
+ by reference. This reason is intended to cover any file access
+ problem, including file does not exist and access denied because of
+ an access control problem. The Printer MAY also indicate the
+ document access error using the "job-document-access-errors" Job
+ Description attribute (see section 4.3.11). Whether the Printer
+ aborts the job and moves the job to the 'aborted' job state or
+ prints all documents that are accessible and moves the job to the
+ 'completed' job state and adds the 'completed-with-errors' value in
+ the job's "job-state-reasons" attribute depends on implementation
+ and/or site policy. This value SHOULD be supported if the Print-
+ URI or Send-URI operations are supported.
+ 'submission-interrupted': The job was not completely submitted for
+ some unforeseen reason, such as: (1) the Printer has crashed before
+ the job was closed by the client, (2) the Printer or the document
+ transfer method has crashed in some non-recoverable way before the
+ document data was entirely transferred to the Printer, (3) the
+ client crashed or failed to close the job before the time-out
+ period. See section 4.4.31.
+ 'job-outgoing': The Printer is transmitting the job to the output
+ device.
+ 'job-hold-until-specified': The value of the job's "job-hold-until"
+ attribute was specified with a time period that is still in the
+ future. The job MUST NOT be a candidate for processing until this
+ reason is removed and there are no other reasons to hold the job.
+ This value SHOULD be supported if the "job-hold-until" Job Template
+ attribute is supported.
+ 'resources-are-not-ready': At least one of the resources needed by
+ the job, such as media, fonts, resource objects, etc., is not ready
+ on any of the physical printer's for which the job is a candidate.
+ This condition MAY be detected when the job is accepted, or
+ subsequently while the job is pending or processing, depending on
+ implementation. The job may remain in its current state or be
+ moved to the 'pending-held' state, depending on implementation
+ and/or job scheduling policy.
+ 'printer-stopped-partly': The value of the Printer's "printer-state-
+ reasons" attribute contains the value 'stopped-partly'.
+ 'printer-stopped': The value of the Printer's "printer-state"
+ attribute is 'stopped'.
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 100]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+ 'job-interpreting': Job is in the 'processing' state, but more
+ specifically, the Printer is interpreting the document data.
+ 'job-queued': Job is in the 'processing' state, but more
+ specifically, the Printer has queued the document data.
+ 'job-transforming': Job is in the 'processing' state, but more
+ specifically, the Printer is interpreting document data and
+ producing another electronic representation.
+ 'job-queued-for-marker': Job is in any of the 'pending-held',
+ 'pending', or 'processing' states, but more specifically, the
+ Printer has completed enough processing of the document to be able
+ to start marking and the job is waiting for the marker. Systems
+ that require human intervention to release jobs using the Release-
+ Job operation, put the job into the 'pending-held' job state.
+ Systems that automatically select a job to use the marker put the
+ job into the 'pending' job state or keep the job in the
+ 'processing' job state while waiting for the marker, depending on
+ implementation. All implementations put the job into (or back
+ into) the 'processing' state when marking does begin.
+ 'job-printing': The output device is marking media. This value is
+ useful for Printers which spend a great deal of time processing (1)
+ when no marking is happening and then want to show that marking is
+ now happening or (2) when the job is in the process of being
+ canceled or aborted while the job remains in the 'processing'
+ state, but the marking has not yet stopped so that impression or
+ sheet counts are still increasing for the job.
+ 'job-canceled-by-user': The job was canceled by the owner of the job
+ using the Cancel-Job request, i.e., by a user whose authenticated
+ identity is the same as the value of the originating user that
+ created the Job object, or by some other authorized end-user, such
+ as a member of the job owner's security group. This value SHOULD
+ be supported.
+ 'job-canceled-by-operator': The job was canceled by the operator
+ using the Cancel-Job request, i.e., by a user who has been
+ authenticated as having operator privileges (whether local or
+ remote). If the security policy is to allow anyone to cancel
+ anyone's job, then this value may be used when the job is canceled
+ by other than the owner of the job. For such a security policy, in
+ effect, everyone is an operator as far as canceling jobs with IPP
+ is concerned. This value SHOULD be supported if the implementation
+ permits canceling by other than the owner of the job.
+ 'job-canceled-at-device': The job was canceled by an unidentified
+ local user, i.e., a user at a console at the device. This value
+ SHOULD be supported if the implementation supports canceling jobs
+ at the console.
+ 'aborted-by-system': The job (1) is in the process of being aborted,
+ (2) has been aborted by the system and placed in the 'aborted'
+ state, or (3) has been aborted by the system and placed in the
+ 'pending-held' state, so that a user or operator can manually try
+ the job again. This value SHOULD be supported.
+ 'unsupported-compression': The job was aborted by the system because
+ the Printer determined while attempting to decompress the document-
+ data's that the compression is actually not among those supported
+ by the Printer. This value MUST be supported, since "compressions
+ is a REQUIRED operation attribute.
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 101]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+ 'compression-error': The job was aborted by the system because the
+ Printer encountered an error in the document-data while
+ decompressing it. If the Printer posts this reason, the document-
+ data has already passed any tests that would have led to the
+ 'unsupported-compression' job-state-reason.
+ 'unsupported-document-format': The job was aborted by the system
+ because the document-data's document-format is not among those
+ supported by the Printer. If the client specifies the document-
+ format as 'application/octet-stream', the printer MAY abort the job
+ and post this reason even though the format is a member of the
+ "document-format-supported" printer attribute, but not among the
+ auto-sensed document-formats. This value MUST be supported, since
+ "document-format" is a REQUIRED operation attribute.
+ 'document-format-error': The job was aborted by the system because
+ the Printer encountered an error in the document-data while
+ processing it. If the Printer posts this reason, the document-data
+ has already passed any tests that would have led to the
+ 'unsupported-document-format' job-state-reason.
+ 'processing-to-stop-point': The requester has issued a Cancel-Job
+ operation or the Printer object has aborted the job, but is still
+ performing some actions on the job until a specified stop point
+ occurs or job termination/cleanup is completed.
+
+ If the implementation requires some measurable time to cancel the
+ job in the 'processing' or 'processing-stopped' job states, the IPP
+ object MUST use this value to indicate that the Printer object is
+ still performing some actions on the job while the job remains in
+ the 'processing' or 'processing-stopped' state. After all the
+ job's job description attributes have stopped incrementing, the
+ Printer object moves the job from the 'processing' state to the
+ 'canceled' or 'aborted' job states.
+
+ 'service-off-line': The Printer is off-line and accepting no jobs.
+ All 'pending' jobs are put into the 'pending-held' state. This
+ situation could be true if the service's or document transform's
+ input is impaired or broken.
+ 'job-completed-successfully': The job completed successfully. This
+ value SHOULD be supported.
+ 'job-completed-with-warnings': The job completed with warnings.
+ This value SHOULD be supported if the implementation detects
+ warnings.
+ 'job-completed-with-errors': The job completed with errors (and
+ possibly warnings too). This value SHOULD be supported if the
+ implementation detects errors.
+ 'job-restartable' - This job is retained (see section 4.3.7.2) and is
+ currently able to be restarted using the Restart-Job operation (see
+ section 3.3.7). If 'job-restartable' is a value of the job's 'job-
+ state-reasons' attribute, then the IPP object MUST accept a
+ Restart-Job operation for that job. This value SHOULD be supported
+ if the Restart-Job operation is supported.
+ 'queued-in-device': The job has been forwarded to a device or print
+ system that is unable to send back status. The Printer sets the
+ job's "job-state " attribute to 'completed' and adds the 'queued-
+ in-device' value to the job's "job-state-reasons" attribute to
+ indicate that the Printer has no additional information about the
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 102]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+ job and never will have any better information. See section
+ 4.3.7.1.
+
+4.3.9 job-state-message (text(MAX))
+
+This attribute specifies information about the "job-state" and "job-
+state-reasons" attributes in human readable text. If the Printer object
+supports this attribute, the Printer object MUST be able to generate
+this message in any of the natural languages identified by the Printer's
+"generated-natural-language-supported" attribute (see the "attributes-
+natural-language" operation attribute specified in Section 3.1.4.1).
+
+The value SHOULD NOT contain additional information not contained in the
+values of the "job-state" and "job-states-reasons" attributes, such as
+interpreter error information. Otherwise, application programs might
+attempt to parse the (localized text). For such additional information
+such as interpreter errors for application program consumption or
+specific document access errors, new attributes with keyword values,
+needs to be developed and registered.
+
+
+4.3.10 job-detailed-status-messages (1setOf text(MAX))
+
+This attribute specifies additional detailed and technical information
+about the job. Neither the Printer nor the client localizes the
+message(s), since they are intended for use by the system administrator
+or other experienced technical persons. Clients MUST NOT attempt to
+parse the value of this attribute. See "job-document-access-errors"
+(section 4.3.11) for additional errors that a program can process.
+
+
+4.3.11 job-document-access-errors (1setOf text(MAX))
+
+This attribute provides additional information about each document
+access error for this job encountered by the Printer after it returned a
+response to the Print-URI or Send-URI operation and subsequently
+attempted to access document(s) supplied in the Print-URI or Send-URI
+operation. For errors in the protocol that is identified by the URI
+scheme in the "document-uri" operation attribute, such as 'http:' or
+'ftp:', the error code is returned in parentheses, followed by the URI.
+For example:
+
+ (404) http://ftp.pwg.org/pub/pwg/ipp/new_MOD/ipp-model-v11-
+ 990510.pdf
+
+Most Internet protocols use decimal error codes (unlike IPP), so the
+ASCII error code representation is in decimal.
+
+4.3.12 number-of-documents (integer(0:MAX))
+
+This attribute indicates the number of documents in the job, i.e., the
+number of Send-Document, Send-URI, Print-Job, or Print-URI operations
+that the Printer has accepted for this job, regardless of whether the
+document data has reached the Printer object or not.
+
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 103]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+Implementations supporting the OPTIONAL Create-Job/Send-Document/Send-
+URI operations SHOULD support this attribute so that clients can query
+the number of documents in each job.
+
+
+4.3.13 output-device-assigned (name(127))
+
+This attribute identifies the output device to which the Printer object
+has assigned this job. If an output device implements an embedded
+Printer object, the Printer object NEED NOT set this attribute. If a
+print server implements a Printer object, the value MAY be empty (zero-
+length string) or not returned until the Printer object assigns an
+output device to the job. This attribute is particularly useful when a
+single Printer object support multiple devices (so called "fan-out").
+
+
+4.3.14 Event Time Job Description Attributes
+
+This section defines the Job Description attributes that indicate the
+time at which certain events occur for a job. If the job event has not
+yet occurred, then the IPP object MUST return the 'no-value' out-of-band
+value (see the beginning of Section 4.1). The "time-at-xxx(integer)"
+attributes represent time as an 'integer' representing the number of
+seconds since the device was powered up (informally called "time
+ticks"). The "date-time-at-xxx(dateTime)" attributes represent time as
+'dateTime' representing date and time (including an offset from UTC).
+
+In order to populate these attributes, the Printer object copies the
+value(s) of the following Printer Description attributes at the time the
+event occurs:
+
+ 1. the value in the Printer's "printer-up-time" attribute for the
+ "time-at-xxx(integer)" attributes
+
+ 2. the value in the Printer's "printer-current-time" attribute for the
+ "date-time-at-xxx(dateTime)" attributes.
+
+If the Printer resets its "printer-up-time" attribute to 1 on power-up
+(see section 4.4.29) and has persistent jobs, then it MUST change all of
+jobs' "time-at-xxx(integer)" (time tick) job attributes whose events
+have occurred either to:
+
+ 1. 0 to indicate that the event happened before the most recent power
+ up OR
+
+ 2. the negative of the number of seconds before the most recent power-
+ up that the event took place, though the negative number NEED NOT
+ reflect the exact number of seconds.
+
+If a client queries a "time-at-xxx(integer)" time tick Job attribute and
+finds the value to be 0 or negative, the client MUST assume that the
+event occurred in some life other than the Printer's current life.
+
+Note: A Printer does not change the values of any "date-time-at-
+xxx(dateTime)" job attributes on power-up.
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 104]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+4.3.14.1 time-at-creation (integer(MIN:MAX))
+
+This REQUIRED attribute indicates the time at which the Job object was
+created.
+
+
+4.3.14.2 time-at-processing (integer(MIN:MAX))
+
+This REQUIRED attribute indicates the time at which the Job object first
+began processing after the create operation or the most recent Restart-
+Job operation. The out-of-band 'no-value' value is returned if the job
+has not yet been in the 'processing' state (see the beginning of Section
+4.1).
+
+
+4.3.14.3 time-at-completed (integer(MIN:MAX))
+
+This REQUIRED attribute indicates the time at which the Job object
+completed (or was cancelled or aborted). The out-of-band 'no-value'
+value is returned if the job has not yet completed, been canceled, or
+aborted (see the beginning of Section 4.1).
+
+
+4.3.14.4 job-printer-up-time (integer(1:MAX))
+
+This REQUIRED Job Description attribute indicates the amount of time (in
+seconds) that the Printer implementation has been up and running. This
+attribute is an alias for the "printer-up-time" Printer Description
+attribute (see Section 4.4.29).
+
+A client MAY request this attribute in a Get-Job-Attributes or Get-Jobs
+request and use the value returned in combination with other requested
+Event Time Job Description Attributes in order to display time
+attributes to a user. The difference between this attribute and the
+'integer' value of a "time-at-xxx" attribute is the number of seconds
+ago that the "time-at-xxx" event occurred. A client can compute the
+wall-clock time at which the "time-at-xxx" event occurred by subtracting
+this difference from the client.s wall-clock time.
+
+
+4.3.14.5 date-time-at-creation (dateTime)
+
+This attribute indicates the date and time at which the Job object was
+created.
+
+
+4.3.14.6 date-time-at-processing (dateTime)
+
+This attribute indicates the date and time at which the Job object first
+began processing after the create operation or the most recent Restart-
+Job operation.
+
+
+4.3.14.7 date-time-at-completed (dateTime)
+
+This attribute indicates the date and time at which the Job object
+completed (or was cancelled or aborted).
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 105]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+4.3.15 number-of-intervening-jobs (integer(0:MAX))
+
+This attribute indicates the number of jobs that are "ahead" of this job
+in the relative chronological order of expected time to complete (i.e.,
+the current scheduled order). For efficiency, it is only necessary to
+calculate this value when an operation is performed that requests this
+attribute.
+
+
+4.3.16 job-message-from-operator (text(127))
+
+This attribute provides a message from an operator, system administrator
+or "intelligent" process to indicate to the end user the reasons for
+modification or other management action taken on a job.
+
+
+4.3.17 Job Size Attributes
+
+This sub-section defines job attributes that describe the size of the
+job. These attributes are not intended to be counters; they are
+intended to be useful routing and scheduling information if known. For
+these attributes, the Printer object may try to compute the value if it
+is not supplied in the create request. Even if the client does supply a
+value for these three attributes in the create request, the Printer
+object MAY choose to change the value if the Printer object is able to
+compute a value which is more accurate than the client supplied value.
+The Printer object may be able to determine the correct value for these
+attributes either right at job submission time or at any later point in
+time.
+
+
+4.3.17.1 job-k-octets (integer(0:MAX))
+
+This attribute specifies the total size of the document(s) in K octets,
+i.e., in units of 1024 octets requested to be processed in the job. The
+value MUST be rounded up, so that a job between 1 and 1024 octets MUST
+be indicated as being 1, 1025 to 2048 MUST be 2, etc.
+
+This value MUST NOT include the multiplicative factors contributed by
+the number of copies specified by the "copies" attribute, independent of
+whether the device can process multiple copies without making multiple
+passes over the job or document data and independent of whether the
+output is collated or not. Thus the value is independent of the
+implementation and indicates the size of the document(s) measured in K
+octets independent of the number of copies.
+
+This value MUST also not include the multiplicative factor due to a
+copies instruction embedded in the document data. If the document data
+actually includes replications of the document data, this value will
+include such replication. In other words, this value is always the size
+of the source document data, rather than a measure of the hardcopy
+output to be produced.
+
+
+
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 106]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+4.3.17.2 job-impressions (integer(0:MAX))
+
+This attribute specifies the total size in number of impressions of the
+document(s) being submitted (see the definition of impression in section
+12.2.5).
+
+As with "job-k-octets", this value MUST NOT include the multiplicative
+factors contributed by the number of copies specified by the "copies"
+attribute, independent of whether the device can process multiple copies
+without making multiple passes over the job or document data and
+independent of whether the output is collated or not. Thus the value is
+independent of the implementation and reflects the size of the
+document(s) measured in impressions independent of the number of copies.
+
+As with "job-k-octets", this value MUST also not include the
+multiplicative factor due to a copies instruction embedded in the
+document data. If the document data actually includes replications of
+the document data, this value will include such replication. In other
+words, this value is always the number of impressions in the source
+document data, rather than a measure of the number of impressions to be
+produced by the job.
+
+
+4.3.17.3 job-media-sheets (integer(0:MAX))
+
+This attribute specifies the total number of media sheets to be produced
+for this job.
+
+Unlike the "job-k-octets" and the "job-impressions" attributes, this
+value MUST include the multiplicative factors contributed by the number
+of copies specified by the "copies" attribute and a 'number of copies'
+instruction embedded in the document data, if any. This difference
+allows the system administrator to control the lower and upper bounds of
+both (1) the size of the document(s) with "job-k-octets-supported" and
+"job-impressions-supported" and (2) the size of the job with "job-media-
+sheets-supported".
+
+
+4.3.18 Job Progress Attributes
+
+This sub-section defines job attributes that describe the progress of
+the job. These attributes are intended to be counters. That is, the
+value for a job that has not started processing MUST be 0. When the
+job's "job-state" is 'processing' or 'processing-stopped', this value is
+intended to contain the amount of the job that has been processed to the
+time at which the attributes are requested. When the job enters the
+'completed', 'canceled', or 'aborted' states, these values are the final
+values for the job.
+
+
+4.3.18.1 job-k-octets-processed (integer(0:MAX))
+
+This attribute specifies the total number of octets processed in K
+octets, i.e., in units of 1024 octets so far. The value MUST be rounded
+
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 107]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+up, so that a job between 1 and 1024 octets inclusive MUST be indicated
+as being 1, 1025 to 2048 inclusive MUST be 2, etc.
+
+For implementations where multiple copies are produced by the
+interpreter with only a single pass over the data, the final value MUST
+be equal to the value of the "job-k-octets" attribute. For
+implementations where multiple copies are produced by the interpreter by
+processing the data for each copy, the final value MUST be a multiple of
+the value of the "job-k-octets" attribute.
+
+
+4.3.18.2 job-impressions-completed (integer(0:MAX))
+
+This job attribute specifies the number of impressions completed for the
+job so far. For printing devices, the impressions completed includes
+interpreting, marking, and stacking the output.
+
+
+4.3.18.3 job-media-sheets-completed (integer(0:MAX))
+
+This job attribute specifies the media-sheets completed marking and
+stacking for the entire job so far whether those sheets have been
+processed on one side or on both.
+
+
+4.3.19 attributes-charset (charset)
+
+This REQUIRED attribute is populated using the value in the client
+supplied "attributes-charset" attribute in the create request. It
+identifies the charset (coded character set and encoding method) used by
+any Job attributes with attribute syntax 'text' and 'name' that were
+supplied by the client in the create request. See Section 3.1.4 for a
+complete description of the "attributes-charset" operation attribute.
+
+This attribute does not indicate the charset in which the 'text' and
+'name' values are stored internally in the Job object. The internal
+charset is implementation-defined. The IPP object MUST convert from
+whatever the internal charset is to that being requested in an operation
+as specified in Section 3.1.4.
+
+
+4.3.20 attributes-natural-language (naturalLanguage)
+
+This REQUIRED attribute is populated using the value in the client
+supplied "attributes-natural-language" attribute in the create request.
+It identifies the natural language used for any Job attributes with
+attribute syntax 'text' and 'name' that were supplied by the client in
+the create request. See Section 3.1.4 for a complete description of the
+"attributes-natural-language" operation attribute. See Sections 4.1.1.2
+and 4.1.2.2 for how a Natural Language Override may be supplied
+explicitly for each 'text' and 'name' attribute value that differs from
+the value identified by the "attributes-natural-language" attribute.
+
+
+
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 108]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+4.4 Printer Description Attributes
+
+
+These attributes form the attribute group called "printer-description".
+The following table summarizes these attributes, their syntax, and
+whether or not they are REQUIRED for a Printer object to support. If
+they are not indicated as REQUIRED, they are OPTIONAL. The maximum size
+in octets for 'text' and 'name' attributes is indicated in
+parenthesizes.
+
+Note: How these attributes are set by an Administrator is outside the
+scope of this IPP/1.1 document.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 109]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
++----------------------------+---------------------------+-----------+
+| Attribute | Syntax | REQUIRED? |
++----------------------------+---------------------------+-----------+
+| printer-uri-supported | 1setOf uri | REQUIRED |
++----------------------------+---------------------------+-----------+
+| uri-security-supported | 1setOf type2 keyword | REQUIRED |
++----------------------------+---------------------------+-----------+
+| uri-authentication- | 1setOf type2 keyword | REQUIRED |
+| supported | | |
++----------------------------+---------------------------+-----------+
+| printer-name | name (127) | REQUIRED |
++----------------------------+---------------------------+-----------+
+| printer-location | text (127) | |
++----------------------------+---------------------------+-----------+
+| printer-info | text (127) | |
++----------------------------+---------------------------+-----------+
+| printer-more-info | uri | |
++----------------------------+---------------------------+-----------+
+| printer-driver-installer | uri | |
++----------------------------+---------------------------+-----------+
+| printer-make-and-model | text (127) | |
++----------------------------+---------------------------+-----------+
+| printer-more-info- | uri | |
+| manufacturer | | |
++----------------------------+---------------------------+-----------+
+| printer-state | type1 enum | REQUIRED |
++----------------------------+---------------------------+-----------+
+| printer-state-reasons | 1setOf type2 keyword | REQUIRED |
++----------------------------+---------------------------+-----------+
+| printer-state-message | text (MAX) | |
++----------------------------+---------------------------+-----------+
+| ipp-versions-supported | 1setOf type2 keyword | REQUIRED |
++----------------------------+---------------------------+-----------+
+| operations-supported | 1setOf type2 enum | REQUIRED |
++----------------------------+---------------------------+-----------+
+| ipp-multiple-document-jobs-| boolean | |
+| supported | | |
++----------------------------+---------------------------+-----------+
+| charset-configured | charset | REQUIRED |
++----------------------------+---------------------------+-----------+
+| charset-supported | 1setOf charset | REQUIRED |
++----------------------------+---------------------------+-----------+
+| natural-language-configured| naturalLanguage | REQUIRED |
++----------------------------+---------------------------+-----------+
+| generated-natural-language-| 1setOf naturalLanguage | REQUIRED |
+| supported | | |
++----------------------------+---------------------------+-----------+
+| document-format-default | mimeMediaType | REQUIRED |
++----------------------------+---------------------------+-----------+
+| document-format-supported | 1setOf mimeMediaType | REQUIRED |
++----------------------------+---------------------------+-----------+
+| printer-is-accepting-jobs | boolean | REQUIRED |
++----------------------------+---------------------------+-----------+
+| queued-job-count | integer (0:MAX) | REQUIRED |
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 110]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
++----------------------------+---------------------------+-----------+
+| printer-message-from- | text (127) | |
+| operator | | |
++----------------------------+---------------------------+-----------+
+| color-supported | boolean | |
++----------------------------+---------------------------+-----------+
+| reference-uri-schemes- | 1setOf uriScheme | |
+| supported | | |
++----------------------------+---------------------------+-----------+
+| pdl-override-supported | type2 keyword | REQUIRED |
++----------------------------+---------------------------+-----------+
+| printer-up-time | integer (1:MAX) | REQUIRED |
++----------------------------+---------------------------+-----------+
+| printer-current-time | dateTime | |
++----------------------------+---------------------------+-----------+
+| multiple-operation-time-out| integer (1:MAX) | |
++----------------------------+---------------------------+-----------+
+| compression-supported | 1setOf type3 keyword | REQUIRED |
++----------------------------+---------------------------+-----------+
+| job-k-octets-supported | rangeOfInteger (0:MAX) | |
++----------------------------+---------------------------+-----------+
+| job-impressions-supported | rangeOfInteger (0:MAX) | |
++----------------------------+---------------------------+-----------+
+| job-media-sheets-supported | rangeOfInteger (0:MAX) | |
++----------------------------+---------------------------+-----------+
+| pages-per-minute | integer(0:MAX) | |
++----------------------------+---------------------------+-----------+
+| pages-per-minute-color | integer(0:MAX) | |
++----------------------------+---------------------------+-----------+
+
+
+4.4.1 printer-uri-supported (1setOf uri)
+
+This REQUIRED Printer attribute contains at least one URI for the
+Printer object. It OPTIONALLY contains more than one URI for the
+Printer object. An administrator determines a Printer object's URI(s)
+and configures this attribute to contain those URIs by some means
+outside the scope of this IPP/1.1 document. The precise format of this
+URI is implementation dependent and depends on the protocol. See the
+next two sections for a description of the "uri-security-supported" and
+"uri-authentication-supported" attributes, both of which are the
+REQUIRED companion attributes to this "printer-uri-supported" attribute.
+See section 2.4 on Printer object identity and section 8.2 on security
+and URIs for more information.
+
+
+4.4.2 uri-authentication-supported (1setOf type2 keyword)
+
+This REQUIRED Printer attribute MUST have the same cardinality (contain
+the same number of values) as the "printer-uri-supported" attribute.
+This attribute identifies the Client Authentication mechanism associated
+with each URI listed in the "printer-uri-supported" attribute. The
+Printer object uses the specified mechanism to identify the
+authenticated user (see section 8.3) . The "i th" value in "uri-
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 111]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+authentication-supported" corresponds to the "i th" value in "printer-
+uri-supported" and it describes the authentication mechanisms used by
+the Printer when accessed via that URI. See [IPP-PRO] for more details
+on Client Authentication.
+
+The following standard keyword values are defined:
+
+ 'none': There is no authentication mechanism associated with the URI.
+ The Printer object assumes that the authenticated user is
+ "anonymous".
+ 'requesting-user-name': When a client performs an operation whose
+ target is the associated URI, the Printer object assumes that the
+ authenticated user is specified by the "requesting-user-name"
+ Operation attribute (see section 8.3). If the "requesting-user-
+ name" attribute is absent in a request, the Printer object assumes
+ that the authenticated user is "anonymous".
+ 'basic': When a client performs an operation whose target is the
+ associated URI, the Printer object challenges the client with HTTP
+ basic authentication [RFC2617]. The Printer object assumes that the
+ authenticated user is the name received via the basic
+ authentication mechanism.
+ 'digest': When a client performs an operation whose target is the
+ associated URI, the Printer object challenges the client with HTTP
+ digest authentication [RFC2617]. The Printer object assumes that
+ the authenticated user is the name received via the digest
+ authentication mechanism.
+ 'certificate': When a client performs an operation whose target is
+ the associated URI, the Printer object expects the client to
+ provide a certificate. The Printer object assumes that the
+ authenticated user is the textual name contained within the
+ certificate.
+
+4.4.3 uri-security-supported (1setOf type2 keyword)
+
+This REQUIRED Printer attribute MUST have the same cardinality (contain
+the same number of values) as the "printer-uri-supported" attribute.
+This attribute identifies the security mechanisms used for each URI
+listed in the "printer-uri-supported" attribute. The "i th" value in
+"uri-security-supported" corresponds to the "i th" value in "printer-
+uri-supported" and it describes the security mechanisms used for
+accessing the Printer object via that URI. See [IPP-PRO] for more
+details on security mechanisms.
+
+The following standard keyword values are defined:
+
+ 'none': There are no secure communication channel protocols in use
+ for the given URI.
+ 'ssl3': SSL3 [SSL] is the secure communications channel protocol in
+ use for the given URI.
+ 'tls': TLS [RFC2246] is the secure communications channel protocol
+ in use for the given URI.
+
+
+
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 112]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+This attribute is orthogonal to the definition of a Client
+Authentication mechanism. Specifically, 'none' does not exclude Client
+Authentication. See section 4.4.2.
+
+Consider the following example. For a single Printer object, an
+administrator configures the "printer-uri-supported", "uri-
+authentication-supported" and "uri-security-supported" attributes as
+follows:
+
+ "printer-uri-supported": 'xxx://acme.com/open-use-printer',
+ 'xxx://acme.com/restricted-use-printer', 'xxx://acme.com/private-
+ printer'
+ "uri-authentication-supported": 'none', 'digest', 'basic'
+ "uri-security-supported": 'none', 'none', 'tls'
+
+
+Note: 'xxx' is not a valid scheme. See the IPP/1.1 "Transport and
+Encoding" document [IPP-PRO] for the actual URI schemes to be used in
+object target attributes.
+
+In this case, one Printer object has three URIs.
+
+ - For the first URI, 'xxx://acme.com/open-use-printer', the value
+ 'none' in "uri-security-supported" indicates that there is no
+ secure channel protocol configured to run under HTTP. The value of
+ 'none' in "uri-authentication-supported" indicates that all users
+ are 'anonymous'. There will be no challenge and the Printer will
+ ignore "requesting-user-name".
+ - For the second URI, 'xxx://acme.com/restricted-use-printer', the
+ value 'none' in "uri-security-supported" indicates that there is no
+ secure channel protocol configured to run under HTTP. The value of
+ 'digest' in "uri-authentication-supported" indicates that the
+ Printer will issue a challenge and that the Printer will use the
+ name supplied by the digest mechanism to determine the
+ authenticated user (see section 8.3).
+ - For the third URI, 'xxx://acme.com/private-printer', the value
+ 'tls' in "uri-security-supported" indicates that TLS is being used
+ to secure the channel. The client SHOULD be prepared to use TLS
+ framing to negotiate an acceptable ciphersuite to use while
+ communicating with the Printer object. In this case, the name
+ implies the use of a secure communications channel, but the fact is
+ made explicit by the presence of the 'tls' value in "uri-security-
+ supported". The client does not need to resort to understanding
+ which security it must use by following naming conventions or by
+ parsing the URI to determine which security mechanisms are implied.
+ The value of 'basic' in "uri-authentication-supported" indicates
+ that the Printer will issue a challenge and that the Printer will
+ use the name supplied by the digest mechanism to determine the
+ authenticated user (see section 8.3) . Because this challenge
+ occurs in a tls session, the channel is secure.
+
+
+It is expected that many IPP Printer objects will be configured to
+support only one channel (either configured to use TLS access or not)
+and only one authentication mechanism. Such Printer objects only have
+one URI listed in the "printer-uri-supported" attribute. No matter the
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 113]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+configuration of the Printer object (whether it has only one URI or more
+than one URI), a client MUST supply only one URI in the target "printer-
+uri" operation attribute.
+
+
+4.4.4 printer-name (name(127))
+
+This REQUIRED Printer attribute contains the name of the Printer object.
+It is a name that is more end-user friendly than a URI. An administrator
+determines a printer's name and sets this attribute to that name. This
+name may be the last part of the printer's URI or it may be unrelated.
+In non-US-English locales, a name may contain characters that are not
+allowed in a URI.
+
+
+4.4.5 printer-location (text(127))
+
+This Printer attribute identifies the location of the device. This could
+include things like: "in Room 123A, second floor of building XYZ".
+
+
+4.4.6 printer-info (text(127))
+
+This Printer attribute identifies the descriptive information about this
+Printer object. This could include things like: "This printer can be
+used for printing color transparencies for HR presentations", or "Out of
+courtesy for others, please print only small (1-5 page) jobs at this
+printer", or even "This printer is going away on July 1, 1997, please
+find a new printer".
+
+
+4.4.7 printer-more-info (uri)
+
+This Printer attribute contains a URI used to obtain more information
+about this specific Printer object. For example, this could be an HTTP
+type URI referencing an HTML page accessible to a Web Browser. The
+information obtained from this URI is intended for end user consumption.
+Features outside the scope of IPP can be accessed from this URI. The
+information is intended to be specific to this printer instance and site
+specific services (e.g. job pricing, services offered, end user
+assistance). The device manufacturer may initially populate this
+attribute.
+
+
+4.4.8 printer-driver-installer (uri)
+
+This Printer attribute contains a URI to use to locate the driver
+installer for this Printer object. This attribute is intended for
+consumption by automata. The mechanics of print driver installation is
+outside the scope of this IPP/1.1 document. The device manufacturer may
+initially populate this attribute.
+
+
+4.4.9 printer-make-and-model (text(127))
+
+This Printer attribute identifies the make and model of the device. The
+device manufacturer may initially populate this attribute.
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 114]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+4.4.10 printer-more-info-manufacturer (uri)
+
+This Printer attribute contains a URI used to obtain more information
+about this type of device. The information obtained from this URI is
+intended for end user consumption. Features outside the scope of IPP
+can be accessed from this URI (e.g., latest firmware, upgrades, print
+drivers, optional features available, details on color support). The
+information is intended to be germane to this printer without regard to
+site specific modifications or services. The device manufacturer may
+initially populate this attribute.
+
+
+4.4.11 printer-state (type1 enum)
+
+This REQUIRED Printer attribute identifies the current state of the
+device. The "printer-state reasons" attribute augments the "printer-
+state" attribute to give more detailed information about the Printer in
+the given printer state.
+
+A Printer object need only update this attribute before responding to an
+operation which requests the attribute; the Printer object NEED NOT
+update this attribute continually, since asynchronous event notification
+is not part of IPP/1.1. A Printer NEED NOT implement all values if they
+are not applicable to a given implementation.
+
+The following standard enum values are defined:
+
+ Value Symbolic Name and Description
+
+ '3' 'idle': Indicates that new jobs can start processing without
+ waiting.
+ '4' 'processing': Indicates that jobs are processing; new jobs
+ will wait before processing.
+ '5' 'stopped': Indicates that no jobs can be processed and
+ intervention is required.
+
+Values of "printer-state-reasons", such as 'spool-area-full' and
+'stopped-partly', MAY be used to provide further information.
+
+
+4.4.12 printer-state-reasons (1setOf type2 keyword)
+
+This REQUIRED Printer attribute supplies additional detail about the
+device's state. Some of the these value definitions indicate
+conformance requirements; the rest are OPTIONAL.
+
+Each keyword value MAY have a suffix to indicate its level of severity.
+The three levels are: report (least severe), warning, and error (most
+severe).
+
+ - '-report': This suffix indicates that the reason is a "report". An
+ implementation may choose to omit some or all reports. Some reports
+ specify finer granularity about the printer state; others serve as
+ a precursor to a warning. A report MUST contain nothing that could
+ affect the printed output.
+
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 115]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+ - '-warning': This suffix indicates that the reason is a "warning".
+ An implementation may choose to omit some or all warnings. Warnings
+ serve as a precursor to an error. A warning MUST contain nothing
+ that prevents a job from completing, though in some cases the
+ output may be of lower quality.
+ - '-error': This suffix indicates that the reason is an "error". An
+ implementation MUST include all errors. If this attribute contains
+ one or more errors, printer MUST be in the stopped state.
+
+
+If the implementation does not add any one of the three suffixes, all
+parties MUST assume that the reason is an "error".
+
+If a Printer object controls more than one output device, each value of
+this attribute MAY apply to one or more of the output devices. An error
+on one output device that does not stop the Printer object as a whole
+MAY appear as a warning in the Printer's "printer-state-reasons
+attribute". If the "printer-state" for such a Printer has a value of
+'stopped', then there MUST be an error reason among the values in the
+"printer-state-reasons" attribute.
+
+The following standard keyword values are defined:
+
+ 'other': The device has detected an error other than one listed in
+ this document.
+ 'none': There are not reasons. This state reason is semantically
+ equivalent to "printer-state-reasons" without any value and MUST be
+ used, since the 1setOf attribute syntax requires at least one
+ value.
+ 'media-needed': A tray has run out of media.
+ 'media-jam': The device has a media jam.
+ 'moving-to-paused': Someone has paused the Printer object using the
+ Pause-Printer operation (see section 3.2.7) or other means, but the
+ device(s) are taking an appreciable time to stop. Later, when all
+ output has stopped, the "printer-state" becomes 'stopped', and the
+ 'paused' value replaces the 'moving-to-paused' value in the
+ "printer-state-reasons" attribute. This value MUST be supported,
+ if the Pause-Printer operation is supported and the implementation
+ takes significant time to pause a device in certain circumstances.
+ 'paused': Someone has paused the Printer object using the Pause-
+ Printer operation (see section 3.2.7) or other means and the
+ Printer object's "printer-state" is 'stopped'. In this state, a
+ Printer MUST NOT produce printed output, but it MUST perform other
+ operations requested by a client. If a Printer had been printing a
+ job when the Printer was paused, the Printer MUST resume printing
+ that job when the Printer is no longer paused and leave no evidence
+ in the printed output of such a pause. This value MUST be
+ supported, if the Pause-Printer operation is supported.
+ 'shutdown': Someone has removed a Printer object from service, and
+ the device may be powered down or physically removed. In this
+ state, a Printer object MUST NOT produce printed output, and unless
+ the Printer object is realized by a print server that is still
+ active, the Printer object MUST perform no other operations
+ requested by a client, including returning this value. If a Printer
+ object had been printing a job when it was shutdown, the Printer
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 116]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+ NEED NOT resume printing that job when the Printer is no longer
+ shutdown. If the Printer resumes printing such a job, it may leave
+ evidence in the printed output of such a shutdown, e.g. the part
+ printed before the shutdown may be printed a second time after the
+ shutdown.
+ 'connecting-to-device': The Printer object has scheduled a job on the
+ output device and is in the process of connecting to a shared
+ network output device (and might not be able to actually start
+ printing the job for an arbitrarily long time depending on the
+ usage of the output device by other servers on the network).
+ 'timed-out': The server was able to connect to the output device (or
+ is always connected), but was unable to get a response from the
+ output device.
+ 'stopping': The Printer object is in the process of stopping the
+ device and will be stopped in a while. When the device is stopped,
+ the Printer object will change the Printer object's state to
+ 'stopped'. The 'stopping-warning' reason is never an error, even
+ for a Printer with a single output device. When an output-device
+ ceases accepting jobs, the Printer will have this reason while the
+ output device completes printing.
+ 'stopped-partly': When a Printer object controls more than one output
+ device, this reason indicates that one or more output devices are
+ stopped. If the reason is a report, fewer than half of the output
+ devices are stopped. If the reason is a warning, fewer than all of
+ the output devices are stopped.
+ 'toner-low': The device is low on toner.
+ 'toner-empty': The device is out of toner.
+ 'spool-area-full': The limit of persistent storage allocated for
+ spooling has been reached. The Printer is temporarily unable to
+ accept more jobs. The Printer will remove this value when it is
+ able to accept more jobs. This value SHOULD be used by a non-
+ spooling Printer that only accepts one or a small number jobs at a
+ time or a spooling Printer that has filled the spool space.
+ 'cover-open': One or more covers on the device are open.
+ 'interlock-open': One or more interlock devices on the printer are
+ unlocked.
+ 'door-open': One or more doors on the device are open.
+ 'input-tray-missing': One or more input trays are not in the device.
+ 'media-low': At least one input tray is low on media.
+ 'media-empty': At least one input tray is empty.
+ 'output-tray-missing': One or more output trays are not in the device
+ 'output-area-almost-full': One or more output area is almost full
+ (e.g. tray, stacker, collator).
+ 'output-area-full': One or more output area is full. (e.g. tray,
+ stacker, collator)
+ 'marker-supply-low': The device is low on at least one marker supply.
+ (e.g. toner, ink, ribbon)
+ 'marker-supply-empty: The device is out of at least one marker
+ supply. (e.g. toner, ink, ribbon)
+ 'marker-waste-almost-full': The device marker supply waste receptacle
+ is almost full.
+ 'marker-waste-full': The device marker supply waste receptacle is
+ full.
+ 'fuser-over-temp': The fuser temperature is above normal.
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 117]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+ 'fuser-under-temp': The fuser temperature is below normal.
+ 'opc-near-eol': The optical photo conductor is near end of life.
+ 'opc-life-over': The optical photo conductor is no longer
+ functioning.
+ 'developer-low': The device is low on developer.
+ 'developer-empty: The device is out of developer.
+ 'interpreter-resource-unavailable': An interpreter resource is
+ unavailable (i.e. font, form)
+
+
+
+4.4.13 printer-state-message (text(MAX))
+
+This Printer attribute specifies the additional information about the
+printer state and printer state reasons in human readable text. If the
+Printer object supports this attribute, the Printer object MUST be able
+to generate this message in any of the natural languages identified by
+the Printer's "generated-natural-language-supported" attribute (see the
+"attributes-natural-language" operation attribute specified in Section
+3.1.4.1).
+
+
+4.4.14 ipp-versions-supported (1setOf type2 keyword)
+
+This REQUIRED attribute identifies the IPP protocol version(s) that this
+Printer supports, including major and minor versions, i.e., the version
+numbers for which this Printer implementation meets the conformance
+requirements. For version number validation, the Printer matches the
+(two-octet binary) "version-number" parameter supplied by the client in
+each request (see sections 3.1.1 and 3.1.8) with the (US-ASCII) keyword
+values of this attribute.
+
+The following standard keyword values are defined:
+
+ '1.0': Meets the conformance requirement of IPP version 1.0 as
+ specified in RFC 2566 [RFC2566] and RFC 2565 [RFC2565] including
+ any extensions registered according to Section 6 and any extension
+ defined in this version or any future version of the IPP "Model and
+ Semantics" document or the IPP "Encoding and Transport" document
+ following the rules, if any, when the "version-number" parameter is
+ '1.0'.
+ '1.1': Meets the conformance requirement of IPP version 1.1 as
+ specified in this document and [IPP-PRO] including any extensions
+ registered according to Section 6 and any extension defined in any
+ future versions of the IPP "Model and Semantics" document or the
+ IPP Encoding and Transport document following the rules, if any,
+ when the "version-number" parameter is '1.1'.
+
+4.4.15 operations-supported (1setOf type2 enum)
+
+This REQUIRED Printer attribute specifies the set of supported
+operations for this Printer object and contained Job objects.
+
+This attribute is encoded as any other enum attribute syntax according
+to [IPP-PRO] as 32-bits. However, all 32-bit enum values for this
+attribute MUST NOT exceed 0x00008FFF, since these same values are also
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 118]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+passed in two octets in the "operation-id" parameter (see section 3.1.1)
+in each Protocol request with the two high order octets omitted in order
+to indicate the operation being performed [IPP-PRO].
+
+The following standard enum and "operation-id" (see section 3.1.2)
+values are defined:
+
+ Value Operation Name
+ ----------------- -------------------------------------
+
+ 0x0000 reserved, not used
+ 0x0001 reserved, not used
+ 0x0002 Print-Job
+ 0x0003 Print-URI
+ 0x0004 Validate-Job
+ 0x0005 Create-Job
+ 0x0006 Send-Document
+ 0x0007 Send-URI
+ 0x0008 Cancel-Job
+ 0x0009 Get-Job-Attributes
+ 0x000A Get-Jobs
+ 0x000B Get-Printer-Attributes
+ 0x000C Hold-Job
+ 0x000D Release-Job
+ 0x000E Restart-Job
+ 0x000F reserved for a future operation
+ 0x0010 Pause-Printer
+ 0x0011 Resume-Printer
+ 0x0012 Purge-Jobs
+ 0x0013-0x3FFF reserved for future operations
+ 0x4000-0x8FFF reserved for private extensions
+
+
+The reserved block for private extensions allows for vendors to
+implement private extensions that are guaranteed to not conflict with
+future registered extensions. However, there is no guarantee that two
+or more private extensions will not conflict.
+
+
+4.4.16 multiple-document-jobs-supported (boolean)
+
+This Printer attribute indicates whether or not the Printer supports
+more than one document per job, i.e., more than one Send-Document or
+Send-Data operation with document data. If the Printer supports the
+Create-Job and Send-Document operations (see section 3.2.4 and 3.3.1),
+it MUST support this attribute.
+
+
+4.4.17 charset-configured (charset)
+
+This REQUIRED Printer attribute identifies the charset that the Printer
+object has been configured to represent 'text' and 'name' Printer
+attributes that are set by the operator, system administrator, or
+manufacturer, i.e., for "printer-name" (name), "printer-location"
+(text), "printer-info" (text), and "printer-make-and-model" (text).
+Therefore, the value of the Printer object's "charset-configured"
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 119]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+attribute MUST also be among the values of the Printer object's
+"charset-supported" attribute.
+
+
+4.4.18 charset-supported (1setOf charset)
+
+This REQUIRED Printer attribute identifies the set of charsets that the
+Printer and contained Job objects support in attributes with attribute
+syntax 'text' and 'name'. At least the value 'utf-8' MUST be present,
+since IPP objects MUST support the UTF-8 [RFC2279] charset. If a
+Printer object supports a charset, it means that for all attributes of
+syntax 'text' and 'name' the IPP object MUST (1) accept the charset in
+requests and return the charset in responses as needed.
+
+If more charsets than UTF-8 are supported, the IPP object MUST perform
+charset conversion between the charsets as described in Section 3.1.4.2.
+
+
+4.4.19 natural-language-configured (naturalLanguage)
+
+This REQUIRED Printer attribute identifies the natural language that the
+Printer object has been configured to represent 'text' and 'name'
+Printer attributes that are set by the operator, system administrator,
+or manufacturer, i.e., for "printer-name" (name), "printer-location"
+(text), "printer-info" (text), and "printer-make-and-model" (text).
+When returning these Printer attributes, the Printer object MAY return
+them in the configured natural language specified by this attribute,
+instead of the natural language requested by the client in the
+"attributes-natural-language" operation attribute. See Section 3.1.4.1
+for the specification of the OPTIONAL multiple natural language support.
+Therefore, the value of the Printer object's "natural-language-
+configured" attribute MUST also be among the values of the Printer
+object's "generated-natural-language-supported" attribute.
+
+
+4.4.20 generated-natural-language-supported (1setOf naturalLanguage)
+
+This REQUIRED Printer attribute identifies the natural language(s) that
+the Printer object and contained Job objects support in attributes with
+attribute syntax 'text' and 'name'. The natural language(s) supported
+depends on implementation and/or configuration. Unlike charsets, IPP
+objects MUST accept requests with any natural language or any Natural
+Language Override whether the natural language is supported or not.
+
+If a Printer object supports a natural language, it means that for any
+of the attributes for which the Printer or Job object generates
+messages, i.e., for the "job-state-message" and "printer-state-message"
+attributes and Operation Messages (see Section 3.1.5) in operation
+responses, the Printer and Job objects MUST be able to generate messages
+in any of the Printer's supported natural languages. See section 3.1.4
+for the definition of 'text' and 'name' attributes in operation requests
+and responses.
+
+
+
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 120]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+Note: A Printer object that supports multiple natural languages, often
+has separate catalogs of messages, one for each natural language
+supported.
+
+
+4.4.21 document-format-default (mimeMediaType)
+
+This REQUIRED Printer attribute identifies the document format that the
+Printer object has been configured to assume if the client does not
+supply a "document-format" operation attribute in any of the operation
+requests that supply document data. The standard values for this
+attribute are Internet Media types (sometimes called MIME types). For
+further details see the description of the 'mimeMediaType' attribute
+syntax in Section 4.1.9.
+
+
+4.4.22 document-format-supported (1setOf mimeMediaType)
+
+This REQUIRED Printer attribute identifies the set of document formats
+that the Printer object and contained Job objects can support. For
+further details see the description of the 'mimeMediaType' attribute
+syntax in Section 4.1.9.
+
+
+4.4.23 printer-is-accepting-jobs (boolean)
+
+This REQUIRED Printer attribute indicates whether the printer is
+currently able to accept jobs, i.e., is accepting Print-Job, Print-URI,
+and Create-Job requests. If the value is 'true', the printer is
+accepting jobs. If the value is 'false', the Printer object is
+currently rejecting any jobs submitted to it. In this case, the Printer
+object returns the 'server-error-not-accepting-jobs' status code.
+
+This value is independent of the "printer-state" and "printer-state-
+reasons" attributes because its value does not affect the current job;
+rather it affects future jobs. This attribute, when 'false', causes the
+Printer to reject jobs even when the "printer-state" is 'idle' or, when
+'true', causes the Printer object to accepts jobs even when the
+"printer-state" is 'stopped'.
+
+
+4.4.24 queued-job-count (integer(0:MAX))
+
+This REQUIRED Printer attribute contains a count of the number of jobs
+that are either 'pending', 'processing', 'pending-held', or 'processing-
+stopped' and is set by the Printer object.
+
+
+4.4.25 printer-message-from-operator (text(127))
+
+This Printer attribute provides a message from an operator, system
+administrator or "intelligent" process to indicate to the end user
+information or status of the printer, such as why it is unavailable or
+when it is expected to be available.
+
+
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 121]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+4.4.26 color-supported (boolean)
+
+This Printer attribute identifies whether the device is capable of any
+type of color printing at all, including highlight color. All document
+instructions having to do with color are embedded within the document
+PDL (none are external IPP attributes in IPP/1.1).
+
+Note: end-users are able to determine the nature and details of the
+color support by querying the "printer-more-info-manufacturer" Printer
+attribute.
+
+
+4.4.27 reference-uri-schemes-supported (1setOf uriScheme)
+
+This Printer attribute specifies which URI schemes are supported for use
+in the "document-uri" operation attribute of the Print-URI or Send-URI
+operation. If a Printer object supports these optional operations, it
+MUST support the "reference-uri-schemes-supported" Printer attribute
+with at least the following schemed URI value:
+
+ 'ftp': The Printer object will use an FTP 'get' operation as defined
+ in RFC 2228 [RFC2228] using FTP URLs as defined by [RFC2396]
+ and[RFC2316].
+
+
+The Printer object MAY OPTIONALLY support other URI schemes (see section
+4.1.6).
+
+
+4.4.28 pdl-override-supported (type2 keyword)
+
+This REQUIRED Printer attribute expresses the ability for a particular
+Printer implementation to either attempt to override document data
+instructions with IPP attributes or not.
+
+This attribute takes on the following values:
+
+ - 'attempted': This value indicates that the Printer object attempts
+ to make the IPP attribute values take precedence over embedded
+ instructions in the document data, however there is no guarantee.
+ - 'not-attempted': This value indicates that the Printer object makes
+ no attempt to make the IPP attribute values take precedence over
+ embedded instructions in the document data.
+
+
+Section 15 contains a full description of how this attribute interacts
+with and affects other IPP attributes, especially the "ipp-attribute-
+fidelity" attribute.
+
+
+4.4.29 printer-up-time (integer(1:MAX))
+
+This REQUIRED Printer attribute indicates the amount of time (in
+seconds) that this Printer instance has been up and running. The value
+is a monotonically increasing value starting from 1 when the Printer
+object is started-up (initialized, booted, etc.). This value is used to
+populate the Event Time Job Description Job attributes "time-at-
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 122]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+creation", "time-at-processing", and "time-at-completed" (see section
+4.3.14).
+
+If the Printer object goes down at some value 'n', and comes back up,
+the implementation MAY:
+
+ 1. Know how long it has been down, and resume at some value greater
+ than 'n', or
+ 2. Restart from 1.
+
+In other words, if the device or devices that the Printer object is
+representing are restarted or power cycled, the Printer object MAY
+continue counting this value or MAY reset this value to 1 depending on
+implementation. However, if the Printer object software ceases running,
+and restarts without knowing the last value for "printer-up-time", the
+implementation MUST reset this value to 1. If this value is reset and
+the Printer has persistent jobs, the Printer MUST reset the "time-at-
+xxx(integer) Event Time Job Description attributes according to Section
+4.3.14. An implementation MAY use both implementation alternatives,
+depending on warm versus cold start, respectively.
+
+
+4.4.30 printer-current-time (dateTime)
+
+This Printer attribute indicates the current date and time. This value
+is used to populate the Event Time Job Description attributes: "time-
+at-creation", "time-at-processing", and "time-at-completed" (see Section
+4.3.14).
+
+The date and time is obtained on a "best efforts basis" and does not
+have to be that precise in order to work in practice. A Printer
+implementation sets the value of this attribute by obtaining the date
+and time via some implementation-dependent means, such as getting the
+value from a network time server, initialization at time of manufacture,
+or setting by an administrator. See [IPP-IIG] for examples. If an
+implementation supports this attribute and the implementation knows that
+it has not yet been set, then the implementation MUST return the value
+of this attribute using the out-of-band 'no-value' meaning not
+configured. See the beginning of section 4.1.
+
+The time zone of this attribute NEED NOT be the time zone used by people
+located near the Printer object or device. The client MUST NOT expect
+that the time zone of any received 'dateTime' value to be in the time
+zone of the client or in the time zone of the people located near the
+printer.
+
+The client SHOULD display any dateTime attributes to the user in client
+local time by converting the 'dateTime' value returned by the server to
+the time zone of the client, rather than using the time zone returned by
+the Printer in attributes that use the 'dateTime' attribute syntax.
+
+
+4.4.31 multiple-operation-time-out (integer(1:MAX))
+
+This Printer attributes identifies the minimum time (in seconds) that
+the Printer object waits for additional Send-Document or Send-URI
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 123]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+operations to follow a still-open multi-document Job object before
+taking any recovery actions, such as the ones indicated in section
+3.3.1. If the Printer object supports the Create-Job and Send-Document
+operations (see section 3.2.4 and 3.3.1), it MUST support this
+attribute.
+
+It is RECOMMENDED that vendors supply a value for this attribute that is
+between 60 and 240 seconds. An implementation MAY allow a system
+administrator to set this attribute (by means outside this IPP/1.1
+document). If so, the system administrator MAY be able to set values
+outside this range.
+
+
+4.4.32 compression-supported (1setOf type3 keyword)
+
+This REQUIRED Printer attribute identifies the set of supported
+compression algorithms for document data. Compression only applies to
+the document data; compression does not apply to the encoding of the IPP
+operation itself. The supported values are used to validate the client
+supplied "compression" operation attributes in Print-Job, Send-Document,
+and Send-URI requests.
+
+Standard values are :
+
+ 'none': no compression is used.
+ 'deflate': ZIP public domain inflate/deflate) compression technology
+ 'gzip' GNU zip compression technology described in RFC 1952
+ [RFC1952].
+ 'compress': UNIX compression technology
+
+
+4.4.33 job-k-octets-supported (rangeOfInteger(0:MAX))
+
+This Printer attribute specifies the upper and lower bounds of total
+sizes of jobs in K octets, i.e., in units of 1024 octets. The supported
+values are used to validate the client supplied "job-k-octets" operation
+attributes in create requests. The corresponding job description
+attribute "job-k-octets" is defined in section 4.3.17.1.
+
+
+4.4.34 job-impressions-supported (rangeOfInteger(0:MAX))
+
+This Printer attribute specifies the upper and lower bounds for the
+number of impressions per job. The supported values are used to validate
+the client supplied "job-impressions" operation attributes in create
+requests. The corresponding job description attribute "job-impressions"
+is defined in section 4.3.17.2.
+
+
+4.4.35 job-media-sheets-supported (rangeOfInteger(0:MAX))
+
+This Printer attribute specifies the upper and lower bounds for the
+number of media sheets per job. The supported values are used to
+validate the client supplied "job-media-sheets" operation attributes in
+create requests. The corresponding Job attribute "job-media-sheets" is
+defined in section 4.3.17.3.
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 124]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+4.4.36 pages-per-minute (integer(0:MAX))
+
+This Printer attributes specifies the nominal number of pages per minute
+to the nearest whole number which may be generated by this printer
+(e.g., simplex, black-and-white). This attribute is informative, not a
+service guarantee. Generally, it is the value used in the marketing
+literature to describe the device.
+
+A value of 0 indicates a device that takes more than two minutes to
+process a page.
+
+
+4.4.37 pages-per-minute-color (integer(0:MAX))
+
+This Printer attributes specifies the nominal number of pages per minute
+to the nearest whole number which may be generated by this printer when
+printing color (e.g., simplex, color). For purposes of this attribute,
+"color" means the same as for the "color-supported" attribute, namely,
+the device is capable of any type of color printing at all, including
+highlight color. This attribute is informative, not a service
+guarantee. Generally, it is the value used in the marketing literature
+to describe the color capabilities of this device.
+
+A value of 0 indicates a device that takes more than two minutes to
+process a page.
+
+If a color device has several color modes, it MAY use the pages-per-
+minute value for this attribute that corresponds to the mode that
+produces the highest number.
+
+Black and white only printers MUST NOT support this attribute. If this
+attribute is present, then the "color-supported" Printer description
+attribute MUST be present and have a 'true' value.
+
+The values of these two attributes returned by the Get-Printer-
+Attributes operation MAY be affected by the "document-format" attribute
+supplied by the client in the Get-Printer-Attributes request. In other
+words, the implementation MAY have different speeds depending on the
+document format being processed. See section 3.2.5.1 Get-Printer-
+Attributes.
+
+
+
+5. Conformance
+
+
+This section describes conformance issues and requirements. This
+document introduces model entities such as objects, operations,
+attributes, attribute syntaxes, and attribute values. These conformance
+sections describe the conformance requirements which apply to these
+model entities.
+
+
+
+
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 125]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+5.1 Client Conformance Requirements
+
+
+This section describes the conformance requirements for a client (see
+section 2.1), whether it be:
+
+ 1. contained within software controlled by an end user, e.g. activated
+ by the "Print" menu item in an application that sends IPP requests
+ or
+
+ 2. the print server component that sends IPP requests to either an
+ output device or another "downstream" print server.
+
+A conforming client MUST support all REQUIRED operations as defined in
+this document. For each attribute included in an operation request, a
+conforming client MUST supply a value whose type and value syntax
+conforms to the requirements of the Model document as specified in
+Sections 3 and 4. A conforming client MAY supply any registered
+extensions and/or private extensions in an operation request, as long as
+they meet the requirements in Section 6.
+
+Otherwise, there are no conformance requirements placed on the user
+interfaces provided by IPP clients or their applications. For example,
+one application might not allow an end user to submit multiple documents
+per job, while another does. One application might first query a
+Printer object in order to supply a graphical user interface (GUI)
+dialogue box with supported and default values whereas a different
+implementation might not.
+
+When sending a request, an IPP client NEED NOT supply any attributes
+that are indicated as OPTIONALLY supplied by the client.
+
+A client MUST be able to accept any of the attribute syntaxes defined in
+Section 4.1, including their full range, that may be returned to it in a
+response from a Printer object. In particular for each attribute that
+the client supports whose attribute syntax is 'text', the client MUST
+accept and process both the 'textWithoutLanguage' and 'textWithLanguage'
+forms. Similarly, for each attribute that the client supports whose
+attribute syntax is 'name', the client MUST accept and process both the
+'nameWithoutLanguage' and 'nameWithLanguage' forms. For presentation
+purposes, truncation of long attribute values is not recommended. A
+recommended approach would be for the client implementation to allow the
+user to scroll through long attribute values.
+
+A response MAY contain attribute groups, attributes, attribute syntaxes,
+values, and status codes that the client does not expect. Therefore, a
+client implementation MUST gracefully handle such responses and not
+refuse to inter-operate with a conforming Printer that is returning
+registered or private extensions, including attribute groups,
+attributes, attribute syntaxes, attribute values, and status codes that
+conform to Section 6. Clients may choose to ignore any parameters,
+attributes, attribute syntaxes, or values that they do not understand.
+
+
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 126]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+While a client is sending data to a printer, it SHOULD do its best to
+prevent a channel from being closed by a lower layer when the channel is
+blocked (i.e. flow-controlled off) for whatever reason, e.g. 'out of
+paper' or 'job ahead hasn't freed up enough memory'. However, the layer
+that launched the print submission (e.g. an end user) MAY close the
+channel in order to cancel the job. When a client closes a channel, a
+Printer MAY print all or part of the received portion of the document.
+See the "Encoding and Transport" document [IPP-PRO] for more details.
+
+A client MUST support Client Authentication as defined in the IPP/1.1
+Encoding and Transport document [IPP-PRO]. A client SHOULD support
+Operation Privacy and Server Authentication as defined in the IPP/1.1
+Encoding and Transport document [IPP-PRO]. See also section 8 of this
+document.
+
+
+5.2 IPP Object Conformance Requirements
+
+
+This section specifies the conformance requirements for conforming
+implementations of IPP objects (see section 2). These requirements
+apply to an IPP object whether it is:
+
+ (1) an (embedded) device component that accepts IPP requests and
+ controls the device or
+
+ (2) a component of a print server that accepts IPP requests (where
+ the print server control one or more networked devices using IPP or
+ other protocols).
+
+
+5.2.1 Objects
+
+Conforming implementations MUST implement all of the model objects as
+defined in this document in the indicated sections:
+
+ Section 2.1 - Printer Object
+ Section 2.2 - Job Object
+
+5.2.2 Operations
+
+Conforming IPP object implementations MUST implement all of the REQUIRED
+model operations, including REQUIRED responses, as defined in this
+document in the indicated sections:
+
+ For a Printer object:
+ Print-Job (section 3.2.1) REQUIRED
+ Print-URI (section 3.2.2) OPTIONAL
+ Validate-Job (section 3.2.3) REQUIRED
+ Create-Job (section 3.2.4) OPTIONAL
+ Get-Printer-Attributes (section 3.2.5) REQUIRED
+ Get-Jobs (section 3.2.6) REQUIRED
+ Pause-Printer (section 3.2.7) OPTIONAL
+ Resume-Printer (section 3.2.8) OPTIONAL
+ Purge-Jobs (section 3.2.9) OPTIONAL
+
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 127]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+ For a Job object:
+ Send-Document (section 3.3.1) OPTIONAL
+ Send-URI (section 3.3.2) OPTIONAL
+ Cancel-Job (section 3.3.3) REQUIRED
+ Get-Job-Attributes (section 3.3.4) REQUIRED
+ Hold-Job (section 3.3.5) OPTIONAL
+ Release-Job (section 3.3.6) OPTIONAL
+ Restart-Job (section 3.3.7) OPTIONAL
+
+
+Conforming IPP objects MUST support all REQUIRED operation attributes
+and all values of such attributes if so indicated in the description.
+Conforming IPP objects MUST ignore all unsupported or unknown operation
+attributes or operation attribute groups received in a request, but MUST
+reject a request that contains a supported operation attribute that
+contains an unsupported value.
+
+Conforming IPP objects MAY return operation responses that contain
+attributes groups, attributes names, attribute syntaxes, attribute
+values, and status codes that are extensions to this standard. The
+additional attribute groups MAY occur in any order.
+
+The following section on object attributes specifies the support
+required for object attributes.
+
+
+5.2.3 IPP Object Attributes
+
+Conforming IPP objects MUST support all of the REQUIRED object
+attributes, as defined in this document in the indicated sections.
+
+If an object supports an attribute, it MUST support only those values
+specified in this document or through the extension mechanism described
+in section 5.2.4. It MAY support any non-empty subset of these values.
+That is, it MUST support at least one of the specified values and at
+most all of them.
+
+
+5.2.4 Versions
+
+IPP/1.1 clients MUST meet the conformance requirements for clients
+specified in this document and [IPP-PRO]. IPP/1.1 clients MUST send
+requests containing a "version-number" parameter with a '1.1' value.
+
+IPP/1.1 Printer and Job objects MUST meet the conformance requirements
+for IPP objects specified in this document and [IPP-PRO]. IPP/1.1
+objects MUST accept requests containing a "version-number" parameter
+with a '1.1' value (or reject the request if the operation is not
+supported).
+
+It is beyond the scope of this specification to mandate conformance with
+previous versions. IPP/1.1 was deliberately designed, however, to make
+supporting previous versions easy. It is worth noting that, at the time
+of composing this specification (1999), we would expect IPP/1.1 Printer
+implementations to:
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 128]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+ understand any valid request in the format of IPP/1.0, or 1.1;
+
+ respond appropriately with a response containing the same "version-
+ number" parameter value used by the client in the request.
+
+And we would expect IPP/1.1 clients to:
+
+ understand any valid response in the format of IPP/1.0, or 1.1.
+
+It is recommended that IPP/1.1 clients try supplying alternate version
+numbers if they receive a 'server-error-version-not-supported' error
+return in a response.
+
+.
+
+
+5.2.5 Extensions
+
+A conforming IPP object MAY support registered extensions and private
+extensions, as long as they meet the requirements specified in Section
+6.
+
+For each attribute included in an operation response, a conforming IPP
+object MUST return a value whose type and value syntax conforms to the
+requirement of the Model document as specified in Sections 3 and 4.
+
+
+5.2.6 Attribute Syntaxes
+
+An IPP object MUST be able to accept any of the attribute syntaxes
+defined in Section 4.1, including their full range, in any operation in
+which a client may supply attributes or the system administrator may
+configure attributes (by means outside the scope of this IPP/1.1
+document). In particular for each attribute that the IPP object
+supports whose attribute syntax is 'text', the IPP object MUST accept
+and process both the 'textWithoutLanguage' and 'textWithLanguage' forms.
+Similarly, for each attribute that the IPP object supports whose
+attribute syntax is 'name', the IPP object MUST accept and process both
+the 'nameWithoutLanguage' and 'nameWithLanguage' forms. Furthermore, an
+IPP object MUST return attributes to the client in operation responses
+that conform to the syntax specified in Section 4.1, including their
+full range if supplied previously by a client.
+
+
+5.2.7 Security
+
+An IPP Printer implementation SHOULD contain support for Client
+Authentication as defined in the IPP/1.1 Encoding and Transport document
+[IPP-PRO]. A Printer implementation MAY allow an administrator to
+configure the Printer so that all, some, or none of the users are
+authenticated. See also section 8 of this document.
+
+An IPP Printer implementation SHOULD contain support for Operation
+Privacy and Server Authentication as defined in the IPP/1.1 Encoding and
+Transport document [IPP-PRO]. A Printer implementation MAY allow an
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 129]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+administrator to configure the degree of support for Operation Privacy
+and Server Authentication. See also section 8 of this document.
+
+Security MUST NOT be compromised when a client supplies a lower
+"version-number" parameter in a request. For example, if an IPP/1.1
+conforming Printer object accepts version '1.0' requests and is
+configured to enforce Digest Authentication, it MUST do the same for a
+version '1.0' request.
+
+
+5.3 Charset and Natural Language Requirements
+
+
+All clients and IPP objects MUST support the 'utf-8' charset as defined
+in section 4.1.7.
+
+IPP objects MUST be able to accept any client request which correctly
+uses the "attributes-natural-language" operation attribute or the
+Natural Language Override mechanism on any individual attribute whether
+or not the natural language is supported by the IPP object. If an IPP
+object supports a natural language, then it MUST be able to translate
+(perhaps by table lookup) all generated 'text' or 'name' attribute
+values into one of the supported languages (see section 3.1.4). That
+is, the IPP object that supports a natural language NEED NOT be a
+general purpose translator of any arbitrary 'text' or 'name' value
+supplied by the client into that natural language. However, the object
+MUST be able to translate (automatically generate) any of its own
+attribute values and messages into that natural language.
+
+
+
+6. IANA Considerations (registered and private extensions)
+
+
+This section describes how IPP can be extended to allow the following
+registered and private extensions to IPP:
+
+ 1. keyword attribute values
+ 2. enum attribute values
+ 3. attributes
+ 4. attribute syntaxes
+ 5. operations
+ 6. attribute groups
+ 7. status codes
+
+
+Extensions registered for use with IPP/1.1 are OPTIONAL for client and
+IPP object conformance to the IPP/1.1 Model document.
+
+These extension procedures are aligned with the guidelines as set forth
+by the IESG [IANA-CON]. Section 11 describes how to propose new
+registrations for consideration. IANA will reject registration
+proposals that leave out required information or do not follow the
+appropriate format described in Section 11. IPP/1.1 may also be
+extended by an appropriate RFC that specifies any of the above
+extensions.
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 130]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+6.1 Typed 'keyword' and 'enum' Extensions
+
+
+IPP allows for 'keyword' and 'enum' extensions (see sections 4.1.2.3 and
+4.1.4). This document uses prefixes to the 'keyword' and 'enum' basic
+attribute syntax type in order to communicate extra information to the
+reader through its name. This extra information is not represented in
+the protocol because it is unimportant to a client or Printer object.
+The list below describes the prefixes and their meaning.
+
+ "type1": This IPP specification document must be revised to add a
+ new keyword or a new enum. No private keywords or enums are
+ allowed.
+
+ "type2": Implementers can, at any time, add new keyword or enum
+ values by proposing the complete specification to IANA:
+
+ iana@iana.org
+
+ IANA will forward the registration proposal to the IPP Designated
+ Expert who will review the proposal with a mailing list that the
+ Designated Expert keeps for this purpose. Initially, that list
+ will be the mailing list used by the IPP WG:
+
+ ipp@pwg.org
+
+ even after the IPP WG is disbanded as permitted by [IANA-CON]. The
+ IPP Designated Expert is appointed by the IESG Area Director
+ responsible for IPP, according to [IANA-CON].
+
+ When a type2 keyword or enum is approved, the IPP Designated Expert
+ becomes the point of contact for any future maintenance that might
+ be required for that registration.
+
+ "type3": Implementers can, at any time, add new keyword and enum
+ values by submitting the complete specification to IANA as for
+ type2 who will forward the proposal to the IPP Designated Expert.
+ While no additional technical review is required, the IPP
+ Designated Expert may, at his/her discretion, forward the proposal
+ to the same mailing list as for type2 registrations for advice and
+ comment.
+
+ When a type3 keyword or enum is approved by the IPP Designated
+ Expert, the original proposer becomes the point of contact for any
+ future maintenance that might be required for that registration.
+
+
+For type2 and type3 keywords, the proposer includes the name of the
+keyword in the registration proposal and the name is part of the
+technical review.
+
+After type2 and type3 enums specifications are approved, the IPP
+Designated Expert in consultation with IANA assigns the next available
+enum number for each enum value.
+
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 131]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+IANA will publish approved type2 and type3 keyword and enum attributes
+value registration specifications in:
+
+ ftp.isi.edu/iana/assignments/ipp/attribute-values/xxx/yyy.txt
+
+where xxx is the attribute name that specifies the initial values and
+yyy.txt is a descriptive file name that contains one or more enums or
+keywords approved at the same time. For example, if several additional
+enums for stapling are approved for use with the "finishings" attribute
+(and "finishings-default" and "finishings-supported" attributes), IANA
+will publish the additional values in the file:
+
+ ftp.isi.edu/iana/assignments/ipp/attribute-
+ values/finishings/stapling.txt
+
+Note: Some attributes are defined to be: 'type3 keywords' | 'name' which
+allows for attribute values to be extended by a site administrator with
+administrator defined names. Such names are not registered with IANA.
+
+By definition, each of the three types above assert some sort of
+registry or review process in order for extensions to be considered
+valid. Each higher numbered level (1, 2, 3) tends to be decreasingly
+less stringent than the previous level. Therefore, any typeN value MAY
+be registered using a process for some typeM where M is less than N,
+however such registration is NOT REQUIRED. For example, a type3 value
+MAY be registered in a type 1 manner (by being included in a future
+version of an IPP specification), however, it is NOT REQUIRED.
+
+This document defines keyword and enum values for all of the above
+types, including type3 keywords.
+
+For private (unregistered) keyword extensions, implementers SHOULD use
+keywords with a suitable distinguishing prefix, such as "xxx-" where xxx
+is the (lowercase) fully qualified company name registered with IANA for
+use in domain names [RFC1035]. For example, if the company XYZ Corp.
+had obtained the domain name "XYZ.com", then a private keyword 'abc'
+would be: 'xyz.com-abc'.
+
+Note: RFC 1035 [RFC1035] indicates that while upper and lower case
+letters are allowed in domain names, no significance is attached to the
+case. That is, two names with the same spelling but different case are
+to be treated as if identical. Also, the labels in a domain name must
+follow the rules for ARPANET host names: They must start with a letter,
+end with a letter or digit, and have as interior characters only
+letters, digits, and hyphen. Labels must be 63 characters or less.
+Labels are separated by the "." character.
+
+For private (unregistered) enum extension, implementers MUST use values
+in the reserved integer range which is 2**30 to 2**31-1.
+
+
+
+
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 132]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+6.2 Attribute Extensibility
+
+
+Attribute names are type2 keywords. Therefore, new attributes may be
+registered and have the same status as attributes in this document by
+following the type2 extension rules. For private (unregistered)
+attribute extensions, implementers SHOULD use keywords with a suitable
+distinguishing prefix as described in Section 6.1.
+
+IANA will publish approved attribute registration specifications as
+separate files:
+
+ ftp.isi.edu/iana/assignments/ipp/attributes/xxx-yyy.txt
+
+where "xxx-yyy" is the new attribute name.
+
+If a new Printer object attribute is defined and its values can be
+affected by a specific document format, its specification needs to
+contain the following sentence:
+
+ "The value of this attribute returned in a Get-Printer-
+ Attributes response MAY depend on the "document-format"
+ attribute supplied (see Section 3.2.5.1)."
+
+If the specification does not, then its value in the Get-Printer-
+Attributes response MUST NOT depend on the "document-format" supplied in
+the request. When a new Job Template attribute is registered, the value
+of the Printer attributes MAY vary with "document-format" supplied in
+the request without the specification having to indicate so.
+
+
+6.3 Attribute Syntax Extensibility
+
+
+Attribute syntaxes are like type2 enums. Therefore, new attribute
+syntaxes may be registered and have the same status as attribute
+syntaxes in this document by following the type2 extension rules
+described in Section 6.1. The value codes that identify each of the
+attribute syntaxes are assigned in the "Encoding and Transport" document
+[IPP-PRO], including a designated range for private, experimental use.
+
+For attribute syntaxes, the IPP Designated Expert in consultation with
+IANA assigns the next attribute syntax code in the appropriate range as
+specified in [IPP-PRO]. IANA will publish approved attribute syntax
+registration specifications as separate files:
+
+ ftp.isi.edu/iana/assignments/ipp/attribute-syntaxes/xxx-yyy.txt
+
+where 'xxx-yyy' is the new attribute syntax name.
+
+
+6.4 Operation Extensibility
+
+
+Operations may also be registered following the type2 procedures
+described in Section 6.1, though major new operations will usually be
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 133]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+done by a new standards track RFC that augments this document. For
+private (unregistered) operation extensions, implementers MUST use the
+range for the "operation-id" in requests specified in Section 4.4.15
+"operations-supported" Printer attribute.
+
+For operations, the IPP Designated Expert in consultation with IANA
+assigns the next operation-id code as specified in Section 4.4.15. IANA
+will publish approved operation registration specifications as separate
+files:
+
+ ftp.isi.edu/iana/assignments/ipp/operations/Xxx-Yyy.txt
+
+where "Xxx-Yyy" is the new operation name.
+
+
+6.5 Attribute Groups
+
+
+Attribute groups passed in requests and responses may be registered
+following the type2 procedures described in Section 6.1. The tags that
+identify each of the attribute groups are assigned in [IPP-PRO].
+
+For attribute groups, the IPP Designated Expert in consultation with
+IANA assigns the next attribute group tag code in the appropriate range
+as specified in [IPP-PRO]. IANA will publish approved attribute group
+registration specifications as separate files:
+
+ ftp.isi.edu/iana/assignments/ipp/attribute-group-tags/xxx-yyy-
+ tag.txt
+
+where 'xxx-yyy-tag' is the new attribute group tag name.
+
+
+6.6 Status Code Extensibility
+
+
+Operation status codes may also be registered following the type2
+procedures described in Section 6.1. The values for status codes are
+allocated in ranges as specified in Section 14 for each status code
+class:
+
+ "informational" - Request received, continuing process
+ "successful" - The action was successfully received, understood, and
+ accepted
+ "redirection" - Further action must be taken in order to complete the
+ request
+ "client-error" - The request contains bad syntax or cannot be
+ fulfilled
+ "server-error" - The IPP object failed to fulfill an apparently
+ valid request
+
+
+For private (unregistered) operation status code extensions,
+implementers MUST use the top of each range as specified in Section 13.
+
+For operation status codes, the IPP Designated Expert in consultation
+with IANA assigns the next status code in the appropriate class range as
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 134]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+specified in Section 13. IANA will publish approved status code
+registration specifications as separate files:
+
+ ftp.isi.edu/iana/assignments/ipp/status-codes/xxx-yyy.txt
+
+where "xxx-yyy" is the new operation status code keyword.
+
+
+6.7 Registration of MIME types/sub-types for document-formats
+
+
+The "document-format" attribute's syntax is 'mimeMediaType'. This means
+that valid values are Internet Media Types (see Section 4.1.9). RFC
+2045 [RFC2045] defines the syntax for valid Internet media types. IANA
+is the registry for all Internet media types.
+
+
+6.8 Registration of charsets for use in 'charset' attribute values
+
+The "attributes-charset" attribute's syntax is 'charset'. This means
+that valid values are charsets names. When a charset in the IANA
+registry has more than one name (alias), the name labeled as "(preferred
+MIME name)", if present, MUST be used (see Section 4.1.7). IANA is the
+registry for charsets following the procedures of [RFC2278].
+
+
+7. Internationalization Considerations
+
+
+Some of the attributes have values that are text strings and names which
+are intended for human understanding rather than machine understanding
+(see the 'text' and 'name' attribute syntaxes in Sections 4.1.1 and
+4.1.2).
+
+In each operation request, the client
+
+ - identifies the charset and natural language of the request which
+ affects each supplied 'text' and 'name' attribute value, and
+ - requests the charset and natural language for attributes returned
+ by the IPP object in operation responses (as described in Section
+ 3.1.4.1).
+
+
+In addition, the client MAY separately and individually identify the
+Natural Language Override of a supplied 'text' or 'name' attribute using
+the 'textWithLanguage' and 'nameWithLanguage' technique described
+section 4.1.1.2 and 4.1.2.2 respectively.
+
+All IPP objects MUST support the UTF-8 [RFC2279] charset in all 'text'
+and 'name' attributes supported. If an IPP object supports more than
+the UTF-8 charset, the object MUST convert between them in order to
+return the requested charset to the client according to Section 3.1.4.2.
+If an IPP object supports more than one natural language, the object
+SHOULD return 'text' and 'name' values in the natural language requested
+where those values are generated by the Printer (see Section 3.1.4.1).
+
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 135]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+For Printers that support multiple charsets and/or multiple natural
+languages in 'text' and 'name' attributes, different jobs may have been
+submitted in differing charsets and/or natural languages. All responses
+MUST be returned in the charset requested by the client. However, the
+Get-Jobs operation uses the 'textWithLanguage' and 'nameWithLanguage'
+mechanism to identify the differing natural languages with each job
+attribute returned.
+
+The Printer object also has configured charset and natural language
+attributes. The client can query the Printer object to determine the
+list of charsets and natural languages supported by the Printer object
+and what the Printer object's configured values are. See the "charset-
+configured", "charset-supported", "natural-language-configured", and
+"generated-natural-language-supported" Printer description attributes
+for more details.
+
+The "charset-supported" attributed identifies the supported charsets.
+If a charset is supported, the IPP object MUST be capable of converting
+to and from that charset into any other supported charset. In many
+cases, an IPP object will support only one charset and it MUST be the
+UTF-8 charset.
+
+The "charset-configured" attribute identifies the one supported charset
+which is the native charset given the current configuration of the IPP
+object (administrator defined).
+
+The "generated-natural-language-supported" attribute identifies the set
+of supported natural languages for generated messages; it is not related
+to the set of natural languages that must be accepted for client
+supplied 'text' and 'name' attributes. For client supplied 'text' and
+'name' attributes, an IPP object MUST accept ALL supplied natural
+languages. Just because a Printer object is currently configured to
+support 'en-us' natural language does not mean that the Printer object
+should reject a job if the client supplies a job name that is in 'fr-
+ca'.
+
+The "natural-language-configured" attribute identifies the one supported
+natural language for generated messages which is the native natural
+language given the current configuration of the IPP object
+(administrator defined).
+
+Attributes of type 'text' and 'name' are populated from different
+sources. These attributes can be categorized into following groups
+(depending on the source of the attribute):
+
+ 1. Some attributes are supplied by the client (e.g., the client
+ supplied "job-name", "document-name", and "requesting-user-name"
+ operation attributes along with the corresponding Job object's
+ "job-name" and "job-originating-user-name" attributes). The IPP
+ object MUST accept these attributes in any natural language no
+ matter what the set of supported languages for generated messages
+ 2. Some attributes are supplied by the system administrator (e.g.,
+ the Printer object's "printer-name" and "printer-location"
+ attributes). These too can be in any natural language. If the
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 136]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+ natural language for these attributes is different than what a
+ client requests, then they must be reported using the Natural
+ Language Override mechanism.
+ 3. Some attributes are supplied by the device manufacturer (e.g., the
+ Printer object's "printer-make-and-model" attribute). These too
+ can be in any natural language. If the natural language for these
+ attributes is different than what a client requests, then they must
+ be reported using the Natural Language Override mechanism.
+ 4. Some attributes are supplied by the operator (e.g., the Job
+ object's "job-message-from-operator" attribute). These too can be
+ in any natural language. If the natural language for these
+ attributes is different than what a client requests, then they must
+ be reported using the Natural Language Override mechanism.
+ 5. Some attributes are generated by the IPP object (e.g., the Job
+ object's "job-state-message" attribute, the Printer object's
+ "printer-state-message" attribute, and the "status-message"
+ operation attribute). These attributes can only be in one of the
+ "generated-natural-language-supported" natural languages. If a
+ client requests some natural language for these attributes other
+ than one of the supported values, the IPP object SHOULD respond
+ using the value of the "natural-language-configured" attribute
+ (using the Natural Language Override mechanism if needed).
+
+
+The 'text' and 'name' attributes specified in this version of this
+document (additional ones will be registered according to the procedures
+in Section 6) are:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 137]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+
+ Attributes Source
+
+
+Operation Attributes:
+ job-name (name) client
+ document-name (name) client
+ requesting-user-name (name) client
+ status-message (text) Job or Printer object
+ detailed-status-message (text) Job or Printer object -
+ see rule 1
+ document-access-error (text) Job or Printer object -
+ see rule 1
+
+Job Template Attributes:
+ job-hold-until (keyword | name) client matches
+ administrator-configured
+ job-hold-until-default (keyword | name) client matches
+ administrator-configured
+ job-hold-until-supported (keyword | client matches
+ name) administrator-configured
+ job-sheets (keyword | name) client matches
+ administrator-configured
+ job-sheets-default (keyword | name) client matches
+ administrator-configured
+ job-sheets-supported (keyword | name) client matches
+ administrator-configured
+ media (keyword | name) client matches
+ administrator-configured
+ media-default (keyword | name) client matches
+ administrator-configured
+ media-supported (keyword | name) client matches
+ administrator-configured
+ media-ready (keyword | name) client matches
+ administrator-configured
+
+Job Description Attributes:
+ job-name (name) client or Printer object
+ job-originating-user-name (name) Printer object
+ job-state-message (text) Job or Printer object
+ output-device-assigned (name(127)) administrator
+ job-message-from-operator (text(127)) operator
+ job-detailed-status-messages (1setOf Job or Printer object -
+ text) see rule 1
+ job-document-access-errors (1setOf Job or Printer object -
+ text) see rule 1
+
+Printer Description Attributes:
+ printer-name (name(127)) administrator
+ printer-location (text(127)) administrator
+ printer-info (text(127)) administrator
+ printer-make-and-model (text(127)) administrator or
+ manufacturer
+ printer-state-message (text) Printer object
+ printer-message-from-operator operator
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 138]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+
+ (text(127))
+
+Rule 1 - Neither the Printer nor the client localizes these message
+attributes, since they are intended for use by the system administrator
+or other experienced technical persons.
+
+
+
+
+
+8. Security Considerations
+
+
+It is difficult to anticipate the security risks that might exist in any
+given IPP environment. For example, if IPP is used within a given
+corporation over a private network, the risks of exposing document data
+may be low enough that the corporation will choose not to use encryption
+on that data. However, if the connection between the client and the IPP
+object is over a public network, the client may wish to protect the
+content of the information during transmission through the network with
+encryption.
+
+Furthermore, the value of the information being printed may vary from
+one IPP environment to the next. Printing payroll checks, for example,
+would have a different value than printing public information from a
+file. There is also the possibly of denial-of-service attacks, but
+denial-of-service attacks against printing resources are not well
+understood and there is no published precedents regarding this scenario.
+
+Once the authenticated identity of the requester has been supplied to
+the IPP object, the object uses that identity to enforce any
+authorization policy that might be in place. For example, one site's
+policy might be that only the job owner is allowed to cancel a job. The
+details and mechanisms to set up a particular access control policy are
+not part of IPP/1.1, and must be established via some other type of
+administrative or access control framework. However, there are
+operation status codes that allow an IPP server to return information
+back to a client about any potential access control violations for an
+IPP object.
+
+During a create operation, the client's identity is recorded in the Job
+object in an implementation-defined attribute. This information can be
+used to verify a client's identity for subsequent operations on that Job
+object in order to enforce any access control policy that might be in
+effect. See section 8.3 below for more details.
+
+Since the security levels or the specific threats that any given IPP
+system administrator may be concerned with cannot be anticipated, IPP
+MUST be capable of operating with different security mechanisms and
+security policies as required by the individual installation. Security
+policies might vary from very strong, to very weak, to none at all, and
+corresponding security mechanisms will be required.
+
+
+
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 139]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+8.1 Security Scenarios
+
+
+The following sections describe specific security attacks for IPP
+environments. Where examples are provided they should be considered
+illustrative of the environment and not an exhaustive set. Not all of
+these environments will necessarily be addressed in initial
+implementations of IPP.
+
+
+8.1.1 Client and Server in the Same Security Domain
+
+This environment is typical of internal networks where traditional
+office workers print the output of personal productivity applications on
+shared work-group printers, or where batch applications print their
+output on large production printers. Although the identity of the user
+may be trusted in this environment, a user might want to protect the
+content of a document against such attacks as eavesdropping, replaying
+or tampering.
+
+
+8.1.2 Client and Server in Different Security Domains
+
+Examples of this environment include printing a document created by the
+client on a publicly available printer, such as at a commercial print
+shop; or printing a document remotely on a business associate's printer.
+This latter operation is functionally equivalent to sending the document
+to the business associate as a facsimile. Printing sensitive information
+on a Printer in a different security domain requires strong security
+measures. In this environment authentication of the printer is required
+as well as protection against unauthorized use of print resources. Since
+the document crosses security domains, protection against eavesdropping
+and document tampering are also required. It will also be important in
+this environment to protect Printers against "spamming" and malicious
+document content.
+
+
+8.1.3 Print by Reference
+
+When the document is not stored on the client, printing can be done by
+reference. That is, the print request can contain a reference, or
+pointer, to the document instead of the actual document itself (see
+sections 3.2.2 and 3.3.2). Standard methods currently do not exist for
+remote entities to "assume" the credentials of a client for forwarding
+requests to a 3rd party. It is anticipated that Print-By-Reference will
+be used to access "public" documents and that sophisticated methods for
+authenticating "proxies" is not specified in this document.
+
+
+8.2 URIs in Operation, Job, and Printer attributes
+
+
+The "printer-uri-supported" attribute contains the Printer object's
+URI(s). Its companion attribute, "uri-security-supported", identifies
+the security mechanism used for each URI listed in the "printer-uri-
+supported" attribute. For each Printer operation request, a client MUST
+supply only one URI in the "printer-uri" operation attribute. In other
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 140]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+words, even though the Printer supports more than one URI, the client
+only interacts with the Printer object using one if its URIs. This
+duality is not needed for Job objects, since the Printer objects is the
+factory for Job objects, and the Printer object will generate the
+correct URI for new Job objects depending on the Printer object's
+security configuration.
+
+
+8.3 URIs for each authentication mechanisms
+
+
+Each URI has an authentication mechanism associated with it. If the URI
+is the i'th element of "printer-uri-supported", then authentication
+mechanism is the "i th" element of "uri-authentication-supported". For a
+list of possible authentication mechanisms, see section 4.4.2.
+
+The Printer object uses an authentication mechanism to determine the
+name of the user performing an operation. This user is called the
+"authenticated user". The credibility of authentication depends on the
+mechanism that the Printer uses to obtain the user.s name. When the
+authentication mechanism is @none@, all authenticated users are
+"anonymous".
+
+During job creation operations, the Printer initializes the value of the
+"job-originating-user-name" attribute (see section 4.3.6) to be the
+authenticated user. The authenticated user is this case is called the
+"job-owner".
+
+If an implementation can be configured to support more than one
+authentication mechanism, then it MUST implement rules for determining
+equality of authenticated user names which have been authenticated via
+different authentication mechanisms. One possible policy is that
+identical names that are authenticated via different mechanism are
+different. For example, a user can cancel his job only if he uses the
+same authentication mechanism for both Cancel-Job and Print-Job.
+Another policy is that identical names that are authenticated via
+different mechanism are the same if the authentication mechanism for the
+later operation is not less strong than the authentication mechanism for
+the earlier job creation operation. For example, a user can cancel his
+job only if he uses the same or stronger authentication mechanism for
+Cancel-Job and Print-Job. With this second policy a job submitted via
+'requesting-user-name' authentication could be cancelled via 'digest'
+authentication. With the first policy, the job could not be cancelled in
+this way.
+
+A client is able to determine the authentication mechanism used to
+create a job. It is the i'th value of the Printer.s "uri-authentication-
+supported" attribute (see section 4.4.2), where i is the index of the
+element of the Printer.s "printer-uri-supported" attribute (see section
+4.4.1) equal to the job.s "job-printer-uri" attribute (see section
+4.3.3).
+
+
+
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 141]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+8.4 Restricted Queries
+
+
+In many IPP operations, a client supplies a list of attributes to be
+returned in the response. For security reasons, an IPP object may be
+configured not to return all attributes (or all values) that a client
+requests. The job attributes returned MAY depend on whether the
+requesting user is the same as the user that submitted the job. The IPP
+object MAY even return none of the requested attributes. In such cases,
+the status returned is the same as if the object had returned all
+requested attributes. The client cannot tell by such a response whether
+the requested attribute was present or absent on the object.
+
+
+8.5 Operations performed by operators and system administrators
+
+For the three printer operations Pause-Printer, Resume-Printer, and
+Purge-Jobs (see sections 3.2.7, 3.2.8 and 3.2.9), the requesting user is
+intended to be an operator or administrator of the Printer object (see
+section 1). For operations on jobs, the requesting user is intended to
+be the job owner or may be an operator or administrator of the Printer
+object. The means for authorizing an operator or administrator of the
+Printer object are not specified in this document.
+
+8.6 Queries on jobs submitted using non-IPP protocols
+
+
+If the device that an IPP Printer is representing is able to accept jobs
+using other job submission protocols in addition to IPP, it is
+RECOMMENDED that such an implementation at least allow such "foreign"
+jobs to be queried using Get-Jobs returning "job-id" and "job-uri" as
+'unknown'. Such an implementation NEED NOT support all of the same IPP
+job attributes as for IPP jobs. The IPP object returns the 'unknown'
+out-of-band value for any requested attribute of a foreign job that is
+supported for IPP jobs, but not for foreign jobs.
+
+It is further RECOMMENDED, that the IPP Printer generate "job-id" and
+"job-uri" values for such "foreign jobs", if possible, so that they may
+be targets of other IPP operations, such as Get-Job-Attributes and
+Cancel-Job. Such an implementation also needs to deal with the problem
+of authentication of such foreign jobs. One approach would be to treat
+all such foreign jobs as belonging to users other than the user of the
+IPP client. Another approach would be for the foreign job to belong to
+'anonymous'. Only if the IPP client has been authenticated as an
+operator or administrator of the IPP Printer object, could the foreign
+jobs be queried by an IPP request. Alternatively, if the security
+policy is to allow users to query other users' jobs, then the foreign
+jobs would also be visible to an end-user IPP client using Get-Jobs and
+Get-Job-Attributes.
+
+
+
+
+
+
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 142]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+9. References
+
+[ASCII]
+ Coded Character Set - 7-bit American Standard Code for Information
+ Interchange (ASCII), ANSI X3.4-1986. This standard is the
+ specification of the US-ASCII charset.
+
+[BCP-11]
+ Bradner S., Hovey R., "The Organizations Involved in the IETF
+ Standards Process", 1996/10/29 (RFC 2028)
+
+[HTPP]
+ J. Barnett, K. Carter, R. DeBry, "Initial Draft - Hypertext
+ Printing Protocol - HTPP/1.0", October 1996,
+ ftp://ftp.pwg.org/pub/pwg/ipp/historic/htpp/overview.ps.gz
+
+[IANA-CON]
+ Narte, T. and Alvestrand, H.T.: Guidelines for Writing an IANA
+ Considerations Section in RFCs, Work in Progress, draft-iesg-iana-
+ considerations-04.txt, May 21, 1998.
+
+[IANA-CS]
+ IANA Registry of Coded Character Sets: ftp://ftp.isi.edu/in-
+ notes/iana/assignments/character-sets
+
+[IANA-MT]
+ IANA Registry of Media Types: ftp://ftp.isi.edu/in-
+ notes/iana/assignments/media-types/
+
+[IPP-IIG]
+ Hastings, T., Manros, C., "Internet Printing Protocol/1.1: draft-
+ ietf-ipp-implementers-guide-v11-??.txt, ?? 1999, work in progress.
+
+[IPP-IIG1.0]
+ Hastings, T., Manros, C., "Internet Printing Protocol/1.0:
+ Implementer's Guide", draft-ietf-ipp-implementers-guide-01.txt,
+ February 1999, work in progress.
+
+[IPP-PRO]
+ Herriot, R., Butler, S., Moore, P., Tuner, R., "Internet Printing
+ Protocol/1.1: Encoding and Transport", draft-ietf-ipp-protocol-v11-
+ 01.txt, May, 1999.
+
+[ISO10646-1]
+ ISO/IEC 10646-1:1993, "Information technology -- Universal
+ Multiple-Octet Coded Character Set (UCS) - Part 1: Architecture and
+ Basic Multilingual Plane, JTC1/SC2."
+
+[ISO8859-1]
+ ISO/IEC 8859-1:1987, "Information technology -- 8-bit One-Byte
+ Coded Character Set - Part 1: Latin Alphabet Nr 1", 1987, JTC1/SC2.
+
+[ISO10175]
+ ISO/IEC 10175 Document Printing Application (DPA), June 1996.
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 143]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+[LDPA]
+ T. Hastings, S. Isaacson, M. MacKay, C. Manros, D. Taylor, P.
+ Zehler, "LDPA - Lightweight Document Printing Application",
+ October 1996,
+ ftp://ftp.pwg.org/pub/pwg/ipp/historic/ldpa/ldpa8.pdf.gz
+
+[P1387.4]
+ Kirk, M. (editor), POSIX System Administration - Part 4: Printing
+ Interfaces, POSIX 1387.4 D8, 1994.
+
+[PSIS] Herriot, R. (editor), X/Open A Printing System
+ Interoperability Specification (PSIS), August 1995.
+
+[PWG]
+ Printer Working Group, http://www.pwg.org.
+
+[RFC1035]
+ P. Mockapetris, "DOMAIN NAMES - IMPLEMENTATION AND SPECIFICATION",
+ RFC 1035, November 1987.
+
+[RFC1179]
+ McLaughlin, L. III, (editor), "Line Printer Daemon Protocol" RFC
+ 1179, August 1990.
+
+[RFC1759]
+ Smith, R., Wright, F., Hastings, T., Zilles, S., and Gyllenskog,
+ J., "Printer MIB", RFC 1759, March 1995.
+
+[RFC1766]
+ H. Alvestrand, "Tags for the Identification of Languages", RFC
+ 1766, March 1995.
+
+[RFC1952]
+ P. Deutsch, "GZIP file format specification version 4.3", RFC 1952,
+ May 1996.
+
+[RFC2026]
+ S. Bradner, "The Internet Standards Process -- Revision 3", RFC
+ 2026, October 1996.
+
+[RFC2045]
+ N. Fried, N. Borenstein, ", Multipurpose Internet Mail Extensions
+ (MIME) Part One: Format of Internet Message Bodies " RFC 2045,
+ November 1996.
+
+[RFC2046]
+ Multipurpose Internet Mail Extensions (MIME) Part Two: Media Types.
+ N. Freed & N. Borenstein. November 1996. (Obsoletes RFC1521,
+ RFC1522, RFC1590), RFC 2046.
+
+[RFC2048]
+ N. Freed, J. Klensin & J. Postel, "Multipurpose Internet Mail
+ Extension (MIME) Part Four: Registration Procedures". RFC 2048,
+ November 1996.
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 144]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+[RFC2616]
+ R. Fielding, J. Gettys, J. Mogul, H. Frystyk, L. Masinter, P.
+ Leach, T. Berners-Lee, "Hypertext Transfer Protocol - HTTP/1.1",
+ RFC 2616, June 1999
+
+[RFC2617]
+ J. Franks, P. Hallam-Baker, J. Hostetler, S. Lawrence, P. Leach, A.
+ Luotonen, L. Stewart, "HTTP Authentication: Basic and Digest Access
+ Authentication", RFC 2617, June 1999.
+
+[RFC2119]
+ S. Bradner, "Key words for use in RFCs to Indicate Requirement
+ Levels", RFC 2119 , March 1997
+
+[RFC2228]
+ M. Horowitz, S. Lunt, "FTP Security Extensions", RFC 2228, October
+ 1997.
+
+[RFC2246]
+ T. Dierks, C. Allen, "The TLS Protocol Version 1.0", RFC 2246,
+ January 1999.
+
+[RFC2277]
+ H. Alvestrand, "IETF Policy on Character Sets and Languages" RFC
+ 2277, January 1998.
+
+[RFC2278]
+ N. Freed, J. Postel: "IANA CharSet Registration Procedures", RFC
+ 2278, January 1998.
+
+[RFC2279]
+ F. Yergeau , "UTF-8, a transformation format of ISO 10646", RFC
+ 2279. January 1998.
+
+[RFC2316]
+ S. Bellovin , "Report of the IAB Security Architecture Workshop",
+ RFC 2316, April 1998.
+
+[RFC2396]
+ Berners-Lee, T., Fielding, R., Masinter, L., "Uniform Resource
+ Identifiers (URI): Generic Syntax", RFC 2396, August 1998.
+
+[RFC2565]
+ Herriot, R., Butler, S., Moore, P., Tuner, R., "Internet Printing
+ Protocol/1.0: Encoding and Transport", RFC 2565, April 1999.
+
+[RFC2566]
+ R. deBry, T. Hastings, R. Herriot, S. Isaacson, P. Powell,
+ "Internet Printing Protocol/1.0: Model and Semantics", RFC 2566,
+ April 1999.
+
+[RFC2567]
+ Wright, D., "Design Goals for an Internet Printing Protocol",
+ draft-ietf-ipp-req-03.txt, November, 1998.
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 145]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+[RFC2568]
+ Zilles, S., "Rationale for the Structure and Model and Protocol for
+ the Internet Printing Protocol", draft-ietf-ipp-rat-04.txt,
+ November, 1998.
+
+[RFC2569]
+ Herriot, R., Hastings, T., Jacobs, N., Martin, J., "Mapping between
+ LPD and IPP Protocols", draft-ietf-ipp-lpd-ipp-map-05.txt, November
+ 1998.
+
+[RFC2579]
+ K. McCloghrie, D. Perkins, J. Schoenwaelder, "Textual Conventions
+ for SMIv2" RFC 2579 (Also STD0058), April 1999, .
+
+[RFC2616]
+ R. Fielding, J. Gettys, J. Mogul, H. Frystyk, L. Masinter, P.
+ Leach, T. Berners-Lee, "Hypertext Transfer Protocol - HTTP/1.1",
+ RFC 2616, June 1999.
+
+[RFC2617]
+ J. Franks, P. Hallam-Baker, J. Hostetler, S. Lawrence, P. Leach, A.
+ Luotonen, L. Stewart, "HTTP Authentication: Basic and Digest Access
+ Authentication", RFC 2617, June 1999.
+
+[SSL]
+ Netscape, The SSL Protocol, Version 3, (Text version 3.02),
+ November 1996.
+
+[SWP]
+ P. Moore, B. Jahromi, S. Butler, "Simple Web Printing SWP/1.0", May
+ 7, 1997, ftp://ftp.pwg.org/pub/pwg/ipp/new_PRO/swp9705.pdf
+
+
+
+10. Author's Address
+
+ Scott A. Isaacson (Editor)
+ Novell, Inc.
+ 122 E 1700 S
+ Provo, UT 84606
+
+ Phone: 801-861-7366
+ Fax: 801-861-2517
+ e-mail: sisaacson@novell.com
+
+ Tom Hastings
+ Xerox Corporation
+ 737 Hawaii St. ESAE 231
+ El Segundo, CA 90245
+
+ Phone: 310-333-6413
+ Fax: 310-333-5514
+ e-mail: hastings@cp10.es.xerox.com
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 146]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+ Robert Herriot
+ Xerox Corp.
+ 3400 Hill View Ave, Building 1
+ Palo Alto, CA 94304
+
+ Phone: 650-813-7696
+ Fax: 650-813-6860
+ e-mail: robert.herriot@pahv.xerox.com
+
+ Roger deBry
+ Utah Valley State College
+ Orem, UT 84058
+
+ Phone: (801) 222-8000
+ EMail: debryro@uvsc.edu
+
+ Patrick Powell
+ Astart Technologies
+ 9475 Chesapeake Dr., Suite D
+ San Diego, CA 95123
+
+ Phone: (619) 874-6543
+ Fax: (619) 279-8424
+ e-mail: papowell@astart.com
+
+ IPP Mailing List: ipp@pwg.org
+ IPP Mailing List Subscription: ipp-request@pwg.org
+ IPP Web Page: http://www.pwg.org/ipp/
+
+Implementers of this specification document are encouraged to join IPP
+Mailing List in order to participate in any discussions of clarification
+issues and review of registration proposals for additional attributes
+and values.
+
+Other Participants:
+
+Chuck Adams - Tektronix Shivaun Albright - HP
+Jeff Barnett - IBM Ron Bergman - Dataproducts Corp.
+Sylvan Butler - HP Keith Carter - IBM Corporation
+Angelo Caruso - Xerox Rajesh Chawla - TR Computing
+ Solutions
+Josh Cohen - Microsoft Jeff Copeland - QMS
+Andy Davidson - Tektronix Mabry Dozier - QMS
+Lee Farrell - Canon Information Steve Gebert - IBM
+Systems
+Sue Gleeson - Digital Charles Gordon - Osicom
+Brian Grimshaw - Apple Jerry Hadsell - IBM
+Richard Hart - Digital Stephen Holmstead
+Zhi-Hong Huang - Zenographics Babek Jahromi - Microsoft
+Swen Johnson - Xerox David Kellerman - Northlake
+ Software
+Carl Kugler - IBM Dave Kuntz - Hewlett-Packard
+Takami Kurono - Brother Rick Landau - Digital
+Scott Lawrence - Agranot Systems Greg LeClair - Epson
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 147]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+
+Harry Lewis - IBM Tony Liao - Vivid Image
+Roy Lomicka - Digital Pete Loya - HP
+Ray Lutz - Cognisys Mike MacKay - Novell, Inc.
+Daniel Manchala - Xerox Carl-Uno Manros - Xerox
+Jay Martin - Underscore Larry Masinter - Xerox
+Stan McConnell - Xerox Ira McDonald - High North Inc.
+Peter Michalek - Shinesoft Paul Moore - Microsoft
+Tetsuya Morita - Ricoh Yuichi Niwa - Ricoh
+Pat Nogay - IBM Ron Norton - Printronics
+Bob Pentecost - HP Rob Rhoads - Intel
+Xavier Riley - Xerox David Roach - Unisys
+Gary Roberts - Ricoh Stuart Rowley - Kyocera
+Hiroyuki Sato - Canon Richard Schneider - Epson
+Kris Schoff - HP Bob Setterbo - Adobe
+Devon Taylor - Novell, Inc. Mike Timperman - Lexmark
+Randy Turner - Sharp Bill Wagner - Osicom
+Jim Walker - DAZEL Chris Wellens - Interworking Labs
+Rob Whittle - Novell, Inc. Jasper Wong - Xionics
+Don Wright - Lexmark Rick Yardumian - Xerox
+Lloyd Young - Lexmark Atsushi Yuki - Kyocera
+Peter Zehler - Xerox Frank Zhao - Panasonic
+Steve Zilles - Adobe
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 148]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+11. Formats for IPP Registration Proposals
+
+In order to propose an IPP extension for registration, the proposer must
+submit an application to IANA by email to "iana@iana.org" or by filling
+out the appropriate form on the IANA web pages (http://www.iana.org).
+This section specifies the required information and the formats for
+proposing registrations of extensions to IPP as provided in Section 6
+for:
+
+ 1. type2 'keyword' attribute values
+ 2. type3 'keyword' attribute values
+ 3. type2 'enum' attribute values
+ 4. type3 'enum' attribute values
+ 5. attributes
+ 6. attribute syntaxes
+ 7. operations
+ 8. status codes
+
+11.1 Type2 keyword attribute values registration
+
+Type of registration: type2 keyword attribute value
+Name of attribute to which this keyword specification is to be added:
+Proposed keyword name of this keyword value:
+Specification of this keyword value (follow the style of IPP Model
+Section 4.1.2.3):
+Name of proposer:
+Address of proposer:
+Email address of proposer:
+
+Note: For type2 keywords, the Designated Expert will be the point of
+contact for the approved registration specification, if any maintenance
+of the registration specification is needed.
+
+11.2 Type3 keyword attribute values registration
+
+Type of registration: type3 keyword attribute value
+Name of attribute to which this keyword specification is to be added:
+Proposed keyword name of this keyword value:
+Specification of this keyword value (follow the style of IPP Model
+Section 4.1.2.3):
+Name of proposer:
+Address of proposer:
+Email address of proposer:
+
+Note: For type3 keywords, the proposer will be the point of contact for
+the approved registration specification, if any maintenance of the
+registration specification is needed.
+
+11.3 Type2 enum attribute values registration
+
+Type of registration: type2 enum attribute value
+Name of attribute to which this enum specification is to be added:
+Keyword symbolic name of this enum value:
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 149]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+Numeric value (to be assigned by the IPP Designated Expert in
+consultation with IANA):
+Specification of this enum value (follow the style of IPP Model Section
+4.1.4):
+Name of proposer:
+Address of proposer:
+Email address of proposer:
+
+Note: For type2 enums, the Designated Expert will be the point of
+contact for the approved registration specification, if any maintenance
+of the registration specification is needed.
+
+11.4 Type3 enum attribute values registration
+
+Type of registration: type3 enum attribute value
+Name of attribute to which this enum specification is to be added:
+Keyword symbolic name of this enum value:
+Numeric value (to be assigned by the IPP Designated Expert in
+consultation with IANA):
+Specification of this enum value (follow the style of IPP Model Section
+4.1.4):
+Name of proposer:
+Address of proposer:
+Email address of proposer:
+
+Note: For type3 enums, the proposer will be the point of contact for
+the approved registration specification, if any maintenance of the
+registration specification is needed.
+
+11.5 Attribute registration
+
+Type of registration: attribute
+Proposed keyword name of this attribute:
+Types of attribute (Operation, Job Template, Job Description, Printer
+Description):
+Operations to be used with if the attribute is an operation attribute:
+Object (Job, Printer, etc. if bound to an object):
+Attribute syntax(es) (include 1setOf and range as in Section 4.2):
+If attribute syntax is 'keyword' or 'enum', is it type2 or type3:
+If this is a Printer attribute, MAY the value returned depend on
+"document-format" (See Section 6.2):
+If this is a Job Template attribute, how does its specification depend
+on the value of the "multiple-document-handling" attribute:
+Specification of this attribute (follow the style of IPP Model Section
+4.2):
+Name of proposer:
+Address of proposer:
+Email address of proposer:
+
+Note: For attributes, the IPP Designated Expert will be the point of
+contact for the approved registration specification, if any maintenance
+of the registration specification is needed.
+
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 150]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+11.6 Attribute Syntax registration
+
+Type of registration: attribute syntax
+Proposed name of this attribute syntax:
+Type of attribute syntax (integer, octetString, character-string, see
+[IPP-PRO]):
+Numeric value (to be assigned by the IPP Designated Expert in
+consultation with IANA):
+Specification of this attribute (follow the style of IPP Model Section
+4.1):
+Name of proposer:
+Address of proposer:
+Email address of proposer:
+
+Note: For attribute syntaxes, the IPP Designated Expert will be the
+point of contact for the approved registration specification, if any
+maintenance of the registration specification is needed.
+
+11.7 Operation registration
+
+Type of registration: operation
+Proposed name of this operation:
+Numeric operation-id value (to be assigned by the IPP Designated Expert
+in consultation with IANA):
+Object Target (Job, Printer, etc. that operation is upon):
+Specification of this operation (follow the style of IPP Model Section
+3):
+Name of proposer:
+Address of proposer:
+Email address of proposer:
+
+Note: For operations, the IPP Designated Expert will be the point of
+contact for the approved registration specification, if any maintenance
+of the registration specification is needed.
+
+11.8 Attribute Group registration
+
+Type of registration: attribute group
+Proposed name of this attribute group:
+Numeric tag according to [IPP-PRO] (to be assigned by the IPP Designated
+Expert in consultation with IANA):
+Operation requests and group number for each operation in which the
+attribute group occurs:
+Operation responses and group number for each operation in which the
+attribute group occurs:
+Specification of this attribute group (follow the style of IPP Model
+Section 3):
+Name of proposer:
+Address of proposer:
+Email address of proposer:
+
+Note: For attribute groups, the IPP Designated Expert will be the point
+of contact for the approved registration specification, if any
+maintenance of the registration specification is needed.
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 151]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+11.9 Status code registration
+
+Type of registration: status code
+Keyword symbolic name of this status code value:
+Numeric value (to be assigned by the IPP Designated Expert in
+consultation with IANA):
+Operations that this status code may be used with:
+Specification of this status code (follow the style of IPP Model Section
+13 APPENDIX B: Status Codes and Suggested Status Code Messages):
+Name of proposer:
+Address of proposer:
+Email address of proposer:
+
+Note: For status codes, the Designated Expert will be the point of
+contact for the approved registration specification, if any maintenance
+of the registration specification is needed.
+
+
+12. APPENDIX A: Terminology
+
+
+This specification document uses the terminology defined in this
+section.
+
+
+12.1 Conformance Terminology
+
+
+The key words "MUST", "MUST NOT", "REQUIRED", "SHOULD", "SHOULD NOT",
+"RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be
+interpreted as described in RFC 2119 [RFC2119].
+
+
+12.1.1 NEED NOT
+
+This term is not included in RFC 2119. The verb "NEED NOT" indicates an
+action that the subject of the sentence does not have to implement in
+order to claim conformance to the standard. The verb "NEED NOT" is used
+instead of "MAY NOT" since "MAY NOT" sounds like a prohibition.
+
+
+12.2 Model Terminology
+
+
+12.2.1 Keyword
+
+Keywords are used within this document as identifiers of semantic
+entities within the abstract model (see section 4.1.2.3). Attribute
+names, some attribute values, attribute syntaxes, and attribute group
+names are represented as keywords.
+
+
+12.2.2 Attributes
+
+An attribute is an item of information that is associated with an
+instance of an IPP object. An attribute consists of an attribute name
+and one or more attribute values. Each attribute has a specific
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 152]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+attribute syntax. All object attributes are defined in section 4 and
+all operation attributes are defined in section 3.
+
+Job Template Attributes are described in section 4.2. The client
+optionally supplies Job Template attributes in a create request
+(operation requests that create Job objects). The Printer object has
+associated attributes which define supported and default values for the
+Printer.
+
+
+12.2.2.1 Attribute Name
+
+Each attribute is uniquely identified in this document by its attribute
+name. An attribute name is a keyword. The keyword attribute name is
+given in the section header describing that attribute. In running text
+in this document, attribute names are indicated inside double quotation
+marks (") where the quotation marks are not part of the keyword itself.
+
+
+12.2.2.2 Attribute Group Name
+
+Related attributes are grouped into named groups. The name of the group
+is a keyword. The group name may be used in place of naming all the
+attributes in the group explicitly. Attribute groups are defined in
+section 3.
+
+
+12.2.2.3 Attribute Value
+
+Each attribute has one or more values. Attribute values are represented
+in the syntax type specified for that attribute. In running text in this
+document, attribute values are indicated inside single quotation marks
+('), whether their attribute syntax is keyword, integer, text, etc.
+where the quotation marks are not part of the value itself.
+
+
+12.2.2.4 Attribute Syntax
+
+Each attribute is defined using an explicit syntax type. In this
+document, each syntax type is defined as a keyword with specific
+meaning. The "Encoding and Transport" document [IPP-PRO] indicates the
+actual "on-the-wire" encoding rules for each syntax type. Attribute
+syntax types are defined in section 4.1.
+
+
+12.2.3 Supports
+
+By definition, a Printer object supports an attribute only if that
+Printer object responds with the corresponding attribute populated with
+some value(s) in a response to a query for that attribute. A Printer
+object supports an attribute value if the value is one of the Printer
+object's "supported values" attributes. The device behind a Printer
+object may exhibit a behavior that corresponds to some IPP attribute,
+but if the Printer object, when queried for that attribute, doesn't
+respond with the attribute, then as far as IPP is concerned, that
+implementation does not support that feature. If the Printer object's
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 153]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+"xxx-supported" attribute is not populated with a particular value (even
+if that value is a legal value for that attribute), then that Printer
+object does not support that particular value.
+
+A conforming implementation MUST support all REQUIRED attributes.
+However, even for REQUIRED attributes, conformance to IPP does not
+mandate that all implementations support all possible values
+representing all possible job processing behaviors and features. For
+example, if a given instance of a Printer supports only certain document
+formats, then that Printer responds with the "document-format-supported"
+attribute populated with a set of values, possibly only one, taken from
+the entire set of possible values defined for that attribute. This
+limited set of values represents the Printer's set of supported document
+formats. Supporting an attribute and some set of values for that
+attribute enables IPP end users to be aware of and make use of those
+features associated with that attribute and those values. If an
+implementation chooses to not support an attribute or some specific
+value, then IPP end users would have no ability to make use of that
+feature within the context of IPP itself. However, due to existing
+practice and legacy systems which are not IPP aware, there might be some
+other mechanism outside the scope of IPP to control or request the
+"unsupported" feature (such as embedded instructions within the document
+data itself).
+
+For example, consider the "finishings-supported" attribute.
+
+ 1) If a Printer object is not physically capable of stapling, the
+ "finishings-supported" attribute MUST NOT be populated with the
+ value of 'staple'.
+ 2) A Printer object is physically capable of stapling, however an
+ implementation chooses not to support stapling in the IPP
+ "finishings" attribute. In this case, 'staple' MUST NOT be a value
+ in the "finishings-supported" Printer object attribute. Without
+ support for the value 'staple', an IPP end user would have no means
+ within the protocol itself to request that a Job be stapled.
+ However, an existing document data formatter might be able to
+ request that the document be stapled directly with an embedded
+ instruction within the document data. In this case, the IPP
+ implementation does not "support" stapling, however the end user is
+ still able to have some control over the stapling of the completed
+ job.
+ 3) A Printer object is physically capable of stapling, and an
+ implementation chooses to support stapling in the IPP "finishings"
+ attribute. In this case, 'staple' MUST be a value in the
+ "finishings-supported" Printer object attribute. Doing so, would
+ enable end users to be aware of and make use of the stapling
+ feature using IPP attributes.
+
+
+Even though support for Job Template attributes by a Printer object is
+OPTIONAL, it is RECOMMENDED that if the device behind a Printer object
+is capable of realizing any feature or function that corresponds to an
+IPP attribute and some associated value, then that implementation SHOULD
+support that IPP attribute and value.
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 154]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+The set of values in any of the supported value attributes is set
+(populated) by some administrative process or automatic sensing
+mechanism that is outside the scope of this IPP/1.1 document. For
+administrative policy and control reasons, an administrator may choose
+to make only a subset of possible values visible to the end user. In
+this case, the real output device behind the IPP Printer abstraction may
+be capable of a certain feature, however an administrator is specifying
+that access to that feature not be exposed to the end user through the
+IPP protocol. Also, since a Printer object may represent a logical
+print device (not just a physical device) the actual process for
+supporting a value is undefined and left up to the implementation.
+However, if a Printer object supports a value, some manual human action
+may be needed to realize the semantic action associated with the value,
+but no end user action is required.
+
+For example, if one of the values in the "finishings-supported"
+attribute is 'staple', the actual process might be an automatic staple
+action by a physical device controlled by some command sent to the
+device. Or, the actual process of stapling might be a manual action by
+an operator at an operator attended Printer object.
+
+For another example of how supported attributes function, consider a
+system administrator who desires to control all print jobs so that no
+job sheets are printed in order to conserve paper. To force no job
+sheets, the system administrator sets the only supported value for the
+"job-sheets-supported" attribute to 'none'. In this case, if a client
+requests anything except 'none', the create request is rejected or the
+"job-sheets" value is ignored (depending on the value of "ipp-attribute-
+fidelity"). To force the use of job start/end sheets on all jobs, the
+administrator does not include the value 'none' in the "job-sheets-
+supported" attribute. In this case, if a client requests 'none', the
+create request is rejected or the "job-sheets" value is ignored (again
+depending on the value of "ipp-attribute-fidelity").
+
+
+12.2.4 print-stream page
+
+A "print-stream page" is a page according to the definition of pages in
+the language used to express the document data.
+
+
+12.2.5 impression
+
+An "impression" is the image (possibly many print-stream pages in
+different configurations) imposed onto a single media page.
+
+
+
+13. APPENDIX B: Status Codes and Suggested Status Code Messages
+
+
+This section defines status code enum keywords and values that are used
+to provide semantic information on the results of an operation request.
+Each operation response MUST include a status code. The response MAY
+also contain a status message that provides a short textual description
+of the status. The status code is intended for use by automata, and the
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 155]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+status message is intended for the human end user. Since the status
+message is an OPTIONAL component of the operation response, an IPP
+application (i.e., a browser, GUI, print driver or gateway) is NOT
+REQUIRED to examine or display the status message, since it MAY not be
+returned to the application.
+
+The prefix of the status keyword defines the class of response as
+follows:
+
+ "informational" - Request received, continuing process
+ "successful" - The action was successfully received, understood, and
+ accepted
+ "redirection" - Further action must be taken in order to complete the
+ request
+ "client-error" - The request contains bad syntax or cannot be
+ fulfilled
+ "server-error" - The IPP object failed to fulfill an apparently
+ valid request
+
+
+As with type2 enums, IPP status codes are extensible. IPP clients are
+NOT REQUIRED to understand the meaning of all registered status codes,
+though such understanding is obviously desirable. However, IPP clients
+MUST understand the class of any status code, as indicated by the
+prefix, and treat any unrecognized response as being equivalent to the
+first status code of that class, with the exception that an unrecognized
+response MUST NOT be cached. For example, if an unrecognized status
+code of "client-error-xxx-yyy" is received by the client, it can safely
+assume that there was something wrong with its request and treat the
+response as if it had received a "client-error-bad-request" status code.
+In such cases, IPP applications SHOULD present the OPTIONAL message (if
+present) to the end user since the message is likely to contain human
+readable information which will help to explain the unusual status. The
+name of the enum is the suggested status message for US English.
+
+The status code values range from 0x0000 to 0x7FFF. The value ranges
+for each status code class are as follows:
+
+ "successful" - 0x0000 to 0x00FF
+ "informational" - 0x0100 to 0x01FF
+ "redirection" - 0x0200 to 0x02FF
+ "client-error" - 0x0400 to 0x04FF
+ "server-error" - 0x0500 to 0x05FF
+
+
+The top half (128 values) of each range (0x0n40 to 0x0nFF, for n = 0 to
+5) is reserved for private use within each status code class. Values
+0x0600 to 0x7FFF are reserved for future assignment and MUST NOT be
+used.
+
+
+13.1 Status Codes
+
+
+Each status code is described below. Section 13.1.5.9 contains a table
+that indicates which status codes apply to which operations. The
+Implementer's Guide [IPP-IIG] describe the suggested steps for
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 156]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+processing IPP attributes for all operations, including returning status
+codes.
+
+
+13.1.1 Informational
+
+This class of status code indicates a provisional response and is to be
+used for informational purposes only.
+
+There are no status codes defined in IPP/1.1 for this class of status
+code.
+
+
+13.1.2 Successful Status Codes
+
+This class of status code indicates that the client's request was
+successfully received, understood, and accepted.
+
+
+13.1.2.1 successful-ok (0x0000)
+
+The request has succeeded and no request attributes were substituted or
+ignored. In the case of a response to a create request, the
+'successful-ok' status code indicates that the request was successfully
+received and validated, and that the Job object has been created; it
+does not indicate that the job has been processed. The transition of
+the Job object into the 'completed' state is the only indicator that the
+job has been printed.
+
+
+13.1.2.2 successful-ok-ignored-or-substituted-attributes (0x0001)
+
+The request has succeeded, but some supplied (1) attributes were ignored
+or (2) unsupported values were substituted with supported values or were
+ignored in order to perform the operation without rejecting it.
+Unsupported attributes, attribute syntaxes, or values MUST be returned
+in the Unsupported Attributes group of the response for all operations.
+There is an exception to this rule for the query operations: Get-
+Printer-Attributes, Get-Jobs, and Get-Job-Attributes for the "requested-
+attributes" operation attribute only. When the supplied values of the
+"requested-attributes" operation attribute are requesting attributes
+that are not supported, the IPP object MAY, but is NOT REQUIRED to,
+return the "requested-attributes" attribute in the Unsupported Attribute
+response group (with the unsupported values only). See sections 3.1.7
+and 3.2.1.2.
+
+
+13.1.2.3 successful-ok-conflicting-attributes (0x0002)
+
+The request has succeeded, but some supplied attribute values conflicted
+with the values of other supplied attributes. These conflicting values
+were either (1) substituted with (supported) values or (2) the
+attributes were removed in order to process the job without rejecting
+it. Attributes or values which conflict with other attributes and have
+been substituted or ignored MUST be returned in the Unsupported
+
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 157]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+Attributes group of the response for all operations as supplied by the
+client. See sections 3.1.7 and 3.2.1.2.
+
+
+13.1.3 Redirection Status Codes
+
+This class of status code indicates that further action needs to be
+taken to fulfill the request.
+
+There are no status codes defined in IPP/1.1 for this class of status
+code.
+
+
+13.1.4 Client Error Status Codes
+
+This class of status code is intended for cases in which the client
+seems to have erred. The IPP object SHOULD return a message containing
+an explanation of the error situation and whether it is a temporary or
+permanent condition.
+
+
+13.1.4.1 client-error-bad-request (0x0400)
+
+The request could not be understood by the IPP object due to malformed
+syntax (such as the value of a fixed length attribute whose length does
+not match the prescribed length for that attribute - see the
+Implementer's Guide [IPP-IIG] ). The IPP application SHOULD NOT repeat
+the request without modifications.
+
+
+13.1.4.2 client-error-forbidden (0x0401)
+
+The IPP object understood the request, but is refusing to fulfill it.
+Additional authentication information or authorization credentials will
+not help and the request SHOULD NOT be repeated. This status code is
+commonly used when the IPP object does not wish to reveal exactly why
+the request has been refused or when no other response is applicable.
+
+
+13.1.4.3 client-error-not-authenticated (0x0402)
+
+The request requires user authentication. The IPP client may repeat the
+request with suitable authentication information. If the request already
+included authentication information, then this status code indicates
+that authorization has been refused for those credentials. If this
+response contains the same challenge as the prior response, and the user
+agent has already attempted authentication at least once, then the
+response message may contain relevant diagnostic information. This
+status codes reveals more information than "client-error-forbidden".
+
+
+13.1.4.4 client-error-not-authorized (0x0403)
+
+The requester is not authorized to perform the request. Additional
+authentication information or authorization credentials will not help
+and the request SHOULD NOT be repeated. This status code is used when
+the IPP object wishes to reveal that the authentication information is
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 158]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+understandable, however, the requester is explicitly not authorized to
+perform the request. This status codes reveals more information than
+"client-error-forbidden" and "client-error-not-authenticated".
+
+
+13.1.4.5 client-error-not-possible (0x0404)
+
+This status code is used when the request is for something that can not
+happen. For example, there might be a request to cancel a job that has
+already been canceled or aborted by the system. The IPP client SHOULD
+NOT repeat the request.
+
+
+13.1.4.6 client-error-timeout (0x0405)
+
+The client did not produce a request within the time that the IPP object
+was prepared to wait. For example, a client issued a Create-Job
+operation and then, after a long period of time, issued a Send-Document
+operation and this error status code was returned in response to the
+Send-Document request (see section 3.3.1). The IPP object might have
+been forced to clean up resources that had been held for the waiting
+additional Documents. The IPP object was forced to close the Job since
+the client took too long. The client SHOULD NOT repeat the request
+without modifications.
+
+
+13.1.4.7 client-error-not-found (0x0406)
+
+The IPP object has not found anything matching the request URI. No
+indication is given of whether the condition is temporary or permanent.
+For example, a client with an old reference to a Job (a URI) tries to
+cancel the Job, however in the mean time the Job might have been
+completed and all record of it at the Printer has been deleted. This
+status code, 'client-error-not-found' is returned indicating that the
+referenced Job can not be found. This error status code is also used
+when a client supplies a URI as a reference to the document data in
+either a Print-URI or Send-URI operation, but the document can not be
+found.
+
+In practice, an IPP application should avoid a not found situation by
+first querying and presenting a list of valid Printer URIs and Job URIs
+to the end-user.
+
+
+13.1.4.8 client-error-gone (0x0407)
+
+The requested object is no longer available and no forwarding address is
+known. This condition should be considered permanent. Clients with
+link editing capabilities should delete references to the request URI
+after user approval. If the IPP object does not know or has no facility
+to determine, whether or not the condition is permanent, the status code
+"client-error-not-found" should be used instead.
+
+This response is primarily intended to assist the task of maintenance by
+notifying the recipient that the resource is intentionally unavailable
+and that the IPP object administrator desires that remote links to that
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 159]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+resource be removed. It is not necessary to mark all permanently
+unavailable resources as "gone" or to keep the mark for any length of
+time -- that is left to the discretion of the IPP object administrator.
+
+
+13.1.4.9 client-error-request-entity-too-large (0x0408)
+
+The IPP object is refusing to process a request because the request
+entity is larger than the IPP object is willing or able to process. An
+IPP Printer returns this status code when it limits the size of print
+jobs and it receives a print job that exceeds that limit or when the
+attributes are so many that their encoding causes the request entity to
+exceed IPP object capacity.
+
+
+13.1.4.10 client-error-request-value-too-long (0x0409)
+
+The IPP object is refusing to service the request because one or more of
+the client-supplied attributes has a variable length value that is
+longer than the maximum length specified for that attribute. The IPP
+object might not have sufficient resources (memory, buffers, etc.) to
+process (even temporarily), interpret, and/or ignore a value larger than
+the maximum length. Another use of this error code is when the IPP
+object supports the processing of a large value that is less than the
+maximum length, but during the processing of the request as a whole, the
+object may pass the value onto some other system component which is not
+able to accept the large value. For more details, see the Implementer's
+Guide [IPP-IIG] .
+
+Note: For attribute values that are URIs, this rare condition is only
+likely to occur when a client has improperly submitted a request with
+long query information (e.g. an IPP application allows an end-user to
+enter an invalid URI), when the client has descended into a URI "black
+hole" of redirection (e.g., a redirected URI prefix that points to a
+suffix of itself), or when the IPP object is under attack by a client
+attempting to exploit security holes present in some IPP objects using
+fixed-length buffers for reading or manipulating the Request-URI.
+
+
+13.1.4.11 client-error-document-format-not-supported (0x040A)
+
+The IPP object is refusing to service the request because the document
+data is in a format, as specified in the "document-format" operation
+attribute, that is not supported by the Printer object. This error is
+returned independent of the client-supplied "ipp-attribute-fidelity".
+The Printer object MUST return this status code, even if there are other
+Job Template attributes that are not supported as well, since this error
+is a bigger problem than with Job Template attributes. See sections
+3.1.7 and 3.2.1.1.
+
+
+13.1.4.12 client-error-attributes-or-values-not-supported (0x040B)
+
+In a create request, if the Printer object does not support one or more
+attributes, attribute syntaxes, or attribute values supplied in the
+request and the client supplied the "ipp-attributes-fidelity" operation
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 160]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+attribute with the 'true' value, the Printer object MUST return this
+status code. The Printer object MUST also return in the Unsupported
+Attributes Group all the attributes and/or values supplied by the client
+that are not supported. See section 3.1.7. For example, if the request
+indicates 'iso-a4' media, but that media type is not supported by the
+Printer object. Or, if the client supplies a Job Template attribute and
+the attribute itself is not even supported by the Printer. If the "ipp-
+attribute-fidelity" attribute is 'false', the Printer MUST ignore or
+substitute values for unsupported Job Template attributes and values
+rather than reject the request and return this status code.
+
+For any operation where a client requests attributes (such as a Get-
+Jobs, Get-Printer-Attributes, or Get-Job-Attributes operation), if the
+IPP object does not support one or more of the requested attributes, the
+IPP object simply ignores the unsupported requested attributes and
+processes the request as if they had not been supplied, rather than
+returning this status code. In this case, the IPP object MUST return
+the 'successful-ok-ignored-or-substituted-attributes' status code and
+MAY return the unsupported attributes as values of the "requested-
+attributes" in the Unsupported Attributes Group (see section 13.1.2.2).
+
+
+13.1.4.13 client-error-uri-scheme-not-supported (0x040C)
+
+The scheme of the client-supplied URI in a Print-URI or a Send-URI
+operation is not supported. See section 3.1.7.
+
+
+13.1.4.14 client-error-charset-not-supported (0x040D)
+
+For any operation, if the IPP Printer does not support the charset
+supplied by the client in the "attributes-charset" operation attribute,
+the Printer MUST reject the operation and return this status and any
+'text' or 'name' attributes using the 'utf-8' charset (see Section
+3.1.4.1). See section 3.1.7.
+
+
+13.1.4.15 client-error-conflicting-attributes (0x040E)
+
+The request is rejected because some attribute values conflicted with
+the values of other attributes which this document does not permit to be
+substituted or ignored. The Printer object MUST also return in the
+Unsupported Attributes Group the conflicting attributes supplied by the
+client. See sections 3.1.7 and 3.2.1.2.
+
+
+13.1.4.16 client-error-compression-not-supported (0x040F)
+
+The IPP object is refusing to service the request because the document
+data, as specified in the "compression" operation attribute, is
+compressed in a way that is not supported by the Printer object. This
+error is returned independent of the client-supplied "ipp-attribute-
+fidelity". The Printer object MUST return this status code, even if
+there are other Job Template attributes that are not supported as well,
+since this error is a bigger problem than with Job Template attributes.
+See sections 3.1.7 and 3.2.1.1.
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 161]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+13.1.4.17 client-error-compression-error (0x0410)
+
+The IPP object is refusing to service the request because the document
+data cannot be decompressed when using the algorithm specified by the
+"compression" operation attribute. This error is returned independent
+of the client-supplied "ipp-attribute-fidelity". The Printer object
+MUST return this status code, even if there are Job Template attributes
+that are not supported as well, since this error is a bigger problem
+than with Job Template attributes. See sections 3.1.7 and 3.2.1.1.
+
+
+13.1.4.18 client-error-document-format-error (0x0411)
+
+The IPP object is refusing to service the request because Printer
+encountered an error in the document data while interpreting it. This
+error is returned independent of the client-supplied "ipp-attribute-
+fidelity". The Printer object MUST return this status code, even if
+there are Job Template attributes that are not supported as well, since
+this error is a bigger problem than with Job Template attributes. See
+sections 3.1.7 and 3.2.1.1.
+
+
+13.1.4.19 client-error-document-access-error (0x0412)
+
+The IPP object is refusing to service the Print-URI or Send-URI request
+because Printer encountered an access error while attempting to validate
+the accessibility or access the document data specified in the
+"document-uri" operation attribute. The Printer MAY also return a
+specific document access error code using the "document-access-error"
+operation attribute (see section 3.1.6.4). This error is returned
+independent of the client-supplied "ipp-attribute-fidelity". The
+Printer object MUST return this status code, even if there are Job
+Template attributes that are not supported as well, since this error is
+a bigger problem than with Job Template attributes. See section 3.1.7.
+
+
+13.1.5 Server Error Status Codes
+
+This class of status codes indicates cases in which the IPP object is
+aware that it has erred or is incapable of performing the request. The
+IPP object SHOULD include a message containing an explanation of the
+error situation, and whether it is a temporary or permanent condition.
+
+
+13.1.5.1 server-error-internal-error (0x0500)
+
+The IPP object encountered an unexpected condition that prevented it
+from fulfilling the request. This error status code differs from
+"server-error-temporary-error" in that it implies a more permanent type
+of internal error. It also differs from "server-error-device-error" in
+that it implies an unexpected condition (unlike a paper-jam or out-of-
+toner problem which is undesirable but expected). This error status
+code indicates that probably some knowledgeable human intervention is
+required.
+
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 162]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+13.1.5.2 server-error-operation-not-supported (0x0501)
+
+The IPP object does not support the functionality required to fulfill
+the request. This is the appropriate response when the IPP object does
+not recognize an operation or is not capable of supporting it. See
+section 3.1.7.
+
+
+13.1.5.3 server-error-service-unavailable (0x0502)
+
+The IPP object is currently unable to handle the request due to a
+temporary overloading or maintenance of the IPP object. The implication
+is that this is a temporary condition which will be alleviated after
+some delay. If known, the length of the delay may be indicated in the
+message. If no delay is given, the IPP application should handle the
+response as it would for a "server-error-temporary-error" response. If
+the condition is more permanent, the error status codes "client-error-
+gone" or "client-error-not-found" could be used.
+
+
+13.1.5.4 server-error-version-not-supported (0x0503)
+
+The IPP object does not support, or refuses to support, the IPP protocol
+version that was supplied as the value of the "version-number" operation
+parameter in the request. The IPP object is indicating that it is
+unable or unwilling to complete the request using the same major and
+minor version number as supplied in the request other than with this
+error message. The error response SHOULD contain a "status-message"
+attribute (see section 3.1.6.2) describing why that version is not
+supported and what other versions are supported by that IPP object. See
+section 3.1.8.
+
+The error response MUST identify in the "version-number" operation
+parameter the closest version number that the IPP object does support.
+For example, if a client supplies version '1.0' and an IPP/1.1 object
+supports version '1.0', then it responds with version '1.0' in all
+responses to such a request. If the IPP/1.1 object does not support
+version '1.0', then it should accept the request and respond with
+version '1.1' or may reject the request and respond with this error code
+and version '1.1'. If a client supplies a version '1.2', the IPP/1.1
+object should accept the request and return version '1.1' or may reject
+the request and respond with this error code and version '1.1'. See
+sections 3.1.8 and 4.4.14.
+
+
+13.1.5.5 server-error-device-error (0x0504)
+
+A printer error, such as a paper jam, occurs while the IPP object
+processes a Print or Send operation. The response contains the true Job
+Status (the values of the "job-state" and "job-state-reasons"
+attributes). Additional information can be returned in the OPTIONAL
+"job-state-message" attribute value or in the OPTIONAL status message
+that describes the error in more detail. This error status code is only
+returned in situations where the Printer is unable to accept the create
+request because of such a device error. For example, if the Printer is
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 163]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+unable to spool, and can only accept one job at a time, the reason it
+might reject a create request is that the printer currently has a paper
+jam. In many cases however, where the Printer object can accept the
+request even though the Printer has some error condition, the
+'successful-ok' status code will be returned. In such a case, the
+client would look at the returned Job Object Attributes or later query
+the Printer to determine its state and state reasons.
+
+
+13.1.5.6 server-error-temporary-error (0x0505)
+
+A temporary error such as a buffer full write error, a memory overflow
+(i.e. the document data exceeds the memory of the Printer), or a disk
+full condition, occurs while the IPP Printer processes an operation.
+The client MAY try the unmodified request again at some later point in
+time with an expectation that the temporary internal error condition may
+have been cleared. Alternatively, as an implementation option, a
+Printer object MAY delay the response until the temporary condition is
+cleared so that no error is returned.
+
+
+13.1.5.7 server-error-not-accepting-jobs (0x0506)
+
+A temporary error indicating that the Printer is not currently accepting
+jobs, because the administrator has set the value of the Printer's
+"printer-is-not-accepting-jobs" attribute to 'false' (by means outside
+the scope of this IPP/1.1 document).
+
+
+13.1.5.8 server-error-busy (0x0507)
+
+A temporary error indicating that the Printer is too busy processing
+jobs and/or other requests. The client SHOULD try the unmodified request
+again at some later point in time with an expectation that the temporary
+busy condition will have been cleared.
+
+
+13.1.5.9 server-error-job-canceled (0x0508)
+
+An error indicating that the job has been canceled by an operator or the
+system while the client was transmitting the data to the IPP Printer.
+If a job-id and job-uri had been created, then they are returned in the
+Print-Job, Send-Document, or Send-URI response as usual; otherwise, no
+job-id and job-uri are returned in the response.
+
+
+13.1.5.10 server-error-multiple-document-jobs-not-supported (0x0509)
+
+The IPP object does not support multiple documents per job and a client
+attempted to supply document data with a second Send-Document or Send-
+URI operation.
+
+
+
+
+
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 164]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+13.2 Status Codes for IPP Operations
+
+PJ = Print-Job, PU = Print-URI, CJ = Create-Job, SD = Send-Document
+SU = Send-URI, V = Validate-Job, GA = Get-Job-Attributes and
+Get-Printer-Attributes, GJ = Get-Jobs, C = Cancel-Job
+
+ IPP Operations
+IPP Status Keyword PJ PU CJ SD SU V GA GJ C
+------------------ -- -- -- -- -- - -- -- -
+successful-ok x x x x x x x x x
+successful-ok-ignored-or-substituted- x x x x x x x x x
+ attributes
+successful-ok-conflicting-attributes x x x x x x x x x
+client-error-bad-request x x x x x x x x x
+client-error-forbidden x x x x x x x x x
+client-error-not-authenticated x x x x x x x x x
+client-error-not-authorized x x x x x x x x x
+client-error-not-possible x x x x x x x x x
+client-error-timeout x x
+client-error-not-found x x x x x x x x x
+client-error-gone x x x x x x x x x
+client-error-request-entity-too-large x x x x x x x x x
+client-error-request-value-too-long x x x x x x x x x
+client-error-document-format-not- x x x x x x
+ supported
+client-error-attributes-or-values-not- x x x x x x x x x
+ supported
+client-error-uri-scheme-not-supported x x
+client-error-charset-not-supported x x x x x x x x x
+client-error-conflicting-attributes x x x x x x x x x
+client-error-compression-not-supported x x x x x
+client-error-compression-error x x x x
+client-error-document-format-error x x x x
+client-error-document-access-error x x
+server-error-internal-error x x x x x x x x x
+server-error-operation-not-supported x x x x
+server-error-service-unavailable x x x x x x x x x
+server-error-version-not-supported x x x x x x x x x
+server-error-device-error x x x x x
+server-error-temporary-error x x x x x
+server-error-not-accepting-jobs x x x x
+server-error-busy x x x x x x x x x
+server-error-job-canceled x x x
+server-error-multiple-document-jobs- x x
+ not-supported
+
+
+
+
+
+
+
+
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 165]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+HJ = Hold-Job, RJ = Release-Job, RS = Restart-Job
+PP = Pause-Printer, RP = Resume-Printer, PJ = Purge-Jobs
+
+ IPP Operations (cont.)
+IPP Status Keyword HJ RJ RS PP RP PJ
+------------------ -- -- -- -- -- --
+successful-ok x x x x x x
+successful-ok-ignored-or-substituted- x x x x x x
+ attributes
+successful-ok-conflicting-attributes x x x x x x
+client-error-bad-request x x x x x x
+client-error-forbidden x x x x x x
+client-error-not-authenticated x x x x x x
+client-error-not-authorized x x x x x x
+client-error-not-possible x x x x x x
+client-error-timeout
+client-error-not-found x x x x x x
+client-error-gone x x x x x x
+client-error-request-entity-too-large x x x x x x
+client-error-request-value-too-long x x x x x x
+client-error-document-format-not-
+ supported
+client-error-attributes-or-values-not- x x x x x x
+ supported
+client-error-uri-scheme-not-supported
+client-error-charset-not-supported x x x x x x
+client-error-conflicting-attributes x x x x x x
+client-error-compression-not-supported
+client-error-compression-error
+client-error-document-format-error
+client-error-document-access-error
+server-error-internal-error x x x x x x
+server-error-operation-not-supported x x x x x x
+server-error-service-unavailable x x x x x x
+server-error-version-not-supported x x x x x x
+server-error-device-error
+server-error-temporary-error x x x x x x
+server-error-not-accepting-jobs
+server-error-busy x x x x x x
+server-error-job-canceled
+server-error-multiple-document-jobs-
+ not-supported
+
+
+
+
+
+
+
+
+
+
+
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 166]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+
+
+
+14. APPENDIX C: "media" keyword values
+
+
+Standard keyword values are taken from several sources.
+
+Standard values are defined (taken from DPA[ISO10175] and the Printer
+MIB[RFC1759]):
+
+ 'default': The default medium for the output device
+ 'iso-a4-white': Specifies the ISO A4 white medium
+ 'iso-a4-colored': Specifies the ISO A4 colored medium
+ 'iso-a4-transparent' Specifies the ISO A4 transparent medium
+ 'iso-a3-white': Specifies the ISO A3 white medium
+ 'iso-a3-colored': Specifies the ISO A3 colored medium
+ 'iso-a5-white': Specifies the ISO A5 white medium
+ 'iso-a5-colored': Specifies the ISO A5 colored medium
+ 'iso-b4-white': Specifies the ISO B4 white medium
+ 'iso-b4-colored': Specifies the ISO B4 colored medium
+ 'iso-b5-white': Specifies the ISO B5 white medium
+ 'iso-b5-colored': Specifies the ISO B5 colored medium
+ 'jis-b4-white': Specifies the JIS B4 white medium
+ 'jis-b4-colored': Specifies the JIS B4 colored medium
+ 'jis-b5-white': Specifies the JIS B5 white medium
+ 'jis-b5-colored': Specifies the JIS B5 colored medium
+
+
+The following standard values are defined for North American media:
+
+ 'na-letter-white': Specifies the North American letter white medium
+ 'na-letter-colored': Specifies the North American letter colored
+ medium
+ 'na-letter-transparent': Specifies the North American letter
+ transparent medium
+ 'na-legal-white': Specifies the North American legal white medium
+ 'na-legal-colored': Specifies the North American legal colored medium
+
+
+The following standard values are defined for envelopes:
+
+ 'iso-b4-envelope': Specifies the ISO B4 envelope medium
+ 'iso-b5-envelope': Specifies the ISO B5 envelope medium
+ 'iso-c3-envelope': Specifies the ISO C3 envelope medium
+ 'iso-c4-envelope': Specifies the ISO C4 envelope medium
+ 'iso-c5-envelope': Specifies the ISO C5 envelope medium
+ 'iso-c6-envelope': Specifies the ISO C6 envelope medium
+ 'iso-designated-long-envelope': Specifies the ISO Designated Long
+ envelope medium
+ 'na-10x13-envelope': Specifies the North American 10x13 envelope
+ medium
+ 'na-9x12-envelope': Specifies the North American 9x12 envelope medium
+ 'monarch-envelope': Specifies the Monarch envelope
+ 'na-number-10-envelope': Specifies the North American number 10
+ business envelope medium
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 167]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+ 'na-7x9-envelope': Specifies the North American 7x9 inch envelope
+ 'na-9x11-envelope': Specifies the North American 9x11 inch envelope
+ 'na-10x14-envelope': Specifies the North American 10x14 inch envelope
+ 'na-number-9-envelope': Specifies the North American number 9
+ business envelope
+ 'na-6x9-envelope': Specifies the North American 6x9 inch envelope
+ 'na-10x15-envelope': Specifies the North American 10x15 inch envelope
+
+
+The following standard values are defined for the less commonly used
+media (white-only):
+
+ 'executive-white': Specifies the white executive medium
+ 'folio-white': Specifies the folio white medium
+ 'invoice-white': Specifies the white invoice medium
+ 'ledger-white': Specifies the white ledger medium
+ 'quarto-white': Specified the white quarto medium
+ 'iso-a0-white': Specifies the ISO A0 white medium
+ 'iso-a1-white': Specifies the ISO A1 white medium
+ 'iso-a2-white': Specifies the ISO A2 white medium
+ 'iso-a6-white': Specifies the ISO A6 white medium
+ 'iso-a7-white': Specifies the ISO A7 white medium
+ 'iso-a8-white': Specifies the ISO A8 white medium
+ 'iso-a9-white': Specifies the ISO A9 white medium
+ 'iso-10-white': Specifies the ISO A10 white medium
+ 'iso-b0-white': Specifies the ISO B0 white medium
+ 'iso-b1-white': Specifies the ISO B1 white medium
+ 'iso-b2-white': Specifies the ISO B2 white medium
+ 'iso-b3-white': Specifies the ISO B3 white medium
+ 'iso-b6-white': Specifies the ISO B6 white medium
+ 'iso-b7-white': Specifies the ISO B7 white medium
+ 'iso-b8-white': Specifies the ISO B8 white medium
+ 'iso-b9-white': Specifies the ISO B9 white medium
+ 'iso-b10-white': Specifies the ISO B10 white medium
+ 'jis-b0-white': Specifies the JIS B0 white medium
+ 'jis-b1-white': Specifies the JIS B1 white medium
+ 'jis-b2-white': Specifies the JIS B2 white medium
+ 'jis-b3-white': Specifies the JIS B3 white medium
+ 'jis-b6-white': Specifies the JIS B6 white medium
+ 'jis-b7-white': Specifies the JIS B7 white medium
+ 'jis-b8-white': Specifies the JIS B8 white medium
+ 'jis-b9-white': Specifies the JIS B9 white medium
+ 'jis-b10-white': Specifies the JIS B10 white medium
+
+
+The following standard values are defined for engineering media (white
+only):
+
+ 'a-white': Specifies the engineering A size medium
+ 'b-white': Specifies the engineering B size medium
+ 'c-white': Specifies the engineering C size medium
+ 'd-white': Specifies the engineering D size medium
+ 'e-white': Specifies the engineering E size medium
+
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 168]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+The following standard values are defined for input-trays (from ISO DPA
+and the Printer MIB):
+
+ 'top': The top input tray in the printer.
+ 'middle': The middle input tray in the printer.
+ 'bottom': The bottom input tray in the printer.
+ 'envelope': The envelope input tray in the printer.
+ 'manual': The manual feed input tray in the printer.
+ 'large-capacity': The large capacity input tray in the printer.
+ 'main': The main input tray
+ 'side': The side input tray
+
+
+The following standard values are defined for media sizes (from ISO
+DPA):
+
+ 'iso-a0': Specifies the ISO A0 size: 841 mm by 1189 mm as defined in
+ ISO 216
+ 'iso-a1': Specifies the ISO A1 size: 594 mm by 841 mm as defined in
+ ISO 216
+ 'iso-a2': Specifies the ISO A2 size: 420 mm by 594 mm as defined in
+ ISO 216
+ 'iso-a3': Specifies the ISO A3 size: 297 mm by 420 mm as defined in
+ ISO 216
+ 'iso-a4': Specifies the ISO A4 size: 210 mm by 297 mm as defined in
+ ISO 216
+ 'iso-a5': Specifies the ISO A5 size: 148 mm by 210 mm as defined in
+ ISO 216
+ 'iso-a6': Specifies the ISO A6 size: 105 mm by 148 mm as defined in
+ ISO 216
+ 'iso-a7': Specifies the ISO A7 size: 74 mm by 105 mm as defined in
+ ISO 216
+ 'iso-a8': Specifies the ISO A8 size: 52 mm by 74 mm as defined in ISO
+ 216
+ 'iso-a9': Specifies the ISO A9 size: 37 mm by 52 mm as defined in ISO
+ 216
+ 'iso-a10': Specifies the ISO A10 size: 26 mm by 37 mm as defined in
+ ISO 216
+ 'iso-b0': Specifies the ISO B0 size: 1000 mm by 1414 mm as defined in
+ ISO 216
+ 'iso-b1': Specifies the ISO B1 size: 707 mm by 1000 mm as defined in
+ ISO 216
+ 'iso-b2': Specifies the ISO B2 size: 500 mm by 707 mm as defined in
+ ISO 216
+ 'iso-b3': Specifies the ISO B3 size: 353 mm by 500 mm as defined in
+ ISO 216
+ 'iso-b4': Specifies the ISO B4 size: 250 mm by 353 mm as defined in
+ ISO 216
+ 'iso-b5': Specifies the ISO B5 size: 176 mm by 250 mm as defined in
+ ISO 216
+ 'iso-b6': Specifies the ISO B6 size: 125 mm by 176 mm as defined in
+ ISO 216
+ 'iso-b7': Specifies the ISO B7 size: 88 mm by 125 mm as defined in
+ ISO 216
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 169]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+ 'iso-b8': Specifies the ISO B8 size: 62 mm by 88 mm as defined in ISO
+ 216
+ 'iso-b9': Specifies the ISO B9 size: 44 mm by 62 mm as defined in ISO
+ 216
+ 'iso-b10': Specifies the ISO B10 size: 31 mm by 44 mm as defined in
+ ISO 216
+ 'na-letter': Specifies the North American letter size: 8.5 inches by
+ 11 inches
+ 'na-legal': Specifies the North American legal size: 8.5 inches by 14
+ inches
+ 'executive': Specifies the executive size (7.25 X 10.5 in)
+ 'folio': Specifies the folio size (8.5 X 13 in)
+ 'invoice': Specifies the invoice size (5.5 X 8.5 in)
+ 'ledger': Specifies the ledger size (11 X 17 in)
+ 'quarto': Specifies the quarto size (8.5 X 10.83 in)
+ 'iso-c3': Specifies the ISO C3 size: 324 mm by 458 mm as defined in
+ ISO 269
+ 'iso-c4': Specifies the ISO C4 size: 229 mm by 324 mm as defined in
+ ISO 269
+ 'iso-c5': Specifies the ISO C5 size: 162 mm by 229 mm as defined in
+ ISO 269
+ 'iso-c6': Specifies the ISO C6 size: 114 mm by 162 mm as defined in
+ ISO 269
+ 'iso-designated-long': Specifies the ISO Designated Long size: 110 mm
+ by 220 mm as defined in ISO 269
+ 'na-10x13-envelope': Specifies the North American 10x13 size: 10
+ inches by 13 inches
+ 'na-9x12-envelope': Specifies the North American 9x12 size: 9 inches
+ by 12 inches
+ 'na-number-10-envelope': Specifies the North American number 10
+ business envelope size: 4.125 inches by 9.5 inches
+ 'na-7x9-envelope': Specifies the North American 7x9 inch envelope
+ size
+ 'na-9x11-envelope': Specifies the North American 9x11 inch envelope
+ size
+ 'na-10x14-envelope': Specifies the North American 10x14 inch envelope
+ size
+ 'na-number-9-envelope': Specifies the North American number 9
+ business envelope size
+ 'na-6x9-envelope': Specifies the North American 6x9 envelope size
+ 'na-10x15-envelope': Specifies the North American 10x15 envelope size
+ 'monarch-envelope': Specifies the Monarch envelope size (3.87 x 7.5
+ in)
+ 'jis-b0': Specifies the JIS B0 size: 1030mm x 1456mm
+ 'jis-b1': Specifies the JIS B1 size: 728mm x 1030mm
+ 'jis-b2': Specifies the JIS B2 size: 515mm x 728mm
+ 'jis-b3': Specifies the JIS B3 size: 364mm x 515mm
+ 'jis-b4': Specifies the JIS B4 size: 257mm x 364mm
+ 'jis-b5': Specifies the JIS B5 size: 182mm x 257mm
+ 'jis-b6': Specifies the JIS B6 size: 128mm x 182mm
+ 'jis-b7': Specifies the JIS B7 size: 91mm x 128mm
+ 'jis-b8': Specifies the JIS B8 size: 64mm x 91mm
+ 'jis-b9': Specifies the JIS B9 size: 45mm x 64mm
+ 'jis-b10': Specifies the JIS B10 size: 32mm x 45mm
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 170]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+The following standard values are defined for engineering media sizes:
+
+ 'a': Specifies the engineering A size: 8.5 inches x 11 inches
+ 'b': Specifies the engineering B size: 11 inches x 17 inches
+ 'c': Specifies the engineering C size: 17 inches x 22 inches
+ 'd': Specifies the engineering D size: 22 inches x 34 inches
+ 'e': Specifies the engineering E size: 34 inches x 44 inches
+
+
+
+15. APPENDIX D: Processing IPP Attributes
+
+
+When submitting a print job to a Printer object, the IPP model allows a
+client to supply operation and Job Template attributes along with the
+document data. These Job Template attributes in the create request
+affect the rendering, production and finishing of the documents in the
+job. Similar types of instructions may also be contained in the
+document to be printed, that is, embedded within the print data itself.
+In addition, the Printer has a set of attributes that describe what
+rendering and finishing options which are supported by that Printer.
+This model, which allows for flexibility and power, also introduces the
+potential that at job submission time, these client-supplied attributes
+may conflict with either:
+
+ - what the implementation is capable of realizing (i.e., what the
+ Printer supports), as well as
+ - the instructions embedded within the print data itself.
+
+
+The following sections describe how these two types of conflicts are
+handled in the IPP model.
+
+
+15.1 Fidelity
+
+
+If there is a conflict between what the client requests and what a
+Printer object supports, the client may request one of two possible
+conflict handling mechanisms:
+
+ 1) either reject the job since the job can not be processed exactly
+ as specified, or
+ 2) allow the Printer to make any changes necessary to proceed with
+ processing the Job the best it can.
+
+
+In the first case the client is indicating to the Printer object: "Print
+the job exactly as specified with no exceptions, and if that can't be
+done, don't even bother printing the job at all." In the second case,
+the client is indicating to the Printer object: "It is more important to
+make sure the job is printed rather than be processed exactly as
+specified; just make sure the job is printed even if client supplied
+attributes need to be changed or ignored."
+
+The IPP model accounts for this situation by introducing an "ipp-
+attribute-fidelity" attribute.
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 171]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+In a create request, "ipp-attribute-fidelity" is a boolean operation
+attribute that is OPTIONALLY supplied by the client. The value 'true'
+indicates that total fidelity to client supplied Job Template attributes
+and values is required. The client is requesting that the Job be
+printed exactly as specified, and if that is not possible then the job
+MUST be rejected rather than processed incorrectly. The value 'false'
+indicates that a reasonable attempt to print the Job is acceptable. If
+a Printer does not support some of the client supplied Job Template
+attributes or values, the Printer MUST ignore them or substitute any
+supported value for unsupported values, respectively. The Printer may
+choose to substitute the default value associated with that attribute,
+or use some other supported value that is similar to the unsupported
+requested value. For example, if a client supplies a "media" value of
+'na-letter', the Printer may choose to substitute 'iso-a4' rather than a
+default value of 'envelope'. If the client does not supply the "ipp-
+attribute-fidelity" attribute, the Printer assumes a value of 'false'.
+
+Each Printer implementation MUST support both types of "fidelity"
+printing (that is whether the client supplies a value of 'true' or
+'false'):
+
+ - If the client supplies 'false' or does not supply the attribute,
+ the Printer object MUST always accept the request by ignoring
+ unsupported Job Template attributes and by substituting unsupported
+ values of supported Job Template attributes with supported values.
+ - If the client supplies 'true', the Printer object MUST reject the
+ request if the client supplies unsupported Job Template attributes.
+
+
+Since a client can always query a Printer to find out exactly what is
+and is not supported, "ipp-attribute-fidelity" set to 'false' is useful
+when:
+
+ 1) The End-User uses a command line interface to request attributes
+ that might not be supported.
+ 2) In a GUI context, if the End User expects the job might be moved
+ to another printer and prefers a sub-optimal result to nothing at
+ all.
+ 3) The End User just wants something reasonable in lieu of nothing at
+ all.
+
+
+15.2 Page Description Language (PDL) Override
+
+
+If there is a conflict between the value of an IPP Job Template
+attribute and a corresponding instruction in the document data, the
+value of the IPP attribute SHOULD take precedence over the document
+instruction. Consider the case where a previously formatted file of
+document data is sent to an IPP Printer. In this case, if the client
+supplies any attributes at job submission time, the client desires that
+those attributes override the embedded instructions. Consider the case
+were a previously formatted document has embedded in it commands to load
+'iso-a4' media. However, the document is passed to an end user that
+only has access to a printer with 'na-letter' media loaded. That end
+user most likely wants to submit that document to an IPP Printer with
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 172]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+the "media" Job Template attribute set to 'na-letter'. The job
+submission attribute should take precedence over the embedded PDL
+instruction. However, until companies that supply document data
+interpreters allow a way for external IPP attributes to take precedence
+over embedded job production instructions, a Printer might not be able
+to support the semantics that IPP attributes override the embedded
+instructions.
+
+The IPP model accounts for this situation by introducing a "pdl-
+override-supported" attribute that describes the Printer objects
+capabilities to override instructions embedded in the PDL data stream.
+The value of the "pdl-override-supported" attribute is configured by
+means outside the scope of this IPP/1.1 document.
+
+This REQUIRED Printer attribute takes on the following values:
+
+ - 'attempted': This value indicates that the Printer object attempts
+ to make the IPP attribute values take precedence over embedded
+ instructions in the document data, however there is no guarantee.
+ - 'not-attempted': This value indicates that the Printer object makes
+ no attempt to make the IPP attribute values take precedence over
+ embedded instructions in the document data.
+
+
+At job processing time, an implementation that supports the value of
+'attempted' might do one of several different actions:
+
+ 1) Generate an output device specific command sequence to realize the
+ feature represented by the IPP attribute value.
+ 2) Parse the document data itself and replace the conflicting
+ embedded instruction with a new embedded instruction that matches
+ the intent of the IPP attribute value.
+ 3) Indicate to the Printer that external supplied attributes take
+ precedence over embedded instructions and then pass the external
+ IPP attribute values to the document data interpreter.
+ 4) Anything else that allows for the semantics that IPP attributes
+ override embedded document data instructions.
+
+
+Since 'attempted' does not offer any type of guarantee, even though a
+given Printer object might not do a very "good" job of attempting to
+ensure that IPP attributes take a higher precedence over instructions
+embedded in the document data, it would still be a conforming
+implementation.
+
+At job processing time, an implementation that supports the value of
+'not-attempted' might do one of the following actions:
+
+ 1) Simply pre-pend the document data with the PDL instruction that
+ corresponds to the client-supplied PDL attribute, such that if the
+ document data also has the same PDL instruction, it will override
+ what the Printer object pre-pended. In other words, this
+ implementation is using the same implementation semantics for the
+ client-supplied IPP attributes as for the Printer object defaults.
+
+
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 173]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+ 2) Parse the document data and replace the conflicting embedded
+ instruction with a new embedded instruction that approximates, but
+ does not match, the semantic intent of the IPP attribute value.
+
+
+Note: The "ipp-attribute-fidelity" attribute applies to the Printer's
+ability to either accept or reject other unsupported Job Template
+attributes. In other words, if "ipp-attribute-fidelity" is set to
+'true', a Job is accepted if and only if the client supplied Job
+Template attributes and values are supported by the Printer. Whether
+these attributes actually affect the processing of the Job when the
+document data contains embedded instructions depends on the ability of
+the Printer to override the instructions embedded in the document data
+with the semantics of the IPP attributes. If the document data
+attributes can be overridden ("pdl-override-supported" set to
+'attempted'), the Printer makes an attempt to use the IPP attributes
+when processing the Job. If the document data attributes can not be
+overridden ("pdl-override-supported" set to 'not-attempted'), the
+Printer makes no attempt to override the embedded document data
+instructions with the IPP attributes when processing the Job, and hence,
+the IPP attributes may fail to affect the Job processing and output when
+the corresponding instruction is embedded in the document data.
+
+
+15.3 Using Job Template Attributes During Document Processing.
+
+
+The Printer object uses some of the Job object's Job Template attributes
+during the processing of the document data associated with that job.
+These include, but are not limited to, "orientation-requested", "number-
+up", "sides", "media", and "copies". The processing of each document in
+a Job Object MUST follow the steps below. These steps are intended only
+to identify when and how attributes are to be used in processing
+document data and any alternative steps that accomplishes the same
+effect can be used to implement this specification document.
+
+ 1. Using the client supplied "document-format" attribute or some form
+ of document format detection algorithm (if the value of "document-
+ format" is not specific enough), determine whether or not the
+ document data has already been formatted for printing. If the
+ document data has been formatted, then go to step 2. Otherwise, the
+ document data MUST be formatted. The formatting detection algorithm
+ is implementation defined and is not specified by this document.
+ The formatting of the document data uses the "orientation-
+ requested" attribute to determine how the formatted print data
+ should be placed on a print-stream page, see section 4.2.10 for the
+ details.
+
+ 2. The document data is in the form of a print-stream in a known
+ media type. The "page-ranges" attribute is used to select, as
+ specified in section 4.2.7, a sub-sequence of the pages in the
+ print-stream that are to be processed and images.
+
+ 3. The input to this step is a sequence of print-stream pages. This
+ step is controlled by the "number-up" attribute. If the value of
+ "number-up" is N, then during the processing of the print-stream
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 174]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+ pages, each N print-stream pages are positioned, as specified in
+ section 4.2.9, to create a single impression. If a given document
+ does not have N more print-stream pages, then the completion of the
+ impression is controlled by the "multiple-document-handling"
+ attribute as described in section 4.2.4; when the value of this
+ attribute is 'single-document' or 'single-document-new-sheet', the
+ print-stream pages of document data from subsequent documents is
+ used to complete the impression.
+
+ The size(scaling), position(translation) and rotation of the print-
+ stream pages on the impression is implementation defined. Note
+ that during this process the print-stream pages may be rendered to
+ a form suitable for placing on the impression; this rendering is
+ controlled by the values of the "printer-resolution" and "print-
+ quality" attributes as described in sections 4.2.12 and 4.2.13. In
+ the case N=1, the impression is nearly the same as the print-stream
+ page; the differences would only be in the size, position and
+ rotation of the print-stream page and/or any decoration, such as a
+ frame to the page, that is added by the implementation.
+
+ 4. The collection of impressions is placed, in sequence, onto sides
+ of the media sheets. This placement is controlled by the "sides"
+ attribute and the orientation of the print-stream page, as
+ described in section 4.2.8. The orientation of the print-stream
+ pages affects the orientation of the impression; for example, if
+ "number-up" equals 2, then, typically, two portrait print-stream
+ pages become one landscape impression. Note that the placement of
+ impressions onto media sheets is also controlled by the "multiple-
+ document-handling" attribute as described in section 4.2.4.
+
+ 5. The "copies" and "multiple-document-handling" attributes are used
+ to determine how many copies of each media instance are created and
+ in what order. See sections 4.2.5 and 4.2.4 for the details.
+
+ 6. When the correct number of copies are created, the media instances
+ are finished according to the values of the "finishings" attribute
+ as described in 4.2.6. Note that sometimes finishing operations may
+ require manual intervention to perform the finishing operations on
+ the copies, especially uncollated copies. This document allows any
+ or all of the processing steps to be performed automatically or
+ manually at the discretion of the Printer object.
+
+
+16. APPENDIX E: Generic Directory Schema
+
+
+This section defines a generic schema for an entry in a directory
+service. A directory service is a means by which service users can
+locate service providers. In IPP environments, this means that IPP
+Printers can be registered (either automatically or with the help of an
+administrator) as entries of type printer in the directory using an
+implementation specific mechanism such as entry attributes, entry type
+fields, specific branches, etc. IPP clients can search or browse for
+entries of type printer. Clients use the directory service to find
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 175]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+entries based on naming, organizational contexts, or filtered searches
+on attribute values of entries. For example, a client can find all
+printers in the "Local Department" context. Authentication and
+authorization are also often part of a directory service so that an
+administrator can place limits on end users so that they are only
+allowed to find entries to which they have certain access rights. IPP
+itself does not require any specific directory service protocol or
+provider.
+
+Note: Some directory implementations allow for the notion of "aliasing".
+That is, one directory entry object can appear as multiple directory
+entry object with different names for each object. In each case, each
+alias refers to the same directory entry object which refers to a single
+IPP Printer object.
+
+The generic schema is a subset of IPP Printer Job Template and Printer
+Description attributes (sections 4.2 and 4.4). These attributes are
+identified as either RECOMMENDED or OPTIONAL for the directory entry
+itself. This conformance labeling is NOT the same conformance labeling
+applied to the attributes of IPP Printers objects. The conformance
+labeling in this Appendix is intended to apply to directory templates
+and to IPP Printer implementations that subscribe by adding one or more
+entries to a directory. RECOMMENDED attributes SHOULD be associated
+with each directory entry. OPTIONAL attributes MAY be associated with
+the directory entry (if known or supported). In addition, all directory
+entry attributes SHOULD reflect the current attribute values for the
+corresponding Printer object.
+
+The names of attributes in directory schema and entries SHOULD be the
+same as the IPP Printer attribute names as shown.
+
+In order to bridge between the directory service and the IPP Printer
+object, one of the RECOMMENDED directory entry attributes is the Printer
+object's "printer-uri-supported" attribute. The IPP client queries the
+"printer-uri-supported" attribute in the directory entry and then
+addresses the IPP Printer object using one of its URIs. The "uri-
+security-supported" attribute identifies the protocol (if any) used to
+secure a channel.
+
+The following attributes define the generic schema for directory entries
+of type PRINTER:
+
+ printer-uri-supported RECOMMENDED Section 4.4.1
+ uri-authentication-supported RECOMMENDED Section 4.4.2
+ uri-security-supported RECOMMENDED Section 4.4.3
+ printer-name RECOMMENDED Section 4.4.4
+ printer-location RECOMMENDED Section 4.4.5
+ printer-info OPTIONAL Section 4.4.6
+ printer-more-info OPTIONAL Section 4.4.7
+ printer-make-and-model RECOMMENDED Section 4.4.9
+ ipp-versions-supported RECOMMENDED Section 4.4.14
+ multiple-document-jobs-supported OPTIONAL Section 4.4.16
+ charset-supported OPTIONAL Section 4.4.18
+ generated-natural-language-
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 176]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+ supported OPTIONAL Section 4.4.20
+ document-format-supported RECOMMENDED Section 4.4.22
+ color-supported RECOMMENDED Section 4.4.26
+ compression-supported RECOMMENDED Section 4.4.32
+ pages-per-minute OPTIONAL Section 4.4.36
+ pages-per-minute-color OPTIONAL Section 4.4.37
+
+ finishings-supported OPTIONAL Section 4.2.6
+ number-up-supported OPTIONAL Section 4.2.7
+ sides-supported RECOMMENDED Section 4.2.8
+ media-supported RECOMMENDED Section 4.2.11
+ printer-resolution-supported OPTIONAL Section 4.2.12
+ print-quality-supported OPTIONAL Section 4.2.13
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 177]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+
+
+
+17. APPENDIX F: Differences between the IPP/1.0 and IPP/1.1 "Model and
+Semantics" Documents
+
+
+This Appendix is divided into two lists that summarize the differences
+between IPP/1.1 (this document) and IPP/1.0 [RFC2566]. The section
+numbers refer to the numbers in this document which in some cases have
+changed from RFC 2566. When a change affects multiple sections, the
+item is listed once in the order of the first section affected and the
+remaining affected section numbers are indicated.
+
+The first list contains extensions and clarifications and the second
+list contains changes in semantics or conformance. However, client and
+IPP object implementations of IPP/1.0 may implement any of the
+extensions and clarifications in this document.
+
+The following extensions and clarifications have been incorporated into
+this document:
+
+ 1. Section 2.1 - clarified that the term "client" can be either
+ contained in software controlled by an end user or a part of a
+ print server that controls devices.
+ 2. Section 2 - clarified that the term "IPP object" and "Printer
+ object" can either be embedded in a device object or part of a
+ print server that accepts IPP requests.
+ 3. Section 2.4 - added the description of the new "uri-authentication-
+ supported" Printer Description attribute.
+ 4. Section 3.1.3, 3.1.6, 3.2.5.2, and 3.2.6.2 - clarified the error
+ handling for operation attributes that have their own status code.
+ 5. Section 3.1.6 - reorganized this section into sub-sections to
+ separately describe "status-code", "status-message", "detailed-
+ status-message", and "document-access-error" attributes.
+ 6. Section 3.1.6.1 - clarified the error status codes and their
+ relationship to operation attributes.
+ 7. Section 3.1.6.3 - Added the OPTIONAL "detailed-status-message
+ (text(MAX))" operation attribute to provide additional more
+ detailed information about a response.
+ 8. Section 3.1.6.4 and 3.2.2 - Added the OPTIONAL "document-access-
+ error (text(MAX))" operation attribute for use with Print-URI and
+ Send-URI responses.
+ 9. Sections 3.1.7 - Added this new section to clarify returning
+ Unsupported Attributes for all operations, including only returning
+ attributes that were in the request. Moved the text from section
+ 3.2.1.2 Unsupported Attributes to this section.
+ 10. Sections 3.1.7 and 4.1 - clarified the encoding of the "out-
+ of-band" 'unsupported' and 'unknown' values.
+ 11. Section 3.1.8 - clarified that only the version number
+ parameter will be carried forward into future major or minor
+ versions of the protocol.
+ 12. Section 3.1.8 - relaxed the requirements to increment the
+ major version number in future versions of the Model and Semantics
+ document.
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 178]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+ 13. Section 3.1.9, and 3.2.5 - added the 'processing' state to the
+ list of job states that a job can be in after a Create-Job
+ operation.
+ 14. Section 3.1.9 - clarified that a non-spooling Printer MAY
+ accept zero or more subsequent jobs while processing a job and flow
+ control them down. Subsequent create requests are rejected with
+ the 'server-error-busy' error status.
+ 15. Section 3.2.1.1 - clarified the validation of the
+ "compression" operation attribute and its relationship to the
+ validation of the "document-format" attribute and returning
+ Unsupported Attributes.
+ 16. Sections 3.2.1.1, 4.3.8, 13.1.4.16, and 13.1.4.17 - added the
+ 'client-error-compression-not-supported', 'client-error-
+ compression-error' status codes and the 'unsupported-compression'
+ and 'compression-error' job-state-reasons.
+ 17. Sections 3.2.1.1 and 4.3.8 - added 'unsupported-document-
+ format' and 'document-format-error' job-state-reasons.
+ 18. Sections 3.2.2, 4.3.8 and 13.1.4.19 - added 'client-error-
+ document-access-error' status code and 'document-access-error' job
+ state reason.
+ 19. Section 3.2.5.2 and 3.2.6.2 - clarified that the Unsupported
+ Attributes group MUST NOT include attributes not requested in the
+ Get-Printer-Attributes request.
+ 20. Section 3.2.6 - clarified that "limit" takes precedence over
+ "which-jobs" and "my-jobs'.
+ 21. Section 3.2.6.2 - clarified that Get-Jobs returns 'successful-
+ ok' when no jobs to return.
+ 22. Sections 3.2.7, 3.2.8, and 3.2.9 - added the OPTIONAL Pause-
+ Printer, Resume-Printer, and Purge-Jobs operations
+ 23. Section 3.3.1 - clarified that the authorization required for
+ a Send-Document request MUST be the same user as the Create-Job or
+ an operator.
+ 24. Sections 3.3.5, 3.3.6, and 3.3.7 - added the OPTIONAL Hold-
+ Job, Release-Job, and Restart-Job operations.
+ 25. Section 4.1 - clarified that the encoding of the out-of-band
+ values are specified in the Encoding and Transport" document.
+ 26. Section 4.1.9.1 - clarified that 'application/octet-stream'
+ auto-sensing can happen at create request time and/or job/document
+ processing time.
+ 27. Section 4.1.14 - clarified that the localization of dateTime
+ by the client includes the time zone.
+ 28. Section 4.2 - clarified that xxx-supported have multiple
+ keywords and/or names by adding parentheses to the table to give:
+ (1setOf (type3 keyword | name))
+ 29. Section 4.2.2 - added the 'indefinite' keyword value to the
+ "job-hold-until" attribute for use with the create operations and
+ Hold-Job and Restart-Job operations.
+ 30. Section 4.2.6 - added more enum values to the "finishings" Job
+ Template attribute.
+ 31. Section 4.3.7 - added that a forwarding server that cannot get
+ any job state MAY return the job's state as 'completed', provided
+ that it also return the new 'queued-in-device' job state reason.
+
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 179]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+ 32. Section 4.3.7.2 - added the Partitioning of Job States section
+ to clarify the concepts of Job Retention, Job History, and Job
+ Removal.
+ 33. Section 4.3.8 - added 'job-data-insufficient' job state reason
+ to indicate whether sufficient data has arrived for the document to
+ start to be processed.
+ 34. Section 4.3.8 - added 'document-access-error' job state reason
+ to indicate an access error of any kind.
+ 35. Section 4.3.8 - added 'job-queued-for-marker' job state reason
+ to indicate whether the job has completed some processing and is
+ waiting for the marker.
+ 36. Section 4.3.8 - added 'unsupported-compression' and
+ 'compression-error' job state reasons to indicate compression not
+ supported or compression processing error after the create has been
+ accepted.
+ 37. Section 4.3.8 - added 'unsupported-document-format' and
+ 'document-format-error' job state reasons to indicate document not
+ supported or document format processing error after the create has
+ been accepted.
+ 38. Section 4.3.8 - added 'queued-in-device' job state reason to
+ indicate that a job as been forwarded to a print system or device
+ that does not provide any job status.
+ 39. Section 4.3.10 - added "job-detailed-status-messages (1setOf
+ text(MAX)) for returning detailed error messages.
+ 40. Section 4.3.11 - added the "job-document-access-errors (1setOf
+ text(MAX))
+ 41. Section 4.3.14.2 - clarified that the time recorded is the
+ first time processing since the create operation or the Restart-Job
+ operation.
+ 42. Section 4.3.14.2 and 4.3.14.3 - clarified that the out-of-band
+ value 'no-value' is returned if the job has not started processing
+ or has not completed, respectively.
+ 43. Section 4.3.14 - Added the OPTIONAL "date-time-at-creation",
+ "date-time-at-processing", and "date-time-at-completed" Event Time
+ Job Description attributes
+ 44. Section 4.4.3 - added the 'tls' value to "uri-security-
+ supported" attribute.
+ 45. Section 4.4.3 - clarified "uri-security-supported" is
+ orthogonal to Client Authentication so that 'none' does not exclude
+ Client Authentication.
+ 46. Section 4.4.11 - simplified the "printer-state" descriptions
+ while generalizing to allow high end devices that interpret one or
+ more jobs while marking another. Indicated that 'spool-area-full'
+ and 'stopped-partly' "printer-state-reasons" may be used to provide
+ further state information.
+ 47. Section 4.4.12 - added the 'moving-to-paused' keyword value to
+ the "printer-state-reasons" attribute for use with the Pause-Job
+ operation.
+ 48. Section 4.4.12 - replaced the duplicate 'marker-supply-low'
+ keyword with the missing 'toner-empty' keyword for the "printer-
+ state-reasons" attribute. (This correction was also made before
+ RFC 2566 was published).
+
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 180]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+ 49. Section 4.4.12 - clarified 'spool-area-full' "printer-state-
+ reasons" to include non-spooling printers to indicate when it can
+ and cannot accept another job.
+ 50. Section 4.4.15 - added the enum values to the "operations-
+ supported" attribute for the new operations. Clarified that the
+ values of this attribute are encoded as any enum, namely 32-bit
+ values.
+ 51. Section 4.4.30 - clarified that the dateTime value of
+ "printer-current-time" is on a "best efforts basis". If a proper
+ date-time cannot be obtained, the implementation returns the 'no-
+ value' out-of-band value. Also clarified that the time zone NEED
+ NOT be the time zone that the people near the device use and that
+ the client SHOULD display the dateTime attributes in the user's
+ local time.
+ 52. Sections 4.4.36 and 4.4.37 - added the OPTIONAL "pages-per-
+ minute" and "pages-per-minute-color" Printer Description
+ attributes.
+ 53. Section 5.1 - clarified that the client conformance
+ requirements apply to clients controlled by an end user and clients
+ in servers.
+ 54. Section 5.1 - clarified that any response MAY contain
+ additional attribute groups, attributes, attribute syntaxes, or
+ attribute values.
+ 55. Section 5.1 - clarified that a client SHOULD do its best to
+ prevent a channel from being closed by a lower layer when the
+ channel is flow controlled off by the IPP Printer.
+ 56. Section 5.2 - clarified that the IPP object requirements apply
+ to objects embedded in devices or that are parts of servers.
+ 57. Section 5.2.2 - clarified that IPP objects MAY return
+ operation responses that contain attribute groups, attribute names,
+ attribute syntaxes, attribute values, and status codes that are
+ extensions to this standard.
+ 58. Section 8.3 - clarified the use of URIs for each Client
+ Authentication mechanism.
+ 59. Section 8.5 - added the security discussion around the new
+ operator/administrator operations.
+ 60. Section 13.1.4.16 - added client-error-compression-not-
+ supported (0x040F)
+ 61. Section 13.1.4.17 - added client-error-compression-error
+ (0x0410)
+ 62. Section 13.1.4.18 - added client-error-document-format-error
+ (0x0411)
+ 63. Section 13.1.4.19 - added client-error-document-access-error
+ (0x0412)
+ 64. Section 13.1.5.10 - added server-error-multiple-document-jobs-
+ not-supported (0x0509)
+ 65. Section 14 - added 'a-white', 'b-white', 'c-white', 'd-white',
+ and 'e-white' and clarified that the existing 'a', 'b', 'c', 'd',
+ and 'e' values are size values.
+ 66. Section 16 - added the OPTIONAL "pages-per-minute" and "pages-
+ per-minute-color" Printer attributes to the Directory schema.
+ 67. Section 16 - added OPTIONAL "multiple-document-jobs-supported"
+ to the Directory schema.
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 181]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+ 68. Section 16 - added RECOMMENDED "uri-authentication-supported",
+ "ipp-versions-supported", and "compression-supported" to the
+ Directory schema.
+
+The following changes in semantics and/or conformance have been
+incorporated into this document:
+
+ 1. Section 3.1.8, 5.2.4, and 13.1.5.4 - Clients and IPP objects MUST
+ support version 1.1 conformance requirements. It is
+ recommended that they interoperate with 1.0. Also clarified
+ that IPP Printers MUST accept '1.1' requests. It is
+ recommended that they also accept '1.x' requests.
+ 2. Section 3.2.1.1 and section 4.4.32 - changed the "compression"
+ operation and the "compression-supported" Printer Description
+ attribute from OPTIONAL to REQUIRED.
+ 3. Sections 3.2.1.2 and 4.3.8 - changed "job-state-reasons" from
+ RECOMMENDED to REQUIRED, so that "job-state-reasons" MUST be
+ returned in create operation responses.
+ 4. Sections 3.2.4, 3.3.1, 4.4.16, and 16 - changed Create-Job/Send-
+ Document so that they MAY be implemented while only supporting
+ one document jobs. Added the "multiple-document-jobs-supported"
+ boolean Printer Description attribute to indicate whether
+ Create-Job/Send-Document support multiple document jobs or not.
+ Added to the Directory schema.
+ 5. Section 4.1.9 - deleted 'text/plain; charset=iso-10646-ucs-2',
+ since binary is not legal with the 'text' type.
+ 6. Section 4.2.4 - indicated that the "multiple-document-handling"
+ Job Template attribute MUST be supported with at least one value
+ if the Printer supports multiple documents per job
+ 7. Section 4.3.7.2 - indicated that the 'job-restartable' job state
+ reason SHOULD be supported if the Restart-Job operation is
+ supported.
+ 8. Section 4.3.8 - changed "job-state-reasons" from RECOMMENDED to
+ REQUIRED.
+ 9. Section 4.3.8 - clarified the conformance of the values of the
+ "job-state-reasons" attribute by copying conformance
+ requirements from other sections of the document so that it is
+ clear from reading the definition of "job-state-reasons" which
+ values MUST or SHOULD be supported. The 'none', 'unsupported-
+ compression', and 'unsupported-document-format' values MUST be
+ supported. The ''job-hold-until-specified' SHOULD be specified
+ if the "job-hold-until" Job Template is supported. The
+ following values SHOULD be supported: 'job-canceled-by-user',
+ 'aborted-by-system', and 'job-completed-successfully'. The
+ 'job-canceled-by-operator' SHOULD be supported if the
+ implementation permits canceling by other than the job owner.
+ The 'job-canceled-at-device' SHOULD be supported if the device
+ supports canceling jobs at the console. The 'job-completed-
+ with-warnings' SHOULD be supported, if the implementation
+ detects warnings. The 'job-completed-with-errors' SHOULD be
+ supported if the implementation detects errors. The 'job-
+ restartable' SHOULD be supported if the Restart-Job operation is
+ supported.
+
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 182]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+ 10. Section 4.3.14 - changed the "time-at-creation", "time-at-
+ processing", and "time-at-completed" Event Time Job Description
+ attributes from OPTIONAL to REQUIRED.
+ 11. Section 4.3.14.4 - added the REQUIRED "job-printer-up-time
+ (integer(1:MAX))" Job Description attribute as an alias for
+ "printer-up-time" to reduce number of operations to get job
+ times.
+ 12. Section 4.4.2 - added the REQUIRED "uri-authentication-supported
+ (1setOf type2 keyword)" Printer Description attribute to
+ describe the Client Authentication used by each Printer URI.
+ 13. Section 4.4.12 - changed "printer-state-reasons" Printer
+ Description attribute from OPTIONAL to REQUIRED.
+ 14. Section 4.4.12 - changed 'paused' value of "printer-state-
+ reasons" to MUST if Pause-Printer operation is supported.
+ 15. Section 4.4.14 - added the REQUIRED "ipp-versions-supported
+ (1setOf keyword)" Printer Description attribute, since IPP/1.1
+ Printers do not have to support version '1.0' conformance
+ requirements. Section 4.4.16 - added the "multiple-document-
+ jobs-supported (boolean)" Printer Description attribute so that
+ a client can tell whether a Printer that supports Create-
+ Job/Send-Document supports multiple document jobs or not. This
+ attribute is REQUIRED if the Create-Job operation is supported.
+ 16. Section 4.4.24 - changed the "queued-job-count" Printer
+ Description attribute from RECOMMENDED to REQUIRED.
+ 17. Section 4.4.32 - changed "compression-supported (1setOf type3
+ keyword)" Printer Description attribute from OPTIONAL to
+ REQUIRED.
+ 18. Section 5.1 - changed the client security requirements from
+ RECOMMENDED non-standards track SSL3 to MUST support Client
+ Authentication as defined in the IPP/1.1 Encoding and Transport
+ document [IPP-PRO]. A client SHOULD support Operation Privacy
+ and Server Authentication as defined in the IPP/1.1 Encoding and
+ Transport document [IPP-PRO].
+ 19. Section 5.2.7 - changed the IPP object security requirements from
+ OPTIONAL non-standards track SSL3 to SHOULD contain support for
+ Client Authentication as defined in the IPP/1.1 Encoding and
+ Transport document [IPP-PRO]. A Printer implementation MAY
+ allow an administrator to configure the Printer so that all,
+ some, or none of the users are authenticated. An IPP Printer
+ implementation SHOULD contain support for Operation Privacy and
+ Server Authentication as defined in the IPP/1.1 Encoding and
+ Transport document [IPP-PRO]. A Printer implementation MAY
+ allow an administrator to configure the degree of support for
+ Operation Privacy and Server Authentication. Security MUST NOT
+ be compromised when the client supplies a lower version-number
+ in a request.
+
+See also the "IPP/1.1 Encoding and Transport" [IPP-PRO] document for
+differences between IPP/1.0 [RFC2565] and IPP/1.1 [IPP-PRO].
+
+
+
+
+
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 183]
+ Expires December 23, 1999
+
+
+
+INTERNET-DRAFT IPP/1.1: Model and Semantics June 23, 1999
+
+
+18. Full Copyright Statement
+
+
+Copyright (C) The Internet Society (1999). All Rights Reserved.
+
+This document and translations of it may be copied and furnished to
+others, and derivative works that comment on or otherwise explain it or
+assist in its implementation may be prepared, copied, published and
+distributed, in whole or in part, without restriction of any kind,
+provided that the above copyright notice and this paragraph are included
+on all such copies and derivative works. However, this document itself
+may not be modified in any way, such as by removing the copyright notice
+or references to the Internet Society or other Internet organizations,
+except as needed for the purpose of developing Internet standards in
+which case the procedures for copyrights defined in the Internet
+Standards process must be followed, or as required to translate it into
+languages other than English.
+
+The limited permissions granted above are perpetual and will not be
+revoked by the Internet Society or its successors or assigns.
+
+This document and the information contained herein is provided on an "AS
+IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING TASK
+FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT
+LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION HEREIN WILL NOT
+INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR
+FITNESS FOR A PARTICULAR PURPOSE.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+deBry, Hastings, Herriot, Isaacson, Powell [Page 184]
+ Expires December 23, 1999
diff --git a/standards/draft-ietf-ipp-protocol-v11-03.txt b/standards/draft-ietf-ipp-protocol-v11-03.txt
new file mode 100644
index 000000000..32e6ee36c
--- /dev/null
+++ b/standards/draft-ietf-ipp-protocol-v11-03.txt
@@ -0,0 +1,2540 @@
+INTERNET-DRAFT Robert Herriot (editor)
+<draft-ietf-ipp-protocol-v11-03.txt> Xerox Corporation
+ Sylvan Butler
+ Hewlett-Packard
+ Paul Moore
+ Microsoft
+ Randy Turner
+ 2wire.com
+ John Wenn
+ Xerox Corporation
+ June 23, 1999
+
+
+ Internet Printing Protocol/1.1: Encoding and Transport
+
+Status of this Memo
+
+This document is an Internet-Draft and is in full conformance with all
+provisions of Section 10 of [RFC2026]. Internet-Drafts are working
+documents of the Internet Engineering Task Force (IETF), its areas, and
+its working groups. Note that other groups may also distribute working
+documents as Internet-Drafts.
+
+Internet-Drafts are draft documents valid for a maximum of six months
+and may be updated, replaced, or obsoleted by other documents at any
+time. It is inappropriate to use Internet-Drafts as reference material
+or to cite them other than as "work in progress".
+
+The list of current Internet-Drafts can be accessed at
+http://www.ietf.org/ietf/1id-abstracts.txt
+
+The list of Internet-Draft Shadow Directories can be accessed as
+http://www.ietf.org/shadow.html.
+
+Copyright Notice
+
+Copyright (C)The Internet Society (1998, 1999). All Rights Reserved.
+
+Abstract
+
+
+This document is one of a set of documents, which together describe all
+aspects of a new Internet Printing Protocol (IPP). IPP is an application
+level protocol that can be used for distributed printing using Internet
+tools and technologies. This document defines the rules for encoding IPP
+operations and IPP attributes into a new Internet mime media type called
+"application/ipp". This document also defines the rules for
+transporting over HTTP a message body whose Content-Type is
+"application/ipp". This document defines a new scheme named 'ipp' for
+identifying IPP printers and jobs.
+
+
+
+
+
+Herriot, et al. Expires December 23, 1999 [Page 1]
+
+
+
+INTERNET-DRAFT IPP/1.1: Encoding and Transport June 23, 1999
+
+
+
+The full set of IPP documents includes:
+
+ Design Goals for an Internet Printing Protocol [RFC2567]
+ Rationale for the Structure and Model and Protocol for the Internet
+ Printing Protocol [RFC2568]
+ Internet Printing Protocol/1.1: Model and Semantics [ipp-mod]
+ Internet Printing Protocol/1.1: Encoding and Transport (this
+ document)
+ Internet Printing Protocol/1.1: Implementer's Guide [ipp-iig]
+ Mapping between LPD and IPP Protocols [RFC2569]
+
+The document, "Design Goals for an Internet Printing Protocol", takes a
+broad look at distributed printing functionality, and it enumerates
+real-life scenarios that help to clarify the features that need to be
+included in a printing protocol for the Internet. It identifies
+requirements for three types of users: end users, operators, and
+administrators. It calls out a subset of end user requirements that are
+satisfied in IPP/1.1. A few OPTIONAL operator operations have been added
+to IPP/1.1.
+
+The document, "Rationale for the Structure and Model and Protocol for
+the Internet Printing Protocol", describes IPP from a high level view,
+defines a roadmap for the various documents that form the suite of IPP
+specification documents, and gives background and rationale for the IETF
+working group's major decisions.
+
+The document, "Internet Printing Protocol/1.1: Model and Semantics",
+describes a simplified model with abstract objects, their attributes,
+and their operations that are independent of encoding and transport. It
+introduces a Printer and a Job object. The Job object optionally
+supports multiple documents per Job. It also addresses security,
+internationalization, and directory issues.
+
+The document "Internet Printing Protocol/1.1: Implementer's Guide",
+gives advice to implementers of IPP clients and IPP objects.
+
+The document "Mapping between LPD and IPP Protocols" gives some advice
+to implementers of gateways between IPP and LPD (Line Printer Daemon)
+implementations.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Herriot, et al. Expires December 23, 1999 [Page 2]
+
+
+
+INTERNET-DRAFT IPP/1.1: Encoding and Transport June 23, 1999
+
+
+ Table of Contents
+
+1. Introduction........................................................4
+2. Conformance Terminology.............................................4
+3. Encoding of the Operation Layer....................................4
+ 3.1 Picture of the Encoding........................................5
+ 3.2 Syntax of Encoding.............................................7
+ 3.3 Version-number.................................................8
+ 3.4 Operation-id...................................................8
+ 3.5 Status-code....................................................9
+ 3.6 Request-id.....................................................9
+ 3.7 Tags...........................................................9
+ 3.7.1 Delimiter Tags...........................................9
+ 3.7.2 Value Tags..............................................11
+ 3.8 Name-Length...................................................12
+ 3.9 (Attribute) Name..............................................12
+ 3.10Value Length..................................................14
+ 3.11(Attribute) Value.............................................15
+ 3.12Data..........................................................17
+4. Encoding of Transport Layer........................................17
+5. IPP URL Scheme.....................................................18
+6. Security Considerations............................................19
+ 6.1 Security Conformance Requirements.............................20
+ 6.1.1 Digest Authentication...................................20
+ 6.1.2 Transport Layer Security (TLS)..........................20
+ 6.2 Using IPP with TLS............................................21
+7. Interoperability with IPP/1.0 Implementations......................21
+ 7.1 The "version-number" Parameter................................21
+ 7.2 Security and URL Schemes......................................22
+8. References.........................................................23
+9. Author's Address...................................................24
+10.Other Participants:...............................................25
+11.Appendix A: Protocol Examples.....................................26
+ 11.1Print-Job Request.............................................26
+ 11.2Print-Job Response (successful)...............................27
+ 11.3Print-Job Response (failure)..................................28
+ 11.4Print-Job Response (success with attributes ignored)..........29
+ 11.5Print-URI Request.............................................32
+ 11.6Create-Job Request............................................33
+ 11.7Get-Jobs Request..............................................34
+ 11.8Get-Jobs Response.............................................35
+12.Appendix C: Registration of MIME Media Type Information for
+"application/ipp".....................................................37
+13.Appendix D: Changes from IPP /1.0.................................38
+14.Full Copyright Statement..........................................39
+
+
+
+
+
+
+
+
+
+
+
+Herriot, et al. Expires December 23, 1999 [Page 3]
+
+
+
+INTERNET-DRAFT IPP/1.1: Encoding and Transport June 23, 1999
+
+
+
+
+1. Introduction
+
+This document contains the rules for encoding IPP operations and
+describes two layers: the transport layer and the operation layer.
+
+The transport layer consists of an HTTP/1.1 request or response. RFC
+2616 [RFC2616] describes HTTP/1.1. This document specifies the HTTP
+headers that an IPP implementation supports.
+
+The operation layer consists of a message body in an HTTP request or
+response. The document "Internet Printing Protocol/1.1: Model and
+Semantics" [ipp-mod] defines the semantics of such a message body and
+the supported values. This document specifies the encoding of an IPP
+operation. The aforementioned document [ipp-mod] is henceforth referred
+to as the "IPP model document"
+
+
+2. Conformance Terminology
+
+The key words "MUST", "MUST NOT", "REQUIRED", "SHOULD", "SHOULD NOT",
+"RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be
+interpreted as described in RFC 2119 [RFC2119].
+
+
+3. Encoding of the Operation Layer
+
+The operation layer MUST contain a single operation request or operation
+response. Each request or response consists of a sequence of values and
+attribute groups. Attribute groups consist of a sequence of attributes
+each of which is a name and value. Names and values are ultimately
+sequences of octets
+
+The encoding consists of octets as the most primitive type. There are
+several types built from octets, but three important types are
+integers, character strings and octet strings, on which most other
+data types are built. Every character string in this encoding MUST be a
+sequence of characters where the characters are associated with some
+charset and some natural language. A character string MUST be in
+"reading order" with the first character in the value (according to
+reading order) being the first character in the encoding. A character
+string whose associated charset is US-ASCII whose associated natural
+language is US English is henceforth called a US-ASCII-STRING. A
+character string whose associated charset and natural language are
+specified in a request or response as described in the model document is
+henceforth called a LOCALIZED-STRING. An octet string MUST be in "IPP
+model document order" with the first octet in the value (according to
+the IPP model document order) being the first octet in the encoding
+Every integer in this encoding MUST be encoded as a signed integer using
+two's-complement binary encoding with big-endian format (also known as
+"network order" and "most significant byte first"). The number of octets
+for an integer MUST be 1, 2 or 4, depending on usage in the protocol.
+Such one-octet integers, henceforth called SIGNED-BYTE, are used for the
+
+
+Herriot, et al. Expires December 23, 1999 [Page 4]
+
+
+
+INTERNET-DRAFT IPP/1.1: Encoding and Transport June 23, 1999
+
+
+version-number and tag fields. Such two-byte integers, henceforth called
+SIGNED-SHORT are used for the operation-id, status-code and length
+fields. Four byte integers, henceforth called SIGNED-INTEGER, are used
+for values fields and the sequence number.
+
+The following two sections present the operation layer in two ways
+
+
+ - informally through pictures and description
+
+ - formally through Augmented Backus-Naur Form (ABNF), as specified by
+ RFC 2234 [RFC2234]
+
+
+
+3.1 Picture of the Encoding
+
+The encoding for an operation request or response consists of:
+
+ -----------------------------------------------
+ | version-number | 2 bytes - required
+ -----------------------------------------------
+ | operation-id (request) |
+ | or | 2 bytes - required
+ | status-code (response) |
+ -----------------------------------------------
+ | request-id | 4 bytes - required
+ -----------------------------------------------------------
+ | xxx-attributes-tag | 1 byte |
+ ----------------------------------------------- |-0 or more
+ | xxx-attribute-sequence | n bytes |
+ -----------------------------------------------------------
+ | end-of-attributes-tag | 1 byte - required
+ -----------------------------------------------
+ | data | q bytes - optional
+ -----------------------------------------------
+
+The xxx-attributes-tag and xxx-attribute-sequence represents four
+different values of "xxx", namely, operation, job, printer and
+unsupported. The xxx-attributes-tag and an xxx-attribute-sequence
+represent attribute groups in the model document. The xxx-attributes-tag
+identifies the attribute group and the xxx-attribute-sequence contains
+the attributes.
+
+The expected sequence of xxx-attributes-tag and xxx-attribute-sequence
+is specified in the IPP model document for each operation request and
+operation response.
+
+A request or response SHOULD contain each xxx-attributes-tag defined for
+that request or response even if there are no attributes except for the
+unsupported-attributes-tag which SHOULD be present only if the
+unsupported-attribute-sequence is non-empty. A receiver of a request
+MUST be able to process as equivalent empty attribute groups:
+
+
+
+Herriot, et al. Expires December 23, 1999 [Page 5]
+
+
+
+INTERNET-DRAFT IPP/1.1: Encoding and Transport June 23, 1999
+
+
+ a) an xxx-attributes-tag with an empty xxx-attribute-sequence,
+
+ b) an expected but missing xxx-attributes-tag.
+
+The data is omitted from some operations, but the end-of-attributes-tag
+is present even when the data is omitted. Note, the xxx-attributes-tags
+and end-of-attributes-tag are called 'delimiter-tags'. Note: the xxx-
+attribute-sequence, shown above may consist of 0 bytes, according to the
+rule below.
+
+An xxx-attributes-sequence consists of zero or more compound-attributes.
+
+ -----------------------------------------------
+ | compound-attribute | s bytes - 0 or more
+ -----------------------------------------------
+
+A compound-attribute consists of an attribute with a single value
+followed by zero or more additional values.
+
+Note: a 'compound-attribute' represents a single attribute in the model
+document. The 'additional value' syntax is for attributes with 2 or
+more values.
+
+Each attribute consists of:
+
+ -----------------------------------------------
+ | value-tag | 1 byte
+ -----------------------------------------------
+ | name-length (value is u) | 2 bytes
+ -----------------------------------------------
+ | name | u bytes
+ -----------------------------------------------
+ | value-length (value is v) | 2 bytes
+ -----------------------------------------------
+ | value | v bytes
+ -----------------------------------------------
+
+An additional value consists of:
+
+ -----------------------------------------------------------
+ | value-tag | 1 byte |
+ ----------------------------------------------- |
+ | name-length (value is 0x0000) | 2 bytes |
+ ----------------------------------------------- |-0 or more
+ | value-length (value is w) | 2 bytes |
+ ----------------------------------------------- |
+ | value | w bytes |
+ -----------------------------------------------------------
+
+Note: an additional value is like an attribute whose name-length is 0.
+
+>From the standpoint of a parsing loop, the encoding consists of:
+
+
+
+
+
+Herriot, et al. Expires December 23, 1999 [Page 6]
+
+
+
+INTERNET-DRAFT IPP/1.1: Encoding and Transport June 23, 1999
+
+
+ -----------------------------------------------
+ | version-number | 2 bytes - required
+ -----------------------------------------------
+ | operation-id (request) |
+ | or | 2 bytes - required
+ | status-code (response) |
+ -----------------------------------------------
+ | request-id | 4 bytes - required
+ -----------------------------------------------------------
+ | tag (delimiter-tag or value-tag) | 1 byte |
+ ----------------------------------------------- |-0 or more
+ | empty or rest of attribute | x bytes |
+ -----------------------------------------------------------
+ | end-of-attributes-tag | 2 bytes - required
+ -----------------------------------------------
+ | data | y bytes - optional
+ -----------------------------------------------
+
+
+The value of the tag determines whether the bytes following the tag are:
+
+
+ - attributes
+
+ - data
+
+ - the remainder of a single attribute where the tag specifies the
+ type of the value.
+
+3.2 Syntax of Encoding
+
+The syntax below is ABNF [RFC2234] except 'strings of literals' MUST be
+case sensitive. For example 'a' means lower case 'a' and not upper case
+'A'. In addition, SIGNED-BYTE and SIGNED-SHORT fields are represented
+as '%x' values which show their range of values.
+
+
+ ipp-message = ipp-request / ipp-response
+ ipp-request = version-number operation-id request-id
+ *(xxx-attributes-tag xxx-attribute-sequence) end-of-
+ attributes-tag data
+ ipp-response = version-number status-code request-id
+ *(xxx-attributes-tag xxx-attribute-sequence) end-of-
+ attributes-tag data
+ xxx-attribute-sequence = *compound-attribute
+
+ xxx-attributes-tag = operation-attributes-tag / job-attributes-tag /
+ printer-attributes-tag / unsupported-attributes-tag
+
+ version-number = major-version-number minor-version-number
+ major-version-number = SIGNED-BYTE ; initially %d1
+ minor-version-number = SIGNED-BYTE ; initially %d0
+
+ operation-id = SIGNED-SHORT ; mapping from model defined below
+ status-code = SIGNED-SHORT ; mapping from model defined below
+
+
+Herriot, et al. Expires December 23, 1999 [Page 7]
+
+
+
+INTERNET-DRAFT IPP/1.1: Encoding and Transport June 23, 1999
+
+
+
+ request-id = SIGNED-INTEGER ; whose value is > 0
+
+ compound-attribute = attribute *additional-values
+
+ attribute = value-tag name-length name value-length value
+ additional-values = value-tag zero-name-length value-length value
+
+ name-length = SIGNED-SHORT ; number of octets of 'name'
+ name = LALPHA *( LALPHA / DIGIT / "-" / "_" / "." )
+ value-length = SIGNED-SHORT ; number of octets of 'value'
+ value = OCTET-STRING
+
+ data = OCTET-STRING
+
+ zero-name-length = %x00.00 ; name-length of 0
+ operation-attributes-tag = %x01 ; tag of 1
+ job-attributes-tag = %x02 ; tag of 2
+ printer-attributes-tag = %x04 ; tag of 4
+ unsupported- attributes-tag = %x05 ; tag of 5
+ end-of-attributes-tag = %x03 ; tag of 3
+ value-tag = %x10-FF
+
+ SIGNED-BYTE = BYTE
+ SIGNED-SHORT = 2BYTE
+ SIGNED-INTEGER = 4BYTE
+ DIGIT = %x30-39 ; "0" to "9"
+ LALPHA = %x61-7A ; "a" to "z"
+ BYTE = %x00-FF
+ OCTET-STRING = *BYTE
+
+The syntax allows an xxx-attributes-tag to be present when the xxx-
+attribute-sequence that follows is empty. The syntax is defined this way
+to allow for the response of Get-Jobs where no attributes are returned
+for some job-objects. Although it is RECOMMENDED that the sender not
+send an xxx-attributes-tag if there are no attributes (except in the
+Get-Jobs response just mentioned), the receiver MUST be able to decode
+such syntax.
+
+
+3.3 Version-number
+
+The version-number MUST consist of a major and minor version-number,
+each of which MUST be represented by a SIGNED-BYTE. The protocol
+described in this document MUST have a major version-number of 1 (0x01)
+and a minor version-number of 1 (0x01). The ABNF for these two bytes
+MUST be %x01.01.
+
+
+3.4 Operation-id
+
+Operation-ids are defined as enums in the model document. An operation-
+ids enum value MUST be encoded as a SIGNED-SHORT.
+
+Note: the values 0x4000 to 0xFFFF are reserved for private extensions.
+
+
+Herriot, et al. Expires December 23, 1999 [Page 8]
+
+
+
+INTERNET-DRAFT IPP/1.1: Encoding and Transport June 23, 1999
+
+
+3.5 Status-code
+
+Status-codes are defined as enums in the model document. A status-code
+enum value MUST be encoded as a SIGNED-SHORT.
+
+The status-code is an operation attribute in the model document. In the
+protocol, the status-code is in a special position, outside of the
+operation attributes.
+
+If an IPP status-code is returned, then the HTTP Status-Code MUST be 200
+(successful-ok). With any other HTTP Status-Code value, the HTTP
+response MUST NOT contain an IPP message-body, and thus no IPP status-
+code is returned.
+
+
+3.6 Request-id
+
+The request-id allows a client to match a response with a request. This
+mechanism is unnecessary in HTTP, but may be useful when application/ipp
+entity bodies are used in another context.
+
+The request-id in a response MUST be the value of the request-id
+received in the corresponding request. A client can set the request-id
+in each request to a unique value or a constant value, such as 1,
+depending on what the client does with the request-id returned in the
+response. The value of the request-id MUST be greater than zero.
+
+
+3.7 Tags
+
+There are two kinds of tags:
+
+
+ - delimiter tags: delimit major sections of the protocol, namely
+ attributes and data
+
+ - value tags: specify the type of each attribute value
+
+3.7.1 Delimiter Tags
+
+
+The following table specifies the values for the delimiter tags:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Herriot, et al. Expires December 23, 1999 [Page 9]
+
+
+
+INTERNET-DRAFT IPP/1.1: Encoding and Transport June 23, 1999
+
+
+
+ Tag Value (Hex) Delimiter
+
+ 0x00 reserved
+ 0x01 operation-attributes-tag
+ 0x02 job-attributes-tag
+ 0x03 end-of-attributes-tag
+ 0x04 printer-attributes-tag
+ 0x05 unsupported-attributes-tag
+ 0x06-0x0e reserved for future delimiters
+ 0x0F reserved for future chunking-end-of-attributes-
+ tag
+
+When an xxx-attributes-tag occurs in the protocol, it MUST mean that
+zero or more following attributes up to the next delimiter tag are
+attributes belonging to group xxx as defined in the model document,
+where xxx is operation, job, printer, unsupported.
+
+Doing substitution for xxx in the above paragraph, this means the
+following. When an operation-attributes-tag occurs in the protocol, it
+MUST mean that the zero or more following attributes up to the next
+delimiter tag are operation attributes as defined in the model document.
+When an job-attributes-tag occurs in the protocol, it MUST mean that the
+zero or more following attributes up to the next delimiter tag are job
+attributes or job template attributes as defined in the model document.
+When a printer-attributes-tag occurs in the protocol, it MUST mean that
+the zero or more following attributes up to the next delimiter tag are
+printer attributes as defined in the model document. When an
+unsupported-attributes-tag occurs in the protocol, it MUST mean that the
+zero or more following attributes up to the next delimiter tag are
+unsupported attributes as defined in the model document.
+
+The operation-attributes-tag and end-of-attributes-tag MUST each occur
+exactly once in an operation. The operation-attributes-tag MUST be the
+first tag delimiter, and the end-of-attributes-tag MUST be the last tag
+delimiter. If the operation has a document-content group, the document
+data in that group MUST follow the end-of-attributes-tag.
+
+Each of the other three xxx-attributes-tags defined above is OPTIONAL
+in an operation and each MUST occur at most once in an operation, except
+for job-attributes-tag in a Get-Jobs response which may occur zero or
+more times.
+
+The order and presence of delimiter tags for each operation request and
+each operation response MUST be that defined in the model document. For
+further details, see section 3.9 "(Attribute) Name" and 11 "Appendix A:
+Protocol Examples".
+
+A Printer MUST treat the reserved delimiter tags differently from
+reserved value tags so that the Printer knows that there is an entire
+attribute group that it doesn't understand as opposed to a single value
+that it doesn't understand.
+
+
+
+
+
+Herriot, et al. Expires December 23, 1999 [Page 10]
+
+
+
+INTERNET-DRAFT IPP/1.1: Encoding and Transport June 23, 1999
+
+
+3.7.2 Value Tags
+
+The remaining tables show values for the value-tag, which is the first
+octet of an attribute. The value-tag specifies the type of the value of
+the attribute. The following table specifies the "out-of-band" values
+for the value-tag.
+
+
+ Tag Value (Hex) Meaning
+
+ 0x10 unsupported
+ 0x11 reserved for future 'default'
+ 0x12 unknown
+ 0x13 no-value
+ 0x14-0x1F reserved for future "out-of-band" values.
+
+The "unsupported" value MUST be used in the attribute-sequence of an
+error response for those attributes which the printer does not support.
+The "default" value is reserved for future use of setting value back to
+their default value. The "unknown" value is used for the value of a
+supported attribute when its value is temporarily unknown. The "no-
+value" value is used for a supported attribute to which no value has
+been assigned, e.g. "job-k-octets-supported" has no value if an
+implementation supports this attribute, but an administrator has not
+configured the printer to have a limit.
+
+The following table specifies the integer values for the value-tag:
+
+
+ Tag Value (Hex) Meaning
+
+ 0x20 reserved
+ 0x21 integer
+ 0x22 boolean
+ 0x23 enum
+ 0x24-0x2F reserved for future integer types
+
+NOTE: 0x20 is reserved for "generic integer" if it should ever be
+needed.
+
+The following table specifies the octetString values for the value-tag:
+
+
+ Tag Value (Hex) Meaning
+
+ 0x30 octetString with an unspecified format
+ 0x31 dateTime
+ 0x32 resolution
+ 0x33 rangeOfInteger
+ 0x34 reserved for collection (in the future)
+ 0x35 textWithLanguage
+ 0x36 nameWithLanguage
+ 0x37-0x3F reserved for future octetString types
+
+The following table specifies the character-string values for the value-
+tag:
+
+
+
+Herriot, et al. Expires December 23, 1999 [Page 11]
+
+
+
+INTERNET-DRAFT IPP/1.1: Encoding and Transport June 23, 1999
+
+
+
+ Tag Value (Hex) Meaning
+
+ 0x40 reserved
+ 0x41 textWithoutLanguage
+ 0x42 nameWithoutLanguage
+ 0x43 reserved
+ 0x44 keyword
+ 0x45 uri
+ 0x46 uriScheme
+ 0x47 charset
+ 0x48 naturalLanguage
+ 0x49 mimeMediaType
+ 0x4A-0x5F reserved for future character string types
+
+NOTE: 0x40 is reserved for "generic character-string" if it should ever
+be needed.
+
+NOTE: an attribute value always has a type, which is explicitly
+specified by its tag; one such tag value is "nameWithoutLanguage". An
+attribute's name has an implicit type, which is keyword.
+
+The values 0x60-0xFF are reserved for future types. There are no values
+allocated for private extensions. A new type MUST be registered via the
+type 2 registration process [ipp-mod].
+
+The tag 0x7F is reserved for extending types beyond the 255 values
+available with a single byte. A tag value of 0x7F MUST signify that the
+first 4 bytes of the value field are interpreted as the tag value.
+Note, this future extension doesn't affect parsers that are unaware of
+this special tag. The tag is like any other unknown tag, and the value
+length specifies the length of a value which contains a value that the
+parser treats atomically. All these 4 byte tag values are currently
+unallocated except that the values 0x40000000-0x7FFFFFFF are reserved
+for experimental use.
+
+
+3.8 Name-Length
+
+The name-length field MUST consist of a SIGNED-SHORT. This field MUST
+specify the number of octets in the name field which follows the name-
+length field, excluding the two bytes of the name-length field.
+
+If a name-length field has a value of zero, the following name field
+MUST be empty, and the following value MUST be treated as an additional
+value for the preceding attribute. Within an attribute-sequence, if two
+attributes have the same name, the first occurrence MUST be ignored. The
+zero-length name is the only mechanism for multi-valued attributes.
+
+
+3.9 (Attribute) Name
+
+Some operation elements are called parameters in the model document
+[ipp-mod]. They MUST be encoded in a special position and they MUST NOT
+appear as an operation attributes. These parameters are:
+
+
+Herriot, et al. Expires December 23, 1999 [Page 12]
+
+
+
+INTERNET-DRAFT IPP/1.1: Encoding and Transport June 23, 1999
+
+
+ - "version-number": The parameter named "version-number" in the IPP
+ model document MUST become the "version-number" field in the
+ operation layer request or response.
+
+ - "operation-id": The parameter named "operation-id" in the IPP model
+ document MUST become the "operation-id" field in the operation
+ layer request.
+
+ - "status-code": The parameter named "status-code" in the IPP model
+ document MUST become the "status-code" field in the operation layer
+ response.
+
+ - "request-id": The parameter named "request-id" in the IPP model
+ document MUST become the "request-id" field in the operation layer
+ request or response.
+
+
+All Printer and Job objects are identified by a Uniform Resource
+Identifier (URI) [RFC2396] so that they can be persistently and
+unambiguously referenced. The notion of a URI is a useful concept,
+however, until the notion of URI is more stable (i.e., defined more
+completely and deployed more widely), it is expected that the URIs used
+for IPP objects will actually be URLs [RFC1738] [RFC1808]. Since every
+URL is a specialized form of a URI, even though the more generic term
+URI is used throughout the rest of this document, its usage is intended
+to cover the more specific notion of URL as well.
+
+
+Some operation elements are encoded twice, once as the request-URI on
+the HTTP Request-Line and a second time as a REQUIRED operation
+attribute in the application/ipp entity. These attributes are the
+target URI for the operation and are called printer-uri and job-uri.
+Note: The target URI is included twice in an operation referencing the
+same IPP object, but the two URIs NEED NOT be literally identical. One
+can be a relative URI and the other can be an absolute URI. HTTP/1.1
+allows clients to generate and send a relative URI rather than an
+absolute URI. A relative URI identifies a resource with the scope of
+the HTTP server, but does not include scheme, host or port. The
+following statements characterize how URLs should be used in the mapping
+of IPP onto HTTP/1.1:
+
+ 1. Although potentially redundant, a client MUST supply the target of
+ the operation both as an operation attribute and as a URI at the
+ HTTP layer. The rationale for this decision is to maintain a
+ consistent set of rules for mapping application/ipp to possibly
+ many communication layers, even where URLs are not used as the
+ addressing mechanism in the transport layer.
+ 2. Even though these two URLs might not be literally identical (one
+ being relative and the other being absolute), they MUST both
+ reference the same IPP object.
+ 3. The URI in the HTTP layer is either relative or absolute and is
+ used by the HTTP server to route the HTTP request to the correct
+ resource relative to that HTTP server. The HTTP server need not be
+ aware of the URI within the operation request.
+
+
+Herriot, et al. Expires December 23, 1999 [Page 13]
+
+
+
+INTERNET-DRAFT IPP/1.1: Encoding and Transport June 23, 1999
+
+
+ 4. Once the HTTP server resource begins to process the HTTP request,
+ it might get the reference to the appropriate IPP Printer object
+ from either the HTTP URI (using to the context of the HTTP server
+ for relative URLs) or from the URI within the operation request;
+ the choice is up to the implementation.
+ 5. HTTP URIs can be relative or absolute, but the target URI in the
+ operation MUST be an absolute URI.
+
+The model document arranges the remaining attributes into groups for
+each operation request and response. Each such group MUST be represented
+in the protocol by an xxx-attribute-sequence preceded by the appropriate
+xxx-attributes-tag (See the table below and section 11 "Appendix A:
+Protocol Examples"). In addition, the order of these xxx-attributes-tags
+and xxx-attribute-sequences in the protocol MUST be the same as in the
+model document, but the order of attributes within each xxx-attribute-
+sequence MUST be unspecified. The table below maps the model document
+group name to xxx-attributes-sequence:
+
+
+ Model Document Group xxx-attributes-sequence
+
+ Operation Attributes operations-attributes-sequence
+ Job Template Attributes job-attributes-sequence
+ Job Object Attributes job-attributes-sequence
+ Unsupported Attributes unsupported- attributes-sequence
+ Requested Attributes (Get-Job- job-attributes-sequence
+ Attributes)
+ Requested Attributes (Get- printer-attributes-sequence
+ Printer-Attributes)
+ Document Content in a special position as described
+ above
+
+If an operation contains attributes from more than one job object (e.g.
+Get-Jobs response), the attributes from each job object MUST be in a
+separate job-attribute-sequence, such that the attributes from the ith
+job object are in the ith job-attribute-sequence. See Section 11
+"Appendix A: Protocol Examples" for table showing the application of the
+rules above.
+
+
+3.10 Value Length
+
+Each attribute value MUST be preceded by a SIGNED-SHORT, which MUST
+specify the number of octets in the value which follows this length,
+exclusive of the two bytes specifying the length.
+
+For any of the types represented by binary signed integers, the sender
+MUST encode the value in exactly four octets.
+
+For any of the types represented by character-strings, the sender MUST
+encode the value with all the characters of the string and without any
+padding characters.
+
+
+
+
+
+Herriot, et al. Expires December 23, 1999 [Page 14]
+
+
+
+INTERNET-DRAFT IPP/1.1: Encoding and Transport June 23, 1999
+
+
+If a value-tag contains an "out-of-band" value, such as "unsupported",
+the value-length MUST be 0 and the value empty . the value has no
+meaning when the value-tag has an "out-of-band" value.
+
+
+3.11 (Attribute) Value
+
+The syntax types and most of the details of their representation are
+defined in the IPP model document. The table below augments the
+information in the model document, and defines the syntax types from the
+model document in terms of the 5 basic types defined in section 3
+"Encoding of the Operation Layer". The 5 types are US-ASCII-STRING,
+LOCALIZED-STRING, SIGNED-INTEGER, SIGNED-SHORT, SIGNED-BYTE, and OCTET-
+STRING.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Herriot, et al. Expires December 23, 1999 [Page 15]
+
+
+
+INTERNET-DRAFT IPP/1.1: Encoding and Transport June 23, 1999
+
+
+
+Syntax of Attribute Encoding
+Value
+
+textWithoutLanguage, LOCALIZED-STRING.
+nameWithoutLanguage
+
+textWithLanguage OCTET_STRING consisting of 4 fields:
+ a) a SIGNED-SHORT which is the number of octets
+ in the following field
+ b) a value of type natural-language,
+ c) a SIGNED-SHORT which is the number of octets
+ in the following field,
+ d) a value of type textWithoutLanguage.
+
+ The length of a textWithLanguage value MUST be 4
+ + the value of field a + the value of field c.
+
+nameWithLanguage OCTET_STRING consisting of 4 fields:
+ a) a SIGNED-SHORT which is the number of octets
+ in the following field
+ b) a value of type natural-language,
+ c) a SIGNED-SHORT which is the number of octets
+ in the following field
+ d) a value of type nameWithoutLanguage.
+
+ The length of a nameWithLanguage value MUST be 4
+ + the value of field a + the value of field c.
+
+charset, US-ASCII-STRING.
+naturalLanguage,
+mimeMediaType,
+keyword, uri, and
+uriScheme
+
+boolean SIGNED-BYTE where 0x00 is 'false' and 0x01 is
+ 'true'.
+
+integer and enum a SIGNED-INTEGER.
+
+dateTime OCTET-STRING consisting of eleven octets whose
+ contents are defined by "DateAndTime" in RFC
+ 1903 [RFC1903].
+
+resolution OCTET_STRING consisting of nine octets of 2
+ SIGNED-INTEGERs followed by a SIGNED-BYTE. The
+ first SIGNED-INTEGER contains the value of cross
+ feed direction resolution. The second SIGNED-
+ INTEGER contains the value of feed direction
+ resolution. The SIGNED-BYTE contains the units
+ value.
+
+rangeOfInteger Eight octets consisting of 2 SIGNED-INTEGERs.
+ The first SIGNED-INTEGER contains the lower
+ bound and the second SIGNED-INTEGER contains the
+
+
+Herriot, et al. Expires December 23, 1999 [Page 16]
+
+
+
+INTERNET-DRAFT IPP/1.1: Encoding and Transport June 23, 1999
+
+
+
+Syntax of Attribute Encoding
+Value
+
+ upper bound.
+
+1setOf X Encoding according to the rules for an attribute
+ with more than 1 value. Each value X is encoded
+ according to the rules for encoding its type.
+
+octetString OCTET-STRING
+
+
+The type of the value in the model document determines the encoding in
+the value and the value of the value-tag.
+
+
+3.12 Data
+
+The data part MUST include any data required by the operation
+
+
+4. Encoding of Transport Layer
+
+HTTP/1.1 [RFC2616] is the transport layer for this protocol.
+
+The operation layer has been designed with the assumption that the
+transport layer contains the following information:
+
+
+ - the URI of the target job or printer operation
+
+ - the total length of the data in the operation layer, either as a
+ single length or as a sequence of chunks each with a length.
+
+
+It is REQUIRED that a printer implementation support HTTP over the IANA
+assigned Well Known Port 631 (the IPP default port), though a printer
+implementation may support HTTP over some other port as well.
+
+Each HTTP operation MUST use the POST method where the request-URI is
+the object target of the operation, and where the "Content-Type" of the
+message-body in each request and response MUST be "application/ipp". The
+message-body MUST contain the operation layer and MUST have the syntax
+described in section 3.2 "Syntax of Encoding". A client implementation
+MUST adhere to the rules for a client described for HTTP1.1 [RFC2616] .
+A printer (server) implementation MUST adhere the rules for an origin
+server described for HTTP1.1 [RFC2616].
+
+An IPP server sends a response for each request that it receives. If an
+IPP server detects an error, it MAY send a response before it has read
+the entire request. If the HTTP layer of the IPP server completes
+processing the HTTP headers successfully, it MAY send an intermediate
+response, such as "100 Continue", with no IPP data before sending the
+IPP response. A client MUST expect such a variety of responses from an
+
+
+Herriot, et al. Expires December 23, 1999 [Page 17]
+
+
+
+INTERNET-DRAFT IPP/1.1: Encoding and Transport June 23, 1999
+
+
+IPP server. For further information on HTTP/1.1, consult the HTTP
+documents [RFC2616].
+
+An HTTP server MUST support chunking for IPP requests, and an IPP client
+MUST support chunking for IPP responses according to HTTP/1.1[RFC2616].
+Note: this rule causes a conflict with non-compliant implementations of
+HTTP/1.1 that don't support chunking for POST methods, and this rule may
+cause a conflict with non-compliant implementations of HTTP/1.1 that
+don't support chunking for CGI scripts
+
+
+5. IPP URL Scheme
+
+The IPP/1.1 document defines a new scheme 'ipp' as the value of a URL
+that identifies either an IPP printer object or an IPP job object. The
+IPP attributes using the 'ipp' scheme are specified below. Because the
+HTTP layer does not support the 'ipp' scheme, a client MUST map 'ipp'
+URLs to 'http' URLs, and then follows the HTTP [RFC2616][RFC2617] rules
+for constructing a Request-Line and HTTP headers. The mapping is simple
+because the 'ipp' scheme implies all of the same protocol semantics as
+that of the 'http' scheme [RFC2616], except that it represents a print
+service and the implicit (default) port number that clients use to
+connect to a server is port 631.
+
+In the remainder of this section the term 'ipp-URL' means a URL whose
+scheme is 'ipp' and whose implicit (default) port is 631. The term
+'http-URL' means a URL whose scheme is 'http', and the term 'https-URL'
+means a URL whose scheme is 'https',
+
+A client and an IPP object (i.e. the server) MUST support the ipp-URL
+value in the following IPP attributes.
+ job attributes:
+ job-uri
+ job-printer-uri
+ printer attributes:
+ printer-uri-supported
+ operation attributes:
+ job-uri
+ printer-uri
+
+Each of the above attributes identifies a printer or job object. The
+ipp-URL is intended as the value of the attributes in this list, and for
+no other attributes. All of these attributes have a syntax type of
+'uri', but there are attributes with a syntax type of 'uri' that do not
+use the 'ipp' scheme, e.g. 'job-more-info'.
+
+If a printer registers its URL with a directory service, the printer
+MUST register an ipp-URL.
+
+User interfaces are beyond the scope of this document. But if software
+exposes the ipp-URL values of any of the above five attributes to a
+human user, it is REQUIRED that the human see the ipp-URL as is.
+
+
+
+
+Herriot, et al. Expires December 23, 1999 [Page 18]
+
+
+
+INTERNET-DRAFT IPP/1.1: Encoding and Transport June 23, 1999
+
+
+When a client sends a request, it MUST convert a target ipp-URL to a
+target http-URL for the HTTP layer according to the following rules:
+ 1. change the 'ipp' scheme to 'http'
+ 2. add an explicit port 631 if the URL does not contain an explicit
+ port. Note: port 631 is the IANA assigned Well Known Port for the
+ 'ipp' scheme.
+
+The client MUST use the target http-URL in both the HTTP Request-Line
+and HTTP headers, as specified by HTTP[RFC2616][RFC2617] . However, the
+client MUST use the target ipp-URL for the value of the "printer-uri" or
+"job-uri" operation attribute within the application/ipp body of the
+request. The server MUST use the ipp-URL for the value of the "printer-
+uri", "job-uri" or "printer-uri-supported" attributes within the
+application/ipp body of the response.
+
+For example, when an IPP client sends a request directly (i.e. no proxy)
+to an ipp-URL "ipp://myhost.com/myprinter/myqueue", it opens a TCP
+connection to port 631 (the ipp implicit port) on the host "myhost.com"
+and sends the following data:
+
+POST /myprinter/myqueue HTTP/1.1
+Host: myhost.com:631
+Content-type: application/ipp
+Transfer-Encoding: chunked
+...
+"printer-uri" "ipp://myhost.com/myprinter/myqueue"
+ (encoded in application/ipp message body)
+...
+
+As another example, when an IPP client sends the same request as above
+via a proxy "myproxy.com", it opens a TCP connection to the proxy port
+8080 on the proxy host "myproxy.com" and sends the following data:
+
+POST http://myhost.com:631/myprinter/myqueue HTTP/1.1
+Host: myhost.com:631
+Content-type: application/ipp
+Transfer-Encoding: chunked
+...
+"printer-uri" "ipp://myhost.com/myprinter/myqueue"
+ (encoded in application/ipp message body)
+...
+
+The proxy then connects to the IPP origin server with headers that are
+the same as the "no-proxy" example above.
+
+6. Security Considerations
+
+The IPP Model and Semantics document [ipp-mod] discusses high level
+security requirements (Client Authentication, Server Authentication and
+Operation Privacy). Client Authentication is the mechanism by which the
+client proves its identity to the server in a secure manner. Server
+Authentication is the mechanism by which the server proves its identity
+to the client in a secure manner. Operation Privacy is defined as a
+mechanism for protecting operations from eavesdropping.
+
+
+Herriot, et al. Expires December 23, 1999 [Page 19]
+
+
+
+INTERNET-DRAFT IPP/1.1: Encoding and Transport June 23, 1999
+
+
+6.1 Security Conformance Requirements
+
+This section defines the security requirements for IPP clients and IPP
+objects.
+
+
+6.1.1 Digest Authentication
+
+IPP clients MUST support:
+
+ Digest Authentication [RFC2617].
+
+ MD5 and MD5-sess MUST be implemented and supported.
+
+ The Message Integrity feature NEED NOT be used.
+
+
+IPP Printers SHOULD support:
+
+ Digest Authentication [RFC2617].
+
+ MD5 and MD5-sess MUST be implemented and supported.
+
+ The Message Integrity feature NEED NOT be used.
+
+
+
+The reasons that IPP Printers SHOULD (rather than MUST) support Digest
+Authentication are:
+
+1.While Client Authentication is important, there is a certain class of
+ printer devices where it does not make sense. Specifically, a low-
+ end device with limited ROM space and low paper throughput may not
+ need Client Authentication. This class of device typically requires
+ firmware designers to make trade-offs between protocols and
+ functionality to arrive at the lowest-cost solution possible.
+ Factored into the designer.s decisions is not just the size of the
+ code, but also the testing, maintenance, usefulness, and time-to-
+ market impact for each feature delivered to the customer. Forcing
+ such low-end devices to provide security in order to claim IPP/1.1
+ conformance would not make business sense and could potentially stall
+ the adoption of the standard.
+
+2.Print devices that have high-volume throughput and have available ROM
+ space have a compelling argument to provide support for Client
+ Authentication that safeguards the device from unauthorized access.
+ These devices are prone to a high loss of consumables and paper if
+ unauthorized access should occur.
+
+
+6.1.2 Transport Layer Security (TLS)
+
+IPP Printers SHOULD support Transport Layer Security (TLS) [RFC2246] for
+Server Authentication and Operation Privacy. IPP Printers MAY also
+support TLS for Client Authentication. If an IPP Printer supports TLS,
+it MUST support the TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA cipher suite as
+
+
+
+Herriot, et al. Expires December 23, 1999 [Page 20]
+
+
+
+INTERNET-DRAFT IPP/1.1: Encoding and Transport June 23, 1999
+
+
+mandated by RFC 2246 [RFC2246]. All other cipher suites are OPTIONAL.
+An IPP Printer MAY support Basic Authentication (described in HTTP/1.1
+[RFC2617]) for Client Authentication if the channel is secure. TLS with
+the above mandated cipher suite can provide such a secure channel.
+
+If a IPP client supports TLS, it MUST support the
+TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA cipher suite as mandated by RFC 2246
+[RFC2246]. All other cipher suites are OPTIONAL.
+
+The IPP Model and Semantics document defines two printer attributes
+("uri-authentication-supported" and "uri-security-supported") that the
+client can use to discover the security policy of a printer. That
+document also outlines IPP-specific security considerations and should
+be the primary reference for security implications with regard to the
+IPP protocol itself. For backward compatibility with IPP version 1.0,
+IPP clients and printers may also support SSL3. This is in addition to
+the security required in this document.
+
+
+6.2 Using IPP with TLS
+
+IPP/1.1 uses the "Upgrading to TLS Within HTTP/1.1" mechanism [http-
+tls]. An initial IPP request never uses TLS. The client requests a
+secure TLS connection by using the HTTP .Upgrade. header, while the
+server agrees in the HTTP response. The switch to TLS occurs either
+because the server grants the client.s request to upgrade to TLS, or a
+server asks to switch to TLS in its response. Secure communication
+begins with a server.s response to switch to TLS.
+
+
+7. Interoperability with IPP/1.0 Implementations
+
+It is beyond the scope of this specification to mandate conformance with
+previous versions. IPP/1.1 was deliberately designed, however, to make
+supporting previous versions easy. It is worth noting that, at the time
+of composing this specification (1999), we would expect IPP/1.1 Printer
+implementations to:
+
+ understand any valid request in the format of IPP/1.0, or 1.1;
+
+ respond appropriately with a response containing the same "version-
+ number" parameter value used by the client in the request.
+
+And we would expect IPP/1.1 clients to:
+
+ understand any valid response in the format of IPP/1.0, or 1.1.
+
+
+7.1 The "version-number" Parameter
+
+The following are rules regarding the "version-number" parameter (see
+section 3.3):
+
+
+
+
+Herriot, et al. Expires December 23, 1999 [Page 21]
+
+
+
+INTERNET-DRAFT IPP/1.1: Encoding and Transport June 23, 1999
+
+
+ 1. Clients MUST send requests containing a "version-number" parameter
+ with a '1.1' value and SHOULD try supplying alternate version
+ numbers if they receive a 'server-error-version-not-supported'
+ error return in a response.
+
+ 2. IPP objects MUST accept requests containing a "version-number"
+ parameter with a '1.1' value (or reject the request for reasons
+ other than 'server-error-version-not-supported').
+
+ 3. It is recommended that IPP objects accept any request with the
+ major version '1' (or reject the request for reasons other than
+ 'server-error-version-not-supported'). See [ipp-mod] "versions"
+ sub-section.
+
+ 4. In any case, security MUST NOT be compromised when a client
+ supplies a lower "version-number" parameter in a request. For
+ example, if an IPP/1.1 conforming Printer object accepts version
+ '1.0' requests and is configured to enforce Digest Authentication,
+ it MUST do the same for a version '1.0' request.
+
+
+7.2 Security and URL Schemes
+
+The following are rules regarding security, the "version-number"
+parameter, and the URL scheme supplied in target attributes and
+responses:
+
+ 1. When a client supplies a request, the "printer-uri" or "job-uri"
+ target operation attribute MUST have the same scheme as that
+ indicated in one of the values of the "printer-uri-supported"
+ Printer attribute.
+
+ 2. When the server returns the "job-printer-uri" or "job-uri" Job
+ Description attributes, it SHOULD return the same scheme ('ipp',
+ 'https', 'http', etc.) that the client supplied in the "printer-
+ uri" or "job-uri" target operation attributes in the Get-Job-
+ Attributes or Get-Jobs request, rather than the scheme used when
+ the job was created. However, when a client requests job
+ attributes using the Get-Job-Attributes or Get-Jobs operations, the
+ jobs and job attributes that the server returns depends on: (1) the
+ security in effect when the job was created, (2) the security in
+ effect in the query request, and (3) the security policy in force.
+
+ 3. It is recommended that if a server registers a non-secure ipp-URL
+ with a directory service (see [IPP-MOD] "Generic Directory Schema"
+ Appendix), then it also register an http-URL for interoperability
+ with IPP/1.0 clients (see section 7).
+
+ 4. In any case, security MUST NOT be compromised when a client
+ supplies an 'http' or other non-secure URL scheme in the target
+ "printer-uri" and "job-uri" operation attributes in a request.
+
+
+
+
+
+Herriot, et al. Expires December 23, 1999 [Page 22]
+
+
+
+INTERNET-DRAFT IPP/1.1: Encoding and Transport June 23, 1999
+
+
+8. References
+
+[dpa] ISO/IEC 10175 Document Printing Application (DPA), June 1996.
+
+[http-tls]R. Khare, S. Lawrence, "Upgrading to TLS Within HTTP/1.1",
+ <draft-ietf-tls-http-upgrade-02>, June 1999.
+
+[iana]IANA Registry of Coded Character Sets: ftp://ftp.isi.edu/in-
+ notes/iana/assignments/character-sets.
+
+[ipp-iig] Hastings, Tom, et al., "Internet Printing Protocol/1.1:
+ Implementer's Guide", work in progress.
+
+[ipp-mod] R. deBry, T. Hastings, R. Herriot, S. Isaacson, P. Powell,
+ "Internet Printing Protocol/1.0: Model and Semantics", <draft-
+ ietf-ipp-model-v11-03.txt>, June, 1999.
+
+[ipp-pro] Herriot, R., Butler, S., Moore, P., Turner, R., "Internet
+ Printing Protocol/1.1: Encoding and Transport", draft-ietf-ipp-
+ protocol-v11-02-.txt, June 1999.
+
+[RFC822] Crocker, D., "Standard for the Format of ARPA Internet Text
+ Messages", RFC 822, August 1982.
+
+[RFC1123] Braden, S., "Requirements for Internet Hosts - Application
+ and Support", RFC 1123, October, 1989.
+
+[RFC1179] McLaughlin, L. III, (editor), "Line Printer Daemon Protocol"
+ RFC 1179, August 1990.
+
+[RFC1543] Postel, J., "Instructions to RFC Authors", RFC 1543, October
+ 1993.
+
+[RFC1738] Berners-Lee, T., Masinter, L., McCahill, M. , "Uniform
+ Resource Locators (URL)", RFC 1738, December, 1994.
+
+[RFC1759] Smith, R., Wright, F., Hastings, T., Zilles, S., and
+ Gyllenskog, J., "Printer MIB", RFC 1759, March 1995.
+
+[RFC1766] H. Alvestrand, " Tags for the Identification of Languages",
+ RFC 1766, March 1995.
+
+[RFC1808] R. Fielding, "Relative Uniform Resource Locators", RFC1808,
+ June 1995.
+
+[RFC1903] J. Case, et al. "Textual Conventions for Version 2 of the
+ Simple Network Management Protocol (SNMPv2)", RFC 1903, January
+ 1996.
+
+[RFC2046] N. Freed & N. Borenstein, Multipurpose Internet Mail
+ Extensions (MIME) Part Two: Media Types. November 1996, RFC 2046.
+
+
+
+
+
+Herriot, et al. Expires December 23, 1999 [Page 23]
+
+
+
+INTERNET-DRAFT IPP/1.1: Encoding and Transport June 23, 1999
+
+
+[RFC2048] N. Freed, J. Klensin & J. Postel. Multipurpose Internet Mail
+ Extension (MIME) Part Four: Registration Procedures. November
+ 1996 (Also BCP0013), RFC 2048.
+
+[RFC2119] S. Bradner, "Key words for use in RFCs to Indicate
+ Requirement Levels", RFC 2119 , March 1997.
+
+[RFC2184] N. Freed, K. Moore, "MIME Parameter Value and Encoded Word
+ Extensions: Character Sets, Languages, and Continuations", RFC
+ 2184, August 1997.
+
+[RFC2234] D. Crocker et al., "Augmented BNF for Syntax Specifications:
+ ABNF", RFC 2234. November 1997.
+
+[RFC2246] T. Dierks et al., "The TLS Protocol", RFC 2246. January 1999.
+
+[RFC2396] Berners-Lee, T., Fielding, R., Masinter, L., "Uniform
+ Resource Identifiers (URI): Generic Syntax", RFC 2396, August
+ 1998.
+
+[RFC2565] Herriot, R., Butler, S., Moore, P., Turner, R., "Internet
+ Printing Protocol/1.0: Encoding and Transport", rfc 2565, April
+ 1999.
+
+[RFC2566] R. deBry, T. Hastings, R. Herriot, S. Isaacson, P. Powell,
+ "Internet Printing Protocol/1.0: Model and Semantics", rfc 2566,
+ April, 1999.
+
+[RFC2567] Wright, D., "Design Goals for an Internet Printing Protocol",
+ RFC2567, April 1999.
+
+[RFC2568] Zilles, S., "Rationale for the Structure and Model and
+ Protocol for the Internet Printing Protocol", RC 2568,April 1999.
+
+[RFC2569] Herriot, R., Hastings, T., Jacobs, N., Martin, J., "Mapping
+ between LPD and IPP Protocols RFC 2569, April 1999.
+
+[RFC2068] R. Fielding, J. Gettys, J. Mogul, H. Frystyk, L. Masinter, P.
+ Leach, T. Berners-Lee, "Hypertext Transfer Protocol - HTTP/1.1",
+ RFC 2616, June 1999.
+
+[RFC2069] J. Franks, P. Hallam-Baker, J. Hostetler, S. Lawrence, P.
+ Leach, A. Luotonen, L. Stewart, "HTTP Authentication: Basic and
+ Digest Access Authentication", RFC 2617, June 1999.
+
+
+9. Author's Address
+
+
+
+
+
+
+
+
+
+Herriot, et al. Expires December 23, 1999 [Page 24]
+
+
+
+INTERNET-DRAFT IPP/1.1: Encoding and Transport June 23, 1999
+
+
+
+Robert Herriot (editor) Paul Moore
+Xerox Corporation Microsoft
+3400 Hillview Ave., Bldg #1 One Microsoft Way
+Palo Alto, CA 94304 Redmond, WA 98053
+
+Phone: 650-813-7696 Phone: 425-936-0908
+Fax: 650-813-6860 Fax: 425-93MS-FAX
+Email: Email: paulmo@microsoft.com
+robert.herriot@pahv.xerox.com
+
+Sylvan Butler Randy Turner
+Hewlett-Packard 2Wire, Inc.
+11311 Chinden Blvd. 694 Tasman Dr.
+Boise, ID 83714 Milpitas, CA 95035
+
+Phone: 208-396-6000 Phone: 408-546-1273
+Fax: 208-396-3457
+Email: sbutler@boi.hp.com
+
+ John Wenn
+ Xerox Corporation
+ 737 Hawaii St
+ El Segundo, CA 90245
+
+IPP Mailing List: ipp@pwg.org Phone: 310-333-5764
+IPP Mailing List Subscription: Fax: 310-333-5514
+ipp-request@pwg.org
+IPP Web Page: Email: jwenn@cp10.es.xerox.com
+http://www.pwg.org/ipp/
+
+
+10. Other Participants:
+
+
+Chuck Adams - Tektronix Shivaun Albright - HP
+Jeff Barnett - IBM Ron Bergman - Dataproducts
+Keith Carter - IBM Angelo Caruso - Xerox
+Rajesh Chawla - TR Computing Josh Cohen - Microsoft
+Solutions
+Jeff Copeland - QMS Andy Davidson - Tektronix
+Roger deBry - IBM Mabry Dozier - QMS
+Lee Farrell - Canon Steve Gebert - IBM
+Sue Gleeson - Digital Charles Gordon - Osicom
+Brian Grimshaw - Apple Jerry Hadsell - IBM
+Richard Hart - Digital Tom Hastings - Xerox
+Stephen Holmstead Zhi-Hong Huang - Zenographics
+Scott Isaacson - Novell Babek Jahromi - Microsoft
+Swen Johnson - Xerox David Kellerman - Northlake
+ Software
+Robert Kline - TrueSpectra Carl Kugler - IBM
+Dave Kuntz - Hewlett-Packard Takami Kurono - Brother
+Rick Landau - Digital Scott Lawrence - Agranot Systems
+Greg LeClair - Epson Harry Lewis - IBM
+Tony Liao - Vivid Image Roy Lomicka - Digital
+Pete Loya - HP Ray Lutz - Cognisys
+
+
+Herriot, et al. Expires December 23, 1999 [Page 25]
+
+
+
+INTERNET-DRAFT IPP/1.1: Encoding and Transport June 23, 1999
+
+
+
+Mike MacKay - Novell, Inc. David Manchala - Xerox
+Carl-Uno Manros - Xerox Jay Martin - Underscore
+Larry Masinter - Xerox Stan McConnell - Xerox
+Ira McDonald - High North Inc. Peter Michalek - Shinesoft
+Tetsuya Morita - Ricoh Yuichi Niwa - Ricoh
+Pat Nogay - IBM Ron Norton - Printronics
+Bob Pentecost - Hewlett-Packard Patrick Powell - Astart
+ Technologies
+Jeff Rackowitz - Intermec Rob Rhoads - Intel
+Xavier Riley - Xerox Gary Roberts - Ricoh
+David Roach - Unisys Stuart Rowley - Kyocera
+Richard Schneider - Epson Kris Schoff - HP
+Bob Setterbo - Adobe Devon Taylor - Novell, Inc.
+Mike Timperman - Lexmark Shigern Ueda - Canon
+Bob Von Andel - Allegro Software William Wagner - Osicom
+Jim Walker - DAZEL Chris Wellens - Interworking Labs
+Rob Whittle - Novell, Inc. Jasper Wong - Xionics
+Don Wright - Lexmark Rick Yardumian - Xerox
+Lloyd Young - Lexmark Atsushi Yuki - Kyocera
+Peter Zehler - Xerox Frank Zhao - Panasonic
+Steve Zilles - Adobe
+
+
+11. Appendix A: Protocol Examples
+
+
+11.1 Print-Job Request
+
+The following is an example of a Print-Job request with job-name,
+copies, and sides specified. The "ipp-attribute-fidelity" attribute is
+set to 'true' so that the print request will fail if the "copies" or the
+"sides" attribute are not supported or their values are not supported.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Herriot, et al. Expires December 23, 1999 [Page 26]
+
+
+
+INTERNET-DRAFT IPP/1.1: Encoding and Transport June 23, 1999
+
+
+
+Octets Symbolic Value Protocol field
+
+0x0101 1.1 version-number
+0x0002 Print-Job operation-id
+0x00000001 1 request-id
+0x01 start operation-attributes operation-attributes-tag
+0x47 charset type value-tag
+0x0012 name-length
+attributes- attributes-charset name
+charset
+0x0008 value-length
+us-ascii US-ASCII value
+0x48 natural-language type value-tag
+0x001B name-length
+attributes- attributes-natural-language name
+natural-
+language
+0x0005 value-length
+en-us en-US value
+0x45 uri type value-tag
+0x000B name-length
+printer-uri printer-uri name
+0x0015 value-length
+ipp://forest/p printer pinetree value
+inetree
+0x42 nameWithoutLanguage type value-tag
+0x0008 name-length
+job-name job-name name
+0x0006 value-length
+foobar foobar value
+0x22 boolean type value-tag
+0x0016 name-length
+ipp-attribute- ipp-attribute-fidelity name
+fidelity
+0x0001 value-length
+0x01 true value
+0x02 start job-attributes job-attributes-tag
+0x21 integer type value-tag
+0x0006 name-length
+copies copies name
+0x0004 value-length
+0x00000014 20 value
+0x44 keyword type value-tag
+0x0005 name-length
+sides sides name
+0x0013 value-length
+two-sided- two-sided-long-edge value
+long-edge
+0x03 end-of-attributes end-of-attributes-tag
+%!PS... <PostScript> data
+
+11.2 Print-Job Response (successful)
+
+
+
+
+Herriot, et al. Expires December 23, 1999 [Page 27]
+
+
+
+INTERNET-DRAFT IPP/1.1: Encoding and Transport June 23, 1999
+
+
+Here is an example of a successful Print-Job response to the previous
+Print-Job request. The printer supported the "copies" and "sides"
+attributes and their supplied values. The status code returned is
+'successful-ok'.
+
+
+Octets Symbolic Value Protocol field
+
+0x0101 1.1 version-number
+0x0000 successful-ok status-code
+0x00000001 1 request-id
+0x01 start operation-attributes operation-attributes-tag
+0x47 charset type value-tag
+0x0012 name-length
+attributes- attributes-charset name
+charset
+0x0008 value-length
+us-ascii US-ASCII value
+0x48 natural-language type value-tag
+0x001B name-length
+attributes- attributes-natural- name
+natural-language language
+0x0005 value-length
+en-us en-US value
+0x41 textWithoutLanguage type value-tag
+0x000E name-length
+status-message status-message name
+0x000D value-length
+successful-ok successful-ok value
+0x02 start job-attributes job-attributes-tag
+0x21 integer value-tag
+0x0006 name-length
+job-id job-id name
+0x0004 value-length
+147 147 value
+0x45 uri type value-tag
+0x0007 name-length
+job-uri job-uri name
+0x0019 value-length
+ipp://forest/pin job 123 on pinetree value
+etree/123
+0x23 enum type value-tag
+0x0009 name-length
+job-state job-state name
+0x0004 value-length
+0x0003 pending value
+0x03 end-of-attributes end-of-attributes-tag
+
+11.3 Print-Job Response (failure)
+
+Here is an example of an unsuccessful Print-Job response to the previous
+Print-Job request. It fails because, in this case, the printer does not
+support the "sides" attribute and because the value '20' for the
+"copies" attribute is not supported. Therefore, no job is created, and
+neither a "job-id" nor a "job-uri" operation attribute is returned. The
+
+
+Herriot, et al. Expires December 23, 1999 [Page 28]
+
+
+
+INTERNET-DRAFT IPP/1.1: Encoding and Transport June 23, 1999
+
+
+error code returned is 'client-error-attributes-or-values-not-supported'
+(0x040B).
+
+
+Octets Symbolic Value Protocol field
+
+0x0101 1.1 version-number
+0x040B client-error-attributes-or- status-code
+ values-not-supported
+0x00000001 1 request-id
+0x01 start operation-attributes operation-attribute tag
+0x47 charset type value-tag
+0x0012 name-length
+attributes- attributes-charset name
+charset
+0x0008 value-length
+us-ascii US-ASCII value
+0x48 natural-language type value-tag
+0x001B name-length
+attributes- attributes-natural-language name
+natural-
+language
+0x0005 value-length
+en-us en-US value
+0x41 textWithoutLanguage type value-tag
+0x000E name-length
+status- status-message name
+message
+0x002F value-length
+client-error- client-error-attributes-or- value
+attributes- values-not-supported
+or-values-
+not-supported
+0x05 start unsupported-attributes unsupported-attributes tag
+0x21 integer type value-tag
+0x0006 name-length
+copies copies name
+0x0004 value-length
+0x00000014 20 value
+0x10 unsupported (type) value-tag
+0x0005 name-length
+sides sides name
+0x0000 value-length
+0x03 end-of-attributes end-of-attributes-tag
+
+11.4 Print-Job Response (success with attributes ignored)
+
+Here is an example of a successful Print-Job response to a Print-Job
+request like the previous Print-Job request, except that the value of
+'ipp-attribute-fidelity' is false. The print request succeeds, even
+though, in this case, the printer supports neither the "sides" attribute
+nor the value '20' for the "copies" attribute. Therefore, a job is
+created, and both a "job-id" and a "job-uri" operation attribute are
+returned. The unsupported attributes are also returned in an Unsupported
+
+
+
+Herriot, et al. Expires December 23, 1999 [Page 29]
+
+
+
+INTERNET-DRAFT IPP/1.1: Encoding and Transport June 23, 1999
+
+
+Attributes Group. The error code returned is 'successful-ok-ignored-or-
+substituted-attributes' (0x0001).
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Herriot, et al. Expires December 23, 1999 [Page 30]
+
+
+
+INTERNET-DRAFT IPP/1.1: Encoding and Transport June 23, 1999
+
+
+
+Octets Symbolic Value Protocol field
+
+0x0101 1.1 version-number
+0x0001 successful-ok-ignored-or- status-code
+ substituted-attributes
+0x00000001 1 request-id
+0x01 start operation-attributes operation-attributes-tag
+0x47 charset type value-tag
+0x0012 name-length
+attributes- attributes-charset name
+charset
+0x0008 value-length
+us-ascii US-ASCII value
+0x48 natural-language type value-tag
+0x001B name-length
+attributes- attributes-natural- name
+natural-language language
+0x0005 value-length
+en-us en-US value
+0x41 textWithoutLanguage type value-tag
+0x000E name-length
+status-message status-message name
+0x002F value-length
+successful-ok- successful-ok-ignored-or- value
+ignored-or- substituted-attributes
+substituted-
+attributes
+0x05 start unsupported- unsupported-attributes
+ attributes tag
+0x21 integer type value-tag
+0x0006 name-length
+copies copies name
+0x0004 value-length
+0x00000014 20 value
+0x10 unsupported (type) value-tag
+0x0005 name-length
+sides sides name
+0x0000 value-length
+0x02 start job-attributes job-attributes-tag
+0x21 integer value-tag
+0x0006 name-length
+job-id job-id name
+0x0004 value-length
+147 147 value
+0x45 uri type value-tag
+0x0007 name-length
+job-uri job-uri name
+0x0019 value-length
+ipp://forest/pin job 123 on pinetree value
+etree/123
+0x23 enum type value-tag
+0x0009 name-length
+job-state job-state name
+0x0004 value-length
+
+
+Herriot, et al. Expires December 23, 1999 [Page 31]
+
+
+
+INTERNET-DRAFT IPP/1.1: Encoding and Transport June 23, 1999
+
+
+
+Octets Symbolic Value Protocol field
+
+0x0003 pending value
+0x03 end-of-attributes end-of-attributes-tag
+
+
+
+11.5 Print-URI Request
+
+The following is an example of Print-URI request with copies and job-
+name parameters:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Herriot, et al. Expires December 23, 1999 [Page 32]
+
+
+
+INTERNET-DRAFT IPP/1.1: Encoding and Transport June 23, 1999
+
+
+
+Octets Symbolic Value Protocol field
+0x0101 1.1 version-number
+0x0003 Print-URI operation-id
+0x00000001 1 request-id
+0x01 start operation-attributes operation-attributes-tag
+0x47 charset type value-tag
+0x0012 name-length
+attributes- attributes-charset name
+charset
+0x0008 value-length
+us-ascii US-ASCII value
+0x48 natural-language type value-tag
+0x001B name-length
+attributes- attributes-natural-language name
+natural-
+language
+0x0005 value-length
+en-us en-US value
+0x45 uri type value-tag
+0x000B name-length
+printer-uri printer-uri name
+0x0015 value-length
+ipp://forest/ printer pinetree value
+pinetree
+0x45 uri type value-tag
+0x000C name-length
+document-uri document-uri name
+0x0011 value-length
+ftp://foo.com ftp://foo.com/foo value
+/foo
+0x42 nameWithoutLanguage type value-tag
+0x0008 name-length
+job-name job-name name
+0x0006 value-length
+foobar foobar value
+0x02 start job-attributes job-attributes-tag
+0x21 integer type value-tag
+0x0006 name-length
+copies copies name
+0x0004 value-length
+0x00000001 1 value
+0x03 end-of-attributes end-of-attributes-tag
+
+11.6 Create-Job Request
+
+The following is an example of Create-Job request with no parameters and
+no attributes:
+
+
+
+
+
+
+
+
+
+Herriot, et al. Expires December 23, 1999 [Page 33]
+
+
+
+INTERNET-DRAFT IPP/1.1: Encoding and Transport June 23, 1999
+
+
+
+Octets Symbolic Value Protocol field
+0x0101 1.1 version-number
+0x0005 Create-Job operation-id
+0x00000001 1 request-id
+0x01 start operation-attributes operation-attributes-tag
+0x47 charset type value-tag
+0x0012 name-length
+attributes- attributes-charset name
+charset
+0x0008 value-length
+us-ascii US-ASCII value
+0x48 natural-language type value-tag
+0x001B name-length
+attributes- attributes-natural-language name
+natural-
+language
+0x0005 value-length
+en-us en-US value
+0x45 uri type value-tag
+0x000B name-length
+printer-uri printer-uri name
+0x0015 value-length
+ipp://forest/p printer pinetree value
+inetree
+0x03 end-of-attributes end-of-attributes-tag
+
+11.7 Get-Jobs Request
+
+The following is an example of Get-Jobs request with parameters but no
+attributes:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Herriot, et al. Expires December 23, 1999 [Page 34]
+
+
+
+INTERNET-DRAFT IPP/1.1: Encoding and Transport June 23, 1999
+
+
+
+Octets Symbolic Value Protocol field
+0x0101 1.1 version-number
+0x000A Get-Jobs operation-id
+0x00000123 0x123 request-id
+0x01 start operation-attributes operation-attributes-tag
+0x47 charset type value-tag
+0x0012 name-length
+attributes- attributes-charset name
+charset
+0x0008 value-length
+us-ascii US-ASCII value
+0x48 natural-language type value-tag
+0x001B name-length
+attributes- attributes-natural-language name
+natural-
+language
+0x0005 value-length
+en-us en-US value
+0x45 uri type value-tag
+0x000B name-length
+printer-uri printer-uri name
+0x0015 value-length
+ipp://forest/pi printer pinetree value
+netree
+0x21 integer type value-tag
+0x0005 name-length
+limit limit name
+0x0004 value-length
+0x00000032 50 value
+0x44 keyword type value-tag
+0x0014 name-length
+requested- requested-attributes name
+attributes
+0x0006 value-length
+job-id job-id value
+0x44 keyword type value-tag
+0x0000 additional value name-length
+0x0008 value-length
+job-name job-name value
+0x44 keyword type value-tag
+0x0000 additional value name-length
+0x000F value-length
+document-format document-format value
+0x03 end-of-attributes end-of-attributes-tag
+
+11.8 Get-Jobs Response
+
+The following is an of Get-Jobs response from previous request with 3
+jobs. The Printer returns no information about the second job (because
+of security reasons):
+
+
+
+
+
+
+Herriot, et al. Expires December 23, 1999 [Page 35]
+
+
+
+INTERNET-DRAFT IPP/1.1: Encoding and Transport June 23, 1999
+
+
+
+Octets Symbolic Value Protocol field
+0x0101 1.1 version-number
+0x0000 successful-ok status-code
+0x00000123 0x123 request-id (echoed
+ back)
+0x01 start operation-attributes operation-attribute-tag
+0x47 charset type value-tag
+0x0012 name-length
+attributes- attributes-charset name
+charset
+0x000A value-length
+ISO-8859-1 ISO-8859-1 value
+0x48 natural-language type value-tag
+0x001B name-length
+attributes- attributes-natural-language name
+natural-
+language
+0x0005 value-length
+en-us en-US value
+0x41 textWithoutLanguage type value-tag
+0x000E name-length
+status-message status-message name
+0x000D value-length
+successful-ok successful-ok value
+0x02 start job-attributes (1st job-attributes-tag
+ object)
+0x21 integer type value-tag
+0x0006 name-length
+job-id job-id name
+0x0004 value-length
+147 147 value
+0x36 nameWithLanguage value-tag
+0x0008 name-length
+job-name job-name name
+0x000C value-length
+0x0005 sub-value-length
+fr-ca fr-CA value
+0x0003 sub-value-length
+fou fou name
+0x02 start job-attributes (2nd job-attributes-tag
+ object)
+0x02 start job-attributes (3rd job-attributes-tag
+ object)
+0x21 integer type value-tag
+0x0006 name-length
+job-id job-id name
+0x0004 value-length
+148 149 value
+0x36 nameWithLanguage value-tag
+0x0008 name-length
+job-name job-name name
+0x0012 value-length
+0x0005 sub-value-length
+de-CH de-CH value
+
+
+Herriot, et al. Expires December 23, 1999 [Page 36]
+
+
+
+INTERNET-DRAFT IPP/1.1: Encoding and Transport June 23, 1999
+
+
+
+Octets Symbolic Value Protocol field
+0x0009 sub-value-length
+isch guet isch guet name
+0x03 end-of-attributes end-of-attributes-tag
+
+12. Appendix C: Registration of MIME Media Type Information for
+"application/ipp"
+
+This appendix contains the information that IANA requires for
+registering a MIME media type. The information following this paragraph
+will be forwarded to IANA to register application/ipp whose contents are
+defined in Section 3 "Encoding of the Operation Layer" in this
+document:
+
+MIME type name: application
+
+MIME subtype name: ipp
+
+A Content-Type of "application/ipp" indicates an Internet Printing
+Protocol message body (request or response). Currently there is one
+version: IPP/1.1, whose syntax is described in Section 3 "Encoding of
+the Operation Layer" of [ipp-pro], and whose semantics are described in
+[ipp-mod].
+
+Required parameters: none
+
+Optional parameters: none
+
+Encoding considerations:
+
+IPP/1.1 protocol requests/responses MAY contain long lines and ALWAYS
+contain binary data (for example attribute value lengths).
+
+Security considerations:
+
+IPP/1.1 protocol requests/responses do not introduce any security risks
+not already inherent in the underlying transport protocols. Protocol
+mixed-version interworking rules in [ipp-mod] as well as protocol
+encoding rules in [ipp-pro] are complete and unambiguous.
+
+Interoperability considerations:
+
+IPP/1.1 requests (generated by clients) and responses (generated by
+servers) MUST comply with all conformance requirements imposed by the
+normative specifications [ipp-mod] and [ipp-pro]. Protocol encoding
+rules specified in [ipp-pro] are comprehensive, so that interoperability
+between conforming implementations is guaranteed (although support for
+specific optional features is not ensured). Both the "charset" and
+"natural-language" of all IPP/1.1 attribute values which are a
+LOCALIZED-STRING are explicit within IPP protocol requests/responses
+(without recourse to any external information in HTTP, SMTP, or other
+message transport headers).
+
+Published specifications:
+
+
+Herriot, et al. Expires December 23, 1999 [Page 37]
+
+
+
+INTERNET-DRAFT IPP/1.1: Encoding and Transport June 23, 1999
+
+
+[ipp-mod] Isaacson, S., deBry, R., Hastings, T., Herriot, R.,
+ Powell, P., "Internet Printing Protocol/1.1: Model and
+ Semantics" draft-ietf-ipp-model-v11-03.txt, June, 1999.
+
+[ipp-pro] Herriot, R., Butler, S., Moore, P., Turner, R.,
+ "Internet Printing Protocol/1.1: Encoding and Transport", draft-
+ ietf-ipp-protocol-v11-02.txt, June, 1999.
+
+Applications which use this media type:
+
+Internet Printing Protocol (IPP) print clients and print servers,
+communicating using HTTP/1.1 (see [IPP-PRO]), SMTP/ESMTP, FTP, or other
+transport protocol. Messages of type "application/ipp" are self-
+contained and transport-independent, including "charset" and "natural-
+language" context for any LOCALIZED-STRING value.
+
+Person & email address to contact for further information:
+
+Tom Hastings
+Xerox Corporation
+ 737 Hawaii St. ESAE-231
+El Segundo, CA
+
+Phone: 310-333-6413
+Fax: 310-333-5514
+Email: thastings@cp10.es.xerox.com
+
+or
+
+Robert Herriot
+Xerox Corporation
+3400 Hillview Ave., Bldg #1
+Palo Alto, CA 94304
+
+Phone: 650-813-7696
+Fax: 650-813-6860
+Email: robert.herriot@pahv.xerox.com
+
+Intended usage:
+
+COMMON
+
+
+13. Appendix D: Changes from IPP/1.0
+
+IPP/1.1 is identical to IPP/1.0 [RFC2565] with the follow changes:
+
+1.Attributes values that identify a printer or job object use a new
+ 'ipp' scheme. The 'http' and 'https' schemes are supported only for
+ backward compatibility. See section 5.
+
+2.Clients MUST support of Digest Authentication, IPP Printers SHOULD
+ support Digest Authentication. See Section 6.1.1
+
+
+
+Herriot, et al. Expires December 23, 1999 [Page 38]
+
+
+
+INTERNET-DRAFT IPP/1.1: Encoding and Transport June 23, 1999
+
+
+3.TLS is recommended for channel security. In addition, SSL3 may be
+ supported for backward compatibility. See Section 6.1.2
+
+4.It is recommended that IPP/1.1 objects accept any request with major
+ version number '1'. See section 7.1.
+
+5.IPP objects SHOULD return the URL scheme requested for "job-printer-
+ uri" and "job-uri" Job Attributes, rather than the URL scheme used to
+ create the job. See section 7.2
+
+
+14. Full Copyright Statement
+
+The IETF takes no position regarding the validity or scope of any
+intellectual property or other rights that might be claimed to pertain
+to the implementation or use of the technology described in this
+document or the extent to which any license under such rights might or
+might not be available; neither does it represent that it has made any
+effort to identify any such rights. Information on the IETF's
+procedures with respect to rights in standards-track and standards-
+related documentation can be found in BCP-11[BCP-11]. Copies of claims
+of rights made available for publication and any assurances of licenses
+to be made available, or the result of an attempt made to obtain a
+general license or permission for the use of such proprietary rights by
+implementers or users of this specification can be obtained from the
+IETF Secretariat.
+
+The IETF invites any interested party to bring to its attention any
+copyrights, patents or patent applications, or other proprietary rights
+which may cover technology that may be required to practice this
+standard. Please address the information to the IETF Executive
+Director.
+
+Copyright (C)The Internet Society (1999). All Rights Reserved
+
+This document and translations of it may be copied and furnished to
+others, and derivative works that comment on or otherwise explain it or
+assist in its implementation may be prepared, copied, published and
+distributed, in whole or in part, without restriction of any kind,
+provided that the above copyright notice and this paragraph are included
+on all such copies and derivative works. However, this document itself
+may not be modified in any way, such as by removing the copyright notice
+or references to the Internet Society or other Internet organizations,
+except as needed for the purpose of developing Internet standards in
+which case the procedures for copyrights defined in the Internet
+Standards process must be followed, or as required to translate it into
+languages other than English.
+
+The limited permissions granted above are perpetual and will not be
+revoked by the Internet Society or its successors or assigns.
+
+This document and the information contained herein is provided on an "AS
+IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING TASK
+FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT
+
+
+Herriot, et al. Expires December 23, 1999 [Page 39]
+
+
+
+INTERNET-DRAFT IPP/1.1: Encoding and Transport June 23, 1999
+
+
+LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION HEREIN WILL NOT
+INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR
+FITNESS FOR A PARTICULAR PURPOSE.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Herriot, et al. Expires December 23, 1999 [Page 40]
diff --git a/standards/rfc1179.txt b/standards/rfc1179.txt
new file mode 100644
index 000000000..ef59411f7
--- /dev/null
+++ b/standards/rfc1179.txt
@@ -0,0 +1,787 @@
+
+
+
+
+
+
+Network Printing Working Group L. McLaughlin III, Editor
+Request for Comments: 1179 The Wollongong Group
+ August 1990
+
+
+ Line Printer Daemon Protocol
+
+Status of this Memo
+
+ This RFC describes an existing print server protocol widely used on
+ the Internet for communicating between line printer daemons (both
+ clients and servers). This memo is for informational purposes only,
+ and does not specify an Internet standard. Please refer to the
+ current edition of the "IAB Official Protocol Standards" for the
+ standardization state and status of this protocol. Distribution of
+ this memo is unlimited.
+
+1. Introduction
+
+ The Berkeley versions of the Unix(tm) operating system provide line
+ printer spooling with a collection of programs: lpr (assign to
+ queue), lpq (display the queue), lprm (remove from queue), and lpc
+ (control the queue). These programs interact with an autonomous
+ process called the line printer daemon. This RFC describes the
+ protocols with which a line printer daemon client may control
+ printing.
+
+ This memo is based almost entirely on the work of Robert Knight at
+ Princeton University. I gratefully acknowledge his efforts in
+ deciphering the UNIX lpr protocol and producing earlier versions of
+ this document.
+
+2. Model of Printing Environment
+
+ A group of hosts request services from a line printer daemon process
+ running on a host. The services provided by the process are related
+ to printing jobs. A printing job produces output from one file.
+ Each job will have a unique job number which is between 0 and 999,
+ inclusive. The jobs are requested by users which have names. These
+ user names may not start with a digit.
+
+3. Specification of the Protocol
+
+ The specification includes file formats for the control and data
+ files as well as messages used by the protocol.
+
+
+
+
+
+
+McLaughlin [Page 1]
+
+RFC 1179 LPR August 1990
+
+
+3.1 Message formats
+
+ LPR is a a TCP-based protocol. The port on which a line printer
+ daemon listens is 515. The source port must be in the range 721 to
+ 731, inclusive. A line printer daemon responds to commands send to
+ its port. All commands begin with a single octet code, which is a
+ binary number which represents the requested function. The code is
+ immediately followed by the ASCII name of the printer queue name on
+ which the function is to be performed. If there are other operands
+ to the command, they are separated from the printer queue name with
+ white space (ASCII space, horizontal tab, vertical tab, and form
+ feed). The end of the command is indicated with an ASCII line feed
+ character.
+
+4. Diagram Conventions
+
+ The diagrams in the rest of this RFC use these conventions. These
+ diagrams show the format of an octet stream sent to the server. The
+ outermost box represents this stream. Each box within the outermost
+ one shows one portion of the stream. If the contents of the box is
+ two decimal digits, this indicates that the binary 8 bit value is to
+ be used. If the contents is two uppercase letters, this indicates
+ that the corresponding ASCII control character is to be used. An
+ exception to this is that the character SP can be interpreted as
+ white space. (See the preceding section for a definition.) If the
+ contents is a single letter, the ASCII code for this letter must be
+ sent. Otherwise, the contents are intended to be mnemonic of the
+ contents of the field which is a sequence of octets.
+
+5. Daemon commands
+
+ The verbs in the command names should be interpreted as statements
+ made to the daemon. Thus, the command "Print any waiting jobs" is an
+ imperative to the line printer daemon to which it is sent. A new
+ connection must be made for each command to be given to the daemon.
+
+5.1 01 - Print any waiting jobs
+
+ +----+-------+----+
+ | 01 | Queue | LF |
+ +----+-------+----+
+ Command code - 1
+ Operand - Printer queue name
+
+ This command starts the printing process if it not already running.
+
+
+
+
+
+
+McLaughlin [Page 2]
+
+RFC 1179 LPR August 1990
+
+
+5.2 02 - Receive a printer job
+
+ +----+-------+----+
+ | 02 | Queue | LF |
+ +----+-------+----+
+ Command code - 2
+ Operand - Printer queue name
+
+ Receiving a job is controlled by a second level of commands. The
+ daemon is given commands by sending them over the same connection.
+ The commands are described in the next section (6).
+
+ After this command is sent, the client must read an acknowledgement
+ octet from the daemon. A positive acknowledgement is an octet of
+ zero bits. A negative acknowledgement is an octet of any other
+ pattern.
+
+5.3 03 - Send queue state (short)
+
+ +----+-------+----+------+----+
+ | 03 | Queue | SP | List | LF |
+ +----+-------+----+------+----+
+ Command code - 3
+ Operand 1 - Printer queue name
+ Other operands - User names or job numbers
+
+ If the user names or job numbers or both are supplied then only those
+ jobs for those users or with those numbers will be sent.
+
+ The response is an ASCII stream which describes the printer queue.
+ The stream continues until the connection closes. Ends of lines are
+ indicated with ASCII LF control characters. The lines may also
+ contain ASCII HT control characters.
+
+5.4 04 - Send queue state (long)
+
+ +----+-------+----+------+----+
+ | 04 | Queue | SP | List | LF |
+ +----+-------+----+------+----+
+ Command code - 4
+ Operand 1 - Printer queue name
+ Other operands - User names or job numbers
+
+ If the user names or job numbers or both are supplied then only those
+ jobs for those users or with those numbers will be sent.
+
+ The response is an ASCII stream which describes the printer queue.
+ The stream continues until the connection closes. Ends of lines are
+
+
+
+McLaughlin [Page 3]
+
+RFC 1179 LPR August 1990
+
+
+ indicated with ASCII LF control characters. The lines may also
+ contain ASCII HT control characters.
+
+5.5 05 - Remove jobs
+
+ +----+-------+----+-------+----+------+----+
+ | 05 | Queue | SP | Agent | SP | List | LF |
+ +----+-------+----+-------+----+------+----+
+ Command code - 5
+ Operand 1 - Printer queue name
+ Operand 2 - User name making request (the agent)
+ Other operands - User names or job numbers
+
+ This command deletes the print jobs from the specified queue which
+ are listed as the other operands. If only the agent is given, the
+ command is to delete the currently active job. Unless the agent is
+ "root", it is not possible to delete a job which is not owned by the
+ user. This is also the case for specifying user names instead of
+ numbers. That is, agent "root" can delete jobs by user name but no
+ other agents can.
+
+6. Receive job subcommands
+
+ These commands are processed when the line printer daemon has
+ been given the receive job command. The daemon will continue to
+ process commands until the connection is closed.
+
+ After a subcommand is sent, the client must wait for an
+ acknowledgement from the daemon. A positive acknowledgement is an
+ octet of zero bits. A negative acknowledgement is an octet of any
+ other pattern.
+
+ LPR clients SHOULD be able to sent the receive data file and receive
+ control file subcommands in either order. LPR servers MUST be able
+ to receive the control file subcommand first and SHOULD be able to
+ receive the data file subcommand first.
+
+6.1 01 - Abort job
+
+ Command code - 1
+ +----+----+
+ | 01 | LF |
+ +----+----+
+
+ No operands should be supplied. This subcommand will remove any
+ files which have been created during this "Receive job" command.
+
+
+
+
+
+McLaughlin [Page 4]
+
+RFC 1179 LPR August 1990
+
+
+6.2 02 - Receive control file
+
+ +----+-------+----+------+----+
+ | 02 | Count | SP | Name | LF |
+ +----+-------+----+------+----+
+ Command code - 2
+ Operand 1 - Number of bytes in control file
+ Operand 2 - Name of control file
+
+ The control file must be an ASCII stream with the ends of lines
+ indicated by ASCII LF. The total number of bytes in the stream is
+ sent as the first operand. The name of the control file is sent as
+ the second. It should start with ASCII "cfA", followed by a three
+ digit job number, followed by the host name which has constructed the
+ control file. Acknowledgement processing must occur as usual after
+ the command is sent.
+
+ The next "Operand 1" octets over the same TCP connection are the
+ intended contents of the control file. Once all of the contents have
+ been delivered, an octet of zero bits is sent as an indication that
+ the file being sent is complete. A second level of acknowledgement
+ processing must occur at this point.
+
+6.3 03 - Receive data file
+
+ +----+-------+----+------+----+
+ | 03 | Count | SP | Name | LF |
+ +----+-------+----+------+----+
+ Command code - 3
+ Operand 1 - Number of bytes in data file
+ Operand 2 - Name of data file
+
+ The data file may contain any 8 bit values at all. The total number
+ of bytes in the stream may be sent as the first operand, otherwise
+ the field should be cleared to 0. The name of the data file should
+ start with ASCII "dfA". This should be followed by a three digit job
+ number. The job number should be followed by the host name which has
+ constructed the data file. Interpretation of the contents of the
+ data file is determined by the contents of the corresponding control
+ file. If a data file length has been specified, the next "Operand 1"
+ octets over the same TCP connection are the intended contents of the
+ data file. In this case, once all of the contents have been
+ delivered, an octet of zero bits is sent as an indication that the
+ file being sent is complete. A second level of acknowledgement
+ processing must occur at this point.
+
+
+
+
+
+
+McLaughlin [Page 5]
+
+RFC 1179 LPR August 1990
+
+
+7. Control file lines
+
+ This section discusses the format of the lines in the control file
+ which is sent to the line printer daemon.
+
+ Each line of the control file consists of a single, printable ASCII
+ character which represents a function to be performed when the file
+ is printed. Interpretation of these command characters are case-
+ sensitive. The rest of the line after the command character is the
+ command's operand. No leading white space is permitted after the
+ command character. The line ends with an ASCII new line.
+
+ Those commands which have a lower case letter as a command code are
+ used to specify an actual printing request. The commands which use
+ upper case are used to describe parametric values or background
+ conditions.
+
+ Some commands must be included in every control file. These are 'H'
+ (responsible host) and 'P' (responsible user). Additionally, there
+ must be at least one lower case command to produce any output.
+
+7.1 C - Class for banner page
+
+ +---+-------+----+
+ | C | Class | LF |
+ +---+-------+----+
+ Command code - 'C'
+ Operand - Name of class for banner pages
+
+ This command sets the class name to be printed on the banner page.
+ The name must be 31 or fewer octets. The name can be omitted. If it
+ is, the name of the host on which the file is printed will be used.
+ The class is conventionally used to display the host from which the
+ printing job originated. It will be ignored unless the print banner
+ command ('L') is also used.
+
+7.2 H - Host name
+
+ +---+------+----+
+ | H | Host | LF |
+ +---+------+----+
+ Command code - 'H'
+ Operand - Name of host
+
+ This command specifies the name of the host which is to be treated as
+ the source of the print job. The command must be included in the
+ control file. The name of the host must be 31 or fewer octets.
+
+
+
+
+McLaughlin [Page 6]
+
+RFC 1179 LPR August 1990
+
+
+7.3 I - Indent Printing
+
+ +---+-------+----+
+ | I | count | LF |
+ +---+-------+----+
+ Command code - 'I'
+ Operand - Indenting count
+
+ This command specifies that, for files which are printed with the
+ 'f', of columns given. (It is ignored for other output generating
+ commands.) The identing count operand must be all decimal digits.
+
+7.4 J - Job name for banner page
+
+ +---+----------+----+
+ | J | Job name | LF |
+ +---+----------+----+
+ Command code - 'J'
+ Operand - Job name
+
+ This command sets the job name to be printed on the banner page. The
+ name of the job must be 99 or fewer octets. It can be omitted. The
+ job name is conventionally used to display the name of the file or
+ files which were "printed". It will be ignored unless the print
+ banner command ('L') is also used.
+
+7.5 L - Print banner page
+
+ +---+------+----+
+ | L | User | LF |
+ +---+------+----+
+ Command code - 'L'
+ Operand - Name of user for burst pages
+
+ This command causes the banner page to be printed. The user name can
+ be omitted. The class name for banner page and job name for banner
+ page commands must precede this command in the control file to be
+ effective.
+
+7.6 M - Mail When Printed
+
+ +---+------+----+
+ | M | user | LF |
+ +---+------+----+
+ Command code - 'M'
+ Operand - User name
+
+ This entry causes mail to be sent to the user given as the operand at
+
+
+
+McLaughlin [Page 7]
+
+RFC 1179 LPR August 1990
+
+
+ the host specified by the 'H' entry when the printing operation ends
+ (successfully or unsuccessfully).
+
+7.7 N - Name of source file
+
+ +---+------+----+
+ | N | Name | LF |
+ +---+------+----+
+ Command code - 'N'
+ Operand - File name
+
+ This command specifies the name of the file from which the data file
+ was constructed. It is returned on a query and used in printing with
+ the 'p' command when no title has been given. It must be 131 or
+ fewer octets.
+
+7.8 P - User identification
+
+ +---+------+----+
+ | P | Name | LF |
+ +---+------+----+
+ Command code - 'P'
+ Operand - User id
+
+ This command specifies the user identification of the entity
+ requesting the printing job. This command must be included in the
+ control file. The user identification must be 31 or fewer octets.
+
+7.9 S - Symbolic link data
+
+ +---+--------+----+-------+----+
+ | S | device | SP | inode | LF |
+ +---+--------+----+-------+----+
+ Command code - 'S'
+ Operand 1 - Device number
+ Operand 2 - Inode number
+
+ This command is used to record symbolic link data on a Unix system so
+ that changing a file's directory entry after a file is printed will
+ not print the new file. It is ignored if the data file is not
+ symbolically linked.
+
+
+
+
+
+
+
+
+
+
+McLaughlin [Page 8]
+
+RFC 1179 LPR August 1990
+
+
+7.10 T - Title for pr
+
+ +---+-------+----+
+ | T | title | LF |
+ +---+-------+----+
+ Command code - 'T'
+ Operand - Title text
+
+ This command provides a title for a file which is to be printed with
+ either the 'p' command. (It is ignored by all of the other printing
+ commands.) The title must be 79 or fewer octets.
+
+7.11 U - Unlink data file
+
+ +---+------+----+
+ | U | file | LF |
+ +---+------+----+
+ Command code - 'U'
+ Operand - File to unlink
+
+ This command indicates that the specified file is no longer needed.
+ This should only be used for data files.
+
+7.12 W - Width of output
+
+ +---+-------+----+
+ | W | width | LF |
+ +---+-------+----+
+ Command code - 'W'
+ Operand - Width count
+
+ This command limits the output to the specified number of columns for
+ the 'f', 'l', and 'p' commands. (It is ignored for other output
+ generating commands.) The width count operand must be all decimal
+ digits. It may be silently reduced to some lower value. The default
+ value for the width is 132.
+
+7.13 1 - troff R font
+
+ +---+------+----+
+ | 1 | file | LF |
+ +---+------+----+
+ Command code - '1'
+ Operand - File name
+
+ This command specifies the file name for the troff R font. [1] This
+ is the font which is printed using Times Roman by default.
+
+
+
+
+McLaughlin [Page 9]
+
+RFC 1179 LPR August 1990
+
+
+7.14 2 - troff I font
+
+ +---+------+----+
+ | 2 | file | LF |
+ +---+------+----+
+ Command code - '2'
+ Operand - File name
+
+ This command specifies the file name for the troff I font. [1] This
+ is the font which is printed using Times Italic by default.
+
+7.15 3 - troff B font
+
+ +---+------+----+
+ | 3 | file | LF |
+ +---+------+----+
+ Command code - '3'
+ Operand - File name
+
+ This command specifies the file name for the troff B font. [1] This
+ is the font which is printed using Times Bold by default.
+
+7.16 4 - troff S font
+
+ +---+------+----+
+ | 4 | file | LF |
+ +---+------+----+
+ Command code - '4'
+ Operand - File name
+
+ This command specifies the file name for the troff S font. [1] This
+ is the font which is printed using Special Mathematical Font by
+ default.
+
+7.17 c - Plot CIF file
+
+ +---+------+----+
+ | c | file | LF |
+ +---+------+----+
+ Command code - 'c'
+ Operand - File to plot
+
+ This command causes the data file to be plotted, treating the data as
+ CIF (CalTech Intermediate Form) graphics language. [2]
+
+
+
+
+
+
+
+McLaughlin [Page 10]
+
+RFC 1179 LPR August 1990
+
+
+7.18 d - Print DVI file
+
+ +---+------+----+
+ | d | file | LF |
+ +---+------+----+
+ Command code - 'd'
+ Operand - File to print
+
+ This command causes the data file to be printed, treating the data as
+ DVI (TeX output). [3]
+
+7.19 f - Print formatted file
+
+ +---+------+----+
+ | f | file | LF |
+ +---+------+----+
+ Command code - 'f'
+ Operand - File to print
+
+ This command cause the data file to be printed as a plain text file,
+ providing page breaks as necessary. Any ASCII control characters
+ which are not in the following list are discarded: HT, CR, FF, LF,
+ and BS.
+
+7.20 g - Plot file
+
+ +---+------+----+
+ | g | file | LF |
+ +---+------+----+
+ Command code - 'g'
+ Operand - File to plot
+
+ This command causes the data file to be plotted, treating the data as
+ output from the Berkeley Unix plot library. [1]
+
+7.21 k - Reserved for use by Kerberized LPR clients and servers.
+
+7.22 l - Print file leaving control characters
+
+ +---+------+----+
+ | l | file | LF |
+ +---+------+----+
+ Command code - 'l' (lower case L)
+ Operand - File to print
+
+ This command causes the specified data file to printed without
+ filtering the control characters (as is done with the 'f' command).
+
+
+
+
+McLaughlin [Page 11]
+
+RFC 1179 LPR August 1990
+
+
+7.23 n - Print ditroff output file
+
+ +---+------+----+
+ | n | file | LF |
+ +---+------+----+
+ Command code - 'n'
+ Operand - File to print
+
+ This command prints the data file to be printed, treating the data as
+ ditroff output. [4]
+
+7.24 o - Print Postscript output file
+
+ +---+------+----+
+ | o | file | LF |
+ +---+------+----+
+ Command code - 'o'
+ Operand - File to print
+
+ This command prints the data file to be printed, treating the data as
+ standard Postscript input.
+
+7.25 p - Print file with 'pr' format
+
+ +---+------+----+
+ | p | file | LF |
+ +---+------+----+
+ Command code - 'p'
+ Operand - File to print
+
+ This command causes the data file to be printed with a heading, page
+ numbers, and pagination. The heading should include the date and
+ time that printing was started, the title, and a page number
+ identifier followed by the page number. The title is the name of
+ file as specified by the 'N' command, unless the 'T' command (title)
+ has been given. After a page of text has been printed, a new page is
+ started with a new page number. (There is no way to specify the
+ length of the page.)
+
+7.26 r - File to print with FORTRAN carriage control
+
+ +---+------+----+
+ | r | file | LF |
+ +---+------+----+
+ Command code - 'r'
+ Operand - File to print
+
+ This command causes the data file to be printed, interpreting the
+
+
+
+McLaughlin [Page 12]
+
+RFC 1179 LPR August 1990
+
+
+ first column of each line as FORTRAN carriage control. The FORTRAN
+ standard limits this to blank, "1", "0", and "+" carriage controls.
+ Most FORTRAN programmers also expect "-" (triple space) to work as
+ well.
+
+7.27 t - Print troff output file
+
+ +---+------+----+
+ | t | file | LF |
+ +---+------+----+
+ Command code - 't'
+ Operand - File to print
+
+ This command prints the data file as Graphic Systems C/A/T
+ phototypesetter input. [5] This is the standard output of the Unix
+ "troff" command.
+
+7.28 v - Print raster file
+
+ +---+------+----+
+ | v | file | LF |
+ +---+------+----+
+ Command code - 'v'
+ Operand - File to print
+
+ This command prints a Sun raster format file. [6]
+
+7.29 z - Reserved for future use with the Palladium print system.
+
+REFERENCES and BIBLIOGRAPHY
+
+ [1] Computer Science Research Group, "UNIX Programmer's Reference
+ Manual", USENIX, 1986.
+
+ [2] Hon and Sequin, "A Guide to LSI Implementation", XEROX PARC,
+ 1980.
+
+ [3] Knuth, D., "TeX The Program".
+
+ [4] Kernighan, B., "A Typesetter-independent TROFF".
+
+ [5] "Model C/A/T Phototypesetter", Graphic Systems, Inc. Hudson, N.H.
+
+ [6] Sun Microsystems, "Pixrect Reference Manual", Sun Microsystems,
+ Mountain View, CA, 1988.
+
+
+
+
+
+
+McLaughlin [Page 13]
+
+RFC 1179 LPR August 1990
+
+
+Security Considerations
+
+ Security issues are not discussed in this memo.
+
+Author's Address
+
+ Leo J. McLaughlin III
+ The Wollongong Group
+ 1129 San Antonio Road
+ Palo Alto, CA 94303
+
+ Phone: 415-962-7100
+
+ EMail: ljm@twg.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+McLaughlin [Page 14]
+ \ No newline at end of file
diff --git a/standards/rfc1321.txt b/standards/rfc1321.txt
new file mode 100644
index 000000000..68af27d2b
--- /dev/null
+++ b/standards/rfc1321.txt
@@ -0,0 +1,1179 @@
+
+
+
+
+
+
+Network Working Group R. Rivest
+Request for Comments: 1321 MIT Laboratory for Computer Science
+ and RSA Data Security, Inc.
+ April 1992
+
+
+ The MD5 Message-Digest Algorithm
+
+Status of this Memo
+
+ This memo provides information for the Internet community. It does
+ not specify an Internet standard. Distribution of this memo is
+ unlimited.
+
+Acknowlegements
+
+ We would like to thank Don Coppersmith, Burt Kaliski, Ralph Merkle,
+ David Chaum, and Noam Nisan for numerous helpful comments and
+ suggestions.
+
+Table of Contents
+
+ 1. Executive Summary 1
+ 2. Terminology and Notation 2
+ 3. MD5 Algorithm Description 3
+ 4. Summary 6
+ 5. Differences Between MD4 and MD5 6
+ References 7
+ APPENDIX A - Reference Implementation 7
+ Security Considerations 21
+ Author's Address 21
+
+1. Executive Summary
+
+ This document describes the MD5 message-digest algorithm. The
+ algorithm takes as input a message of arbitrary length and produces
+ as output a 128-bit "fingerprint" or "message digest" of the input.
+ It is conjectured that it is computationally infeasible to produce
+ two messages having the same message digest, or to produce any
+ message having a given prespecified target message digest. The MD5
+ algorithm is intended for digital signature applications, where a
+ large file must be "compressed" in a secure manner before being
+ encrypted with a private (secret) key under a public-key cryptosystem
+ such as RSA.
+
+
+
+
+
+
+
+Rivest [Page 1]
+
+RFC 1321 MD5 Message-Digest Algorithm April 1992
+
+
+ The MD5 algorithm is designed to be quite fast on 32-bit machines. In
+ addition, the MD5 algorithm does not require any large substitution
+ tables; the algorithm can be coded quite compactly.
+
+ The MD5 algorithm is an extension of the MD4 message-digest algorithm
+ 1,2]. MD5 is slightly slower than MD4, but is more "conservative" in
+ design. MD5 was designed because it was felt that MD4 was perhaps
+ being adopted for use more quickly than justified by the existing
+ critical review; because MD4 was designed to be exceptionally fast,
+ it is "at the edge" in terms of risking successful cryptanalytic
+ attack. MD5 backs off a bit, giving up a little in speed for a much
+ greater likelihood of ultimate security. It incorporates some
+ suggestions made by various reviewers, and contains additional
+ optimizations. The MD5 algorithm is being placed in the public domain
+ for review and possible adoption as a standard.
+
+ For OSI-based applications, MD5's object identifier is
+
+ md5 OBJECT IDENTIFIER ::=
+ iso(1) member-body(2) US(840) rsadsi(113549) digestAlgorithm(2) 5}
+
+ In the X.509 type AlgorithmIdentifier [3], the parameters for MD5
+ should have type NULL.
+
+2. Terminology and Notation
+
+ In this document a "word" is a 32-bit quantity and a "byte" is an
+ eight-bit quantity. A sequence of bits can be interpreted in a
+ natural manner as a sequence of bytes, where each consecutive group
+ of eight bits is interpreted as a byte with the high-order (most
+ significant) bit of each byte listed first. Similarly, a sequence of
+ bytes can be interpreted as a sequence of 32-bit words, where each
+ consecutive group of four bytes is interpreted as a word with the
+ low-order (least significant) byte given first.
+
+ Let x_i denote "x sub i". If the subscript is an expression, we
+ surround it in braces, as in x_{i+1}. Similarly, we use ^ for
+ superscripts (exponentiation), so that x^i denotes x to the i-th
+ power.
+
+ Let the symbol "+" denote addition of words (i.e., modulo-2^32
+ addition). Let X <<< s denote the 32-bit value obtained by circularly
+ shifting (rotating) X left by s bit positions. Let not(X) denote the
+ bit-wise complement of X, and let X v Y denote the bit-wise OR of X
+ and Y. Let X xor Y denote the bit-wise XOR of X and Y, and let XY
+ denote the bit-wise AND of X and Y.
+
+
+
+
+
+Rivest [Page 2]
+
+RFC 1321 MD5 Message-Digest Algorithm April 1992
+
+
+3. MD5 Algorithm Description
+
+ We begin by supposing that we have a b-bit message as input, and that
+ we wish to find its message digest. Here b is an arbitrary
+ nonnegative integer; b may be zero, it need not be a multiple of
+ eight, and it may be arbitrarily large. We imagine the bits of the
+ message written down as follows:
+
+ m_0 m_1 ... m_{b-1}
+
+ The following five steps are performed to compute the message digest
+ of the message.
+
+3.1 Step 1. Append Padding Bits
+
+ The message is "padded" (extended) so that its length (in bits) is
+ congruent to 448, modulo 512. That is, the message is extended so
+ that it is just 64 bits shy of being a multiple of 512 bits long.
+ Padding is always performed, even if the length of the message is
+ already congruent to 448, modulo 512.
+
+ Padding is performed as follows: a single "1" bit is appended to the
+ message, and then "0" bits are appended so that the length in bits of
+ the padded message becomes congruent to 448, modulo 512. In all, at
+ least one bit and at most 512 bits are appended.
+
+3.2 Step 2. Append Length
+
+ A 64-bit representation of b (the length of the message before the
+ padding bits were added) is appended to the result of the previous
+ step. In the unlikely event that b is greater than 2^64, then only
+ the low-order 64 bits of b are used. (These bits are appended as two
+ 32-bit words and appended low-order word first in accordance with the
+ previous conventions.)
+
+ At this point the resulting message (after padding with bits and with
+ b) has a length that is an exact multiple of 512 bits. Equivalently,
+ this message has a length that is an exact multiple of 16 (32-bit)
+ words. Let M[0 ... N-1] denote the words of the resulting message,
+ where N is a multiple of 16.
+
+3.3 Step 3. Initialize MD Buffer
+
+ A four-word buffer (A,B,C,D) is used to compute the message digest.
+ Here each of A, B, C, D is a 32-bit register. These registers are
+ initialized to the following values in hexadecimal, low-order bytes
+ first):
+
+
+
+
+Rivest [Page 3]
+
+RFC 1321 MD5 Message-Digest Algorithm April 1992
+
+
+ word A: 01 23 45 67
+ word B: 89 ab cd ef
+ word C: fe dc ba 98
+ word D: 76 54 32 10
+
+3.4 Step 4. Process Message in 16-Word Blocks
+
+ We first define four auxiliary functions that each take as input
+ three 32-bit words and produce as output one 32-bit word.
+
+ F(X,Y,Z) = XY v not(X) Z
+ G(X,Y,Z) = XZ v Y not(Z)
+ H(X,Y,Z) = X xor Y xor Z
+ I(X,Y,Z) = Y xor (X v not(Z))
+
+ In each bit position F acts as a conditional: if X then Y else Z.
+ The function F could have been defined using + instead of v since XY
+ and not(X)Z will never have 1's in the same bit position.) It is
+ interesting to note that if the bits of X, Y, and Z are independent
+ and unbiased, the each bit of F(X,Y,Z) will be independent and
+ unbiased.
+
+ The functions G, H, and I are similar to the function F, in that they
+ act in "bitwise parallel" to produce their output from the bits of X,
+ Y, and Z, in such a manner that if the corresponding bits of X, Y,
+ and Z are independent and unbiased, then each bit of G(X,Y,Z),
+ H(X,Y,Z), and I(X,Y,Z) will be independent and unbiased. Note that
+ the function H is the bit-wise "xor" or "parity" function of its
+ inputs.
+
+ This step uses a 64-element table T[1 ... 64] constructed from the
+ sine function. Let T[i] denote the i-th element of the table, which
+ is equal to the integer part of 4294967296 times abs(sin(i)), where i
+ is in radians. The elements of the table are given in the appendix.
+
+ Do the following:
+
+ /* Process each 16-word block. */
+ For i = 0 to N/16-1 do
+
+ /* Copy block i into X. */
+ For j = 0 to 15 do
+ Set X[j] to M[i*16+j].
+ end /* of loop on j */
+
+ /* Save A as AA, B as BB, C as CC, and D as DD. */
+ AA = A
+ BB = B
+
+
+
+Rivest [Page 4]
+
+RFC 1321 MD5 Message-Digest Algorithm April 1992
+
+
+ CC = C
+ DD = D
+
+ /* Round 1. */
+ /* Let [abcd k s i] denote the operation
+ a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */
+ /* Do the following 16 operations. */
+ [ABCD 0 7 1] [DABC 1 12 2] [CDAB 2 17 3] [BCDA 3 22 4]
+ [ABCD 4 7 5] [DABC 5 12 6] [CDAB 6 17 7] [BCDA 7 22 8]
+ [ABCD 8 7 9] [DABC 9 12 10] [CDAB 10 17 11] [BCDA 11 22 12]
+ [ABCD 12 7 13] [DABC 13 12 14] [CDAB 14 17 15] [BCDA 15 22 16]
+
+ /* Round 2. */
+ /* Let [abcd k s i] denote the operation
+ a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */
+ /* Do the following 16 operations. */
+ [ABCD 1 5 17] [DABC 6 9 18] [CDAB 11 14 19] [BCDA 0 20 20]
+ [ABCD 5 5 21] [DABC 10 9 22] [CDAB 15 14 23] [BCDA 4 20 24]
+ [ABCD 9 5 25] [DABC 14 9 26] [CDAB 3 14 27] [BCDA 8 20 28]
+ [ABCD 13 5 29] [DABC 2 9 30] [CDAB 7 14 31] [BCDA 12 20 32]
+
+ /* Round 3. */
+ /* Let [abcd k s t] denote the operation
+ a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */
+ /* Do the following 16 operations. */
+ [ABCD 5 4 33] [DABC 8 11 34] [CDAB 11 16 35] [BCDA 14 23 36]
+ [ABCD 1 4 37] [DABC 4 11 38] [CDAB 7 16 39] [BCDA 10 23 40]
+ [ABCD 13 4 41] [DABC 0 11 42] [CDAB 3 16 43] [BCDA 6 23 44]
+ [ABCD 9 4 45] [DABC 12 11 46] [CDAB 15 16 47] [BCDA 2 23 48]
+
+ /* Round 4. */
+ /* Let [abcd k s t] denote the operation
+ a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */
+ /* Do the following 16 operations. */
+ [ABCD 0 6 49] [DABC 7 10 50] [CDAB 14 15 51] [BCDA 5 21 52]
+ [ABCD 12 6 53] [DABC 3 10 54] [CDAB 10 15 55] [BCDA 1 21 56]
+ [ABCD 8 6 57] [DABC 15 10 58] [CDAB 6 15 59] [BCDA 13 21 60]
+ [ABCD 4 6 61] [DABC 11 10 62] [CDAB 2 15 63] [BCDA 9 21 64]
+
+ /* Then perform the following additions. (That is increment each
+ of the four registers by the value it had before this block
+ was started.) */
+ A = A + AA
+ B = B + BB
+ C = C + CC
+ D = D + DD
+
+ end /* of loop on i */
+
+
+
+Rivest [Page 5]
+
+RFC 1321 MD5 Message-Digest Algorithm April 1992
+
+
+3.5 Step 5. Output
+
+ The message digest produced as output is A, B, C, D. That is, we
+ begin with the low-order byte of A, and end with the high-order byte
+ of D.
+
+ This completes the description of MD5. A reference implementation in
+ C is given in the appendix.
+
+4. Summary
+
+ The MD5 message-digest algorithm is simple to implement, and provides
+ a "fingerprint" or message digest of a message of arbitrary length.
+ It is conjectured that the difficulty of coming up with two messages
+ having the same message digest is on the order of 2^64 operations,
+ and that the difficulty of coming up with any message having a given
+ message digest is on the order of 2^128 operations. The MD5 algorithm
+ has been carefully scrutinized for weaknesses. It is, however, a
+ relatively new algorithm and further security analysis is of course
+ justified, as is the case with any new proposal of this sort.
+
+5. Differences Between MD4 and MD5
+
+ The following are the differences between MD4 and MD5:
+
+ 1. A fourth round has been added.
+
+ 2. Each step now has a unique additive constant.
+
+ 3. The function g in round 2 was changed from (XY v XZ v YZ) to
+ (XZ v Y not(Z)) to make g less symmetric.
+
+ 4. Each step now adds in the result of the previous step. This
+ promotes a faster "avalanche effect".
+
+ 5. The order in which input words are accessed in rounds 2 and
+ 3 is changed, to make these patterns less like each other.
+
+ 6. The shift amounts in each round have been approximately
+ optimized, to yield a faster "avalanche effect." The shifts in
+ different rounds are distinct.
+
+
+
+
+
+
+
+
+
+
+Rivest [Page 6]
+
+RFC 1321 MD5 Message-Digest Algorithm April 1992
+
+
+References
+
+ [1] Rivest, R., "The MD4 Message Digest Algorithm", RFC 1320, MIT and
+ RSA Data Security, Inc., April 1992.
+
+ [2] Rivest, R., "The MD4 message digest algorithm", in A.J. Menezes
+ and S.A. Vanstone, editors, Advances in Cryptology - CRYPTO '90
+ Proceedings, pages 303-311, Springer-Verlag, 1991.
+
+ [3] CCITT Recommendation X.509 (1988), "The Directory -
+ Authentication Framework."
+
+APPENDIX A - Reference Implementation
+
+ This appendix contains the following files taken from RSAREF: A
+ Cryptographic Toolkit for Privacy-Enhanced Mail:
+
+ global.h -- global header file
+
+ md5.h -- header file for MD5
+
+ md5c.c -- source code for MD5
+
+ For more information on RSAREF, send email to <rsaref@rsa.com>.
+
+ The appendix also includes the following file:
+
+ mddriver.c -- test driver for MD2, MD4 and MD5
+
+ The driver compiles for MD5 by default but can compile for MD2 or MD4
+ if the symbol MD is defined on the C compiler command line as 2 or 4.
+
+ The implementation is portable and should work on many different
+ plaforms. However, it is not difficult to optimize the implementation
+ on particular platforms, an exercise left to the reader. For example,
+ on "little-endian" platforms where the lowest-addressed byte in a 32-
+ bit word is the least significant and there are no alignment
+ restrictions, the call to Decode in MD5Transform can be replaced with
+ a typecast.
+
+A.1 global.h
+
+/* GLOBAL.H - RSAREF types and constants
+ */
+
+/* PROTOTYPES should be set to one if and only if the compiler supports
+ function argument prototyping.
+The following makes PROTOTYPES default to 0 if it has not already
+
+
+
+Rivest [Page 7]
+
+RFC 1321 MD5 Message-Digest Algorithm April 1992
+
+
+ been defined with C compiler flags.
+ */
+#ifndef PROTOTYPES
+#define PROTOTYPES 0
+#endif
+
+/* POINTER defines a generic pointer type */
+typedef unsigned char *POINTER;
+
+/* UINT2 defines a two byte word */
+typedef unsigned short int UINT2;
+
+/* UINT4 defines a four byte word */
+typedef unsigned long int UINT4;
+
+/* PROTO_LIST is defined depending on how PROTOTYPES is defined above.
+If using PROTOTYPES, then PROTO_LIST returns the list, otherwise it
+ returns an empty list.
+ */
+#if PROTOTYPES
+#define PROTO_LIST(list) list
+#else
+#define PROTO_LIST(list) ()
+#endif
+
+A.2 md5.h
+
+/* MD5.H - header file for MD5C.C
+ */
+
+/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
+rights reserved.
+
+License to copy and use this software is granted provided that it
+is identified as the "RSA Data Security, Inc. MD5 Message-Digest
+Algorithm" in all material mentioning or referencing this software
+or this function.
+
+License is also granted to make and use derivative works provided
+that such works are identified as "derived from the RSA Data
+Security, Inc. MD5 Message-Digest Algorithm" in all material
+mentioning or referencing the derived work.
+
+RSA Data Security, Inc. makes no representations concerning either
+the merchantability of this software or the suitability of this
+software for any particular purpose. It is provided "as is"
+without express or implied warranty of any kind.
+
+
+
+
+Rivest [Page 8]
+
+RFC 1321 MD5 Message-Digest Algorithm April 1992
+
+
+These notices must be retained in any copies of any part of this
+documentation and/or software.
+ */
+
+/* MD5 context. */
+typedef struct {
+ UINT4 state[4]; /* state (ABCD) */
+ UINT4 count[2]; /* number of bits, modulo 2^64 (lsb first) */
+ unsigned char buffer[64]; /* input buffer */
+} MD5_CTX;
+
+void MD5Init PROTO_LIST ((MD5_CTX *));
+void MD5Update PROTO_LIST
+ ((MD5_CTX *, unsigned char *, unsigned int));
+void MD5Final PROTO_LIST ((unsigned char [16], MD5_CTX *));
+
+A.3 md5c.c
+
+/* MD5C.C - RSA Data Security, Inc., MD5 message-digest algorithm
+ */
+
+/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
+rights reserved.
+
+License to copy and use this software is granted provided that it
+is identified as the "RSA Data Security, Inc. MD5 Message-Digest
+Algorithm" in all material mentioning or referencing this software
+or this function.
+
+License is also granted to make and use derivative works provided
+that such works are identified as "derived from the RSA Data
+Security, Inc. MD5 Message-Digest Algorithm" in all material
+mentioning or referencing the derived work.
+
+RSA Data Security, Inc. makes no representations concerning either
+the merchantability of this software or the suitability of this
+software for any particular purpose. It is provided "as is"
+without express or implied warranty of any kind.
+
+These notices must be retained in any copies of any part of this
+documentation and/or software.
+ */
+
+#include "global.h"
+#include "md5.h"
+
+/* Constants for MD5Transform routine.
+ */
+
+
+
+Rivest [Page 9]
+
+RFC 1321 MD5 Message-Digest Algorithm April 1992
+
+
+#define S11 7
+#define S12 12
+#define S13 17
+#define S14 22
+#define S21 5
+#define S22 9
+#define S23 14
+#define S24 20
+#define S31 4
+#define S32 11
+#define S33 16
+#define S34 23
+#define S41 6
+#define S42 10
+#define S43 15
+#define S44 21
+
+static void MD5Transform PROTO_LIST ((UINT4 [4], unsigned char [64]));
+static void Encode PROTO_LIST
+ ((unsigned char *, UINT4 *, unsigned int));
+static void Decode PROTO_LIST
+ ((UINT4 *, unsigned char *, unsigned int));
+static void MD5_memcpy PROTO_LIST ((POINTER, POINTER, unsigned int));
+static void MD5_memset PROTO_LIST ((POINTER, int, unsigned int));
+
+static unsigned char PADDING[64] = {
+ 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+/* F, G, H and I are basic MD5 functions.
+ */
+#define F(x, y, z) (((x) & (y)) | ((~x) & (z)))
+#define G(x, y, z) (((x) & (z)) | ((y) & (~z)))
+#define H(x, y, z) ((x) ^ (y) ^ (z))
+#define I(x, y, z) ((y) ^ ((x) | (~z)))
+
+/* ROTATE_LEFT rotates x left n bits.
+ */
+#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n))))
+
+/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4.
+Rotation is separate from addition to prevent recomputation.
+ */
+#define FF(a, b, c, d, x, s, ac) { \
+ (a) += F ((b), (c), (d)) + (x) + (UINT4)(ac); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+
+
+
+Rivest [Page 10]
+
+RFC 1321 MD5 Message-Digest Algorithm April 1992
+
+
+ (a) += (b); \
+ }
+#define GG(a, b, c, d, x, s, ac) { \
+ (a) += G ((b), (c), (d)) + (x) + (UINT4)(ac); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ (a) += (b); \
+ }
+#define HH(a, b, c, d, x, s, ac) { \
+ (a) += H ((b), (c), (d)) + (x) + (UINT4)(ac); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ (a) += (b); \
+ }
+#define II(a, b, c, d, x, s, ac) { \
+ (a) += I ((b), (c), (d)) + (x) + (UINT4)(ac); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ (a) += (b); \
+ }
+
+/* MD5 initialization. Begins an MD5 operation, writing a new context.
+ */
+void MD5Init (context)
+MD5_CTX *context; /* context */
+{
+ context->count[0] = context->count[1] = 0;
+ /* Load magic initialization constants.
+*/
+ context->state[0] = 0x67452301;
+ context->state[1] = 0xefcdab89;
+ context->state[2] = 0x98badcfe;
+ context->state[3] = 0x10325476;
+}
+
+/* MD5 block update operation. Continues an MD5 message-digest
+ operation, processing another message block, and updating the
+ context.
+ */
+void MD5Update (context, input, inputLen)
+MD5_CTX *context; /* context */
+unsigned char *input; /* input block */
+unsigned int inputLen; /* length of input block */
+{
+ unsigned int i, index, partLen;
+
+ /* Compute number of bytes mod 64 */
+ index = (unsigned int)((context->count[0] >> 3) & 0x3F);
+
+ /* Update number of bits */
+ if ((context->count[0] += ((UINT4)inputLen << 3))
+
+
+
+Rivest [Page 11]
+
+RFC 1321 MD5 Message-Digest Algorithm April 1992
+
+
+ < ((UINT4)inputLen << 3))
+ context->count[1]++;
+ context->count[1] += ((UINT4)inputLen >> 29);
+
+ partLen = 64 - index;
+
+ /* Transform as many times as possible.
+*/
+ if (inputLen >= partLen) {
+ MD5_memcpy
+ ((POINTER)&context->buffer[index], (POINTER)input, partLen);
+ MD5Transform (context->state, context->buffer);
+
+ for (i = partLen; i + 63 < inputLen; i += 64)
+ MD5Transform (context->state, &input[i]);
+
+ index = 0;
+ }
+ else
+ i = 0;
+
+ /* Buffer remaining input */
+ MD5_memcpy
+ ((POINTER)&context->buffer[index], (POINTER)&input[i],
+ inputLen-i);
+}
+
+/* MD5 finalization. Ends an MD5 message-digest operation, writing the
+ the message digest and zeroizing the context.
+ */
+void MD5Final (digest, context)
+unsigned char digest[16]; /* message digest */
+MD5_CTX *context; /* context */
+{
+ unsigned char bits[8];
+ unsigned int index, padLen;
+
+ /* Save number of bits */
+ Encode (bits, context->count, 8);
+
+ /* Pad out to 56 mod 64.
+*/
+ index = (unsigned int)((context->count[0] >> 3) & 0x3f);
+ padLen = (index < 56) ? (56 - index) : (120 - index);
+ MD5Update (context, PADDING, padLen);
+
+ /* Append length (before padding) */
+ MD5Update (context, bits, 8);
+
+
+
+Rivest [Page 12]
+
+RFC 1321 MD5 Message-Digest Algorithm April 1992
+
+
+ /* Store state in digest */
+ Encode (digest, context->state, 16);
+
+ /* Zeroize sensitive information.
+*/
+ MD5_memset ((POINTER)context, 0, sizeof (*context));
+}
+
+/* MD5 basic transformation. Transforms state based on block.
+ */
+static void MD5Transform (state, block)
+UINT4 state[4];
+unsigned char block[64];
+{
+ UINT4 a = state[0], b = state[1], c = state[2], d = state[3], x[16];
+
+ Decode (x, block, 64);
+
+ /* Round 1 */
+ FF (a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */
+ FF (d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */
+ FF (c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */
+ FF (b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */
+ FF (a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */
+ FF (d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */
+ FF (c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */
+ FF (b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */
+ FF (a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */
+ FF (d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */
+ FF (c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */
+ FF (b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */
+ FF (a, b, c, d, x[12], S11, 0x6b901122); /* 13 */
+ FF (d, a, b, c, x[13], S12, 0xfd987193); /* 14 */
+ FF (c, d, a, b, x[14], S13, 0xa679438e); /* 15 */
+ FF (b, c, d, a, x[15], S14, 0x49b40821); /* 16 */
+
+ /* Round 2 */
+ GG (a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */
+ GG (d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */
+ GG (c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */
+ GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */
+ GG (a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */
+ GG (d, a, b, c, x[10], S22, 0x2441453); /* 22 */
+ GG (c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */
+ GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */
+ GG (a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */
+ GG (d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */
+ GG (c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */
+
+
+
+Rivest [Page 13]
+
+RFC 1321 MD5 Message-Digest Algorithm April 1992
+
+
+ GG (b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */
+ GG (a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */
+ GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */
+ GG (c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */
+ GG (b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */
+
+ /* Round 3 */
+ HH (a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */
+ HH (d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */
+ HH (c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */
+ HH (b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */
+ HH (a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */
+ HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */
+ HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */
+ HH (b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */
+ HH (a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */
+ HH (d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */
+ HH (c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */
+ HH (b, c, d, a, x[ 6], S34, 0x4881d05); /* 44 */
+ HH (a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */
+ HH (d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */
+ HH (c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */
+ HH (b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */
+
+ /* Round 4 */
+ II (a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */
+ II (d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */
+ II (c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */
+ II (b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */
+ II (a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */
+ II (d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */
+ II (c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */
+ II (b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */
+ II (a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */
+ II (d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */
+ II (c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */
+ II (b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */
+ II (a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */
+ II (d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */
+ II (c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */
+ II (b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */
+
+ state[0] += a;
+ state[1] += b;
+ state[2] += c;
+ state[3] += d;
+
+ /* Zeroize sensitive information.
+
+
+
+Rivest [Page 14]
+
+RFC 1321 MD5 Message-Digest Algorithm April 1992
+
+
+*/
+ MD5_memset ((POINTER)x, 0, sizeof (x));
+}
+
+/* Encodes input (UINT4) into output (unsigned char). Assumes len is
+ a multiple of 4.
+ */
+static void Encode (output, input, len)
+unsigned char *output;
+UINT4 *input;
+unsigned int len;
+{
+ unsigned int i, j;
+
+ for (i = 0, j = 0; j < len; i++, j += 4) {
+ output[j] = (unsigned char)(input[i] & 0xff);
+ output[j+1] = (unsigned char)((input[i] >> 8) & 0xff);
+ output[j+2] = (unsigned char)((input[i] >> 16) & 0xff);
+ output[j+3] = (unsigned char)((input[i] >> 24) & 0xff);
+ }
+}
+
+/* Decodes input (unsigned char) into output (UINT4). Assumes len is
+ a multiple of 4.
+ */
+static void Decode (output, input, len)
+UINT4 *output;
+unsigned char *input;
+unsigned int len;
+{
+ unsigned int i, j;
+
+ for (i = 0, j = 0; j < len; i++, j += 4)
+ output[i] = ((UINT4)input[j]) | (((UINT4)input[j+1]) << 8) |
+ (((UINT4)input[j+2]) << 16) | (((UINT4)input[j+3]) << 24);
+}
+
+/* Note: Replace "for loop" with standard memcpy if possible.
+ */
+
+static void MD5_memcpy (output, input, len)
+POINTER output;
+POINTER input;
+unsigned int len;
+{
+ unsigned int i;
+
+ for (i = 0; i < len; i++)
+
+
+
+Rivest [Page 15]
+
+RFC 1321 MD5 Message-Digest Algorithm April 1992
+
+
+ output[i] = input[i];
+}
+
+/* Note: Replace "for loop" with standard memset if possible.
+ */
+static void MD5_memset (output, value, len)
+POINTER output;
+int value;
+unsigned int len;
+{
+ unsigned int i;
+
+ for (i = 0; i < len; i++)
+ ((char *)output)[i] = (char)value;
+}
+
+A.4 mddriver.c
+
+/* MDDRIVER.C - test driver for MD2, MD4 and MD5
+ */
+
+/* Copyright (C) 1990-2, RSA Data Security, Inc. Created 1990. All
+rights reserved.
+
+RSA Data Security, Inc. makes no representations concerning either
+the merchantability of this software or the suitability of this
+software for any particular purpose. It is provided "as is"
+without express or implied warranty of any kind.
+
+These notices must be retained in any copies of any part of this
+documentation and/or software.
+ */
+
+/* The following makes MD default to MD5 if it has not already been
+ defined with C compiler flags.
+ */
+#ifndef MD
+#define MD MD5
+#endif
+
+#include <stdio.h>
+#include <time.h>
+#include <string.h>
+#include "global.h"
+#if MD == 2
+#include "md2.h"
+#endif
+#if MD == 4
+
+
+
+Rivest [Page 16]
+
+RFC 1321 MD5 Message-Digest Algorithm April 1992
+
+
+#include "md4.h"
+#endif
+#if MD == 5
+#include "md5.h"
+#endif
+
+/* Length of test block, number of test blocks.
+ */
+#define TEST_BLOCK_LEN 1000
+#define TEST_BLOCK_COUNT 1000
+
+static void MDString PROTO_LIST ((char *));
+static void MDTimeTrial PROTO_LIST ((void));
+static void MDTestSuite PROTO_LIST ((void));
+static void MDFile PROTO_LIST ((char *));
+static void MDFilter PROTO_LIST ((void));
+static void MDPrint PROTO_LIST ((unsigned char [16]));
+
+#if MD == 2
+#define MD_CTX MD2_CTX
+#define MDInit MD2Init
+#define MDUpdate MD2Update
+#define MDFinal MD2Final
+#endif
+#if MD == 4
+#define MD_CTX MD4_CTX
+#define MDInit MD4Init
+#define MDUpdate MD4Update
+#define MDFinal MD4Final
+#endif
+#if MD == 5
+#define MD_CTX MD5_CTX
+#define MDInit MD5Init
+#define MDUpdate MD5Update
+#define MDFinal MD5Final
+#endif
+
+/* Main driver.
+
+Arguments (may be any combination):
+ -sstring - digests string
+ -t - runs time trial
+ -x - runs test script
+ filename - digests file
+ (none) - digests standard input
+ */
+int main (argc, argv)
+int argc;
+
+
+
+Rivest [Page 17]
+
+RFC 1321 MD5 Message-Digest Algorithm April 1992
+
+
+char *argv[];
+{
+ int i;
+
+ if (argc > 1)
+ for (i = 1; i < argc; i++)
+ if (argv[i][0] == '-' && argv[i][1] == 's')
+ MDString (argv[i] + 2);
+ else if (strcmp (argv[i], "-t") == 0)
+ MDTimeTrial ();
+ else if (strcmp (argv[i], "-x") == 0)
+ MDTestSuite ();
+ else
+ MDFile (argv[i]);
+ else
+ MDFilter ();
+
+ return (0);
+}
+
+/* Digests a string and prints the result.
+ */
+static void MDString (string)
+char *string;
+{
+ MD_CTX context;
+ unsigned char digest[16];
+ unsigned int len = strlen (string);
+
+ MDInit (&context);
+ MDUpdate (&context, string, len);
+ MDFinal (digest, &context);
+
+ printf ("MD%d (\"%s\") = ", MD, string);
+ MDPrint (digest);
+ printf ("\n");
+}
+
+/* Measures the time to digest TEST_BLOCK_COUNT TEST_BLOCK_LEN-byte
+ blocks.
+ */
+static void MDTimeTrial ()
+{
+ MD_CTX context;
+ time_t endTime, startTime;
+ unsigned char block[TEST_BLOCK_LEN], digest[16];
+ unsigned int i;
+
+
+
+
+Rivest [Page 18]
+
+RFC 1321 MD5 Message-Digest Algorithm April 1992
+
+
+ printf
+ ("MD%d time trial. Digesting %d %d-byte blocks ...", MD,
+ TEST_BLOCK_LEN, TEST_BLOCK_COUNT);
+
+ /* Initialize block */
+ for (i = 0; i < TEST_BLOCK_LEN; i++)
+ block[i] = (unsigned char)(i & 0xff);
+
+ /* Start timer */
+ time (&startTime);
+
+ /* Digest blocks */
+ MDInit (&context);
+ for (i = 0; i < TEST_BLOCK_COUNT; i++)
+ MDUpdate (&context, block, TEST_BLOCK_LEN);
+ MDFinal (digest, &context);
+
+ /* Stop timer */
+ time (&endTime);
+
+ printf (" done\n");
+ printf ("Digest = ");
+ MDPrint (digest);
+ printf ("\nTime = %ld seconds\n", (long)(endTime-startTime));
+ printf
+ ("Speed = %ld bytes/second\n",
+ (long)TEST_BLOCK_LEN * (long)TEST_BLOCK_COUNT/(endTime-startTime));
+}
+
+/* Digests a reference suite of strings and prints the results.
+ */
+static void MDTestSuite ()
+{
+ printf ("MD%d test suite:\n", MD);
+
+ MDString ("");
+ MDString ("a");
+ MDString ("abc");
+ MDString ("message digest");
+ MDString ("abcdefghijklmnopqrstuvwxyz");
+ MDString
+ ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789");
+ MDString
+ ("1234567890123456789012345678901234567890\
+1234567890123456789012345678901234567890");
+}
+
+/* Digests a file and prints the result.
+
+
+
+Rivest [Page 19]
+
+RFC 1321 MD5 Message-Digest Algorithm April 1992
+
+
+ */
+static void MDFile (filename)
+char *filename;
+{
+ FILE *file;
+ MD_CTX context;
+ int len;
+ unsigned char buffer[1024], digest[16];
+
+ if ((file = fopen (filename, "rb")) == NULL)
+ printf ("%s can't be opened\n", filename);
+
+ else {
+ MDInit (&context);
+ while (len = fread (buffer, 1, 1024, file))
+ MDUpdate (&context, buffer, len);
+ MDFinal (digest, &context);
+
+ fclose (file);
+
+ printf ("MD%d (%s) = ", MD, filename);
+ MDPrint (digest);
+ printf ("\n");
+ }
+}
+
+/* Digests the standard input and prints the result.
+ */
+static void MDFilter ()
+{
+ MD_CTX context;
+ int len;
+ unsigned char buffer[16], digest[16];
+
+ MDInit (&context);
+ while (len = fread (buffer, 1, 16, stdin))
+ MDUpdate (&context, buffer, len);
+ MDFinal (digest, &context);
+
+ MDPrint (digest);
+ printf ("\n");
+}
+
+/* Prints a message digest in hexadecimal.
+ */
+static void MDPrint (digest)
+unsigned char digest[16];
+{
+
+
+
+Rivest [Page 20]
+
+RFC 1321 MD5 Message-Digest Algorithm April 1992
+
+
+ unsigned int i;
+
+ for (i = 0; i < 16; i++)
+ printf ("%02x", digest[i]);
+}
+
+A.5 Test suite
+
+ The MD5 test suite (driver option "-x") should print the following
+ results:
+
+MD5 test suite:
+MD5 ("") = d41d8cd98f00b204e9800998ecf8427e
+MD5 ("a") = 0cc175b9c0f1b6a831c399e269772661
+MD5 ("abc") = 900150983cd24fb0d6963f7d28e17f72
+MD5 ("message digest") = f96b697d7cb7938d525a2f31aaf161d0
+MD5 ("abcdefghijklmnopqrstuvwxyz") = c3fcd3d76192e4007dfb496cca67e13b
+MD5 ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789") =
+d174ab98d277d9f5a5611c2c9f419d9f
+MD5 ("123456789012345678901234567890123456789012345678901234567890123456
+78901234567890") = 57edf4a22be3c955ac49da2e2107b67a
+
+Security Considerations
+
+ The level of security discussed in this memo is considered to be
+ sufficient for implementing very high security hybrid digital-
+ signature schemes based on MD5 and a public-key cryptosystem.
+
+Author's Address
+
+ Ronald L. Rivest
+ Massachusetts Institute of Technology
+ Laboratory for Computer Science
+ NE43-324
+ 545 Technology Square
+ Cambridge, MA 02139-1986
+
+ Phone: (617) 253-5880
+ EMail: rivest@theory.lcs.mit.edu
+
+
+
+
+
+
+
+
+
+
+
+
+Rivest [Page 21]
+ \ No newline at end of file
diff --git a/standards/rfc2565.txt b/standards/rfc2565.txt
new file mode 100644
index 000000000..56511d478
--- /dev/null
+++ b/standards/rfc2565.txt
@@ -0,0 +1,2075 @@
+
+
+
+
+
+
+Network Working Group R. Herriot, Ed.
+Request for Comments: 2565 Xerox Corporation
+Category: Experimental S. Butler
+ Hewlett-Packard
+ P. Moore
+ Microsoft
+ R. Turner
+ Sharp Labs
+ April 1999
+
+
+ Internet Printing Protocol/1.0: Encoding and Transport
+
+Status of this Memo
+
+ This memo defines an Experimental Protocol for the Internet
+ community. It does not specify an Internet standard of any kind.
+ Discussion and suggestions for improvement are requested.
+ Distribution of this memo is unlimited.
+
+Copyright Notice
+
+ Copyright (C) The Internet Society (1999). All Rights Reserved.
+
+IESG Note
+
+ This document defines an Experimental protocol for the Internet
+ community. The IESG expects that a revised version of this protocol
+ will be published as Proposed Standard protocol. The Proposed
+ Standard, when published, is expected to change from the protocol
+ defined in this memo. In particular, it is expected that the
+ standards-track version of the protocol will incorporate strong
+ authentication and privacy features, and that an "ipp:" URL type will
+ be defined which supports those security measures. Other changes to
+ the protocol are also possible. Implementors are warned that future
+ versions of this protocol may not interoperate with the version of
+ IPP defined in this document, or if they do interoperate, that some
+ protocol features may not be available.
+
+ The IESG encourages experimentation with this protocol, especially in
+ combination with Transport Layer Security (TLS) [RFC 2246], to help
+ determine how TLS may effectively be used as a security layer for
+ IPP.
+
+
+
+
+
+
+
+
+Herriot, et al. Experimental [Page 1]
+
+RFC 2565 IPP/1.0: Encoding and Transport April 1999
+
+
+Abstract
+
+ This document is one of a set of documents, which together describe
+ all aspects of a new Internet Printing Protocol (IPP). IPP is an
+ application level protocol that can be used for distributed printing
+ using Internet tools and technologies. This document defines the
+ rules for encoding IPP operations and IPP attributes into a new
+ Internet mime media type called "application/ipp". This document
+ also defines the rules for transporting over HTTP a message body
+ whose Content-Type is "application/ipp".
+
+ The full set of IPP documents includes:
+
+ Design Goals for an Internet Printing Protocol [RFC2567]
+ Rationale for the Structure and Model and Protocol for the
+ Internet Printing Protocol [RFC2568]
+ Internet Printing Protocol/1.0: Model and Semantics [RFC2566]
+ Internet Printing Protocol/1.0: Encoding and Transport (this
+ document)
+ Internet Printing Protocol/1.0: Implementer's Guide [ipp-iig]
+ Mapping between LPD and IPP Protocols [RFC2569]
+
+ The document, "Design Goals for an Internet Printing Protocol", takes
+ a broad look at distributed printing functionality, and it enumerates
+ real-life scenarios that help to clarify the features that need to be
+ included in a printing protocol for the Internet. It identifies
+ requirements for three types of users: end users, operators, and
+ administrators. It calls out a subset of end user requirements that
+ are satisfied in IPP/1.0. Operator and administrator requirements are
+ out of scope for version 1.0.
+
+ The document, "Rationale for the Structure and Model and Protocol for
+ the Internet Printing Protocol", describes IPP from a high level
+ view, defines a roadmap for the various documents that form the suite
+ of IPP specifications, and gives background and rationale for the
+ IETF working group's major decisions.
+
+ The document, "Internet Printing Protocol/1.0: Model and Semantics",
+ describes a simplified model with abstract objects, their attributes,
+ and their operations that are independent of encoding and transport.
+ It introduces a Printer and a Job object. The Job object optionally
+ supports multiple documents per Job. It also addresses security,
+ internationalization, and directory issues.
+
+ This document "Internet Printing Protocol/1.0: Implementer's Guide",
+ gives advice to implementers of IPP clients and IPP objects.
+
+
+
+
+
+Herriot, et al. Experimental [Page 2]
+
+RFC 2565 IPP/1.0: Encoding and Transport April 1999
+
+
+ The document "Mapping between LPD and IPP Protocols" gives some
+ advice to implementers of gateways between IPP and LPD (Line Printer
+ Daemon) implementations.
+
+Table of Contents
+
+ 1. Introduction.....................................................4
+ 2. Conformance Terminology..........................................4
+ 3. Encoding of the Operation Layer.................................4
+ 3.1 Picture of the Encoding.....................................5
+ 3.2 Syntax of Encoding..........................................7
+ 3.3 Version-number..............................................9
+ 3.4 Operation-id................................................9
+ 3.5 Status-code.................................................9
+ 3.6 Request-id..................................................9
+ 3.7 Tags.......................................................10
+ 3.7.1 Delimiter Tags.........................................10
+ 3.7.2 Value Tags.............................................11
+ 3.8 Name-Length................................................13
+ 3.9 (Attribute) Name...........................................13
+ 3.10 Value Length...............................................16
+ 3.11 (Attribute) Value..........................................16
+ 3.12 Data.......................................................18
+ 4. Encoding of Transport Layer.....................................18
+ 5. Security Considerations.........................................19
+ 5.1 Using IPP with SSL3........................................19
+ 6. References......................................................20
+ 7. Authors' Addresses..............................................22
+ 8. Other Participants:.............................................24
+ 9. Appendix A: Protocol Examples...................................25
+ 9.1 Print-Job Request..........................................25
+ 9.2 Print-Job Response (successful)............................26
+ 9.3 Print-Job Response (failure)...............................27
+ 9.4 Print-Job Response (success with attributes ignored).......28
+ 9.5 Print-URI Request..........................................30
+ 9.6 Create-Job Request.........................................31
+ 9.7 Get-Jobs Request...........................................31
+ 9.8 Get-Jobs Response..........................................32
+ 10. Appendix C: Registration of MIME Media Type Information for
+ "application/ipp"..............................................35
+ 11. Full Copyright Statement.......................................37
+
+
+
+
+
+
+
+
+
+
+Herriot, et al. Experimental [Page 3]
+
+RFC 2565 IPP/1.0: Encoding and Transport April 1999
+
+
+1. Introduction
+
+ This document contains the rules for encoding IPP operations and
+ describes two layers: the transport layer and the operation layer.
+
+ The transport layer consists of an HTTP/1.1 request or response. RFC
+ 2068 [RFC2068] describes HTTP/1.1. This document specifies the HTTP
+ headers that an IPP implementation supports.
+
+ The operation layer consists of a message body in an HTTP request or
+ response. The document "Internet Printing Protocol/1.0: Model and
+ Semantics" [RFC2566] defines the semantics of such a message body and
+ the supported values. This document specifies the encoding of an IPP
+ operation. The aforementioned document [RFC2566] is henceforth
+ referred to as the "IPP model document"
+
+2. Conformance Terminology
+
+ The key words "MUST", "MUST NOT", "REQUIRED", "SHOULD", "SHOULD NOT",
+ "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be
+ interpreted as described in RFC 2119 [RFC2119].
+
+3. Encoding of the Operation Layer
+
+ The operation layer MUST contain a single operation request or
+ operation response. Each request or response consists of a sequence
+ of values and attribute groups. Attribute groups consist of a
+ sequence of attributes each of which is a name and value. Names and
+ values are ultimately sequences of octets
+
+ The encoding consists of octets as the most primitive type. There are
+ several types built from octets, but three important types are
+ integers, character strings and octet strings, on which most other
+ data types are built. Every character string in this encoding MUST be
+ a sequence of characters where the characters are associated with
+ some charset and some natural language. A character string MUST be in
+ "reading order" with the first character in the value (according to
+ reading order) being the first character in the encoding. A character
+ string whose associated charset is US-ASCII whose associated natural
+ language is US English is henceforth called a US-ASCII-STRING. A
+ character string whose associated charset and natural language are
+ specified in a request or response as described in the model document
+ is henceforth called a LOCALIZED-STRING. An octet string MUST be in
+ "IPP model document order" with the first octet in the value
+ (according to the IPP model document order) being the first octet in
+ the encoding Every integer in this encoding MUST be encoded as a
+ signed integer using two's-complement binary encoding with big-endian
+ format (also known as "network order" and "most significant byte
+
+
+
+Herriot, et al. Experimental [Page 4]
+
+RFC 2565 IPP/1.0: Encoding and Transport April 1999
+
+
+ first"). The number of octets for an integer MUST be 1, 2 or 4,
+ depending on usage in the protocol. Such one-octet integers,
+ henceforth called SIGNED-BYTE, are used for the version-number and
+ tag fields. Such two-byte integers, henceforth called SIGNED-SHORT
+ are used for the operation-id, status-code and length fields. Four
+ byte integers, henceforth called SIGNED-INTEGER, are used for values
+ fields and the sequence number.
+
+ The following two sections present the operation layer in two ways
+
+ - informally through pictures and description
+ - formally through Augmented Backus-Naur Form (ABNF), as specified
+ by RFC 2234 [RFC2234]
+
+3.1 Picture of the Encoding
+
+ The encoding for an operation request or response consists of:
+
+ -----------------------------------------------
+ | version-number | 2 bytes - required
+ -----------------------------------------------
+ | operation-id (request) |
+ | or | 2 bytes - required
+ | status-code (response) |
+ -----------------------------------------------
+ | request-id | 4 bytes - required
+ -----------------------------------------------------------
+ | xxx-attributes-tag | 1 byte |
+ ----------------------------------------------- |-0 or more
+ | xxx-attribute-sequence | n bytes |
+ -----------------------------------------------------------
+ | end-of-attributes-tag | 1 byte - required
+ -----------------------------------------------
+ | data | q bytes - optional
+ -----------------------------------------------
+
+ The xxx-attributes-tag and xxx-attribute-sequence represents four
+ different values of "xxx", namely, operation, job, printer and
+ unsupported. The xxx-attributes-tag and an xxx-attribute-sequence
+ represent attribute groups in the model document. The xxx-
+ attributes-tag identifies the attribute group and the xxx-attribute-
+ sequence contains the attributes.
+
+ The expected sequence of xxx-attributes-tag and xxx-attribute-
+ sequence is specified in the IPP model document for each operation
+ request and operation response.
+
+
+
+
+
+Herriot, et al. Experimental [Page 5]
+
+RFC 2565 IPP/1.0: Encoding and Transport April 1999
+
+
+ A request or response SHOULD contain each xxx-attributes-tag defined
+ for that request or response even if there are no attributes except
+ for the unsupported-attributes-tag which SHOULD be present only if
+ the unsupported-attribute-sequence is non-empty. A receiver of a
+ request MUST be able to process as equivalent empty attribute groups:
+
+ a) an xxx-attributes-tag with an empty xxx-attribute-sequence,
+ b) an expected but missing xxx-attributes-tag.
+
+ The data is omitted from some operations, but the end-of-attributes-
+ tag is present even when the data is omitted. Note, the xxx-
+ attributes-tags and end-of-attributes-tag are called 'delimiter-
+ tags'. Note: the xxx-attribute-sequence, shown above may consist of 0
+ bytes, according to the rule below.
+
+ An xxx-attributes-sequence consists of zero or more compound-
+ attributes.
+
+ -----------------------------------------------
+ | compound-attribute | s bytes - 0 or more
+ -----------------------------------------------
+
+ A compound-attribute consists of an attribute with a single value
+ followed by zero or more additional values.
+
+ Note: a 'compound-attribute' represents a single attribute in the
+ model document. The 'additional value' syntax is for attributes with
+ 2 or more values.
+
+ Each attribute consists of:
+
+ -----------------------------------------------
+ | value-tag | 1 byte
+ -----------------------------------------------
+ | name-length (value is u) | 2 bytes
+ -----------------------------------------------
+ | name | u bytes
+ -----------------------------------------------
+ | value-length (value is v) | 2 bytes
+ -----------------------------------------------
+ | value | v bytes
+ -----------------------------------------------
+
+
+
+
+
+
+
+
+
+Herriot, et al. Experimental [Page 6]
+
+RFC 2565 IPP/1.0: Encoding and Transport April 1999
+
+
+ An additional value consists of:
+
+ -----------------------------------------------------------
+ | value-tag | 1 byte |
+ ----------------------------------------------- |
+ | name-length (value is 0x0000) | 2 bytes |
+ ----------------------------------------------- |-0 or more
+ | value-length (value is w) | 2 bytes |
+ ----------------------------------------------- |
+ | value | w bytes |
+ -----------------------------------------------------------
+
+ Note: an additional value is like an attribute whose name-length is 0.
+
+ From the standpoint of a parsing loop, the encoding consists of:
+
+ -----------------------------------------------
+ | version-number | 2 bytes - required
+ -----------------------------------------------
+ | operation-id (request) |
+ | or | 2 bytes - required
+ | status-code (response) |
+ -----------------------------------------------
+ | request-id | 4 bytes - required
+ -----------------------------------------------------------
+ | tag (delimiter-tag or value-tag) | 1 byte |
+ ----------------------------------------------- |-0 or more
+ | empty or rest of attribute | x bytes |
+ -----------------------------------------------------------
+ | end-of-attributes-tag | 2 bytes - required
+ -----------------------------------------------
+ | data | y bytes - optional
+ -----------------------------------------------
+
+ The value of the tag determines whether the bytes following the
+ tag are:
+
+ - attributes
+ - data
+ - the remainder of a single attribute where the tag specifies the
+ type of the value.
+
+3.2 Syntax of Encoding
+
+ The syntax below is ABNF [RFC2234] except 'strings of literals' MUST
+ be case sensitive. For example 'a' means lower case 'a' and not
+ upper case 'A'. In addition, SIGNED-BYTE and SIGNED-SHORT fields
+ are represented as '%x' values which show their range of values.
+
+
+
+Herriot, et al. Experimental [Page 7]
+
+RFC 2565 IPP/1.0: Encoding and Transport April 1999
+
+
+ ipp-message = ipp-request / ipp-response
+ ipp-request = version-number operation-id request-id
+ *(xxx-attributes-tag xxx-attribute-sequence)
+ end-of-attributes-tag data
+ ipp-response = version-number status-code request-id
+ *(xxx-attributes-tag xxx-attribute-sequence)
+ end-of-attributes-tag data
+ xxx-attribute-sequence = *compound-attribute
+
+ xxx-attributes-tag = operation-attributes-tag / job-attributes-tag /
+ printer-attributes-tag / unsupported-attributes-tag
+
+ version-number = major-version-number minor-version-number
+ major-version-number = SIGNED-BYTE ; initially %d1
+ minor-version-number = SIGNED-BYTE ; initially %d0
+
+ operation-id = SIGNED-SHORT ; mapping from model defined below
+ status-code = SIGNED-SHORT ; mapping from model defined below
+ request-id = SIGNED-INTEGER ; whose value is > 0
+
+ compound-attribute = attribute *additional-values
+ attribute = value-tag name-length name value-length value
+ additional-values = value-tag zero-name-length value-length value
+
+ name-length = SIGNED-SHORT ; number of octets of 'name'
+ name = LALPHA *( LALPHA / DIGIT / "-" / "_" / "." )
+ value-length = SIGNED-SHORT ; number of octets of 'value'
+ value = OCTET-STRING
+
+ data = OCTET-STRING
+
+ zero-name-length = %x00.00 ; name-length of 0
+ operation-attributes-tag = %x01 ; tag of 1
+ job-attributes-tag = %x02 ; tag of 2
+ printer-attributes-tag = %x04 ; tag of 4
+ unsupported-attributes-tag = %x05 ; tag of 5
+ end-of-attributes-tag = %x03 ; tag of 3
+ value-tag = %x10-FF
+
+ SIGNED-BYTE = BYTE
+ SIGNED-SHORT = 2BYTE
+ SIGNED-INTEGER = 4BYTE
+ DIGIT = %x30-39 ; "0" to "9"
+ LALPHA = %x61-7A ; "a" to "z"
+ BYTE = %x00-FF
+ OCTET-STRING = *BYTE
+
+
+
+
+
+Herriot, et al. Experimental [Page 8]
+
+RFC 2565 IPP/1.0: Encoding and Transport April 1999
+
+
+ The syntax allows an xxx-attributes-tag to be present when the xxx-
+ attribute-sequence that follows is empty. The syntax is defined this
+ way to allow for the response of Get-Jobs where no attributes are
+ returned for some job-objects. Although it is RECOMMENDED that the
+ sender not send an xxx-attributes-tag if there are no attributes
+ (except in the Get-Jobs response just mentioned), the receiver MUST
+ be able to decode such syntax.
+
+3.3 Version-number
+
+ The version-number MUST consist of a major and minor version-number,
+ each of which MUST be represented by a SIGNED-BYTE. The protocol
+ described in this document MUST have a major version-number of 1
+ (0x01) and a minor version-number of 0 (0x00). The ABNF for these
+ two bytes MUST be %x01.00.
+
+3.4 Operation-id
+
+ Operation-ids are defined as enums in the model document. An
+ operation-ids enum value MUST be encoded as a SIGNED-SHORT.
+
+ Note: the values 0x4000 to 0xFFFF are reserved for private
+ extensions.
+
+3.5 Status-code
+
+ Status-codes are defined as enums in the model document. A status-
+ code enum value MUST be encoded as a SIGNED-SHORT.
+
+ The status-code is an operation attribute in the model document. In
+ the protocol, the status-code is in a special position, outside of
+ the operation attributes.
+
+ If an IPP status-code is returned, then the HTTP Status-Code MUST be
+ 200 (successful-ok). With any other HTTP Status-Code value, the HTTP
+ response MUST NOT contain an IPP message-body, and thus no IPP
+ status-code is returned.
+
+3.6 Request-id
+
+ The request-id allows a client to match a response with a request.
+ This mechanism is unnecessary in HTTP, but may be useful when
+ application/ipp entity bodies are used in another context.
+
+ The request-id in a response MUST be the value of the request-id
+ received in the corresponding request. A client can set the
+ request-id in each request to a unique value or a constant value,
+ such as 1, depending on what the client does with the request-id
+
+
+
+Herriot, et al. Experimental [Page 9]
+
+RFC 2565 IPP/1.0: Encoding and Transport April 1999
+
+
+ returned in the response. The value of the request-id MUST be greater
+ than zero.
+
+3.7 Tags
+
+ There are two kinds of tags:
+
+ - delimiter tags: delimit major sections of the protocol, namely
+ attributes and data
+ - value tags: specify the type of each attribute value
+
+3.7.1 Delimiter Tags
+
+ The following table specifies the values for the delimiter tags:
+
+ Tag Value (Hex) Delimiter
+
+ 0x00 reserved
+ 0x01 operation-attributes-tag
+ 0x02 job-attributes-tag
+ 0x03 end-of-attributes-tag
+ 0x04 printer-attributes-tag
+ 0x05 unsupported-attributes-tag
+ 0x06-0x0e reserved for future delimiters
+ 0x0F reserved for future chunking-end-of-attributes-
+ tag
+
+ When an xxx-attributes-tag occurs in the protocol, it MUST mean that
+ zero or more following attributes up to the next delimiter tag are
+ attributes belonging to group xxx as defined in the model document,
+ where xxx is operation, job, printer, unsupported.
+
+ Doing substitution for xxx in the above paragraph, this means the
+ following. When an operation-attributes-tag occurs in the protocol,
+ it MUST mean that the zero or more following attributes up to the
+ next delimiter tag are operation attributes as defined in the model
+ document. When an job-attributes-tag occurs in the protocol, it MUST
+ mean that the zero or more following attributes up to the next
+ delimiter tag are job attributes or job template attributes as
+ defined in the model document. When a printer-attributes-tag occurs
+ in the protocol, it MUST mean that the zero or more following
+ attributes up to the next delimiter tag are printer attributes as
+ defined in the model document. When an unsupported-attributes-tag
+ occurs in the protocol, it MUST mean that the zero or more following
+ attributes up to the next delimiter tag are unsupported attributes as
+ defined in the model document.
+
+
+
+
+
+Herriot, et al. Experimental [Page 10]
+
+RFC 2565 IPP/1.0: Encoding and Transport April 1999
+
+
+ The operation-attributes-tag and end-of-attributes-tag MUST each
+ occur exactly once in an operation. The operation-attributes-tag MUST
+ be the first tag delimiter, and the end-of-attributes-tag MUST be the
+ last tag delimiter. If the operation has a document-content group,
+ the document data in that group MUST follow the end-of-attributes-
+ tag.
+
+ Each of the other three xxx-attributes-tags defined above is
+ OPTIONAL in an operation and each MUST occur at most once in an
+ operation, except for job-attributes-tag in a Get-Jobs response which
+ may occur zero or more times.
+
+ The order and presence of delimiter tags for each operation request
+ and each operation response MUST be that defined in the model
+ document. For further details, see section 3.9 "(Attribute) Name" and
+ section 9 "Appendix A: Protocol Examples".
+
+ A Printer MUST treat the reserved delimiter tags differently from
+ reserved value tags so that the Printer knows that there is an entire
+ attribute group that it doesn't understand as opposed to a single
+ value that it doesn't understand.
+
+3.7.2 Value Tags
+
+ The remaining tables show values for the value-tag, which is the
+ first octet of an attribute. The value-tag specifies the type of the
+ value of the attribute. The following table specifies the "out-of-
+ band" values for the value-tag.
+
+ Tag Value (Hex) Meaning
+
+ 0x10 unsupported
+ 0x11 reserved for future 'default'
+ 0x12 unknown
+ 0x13 no-value
+
+ Tag Value (Hex) Meaning
+
+ 0x14-0x1F reserved for future "out-of-band" values.
+
+ The "unsupported" value MUST be used in the attribute-sequence of an
+ error response for those attributes which the printer does not
+ support. The "default" value is reserved for future use of setting
+ value back to their default value. The "unknown" value is used for
+ the value of a supported attribute when its value is temporarily
+ unknown. The "no-value" value is used for a supported attribute to
+ which
+
+
+
+
+Herriot, et al. Experimental [Page 11]
+
+RFC 2565 IPP/1.0: Encoding and Transport April 1999
+
+
+ no value has been assigned, e.g. "job-k-octets-supported" has no
+ value if an implementation supports this attribute, but an
+ administrator has not configured the printer to have a limit.
+
+ The following table specifies the integer values for the value-tag:
+
+ Tag Value (Hex) Meaning
+
+ 0x20 reserved
+ 0x21 integer
+ 0x22 boolean
+ 0x23 enum
+ 0x24-0x2F reserved for future integer types
+
+ NOTE: 0x20 is reserved for "generic integer" if it should ever be
+ needed.
+
+ The following table specifies the octetString values for the value-
+ tag:
+
+ Tag Value (Hex) Meaning
+
+ 0x30 octetString with an unspecified format
+ 0x31 dateTime
+ 0x32 resolution
+ 0x33 rangeOfInteger
+ 0x34 reserved for collection (in the future)
+ 0x35 textWithLanguage
+ 0x36 nameWithLanguage
+ 0x37-0x3F reserved for future octetString types
+
+ The following table specifies the character-string values for the
+ value-tag:
+
+ Tag Value (Hex) Meaning
+
+ 0x40 reserved
+ 0x41 textWithoutLanguage
+ 0x42 nameWithoutLanguage
+ 0x43 reserved
+ 0x44 keyword
+ 0x45 uri
+ 0x46 uriScheme
+ 0x47 charset
+ 0x48 naturalLanguage
+
+
+
+
+
+
+Herriot, et al. Experimental [Page 12]
+
+RFC 2565 IPP/1.0: Encoding and Transport April 1999
+
+
+ Tag Value (Hex) Meaning
+
+ 0x49 mimeMediaType
+ 0x4A-0x5F reserved for future character string types
+
+ NOTE: 0x40 is reserved for "generic character-string" if it should
+ ever be needed.
+
+ NOTE: an attribute value always has a type, which is explicitly
+ specified by its tag; one such tag value is "nameWithoutLanguage".
+ An attribute's name has an implicit type, which is keyword.
+
+ The values 0x60-0xFF are reserved for future types. There are no
+ values allocated for private extensions. A new type MUST be
+ registered via the type 2 registration process [RFC2566].
+
+ The tag 0x7F is reserved for extending types beyond the 255 values
+ available with a single byte. A tag value of 0x7F MUST signify that
+ the first 4 bytes of the value field are interpreted as the tag
+ value. Note, this future extension doesn't affect parsers that are
+ unaware of this special tag. The tag is like any other unknown tag,
+ and the value length specifies the length of a value which contains a
+ value that the parser treats atomically. All these 4 byte tag values
+ are currently unallocated except that the values 0x40000000-
+ 0x7FFFFFFF are reserved for experimental use.
+
+3.8 Name-Length
+
+ The name-length field MUST consist of a SIGNED-SHORT. This field MUST
+ specify the number of octets in the name field which follows the
+ name-length field, excluding the two bytes of the name-length field.
+
+ If a name-length field has a value of zero, the following name field
+ MUST be empty, and the following value MUST be treated as an
+ additional value for the preceding attribute. Within an attribute-
+ sequence, if two attributes have the same name, the first occurrence
+ MUST be ignored. The zero-length name is the only mechanism for
+ multi-valued attributes.
+
+3.9 (Attribute) Name
+
+ Some operation elements are called parameters in the model document
+ [RFC2566]. They MUST be encoded in a special position and they MUST
+ NOT appear as an operation attributes. These parameters are:
+
+ - "version-number": The parameter named "version-number" in the
+ IPP model document MUST become the "version-number" field in the
+ operation layer request or response.
+
+
+
+Herriot, et al. Experimental [Page 13]
+
+RFC 2565 IPP/1.0: Encoding and Transport April 1999
+
+
+ - "operation-id": The parameter named "operation-id" in the IPP
+ model document MUST become the "operation-id" field in the
+ operation layer request.
+ - "status-code": The parameter named "status-code" in the IPP
+ model document MUST become the "status-code" field in the
+ operation layer response.
+ - "request-id": The parameter named "request-id" in the IPP model
+ document MUST become the "request-id" field in the operation
+ layer request or response.
+
+ All Printer and Job objects are identified by a Uniform Resource
+ Identifier (URI) [RFC2396] so that they can be persistently and
+ unambiguously referenced. The notion of a URI is a useful concept,
+ however, until the notion of URI is more stable (i.e., defined more
+ completely and deployed more widely), it is expected that the URIs
+ used for IPP objects will actually be URLs [RFC1738] [RFC1808].
+ Since every URL is a specialized form of a URI, even though the more
+ generic term URI is used throughout the rest of this document, its
+ usage is intended to cover the more specific notion of URL as well.
+
+ Some operation elements are encoded twice, once as the request-URI on
+ the HTTP Request-Line and a second time as a REQUIRED operation
+ attribute in the application/ipp entity. These attributes are the
+ target URI for the operation:
+
+ - "printer-uri": When the target is a printer and the transport is
+ HTTP or HTTPS (for SSL3 [ssl]), the target printer-uri defined
+ in each operation in the IPP model document MUST be an operation
+ attribute called "printer-uri" and it MUST also be specified
+ outside of the operation layer as the request-URI on the
+ Request-Line at the HTTP level.
+ - "job-uri": When the target is a job and the transport is HTTP or
+ HTTPS (for SSL3), the target job-uri of each operation in the
+ IPP model document MUST be an operation attribute called "job-
+ uri" and it MUST also be specified outside of the operation
+ layer as the request-URI on the Request-Line at the HTTP level.
+
+ Note: The target URI is included twice in an operation referencing
+ the same IPP object, but the two URIs NEED NOT be literally
+ identical. One can be a relative URI and the other can be an absolute
+ URI. HTTP/1.1 allows clients to generate and send a relative URI
+ rather than an absolute URI. A relative URI identifies a resource
+ with the scope of the HTTP server, but does not include scheme, host
+ or port. The following statements characterize how URLs should be
+ used in the mapping of IPP onto HTTP/1.1:
+
+
+
+
+
+
+Herriot, et al. Experimental [Page 14]
+
+RFC 2565 IPP/1.0: Encoding and Transport April 1999
+
+
+ 1. Although potentially redundant, a client MUST supply the target
+ of the operation both as an operation attribute and as a URI at
+ the HTTP layer. The rationale for this decision is to maintain
+ a consistent set of rules for mapping application/ipp to
+ possibly many communication layers, even where URLs are not
+ used as the addressing mechanism in the transport layer.
+ 2. Even though these two URLs might not be literally identical
+ (one being relative and the other being absolute), they MUST
+ both reference the same IPP object.
+ 3. The URI in the HTTP layer is either relative or absolute and is
+ used by the HTTP server to route the HTTP request to the
+ correct resource relative to that HTTP server. The HTTP server
+ need not be aware of the URI within the operation request.
+ 4. Once the HTTP server resource begins to process the HTTP
+ request, it might get the reference to the appropriate IPP
+ Printer object from either the HTTP URI (using to the context
+ of the HTTP server for relative URLs) or from the URI within
+ the operation request; the choice is up to the implementation.
+ 5. HTTP URIs can be relative or absolute, but the target URI in
+ the operation MUST be an absolute URI.
+
+ The model document arranges the remaining attributes into groups for
+ each operation request and response. Each such group MUST be
+ represented in the protocol by an xxx-attribute-sequence preceded by
+ the appropriate xxx-attributes-tag (See the table below and section 9
+ "Appendix A: Protocol Examples"). In addition, the order of these
+ xxx-attributes-tags and xxx-attribute-sequences in the protocol MUST
+ be the same as in the model document, but the order of attributes
+ within each xxx-attribute-sequence MUST be unspecified. The table
+ below maps the model document group name to xxx-attributes-sequence:
+
+ Model Document Group xxx-attributes-sequence
+
+ Operation Attributes operations-attributes-sequence
+ Job Template Attributes job-attributes-sequence
+ Job Object Attributes job-attributes-sequence
+ Unsupported Attributes unsupported-attributes-sequence
+ Requested Attributes job-attributes-sequence
+ Get-Job-Attributes)
+ Requested Attributes printer-attributes-sequence
+ Get-Printer-Attributes)
+ Document Content in a special position as described
+ above
+
+ If an operation contains attributes from more than one job object
+ (e.g. Get-Jobs response), the attributes from each job object MUST
+ be in a separate job-attribute-sequence, such that the attributes
+
+
+
+
+Herriot, et al. Experimental [Page 15]
+
+RFC 2565 IPP/1.0: Encoding and Transport April 1999
+
+
+ from the ith job object are in the ith job-attribute-sequence. See
+ Section 9 "Appendix A: Protocol Examples" for table showing the
+ application of the rules above.
+
+3.10 Value Length
+
+ Each attribute value MUST be preceded by a SIGNED-SHORT, which MUST
+ specify the number of octets in the value which follows this length,
+ exclusive of the two bytes specifying the length.
+
+ For any of the types represented by binary signed integers, the
+ sender MUST encode the value in exactly four octets.
+
+ For any of the types represented by character-strings, the sender
+ MUST encode the value with all the characters of the string and
+ without any padding characters.
+
+ If a value-tag contains an "out-of-band" value, such as
+ "unsupported", the value-length MUST be 0 and the value empty. The
+ value has no meaning when the value-tag has an "out-of-band" value.
+ If a client receives a response with a nonzero value-length in this
+ case, it MUST ignore the value field. If a printer receives a request
+ with a nonzero value-length in this case, it MUST reject the request.
+
+3.11 (Attribute) Value
+
+ The syntax types and most of the details of their representation are
+ defined in the IPP model document. The table below augments the
+ information in the model document, and defines the syntax types from
+ the model document in terms of the 5 basic types defined in section 3
+ "Encoding of the Operation Layer". The 5 types are US-ASCII-STRING,
+ LOCALIZED-STRING, SIGNED-INTEGER, SIGNED-SHORT, SIGNED-BYTE, and
+ OCTET-STRING.
+
+Syntax of Attribute Encoding
+Value
+
+textWithoutLanguage, LOCALIZED-STRING.
+nameWithoutLanguage
+
+textWithLanguage OCTET_STRING consisting of 4 fields:
+ a) a SIGNED-SHORT which is the number of octets
+ in the following field
+ b) a value of type natural-language,
+ c) a SIGNED-SHORT which is the number of octets
+ in the following field,
+ d) a value of type textWithoutLanguage.
+
+
+
+
+Herriot, et al. Experimental [Page 16]
+
+RFC 2565 IPP/1.0: Encoding and Transport April 1999
+
+
+ The length of a textWithLanguage value MUST be 4
+ + the value of field a + the value of field c.
+
+nameWithLanguage OCTET_STRING consisting of 4 fields:
+ a) a SIGNED-SHORT which is the number of octets
+ in the following field
+ b) a value of type natural-language,
+ c) a SIGNED-SHORT which is the number of octets
+ in the following field
+ d) a value of type nameWithoutLanguage.
+
+ The length of a nameWithLanguage value MUST be 4
+ + the value of field a + the value of field c.
+
+charset, US-ASCII-STRING.
+naturalLanguage,
+mimeMediaType,
+keyword, uri, and
+uriScheme
+
+boolean SIGNED-BYTE where 0x00 is 'false' and 0x01 is
+ 'true'.
+
+Syntax of Attribute Encoding
+Value
+
+
+integer and enum a SIGNED-INTEGER.
+
+dateTime OCTET-STRING consisting of eleven octets whose
+ contents are defined by "DateAndTime" in RFC
+ 2579 [RFC2579].
+
+resolution OCTET_STRING consisting of nine octets of 2
+ SIGNED-INTEGERs followed by a SIGNED-BYTE. The
+ first SIGNED-INTEGER contains the value of cross
+ feed direction resolution. The second SIGNED-
+ INTEGER contains the value of feed direction
+ resolution. The SIGNED-BYTE contains the units
+ value.
+
+rangeOfInteger Eight octets consisting of 2 SIGNED-INTEGERs.
+ The first SIGNED-INTEGER contains the lower
+ bound and the second SIGNED-INTEGER contains the
+ upper bound.
+
+
+
+
+
+
+Herriot, et al. Experimental [Page 17]
+
+RFC 2565 IPP/1.0: Encoding and Transport April 1999
+
+
+1setOf X Encoding according to the rules for an attribute
+ with more than 1 value. Each value X is encoded
+ according to the rules for encoding its type.
+
+octetString OCTET-STRING
+
+ The type of the value in the model document determines the encoding
+ in the value and the value of the value-tag.
+
+3.12 Data
+
+ The data part MUST include any data required by the operation
+
+4. Encoding of Transport Layer
+
+ HTTP/1.1 [RFC2068] is the transport layer for this protocol.
+
+ The operation layer has been designed with the assumption that the
+ transport layer contains the following information:
+
+ - the URI of the target job or printer operation
+ - the total length of the data in the operation layer, either as a
+ single length or as a sequence of chunks each with a length.
+
+ It is REQUIRED that a printer implementation support HTTP over the
+ IANA assigned Well Known Port 631 (the IPP default port), though a
+ printer implementation may support HTTP over some other port as well.
+ In addition, a printer may have to support another port for privacy
+ (See Section 5 "Security Considerations").
+
+ Note: even though port 631 is the IPP default, port 80 remains the
+ default for an HTTP URI. Thus a URI for a printer using port 631
+ MUST contain an explicit port, e.g. "http://forest:631/pinetree". An
+ HTTP URI for IPP with no explicit port implicitly reference port 80,
+ which is consistent with the rules for HTTP/1.1. Each HTTP operation
+ MUST use the POST method where the request-URI is the object target
+ of the operation, and where the "Content-Type" of the message-body in
+ each request and response MUST be "application/ipp". The message-body
+ MUST contain the operation layer and MUST have the syntax described
+ in section 3.2 "Syntax of Encoding". A client implementation MUST
+ adhere to the rules for a client described for HTTP1.1 [RFC2068]. A
+ printer (server) implementation MUST adhere the rules for an origin
+ server described for HTTP1.1 [RFC2068].
+
+ An IPP server sends a response for each request that it receives. If
+ an IPP server detects an error, it MAY send a response before it has
+ read the entire request. If the HTTP layer of the IPP server
+ completes processing the HTTP headers successfully, it MAY send an
+
+
+
+Herriot, et al. Experimental [Page 18]
+
+RFC 2565 IPP/1.0: Encoding and Transport April 1999
+
+
+ intermediate response, such as "100 Continue", with no IPP data
+ before sending the IPP response. A client MUST expect such a variety
+ of responses from an IPP server. For further information on HTTP/1.1,
+ consult the HTTP documents [RFC2068].
+
+5. Security Considerations
+
+ The IPP Model document defines an IPP implementation with "privacy"
+ as one that implements Secure Socket Layer Version 3 (SSL3). Note:
+ SSL3 is not an IETF standards track specification. SSL3 meets the
+ requirements for IPP security with regards to features such as mutual
+ authentication and privacy (via encryption). The IPP Model document
+ also outlines IPP-specific security considerations and should be the
+ primary reference for security implications with regards to the IPP
+ protocol itself.
+
+ The IPP Model document defines an IPP implementation with
+ "authentication" as one that implements the standard way for
+ transporting IPP messages within HTTP 1.1. These include the security
+ considerations outlined in the HTTP 1.1 standard document [RFC2068]
+ and Digest Access Authentication extension [RFC2069].
+
+ The current HTTP infrastructure supports HTTP over TCP port 80. IPP
+ server implementations MUST offer IPP services using HTTP over the
+ IANA assigned Well Known Port 631 (the IPP default port). IPP server
+ implementations may support other ports, in addition to this port.
+
+ See further discussion of IPP security concepts in the model document
+ [RFC2566].
+
+5.1 Using IPP with SSL3
+
+ An assumption is that the URI for a secure IPP Printer object has
+ been found by means outside the IPP printing protocol, via a
+ directory service, web site or other means.
+
+ IPP provides a transparent connection to SSL by calling the
+ corresponding URL (a https URI connects by default to port 443).
+ However, the following functions can be provided to ease the
+ integration of IPP with SSL during implementation:
+
+ connect (URI), returns a status
+
+ "connect" makes an https call and returns the immediate status
+ of the connection as returned by SSL to the user. The status
+ values are explained in section 5.4.2 of the SSL document
+ [ssl].
+
+
+
+
+Herriot, et al. Experimental [Page 19]
+
+RFC 2565 IPP/1.0: Encoding and Transport April 1999
+
+
+ A session-id may also be retained to later resume a session.
+ The SSL handshake protocol may also require the cipher
+ specifications supported by the client, key length of the
+ ciphers, compression methods, certificates, etc. These should
+ be sent to the server and hence should be available to the IPP
+ client (although as part of administration features).
+
+ disconnect (session)
+
+ to disconnect a particular session.
+
+ The session-id available from the "connect" could be used.
+
+ resume (session)
+
+ to reconnect using a previous session-id.
+
+ The availability of this information as administration features are
+ left for implementers, and need not be specified at this time.
+
+6. References
+
+ [RFC2278] Freed, N. and J. Postel, "IANA Charset Registration
+ Procedures", BCP 19, RFC 2278, January 1998.
+
+ [dpa] ISO/IEC 10175 Document Printing Application (DPA), June
+ 1996.
+
+ [iana] IANA Registry of Coded Character Sets:
+ ftp://ftp.isi.edu/in-notes/iana/assignments/character-sets.
+
+ [ipp-iig] Hastings, Tom, et al., "Internet Printing Protocol/1.0:
+ Implementer's Guide", Work in Progress.
+
+ [RFC2569] Herriot, R., Hastings, T., Jacobs, N. and J. Martin,
+ "Mapping between LPD and IPP Protocols", RFC 2569, April
+ 1999.
+
+ [RFC2566] deBry, R., Hastings, T., Herriot, R., Isaacson, S. and P.
+ Powell, "Internet Printing Protocol/1.0: Model and
+ Semantics", RFC 2566, April 1999.
+
+ [RFC2565] Herriot, R., Butler, S., Moore, P., Tuner, R., "Internet
+ Printing Protocol/1.0: Encoding and Transport", RFC 2565,
+ April 1999.
+
+
+
+
+
+
+Herriot, et al. Experimental [Page 20]
+
+RFC 2565 IPP/1.0: Encoding and Transport April 1999
+
+
+ [RFC2568] Zilles, S., "Rationale for the Structure and Model and
+ Protocol for the Internet Printing Protocol", RFC 2568,
+ April 1999.
+
+ [RFC2567] Wright, D., "Design Goals for an Internet Printing
+ Protocol", RFC 2567, April 1999.
+
+ [RFC822] Crocker, D., "Standard for the Format of ARPA Internet Text
+ Messages", STD 11, RFC 822, August 1982.
+
+ [RFC1123] Braden, R., "Requirements for Internet Hosts - Application
+ and Support", STD 3, RFC 1123, October 1989.
+
+ [RFC1179] McLaughlin, L. III, (editor), "Line Printer Daemon
+ Protocol" RFC 1179, August 1990.
+
+ [RFC2223] Postel, J. and J. Reynolds, "Instructions to RFC Authors",
+ RFC 2223, October 1997.
+
+ [RFC1738] Berners-Lee, T., Masinter, L. and M. McCahill, "Uniform
+ Resource Locators (URL)", RFC 1738, December 1994.
+
+ [RFC1759] Smith, R., Wright, F., Hastings, T., Zilles, S. and J.
+ Gyllenskog, "Printer MIB", RFC 1759, March 1995.
+
+ [RFC1766] Alvestrand, H., " Tags for the Identification of
+ Languages", RFC 1766, March 1995.
+
+ [RFC1808] Fielding, R., "Relative Uniform Resource Locators", RFC
+ 1808, June 1995.
+
+ [RFC2579] McCloghrie, K., Perkins, D. and J. Schoenwaelder, "Textual
+ Conventions for SMIv2", STD 58, RFC 2579, April 1999.
+
+ [RFC2046] Freed, N. and N. Borenstein, Multipurpose Internet Mail
+ Extensions (MIME) Part Two: Media Types", RFC 2046,
+ November 1996.
+
+ [RFC2048] Freed, N., Klensin J. and J. Postel. Multipurpose Internet
+ Mail Extension (MIME) Part Four: Registration Procedures",
+ BCP 13, RFC 2048, November 1996.
+
+ [RFC2068] Fielding, R., Gettys, J., Mogul, J., Frystyk, H. and T.
+ Berners-Lee, "Hypertext Transfer Protocol -- HTTP/1.1", RFC
+ 2068, January 1997.
+
+
+
+
+
+
+Herriot, et al. Experimental [Page 21]
+
+RFC 2565 IPP/1.0: Encoding and Transport April 1999
+
+
+ [RFC2069] Franks, J., Hallam-Baker, P., Hostetler, J., Leach, P.,
+ Luotonen, A., Sink, E. and L. Stewart, "An Extension to
+ HTTP: Digest Access Authentication", RFC 2069, January
+ 1997.
+
+ [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate
+ Requirement Levels", BCP 14, RFC 2119, March 1997.
+
+ [RFC2184] Freed, N. and K. Moore, "MIME Parameter Value and Encoded
+ Word Extensions: Character Sets, Languages, and
+ Continuations", RFC 2184, August 1997.
+
+ [RFC2234] Crocker, D. and P. Overell, "Augmented BNF for Syntax
+ Specifications: ABNF", RFC 2234. November 1997.
+
+ [RFC2396] Berners-Lee, T., Fielding, R. and L. Masinter, "Uniform
+ Resource Identifiers (URI): Generic Syntax", RFC 2396,
+ August 1998.
+
+7. Authors' Addresses
+
+ Robert Herriot (Editor)
+ Xerox Corporation
+ 3400 Hillview Ave., Bldg #1
+ Palo Alto, CA 94304
+
+ Phone: 650-813-7696
+ Fax: 650-813-6860
+ EMail: rherriot@pahv.xerox.com
+
+
+ Sylvan Butler
+ Hewlett-Packard
+ 11311 Chinden Blvd.
+ Boise, ID 83714
+
+ Phone: 208-396-6000
+ Fax: 208-396-3457
+ EMail: sbutler@boi.hp.com
+
+
+
+
+
+
+
+
+
+
+
+
+Herriot, et al. Experimental [Page 22]
+
+RFC 2565 IPP/1.0: Encoding and Transport April 1999
+
+
+ Paul Moore
+ Microsoft
+ One Microsoft Way
+ Redmond, WA 98053
+
+ Phone: 425-936-0908
+ Fax: 425-93MS-FAX
+ EMail: paulmo@microsoft.com
+
+
+ Randy Turner
+ Sharp Laboratories
+ 5750 NW Pacific Rim Blvd
+ Camas, WA 98607
+
+ Phone: 360-817-8456
+ Fax: 360-817-8436
+ EMail: rturner@sharplabs.com
+
+
+ IPP Mailing List: ipp@pwg.org
+ IPP Mailing List Subscription: ipp-request@pwg.org
+ IPP Web Page: http://www.pwg.org/ipp/
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Herriot, et al. Experimental [Page 23]
+
+RFC 2565 IPP/1.0: Encoding and Transport April 1999
+
+
+8. Other Participants:
+
+ Chuck Adams - Tektronix Harry Lewis - IBM
+ Ron Bergman - Dataproducts Tony Liao - Vivid Image
+ Keith Carter - IBM David Manchala - Xerox
+ Angelo Caruso - Xerox Carl-Uno Manros - Xerox
+ Jeff Copeland - QMS Jay Martin - Underscore
+ Roger deBry - IBM Larry Masinter - Xerox
+ Lee Farrell - Canon Ira McDonald - High North Inc.
+ Sue Gleeson - Digital Bob Pentecost - Hewlett-Packard
+ Charles Gordon - Osicom Patrick Powell - Astart
+ Technologies
+ Brian Grimshaw - Apple Jeff Rackowitz - Intermec
+ Jerry Hadsell - IBM Xavier Riley - Xerox
+ Richard Hart - Digital Gary Roberts - Ricoh
+ Tom Hastings - Xerox Stuart Rowley - Kyocera
+ Stephen Holmstead Richard Schneider - Epson
+ Zhi-Hong Huang - Zenographics Shigern Ueda - Canon
+ Scott Isaacson - Novell Bob Von Andel - Allegro Software
+ Rich Lomicka - Digital William Wagner - Digital Products
+ David Kellerman - Northlake Jasper Wong - Xionics
+ Software
+ Robert Kline - TrueSpectra Don Wright - Lexmark
+ Dave Kuntz - Hewlett-Packard Rick Yardumian - Xerox
+ Takami Kurono - Brother Lloyd Young - Lexmark
+ Rich Landau - Digital Peter Zehler - Xerox
+ Greg LeClair - Epson Frank Zhao - Panasonic
+ Steve Zilles - Adobe
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Herriot, et al. Experimental [Page 24]
+
+RFC 2565 IPP/1.0: Encoding and Transport April 1999
+
+
+9. Appendix A: Protocol Examples
+
+9.1 Print-Job Request
+
+ The following is an example of a Print-Job request with job-name,
+ copies, and sides specified. The "ipp-attribute-fidelity" attribute
+ is set to 'true' so that the print request will fail if the "copies"
+ or the "sides" attribute are not supported or their values are not
+ supported.
+
+ Octets Symbolic Value Protocol field
+
+ 0x0100 1.0 version-number
+ 0x0002 Print-Job operation-id
+ 0x00000001 1 request-id
+ 0x01 start operation-attributes operation-attributes-tag
+ 0x47 charset type value-tag
+ 0x0012 name-length
+ attributes- attributes-charset name
+ charset
+ 0x0008 value-length
+ us-ascii US-ASCII value
+ 0x48 natural-language type value-tag
+ 0x001B name-length
+ attributes- attributes-natural-language name
+ natural-
+ language
+ 0x0005 value-length
+ en-us en-US value
+ 0x45 uri type value-tag
+ 0x000B name-length
+ printer-uri printer-uri name
+ 0x001A value-length
+ http://forest: printer pinetree value
+ 631/pinetree
+ 0x42 nameWithoutLanguage type value-tag
+ 0x0008 name-length
+ job-name job-name name
+ 0x0006 value-length
+ foobar foobar value
+ 0x22 boolean type value-tag
+ 0x16 name-length
+ ipp-attribute- ipp-attribute-fidelity name
+ fidelity
+ 0x01 value-length
+ 0x01 true value
+ 0x02 start job-attributes job-attributes-tag
+ 0x21 integer type value-tag
+
+
+
+Herriot, et al. Experimental [Page 25]
+
+RFC 2565 IPP/1.0: Encoding and Transport April 1999
+
+
+ 0x0006 name-length
+ copies copies name
+ 0x0004 value-length
+ 0x00000014 20 value
+ 0x44 keyword type value-tag
+ 0x0005 name-length
+ sides sides name
+ 0x0013 value-length
+ two-sided- two-sided-long-edge value
+ long-edge
+ 0x03 end-of-attributes end-of-attributes-tag
+ %!PS... <PostScript> data
+
+9.2 Print-Job Response (successful)
+
+ Here is an example of a successful Print-Job response to the previous
+ Print-Job request. The printer supported the "copies" and "sides"
+ attributes and their supplied values. The status code returned is '
+ successful-ok'.
+
+ Octets Symbolic Value Protocol field
+
+ 0x0100 1.0 version-number
+ 0x0000 successful-ok status-code
+ 0x00000001 1 request-id
+ 0x01 start operation-attributes operation-attributes-tag
+ 0x47 charset type value-tag
+ 0x0012 name-length
+ attributes- attributes-charset name
+ charset
+ 0x0008 value-length
+ us-ascii US-ASCII value
+ 0x48 natural-language type value-tag
+ 0x001B name-length
+ attributes- attributes-natural- name
+ natural-language language
+ 0x0005 value-length
+ en-us en-US value
+ 0x41 textWithoutLanguage type value-tag
+ 0x000E name-length
+ status-message status-message name
+ 0x000D value-length
+ successful-ok successful-ok value
+ 0x02 start job-attributes job-attributes-tag
+ 0x21 integer value-tag
+ 0x0006 name-length
+
+
+
+
+
+Herriot, et al. Experimental [Page 26]
+
+RFC 2565 IPP/1.0: Encoding and Transport April 1999
+
+
+ Octets Symbolic Value Protocol field
+
+ job-id job-id name
+ 0x0004 value-length
+ 147 147 value
+ 0x45 uri type value-tag
+ 0x0007 name-length
+ job-uri job-uri name
+ 0x001E value-length
+ http://forest:63 job 123 on pinetree value
+ 1/pinetree/123
+ 0x42 nameWithoutLanguage type value-tag
+ 0x0009 name-length
+ job-state job-state name
+ 0x0004 value-length
+ 0x0003 pending value
+ 0x03 end-of-attributes end-of-attributes-tag
+
+9.3 Print-Job Response (failure)
+
+ Here is an example of an unsuccessful Print-Job response to the
+ previous Print-Job request. It fails because, in this case, the
+ printer does not support the "sides" attribute and because the value
+ '20' for the "copies" attribute is not supported. Therefore, no job
+ is created, and neither a "job-id" nor a "job-uri" operation
+ attribute is returned. The error code returned is 'client-error-
+ attributes-or-values-not-supported' (0x040B).
+
+ Octets Symbolic Value Protocol field
+
+ 0x0100 1.0 version-number
+ 0x040B client-error-attributes-or- status-code
+ values-not-supported
+ 0x00000001 1 request-id
+ 0x01 start operation-attributes operation-attribute tag
+ 0x47 charset type value-tag
+ 0x0012 name-length
+ attributes- attributes-charset name
+ charset
+ 0x0008 value-length
+ us-ascii US-ASCII value
+ 0x48 natural-language type value-tag
+ 0x001B name-length
+ attributes- attributes-natural-language name
+ natural-
+ language
+ 0x0005 value-length
+
+
+
+
+Herriot, et al. Experimental [Page 27]
+
+RFC 2565 IPP/1.0: Encoding and Transport April 1999
+
+
+ Octets Symbolic Value Protocol field
+
+ en-us en-US value
+ 0x41 textWithoutLanguage type value-tag
+ 0x000E name-length
+ status- status-message name
+ message
+ 0x002F value-length
+ client-error- client-error-attributes-or- value
+ attributes- values-not-supported
+ or-values-
+ not-supported
+ 0x05 start unsupported-attributes unsupported-attributes tag
+ 0x21 integer type value-tag
+ 0x0006 name-length
+ copies copies name
+ 0x0004 value-length
+ 0x00000014 20 value
+ 0x10 unsupported (type) value-tag
+ 0x0005 name-length
+ sides sides name
+ 0x0000 value-length
+ 0x03 end-of-attributes end-of-attributes-tag
+
+9.4 Print-Job Response (success with attributes ignored)
+
+ Here is an example of a successful Print-Job response to a Print-Job
+ request like the previous Print-Job request, except that the value of
+ 'ipp-attribute-fidelity' is false. The print request succeeds, even
+ though, in this case, the printer supports neither the "sides"
+ attribute nor the value '20' for the "copies" attribute. Therefore, a
+ job is created, and both a "job-id" and a "job-uri" operation
+ attribute are returned. The unsupported attributes are also returned
+ in an Unsupported Attributes Group. The error code returned is '
+ successful-ok-ignored-or-substituted-attributes' (0x0001).
+
+ Octets Symbolic Value Protocol field
+
+ 0x0100 1.0 version-number
+ 0x0001 successful-ok-ignored-or- status-code
+ substituted-attributes
+ 0x00000001 1 request-id
+ 0x01 start operation-attributes operation-attributes-tag
+ 0x47 charset type value-tag
+ 0x0012 name-length
+ attributes- attributes-charset name
+ charset
+ 0x0008 value-length
+
+
+
+Herriot, et al. Experimental [Page 28]
+
+RFC 2565 IPP/1.0: Encoding and Transport April 1999
+
+
+ Octets Symbolic Value Protocol field
+
+ us-ascii US-ASCII value
+ 0x48 natural-language type value-tag
+ 0x001B name-length
+ attributes- attributes-natural- name
+ natural-language language
+ 0x0005 value-length
+ en-us en-US value
+ 0x41 textWithoutLanguage type value-tag
+ 0x000E name-length
+ status-message status-message name
+ 0x002F value-length
+ successful-ok- successful-ok-ignored-or- value
+ ignored-or- substituted-attributes
+ substituted-
+ attributes
+ 0x05 start unsupported- unsupported-attributes
+ attributes tag
+ 0x21 integer type value-tag
+ 0x0006 name-length
+ copies copies name
+ 0x0004 value-length
+ 0x00000014 20 value
+ 0x10 unsupported (type) value-tag
+ 0x0005 name-length
+ sides sides name
+ 0x0000 value-length
+ 0x02 start job-attributes job-attributes-tag
+ 0x21 integer value-tag
+ 0x0006 name-length
+ job-id job-id name
+ 0x0004 value-length
+ 147 147 value
+ 0x45 uri type value-tag
+ 0x0007 name-length
+ job-uri job-uri name
+ 0x001E value-length
+ http://forest:63 job 123 on pinetree value
+ 1/pinetree/123
+ 0x42 nameWithoutLanguage type value-tag
+ 0x0009 name-length
+ job-state job-state name
+ 0x0004 value-length
+ 0x0003 pending value
+ 0x03 end-of-attributes end-of-attributes-tag
+
+
+
+
+
+Herriot, et al. Experimental [Page 29]
+
+RFC 2565 IPP/1.0: Encoding and Transport April 1999
+
+
+9.5 Print-URI Request
+
+ The following is an example of Print-URI request with copies and
+ job-name parameters:
+
+ Octets Symbolic Value Protocol field
+
+ 0x0100 1.0 version-number
+
+ Octets Symbolic Value Protocol field
+ 0x0003 Print-URI operation-id
+ 0x00000001 1 request-id
+ 0x01 start operation-attributes operation-attributes-tag
+ 0x47 charset type value-tag
+ 0x0012 name-length
+ attributes- attributes-charset name
+ charset
+ 0x0008 value-length
+ us-ascii US-ASCII value
+ 0x48 natural-language type value-tag
+ 0x001B name-length
+ attributes- attributes-natural-language name
+ natural-
+ language
+ 0x0005 value-length
+ en-us en-US value
+ 0x45 uri type value-tag
+ 0x000B name-length
+ printer-uri printer-uri name
+ 0x001A value-length
+ http://forest printer pinetree value
+ :631/pinetree
+ 0x45 uri type value-tag
+ 0x000C name-length
+ document-uri document-uri name
+ 0x11 value-length
+ ftp://foo.com ftp://foo.com/foo value
+ /foo
+ 0x42 nameWithoutLanguage type value-tag
+ 0x0008 name-length
+ job-name job-name name
+ 0x0006 value-length
+ foobar foobar value
+ 0x02 start job-attributes job-attributes-tag
+ 0x21 integer type value-tag
+ 0x0006 name-length
+ copies copies name
+ 0x0004 value-length
+
+
+
+Herriot, et al. Experimental [Page 30]
+
+RFC 2565 IPP/1.0: Encoding and Transport April 1999
+
+
+ 0x00000001 1 value
+ 0x03 end-of-attributes end-of-attributes-tag
+
+9.6 Create-Job Request
+
+ The following is an example of Create-Job request with no parameters
+ and no attributes:
+
+ Octets Symbolic Value Protocol field
+ 0x0100 1.0 version-number
+ 0x0005 Create-Job operation-id
+ 0x00000001 1 request-id
+ 0x01 start operation-attributes operation-attributes-tag
+ 0x47 charset type value-tag
+ 0x0012 name-length
+
+ Octets Symbolic Value Protocol field
+ attributes- attributes-charset name
+ charset
+ 0x0008 value-length
+ us-ascii US-ASCII value
+ 0x48 natural-language type value-tag
+ 0x001B name-length
+ attributes- attributes-natural-language name
+ natural-
+ language
+ 0x0005 value-length
+ en-us en-US value
+ 0x45 uri type value-tag
+ 0x000B name-length
+ printer-uri printer-uri name
+ 0x001A value-length
+ http://forest: printer pinetree value
+ 631/pinetree
+ 0x03 end-of-attributes end-of-attributes-tag
+
+9.7 Get-Jobs Request
+
+ The following is an example of Get-Jobs request with parameters but
+ no attributes:
+
+ Octets Symbolic Value Protocol field
+
+ 0x0100 1.0 version-number
+ 0x000A Get-Jobs operation-id
+ 0x00000123 0x123 request-id
+ 0x01 start operation-attributes operation-attributes-tag
+ 0x47 charset type value-tag
+
+
+
+Herriot, et al. Experimental [Page 31]
+
+RFC 2565 IPP/1.0: Encoding and Transport April 1999
+
+
+ Octets Symbolic Value Protocol field
+
+ 0x0012 name-length
+ attributes- attributes-charset name
+ charset
+ 0x0008 value-length
+ us-ascii US-ASCII value
+ 0x48 natural-language type value-tag
+ 0x001B name-length
+ attributes- attributes-natural-language name
+ natural-
+ language
+ 0x0005 value-length
+ en-us en-US value
+ 0x45 uri type value-tag
+ 0x000B name-length
+ printer-uri printer-uri name
+ 0x001A value-length
+ http://forest:6 printer pinetree value
+ 31/pinetree
+ 0x21 integer type value-tag
+ 0x0005 name-length
+ limit limit name
+ 0x0004 value-length
+ 0x00000032 50 value
+ 0x44 keyword type value-tag
+ 0x0014 name-length
+ requested- requested-attributes name
+ attributes
+ 0x0006 value-length
+ job-id job-id value
+ 0x44 keyword type value-tag
+ 0x0000 additional value name-length
+ 0x0008 value-length
+ job-name job-name value
+ 0x44 keyword type value-tag
+ 0x0000 additional value name-length
+ 0x000F value-length
+ document-format document-format value
+ 0x03 end-of-attributes end-of-attributes-tag
+
+9.8 Get-Jobs Response
+
+ The following is an of Get-Jobs response from previous request with 3
+ jobs. The Printer returns no information about the second job
+ (because of security reasons):
+
+
+
+
+
+Herriot, et al. Experimental [Page 32]
+
+RFC 2565 IPP/1.0: Encoding and Transport April 1999
+
+
+ Octets Symbolic Value Protocol field
+
+ 0x0100 1.0 version-number
+ 0x0000 successful-ok status-code
+ 0x00000123 0x123 request-id (echoed
+ back)
+ 0x01 start operation-attributes operation-attribute-tag
+ 0x47 charset type value-tag
+ 0x0012 name-length
+ attributes- attributes-charset name
+ charset
+ 0x000A value-length
+ ISO-8859-1 ISO-8859-1 value
+ 0x48 natural-language type value-tag
+ 0x001B name-length
+ attributes- attributes-natural-language name
+ natural-
+ language
+ 0x0005 value-length
+ en-us en-US value
+ 0x41 textWithoutLanguage type value-tag
+ 0x000E name-length
+ status-message status-message name
+ 0x000D value-length
+ successful-ok successful-ok value
+ 0x02 start job-attributes (1st job-attributes-tag
+ object)
+ 0x21 integer type value-tag
+ 0x0006 name-length
+ job-id job-id name
+ 0x0004 value-length
+ 147 147 value
+ 0x36 nameWithLanguage value-tag
+ 0x0008 name-length
+ job-name job-name name
+ 0x000C value-length
+ 0x0005 sub-value-length
+ fr-ca fr-CA value
+ 0x0003 sub-value-length
+ fou fou name
+ 0x02 start job-attributes (2nd job-attributes-tag
+ object)
+ 0x02 start job-attributes (3rd job-attributes-tag
+ object)
+ 0x21 integer type value-tag
+ 0x0006 name-length
+ job-id job-id name
+ 0x0004 value-length
+
+
+
+Herriot, et al. Experimental [Page 33]
+
+RFC 2565 IPP/1.0: Encoding and Transport April 1999
+
+
+ Octets Symbolic Value Protocol field
+
+ 148 148 value
+ 0x36 nameWithLanguage value-tag
+ 0x0008 name-length
+ job-name job-name name
+ 0x0012 value-length
+ 0x0005 sub-value-length
+ de-CH de-CH value
+ 0x0009 sub-value-length
+ isch guet isch guet name
+ 0x03 end-of-attributes end-of-attributes-tag
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Herriot, et al. Experimental [Page 34]
+
+RFC 2565 IPP/1.0: Encoding and Transport April 1999
+
+
+10. Appendix C: Registration of MIME Media Type Information for
+ "application/ipp"
+
+ This appendix contains the information that IANA requires for
+ registering a MIME media type. The information following this
+ paragraph will be forwarded to IANA to register application/ipp whose
+ contents are defined in Section 3 "Encoding of the Operation Layer"
+ in this document:
+
+ MIME type name: application
+
+ MIME subtype name: ipp
+
+ A Content-Type of "application/ipp" indicates an Internet Printing
+ Protocol message body (request or response). Currently there is one
+ version: IPP/1.0, whose syntax is described in Section 3 "Encoding of
+ the Operation Layer" of [RFC2565], and whose semantics are described
+ in [RFC2566].
+
+ Required parameters: none
+
+ Optional parameters: none
+
+ Encoding considerations:
+
+ IPP/1.0 protocol requests/responses MAY contain long lines and ALWAYS
+ contain binary data (for example attribute value lengths).
+
+ Security considerations:
+
+ IPP/1.0 protocol requests/responses do not introduce any security
+ risks not already inherent in the underlying transport protocols.
+ Protocol mixed-version interworking rules in [RFC2566] as well as
+ protocol encoding rules in [RFC2565] are complete and unambiguous.
+
+ Interoperability considerations:
+
+ IPP/1.0 requests (generated by clients) and responses (generated by
+ servers) MUST comply with all conformance requirements imposed by the
+ normative specifications [RFC2566] and [RFC2565]. Protocol encoding
+ rules specified in [RFC2565] are comprehensive, so that
+ interoperability between conforming implementations is guaranteed
+ (although support for specific optional features is not ensured).
+ Both the "charset" and "natural-language" of all IPP/1.0 attribute
+ values which are a LOCALIZED-STRING are explicit within IPP protocol
+ requests/responses (without recourse to any external information in
+ HTTP, SMTP, or other message transport headers).
+
+
+
+
+Herriot, et al. Experimental [Page 35]
+
+RFC 2565 IPP/1.0: Encoding and Transport April 1999
+
+
+ Published specification:
+
+ [RFC2566] Isaacson, S., deBry, R., Hastings, T., Herriot, R. and P.
+ Powell, "Internet Printing Protocol/1.0: Model and
+ Semantics" RFC 2566, April 1999.
+
+ [RFC2565] Herriot, R., Butler, S., Moore, P., Tuner, R., "Internet
+ Printing Protocol/1.0: Encoding and Transport", RFC 2565,
+ April 1999.
+
+ Applications which use this media type:
+
+ Internet Printing Protocol (IPP) print clients and print servers,
+ communicating using HTTP/1.1 (see [RFC2565]), SMTP/ESMTP, FTP, or
+ other transport protocol. Messages of type "application/ipp" are
+ self-contained and transport-independent, including "charset" and
+ "natural-language" context for any LOCALIZED-STRING value.
+
+ Person & email address to contact for further information:
+
+ Scott A. Isaacson
+ Novell, Inc.
+ 122 E 1700 S
+ Provo, UT 84606
+
+ Phone: 801-861-7366
+ Fax: 801-861-4025
+ Email: sisaacson@novell.com
+
+ or
+
+ Robert Herriot (Editor)
+ Xerox Corporation
+ 3400 Hillview Ave., Bldg #1
+ Palo Alto, CA 94304
+
+ Phone: 650-813-7696
+ Fax: 650-813-6860
+ EMail: rherriot@pahv.xerox.com
+
+
+
+
+
+
+
+
+
+
+
+
+Herriot, et al. Experimental [Page 36]
+
+RFC 2565 IPP/1.0: Encoding and Transport April 1999
+
+
+11. Full Copyright Statement
+
+ Copyright (C) The Internet Society (1999). All Rights Reserved.
+
+ This document and translations of it may be copied and furnished to
+ others, and derivative works that comment on or otherwise explain it
+ or assist in its implementation may be prepared, copied, published
+ and distributed, in whole or in part, without restriction of any
+ kind, provided that the above copyright notice and this paragraph are
+ included on all such copies and derivative works. However, this
+ document itself may not be modified in any way, such as by removing
+ the copyright notice or references to the Internet Society or other
+ Internet organizations, except as needed for the purpose of
+ developing Internet standards in which case the procedures for
+ copyrights defined in the Internet Standards process must be
+ followed, or as required to translate it into languages other than
+ English.
+
+ The limited permissions granted above are perpetual and will not be
+ revoked by the Internet Society or its successors or assigns.
+
+ This document and the information contained herein is provided on an
+ "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING
+ TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+ HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+ MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Herriot, et al. Experimental [Page 37]
+
diff --git a/standards/rfc2566.txt b/standards/rfc2566.txt
new file mode 100644
index 000000000..f373d6a0f
--- /dev/null
+++ b/standards/rfc2566.txt
@@ -0,0 +1,9691 @@
+
+
+
+
+
+
+Network Working Group R. deBry
+Request for Comments: 2566 Utah Valley State College
+Category: Experimental T. Hastings
+ Xerox Corporation
+ R. Herriot
+ Xerox Corporation
+ S. Isaacson
+ Novell, Inc.
+ P. Powell
+ Astart Technologies
+ April 1999
+
+
+ Internet Printing Protocol/1.0: Model and Semantics
+
+Status of this Memo
+
+ This memo defines an Experimental Protocol for the Internet
+ community. It does not specify an Internet standard of any kind.
+ Discussion and suggestions for improvement are requested.
+ Distribution of this memo is unlimited.
+
+Copyright Notice
+
+ Copyright (C) The Internet Society (1999). All Rights Reserved.
+
+IESG Note
+
+ This document defines an Experimental protocol for the Internet
+ community. The IESG expects that a revised version of this protocol
+ will be published as Proposed Standard protocol. The Proposed
+ Standard, when published, is expected to change from the protocol
+ defined in this memo. In particular, it is expected that the
+ standards-track version of the protocol will incorporate strong
+ authentication and privacy features, and that an "ipp:" URL type will
+ be defined which supports those security measures. Other changes to
+ the protocol are also possible. Implementors are warned that future
+ versions of this protocol may not interoperate with the version of
+ IPP defined in this document, or if they do interoperate, that some
+ protocol features may not be available.
+
+ The IESG encourages experimentation with this protocol, especially in
+ combination with Transport Layer Security (TLS) [RFC 2246], to help
+ determine how TLS may effectively be used as a security layer for
+ IPP.
+
+
+
+
+
+
+deBry, et al. Experimental [Page 1]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+Abstract
+
+ This document is one of a set of documents, which together describe
+ all aspects of a new Internet Printing Protocol (IPP). IPP is an
+ application level protocol that can be used for distributed printing
+ using Internet tools and technologies. This document describes a
+ simplified model consisting of abstract objects, their attributes,
+ and their operations that is independent of encoding and transport.
+ The model consists of a Printer and a Job object. A Job optionally
+ supports multiple documents. IPP 1.0 semantics allow end-users and
+ operators to query printer capabilities, submit print jobs, inquire
+ about the status of print jobs and printers, and cancel print jobs.
+ This document also addresses security, internationalization, and
+ directory issues.
+
+ The full set of IPP documents includes:
+
+ Design Goals for an Internet Printing Protocol [RFC2567]
+ Rationale for the Structure and Model and Protocol for the Internet
+ Printing Protocol [RFC2568]
+ Internet Printing Protocol/1.0: Model and Semantics (this document)
+ Internet Printing Protocol/1.0: Encoding and Transport [RFC2565]
+ Internet Printing Protocol/1.0: Implementer's Guide [ipp-iig]
+ Mapping between LPD and IPP Protocols [RFC2569]
+
+ The "Design Goals for an Internet Printing Protocol" document takes a
+ broad look at distributed printing functionality, and it enumerates
+ real-life scenarios that help to clarify the features that need to be
+ included in a printing protocol for the Internet. It identifies
+ requirements for three types of users: end users, operators, and
+ administrators. It calls out a subset of end user requirements that
+ are satisfied in IPP/1.0. Operator and administrator requirements
+ are out of scope for version 1.0.
+
+ The "Rationale for the Structure and Model and Protocol for the
+ Internet Printing Protocol" document describes IPP from a high level
+ view, defines a roadmap for the various documents that form the suite
+ of IPP specifications, and gives background and rationale for the
+ IETF working group's major decisions.
+
+ The "Internet Printing Protocol/1.0: Encoding and Transport" document
+ is a formal mapping of the abstract operations and attributes defined
+ in the model document onto HTTP/1.1. It defines the encoding rules
+ for a new Internet media type called "application/ipp".
+
+ The "Internet Printing Protocol/1.0: Implementer's Guide" document
+ gives insight and advice to implementers of IPP clients and IPP
+ objects. It is intended to help them understand IPP/1.0 and some of
+
+
+
+deBry, et al. Experimental [Page 2]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ the considerations that may assist them in the design of their client
+ and/or IPP object implementations. For example, a typical order of
+ processing requests is given, including error checking. Motivation
+ for some of the specification decisions is also included.
+
+ The "Mapping between LPD and IPP Protocols" document gives some
+ advice to implementers of gateways between IPP and LPD (Line Printer
+ Daemon) implementations.
+
+Table of Contents
+
+1. Introduction 8
+ 1.1 Simplified Printing Model 9
+2. IPP Objects 11
+ 2.1 Printer Object 12
+ 2.2 Job Object 14
+ 2.3 Object Relationships 14
+ 2.4 Object Identity 15
+3. IPP Operations 18
+ 3.1 Common Semantics 19
+ 3.1.1 Required Parameters 19
+ 3.1.2 Operation IDs and Request IDs 20
+ 3.1.3 Attributes 20
+ 3.1.4 Character Set and Natural Language Operation Attributes 22
+ 3.1.4.1 Request Operation Attributes 22
+ 3.1.4.2 Response Operation Attributes 26
+ 3.1.5 Operation Targets 28
+ 3.1.6 Operation Status Codes and Messages 29
+ 3.1.7 Versions 30
+ 3.1.8 Job Creation Operations 32
+ 3.2 Printer Operations 34
+ 3.2.1 Print-Job Operation 34
+ 3.2.1.1 Print-Job Request 34
+ 3.2.1.2 Print-Job Response 38
+ 3.2.2 Print-URI Operation 41
+ 3.2.3 Validate-Job Operation 42
+ 3.2.4 Create-Job Operation 42
+ 3.2.5 Get-Printer-Attributes Operation 43
+ 3.2.5.1 Get-Printer-Attributes Request 44
+ 3.2.5.2 Get-Printer-Attributes Response 46
+ 3.2.6 Get-Jobs Operation 47
+ 3.2.6.1 Get-Jobs Request 47
+ 3.2.6.2 Get-Jobs Response 49
+ 3.3 Job Operations 50
+ 3.3.1 Send-Document Operation 50
+ 3.3.1.1 Send-Document Request 51
+ 3.3.1.2 Send-Document Response 53
+ 3.3.2 Send-URI Operation 54
+
+
+
+deBry, et al. Experimental [Page 3]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ 3.3.3 Cancel-Job Operation 54
+ 3.3.3.1 Cancel-Job Request 54
+ 3.3.3.2 Cancel-Job Response 55
+ 3.3.4 Get-Job-Attributes Operation 56
+ 3.3.4.1 Get-Job-Attributes Request 57
+ 3.3.4.2 Get-Job-Attributes Response 57
+4. Object Attributes 58
+ 4.1 Attribute Syntaxes 59
+ 4.1.1 'text' 60
+ 4.1.1.1 'textWithoutLanguage' 61
+ 4.1.1.2 'textWithLanguage' 61
+ 4.1.2 'name' 62
+ 4.1.2.1 'nameWithoutLanguage' 62
+ 4.1.2.2 'nameWithLanguage' 63
+ 4.1.2.3 Matching 'name' attribute values 63
+ 4.1.3 'keyword' 64
+ 4.1.4 'enum' 65
+ 4.1.5 'uri' 65
+ 4.1.6 'uriScheme' 65
+ 4.1.7 'charset' 66
+ 4.1.8 'naturalLanguage' 67
+ 4.1.9 'mimeMediaType' 67
+ 4.1.10 'octetString' 69
+ 4.1.11 'boolean' 69
+ 4.1.12 'integer' 69
+ 4.1.13 'rangeOfInteger' 69
+ 4.1.14 'dateTime' 69
+ 4.1.15 'resolution' 69
+ 4.1.16 '1setOf X' 70
+ 4.2 Job Template Attributes 70
+ 4.2.1 job-priority (integer(1:100)) 74
+ 4.2.2 job-hold-until (type3 keyword | name (MAX)) 75
+ 4.2.3 job-sheets (type3 keyword | name(MAX)) 75
+ 4.2.4 multiple-document-handling (type2 keyword) 76
+ 4.2.5 copies (integer(1:MAX)) 77
+ 4.2.6 finishings (1setOf type2 enum) 78
+ 4.2.7 page-ranges (1setOf rangeOfInteger (1:MAX)) 79
+ 4.2.8 sides (type2 keyword) 80
+ 4.2.9 number-up (integer(1:MAX)) 80
+ 4.2.10 orientation-requested (type2 enum) 81
+ 4.2.11 media (type3 keyword | name(MAX)) 82
+ 4.2.12 printer-resolution (resolution) 83
+ 4.2.13 print-quality (type2 enum) 83
+ 4.3 Job Description Attributes 84
+ 4.3.1 job-uri (uri) 85
+ 4.3.2 job-id (integer(1:MAX)) 85
+ 4.3.3 job-printer-uri (uri) 86
+ 4.3.4 job-more-info (uri) 86
+
+
+
+deBry, et al. Experimental [Page 4]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ 4.3.5 job-name (name(MAX)) 86
+ 4.3.6 job-originating-user-name (name(MAX)) 86
+ 4.3.7 job-state (type1 enum) 87
+ 4.3.8 job-state-reasons (1setOf type2 keyword) 90
+ 4.3.9 job-state-message (text(MAX)) 92
+ 4.3.10 number-of-documents (integer(0:MAX)) 93
+ 4.3.11 output-device-assigned (name(127)) 93
+ 4.3.12 time-at-creation (integer(0:MAX)) 93
+ 4.3.13 time-at-processing (integer(0:MAX)) 93
+ 4.3.14 time-at-completed (integer(0:MAX)) 94
+ 4.3.15 number-of-intervening-jobs (integer(0:MAX)) 94
+ 4.3.16 job-message-from-operator (text(127)) 94
+ 4.3.17 job-k-octets (integer(0:MAX)) 94
+ 4.3.18 job-impressions (integer(0:MAX)) 95
+ 4.3.19 job-media-sheets (integer(0:MAX)) 95
+ 4.3.20 job-k-octets-processed (integer(0:MAX)) 96
+ 4.3.21 job-impressions-completed (integer(0:MAX)) 96
+ 4.3.22 job-media-sheets-completed (integer(0:MAX)) 96
+ 4.3.23 attributes-charset (charset) 97
+ 4.3.24 attributes-natural-language (naturalLanguage) 97
+ 4.4 Printer Description Attributes 97
+ 4.4.1 printer-uri-supported (1setOf uri) 99
+ 4.4.2 uri-security-supported (1setOf type2 keyword) 100
+ 4.4.3 printer-name (name(127)) 101
+ 4.4.4 printer-location (text(127)) 101
+ 4.4.5 printer-info (text(127)) 101
+ 4.4.6 printer-more-info (uri) 101
+ 4.4.7 printer-driver-installer (uri) 102
+ 4.4.8 printer-make-and-model (text(127)) 102
+ 4.4.9 printer-more-info-manufacturer (uri) 102
+ 4.4.10 printer-state (type1 enum) 102
+ 4.4.11 printer-state-reasons (1setOf type2 keyword) 103
+ 4.4.12 printer-state-message (text(MAX)) 106
+ 4.4.13 operations-supported (1setOf type2 enum) 106
+ 4.4.14 charset-configured (charset) 107
+ 4.4.15 charset-supported (1setOf charset) 107
+ 4.4.16 natural-language-configured (naturalLanguage) 107
+ 4.4.17 generated-natural-language-supported(1setOf naturalLanguage108
+ 4.4.18 document-format-default (mimeMediaType) 108
+ 4.4.19 document-format-supported (1setOf mimeMediaType) 108
+ 4.4.20 printer-is-accepting-jobs (boolean) 109
+ 4.4.21 queued-job-count (integer(0:MAX)) 109
+ 4.4.22 printer-message-from-operator (text(127)) 109
+ 4.4.23 color-supported (boolean) 109
+ 4.4.24 reference-uri-schemes-supported (1setOf uriScheme) 109
+ 4.4.25 pdl-override-supported (type2 keyword) 110
+ 4.4.26 printer-up-time (integer(1:MAX)) 110
+ 4.4.27 printer-current-time (dateTime) 111
+
+
+
+deBry, et al. Experimental [Page 5]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ 4.4.28 multiple-operation-time-out (integer(1:MAX)) 111
+ 4.4.29 compression-supported (1setOf type3 keyword) 111
+ 4.4.30 job-k-octets-supported (rangeOfInteger(0:MAX)) 112
+ 4.4.31 job-impressions-supported (rangeOfInteger(0:MAX)) 112
+ 4.4.32 job-media-sheets-supported (rangeOfInteger(0:MAX)) 112
+5. Conformance 112
+ 5.1 Client Conformance Requirements 112
+ 5.2 IPP Object Conformance Requirements 113
+ 5.2.1 Objects 113
+ 5.2.2 Operations 113
+ 5.2.3 IPP Object Attributes 114
+ 5.2.4 Extensions 114
+ 5.2.5 Attribute Syntaxes 115
+ 5.3 Charset and Natural Language Requirements 115
+ 5.4 Security Conformance Requirements 115
+6. IANA Considerations (registered and private extensions) 116
+ 6.1 Typed 'keyword' and 'enum' Extensions 116
+ 6.2 Attribute Extensibility 119
+ 6.3 Attribute Syntax Extensibility 119
+ 6.4 Operation Extensibility 120
+ 6.5 Attribute Groups 120
+ 6.6 Status Code Extensibility 120
+ 6.7 Registration of MIME types/sub-types for document-formats 121
+ 6.8 Registration of charsets for use in 'charset' attribute values121
+7. Internationalization Considerations 121
+8. Security Considerations 125
+ 8.1 Security Scenarios 126
+ 8.1.1 Client and Server in the Same Security Domain 126
+ 8.1.2 Client and Server in Different Security Domains 126
+ 8.1.3 Print by Reference 127
+ 8.2 URIs for SSL3 and non-SSL3 Access 127
+ 8.3 The "requesting-user-name" (name(MAX)) Operation Attribute 127
+ 8.4 Restricted Queries 129
+ 8.5 Queries on jobs submitted using non-IPP protocols 129
+ 8.6 IPP Security Application Profile for SSL3 130
+9. References 131
+10. Authors' Addresses 134
+11. Formats for IPP Registration Proposals 136
+ 11.1 Type2 keyword attribute values registration 136
+ 11.2 Type3 keyword attribute values registration 137
+ 11.3 Type2 enum attribute values registration 137
+ 11.4 Type3 enum attribute values registration 137
+ 11.5 Attribute registration 138
+ 11.6 Attribute Syntax registration 138
+ 11.7 Operation registration 139
+ 11.8 Attribute Group registration 139
+ 11.9 Status code registration 139
+12.APPENDIX A: Terminology 141
+
+
+
+deBry, et al. Experimental [Page 6]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ 12.1 Conformance Terminology 141
+ 12.1.1 NEED NOT 141
+ 12.2 Model Terminology 141
+ 12.2.1 Keyword 141
+ 12.2.2 Attributes 141
+ 12.2.2.1 Attribute Name 141
+ 12.2.2.2 Attribute Group Name 142
+ 12.2.2.3 Attribute Value 142
+ 12.2.2.4 Attribute Syntax 142
+ 12.2.3 Supports 142
+ 12.2.4 print-stream page 144
+ 12.2.5 impression 144
+13.APPENDIX B: Status Codes and Suggested Status Code Messages 145
+ 13.1 Status Codes 146
+ 13.1.1 Informational 146
+ 13.1.2 Successful Status Codes 146
+ 13.1.2.1 successful-ok (0x0000) 146
+ 13.1.2.2 successful-ok-ignored-or-substituted-attributes (0x0001) 146
+ 13.1.2.3 successful-ok-conflicting-attributes (0x0002) 147
+ 13.1.3 Redirection Status Codes 147
+ 13.1.4 Client Error Status Codes 147
+ 13.1.4.1 client-error-bad-request (0x0400) 147
+ 13.1.4.2 client-error-forbidden (0x0401) 147
+ 13.1.4.3 client-error-not-authenticated (0x0402) 148
+ 13.1.4.4 client-error-not-authorized (0x0403) 148
+ 13.1.4.5 client-error-not-possible (0x0404) 148
+ 13.1.4.6 client-error-timeout (0x0405) 148
+ 13.1.4.7 client-error-not-found (0x0406) 149
+ 13.1.4.8 client-error-gone (0x0407) 149
+ 13.1.4.9 client-error-request-entity-too-large (0x0408) 149
+ 13.1.4.10client-error-request-value-too-long (0x0409) 150
+ 13.1.4.11client-error-document-format-not-supported (0x040A) 150
+ 13.1.4.12client-error-attributes-or-values-not-supported (0x040B) 150
+ 13.1.4.13client-error-uri-scheme-not-supported (0x040C) 151
+ 13.1.4.14client-error-charset-not-supported (0x040D) 151
+ 13.1.4.15client-error-conflicting-attributes (0x040E) 151
+ 13.1.5 Server Error Status Codes 151
+ 13.1.5.1 server-error-internal-error (0x0500) 151
+ 13.1.5.2 server-error-operation-not-supported (0x0501) 152
+ 13.1.5.3 server-error-service-unavailable (0x0502) 152
+ 13.1.5.4 server-error-version-not-supported (0x0503) 152
+ 13.1.5.5 server-error-device-error (0x0504) 152
+ 13.1.5.6 server-error-temporary-error (0x0505) 153
+ 13.1.5.7 server-error-not-accepting-jobs (0x0506) 153
+ 13.1.5.8 server-error-busy (0x0507) 153
+ 13.1.5.9 server-error-job-canceled (0x0508) 153
+ 13.2 Status Codes for IPP Operations 153
+14.APPENDIX C: "media" keyword values 155
+
+
+
+deBry, et al. Experimental [Page 7]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+15.APPENDIX D: Processing IPP Attributes 160
+ 15.1 Fidelity 160
+ 15.2 Page Description Language (PDL) Override 161
+ 15.3 Using Job Template Attributes During Document Processing. 163
+16.APPENDIX E: Generic Directory Schema 166
+17.APPENDIX F: Change History for the Model and Semantics document 168
+18.FULL COPYRIGHT STATEMENT 173
+
+1. Introduction
+
+ The Internet Printing Protocol (IPP) is an application level protocol
+ that can be used for distributed printing using Internet tools and
+ technologies. IPP version 1.0 (IPP/1.0) focuses only on end user
+ functionality. This document is just one of a suite of documents
+ that fully define IPP. The full set of IPP documents includes:
+
+ Design Goals for an Internet Printing Protocol [RFC2567]
+ Rationale for the Structure and Model and Protocol for the Internet
+ Printing Protocol [RFC2568]
+ Internet Printing Protocol/1.0: Model and Semantics (this document)
+ Internet Printing Protocol/1.0: Encoding and Transport [RFC2565]
+ Internet Printing Protocol/1.0: Implementer's Guide [ipp-iig]
+ Mapping between LPD and IPP Protocols [RFC2569]
+
+ Anyone reading these documents for the first time is strongly
+ encouraged to read the IPP documents in the above order.
+
+ This document is laid out as follows:
+
+ - The rest of Section 1 is an introduction to the IPP simplified
+ model for distributed printing.
+ - Section 2 introduces the object types covered in the model with
+ their basic behaviors, attributes, and interactions.
+ - Section 3 defines the operations included in IPP/1.0. IPP
+ operations are synchronous, therefore, for each operation, there
+ is a both request and a response.
+ - Section 4 defines the attributes (and their syntaxes) that are
+ used in the model.
+ - Sections 5 - 6 summarizes the implementation conformance
+ requirements for objects that support the protocol and IANA
+ considerations, respectively.
+ - Sections 7 - 11 cover the Internationalization and Security
+ considerations as well as References, Author contact information,
+ and Formats for Registration Proposals.
+ - Sections 12 - 14 are appendices that cover Terminology, Status
+ Codes and Messages, and "media" keyword values.
+
+
+
+
+
+deBry, et al. Experimental [Page 8]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ Note: This document uses terms such as "attributes",
+ "keywords", and "support". These terms have special
+ meaning and are defined in the model terminology section
+ 12.2. Capitalized terms, such as MUST, MUST NOT, REQUIRED,
+ SHOULD, SHOULD NOT, MAY, NEED NOT, and OPTIONAL, have
+ special meaning relating to conformance. These terms are
+ defined in section 12.1 on conformance terminology, most of
+ which is taken from RFC 2119 [RFC2119].
+
+ - Section 15 is an appendix that helps to clarify the effects of
+ interactions between related attributes and their values.
+ - Section 16 is an appendix that enumerates the subset of Printer
+ attributes that form a generic directory schema. These
+ attributes are useful when registering a Printer so that a
+ client can find the Printer not just by name, but by filtered
+ searches as well.
+ - Section 17 is an appendix that provides a Change History
+ summarizing the clarification and changes that might affect an
+ implementation since the June 30, 1998 draft.
+
+1.1 Simplified Printing Model
+
+ In order to achieve its goal of realizing a workable printing
+ protocol for the Internet, the Internet Printing Protocol (IPP) is
+ based on a simplified printing model that abstracts the many
+ components of real world printing solutions. The Internet is a
+ distributed computing environment where requesters of print services
+ (clients, applications, printer drivers, etc.) cooperate and interact
+ with print service providers. This model and semantics document
+ describes a simple, abstract model for IPP even though the underlying
+ configurations may be complex "n-tier" client/server systems. An
+ important simplifying step in the IPP model is to expose only the key
+ objects and interfaces required for printing. The model described in
+ this model document does not include features, interfaces, and
+ relationships that are beyond the scope of the first version of IPP
+ (IPP/1.0). IPP/1.0 incorporates many of the relevant ideas and
+ lessons learned from other specification and development efforts
+ [HTPP] [ISO10175] [LDPA] [P1387.4] [PSIS] [RFC1179] [SWP]. IPP is
+ heavily influenced by the printing model introduced in the Document
+ Printing Application (DPA) [ISO10175] standard. Although DPA
+ specifies both end user and administrative features, IPP version 1.0
+ (IPP/1.0) focuses only on end user functionality.
+
+ The IPP/1.0 model encapsulates the important components of
+ distributed printing into two object types:
+
+ - Printer (Section 2.1)
+ - Job (Section 2.2)
+
+
+
+deBry, et al. Experimental [Page 9]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ Each object type has an associated set of operations (see section 3)
+ and attributes (see section 4).
+
+ It is important, however, to understand that in real system
+ implementations (which lie underneath the abstracted IPP/1.0 model),
+ there are other components of a print service which are not
+ explicitly defined in the IPP/1.0 model. The following figure
+ illustrates where IPP/1.0 fits with respect to these other
+ components.
+
+ +--------------+
+ | Application |
+ o +. . . . . . . |
+ \|/ | Spooler |
+ / \ +. . . . . . . | +---------+
+ End-User | Print Driver |---| File |
+ +-----------+ +-----+ +------+-------+ +----+----+
+ | Browser | | GUI | | |
+ +-----+-----+ +--+--+ | |
+ | | | |
+ | +---+------------+---+ |
+ N D S | | IPP Client |------------+
+ O I E | +---------+----------+
+ T R C | |
+ I E U |
+ F C R -------------- Transport ------------------
+ I T I
+ C O T | --+
+ A R Y +--------+--------+ |
+ T Y | IPP Server | |
+ I +--------+--------+ |
+ O | |
+ N +-----------------+ | IPP Printer
+ | Print Service | |
+ +-----------------+ |
+ | --+
+ +-----------------+
+ | Output Device(s)|
+ +-----------------+
+
+ An IPP Printer object encapsulates the functions normally associated
+ with physical output devices along with the spooling, scheduling and
+ multiple device management functions often associated with a print
+ server. Printer objects are optionally registered as entries in a
+ directory where end users find and select them based on some sort of
+ filtered and context based searching mechanism (see section 16). The
+ directory is used to store relatively static information about the
+ Printer, allowing end users to search for and find Printers that
+
+
+
+deBry, et al. Experimental [Page 10]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ match their search criteria, for example: name, context, printer
+ capabilities, etc. The more dynamic information, such as state,
+ currently loaded and ready media, number of jobs at the Printer,
+ errors, warnings, and so forth, is directly associated with the
+ Printer object itself rather than with the entry in the directory
+ which only represents the Printer object.
+
+ IPP clients implement the IPP protocol on the client side and give
+ end users (or programs running on behalf of end users) the ability to
+ query Printer objects and submit and manage print jobs. An IPP
+ server is just that part of the Printer object that implements the
+ server-side protocol. The rest of the Printer object implements (or
+ gateways into) the application semantics of the print service itself.
+ The Printer objects may be embedded in an output device or may be
+ implemented on a host on the network that communicates with an output
+ device.
+
+ When a job is submitted to the Printer object and the Printer object
+ validates the attributes in the submission request, the Printer
+ object creates a new Job object. The end user then interacts with
+ this new Job object to query its status and monitor the progress of
+ the job. End users may also cancel the print job by using the Job
+ object's Cancel-Job operation. The notification service is out of
+ scope for IPP/1.0, but using such a notification service, the end
+ user is able to register for and receive Printer specific and Job
+ specific events. An end user can query the status of Printer objects
+ and can follow the progress of Job objects by polling using the Get-
+ Printer-Attributes, Get-Jobs, and Get-Job-Attributes operations.
+
+2. IPP Objects
+
+ The IPP/1.0 model introduces objects of type Printer and Job. Each
+ type of object models relevant aspects of a real-world entity such as
+ a real printer or real print job. Each object type is defined as a
+ set of possible attributes that may be supported by instances of that
+ object type. For each object (instance), the actual set of supported
+ attributes and values describe a specific implementation. The
+ object's attributes and values describe its state, capabilities,
+ realizable features, job processing functions, and default behaviors
+ and characteristics. For example, the Printer object type is defined
+ as a set of attributes that each Printer object potentially supports.
+ In the same manner, the Job object type is defined as a set of
+ attributes that are potentially supported by each Job object.
+
+ Each attribute included in the set of attributes defining an object
+ type is labeled as:
+
+
+
+
+
+deBry, et al. Experimental [Page 11]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ - "REQUIRED": each object MUST support the attribute.
+ - "OPTIONAL": each object MAY support the attribute.
+
+ There is no such similar labeling of attribute values. However, if
+ an implementation supports an attribute, it MUST support at least one
+ of the possible values for that attribute.
+
+2.1 Printer Object
+
+ The major component of the IPP/1.0 model is the Printer object. A
+ Printer object implements the server-side of the IPP/1.0 protocol.
+ Using the protocol, end users may query the attributes of the Printer
+ object and submit print jobs to the Printer object. The actual
+ implementation components behind the Printer abstraction may take on
+ different forms and different configurations. However, the model
+ abstraction allows the details of the configuration of real
+ components to remain opaque to the end user. Section 3 describes
+ each of the Printer operations in detail.
+
+ The capabilities and state of a Printer object are described by its
+ attributes. Printer attributes are divided into two groups:
+
+ - "job-template" attributes: These attributes describe supported
+ job processing capabilities and defaults for the Printer object.
+ (See section 4.2)
+ - "printer-description" attributes: These attributes describe the
+ Printer object's identification, state, location, references to
+ other sources of information about the Printer object, etc. (see
+ section 4.4)
+
+ Since a Printer object is an abstraction of a generic document output
+ device and print service provider, a Printer object could be used to
+ represent any real or virtual device with semantics consistent with
+ the Printer object, such as a fax device, an imager, or even a CD
+ writer.
+
+ Some examples of configurations supporting a Printer object include:
+
+ 1) An output device with no spooling capabilities
+ 2) An output device with a built-in spooler
+ 3) A print server supporting IPP with one or more associated output
+ devices
+ 3a) The associated output devices may or may not be capable of
+ spooling jobs
+ 3b) The associated output devices may or may not support IPP
+
+
+
+
+
+
+deBry, et al. Experimental [Page 12]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ The following figures show some examples of how Printer objects can
+ be realized on top of various distributed printing configurations.
+ The embedded case below represents configurations 1 and 2. The hosted
+ and fan-out figures below represent configurations 3a and 3b.
+
+ Legend:
+
+ ##### indicates a Printer object which is
+ either embedded in an output device or is
+ hosted in a server. The Printer object
+ might or might not be capable of queuing/spooling.
+
+ any indicates any network protocol or direct
+ connect, including IPP
+
+
+ embedded printer:
+ output device
+ +---------------+
+ O +--------+ | ########### |
+ /|\ | client |------------IPP------------># Printer # |
+ / \ +--------+ | # Object # |
+ | ########### |
+ +---------------+
+
+
+ hosted printer:
+ +---------------+
+ O +--------+ ########### | |
+ /|\ | client |--IPP--># Printer #-any->| output device |
+ / \ +--------+ # Object # | |
+ ########### +---------------+
+
+
+
+ +---------------+
+ fan out: | |
+ +-->| output device |
+ any/ | |
+ O +--------+ ########### / +---------------+
+ /|\ | client |-IPP-># Printer #--*
+ / \ +--------+ # Object # \ +---------------+
+ ########### any\ | |
+ +-->| output device |
+ | |
+ +---------------+
+
+
+
+
+
+deBry, et al. Experimental [Page 13]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+2.2 Job Object
+
+ A Job object is used to model a print job. A Job object contains
+ documents. The information required to create a Job object is sent
+ in a create request from the end user via an IPP Client to the
+ Printer object. The Printer object validates the create request, and
+ if the Printer object accepts the request, the Printer object creates
+ the new Job object. Section 3 describes each of the Job operations
+ in detail.
+
+ The characteristics and state of a Job object are described by its
+ attributes. Job attributes are grouped into two groups as follows:
+
+ - "job-template" attributes: These attributes can be supplied by
+ the client or end user and include job processing instructions
+ which are intended to override any Printer object defaults and/or
+ instructions embedded within the document data. (See section 4.2)
+ - "job-description" attributes: These attributes describe the Job
+ object's identification, state, size, etc. The client supplies
+ some of these attributes, and the Printer object generates others.
+ (See section 4.3)
+
+ An implementation MUST support at least one document per Job object.
+ An implementation MAY support multiple documents per Job object. A
+ document is either:
+
+ - a stream of document data in a format supported by the Printer
+ object (typically a Page Description Language - PDL), or
+ - a reference to such a stream of document data
+
+ In IPP/1.0, a document is not modeled as an IPP object, therefore it
+ has no object identifier or associated attributes. All job
+ processing instructions are modeled as Job object attributes. These
+ attributes are called Job Template attributes and they apply equally
+ to all documents within a Job object.
+
+2.3 Object Relationships
+
+ IPP objects have relationships that are maintained persistently along
+ with the persistent storage of the object attributes.
+
+ A Printer object can represent either one or more physical output
+ devices or a logical device which "processes" jobs but never actually
+ uses a physical output device to put marks on paper. Examples of
+ logical devices include a Web page publisher or a gateway into an
+ online document archive or repository. A Printer object contains
+ zero or more Job objects.
+
+
+
+
+deBry, et al. Experimental [Page 14]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ A Job object is contained by exactly one Printer object, however the
+ identical document data associated with a Job object could be sent to
+ either the same or a different Printer object. In this case, a
+ second Job object would be created which would be almost identical to
+ the first Job object, however it would have new (different) Job
+ object identifiers (see section 2.4).
+
+ A Job object is either empty (before any documents have been added)
+ or contains one or more documents. If the contained document is a
+ stream of document data, that stream can be contained in only one
+ document. However, there can be identical copies of the stream in
+ other documents in the same or different Job objects. If the
+ contained document is just a reference to a stream of document data,
+ other documents (in the same or different Job object(s)) may contain
+ the same reference.
+
+2.4 Object Identity
+
+ All Printer and Job objects are identified by a Uniform Resource
+ Identifier (URI) [RFC2396] so that they can be persistently and
+ unambiguously referenced. The notion of a URI is a useful concept,
+ however, until the notion of URI is more stable (i.e., defined more
+ completely and deployed more widely), it is expected that the URIs
+ used for IPP objects will actually be URLs [RFC2396]. Since every
+ URL is a specialized form of a URI, even though the more generic term
+ URI is used throughout the rest of this document, its usage is
+ intended to cover the more specific notion of URL as well.
+
+ An administrator configures Printer objects to either support or not
+ support authentication and/or message privacy using SSL3 [SSL] (the
+ mechanism for security configuration is outside the scope of
+ IPP/1.0). In some situations, both types of connections (both
+ authenticated and unauthenticated) can be established using a single
+ communication channel that has some sort of negotiation mechanism.
+ In other situations, multiple communication channels are used, one
+ for each type of security configuration. Section 8 provides a full
+ description of all security considerations and configurations.
+
+ If a Printer object supports more than one communication channel,
+ some or all of those channels might support and/or require different
+ security mechanisms. In such cases, an administrator could expose
+ the simultaneous support for these multiple communication channels as
+ multiple URIs for a single Printer object where each URI represents
+ one of the communication channels to the Printer object. To support
+ this flexibility, the IPP Printer object type defines a multi-valued
+ identification attribute called the "printer-uri-supported"
+ attribute. It MUST contain at least one URI. It MAY contain more
+ than one URI. That is, every Printer object will have at least one
+
+
+
+deBry, et al. Experimental [Page 15]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ URI that identifies at least one communication channel to the Printer
+ object, but it may have more than one URI where each URI identifies a
+ different communication channel to the Printer object. The
+ "printer-uri-supported" attribute has a companion attribute, the
+ "uri-security-supported" attribute, that has the same cardinality as
+ "printer-uri-supported". The purpose of the "uri-security-supported"
+ attribute is to indicate the security mechanisms (if any) used for
+ each URI listed in "printer-uri-supported". These two attributes are
+ fully described in sections 4.4.1 and 4.4.2.
+
+ When a job is submitted to the Printer object via a create request,
+ the client supplies only a single Printer object URI. The client
+ supplied Printer object URI MUST be one of the values in the
+ "printer-uri-supported" Printer attribute.
+
+ Note: IPP/1.0 does not specify how the client obtains the client
+ supplied URI, but it is RECOMMENDED that a Printer object be
+ registered as an entry in a directory service. End-users and
+ programs can then interrogate the directory searching for Printers.
+ Section 16 defines a generic schema for Printer object entries in the
+ directory service and describes how the entry acts as a bridge to the
+ actual IPP Printer object. The entry in the directory that
+ represents the IPP Printer object includes the possibly many URIs for
+ that Printer object as values in one its attributes.
+
+ When a client submits a create request to the Printer object, the
+ Printer object validates the request and creates a new Job object.
+ The Printer object assigns the new Job object a URI which is stored
+ in the "job-uri" Job attribute. This URI is then used by clients as
+ the target for subsequent Job operations. The Printer object
+ generates a Job URI based on its configured security policy and the
+ URI used by the client in the create request.
+
+ For example, consider a Printer object that supports both a
+ communication channel secured by the use of SSL3 (using HTTP over
+ SSL3 with an "https" schemed URI) and another open communication
+ channel that is not secured with SSL3 (using a simple "http" schemed
+ URI). If a client were to submit a job using the secure URI, the
+ Printer object would assign the new Job object a secure URI as well.
+ If a client were to submit a job using the open-channel URI, the
+ Printer would assign the new Job object an open-channel URI.
+
+ In addition, the Printer object also populates the Job object's
+ "job-printer-uri" attribute. This is a reference back to the Printer
+ object that created the Job object. If a client only has access to a
+ Job object's "job-uri" identifier, the client can query the Job's
+ "job-printer-uri" attribute in order to determine which Printer
+ object created the Job object. If the Printer object supports more
+
+
+
+deBry, et al. Experimental [Page 16]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ than one URI, the Printer object picks the one URI supplied by the
+ client when creating the job to build the value for and to populate
+ the Job's "job-printer-uri" attribute.
+
+ Allowing Job objects to have URIs allows for flexibility and
+ scalability. For example, in some implementations, the Printer
+ object might create Jobs that are processed in the same local
+ environment as the Printer object itself. In this case, the Job URI
+ might just be a composition of the Printer's URI and some unique
+ component for the Job object, such as the unique 32-bit positive
+ integer mentioned later in this paragraph. In other implementations,
+ the Printer object might be a central clearing-house for validating
+ all Job object creation requests, but the Job object itself might be
+ created in some environment that is remote from the Printer object.
+ In this case, the Job object's URI may have no physical-location
+ relationship at all to the Printer object's URI. Again, the fact
+ that Job objects have URIs allows for flexibility and scalability,
+ however, many existing printing systems have local models or
+ interface constraints that force print jobs to be identified using
+ only a 32-bit positive integer rather than an independent URI. This
+ numeric Job ID is only unique within the context of the Printer
+ object to which the create request was originally submitted.
+ Therefore, in order to allow both types of client access to IPP Job
+ objects (either by Job URI or by numeric Job ID), when the Printer
+ object successfully processes a create request and creates a new Job
+ object, the Printer object MUST generate both a Job URI and a Job ID.
+ The Job ID (stored in the "job-id" attribute) only has meaning in the
+ context of the Printer object to which the create request was
+ originally submitted. This requirement to support both Job URIs and
+ Job IDs allows all types of clients to access Printer objects and Job
+ objects no matter the local constraints imposed on the client
+ implementation.
+
+ In addition to identifiers, Printer objects and Job objects have
+ names ("printer-name" and "job-name"). An object name NEED NOT be
+ unique across all instances of all objects. A Printer object's name
+ is chosen and set by an administrator through some mechanism outside
+ the scope of IPP/1.0. A Job object's name is optionally chosen and
+ supplied by the IPP client submitting the job. If the client does
+ not supply a Job object name, the Printer object generates a name for
+ the new Job object. In all cases, the name only has local meaning.
+
+ To summarize:
+
+ - Each Printer object is identified with one or more URIs. The
+ Printer's "printer-uri-supported" attribute contains the URI(s).
+
+
+
+
+
+deBry, et al. Experimental [Page 17]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ - The Printer object's "uri-security-supported" attribute
+ identifies the communication channel security protocols that may
+ or may not have been configured for the various Printer object
+ URIs (e.g., 'ssl3' or 'none').
+ - Each Job object is identified with a Job URI. The Job's "job-uri"
+ attribute contains the URI.
+ - Each Job object is also identified with Job ID which is a 32-bit,
+ positive integer. The Job's "job-id" attribute contains the Job
+ ID. The Job ID is only unique within the context of the Printer
+ object which created the Job object.
+ - Each Job object has a "job-printer-uri" attribute which contains
+ the URI of the Printer object that was used to create the Job
+ object. This attribute is used to determine the Printer object
+ that created a Job object when given only the URI for the Job
+ object. This linkage is necessary to determine the languages,
+ charsets, and operations which are supported on that Job (the
+ basis for such support comes from the creating Printer object).
+ - Each Printer object has a name (which is not necessarily unique).
+ The administrator chooses and sets this name through some
+ mechanism outside the scope of IPP/1.0 itself. The Printer
+ object's "printer-name" attribute contains the name.
+ - Each Job object has a name (which is not necessarily unique). The
+ client optionally supplies this name in the create request. If
+ the client does not supply this name, the Printer object generates
+ a name for the Job object. The Job object's "job-name" attribute
+ contains the name.
+
+3. IPP Operations
+
+ IPP objects support operations. An operation consists of a request
+ and a response. When a client communicates with an IPP object, the
+ client issues an operation request to the URI for that object.
+ Operation requests and responses have parameters that identify the
+ operation. Operations also have attributes that affect the run-time
+ characteristics of the operation (the intended target, localization
+ information, etc.). These operation-specific attributes are called
+ operation attributes (as compared to object attributes such as
+ Printer object attributes or Job object attributes). Each request
+ carries along with it any operation attributes, object attributes,
+ and/or document data required to perform the operation. Each request
+ requires a response from the object. Each response indicates success
+ or failure of the operation with a status code as a response
+ parameter. The response contains any operation attributes, object
+ attributes, and/or status messages generated during the execution of
+ the operation request.
+
+
+
+
+
+
+deBry, et al. Experimental [Page 18]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ This section describes the semantics of the IPP operations, both
+ requests and responses, in terms of the parameters, attributes, and
+ other data associated with each operation.
+
+ The IPP/1.0 Printer operations are:
+
+ Print-Job (section 3.2.1)
+ Print-URI (section 3.2.2)
+ Validate-Job (section 3.2.3)
+ Create-Job (section 3.2.4)
+ Get-Printer-Attributes (section 3.2.5)
+ Get-Jobs (section 3.2.6)
+
+ The Job operations are:
+
+ Send-Document (section 3.3.1)
+ Send-URI (section 3.3.2)
+ Cancel-Job (section 3.3.3)
+ Get-Job-Attributes (section 3.3.4)
+
+ The Send-Document and Send-URI Job operations are used to add a new
+ document to an existing multi-document Job object created using the
+ Create-Job operation.
+
+3.1 Common Semantics
+
+ All IPP operations require some common parameters and operation
+ attributes. These common elements and their semantic characteristics
+ are defined and described in more detail in the following sections.
+
+3.1.1 Required Parameters
+
+ Every operation request contains the following REQUIRED parameters:
+
+ - a "version-number",
+ - an "operation-id",
+ - a "request-id", and
+ - the attributes that are REQUIRED for that type of request.
+
+ Every operation response contains the following REQUIRED parameters:
+
+ - a "version-number",
+ - a "status-code",
+ - the "request-id" that was supplied in the corresponding request,
+ and
+ - the attributes that are REQUIRED for that type of response.
+
+
+
+
+
+deBry, et al. Experimental [Page 19]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ The encoding and transport document [RFC2565] defines special rules
+ for the encoding of these parameters. All other operation elements
+ are represented using the more generic encoding rules for attributes
+ and groups of attributes.
+
+3.1.2 Operation IDs and Request IDs
+
+ Each IPP operation request includes an identifying "operation-id"
+ value. Valid values are defined in the "operations-supported"
+ Printer attribute section (see section 4.4.13). The client specifies
+ which operation is being requested by supplying the correct
+ "operation-id" value.
+
+ In addition, every invocation of an operation is identified by a
+ "request-id" value. For each request, the client chooses the
+ "request-id" which MUST be an integer (possibly unique depending on
+ client requirements) in the range from 1 to 2**31 - 1 (inclusive).
+ This "request-id" allows clients to manage multiple outstanding
+ requests. The receiving IPP object copies all 32-bits of the client-
+ supplied "request-id" attribute into the response so that the client
+ can match the response with the correct outstanding request, even if
+ the "request-id" is out of range. If the request is terminated
+ before the complete "request-id" is received, the IPP object rejects
+ the request and returns a response with a "request-id" of 0.
+
+ Note: In some cases, the transport protocol underneath IPP might be a
+ connection oriented protocol that would make it impossible for a
+ client to receive responses in any order other than the order in
+ which the corresponding requests were sent. In such cases, the
+ "request-id" attribute would not be essential for correct protocol
+ operation. However, in other mappings, the operation responses can
+ come back in any order. In these cases, the "request-id" would be
+ essential.
+
+3.1.3 Attributes
+
+ Operation requests and responses are both composed of groups of
+ attributes and/or document data. The attributes groups are:
+
+ - Operation Attributes: These attributes are passed in the
+ operation and affect the IPP object's behavior while processing
+ the operation request and may affect other attributes or groups
+ of attributes. Some operation attributes describe the document
+ data associated with the print job and are associated with new
+ Job objects, however most operation attributes do not persist
+ beyond the life of the operation. The description of each
+ operation attribute includes conformance statements indicating
+ which operation attributes are REQUIRED and which are OPTIONAL
+
+
+
+deBry, et al. Experimental [Page 20]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ for an IPP object to support and which attributes a client MUST
+ supply in a request and an IPP object MUST supply in a response.
+ - Job Template Attributes: These attributes affect the processing
+ of a job. A client OPTIONALLY supplies Job Template Attributes
+ in a create request, and the receiving object MUST be prepared to
+ receive all supported attributes. The Job object can later be
+ queried to find out what Job Template attributes were originally
+ requested in the create request, and such attributes are returned
+ in the response as Job Object Attributes. The Printer object can
+ be queried about its Job Template attributes to find out what
+ type of job processing capabilities are supported and/or what the
+ default job processing behaviors are, though such attributes are
+ returned in the response as Printer Object Attributes. The
+ "ipp-attribute-fidelity" operation attribute affects processing
+ of all client-supplied Job Template attributes (see section 15
+ for a full description of "ipp-attribute-fidelity" and its
+ relationship to other attributes).
+ - Job Object Attributes: These attributes are returned in response
+ to a query operation directed at a Job object.
+ - Printer Object Attributes: These attributes are returned in
+ response to a query operation directed at a Printer object.
+ - Unsupported Attributes: In a create request, the client supplies
+ a set of Operation and Job Template attributes. If any of these
+ attributes or their values is unsupported by the Printer object,
+ the Printer object returns the set of unsupported attributes in
+ the response. Section 15 gives a full description of how Job
+ Template attributes supplied by the client in a create request
+ are processed by the Printer object and how unsupported
+ attributes are returned to the client. Because of extensibility,
+ any IPP object might receive a request that contains new or
+ unknown attributes or values for which it has no support. In such
+ cases, the IPP object processes what it can and returns the
+ unsupported attributes in the response.
+
+ Later in this section, each operation is formally defined by
+ identifying the allowed and expected groups of attributes for each
+ request and response. The model identifies a specific order for each
+ group in each request or response, but the attributes within each
+ group may be in any order, unless specified otherwise.
+
+ Each attribute specification includes the attribute's name followed
+ by the name of its attribute syntax(es) in parenthesizes. In
+ addition, each 'integer' attribute is followed by the allowed range
+ in parentheses, (m:n), for values of that attribute. Each 'text' or
+ 'name' attribute is followed by the maximum size in octets in
+ parentheses, (size), for values of that attribute. For more details
+ on attribute syntax notation, see the descriptions of these
+ attributes syntaxes in section 4.1.
+
+
+
+deBry, et al. Experimental [Page 21]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ Note: Document data included in the operation is not strictly an
+ attribute, but it is treated as a special attribute group for
+ ordering purposes. The only operations that support supplying the
+ document data within an operation request are Print-Job and Send-
+ Document. There are no operation responses that include document
+ data.
+
+ Note: Some operations are REQUIRED for IPP objects to support; the
+ others are OPTIONAL (see section 5.2.2). Therefore, before using an
+ OPTIONAL operation, a client SHOULD first use the REQUIRED Get-
+ Printer-Attributes operation to query the Printer's "operations-
+ supported" attribute in order to determine which OPTIONAL Printer and
+ Job operations are actually supported. The client SHOULD NOT use an
+ OPTIONAL operation that is not supported. When an IPP object
+ receives a request to perform an operation it does not support, it
+ returns the 'server-error-operation-not-supported' status code (see
+ section 13.1.5.2). An IPP object is non-conformant if it does not
+ support a REQUIRED operation.
+
+3.1.4 Character Set and Natural Language Operation Attributes
+
+ Some Job and Printer attributes have values that are text strings and
+ names intended for human understanding rather than machine
+ understanding (see the 'text' and 'name' attribute syntax
+ descriptions in section 4.1). The following sections describe two
+ special Operation Attributes called "attributes-charset" and
+ "attributes-natural-language". These attributes are always part of
+ the Operation Attributes group. For most attribute groups, the order
+ of the attributes within the group is not important. However, for
+ these two attributes within the Operation Attributes group, the order
+ is critical. The "attributes-charset" attribute MUST be the first
+ attribute in the group and the "attributes-natural-language"
+ attribute MUST be the second attribute in the group. In other words,
+ these attributes MUST be supplied in every IPP request and response,
+ they MUST come first in the group, and MUST come in the specified
+ order. For job creation operations, the IPP Printer implementation
+ saves these two attributes with the new Job object as Job Description
+ attributes. For the sake of brevity in this document, these
+ operation attribute descriptions are not repeated with every
+ operation request and response, but have a reference back to this
+ section instead.
+
+3.1.4.1 Request Operation Attributes
+
+ The client MUST supply and the Printer object MUST support the
+ following REQUIRED operation attributes in every IPP/1.0 operation
+ request:
+
+
+
+
+deBry, et al. Experimental [Page 22]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ "attributes-charset" (charset):
+ This operation attribute identifies the charset (coded character
+ set and encoding method) used by any 'text' and 'name'
+ attributes that the client is supplying in this request. It
+ also identifies the charset that the Printer object MUST use (if
+ supported) for all 'text' and 'name' attributes and status
+ messages that the Printer object returns in the response to this
+ request. See Sections 4.1.1 and 4.1.2 for the specification of
+ the 'text' and 'name' attribute syntaxes.
+
+ All clients and IPP objects MUST support the 'utf-8' charset
+ [RFC2279] and MAY support additional charsets provided that they
+ are registered with IANA [IANA-CS]. If the Printer object does
+ not support the client supplied charset value, the Printer
+ object MUST reject the request, set the "attributes-charset" to
+ 'utf-8' in the response, and return the 'client-error-charset-
+ not-supported' status code and any 'text' or 'name' attributes
+ using the 'utf-8' charset. The Printer object MUST indicate the
+ charset(s) supported as the values of the "charset-supported"
+ Printer attribute (see Section 4.4.15), so that the client can
+ query to determine which charset(s) are supported.
+
+ Note to client implementers: Since IPP objects are only required
+ to support the 'utf-8' charset, in order to maximize
+ interoperability with multiple IPP object implementations, a
+ client may want to supply 'utf-8' in the "attributes-charset"
+ operation attribute, even though the client is only passing and
+ able to present a simpler charset, such as US-ASCII or ISO-
+ 8859-1. Then the client will have to filter out (or charset
+ convert) those characters that are returned in the response that
+ it cannot present to its user. On the other hand, if both the
+ client and the IPP objects also support a charset in common
+ besides utf-8, the client may want to use that charset in order
+ to avoid charset conversion or data loss.
+
+ See the 'charset' attribute syntax description in Section 4.1.7
+ for the syntax and semantic interpretation of the values of this
+ attribute and for example values.
+
+ "attributes-natural-language" (naturalLanguage):
+ This operation attribute identifies the natural language used by
+ any 'text' and 'name' attributes that the client is supplying in
+ this request. This attribute also identifies the natural
+ language that the Printer object SHOULD use for all 'text' and '
+ name' attributes and status messages that the Printer object
+ returns in the response to this request.
+
+
+
+
+
+deBry, et al. Experimental [Page 23]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ There are no REQUIRED natural languages required for the Printer
+ object to support. However, the Printer object's "generated-
+ natural-language-supported" attribute identifies the natural
+ languages supported by the Printer object and any contained Job
+ objects for all text strings generated by the IPP object. A
+ client MAY query this attribute to determine which natural
+ language(s) are supported for generated messages.
+
+ For any of the attributes for which the Printer object generates
+ text, i.e., for the "job-state-message", "printer-state-
+ message", and status messages (see Section 3.1.6), the Printer
+ object MUST be able to generate these text strings in any of its
+ supported natural languages. If the client requests a natural
+ language that is not supported, the Printer object MUST return
+ these generated messages in the Printer's configured natural
+ language as specified by the Printer's "natural-language-
+ configured" attribute" (see Section 4.4.16).
+
+ For other 'text' and 'name' attributes supplied by the client,
+ authentication system, operator, system administrator, or
+ manufacturer (i.e., for "job-originating-user-name", "printer-
+ name" (name), "printer-location" (text), "printer-info" (text),
+ and "printer-make-and-model" (text)), the Printer object is only
+ required to support the configured natural language of the
+ Printer identified by the Printer object's "natural-language-
+ configured" attribute, though support of additional natural
+ languages for these attributes is permitted.
+
+ For any 'text' or 'name' attribute in the request that is in a
+ different natural language than the value supplied in the
+ "attributes-natural-language" operation attribute, the client
+ MUST use the Natural Language Override mechanism (see sections
+ 4.1.1.2 and 4.1.2.2) for each such attribute value supplied.
+ The client MAY use the Natural Language Override mechanism
+ redundantly, i.e., use it even when the value is in the same
+ natural language as the value supplied in the "attributes-
+ natural-language" operation attribute of the request.
+
+ The IPP object MUST accept any natural language and any Natural
+ Language Override, whether the IPP object supports that natural
+ language or not (and independent of the value of the "ipp-
+ attribute-fidelity" Operation attribute). That is the IPP
+ object accepts all client supplied values no matter what the
+ values are in the Printer object's "generated-natural-language-
+ supported" attribute. That attribute, "generated-natural-
+ language-supported", only applies to generated messages,
+
+
+
+
+
+deBry, et al. Experimental [Page 24]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ not client supplied messages. The IPP object MUST remember that
+ natural language for all client-supplied attributes, and when
+ returning those attributes in response to a query, the IPP
+ object MUST indicate that natural language.
+
+ Each value whose attribute syntax type is 'text' or 'name' (see
+ sections 4.1.1 and 4.1.2) has an Associated Natural-Language.
+ This document does not specify how this association is stored in
+ a Printer or Job object. When such a value is encoded in a
+ request or response, the natural language is either implicit or
+ explicit:
+
+ - In the implicit case, the value contains only the
+ text/name value, and the language is specified by the
+ "attributes-natural-language" operation attribute in the
+ request or response (see sections 4.1.1.1
+ textWithoutLanguage and 4.1.2.1 nameWithoutLanguage).
+
+ - In the explicit case (also known as the Natural-Language
+ Override case), the value contains both the language and
+ the text/name value (see sections 4.1.1.2
+ textWithLanguage and 4.1.2.2 nameWithLanguage).
+
+ For example, the "job-name" attribute MAY be supplied by the
+ client in a create request. The text value for this attribute
+ will be in the natural language identified by the "attribute-
+ natural-language" attribute, or if different, as identified by
+ the Natural Language Override mechanism. If supplied, the IPP
+ object will use the value of the "job-name" attribute to
+ populate the Job object's "job-name" attribute. Whenever any
+ client queries the Job object's "job-name" attribute, the IPP
+ object returns the attribute as stored and uses the Natural
+ Language Override mechanism to specify the natural language, if
+ it is different from that reported in the "attributes-natural-
+ language" operation attribute of the response. The IPP object
+ MAY use the Natural Language Override mechanism redundantly,
+ i.e., use it even when the value is in the same natural language
+ as the value supplied in the "attributes-natural-language"
+ operation attribute of the response.
+
+ An IPP object MUST NOT reject a request based on a supplied
+ natural language in an "attributes-natural-language" Operation
+ attribute or in any attribute that uses the Natural Language
+ Override.
+
+ See the 'naturalLanguage' attribute syntax description in
+ section 4.1.8 for the syntax and semantic interpretation of the
+ values of this attribute and for example values.
+
+
+
+deBry, et al. Experimental [Page 25]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ Clients SHOULD NOT supply 'text' or 'name' attributes that use an
+ illegal combination of natural language and charset. For example,
+ suppose a Printer object supports charsets 'utf-8', 'iso-8859-1', and
+ 'iso-8859-7'. Suppose also, that it supports natural languages 'en'
+ (English), 'fr' (French), and 'el' (Greek). Although the Printer
+ object supports the charset 'iso-8859-1' and natural language 'el',
+ it probably does not support the combination of Greek text strings
+ using the 'iso-8859-1' charset. The Printer object handles this
+ apparent incompatibility differently depending on the context in
+ which it occurs:
+
+ - In a create request: If the client supplies a text or name
+ attribute (for example, the "job-name" operation attribute) that
+ uses an apparently incompatible combination, it is a client
+ choice that does not affect the Printer object or its correct
+ operation. Therefore, the Printer object simply accepts the
+ client supplied value, stores it with the Job object, and
+ responds back with the same combination whenever the client (or
+ any client) queries for that attribute.
+ - In a query-type operation, like Get-Printer-Attributes: If the
+ client requests an apparently incompatible combination, the
+ Printer object responds (as described in section 3.1.4.2) using
+ the Printer's configured natural language rather than the natural
+ language requested by the client.
+
+ In either case, the Printer object does not reject the request
+ because of the apparent incompatibility. The potential incompatible
+ combination of charset and natural language can occur either at the
+ global operation level or at the Natural Language Override
+ attribute-by-attribute level. In addition, since the response always
+ includes explicit charset and natural language information, there is
+ never any question or ambiguity in how the client interprets the
+ response.
+
+3.1.4.2 Response Operation Attributes
+
+ The Printer object MUST supply and the client MUST support the
+ following REQUIRED operation attributes in every IPP/1.0 operation
+ response:
+
+ "attributes-charset" (charset):
+ This operation attribute identifies the charset used by any '
+ text' and 'name' attributes that the Printer object is returning
+ in this response. The value in this response MUST be the same
+ value as the "attributes-charset" operation attribute supplied
+ by the client in the request. If this is not possible
+
+
+
+
+
+deBry, et al. Experimental [Page 26]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ (i.e., the charset requested is not supported), the request
+ would have been rejected. See "attributes-charset" described in
+ Section 3.1.4.1 above.
+
+ If the Printer object supports more than just the 'utf-8'
+ charset, the Printer object MUST be able to code convert between
+ each of the charsets supported on a highest fidelity possible
+ basis in order to return the 'text' and 'name' attributes in the
+ charset requested by the client. However, some information loss
+ MAY occur during the charset conversion depending on the
+ charsets involved. For example, the Printer object may convert
+ from a UTF-8 'a' to a US-ASCII 'a' (with no loss of
+ information), from an ISO Latin 1 CAPITAL LETTER A WITH ACUTE
+ ACCENT to US-ASCII 'A' (losing the accent), or from a UTF-8
+ Japanese Kanji character to some ISO Latin 1 error character
+ indication such as '?', decimal code equivalent, or to the
+ absence of a character, depending on implementation.
+
+ Note: Whether an implementation that supports more than one
+ charset stores the data in the charset supplied by the client or
+ code converts to one of the other supported charsets, depends on
+ implementation. The strategy should try to minimize loss of
+ information during code conversion. On each response, such an
+ implementation converts from its internal charset to that
+ requested.
+
+ "attributes-natural-language" (naturalLanguage):
+ This operation attribute identifies the natural language used by
+ any 'text' and 'name' attributes that the IPP object is
+ returning in this response. Unlike the "attributes-charset"
+ operation attribute, the IPP object NEED NOT return the same
+ value as that supplied by the client in the request. The IPP
+ object MAY return the natural language of the Job object or the
+ Printer's configured natural language as identified by the
+ Printer object's "natural-language-configured" attribute, rather
+ than the natural language supplied by the client. For any '
+ text' or 'name' attribute or status message in the response that
+ is in a different natural language than the value returned in
+ the "attributes-natural-language" operation attribute, the IPP
+ object MUST use the Natural Language Override mechanism (see
+ sections 4.1.1.2 and 4.1.2.2) on each attribute value returned.
+ The IPP object MAY use the Natural Language Override mechanism
+ redundantly, i.e., use it even when the value is in the same
+ natural language as the value supplied in the "attributes-
+ natural-language" operation attribute of the response.
+
+
+
+
+
+
+deBry, et al. Experimental [Page 27]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+3.1.5 Operation Targets
+
+ All IPP operations are directed at IPP objects. For Printer
+ operations, the operation is always directed at a Printer object
+ using one of its URIs (i.e., one of the values in the Printer
+ object's "printer-uri-supported" attribute). Even if the Printer
+ object supports more than one URI, the client supplies only one URI
+ as the target of the operation. The client identifies the target
+ object by supplying the correct URI in the "printer-uri (uri)"
+ operation attribute.
+
+ For Job operations, the operation is directed at either:
+
+ - The Job object itself using the Job object's URI. In this case,
+ the client identifies the target object by supplying the correct
+ URI in the "job-uri (uri)" operation attribute.
+ - The Printer object that created the Job object using both the
+ Printer objects URI and the Job object's Job ID. Since the
+ Printer object that created the Job object generated the Job ID,
+ it MUST be able to correctly associate the client supplied Job ID
+ with the correct Job object. The client supplies the Printer
+ object's URI in the "printer-uri (uri)" operation attribute and
+ the Job object's Job ID in the "job-id (integer(1:MAX))"
+ operation attribute.
+
+ If the operation is directed at the Job object directly using the Job
+ object's URI, the client MUST NOT include the redundant "job-id"
+ operation attribute.
+
+ The operation target attributes are REQUIRED operation attributes
+ that MUST be included in every operation request. Like the charset
+ and natural language attributes (see section 3.1.4), the operation
+ target attributes are specially ordered operation attributes. In all
+ cases, the operation target attributes immediately follow the
+ "attributes-charset" and "attributes-natural-language" attributes
+ within the operation attribute group, however the specific ordering
+ rules are:
+
+ - In the case where there is only one operation target attribute
+ (i.e., either only the "printer-uri" attribute or only the "job-
+ uri" attribute), that attribute MUST be the third attribute in
+ the operation attributes group.
+ - In the case where Job operations use two operation target
+ attributes (i.e., the "printer-uri" and "job-id" attributes), the
+ "printer-uri" attribute MUST be the third attribute and the
+ "job-id" attribute MUST be the fourth attribute.
+
+
+
+
+
+deBry, et al. Experimental [Page 28]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ In all cases, the target URIs contained within the body of IPP
+ operation requests and responses must be in absolute format rather
+ than relative format (a relative URL identifies a resource with the
+ scope of the HTTP server, but does not include scheme, host or port).
+
+ The following rules apply to the use of port numbers in URIs that
+ identify IPP objects:
+
+ 1. If the URI scheme allows the port number to be explicitly
+ included in the URI string, and a port number is specified
+ within the URI, then that port number MUST be used by the client
+ to contact the IPP object.
+
+ 2. If the URI scheme allows the port number to be explicitly
+ included in the URI string, and a port number is not specified
+ within the URI, then default port number implied by that URI
+ scheme MUST be used by the client to contact the IPP object.
+
+ 3. If the URI scheme does not allow an explicit port number to be
+ specified within the URI, then the default port number implied
+ by that URI MUST be used by the client to contact the IPP
+ object.
+
+ Note: The IPP encoding and transport document [RFC2565] shows a
+ mapping of IPP onto HTTP/1.1 and defines a new default port number
+ for using IPP over HTTP/1.1.
+
+3.1.6 Operation Status Codes and Messages
+
+ Every operation response includes a REQUIRED "status-code" parameter
+ and an OPTIONAL "status-message" operation attribute. The "status-
+ code" provides information on the processing of a request. A
+ "status-message" attribute provides a short textual description of
+ the status of the operation. The status code is intended for use by
+ automata, and the status message is intended for the human end user.
+ If a response does include a "status-message" attribute, an IPP
+ client NEED NOT examine or display the message, however it SHOULD do
+ so in some implementation specific manner.
+
+ The "status-code" value is a numeric value that has semantic meaning.
+ The "status-code" syntax is similar to a "type2 enum" (see section
+ 4.1 on "Attribute Syntaxes") except that values can range only from
+ 0x0000 to 0x7FFF. Section 13 describes the status codes, assigns the
+ numeric values, and suggests a corresponding status message for each
+ status code. The "status-message" attribute's syntax is "text(255)".
+ A client implementation of IPP SHOULD convert status code values into
+ any localized message that has semantic meaning to the end user.
+
+
+
+
+deBry, et al. Experimental [Page 29]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ If the Printer object supports the "status-message" operation
+ attribute, the Printer object MUST be able to generate this message
+ in any of the natural languages identified by the Printer object's
+ "generated-natural-language-supported" attribute (see the
+ "attributes-natural-language" operation attribute specified in
+ section 3.1.4.1). As described in section 3.1.4.1 for any returned '
+ text' attribute, if there is a choice for generating this message,
+ the Printer object uses the natural language indicated by the value
+ of the "attributes-natural-language" in the client request if
+ supported, otherwise the Printer object uses the value in the Printer
+ object's own "natural-language-configured" attribute. If the Printer
+ object supports the "status-message" operation attribute, it SHOULD
+ use the REQUIRED 'utf-8' charset to return a status message for the
+ following error status codes (see section 13): 'client-error-bad-
+ request', 'client-error-charset-not-supported', 'server-error-
+ internal-error', 'server-error-operation-not-supported', and '
+ server-error-version-not-supported'. In this case, it MUST set the
+ value of the "attributes-charset" operation attribute to 'utf-8' in
+ the error response.
+
+3.1.7 Versions
+
+ Each operation request and response carries with it a "version-
+ number" parameter. Each value of the "version-number" is in the form
+ "X.Y" where X is the major version number and Y is the minor version
+ number. By including a version number in the client request, it
+ allows the client to identify which version of IPP it is interested
+ in using. If the IPP object does not support that version, the
+ object responds with a status code of 'server-error-version-not-
+ supported' along with the closest version number that is supported
+ (see section 13.1.5.4).
+
+ There is no version negotiation per se. However, if after receiving
+ a 'server-error-version-not-supported' status code from an IPP
+ object, there is nothing that prevents a client from trying again
+ with a different version number. In order to conform to IPP/1.0, an
+ implementation MUST support at least version '1.0'.
+
+ There is only one notion of "version number" that covers both IPP
+ Model and IPP Protocol changes. Thus the version number MUST change
+ when introducing a new version of the Model and Semantics document
+ [RFC2566] or a new version of the Encoding and Transport document
+ [RFC2565].
+
+ Changes to the major version number indicate structural or syntactic
+ changes that make it impossible for older version of IPP clients and
+ Printer objects to correctly parse and process the new or changed
+ attributes, operations and responses. If the major version number
+
+
+
+deBry, et al. Experimental [Page 30]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ changes, the minor version numbers is set to zero. As an example,
+ adding the "ipp-attribute-fidelity" attribute (if it had not been
+ part of version '1.0'), would have required a change to the major
+ version number. Items that might affect the changing of the major
+ version number include any changes to the Model and Semantics
+ document [RFC2566] or the Encoding and Transport [RFC2565] itself,
+ such as:
+
+ - reordering of ordered attributes or attribute sets
+ - changes to the syntax of existing attributes
+ - changing Operation or Job Template attributes from OPTIONAL to
+ REQUIRED and vice versa
+ - adding REQUIRED (for an IPP object to support) operation
+ attributes
+ - adding REQUIRED (for an IPP object to support) operation
+ attribute groups
+ - adding values to existing operation attributes
+ - adding REQUIRED operations
+
+ Changes to the minor version number indicate the addition of new
+ features, attributes and attribute values that may not be understood
+ by all IPP objects, but which can be ignored if not understood.
+ Items that might affect the changing of the minor version number
+ include any changes to the model objects and attributes but not the
+ encoding and transport rules [RFC2565] (except adding attribute
+ syntaxes). Examples of such changes are:
+
+ - grouping all extensions not included in a previous version into
+ a new version
+ - adding new attribute values
+ - adding new object attributes
+ - adding OPTIONAL (for an IPP object to support) operation
+ attributes (i.e., those attributes that an IPP object can ignore
+ without confusing clients)
+ - adding OPTIONAL (for an IPP object to support) operation
+ attribute groups (i.e., those attributes that an IPP object can
+ ignore without confusing clients)
+ - adding new attribute syntaxes
+ - adding OPTIONAL operations
+ - changing Job Description attributes or Printer Description
+ attributes from OPTIONAL to REQUIRED or vice versa.
+
+ The encoding of the "operation-id", the "version-number", the
+ "status-code", and the "request-id" MUST NOT change over any version
+ number (either major or minor). This rule guarantees that all future
+ versions will be backwards compatible with all previous versions (at
+ least for checking the "operation-id", the "version-number", and the
+ "request-id"). In addition, any protocol elements (attributes, error
+
+
+
+deBry, et al. Experimental [Page 31]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ codes, tags, etc.) that are not carried forward from one version to
+ the next are deprecated so that they can never be reused with new
+ semantics.
+
+ Implementations that support a certain major version NEED NOT support
+ ALL previous versions. As each new major version is defined (through
+ the release of a new specification), that major version will specify
+ which previous major versions MUST be supported in compliant
+ implementations.
+
+3.1.8 Job Creation Operations
+
+ In order to "submit a print job" and create a new Job object, a
+ client issues a create request. A create request is any one of
+ following three operation requests:
+
+ - The Print-Job Request: A client that wants to submit a print job
+ with only a single document uses the Print-Job operation. The
+ operation allows for the client to "push" the document data to
+ the Printer object by including the document data in the request
+ itself.
+
+ - The Print-URI Request: A client that wants to submit a print job
+ with only a single document (where the Printer object "pulls" the
+ document data instead of the client "pushing" the data to the
+ Printer object) uses the Print-URI operation. In this case, the
+ client includes in the request only a URI reference to the
+ document data (not the document data itself).
+
+ - The Create-Job Request: A client that wants to submit a print job
+ with multiple documents uses the Create-Job operation. This
+ operation is followed by an arbitrary number of Send-Document
+ and/or Send-URI operations (each creating another document for
+ the newly create Job object). The Send-Document operation
+ includes the document data in the request (the client "pushes"
+ the document data to the printer), and the Send-URI operation
+ includes only a URI reference to the document data in the request
+ (the Printer "pulls" the document data from the referenced
+ location). The last Send-Document or Send-URI request for a
+ given Job object includes a "last-document" operation attribute
+ set to 'true' indicating that this is the last request.
+
+ Throughout this model specification, the term "create request" is
+ used to refer to any of these three operation requests.
+
+ A Create-Job operation followed by only one Send-Document operation
+ is semantically equivalent to a Print-Job operation, however, for
+ performance reasons, the client SHOULD use the Print-Job operation
+
+
+
+deBry, et al. Experimental [Page 32]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ for all single document jobs. Also, Print-Job is a REQUIRED
+ operation (all implementations MUST support it) whereas Create-Job is
+ an OPTIONAL operation, hence some implementations might not support
+ it.
+
+ Job submission time is the point in time when a client issues a
+ create request. The initial state of every Job object is the '
+ pending' or 'pending-held' state. Later, the Printer object begins
+ processing the print job. At this point in time, the Job object's
+ state moves to 'processing'. This is known as job processing time.
+ There are validation checks that must be done at job submission time
+ and others that must be performed at job processing time.
+
+ At job submission time and at the time a Validate-Job operation is
+ received, the Printer MUST do the following:
+
+ 1. Process the client supplied attributes and either accept or
+ reject the request
+ 2. Validate the syntax of and support for the scheme of any client
+ supplied URI
+
+ At job submission time the Printer object MUST validate whether or
+ not the supplied attributes, attribute syntaxes, and values are
+ supported by matching them with the Printer object's corresponding
+ "xxx-supported" attributes. See section 3.2.1.2 for details. [ipp-
+ iig] presents suggested steps for an IPP object to either accept or
+ reject any request and additional steps for processing create
+ requests.
+
+ At job submission time the Printer object NEED NOT perform the
+ validation checks reserved for job processing time such as:
+
+ 1. Validating the document data
+ 2. Validating the actual contents of any client supplied URI
+ (resolve the reference and follow the link to the document data)
+
+ At job submission time, these additional job processing time
+ validation checks are essentially useless, since they require
+ actually parsing and interpreting the document data, are not
+ guaranteed to be 100% accurate, and MUST be done, yet again, at job
+ processing time. Also, in the case of a URI, checking for
+ availability at job submission time does not guarantee availability
+ at job processing time. In addition, at job processing time, the
+ Printer object might discover any of the following conditions that
+ were not detectable at job submission time:
+
+ - runtime errors in the document data,
+ - nested document data that is in an unsupported format,
+
+
+
+deBry, et al. Experimental [Page 33]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ - the URI reference is no longer valid (i.e., the server hosting
+ the document might be down), or
+ - any other job processing error
+
+ At job processing time, since the Printer object has already
+ responded with a successful status code in the response to the create
+ request, if the Printer object detects an error, the Printer object
+ is unable to inform the end user of the error with an operation
+ status code. In this case, the Printer, depending on the error, can
+ set the "job-state", "job-state-reasons", or "job-state-message"
+ attributes to the appropriate value(s) so that later queries can
+ report the correct job status.
+
+ Note: Asynchronous notification of events is outside the scope of
+ IPP/1.0.
+
+3.2 Printer Operations
+
+ All Printer operations are directed at Printer objects. A client
+ MUST always supply the "printer-uri" operation attribute in order to
+ identify the correct target of the operation.
+
+3.2.1 Print-Job Operation
+
+ This REQUIRED operation allows a client to submit a print job with
+ only one document and supply the document data (rather than just a
+ reference to the data). See Section 15 for the suggested steps for
+ processing create operations and their Operation and Job Template
+ attributes.
+
+3.2.1.1 Print-Job Request
+
+ The following groups of attributes are supplied as part of the
+ Print-Job Request:
+
+ Group 1: Operation Attributes
+
+ Natural Language and Character Set:
+ The "attributes-charset" and "attributes-natural-language"
+ attributes as described in section 3.1.4.1. The Printer object
+ MUST copy these values to the corresponding Job Description
+ attributes described in sections 4.3.23 and 4.3.24.
+
+ Target:
+ The "printer-uri" (uri) operation attribute which is the target
+ for this operation as described in section 3.1.5.
+
+
+
+
+
+deBry, et al. Experimental [Page 34]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ Requesting User Name:
+ The "requesting-user-name" (name(MAX)) attribute SHOULD be
+ supplied by the client as described in section 8.3.
+
+ "job-name" (name(MAX)):
+ The client OPTIONALLY supplies this attribute. The Printer
+ object MUST support this attribute. It contains the client
+ supplied Job name. If this attribute is supplied by the client,
+ its value is used for the "job-name" attribute of the newly
+ created Job object. The client MAY automatically include any
+ information that will help the end-user distinguish amongst
+ his/her jobs, such as the name of the application program along
+ with information from the document, such as the document name,
+ document subject, or source file name. If this attribute is not
+ supplied by the client, the Printer generates a name to use in
+ the "job-name" attribute of the newly created Job object (see
+ Section 4.3.5).
+
+ "ipp-attribute-fidelity" (boolean):
+ The client OPTIONALLY supplies this attribute. The Printer
+ object MUST support this attribute. The value 'true' indicates
+ that total fidelity to client supplied Job Template attributes
+ and values is required, else the Printer object MUST reject the
+ Print-Job request. The value 'false' indicates that a
+ reasonable attempt to print the Job object is acceptable and the
+ Printer object MUST accept the Print-job request. If not
+ supplied, the Printer object assumes the value is 'false'. All
+ Printer objects MUST support both types of job processing. See
+ section 15 for a full description of "ipp-attribute-fidelity"
+ and its relationship to other attributes, especially the Printer
+ object's "pdl-override-supported" attribute.
+
+ "document-name" (name(MAX)):
+ The client OPTIONALLY supplies this attribute. The Printer
+ object MUST support this attribute. It contains the client
+ supplied document name. The document name MAY be different than
+ the Job name. Typically, the client software automatically
+ supplies the document name on behalf of the end user by using a
+ file name or an application generated name. If this attribute
+ is supplied, its value can be used in a manner defined by each
+ implementation. Examples include: printed along with the Job
+ (job start sheet, page adornments, etc.), used by accounting or
+ resource tracking management tools, or even stored along with
+ the document as a document level attribute. IPP/1.0 does not
+ support the concept of document level attributes.
+
+
+
+
+
+
+deBry, et al. Experimental [Page 35]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ "document-format" (mimeMediaType) :
+ The client OPTIONALLY supplies this attribute. The Printer
+ object MUST support this attribute. The value of this attribute
+ identifies the format of the supplied document data. If the
+ client does not supply this attribute, the Printer object
+ assumes that the document data is in the format defined by the
+ Printer object's "document-format-default" attribute. If the
+ client supplies this attribute, but the value is not supported
+ by the Printer object, i.e., the value is not one of the values
+ of the Printer object's "document-format-supported" attribute,
+ the Printer object MUST reject the request and return the '
+ client-error-document-format-not-supported' status code.
+
+ "document-natural-language" (naturalLanguage):
+ The client OPTIONALLY supplies this attribute. The Printer
+ object OPTIONALLY supports this attribute. This attribute
+ specifies the natural language of the document for those
+ document-formats that require a specification of the natural
+ language in order to image the document unambiguously. There are
+ no particular values required for the Printer object to support.
+
+ "compression" (type3 keyword)
+ The client OPTIONALLY supplies this attribute. The Printer
+ object OPTIONALLY supports this attribute and the "compression-
+ supported" attribute (see section 4.4.29). The client supplied
+ "compression" operation attribute identifies the compression
+ algorithm used on the document data. If the client omits this
+ attribute, the Printer object MUST assume that the data is not
+ compressed. If the client supplies the attribute and the
+ Printer object supports the attribute, the Printer object uses
+ the corresponding decompression algorithm on the document data.
+ If the client supplies this attribute, but the value is not
+ supported by the Printer object, i.e., the value is not one of
+ the values of the Printer object's "compression-supported"
+ attribute, the Printer object MUST copy the attribute and its
+ value to the Unsupported Attributes response group, reject the
+ request, and return the 'client-error-attributes-or-values-not-
+ supported' status code.
+
+ "job-k-octets" (integer(0:MAX))
+ The client OPTIONALLY supplies this attribute. The Printer
+ object OPTIONALLY supports this attribute and the "job-k-
+ octets-supported" attribute (see section 4.4.30). The client
+ supplied "job-k-octets" operation attribute identifies the total
+ size of the document(s) in K octets being submitted (see section
+ 4.3.17 for the complete semantics). If the client supplies the
+
+
+
+
+
+deBry, et al. Experimental [Page 36]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ attribute and the Printer object supports the attribute, the
+ value of the attribute is used to populate the Job object's
+ "job-k-octets" Job Description attribute.
+
+ Note: For this attribute and the following two attributes
+ ("job-impressions", and "job-media-sheets"), if the client
+ supplies the attribute, but the Printer object does not support
+ the attribute, the Printer object ignores the client-supplied
+ value. If the client supplies the attribute and the Printer
+ supports the attribute, and the value is within the range of the
+ corresponding Printer object's "xxx-supported" attribute, the
+ Printer object MUST use the value to populate the Job object's
+ "xxx" attribute. If the client supplies the attribute and the
+ Printer supports the attribute, but the value is outside the
+ range of the corresponding Printer object's "xxx-supported"
+ attribute, the Printer object MUST copy the attribute and its
+ value to the Unsupported Attributes response group, reject the
+ request, and return the 'client-error-attributes-or-values-not-
+ supported' status code. If the client does not supply the
+ attribute, the Printer object MAY choose to populate the
+ corresponding Job object attribute depending on whether the
+ Printer object supports the attribute and is able to calculate
+ or discern the correct value.
+
+ "job-impressions" (integer(0:MAX))
+ The client OPTIONALLY supplies this attribute. The Printer
+ object OPTIONALLY supports this attribute and the "job-
+ impressions-supported" attribute (see section 4.4.31). The
+ client supplied "job-impressions" operation attribute identifies
+ the total size in number of impressions of the document(s) being
+ submitted (see section 4.3.18 for the complete semantics).
+
+ See note under "job-k-octets".
+
+ "job-media-sheets" (integer(0:MAX))
+ The client OPTIONALLY supplies this attribute. The Printer
+ object OPTIONALLY supports this attribute and the "job-media-
+ sheets-supported" attribute (see section 4.4.32). The client
+ supplied "job-media-sheets" operation attribute identifies the
+ total number of media sheets to be produced for this job (see
+ section 4.3.19 for the complete semantics).
+
+ See note under "job-k-octets".
+
+
+
+
+
+
+
+
+deBry, et al. Experimental [Page 37]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ Group 2: Job Template Attributes
+
+ The client OPTIONALLY supplies a set of Job Template attributes
+ as defined in section 4.2. If the client is not supplying any
+ Job Template attributes in the request, the client SHOULD omit
+ Group 2 rather than sending an empty group. However, a Printer
+ object MUST be able to accept an empty group.
+
+ Group 3: Document Content
+
+ The client MUST supply the document data to be processed.
+
+ Note: In addition to the MANDATORY parameters required for every
+ operation request, the simplest Print-Job Request consists of just
+ the "attributes-charset" and "attributes-natural-language" operation
+ attributes; the "printer-uri" target operation attribute; the
+ Document Content and nothing else. In this simple case, the Printer
+ object:
+
+ - creates a new Job object (the Job object contains a single
+ document),
+ - stores a generated Job name in the "job-name" attribute in the
+ natural language and charset requested (see Section 3.1.4.1) (if
+ those are supported, otherwise using the Printer object's default
+ natural language and charset), and
+ - at job processing time, uses its corresponding default value
+ attributes for the supported Job Template attributes that were
+ not supplied by the client as IPP attribute or embedded
+ instructions in the document data.
+
+3.2.1.2 Print-Job Response
+
+ The Printer object MUST return to the client the following sets
+ of attributes as part of the Print-Job Response:
+
+ Group 1: Operation Attributes
+
+ Status Message:
+ In addition to the REQUIRED status code returned in every
+ response, the response OPTIONALLY includes a "status-message"
+ (text) operation attribute as described in sections 14 and
+ 3.1.6. If the client supplies unsupported or conflicting Job
+ Template attributes or values, the Printer object MUST reject or
+ accept the Print-Job request depending on the whether the client
+ supplied a 'true' or 'false' value for the "ipp-attribute-
+ fidelity" operation attribute. See the Implementer's Guide
+ [ipp-iig] for a complete description of the suggested steps for
+ processing a create request.
+
+
+
+deBry, et al. Experimental [Page 38]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ Natural Language and Character Set:
+ The "attributes-charset" and "attributes-natural-language"
+ attributes as described in section 3.1.4.2.
+
+ Group 2: Unsupported Attributes
+
+ This is a set of Operation and Job Template attributes supplied
+ by the client (in the request) that are not supported by the
+ Printer object or that conflict with one another (see the
+ Implementer's Guide [ipp-iig]). If the Printer object is not
+ returning any Unsupported Attributes in the response, the
+ Printer object SHOULD omit Group 2 rather than sending an empty
+ group. However, a client MUST be able to accept an empty group.
+
+ Unsupported attributes fall into three categories:
+
+ 1. The Printer object does not support the supplied attribute
+ (no matter what the attribute syntax or value).
+ 2. The Printer object does support the attribute, but does not
+ support some or all of the particular attribute syntaxes or
+ values supplied by the client (i.e., the Printer object does
+ not have those attribute syntaxes or values in its
+ corresponding "xxx-supported" attribute).
+ 3. The Printer object does support the attributes and values
+ supplied, but the particular values are in conflict with one
+ another, because they violate a constraint, such as not being
+ able to staple transparencies.
+
+ In the case of an unsupported attribute name, the Printer object
+ returns the client-supplied attribute with a substituted "out-
+ of-band" value of 'unsupported' indicating no support for the
+ attribute itself (see the beginning of section 4.1).
+
+ In the case of a supported attribute with one or more
+ unsupported attribute syntaxes or values, the Printer object
+ simply returns the client-supplied attribute with the
+ unsupported attribute syntaxes or values as supplied by the
+ client. This indicates support for the attribute, but no
+ support for that particular attribute syntax or value. If the
+ client supplies a multi-valued attribute with more than one
+ value and the Printer object supports the attribute but only
+ supports a subset of the client-supplied attribute syntaxes or
+ values, the Printer object MUST return only those attribute
+ syntaxes or values that are unsupported.
+
+ In the case of two (or more) supported attribute values that are
+ in conflict with one another (although each is supported
+ independently, the values conflict when requested together
+
+
+
+deBry, et al. Experimental [Page 39]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ within the same job), the Printer object MUST return all the
+ values that it ignores or substitutes to resolve the conflict,
+ but not any of the values that it is still using. The choice
+ for exactly how to resolve the conflict is implementation
+ dependent. See The Implementer's Guide [ipp-iig] for an
+ example.
+
+ In these three cases, the value of the "ipp-attribute-fidelity"
+ supplied by the client does not affect what the Printer object
+ returns. The value of "ipp-attribute-fidelity" only affects
+ whether the Print-Job operation is accepted or rejected. If the
+ job is accepted, the client may query the job using the Get-
+ Job-Attributes operation requesting the unsupported attributes
+ that were returned in the create response to see which
+ attributes were ignored (not stored on the Job object) and which
+ attributes were stored with other (substituted) values.
+
+ Group 3: Job Object Attributes
+
+ "job-uri" (uri):
+ The Printer object MUST return the Job object's URI by returning
+ the contents of the REQUIRED "job-uri" Job object attribute.
+ The client uses the Job object's URI when directing operations
+ at the Job object. The Printer object always uses its
+ configured security policy when creating the new URI. However,
+ if the Printer object supports more than one URI, the Printer
+ object also uses information about which URI was used in the
+ Print-Job Request to generated the new URI so that the new URI
+ references the correct access channel. In other words, if the
+ Print-Job Request comes in over a secure channel, the Printer
+ object MUST generate a Job URI that uses the secure channel as
+ well.
+
+ "job-id" (integer(1:MAX)):
+ The Printer object MUST return the Job object's Job ID by
+ returning the REQUIRED "job-id" Job object attribute. The
+ client uses this "job-id" attribute in conjunction with the
+ "printer-uri" attribute used in the Print-Job Request when
+ directing Job operations at the Printer object.
+
+ "job-state":
+ The Printer object MUST return the Job object's REQUIRED "job-
+ state" attribute. The value of this attribute (along with the
+ value of the next attribute "job-state-reasons") is taken from a
+ "snapshot" of the new Job object at some meaningful point in
+ time (implementation defined) between when the Printer object
+ receives the Print-Job Request and when the Printer object
+ returns the response.
+
+
+
+deBry, et al. Experimental [Page 40]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ "job-state-reasons":
+ The Printer object OPTIONALLY returns the Job object's OPTIONAL
+ "job-state-reasons" attribute. If the Printer object supports
+ this attribute then it MUST be returned in the response. If
+ this attribute is not returned in the response, the client can
+ assume that the "job-state-reasons" attribute is not supported
+ and will not be returned in a subsequent Job object query.
+
+ "job-state-message":
+ The Printer object OPTIONALLY returns the Job object's OPTIONAL
+ "job-state-message" attribute. If the Printer object supports
+ this attribute then it MUST be returned in the response. If
+ this attribute is not returned in the response, the client can
+ assume that the "job-state-message" attribute is not supported
+ and will not be returned in a subsequent Job object query.
+
+ "number-of-intervening-jobs":
+ The Printer object OPTIONALLY returns the Job object's OPTIONAL
+ "number-of-intervening-jobs" attribute. If the Printer object
+ supports this attribute then it MUST be returned in the
+ response. If this attribute is not returned in the response,
+ the client can assume that the "number-of-intervening-jobs"
+ attribute is not supported and will not be returned in a
+ subsequent Job object query.
+
+ Note: Since any printer state information which affects a job's
+ state is reflected in the "job-state" and "job-state-reasons"
+ attributes, it is sufficient to return only these attributes and
+ no specific printer status attributes.
+
+ Note: In addition to the MANDATORY parameters required for every
+ operation response, the simplest response consists of the just the
+ "attributes-charset" and "attributes-natural-language" operation
+ attributes and the "job-uri", "job-id", and "job-state" Job Object
+ Attributes. In this simplest case, the status code is "successful-
+ ok" and there is no "status-message" operation attribute.
+
+3.2.2 Print-URI Operation
+
+ This OPTIONAL operation is identical to the Print-Job operation
+ (section 3.2.1) except that a client supplies a URI reference to the
+ document data using the "document-uri" (uri) operation attribute (in
+ Group 1) rather than including the document data itself. Before
+ returning the response, the Printer MUST validate that the Printer
+ supports the retrieval method (e.g., http, ftp, etc.) implied by the
+ URI, and MUST check for valid URI syntax. If the client-supplied URI
+ scheme is not supported, i.e. the value is not in the Printer
+ object's "referenced-uri-scheme-supported" attribute, the Printer
+
+
+
+deBry, et al. Experimental [Page 41]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ object MUST reject the request and return the 'client-error-uri-
+ scheme-not-supported' status code. See The Implementer's Guide
+ [ipp-iig] for suggested additional checks. The Printer NEED NOT
+ follow the reference and validate the contents of the reference.
+
+ If the Printer object supports this operation, it MUST support the
+ "reference-uri-schemes-supported" Printer attribute (see section
+ 4.4.24).
+
+ It is up to the IPP object to interpret the URI and subsequently
+ "pull" the document from the source referenced by the URI string.
+
+3.2.3 Validate-Job Operation
+
+ This REQUIRED operation is similar to the Print-Job operation
+ (section 3.2.1) except that a client supplies no document data and
+ the Printer allocates no resources (i.e., it does not create a new
+ Job object). This operation is used only to verify capabilities of a
+ printer object against whatever attributes are supplied by the client
+ in the Validate-Job request. By using the Validate-Job operation a
+ client can validate that an identical Print-Job operation (with the
+ document data) would be accepted. The Validate-Job operation also
+ performs the same security negotiation as the Print-Job operation
+ (see section 8), so that a client can check that the client and
+ Printer object security requirements can be met before performing a
+ Print-Job operation.
+
+ Note: The Validate-Job operation does not accept a "document-uri"
+ attribute in order to allow a client to check that the same Print-URI
+ operation will be accepted, since the client doesn't send the data
+ with the Print-URI operation. The client SHOULD just issue the
+ Print-URI request.
+
+ The Printer object returns the same status codes, Operation
+ Attributes (Group 1) and Unsupported Attributes (Group 2) as the
+ Print-Job operation. However, no Job Object Attributes (Group 3) are
+ returned, since no Job object is created.
+
+3.2.4 Create-Job Operation
+
+ This OPTIONAL operation is similar to the Print-Job operation
+ (section 3.2.1) except that in the Create-Job request, a client does
+ not supply document data or any reference to document data. Also,
+ the client does not supply any of the "document-name", "document-
+ format", "compression", or "document-natural-language" operation
+ attributes. This operation is followed by one or more Send-Document
+ or Send-URI operations. In each of those operation requests, the
+
+
+
+
+deBry, et al. Experimental [Page 42]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ client OPTIONALLY supplies the "document-name", "document-format",
+ and "document-natural-language" attributes for each document in the
+ multi-document Job object.
+
+ If a Printer object supports the Create-Job operation, it MUST also
+ support the Send-Document operation and also MAY support the Send-URI
+ operation.
+
+ If the Printer object supports this operation, it MUST support the
+ "multiple-operation-time-out" Printer attribute (see section 4.4.28).
+
+
+3.2.5 Get-Printer-Attributes Operation
+
+ This REQUIRED operation allows a client to request the values of the
+ attributes of a Printer object. In the request, the client supplies
+ the set of Printer attribute names and/or attribute group names in
+ which the requester is interested. In the response, the Printer
+ object returns a corresponding attribute set with the appropriate
+ attribute values filled in.
+
+ For Printer objects, the possible names of attribute groups are:
+
+ - 'job-template': all of the Job Template attributes that apply to
+ a Printer object (the last two columns of the table in Section
+ 4.2).
+ - 'printer-description': the attributes specified in Section 4.4.
+ - 'all': the special group 'all' that includes all supported
+ attributes.
+
+ Since a client MAY request specific attributes or named groups, there
+ is a potential that there is some overlap. For example, if a client
+ requests, 'printer-name' and 'all', the client is actually requesting
+ the "printer-name" attribute twice: once by naming it explicitly, and
+ once by inclusion in the 'all' group. In such cases, the Printer
+ object NEED NOT return each attribute only once in the response even
+ if it is requested multiple times. The client SHOULD NOT request the
+ same attribute in multiple ways.
+
+ It is NOT REQUIRED that a Printer object support all attributes
+ belonging to a group (since some attributes are OPTIONAL). However,
+ it is REQUIRED that each Printer object support all group names.
+
+
+
+
+
+
+
+
+
+deBry, et al. Experimental [Page 43]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+3.2.5.1 Get-Printer-Attributes Request
+
+ The following sets of attributes are part of the Get-Printer-
+ Attributes Request:
+
+ Group 1: Operation Attributes
+
+ Natural Language and Character Set:
+ attributes-charset" and "attributes-natural-language" butes as
+ described in section 3.1.4.1.
+
+ Target:
+ The "printer-uri" (uri) operation attribute which is the target
+ for this operation as described in section 3.1.5.
+
+ Requesting User Name:
+ The "requesting-user-name" (name(MAX)) attribute SHOULD be
+ supplied by the client as described in section 8.3.
+
+ "requested-attributes" (1setOf keyword) :
+ The client OPTIONALLY supplies a set of attribute names and/or
+ attribute group names in whose values the requester is
+ interested. The Printer object MUST support this attribute. If
+ the client omits this attribute, the Printer MUST respond as if
+ this attribute had been supplied with a value of 'all'.
+
+ "document-format" (mimeMediaType) :
+ The client OPTIONALLY supplies this attribute. The Printer
+ object MUST support this attribute. This attribute is useful
+ for a Printer object to determine the set of supported attribute
+ values that relate to the requested document format. The
+ Printer object MUST return the attributes and values that it
+ uses to validate a job on a create or Validate-Job operation in
+ which this document format is supplied. The Printer object
+ SHOULD return only (1) those attributes that are supported for
+ the specified format and (2) the attribute values that are
+ supported for the specified document format. By specifying the
+ document format, the client can get the Printer object to
+ eliminate the attributes and values that are not supported for a
+ specific document format. For example, a Printer object might
+ have multiple interpreters to support both '
+ application/postscript' (for PostScript) and 'text/plain' (for
+ text) documents. However, for only one of those interpreters
+ might the Printer object be able to support "number-up" with
+ values of '1', '2', and '4'. For the other interpreter it might
+ be able to only support "number-up" with a value of '1'. Thus a
+
+
+
+
+
+deBry, et al. Experimental [Page 44]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ client can use the Get-Printer-Attributes operation to obtain
+ the attributes and values that will be used to accept/reject a
+ create job operation.
+
+ If the Printer object does not distinguish between different
+ sets of supported values for each different document format when
+ validating jobs in the create and Validate-Job operations, it
+ MUST NOT distinguish between different document formats in the
+ Get-Printer-Attributes operation. If the Printer object does
+ distinguish between different sets of supported values for each
+ different document format specified by the client, this
+ specialization applies only to the following Printer object
+ attributes:
+
+ - Printer attributes that are Job Template attributes ("xxx-
+ default" "xxx-supported", and "xxx-ready" in the Table in
+ Section 4.2),
+ - "pdl-override-supported",
+ - "compression-supported",
+ - "job-k-octets-supported",
+ - "job-impressions-supported,
+ - "job-media-sheets-supported"
+ - "printer-driver-installer",
+ - "color-supported", and
+ - "reference-uri-schemes-supported"
+
+ The values of all other Printer object attributes (including
+ "document-format-supported") remain invariant with respect to
+ the client supplied document format (except for new Printer
+ description attribute as registered according to section 6.2).
+
+ If the client omits this "document-format" operation attribute,
+ the Printer object MUST respond as if the attribute had been
+ supplied with the value of the Printer object's "document-
+ format-default" attribute. It is recommended that the client
+ always supply a value for "document-format", since the Printer
+ object's "document-format-default" may be 'application/octet-
+ stream', in which case the returned attributes and values are
+ for the union of the document formats that the Printer can
+ automatically sense. For more details, see the description of
+ the 'mimeMediaType' attribute syntax in section 4.1.9.
+
+ If the client supplies a value for the "document-format"
+ Operation attribute that is not supported by the Printer, i.e.,
+ is not among the values of the Printer object's "document-
+ format-supported" attribute, the Printer object MUST reject the
+ operation and return the 'client-error-document-format-not-
+ supported' status code.
+
+
+
+deBry, et al. Experimental [Page 45]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+3.2.5.2 Get-Printer-Attributes Response
+
+ The Printer object returns the following sets of attributes as part
+ of the Get-Printer-Attributes Response:
+
+ Group 1: Operation Attributes
+
+ Status Message:
+ In addition to the REQUIRED status code returned in every
+ response, the response OPTIONALLY includes a "status-message"
+ (text) operation attribute as described in section 3.1.6.
+
+ Natural Language and Character Set:
+ The "attributes-charset" and "attributes-natural-language"
+ attributes as described in section 3.1.4.2.
+
+ Group 2: Unsupported Attributes
+
+ This is a set of Operation attributes supplied by the client (in
+ the request) that are not supported by the Printer object or
+ that conflict with one another (see sections 3.2.1.2 and 16).
+ The response NEED NOT contain the "requested-attributes"
+ operation attribute with any supplied values (attribute
+ keywords) that were requested by the client but are not
+ supported by the IPP object. If the Printer object is not
+ returning any Unsupported Attributes in the response, the
+ Printer object SHOULD omit Group 2 rather than sending an empty
+ group. However, a client MUST be able to accept an empty group.
+
+ Group 3: Printer Object Attributes
+
+ This is the set of requested attributes and their current
+ values. The Printer object ignores (does not respond with) any
+ requested attribute which is not supported. The Printer object
+ MAY respond with a subset of the supported attributes and
+ values, depending on the security policy in force. However, the
+ Printer object MUST respond with the 'unknown' value for any
+ supported attribute (including all REQUIRED attributes) for
+ which the Printer object does not know the value. Also the
+ Printer object MUST respond with the 'no-value' for any
+ supported attribute (including all REQUIRED attributes) for
+ which the system administrator has not configured a value. See
+ the description of the "out-of-band" values in the beginning of
+ Section 4.1.
+
+
+
+
+
+
+
+deBry, et al. Experimental [Page 46]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+3.2.6 Get-Jobs Operation
+
+ This REQUIRED operation allows a client to retrieve the list of Job
+ objects belonging to the target Printer object. The client may also
+ supply a list of Job attribute names and/or attribute group names. A
+ group of Job object attributes will be returned for each Job object
+ that is returned.
+
+ This operation is similar to the Get-Job-Attributes operation, except
+ that this Get-Jobs operation returns attributes from possibly more
+ than one object (see the description of Job attribute group names in
+ section 3.3.4).
+
+3.2.6.1 Get-Jobs Request
+
+ The client submits the Get-Jobs request to a Printer object.
+
+ The following groups of attributes are part of the Get-Jobs Request:
+
+ Group 1: Operation Attributes
+
+ Natural Language and Character Set:
+ The "attributes-charset" and "attributes-natural-language"
+ attributes as described in section 3.1.4.1.
+
+ Target:
+ The "printer-uri" (uri) operation attribute which is the target
+ for this operation as described in section 3.1.5.
+
+ Requesting User Name:
+ The "requesting-user-name" (name(MAX)) attribute SHOULD be
+ supplied by the client as described in section 8.3.
+
+ "limit" (integer(1:MAX)):
+ The client OPTIONALLY supplies this attribute. The Printer
+ object MUST support this attribute. It is an integer value that
+ indicates a limit to the number of Job objects returned. The
+ limit is a "stateless limit" in that if the value supplied by
+ the client is 'N', then only the first 'N' jobs are returned in
+ the Get-Jobs Response. There is no mechanism to allow for the
+ next 'M' jobs after the first 'N' jobs. If the client does not
+ supply this attribute, the Printer object responds with all
+ applicable jobs.
+
+ "requested-attributes" (1setOf keyword):
+ The client OPTIONALLY supplies this attribute. The Printer
+ object MUST support this attribute. It is a set of Job
+ attribute names and/or attribute groups names in whose values
+
+
+
+deBry, et al. Experimental [Page 47]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ the requester is interested. This set of attributes is returned
+ for each Job object that is returned. The allowed attribute
+ group names are the same as those defined in the Get-Job-
+ Attributes operation in section 3.3.4. If the client does not
+ supply this attribute, the Printer MUST respond as if the client
+ had supplied this attribute with two values: 'job-uri' and '
+ job-id'.
+
+ "which-jobs" (type2 keyword):
+ The client OPTIONALLY supplies this attribute. The Printer
+ object MUST support this attribute. It indicates which Job
+ objects MUST be returned by the Printer object. The values for
+ this attribute are:
+
+ 'completed': This includes any Job object whose state is
+ 'completed', 'canceled', or 'aborted'.
+ 'not-completed': This includes any Job object whose state is '
+ pending', 'processing', 'processing-stopped', or 'pending-
+ held'.
+
+ A Printer object MUST support both values. However, if the
+ mentation does not keep jobs in the 'completed', 'canceled', '
+ aborted' states, then it returns no jobs when the 'completed'
+ value is supplied.
+
+ If a client supplies some other value, the Printer object MUST
+ copy the attribute and the unsupported value to the Unsupported
+ Attributes response group, reject the request, and return the '
+ client-error-attributes-or-values-not-supported' status code.
+
+ If the client does not supply this attribute, the Printer object
+ MUST respond as if the client had supplied the attribute with a
+ value of 'not-completed'.
+
+ "my-jobs" (boolean):
+ The client OPTIONALLY supplies this attribute. The Printer
+ object MUST support this attribute. It indicates whether all
+ jobs or just the jobs submitted by the requesting user of this
+ request MUST be returned by the Printer object. If the client
+ does not supply this attribute, the Printer object MUST respond
+ as if the client had supplied the attribute with a value of '
+ false', i.e., all jobs. The means for authenticating the
+ requesting user and matching the jobs is described in section 8.
+
+
+
+
+
+
+
+
+deBry, et al. Experimental [Page 48]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+3.2.6.2 Get-Jobs Response
+
+ The Printer object returns all of the Job objects that match the
+ criteria as defined by the attribute values supplied by the client in
+ the request. It is possible that no Job objects are returned since
+ there may literally be no Job objects at the Printer, or there may be
+ no Job objects that match the criteria supplied by the client. If
+ the client requests any Job attributes at all, there is a set of Job
+ Object Attributes returned for each Job object.
+
+ Group 1: Operation Attributes
+
+ Status Message:
+ In addition to the REQUIRED status code returned in every
+ response, the response OPTIONALLY includes a "status-message"
+ (text) operation attribute as described in sections 14 and
+ 3.1.6.
+
+ Natural Language and Character Set:
+ The "attributes-charset" and "attributes-natural-language"
+ attributes as described in section 3.1.4.2.
+
+ Group 2: Unsupported Attributes
+
+ This is a set of Operation attributes supplied by the client (in
+ the request) that are not supported by the Printer object or
+ that conflict with one another (see sections 3.2.1.2 and the
+ Implementer's Guide [ipp-iig]). The response NEED NOT contain
+ the "requested-attributes" operation attribute with any supplied
+ values (attribute keywords) that were requested by the client
+ but are not supported by the IPP object. If the Printer object
+ is not returning any Unsupported Attributes in the response, the
+ Printer object SHOULD omit Group 2 rather than sending an empty
+ group. However, a client MUST be able to accept an empty group.
+
+ Groups 3 to N: Job Object Attributes
+
+ The Printer object responds with one set of Job Object
+ Attributes for each returned Job object. The Printer object
+ ignores (does not respond with) any requested attribute or value
+ which is not supported or which is restricted by the security
+ policy in force, including whether the requesting user is the
+ user that submitted the job (job originating user) or not (see
+ section 8). However, the Printer object MUST respond with the '
+ unknown' value for any supported attribute (including all
+ REQUIRED attributes) for which the Printer object does not know
+
+
+
+
+
+deBry, et al. Experimental [Page 49]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ the value, unless it would violate the security policy. See the
+ description of the "out-of-band" values in the beginning of
+ Section 4.1.
+
+ Jobs are returned in the following order:
+
+ - If the client requests all 'completed' Jobs (Jobs in the '
+ completed', 'aborted', or 'canceled' states), then the Jobs
+ are returned newest to oldest (with respect to actual
+ completion time)
+ - If the client requests all 'not-completed' Jobs (Jobs in the
+ 'pending', 'processing', 'pending-held', and 'processing-
+ stopped' states), then Jobs are returned in relative
+ chronological order of expected time to complete (based on
+ whatever scheduling algorithm is configured for the Printer
+ object).
+
+3.3 Job Operations
+
+ All Job operations are directed at Job objects. A client MUST always
+ supply some means of identifying the Job object in order to identify
+ the correct target of the operation. That job identification MAY
+ either be a single Job URI or a combination of a Printer URI with a
+ Job ID. The IPP object implementation MUST support both forms of
+ identification for every job.
+
+3.3.1 Send-Document Operation
+
+ This OPTIONAL operation allows a client to create a multi-document
+ Job object that is initially "empty" (contains no documents). In the
+ Create-Job response, the Printer object returns the Job object's URI
+ (the "job-uri" attribute) and the Job object's 32-bit identifier (the
+ "job-id" attribute). For each new document that the client desires
+ to add, the client uses a Send-Document operation. Each Send-
+ Document Request contains the entire stream of document data for one
+ document.
+
+ Since the Create-Job and the send operations (Send-Document or Send-
+ URI operations) that follow could occur over an arbitrarily long
+ period of time for a particular job, a client MUST send another send
+ operation within an IPP Printer defined minimum time interval after
+ the receipt of the previous request for the job. If a Printer object
+ supports multiple document jobs, the Printer object MUST support the
+ "multiple-operation-time-out" attribute (see section 4.4.28). This
+ attribute indicates the minimum number of seconds the Printer object
+ will wait for the next send operation before taking some recovery
+ action.
+
+
+
+
+deBry, et al. Experimental [Page 50]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ An IPP object MUST recover from an errant client that does not supply
+ a send operation, sometime after the minimum time interval specified
+ by the Printer object's "multiple-operation-time-out" attribute.
+ Such recovery MAY include any of the following or other recovery
+ actions:
+
+ 1. Assume that the Job is an invalid job, start the process of
+ changing the job state to 'aborted', add the 'aborted-by-system'
+ value to the job's "job-state-reasons" attribute (see section
+ 4.3.8), if supported, and clean up all resources associated with
+ the Job. In this case, if another send operation is finally
+ received, the Printer responds with an "client-error-not-
+ possible" or "client-error-not-found" depending on whether or
+ not the Job object is still around when the send operation
+ finally arrives.
+ 2. Assume that the last send operation received was in fact the
+ last document (as if the "last-document" flag had been set to '
+ true'), close the Job object, and proceed to process it (i.e.,
+ move the Job's state to 'pending').
+ 3. Assume that the last send operation received was in fact the
+ last document, close the Job, but move it to the 'pending-held'
+ and add the 'submission-interrupted' value to the job's "job-
+ state-reasons" attribute (see section 4.3.8), if supported.
+ This action allows the user or an operator to determine whether
+ to continue processing the Job by moving it back to the '
+ pending' state or to cancel the job.
+
+ Each implementation is free to decide the "best" action to take
+ depending on local policy, whether any documents have been added,
+ whether the implementation spools jobs or not, and/or any other piece
+ of information available to it. If the choice is to abort the Job
+ object, it is possible that the Job object may already have been
+ processed to the point that some media sheet pages have been printed.
+
+3.3.1.1 Send-Document Request
+
+ The following attribute sets are part of the Send-Document Request:
+
+ Group 1: Operation Attributes
+
+ Natural Language and Character Set:
+ The "attributes-charset" and "attributes-natural-language"
+ attributes as described in section 3.1.4.1.
+
+
+
+
+
+
+
+
+deBry, et al. Experimental [Page 51]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ Target:
+ Either (1) the "printer-uri" (uri) plus "job-id"
+ (integer(1:MAX))or (2) the "job-uri" (uri) operation
+ attribute(s) which define the target for this operation as
+ described in section 3.1.5.
+
+ Requesting User Name:
+ "requesting-user-name" (name(MAX)) attribute SHOULD be supplied
+ by the client as described in section 8.3.
+
+ "document-name" (name(MAX)):
+ The client OPTIONALLY supplies this attribute. The Printer
+ object MUST support this attribute. It contains the client
+ supplied document name. The document name MAY be different than
+ the Job name. It might be helpful, but NEED NOT be unique
+ across multiple documents in the same Job. Typically, the
+ client software automatically supplies the document name on
+ behalf of the end user by using a file name or an application
+ generated name. See the description of the "document-name"
+ operation attribute in the Print-Job Request (section 3.2.1.1)
+ for more information about this attribute
+
+ "document-format" (mimeMediaType):
+ The client OPTIONALLY supplies this attribute. The Printer
+ object MUST support this attribute. The value of this attribute
+ identifies the format of the supplied document data. If the
+ client does not supply this attribute, the Printer object
+ assumes that the document data is in the format defined by the
+ Printer object's "document-format-default" attribute. If the
+ client supplies this attribute, but the value is not supported
+ by the Printer object, i.e., the value is not one of the values
+ of the Printer object's "document-format-supported" attribute,
+ the Printer object MUST reject the request and return the '
+ client-error-document-format-not-supported' status code.
+
+ "document-natural-language" (naturalLanguage):
+ The client OPTIONALLY supplies this attribute. The Printer
+ object OPTIONALLY supports this attribute. This attribute
+ specifies the natural language of the document for those
+ document-formats that require a specification of the natural
+ language in order to image the document unambiguously. There
+ are no particular values required for the Printer object to
+ support.
+
+ "compression" (type3 keyword)
+ The client OPTIONALLY supplies this attribute. The Printer
+ object OPTIONALLY supports this attribute and the "compression-
+ supported" attribute (see section 4.4.29). The client supplied
+
+
+
+deBry, et al. Experimental [Page 52]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ "compression" operation attribute identifies the compression
+ algorithm used on the document data. If the client omits this
+ attribute, the Printer object MUST assume that the data is not
+ compressed. If the client supplies the attribute and the
+ Printer object supports the attribute, the Printer object MUST
+ use the corresponding decompression algorithm on the document
+ data. If the client supplies this attribute, but the value is
+ not supported by the Printer object, i.e., the value is not one
+ of the values of the Printer object's "compression-supported"
+ attribute, the Printer object MUST copy the attribute and its
+ value to the Unsupported Attributes response group, reject the
+ request, and return the 'client-error-attributes-or-values-not-
+ supported' status code.
+
+ "last-document" (boolean):
+ The client MUST supply this attribute. The Printer object MUST
+ support this attribute. It is a boolean flag that is set to '
+ true' if this is the last document for the Job, 'false'
+ otherwise.
+
+ Group 2: Document Content
+
+ The client MUST supply the document data if the "last-document"
+ flag is set to 'false'. However, since a client might not know
+ that the previous document sent with a Send-Document (or Send-
+ URI) operation was the last document (i.e., the "last-document"
+ attribute was set to 'false'), it is legal to send a Send-
+ Document request with no document data where the "last-document"
+ flag is set to 'true'. Such a request MUST NOT increment the
+ value of the Job object's "number-of-documents" attribute, since
+ no real document was added to the job.
+
+3.3.1.2 Send-Document Response
+
+ The following sets of attributes are part of the Send-Document
+ Response:
+
+ Group 1: Operation Attributes
+
+ Status Message:
+ In addition to the REQUIRED status code returned in every
+ response, the response OPTIONALLY includes a "status-message"
+ (text) operation attribute as described in sections 14 and
+ 3.1.6.
+
+ Natural Language and Character Set:
+ The "attributes-charset" and "attributes-natural-language"
+ attributes as described in section 3.1.4.2.
+
+
+
+deBry, et al. Experimental [Page 53]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ Group 2: Unsupported Attributes
+
+ This is a set of Operation attributes supplied by the client (in
+ the request) that are not supported by the Printer object or
+ that conflict with one another (see sections 3.2.1.2 and the
+ Implementer's Guide [ipp-iig]). If the Printer object is not
+ returning any Unsupported Attributes in the response, the
+ Printer object SHOULD omit Group 2 rather than sending an empty
+ group. However, a client MUST be able to accept an empty group.
+
+ Group 3: Job Object Attributes
+
+ This is the same set of attributes as described in the Print-Job
+ response (see section 3.2.1.2).
+
+3.3.2 Send-URI Operation
+
+ This OPTIONAL operation is identical to the Send-Document operation
+ (see section 3.3.1) except that a client MUST supply a URI reference
+ ("document-uri" operation attribute) rather than the document data
+ itself. If a Printer object supports this operation, clients can use
+ both Send-URI or Send-Document operations to add new documents to an
+ existing multi-document Job object. However, if a client needs to
+ indicate that the previous Send-URI or Send-Document was the last
+ document, the client MUST use the Send-Document operation with no
+ document data and the "last-document" flag set to 'true' (rather than
+ using a Send-URI operation with no "document-uri" operation
+ attribute).
+
+ If a Printer object supports this operation, it MUST also support the
+ Print-URI operation (see section 3.2.2).
+
+ The Printer object MUST validate the syntax and URI scheme of the
+ supplied URI before returning a response, just as in the Print-URI
+ operation.
+
+3.3.3 Cancel-Job Operation
+
+ This REQUIRED operation allows a client to cancel a Print Job from
+ the time the job is created up to the time it is completed, canceled,
+ or aborted. Since a Job might already be printing by the time a
+ Cancel-Job is received, some media sheet pages might be printed
+ before the job is actually terminated.
+
+3.3.3.1 Cancel-Job Request
+
+ The following groups of attributes are part of the Cancel-Job
+ Request:
+
+
+
+deBry, et al. Experimental [Page 54]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ Group 1: Operation Attributes
+
+ Natural Language and Character Set:
+ The "attributes-charset" and "attributes-natural-language"
+ attributes as described in section 3.1.4.1.
+
+ Target:
+ Either (1) the "printer-uri" (uri) plus "job-id"
+ (integer(1:MAX))or (2) the "job-uri" (uri) operation
+ attribute(s) which define the target for this operation as
+ described in section 3.1.5.
+
+ Requesting User Name:
+ The "requesting-user-name" (name(MAX)) attribute SHOULD be
+ supplied by the client as described in section 8.3.
+
+ "message" (text(127)):
+ The client OPTIONALLY supplies this attribute. The Printer
+ object OPTIONALLY supports this attribute. It is a message to
+ the operator. This "message" attribute is not the same as the
+ "job-message-from-operator" attribute. That attribute is used
+ to report a message from the operator to the end user that
+ queries that attribute. This "message" operation attribute is
+ used to send a message from the client to the operator along
+ with the operation request. It is an implementation decision of
+ how or where to display this message to the operator (if at
+ all).
+
+3.3.3.2 Cancel-Job Response
+
+ The following sets of attributes are part of the Cancel-Job Response:
+
+ Group 1: Operation Attributes
+
+ Status Message:
+ In addition to the REQUIRED status code returned in every
+ response, the response OPTIONALLY includes a "status-message"
+ (text) operation attribute as described in sections 14 and
+ 3.1.6.
+
+ If the job is already in the 'completed', 'aborted', or '
+ canceled' state, or the 'process-to-stop-point' value is set in
+ the Job's "job-state-reasons" attribute, the Printer object MUST
+ reject the request and return the 'client-error-not-possible'
+ error status code.
+
+
+
+
+
+
+deBry, et al. Experimental [Page 55]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ Natural Language and Character Set:
+ The "attributes-charset" and "attributes-natural-language"
+ attributes as described in section 3.1.4.2.
+
+ Group 2: Unsupported Attributes
+
+ This is a set of Operation attributes supplied by the client (in
+ the request) that are not supported by the Printer object or
+ that conflict with one another (see section 3.2.1.2 and the
+ Implementer's Guide [ipp-iig]). If the Printer object is not
+ returning any Unsupported Attributes in the response, the
+ Printer object SHOULD omit Group 2 rather than sending an empty
+ group. However, a client MUST be able to accept an empty group.
+
+ Once a successful response has been sent, the implementation
+ guarantees that the Job will eventually end up in the 'canceled'
+ state. Between the time of the Cancel-Job operation is accepted and
+ when the job enters the 'canceled' job-state (see section 4.3.7), the
+ "job-state-reasons" attribute SHOULD contain the 'processing-to-
+ stop-point' value which indicates to later queries that although the
+ Job might still be 'processing', it will eventually end up in the '
+ canceled' state, not the 'completed' state.
+
+3.3.4 Get-Job-Attributes Operation
+
+ This REQUIRED operation allows a client to request the values of
+ attributes of a Job object and it is almost identical to the Get-
+ Printer-Attributes operation (see section 3.2.5). The only
+ differences are that the operation is directed at a Job object rather
+ than a Printer object, there is no "document-format" operation
+ attribute used when querying a Job object, and the returned attribute
+ group is a set of Job object attributes rather than a set of Printer
+ object attributes.
+
+ For Jobs, the possible names of attribute groups are:
+
+ - 'job-template': all of the Job Template attributes that apply to a
+ Job object (the first column of the table in Section 4.2).
+ - 'job-description': all of the Job Description attributes specified
+ in Section 4.3.
+ - 'all': the special group 'all' that includes all supported
+ attributes.
+
+ Since a client MAY request specific attributes or named groups, there
+ is a potential that there is some overlap. For example, if a client
+ requests, 'job-name' and 'job-description', the client is actually
+ requesting the "job-name" attribute once by naming it explicitly, and
+ once by inclusion in the 'job-description' group. In such cases, the
+
+
+
+deBry, et al. Experimental [Page 56]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ Printer object NEED NOT return the attribute only once in the
+ response even if it is requested multiple times. The client SHOULD
+ NOT request the same attribute in multiple ways.
+
+ It is NOT REQUIRED that a Job object support all attributes belonging
+ to a group (since some attributes are OPTIONAL). However it is
+ REQUIRED that each Job object support all group names.
+
+3.3.4.1 Get-Job-Attributes Request
+
+ The following groups of attributes are part of the Get-Job-Attributes
+ Request when the request is directed at a Job object:
+
+ Group 1: Operation Attributes
+
+ Natural Language and Character Set:
+ The "attributes-charset" and "attributes-natural-language"
+ attributes as described in section 3.1.4.1.
+
+ Target:
+ Either (1) the "printer-uri" (uri) plus "job-id"
+ (integer(1:MAX)) or (2) the "job-uri" (uri) operation
+ attribute(s) which define the target for this operation as
+ described in section 3.1.5.
+
+ Requesting User Name:
+ The "requesting-user-name" (name(MAX)) attribute SHOULD be
+ supplied by the client as described in section 8.3.
+
+ "requested-attributes" (1setOf keyword) :
+ The client OPTIONALLY supplies this attribute. The IPP object
+ MUST support this attribute. It is a set of attribute names
+ and/or attribute group names in whose values the requester is
+ interested. If the client omits this attribute, the IPP object
+ MUST respond as if this attribute had been supplied with a value
+ of 'all'.
+
+3.3.4.2 Get-Job-Attributes Response
+
+ The Printer object returns the following sets of attributes as part
+ of the Get-Job-Attributes Response:
+
+
+
+
+
+
+
+
+
+
+deBry, et al. Experimental [Page 57]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ Group 1: Operation Attributes
+
+ Status Message:
+ In addition to the REQUIRED status code returned in every
+ response, the response OPTIONALLY includes a "status-message"
+ (text) operation attribute as described in sections 14 and
+ 3.1.6.
+
+ Natural Language and Character Set:
+ The "attributes-charset" and "attributes-natural-language"
+ attributes as described in section 3.1.4.2. The "attributes-
+ natural-language" MAY be the natural language of the Job object,
+ rather than the one requested.
+
+ Group 2: Unsupported Attributes
+
+ This is a set of Operation attributes supplied by the client (in
+ the request) that are not supported by the Printer object or
+ that conflict with one another (see sections 3.2.1.2 and the
+ Implementer's Guide [ipp-iig]). The response NEED NOT contain
+ the "requested-attributes" operation attribute with any supplied
+ values (attribute keywords) that were requested by the client
+ but are not supported by the IPP object. If the Printer object
+ is not returning any Unsupported Attributes in the response, the
+ Printer object SHOULD omit Group 2 rather than sending an empty
+ group. However, a client MUST be able to accept an empty group.
+
+ Group 3: Job Object Attributes
+
+ This is the set of requested attributes and their current
+ values. The IPP object ignores (does not respond with) any
+ requested attribute or value which is not supported or which is
+ restricted by the security policy in force, including whether
+ the requesting user is the user that submitted the job (job
+ originating user) or not (see section 8). However, the IPP
+ object MUST respond with the 'unknown' value for any supported
+ attribute (including all RED butes) for which the IPP object
+ does not know the value, s it would violate the security policy.
+ See the description e "out-of-band" values in the beginning of
+ Section 4.1.
+
+4. Object Attributes
+
+ This section describes the attributes with their corresponding
+ attribute syntaxes and values that are part of the IPP model. The
+ sections below show the objects and their associated attributes which
+ are included within the scope of this protocol. Many of these
+ attributes are derived from other relevant specifications:
+
+
+
+deBry, et al. Experimental [Page 58]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ - Document Printing Application (DPA) [ISO10175]
+ - RFC 1759 Printer MIB [RFC1759]
+
+ Each attribute is uniquely identified in this document using a
+ "keyword" (see section 12.2.1) which is the name of the attribute.
+ The keyword is included in the section header describing that
+ attribute.
+
+ Note: Not only are keywords used to identify attributes, but one of
+ the attribute syntaxes described below is "keyword" so that some
+ attributes have keyword values. Therefore, these attributes are
+ defined as having an attribute syntax that is a set of keywords.
+
+4.1 Attribute Syntaxes
+
+ This section defines the basic attribute syntax types that all clients
+ and IPP objects MUST be able to accept in responses and accept in
+ requests, respectively. Each attribute description in sections 3 and
+ 4 includes the name of attribute syntax(es) in the heading (in
+ parentheses). A conforming implementation of an attribute MUST
+ include the semantics of the attribute syntax(es) so identified.
+ Section 6.3 describes how the protocol can be extended with new
+ attribute syntaxes.
+
+ The attribute syntaxes are specified in the following sub-sections,
+ where the sub-section heading is the keyword name of the attribute
+ syntax inside the single quotes. In operation requests and responses
+ each attribute value MUST be represented as one of the attribute
+ syntaxes specified in the sub-section heading for the attribute. In
+ addition, the value of an attribute in a response (but not in a
+ request) MAY be one of the "out-of-band" values. Standard
+ "out-of-band" values are:
+
+ 'unknown': The attribute is supported by the IPP object, but the
+ value is unknown to the IPP object for some reason.
+ 'unsupported': The attribute is unsupported by the IPP object. This
+ value MUST be returned only as the value of an attribute in the
+ Unsupported Attributes Group.
+ 'no-value': The attribute is supported by the Printer object, but
+ the system administrator has not yet configured a value.
+
+ The Encoding and Transport specification [RFC2565] defines mechanisms
+ for passing "out-of-band" values. All attributes in a request MUST
+ have one or more values as defined in Sections 4.2 to 4.4. Thus
+ clients MUST NOT supply attributes with "out-of-band" values. All
+ attribute in a response MUST have one or more values as defined in
+ Sections 4.2 to 4.4 or a single "out-of-band" value.
+
+
+
+
+deBry, et al. Experimental [Page 59]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ Most attributes are defined to have a single attribute syntax.
+ However, a few attributes (e.g., "job-sheet", "media", "job-hold-
+ until") are defined to have several attribute syntaxes, depending on
+ the value. These multiple attribute syntaxes are separated by the
+ "|" character in the sub-section heading to indicate the choice.
+ Since each value MUST be tagged as to its attribute syntax in the
+
+ protocol, a single-valued attribute instance may have any one of its
+ attribute syntaxes and a multi-valued attribute instance may have a
+ mixture of its defined attribute syntaxes.
+
+4.1.1 'text'
+
+ A text attribute is an attribute whose value is a sequence of zero or
+ more characters encoded in a maximum of 1023 ('MAX') octets. MAX is
+ the maximum length for each value of any text attribute. However, if
+ an attribute will always contain values whose maximum length is much
+ less than MAX, the definition of that attribute will include a
+ qualifier that defines the maximum length for values of that
+ attribute. For example: the "printer-location" attribute is
+ specified as "printer-location (text(127))". In this case, text
+ values for "printer-location" MUST NOT exceed 127 octets; if supplied
+ with a longer text string via some external interface (other than the
+ protocol), implementations are free to truncate to this shorter
+ length limitation.
+
+ In this specification, all text attributes are defined using the '
+ text' syntax. However, 'text' is used only for brevity; the formal
+ interpretation of 'text' is: 'textWithoutLanguage |
+ textWithLanguage'. That is, for any attribute defined in this
+ specification using the 'text' attribute syntax, all IPP objects and
+ clients MUST support both the 'textWithoutLanguage' and '
+ textWithLanguage' attribute syntaxes. However, in actual usage and
+ protocol execution, objects and clients accept and return only one of
+ the two syntax per attribute. The syntax 'text' never appears "on-
+ the-wire".
+
+ Both 'textWithoutLanguage' and 'textWithLanguage' are needed to
+ support the real world needs of interoperability between sites and
+ systems that use different natural languages as the basis for human
+ communication. Generally, one natural language applies to all text
+ attributes in a given request or response. The language is indicated
+ by the "attributes-natural-language" operation attribute defined in
+ section 3.1.4 or "attributes-natural-language" job attribute defined
+ in section 4.3.24, and there is no need to identify the natural
+ language for each text string on a value-by-value basis. In these
+ cases, the attribute syntax 'textWithoutLanguage' is used for text
+ attributes. In other cases, the client needs to supply or the
+
+
+
+deBry, et al. Experimental [Page 60]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ Printer object needs to return a text value in a natural language
+ that is different from the rest of the text values in the request or
+ response. In these cases, the client or Printer object uses the
+ attribute syntax 'textWithLanguage' for text attributes (this is the
+ Natural Language Override mechanism described in section 3.1.4).
+
+ The 'textWithoutLanguage' and 'textWithLanguage' attribute syntaxes
+ are described in more detail in the following sections.
+
+4.1.1.1 'textWithoutLanguage'
+
+ The 'textWithoutLanguage' syntax indicates a value that is sequence
+ of zero or more characters. Text strings are encoded using the rules
+ of some charset. The Printer object MUST support the UTF-8 charset
+ [RFC2279] and MAY support additional charsets to represent 'text'
+ values, provided that the charsets are registered with IANA [IANA-
+ CS]. See Section 4.1.7 for the specification of the 'charset'
+ attribute syntax, including restricted semantics and examples of
+ charsets.
+
+4.1.1.2 'textWithLanguage'
+
+ The 'textWithLanguage' attribute syntax is a compound attribute
+ syntax consisting of two parts: a 'textWithoutLanguage' part plus an
+ additional 'naturalLanguage' (see section 4.1.8) part that overrides
+ the natural language in force. The 'naturalLanguage' part explicitly
+ identifies the natural language that applies to the text part of that
+ value and that value alone. For any give text attribute, the '
+ textWithoutLanguage' part is limited to the maximum length defined
+ for that attribute, but the 'naturalLanguage' part is always limited
+ to 63 octets. Using the 'textWithLanguage' attribute syntax rather
+ than the normal 'textWithoutLanguage' syntax is the so-called Natural
+ Language Override mechanism and MUST be supported by all IPP objects
+ and clients.
+
+ If the attribute is multi-valued (1setOf text), then the '
+ textWithLanguage' attribute syntax MUST be used to explicitly specify
+ each attribute value whose natural language needs to be overridden.
+ Other values in a multi-valued 'text' attribute in a request or a
+ response revert to the natural language of the operation attribute.
+
+ In a create request, the Printer object MUST accept and store with
+ the Job object any natural language in the "attributes-natural-
+ language" operation attribute, whether the Printer object supports
+ that natural language or not. Furthermore, the Printer object MUST
+ accept and store any 'textWithLanguage' attribute value, whether the
+
+
+
+
+
+deBry, et al. Experimental [Page 61]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ Printer object supports that natural language or not. These
+ requirements are independent of the value of the "ipp-attribute-
+ fidelity" operation attribute that the client MAY supply.
+
+ Example: If the client supplies the "attributes-natural-language"
+ operation attribute with the value: 'en' indicating English, but the
+ value of the "job-name" attribute is in French, the client MUST use
+ the 'textWithLanguage' attribute syntax with the following two
+ values:
+
+ 'fr': Natural Language Override indicating French
+ 'Rapport Mensuel': the job name in French
+
+ See the Encoding and Transport document [RFC2565] for a detailed
+ example of the 'textWithLanguage' attribute syntax.
+
+4.1.2 'name'
+
+ This syntax type is used for user-friendly strings, such as a Printer
+ name, that, for humans, are more meaningful than identifiers. Names
+ are never translated from one natural language to another. The '
+ name' attribute syntax is essentially the same as 'text', including
+ the REQUIRED support of UTF-8 except that the sequence of characters
+ is limited so that its encoded form MUST NOT exceed 255 (MAX) octets.
+
+ Also like 'text', 'name' is really an abbreviated notation for either
+ 'nameWithoutLanguage' or 'nameWithLanguage'. That is, all IPP
+ objects and clients MUST support both the 'nameWithoutLanguage' and '
+ nameWithLanguage' attribute syntaxes. However, in actual usage and
+ protocol execution, objects and clients accept and return only one of
+ the two syntax per attribute. The syntax 'name' never appears "on-
+ the-wire".
+
+ Note: Only the 'text' and 'name' attribute syntaxes permit the
+ Natural Language Override mechanism.
+
+ Some attributes are defined as 'type3 keyword | name'. These
+ attributes support values that are either type3 keywords or names.
+ This dual-syntax mechanism enables a site administrator to extend
+ these attributes to legally include values that are locally defined
+ by the site administrator. Such names are not registered with IANA.
+
+4.1.2.1 'nameWithoutLanguage'
+
+ The 'nameWithoutLanguage' syntax indicates a value that is sequence
+ of zero or more characters so that its encoded form does not exceed
+ MAX octets.
+
+
+
+
+deBry, et al. Experimental [Page 62]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+4.1.2.2 'nameWithLanguage'
+
+ The 'nameWithLanguage' attribute syntax is a compound attribute
+ syntax consisting of two parts: a 'nameWithoutLanguage' part plus an
+ additional 'naturalLanguage' (see section 4.1.8) part that overrides
+ the natural language in force. The 'naturalLanguage' part explicitly
+ identifies the natural language that applies to that name value and
+ that name value alone.
+
+ The 'nameWithLanguage' attribute syntax behaves the same as the '
+ textWithLanguage' syntax. If a name is in a language that is
+ different than the rest of the object or operation, then this '
+ nameWithLanguage' syntax is used rather than the generic '
+ nameWithoutLanguage' syntax.
+
+ Example: If the client supplies the "attributes-natural-language"
+ operation attribute with the value: 'en' indicating English, but the
+ "printer-name" attribute is in German, the client MUST use the '
+ nameWithLanguage' attribute syntax as follows:
+
+ 'de': Natural Language Override indicating German
+ 'Farbdrucker': the Printer name in German
+
+4.1.2.3 Matching 'name' attribute values
+
+ For purposes of matching two 'name' attribute values for equality,
+ such as in job validation (where a client-supplied value for
+ attribute "xxx" is checked to see if the value is among the values of
+ the Printer object's corresponding "xxx-supported" attribute), the
+ following match rules apply:
+
+ 1. 'keyword' values never match 'name' values.
+
+ 2. 'name' (nameWithoutLanguage and nameWithLanguage) values
+ match if (1) the name parts match and (2) the Associated
+ Natural-Language parts (see section 3.1.4.1) match. The
+ matching rules are:
+
+ a. the name parts match if the two names are identical
+ character by character, except it is RECOMMENDED that case
+ be ignored. For example: 'Ajax-letter-head-white' MUST
+ match 'Ajax-letter-head-white' and SHOULD match 'ajax-
+ letter-head-white' and 'AJAX-LETTER-HEAD-WHITE'.
+
+ b. the Associated Natural-Language parts match if the
+ shorter of the two meets the syntactic requirements of RFC
+ 1766 [RFC1766] and matches byte for byte with the longer.
+ For example, 'en' matches 'en', 'en-us' and 'en-gb', but
+
+
+
+deBry, et al. Experimental [Page 63]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ matches neither 'fr' nor 'e'.
+
+4.1.3 'keyword'
+
+ The 'keyword' attribute syntax is a sequence of characters, length: 1
+ to 255, containing only the US-ASCII [ASCII] encoded values for
+ lowercase letters ("a" - "z"), digits ("0" - "9"), hyphen ("-"), dot
+ ("."), and underscore ("_"). The first character MUST be a lowercase
+ letter. Furthermore, keywords MUST be in U.S. English.
+
+ This syntax type is used for enumerating semantic identifiers of
+ entities in the abstract protocol, i.e., entities identified in this
+ document. Keywords are used as attribute names or values of
+ attributes. Unlike 'text' and 'name' attribute values, 'keyword'
+ values MUST NOT use the Natural Language Override mechanism, since
+ they MUST always be US-ASCII and U.S. English.
+
+ Keywords are for use in the protocol. A user interface will likely
+ provide a mapping between protocol keywords and displayable user-
+ friendly words and phrases which are localized to the natural
+ language of the user. While the keywords specified in this document
+ MAY be displayed to users whose natural language is U.S. English,
+ they MAY be mapped to other U.S. English words for U.S. English
+ users, since the user interface is outside the scope of this
+ document.
+
+ In the definition for each attribute of this syntax type, the full
+ set of defined keyword values for that attribute are listed.
+
+ When a keyword is used to represent an attribute (its name), it MUST
+ be unique within the full scope of all IPP objects and attributes.
+ When a keyword is used to represent a value of an attribute, it MUST
+ be unique just within the scope of that attribute. That is, the same
+ keyword MUST NOT be used for two different values within the same
+ attribute to mean two different semantic ideas. However, the same
+ keyword MAY be used across two or more attributes, representing
+ different semantic ideas for each attribute. Section 6.1 describes
+ how the protocol can be extended with new keyword values. Examples
+ of attribute name keywords:
+
+ "job-name"
+ "attributes-charset"
+
+ Note: This document uses "type1", "type2", and "type3" prefixes to
+ the "keyword" basic syntax to indicate different levels of review for
+ extensions (see section 6.1).
+
+
+
+
+
+deBry, et al. Experimental [Page 64]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+4.1.4 'enum'
+
+ The 'enum' attribute syntax is an enumerated integer value that is in
+ the range from 1 to 2**31 - 1 (MAX). Each value has an associated '
+ keyword' name. In the definition for each attribute of this syntax
+ type, the full set of possible values for that attribute are listed.
+ This syntax type is used for attributes for which there are enum
+ values assigned by other standards, such as SNMP MIBs. A number of
+ attribute enum values in this specification are also used for
+ corresponding attributes in other standards [RFC1759]. This syntax
+ type is not used for attributes to which the system administrator may
+ assign values. Section 6.1 describes how the protocol can be
+ extended with new enum values.
+
+ Enum values are for use in the protocol. A user interface will
+ provide a mapping between protocol enum values and displayable user-
+ friendly words and phrases which are localized to the natural
+ language of the user. While the enum symbols specified in this
+ document MAY be displayed to users whose natural language is U.S.
+ English, they MAY be mapped to other U.S. English words for U.S.
+ English users, since the user interface is outside the scope of this
+ document.
+
+ Note: SNMP MIBs use '2' for 'unknown' which corresponds to the IPP
+ "out-of-band" value 'unknown'. See the description of the "out-of-
+ band" values at the beginning of Section 4.1. Therefore, attributes
+ of type 'enum' start at '3'.
+
+ Note: This document uses "type1", "type2", and "type3" prefixes to
+ the "enum" basic syntax to indicate different levels of review for
+ extensions (see section 6.1).
+
+4.1.5 'uri'
+
+ The 'uri' attribute syntax is any valid Uniform Resource Identifier
+ or URI [RFC2396]. Most often, URIs are simply Uniform Resource
+ Locators or URLs. The maximum length of URIs used as values of IPP
+ attributes is 1023 octets. Although most other IPP attribute syntax
+ types allow for only lower-cased values, this attribute syntax type
+ conforms to the case-sensitive and case-insensitive rules specified
+ in [RFC2396].
+
+4.1.6 'uriScheme'
+
+ The 'uriScheme' attribute syntax is a sequence of characters
+ representing a URI scheme according to RFC 2396 [RFC2396]. Though
+ RFC 2396 requires that the values be case-insensitive, IPP requires
+
+
+
+
+deBry, et al. Experimental [Page 65]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ all lower case values in IPP attributes to simplify comparing by IPP
+ clients and Printer objects. Standard values for this syntax type
+ are the following keywords:
+
+ 'http': for HTTP schemed URIs (e.g., "http:...")
+ 'https': for use with HTTPS schemed URIs (e.g., "https:...")
+ (not on IETF standards track)
+ 'ftp': for FTP schemed URIs (e.g., "ftp:...")
+ 'mailto': for SMTP schemed URIs (e.g., "mailto:...")
+ 'file': for file schemed URIs (e.g., "file:...")
+
+ A Printer object MAY support any URI 'scheme' that has been
+ registered with IANA [IANA-MT]. The maximum length of URI 'scheme'
+ values used to represent IPP attribute values is 63 octets.
+
+4.1.7 'charset'
+
+ The 'charset' attribute syntax is a standard identifier for a
+ charset. A charset is a coded character set and encoding scheme.
+ Charsets are used for labeling certain document contents and 'text'
+ and 'name' attribute values. The syntax and semantics of this
+ attribute syntax are specified in RFC 2046 [RFC2046] and contained in
+ the IANA character-set Registry [IANA-CS] according to the IANA
+ procedures [RFC2278]. Though RFC 2046 requires that the values be
+ case-insensitive US-ASCII, IPP requires all lower case values in IPP
+ attributes to simplify comparing by IPP clients and Printer objects.
+ When a character-set in the IANA registry has more than one name
+ (alias), the name labeled as "(preferred MIME name)", if present,
+ MUST be used.
+
+ The maximum length of 'charset' values used to represent IPP
+ attribute values is 63 octets.
+
+ Some examples are:
+
+ 'utf-8': ISO 10646 Universal Multiple-Octet Coded Character Set
+ (UCS) represented as the UTF-8 [RFC2279] transfer encoding
+ scheme in which US-ASCII is a subset charset.
+ 'us-ascii': 7-bit American Standard Code for Information
+ Interchange (ASCII), ANSI X3.4-1986 [ASCII]. That standard
+ defines US-ASCII, but RFC 2045 [RFC2045] eliminates most of the
+ control characters from conformant usage in MIME and IPP.
+ 'iso-8859-1': 8-bit One-Byte Coded Character Set, Latin Alphabet
+ Nr 1 [ISO8859-1]. That standard defines a coded character set
+ that is used by Latin languages in the Western Hemisphere and
+ Western Europe. US-ASCII is a subset charset.
+
+
+
+
+
+deBry, et al. Experimental [Page 66]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ 'iso-10646-ucs-2': ISO 10646 Universal Multiple-Octet Coded
+ Character Set (UCS) represented as two octets (UCS-2), with the
+ high order octet of each pair coming first (so-called Big Endian
+ integer).
+
+ Some attribute descriptions MAY place additional requirements on
+ charset values that may be used, such as REQUIRED values that MUST be
+ supported or additional restrictions, such as requiring that the
+ charset have US-ASCII as a subset charset.
+
+4.1.8 'naturalLanguage'
+
+ The 'naturalLanguage' attribute syntax is a standard identifier for a
+ natural language and optionally a country. The values for this
+ syntax type are defined by RFC 1766 [RFC1766]. Though RFC 1766
+ requires that the values be case-insensitive US-ASCII, IPP requires
+ all lower case to simplify comparing by IPP clients and Printer
+ objects. Examples include:
+
+ 'en': for English
+ 'en-us': for US English
+ 'fr': for French
+ 'de': for German
+
+ The maximum length of 'naturalLanguage' values used to represent IPP
+ attribute values is 63 octets.
+
+4.1.9 'mimeMediaType'
+
+ The 'mimeMediaType' attribute syntax is the Internet Media Type
+ (sometimes called MIME type) as defined by RFC 2046 [RFC2046] and
+ registered according to the procedures of RFC 2048 [RFC2048] for
+ identifying a document format. The value MAY include a charset
+ parameter, depending on the specification of the Media Type in the
+ IANA Registry [IANA-MT]. Although most other IPP syntax types allow
+ for only lower-cased values, this syntax type allows for mixed-case
+ values which are case-insensitive.
+
+ Examples are:
+
+ 'text/html': An HTML document
+ 'text/plain': A plain text document in US-ASCII (RFC 2046 indicates
+ that in the absence of the charset parameter MUST mean US-ASCII
+ rather than simply unspecified) [RFC2046].
+ 'text/plain; charset=US-ASCII': A plain text document in US-ASCII
+ [52, 56].
+ 'text/plain; charset=ISO-8859-1': A plain text document in ISO
+ 8859-1 (Latin 1) [ISO8859-1].
+
+
+
+deBry, et al. Experimental [Page 67]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ 'text/plain; charset=utf-8': A plain text document in ISO 10646
+ represented as UTF-8 [RFC2279]
+ 'text/plain, charset=iso-10646-ucs-2': A plain text document in
+ ISO 10646 represented in two octets (UCS-2) [ISO10646-1]
+ 'application/postscript': A PostScript document [RFC2046]
+ 'application/vnd.hp-PCL': A PCL document [IANA-MT] (charset escape
+ sequence embedded in the document data)
+ 'application/octet-stream': Auto-sense - see below
+
+ One special type is 'application/octet-stream'. If the Printer
+ object supports this value, the Printer object MUST be capable of
+ auto-sensing the format of the document data. If the Printer
+ object's default value attribute "document-format-default" is set to
+ 'application/octet-stream', the Printer object not only supports
+ auto-sensing of the document format, but will depend on the result of
+ applying its auto-sensing when the client does not supply the
+ "document-format" attribute. If the client supplies a document
+ format value, the Printer MUST rely on the supplied attribute, rather
+ than trust its auto-sensing algorithm. To summarize:
+
+ 1. If the client does not supply a document format value, the
+ Printer MUST rely on its default value setting (which may be '
+ application/octet-stream' indicating an auto-sensing mechanism).
+ 2. If the client supplies a value other than 'application/octet-
+ stream', the client is supplying valid information about the
+ format of the document data and the Printer object MUST trust
+ the client supplied value more than the outcome of applying an
+ automatic format detection mechanism. For example, the client
+ may be requesting the printing of a PostScript file as a '
+ text/plain' document. The Printer object MUST print a text
+ representation of the PostScript commands rather than interpret
+ the stream of PostScript commands and print the result.
+ 3. If the client supplies a value of 'application/octet-stream',
+ the client is indicating that the Printer object MUST use its
+ auto-sensing mechanism on the client supplied document data
+ whether auto-sensing is the Printer object's default or not.
+
+ Note: Since the auto-sensing algorithm is probabilistic, if the
+ client requests both auto-sensing ("document-format" set to '
+ application/octet-stream') and true fidelity ("ipp-attribute-
+ fidelity" set to 'true'), the Printer object might not be able to
+ guarantee exactly what the end user intended (the auto-sensing
+ algorithm might mistake one document format for another ), but it is
+ able to guarantee that its auto-sensing mechanism be used.
+
+ The maximum length of a 'mimeMediaType' value to represent IPP
+ attribute values is 255 octets.
+
+
+
+
+deBry, et al. Experimental [Page 68]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+4.1.10 'octetString'
+
+ The 'octetString' attribute syntax is a sequence of octets encoded in
+ a maximum of 1023 octets which is indicated in sub-section headers
+ using the notation: octetString(MAX). This syntax type is used for
+ opaque data.
+
+4.1.11 'boolean'
+
+ The 'boolean' attribute syntax has only two values: 'true' and '
+ false'.
+
+4.1.12 'integer'
+
+ The 'integer' attribute syntax is an integer value that is in the
+ range from -2**31 (MIN) to 2**31 - 1 (MAX). Each individual
+ attribute may specify the range constraint explicitly in sub-section
+ headers if the range is different from the full range of possible
+ integer values. For example: job-priority (integer(1:100)) for the
+ "job-priority" attribute. However, the enforcement of that
+ additional constraint is up to the IPP objects, not the protocol.
+
+4.1.13 'rangeOfInteger'
+
+ The 'rangeOfInteger' attribute syntax is an ordered pair of integers
+ that defines an inclusive range of integer values. The first integer
+ specifies the lower bound and the second specifies the upper bound.
+ If a range constraint is specified in the header description for an
+ attribute in this document whose attribute syntax is 'rangeOfInteger'
+ (i.e., 'X:Y' indicating X as a minimum value and Y as a maximum
+ value), then the constraint applies to both integers.
+
+4.1.14 'dateTime'
+
+ The 'dateTime' attribute syntax is a standard, fixed length, 11 octet
+ representation of the "DateAndTime" syntax as defined in RFC 2579
+ [RFC2579]. RFC 2579 also identifies an 8 octet representation of a
+ "DateAndTime" value, but IPP objects MUST use the 11 octet
+ representation. A user interface will provide a mapping between
+ protocol dateTime values and displayable user-friendly words or
+ presentation values and phrases which are localized to the natural
+ language and date format of the user.
+
+4.1.15 'resolution'
+
+ The 'resolution' attribute syntax specifies a two-dimensional
+ resolution in the indicated units. It consists of 3 values: a cross
+ feed direction resolution (positive integer value), a feed direction
+
+
+
+deBry, et al. Experimental [Page 69]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ resolution (positive integer value), and a units value. The
+ semantics of these three components are taken from the Printer MIB
+ [RFC1759] suggested values. That is, the cross feed direction
+ component resolution component is the same as the
+ prtMarkerAddressabilityXFeedDir object in the Printer MIB, the feed
+ direction component resolution component is the same as the
+ prtMarkerAddressabilityFeedDir in the Printer MIB, and the units
+ component is the same as the prtMarkerAddressabilityUnit object in
+ the Printer MIB (namely, '3' indicates dots per inch and '4'
+ indicates dots per centimeter). All three values MUST be present
+ even if the first two values are the same. Example: '300', '600', '
+ 3' indicates a 300 dpi cross-feed direction resolution, a 600 dpi
+ feed direction resolution, since a '3' indicates dots per inch (dpi).
+
+4.1.16 '1setOf X'
+
+ The '1setOf X' attribute syntax is 1 or more values of attribute
+ syntax type X. This syntax type is used for multi-valued attributes.
+ The syntax type is called '1setOf' rather than just 'setOf' as a
+ reminder that the set of values MUST NOT be empty (i.e., a set of
+ size 0). Sets are normally unordered. However each attribute
+ description of this type may specify that the values MUST be in a
+ certain order for that attribute.
+
+4.2 Job Template Attributes
+
+ Job Template attributes describe job processing behavior. Support
+ for Job Template attributes by a Printer object is OPTIONAL (see
+ section 13.2.3 for a description of support for OPTIONAL attributes).
+ Also, clients OPTIONALLY supply Job Template attributes in create
+ requests.
+
+ Job Template attributes conform to the following rules. For each Job
+ Template attribute called "xxx":
+
+ 1. If the Printer object supports "xxx" then it MUST support both a
+ "xxx-default" attribute (unless there is a "No" in the table
+ below) and a "xxx-supported" attribute. If the Printer object
+ doesn't support "xxx", then it MUST support neither an "xxx-
+ default" attribute nor an "xxx-supported" attribute, and it MUST
+ treat an attribute "xxx" supplied by a client as unsupported.
+ An attribute "xxx" may be supported for some document formats
+ and not supported for other document formats. For example, it
+ is expected that a Printer object would only support
+ "orientation-requested" for some document formats (such as '
+ text/plain' or 'text/html') but not others (such as '
+ application/postscript').
+
+
+
+
+deBry, et al. Experimental [Page 70]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ 2. "xxx" is OPTIONALLY supplied by the client in a create request.
+ If "xxx" is supplied, the client is indicating a desired job
+ processing behavior for this Job. When "xxx" is not supplied,
+ the client is indicating that the Printer object apply its
+ default job processing behavior at job processing time if the
+ document content does not contain an embedded instruction
+ indicating an xxx-related behavior.
+
+ Note: Since an administrator MAY change the default value
+ attribute after a Job object has been submitted but before it
+ has been processed, the default value used by the Printer object
+ at job processing time may be different that the default value
+ in effect at job submission time.
+
+ 3. The "xxx-supported" attribute is a Printer object attribute that
+ describes which job processing behaviors are supported by that
+ Printer object. A client can query the Printer object to find
+ out what xxx-related behaviors are supported by inspecting the
+ returned values of the "xxx-supported" attribute.
+
+ Note: The "xxx" in each "xxx-supported" attribute name is
+ singular, even though an "xxx-supported" attribute usually has
+ more than one value, such as "job-sheet-supported", unless the
+ "xxx" Job Template attribute is plural, such as "finishings" or
+ "sides". In such cases the "xxx-supported" attribute names are:
+ "finishings-supported" and "sides-supported".
+
+ 4. The "xxx-default" default value attribute describes what will be
+ done at job processing time when no other job processing
+ information is supplied by the client (either explicitly as an
+ IPP attribute in the create request or implicitly as an embedded
+ instruction within the document data).
+
+ If an application wishes to present an end user with a list of
+ supported values from which to choose, the application SHOULD query
+ the Printer object for its supported value attributes. The
+ application SHOULD also query the default value attributes. If the
+ application then limits selectable values to only those value that
+ are supported, the application can guarantee that the values supplied
+ by the client in the create request all fall within the set of
+ supported values at the Printer. When querying the Printer, the
+ client MAY enumerate each attribute by name in the Get-Printer-
+ Attributes Request, or the client MAY just name the "job-template"
+ group in order to get the complete set of supported attributes (both
+ supported and default attributes).
+
+
+
+
+
+
+deBry, et al. Experimental [Page 71]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ The "finishings" attribute is an example of a Job Template attribute.
+ It can take on a set of values such as 'staple', 'punch', and/or '
+ cover'. A client can query the Printer object for the "finishings-
+ supported" attribute and the "finishings-default" attribute. The
+ supported attribute contains a set of supported values. The default
+ value attribute contains the finishing value(s) that will be used for
+ a new Job if the client does not supply a "finishings" attribute in
+ the create request and the document data does not contain any
+ corresponding finishing instructions. If the client does supply the
+ "finishings" attribute in the create request, the IPP object
+ validates the value or values to make sure that they are a subset of
+ the supported values identified in the Printer object's "finishings-
+ supported" attribute. See section 3.2.1.2.
+
+ The table below summarizes the names and relationships for all Job
+ Template attributes. The first column of the table (labeled "Job
+ Attribute") shows the name and syntax for each Job Template attribute
+ in the Job object. These are the attributes that can optionally be
+ supplied by the client in a create request. The last two columns
+ (labeled "Printer: Default Value Attribute" and "Printer: Supported
+ Values Attribute") shows the name and syntax for each Job Template
+ attribute in the Printer object (the default value attribute and the
+ supported values attribute). A "No" in the table means the Printer
+ MUST NOT support the attribute (that is, the attribute is simply not
+ applicable). For brevity in the table, the 'text' and 'name' entries
+ do not show the maximum length for each attribute.
+
+ +===================+======================+======================+
+ | Job Attribute |Printer: Default Value| Printer: Supported |
+ | | Attribute | Values Attribute |
+ +===================+======================+======================+
+ | job-priority | job-priority-default |job-priority-supported|
+ | (integer 1:100) | (integer 1:100) |(integer 1:100) |
+ +-------------------+----------------------+----------------------+
+ | job-hold-until | job-hold-until- |job-hold-until- |
+ | (type3 keyword | | default | supported |
+ | name) | (type3 keyword | |(1setOf |
+ | | name) | type3 keyword | name)|
+ +-------------------+----------------------+----------------------+
+ | job-sheets | job-sheets-default |job-sheets-supported |
+ | (type3 keyword | | (type3 keyword | |(1setOf |
+ | name) | name) | type3 keyword | name)|
+ +-------------------+----------------------+----------------------+
+ |multiple-document- |multiple-document- |multiple-document- |
+ | handling | handling-default |handling-supported |
+ | (type2 keyword) | (type2 keyword) |(1setOf type2 keyword)|
+ +-------------------+----------------------+----------------------+
+
+
+
+
+deBry, et al. Experimental [Page 72]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ +===================+======================+======================+
+ | Job Attribute |Printer: Default Value| Printer: Supported |
+ | | Attribute | Values Attribute |
+ +===================+======================+======================+
+ | copies | copies-default | copies-supported |
+ | (integer (1:MAX)) | (integer (1:MAX)) | (rangeOfInteger |
+ | | | (1:MAX)) |
+ +-------------------+----------------------+----------------------+
+ | finishings | finishings-default | finishings-supported |
+ |(1setOf type2 enum)|(1setOf type2 enum) |(1setOf type2 enum) |
+ +-------------------+----------------------+----------------------+
+ | page-ranges | No | page-ranges- |
+ | (1setOf | | supported (boolean) |
+ | rangeOfInteger | | |
+ | (1:MAX)) | | |
+ +-------------------+----------------------+----------------------+
+ | sides | sides-default | sides-supported |
+ | (type2 keyword) | (type2 keyword) |(1setOf type2 keyword)|
+ +-------------------+----------------------+----------------------+
+ | number-up | number-up-default | number-up-supported |
+ | (integer (1:MAX)) | (integer (1:MAX)) |(1setOf integer |
+ | | | (1:MAX) | |
+ | | | rangeOfInteger |
+ | | | (1:MAX)) |
+ +-------------------+----------------------+----------------------+
+ | orientation- |orientation-requested-|orientation-requested-|
+ | requested | default | supported |
+ | (type2 enum) | (type2 enum) | (1setOf type2 enum) |
+ +-------------------+----------------------+----------------------+
+ | media | media-default | media-supported |
+ | (type3 keyword | | (type3 keyword | |(1setOf |
+ | name) | name) | type3 keyword | name)|
+ | | | |
+ | | | media-ready |
+ | | |(1setOf |
+ | | | type3 keyword | name)|
+ +-------------------+----------------------+----------------------+
+ | printer-resolution| printer-resolution- | printer-resolution- |
+ | (resolution) | default | supported |
+ | | (resolution) |(1setOf resolution) |
+ +-------------------+----------------------+----------------------+
+ | print-quality | print-quality-default| print-quality- |
+ | (type2 enum) | (type2 enum) | supported |
+ | | |(1setOf type2 enum) |
+ +-------------------+----------------------+----------------------+
+
+
+
+
+
+
+deBry, et al. Experimental [Page 73]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+4.2.1 job-priority (integer(1:100))
+
+ This attribute specifies a priority for scheduling the Job. A higher
+ value specifies a higher priority. The value 1 indicates the lowest
+ possible priority. The value 100 indicates the highest possible
+ priority. Among those jobs that are ready to print, a Printer MUST
+ print all jobs with a priority value of n before printing those with
+ a priority value of n-1 for all n.
+
+ If the Printer object supports this attribute, it MUST always support
+ the full range from 1 to 100. No administrative restrictions are
+ permitted. This way an end-user can always make full use of the
+ entire range with any Printer object. If privileged jobs are
+ implemented outside IPP/1.0, they MUST have priorities higher than
+ 100, rather than restricting the range available to end-users.
+
+ If the client does not supply this attribute and this attribute is
+ supported by the Printer object, the Printer object MUST use the
+ value of the Printer object's "job-priority-default" at job
+ submission time (unlike most Job Template attributes that are used if
+ necessary at job processing time).
+
+ The syntax for the "job-priority-supported" is also integer(1:100).
+ This single integer value indicates the number of priority levels
+ supported. The Printer object MUST take the value supplied by the
+ client and map it to the closest integer in a sequence of n integers
+ values that are evenly distributed over the range from 1 to 100 using
+ the formula:
+
+ roundToNearestInt((100x+50)/n)
+
+ where n is the value of "job-priority-supported" and x ranges from 0
+ through n-1.
+
+ For example, if n=1 the sequence of values is 50; if n=2, the
+ sequence of values is: 25 and 75; if n = 3, the sequence of values
+ is: 17, 50 and 83; if n = 10, the sequence of values is: 5, 15, 25,
+ 35, 45, 55, 65, 75, 85, and 95; if n = 100, the sequence of values
+ is: 1, 2, 3, . 100.
+
+ If the value of the Printer object's "job-priority-supported" is 10
+ and the client supplies values in the range 1 to 10, the Printer
+ object maps them to 5, in the range 11 to 20, the Printer object maps
+ them to 15, etc.
+
+
+
+
+
+
+
+deBry, et al. Experimental [Page 74]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+4.2.2 job-hold-until (type3 keyword | name (MAX))
+
+ This attribute specifies the named time period during which the Job
+ MUST become a candidate for printing.
+
+ Standard keyword values for named time periods are:
+
+ 'no-hold': immediately, if there are not other reasons to hold the
+ job
+ 'day-time': during the day
+ 'evening': evening
+ 'night': night
+ 'weekend': weekend
+ 'second-shift': second-shift (after close of business)
+ 'third-shift': third-shift (after midnight)
+
+ An administrator MUST associate allowable print times with a named
+ time period (by means outside IPP/1.0). An administrator is
+ encouraged to pick names that suggest the type of time period. An
+ administrator MAY define additional values using the 'name' or '
+ keyword' attribute syntax, depending on implementation.
+
+ If the value of this attribute specifies a time period that is in the
+ future, the Printer MUST add the 'job-hold-until-specified' value to
+ the job's "job-state-reasons" attribute, move the job to the '
+ pending-held' state, and MUST NOT schedule the job for printing until
+ the specified time-period arrives. When the specified time period
+ arrives, the Printer MUST remove the 'job-hold-until-specified' value
+ from the job's "job-state-reason" attribute and, if there are no
+ other job state reasons that keep the job in the 'pending-held'
+ state, the Printer MUST consider the job as a candidate for
+ processing by moving the job to the 'pending' state.
+
+ If this job attribute value is the named value 'no-hold', or the
+ specified time period has already started, the job MUST be a
+ candidate for processing immediately.
+
+ If the client does not supply this attribute and this attribute is
+ supported by the Printer object, the Printer object MUST use the
+ value of the Printer object's "job-hold-until-default" at job
+ submission time (unlike most Job Template attributes that are used if
+ necessary at job processing time).
+
+4.2.3 job-sheets (type3 keyword | name(MAX))
+
+ This attribute determines which job start/end sheet(s), if any, MUST
+ be printed with a job.
+
+
+
+
+deBry, et al. Experimental [Page 75]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ Standard keyword values are:
+
+ 'none': no job sheet is printed
+ 'standard': one or more site specific standard job sheets are
+ printed, e.g. a single start sheet or both start and end sheet
+ is printed
+
+ An administrator MAY define additional values using the 'name' or '
+ keyword' attribute syntax, depending on implementation.
+
+ Note: The effect of this attribute on jobs with multiple documents
+ MAY be affected by the "multiple-document-handling" job attribute
+ (section 4.2.4), depending on the job sheet semantics.
+
+4.2.4 multiple-document-handling (type2 keyword)
+
+ This attribute is relevant only if a job consists of two or more
+ documents. The attribute controls finishing operations and the
+ placement of one or more print-stream pages into impressions and onto
+ media sheets. When the value of the "copies" attribute exceeds 1, it
+ also controls the order in which the copies that result from
+ processing the documents are produced. For the purposes of this
+ explanations, if "a" represents an instance of document data, then
+ the result of processing the data in document "a" is a sequence of
+ media sheets represented by "a(*)".
+
+ Standard keyword values are:
+
+ 'single-document': If a Job object has multiple documents, say, the
+ document data is called a and b, then the result of processing
+ all the document data (a and then b) MUST be treated as a single
+ sequence of media sheets for finishing operations; that is,
+ finishing would be performed on the concatenation of the
+ sequences a(*),b(*). The Printer object MUST NOT force the data
+ in each document instance to be formatted onto a new print-
+ stream page, nor to start a new impression on a new media sheet.
+ If more than one copy is made, the ordering of the sets of media
+ sheets resulting from processing the document data MUST be a(*),
+ b(*), a(*), b(*), ..., and the Printer object MUST force each
+ copy (a(*),b(*)) to start on a new media sheet.
+ 'separate-documents-uncollated-copies': If a Job object has
+ multiple documents, say, the document data is called a and b,
+ then the result of processing the data in each document instance
+ MUST be treated as a single sequence of media sheets for
+ finishing operations; that is, the sets a(*) and b(*) would each
+ be finished separately. The Printer object MUST force each copy
+ of the result of processing the data in a single document to
+ start on a new media sheet. If more than one copy is made, the
+
+
+
+deBry, et al. Experimental [Page 76]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ ordering of the sets of media sheets resulting from processing
+ the document data MUST be a(*), a(*), ..., b(*), b(*) ... .
+ 'separate-documents-collated-copies': If a Job object has multiple
+ documents, say, the document data is called a and b, then the
+ result of processing the data in each document instance MUST be
+ treated as a single sequence of media sheets for finishing
+ operations; that is, the sets a(*) and b(*) would each be
+ finished separately. The Printer object MUST force each copy of
+ the result of processing the data in a single document to start
+ on a new media sheet. If more than one copy is made, the
+ ordering of the sets of media sheets resulting from processing
+ the document data MUST be a(*), b(*), a(*), b(*), ... .
+ 'single-document-new-sheet': Same as 'single-document', except
+ that the Printer object MUST ensure that the first impression of
+ each document instance in the job is placed on a new media
+ sheet. This value allows multiple documents to be stapled
+ together with a single staple where each document starts on a
+ new sheet.
+
+ The 'single-document' value is the same as 'separate-documents-
+ collated-copies' with respect to ordering of print-stream pages, but
+ not media sheet generation, since 'single-document' will put the
+ first page of the next document on the back side of a sheet if an odd
+ number of pages have been produced so far for the job, while '
+ separate-documents-collated-copies' always forces the next document
+ or document copy on to a new sheet. In addition, if the "finishings"
+ attribute specifies 'staple', then with 'single-document', documents
+ a and b are stapled together as a single document with no regard to
+ new sheets, with 'single-document-new-sheet', documents a and b are
+ stapled together as a single document, but document b starts on a new
+ sheet, but with 'separate-documents-uncollated-copies' and '
+ separate-documents-collated-copies', documents a and b are stapled
+ separately.
+
+ Note: None of these values provide means to produce uncollated sheets
+ within a document, i.e., where multiple copies of sheet n are
+ produced before sheet n+1 of the same document.
+
+ The relationship of this attribute and the other attributes that
+ control document processing is described in section 15.3.
+
+4.2.5 copies (integer(1:MAX))
+
+ This attribute specifies the number of copies to be printed.
+
+ On many devices the supported number of collated copies will be
+ limited by the number of physical output bins on the device, and may
+ be different from the number of uncollated copies which can be
+
+
+
+deBry, et al. Experimental [Page 77]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ supported.
+
+ Note: The effect of this attribute on jobs with multiple documents is
+ controlled by the "multiple-document-handling" job attribute (section
+ 4.2.4) and the relationship of this attribute and the other
+ attributes that control document processing is described in section
+ 15.3.
+
+4.2.6 finishings (1setOf type2 enum)
+
+ This attribute identifies the finishing operations that the Printer
+ uses for each copy of each printed document in the Job. For Jobs with
+ multiple documents, the "multiple-document-handling" attribute
+ determines what constitutes a "copy" for purposes of finishing.
+
+ Standard enum values are:
+
+ Value Symbolic Name and Description
+
+ '3' 'none': Perform no finishing
+ '4' 'staple': Bind the document(s) with one or more staples.
+ The exact number and placement of the staples is
+ site-defined.
+ '5' 'punch': This value indicates that holes are required in
+ the finished document. The exact number and placement
+ of the holes is site-defined The punch specification
+ MAY be satisfied (in a site- and implementation-
+ specific manner) either by drilling/punching, or by
+ substituting pre-drilled media.
+ '6' 'cover': This value is specified when it is desired to
+ select a non-printed (or pre-printed) cover for the
+ document. This does not supplant the specification of
+ a printed cover (on cover stock medium) by the
+ document itself.
+ '7' 'bind': This value indicates that a binding is to be
+ applied to the document; the type and placement of the
+ binding is site-defined."
+
+ Note: The effect of this attribute on jobs with multiple documents is
+ controlled by the "multiple-document-handling" job attribute (section
+ 4.2.4) and the relationship of this attribute and the other
+ attributes that control document processing is described in section
+ 15.3.
+
+ If the client supplies a value of 'none' along with any other
+ combination of values, it is the same as if only that other
+ combination of values had been supplied (that is the 'none' value has
+ no effect).
+
+
+
+deBry, et al. Experimental [Page 78]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+4.2.7 page-ranges (1setOf rangeOfInteger (1:MAX))
+
+ This attribute identifies the range(s) of print-stream pages that the
+ Printer object uses for each copy of each document which are to be
+ printed. Nothing is printed for any pages identified that do not
+ exist in the document(s). Ranges MUST be in ascending order, for
+ example: 1-3, 5-7, 15-19 and MUST NOT overlap, so that a non-spooling
+ Printer object can process the job in a single pass. If the ranges
+ are not ascending or are overlapping, the IPP object MUST reject the
+ request and return the 'client-error-bad-request' status code. The
+ attribute is associated with print-stream pages not application-
+ numbered pages (for example, the page numbers found in the headers
+ and or footers for certain word processing applications).
+
+ For Jobs with multiple documents, the "multiple-document-handling"
+ attribute determines what constitutes a "copy" for purposes of the
+ specified page range(s). When "multiple-document-handling" is '
+ single-document', the Printer object MUST apply each supplied page
+ range once to the concatenation of the print-stream pages. For
+ example, if there are 8 documents of 10 pages each, the page-range '
+ 41:60' prints the pages in the 5th and 6th documents as a single
+ document and none of the pages of the other documents are printed.
+ When "multiple-document-handling" is 'separate-documents-uncollated-
+ copies' or 'separate-documents-collated-copies', the Printer object
+ MUST apply each supplied page range repeatedly to each document copy.
+ For the same job, the page-range '1:3, 10:10' would print the first 3
+ pages and the 10th page of each of the 8 documents in the Job, as 8
+ separate documents.
+
+ In most cases, the exact pages to be printed will be generated by a
+ device driver and this attribute would not be required. However,
+ when printing an archived document which has already been formatted,
+ the end user may elect to print just a subset of the pages contained
+ in the document. In this case, if page-range = n.m is specified, the
+ first page to be printed will be page n. All subsequent pages of the
+ document will be printed through and including page m.
+
+ "page-ranges-supported" is a boolean value indicating whether or not
+ the printer is capable of supporting the printing of page ranges.
+ This capability may differ from one PDL to another. There is no
+ "page-ranges-default" attribute. If the "page-ranges" attribute is
+ not supplied by the client, all pages of the document will be
+ printed.
+
+
+
+
+
+
+
+
+deBry, et al. Experimental [Page 79]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ Note: The effect of this attribute on jobs with multiple documents is
+ controlled by the "multiple-document-handling" job attribute (section
+ 4.2.4) and the relationship of this attribute and the other
+ attributes that control document processing is described in section
+ 15.3.
+
+4.2.8 sides (type2 keyword)
+
+ This attribute specifies how print-stream pages are to be imposed
+ upon the sides of an instance of a selected medium, i.e., an
+ impression.
+
+ The standard keyword values are:
+
+ 'one-sided': imposes each consecutive print-stream page upon the
+ same side of consecutive media sheets.
+ 'two-sided-long-edge': imposes each consecutive pair of print-
+ stream pages upon front and back sides of consecutive media
+ sheets, such that the orientation of each pair of print-stream
+ pages on the medium would be correct for the reader as if for
+ binding on the long edge. This imposition is sometimes called '
+ duplex' or 'head-to-head'.
+ 'two-sided-short-edge': imposes each consecutive pair of print-
+ stream pages upon front and back sides of consecutive media
+ sheets, such that the orientation of each pair of print-stream
+ pages on the medium would be correct for the reader as if for
+ binding on the short edge. This imposition is sometimes called
+ 'tumble' or 'head-to-toe'.
+
+ 'two-sided-long-edge', 'two-sided-short-edge', 'tumble', and 'duplex'
+ all work the same for portrait or landscape. However 'head-to-toe'
+ is 'tumble' in portrait but 'duplex' in landscape. 'head-to-head'
+ also switches between 'duplex' and 'tumble' when using portrait and
+ landscape modes.
+
+ Note: The effect of this attribute on jobs with multiple documents is
+ controlled by the "multiple-document-handling" job attribute (section
+ 4.2.4) and the relationship of this attribute and the other
+ attributes that control document processing is described in section
+ 15.3.
+
+4.2.9 number-up (integer(1:MAX))
+
+ This attribute specifies the number of print-stream pages to impose
+ upon a single side of an instance of a selected medium. For example,
+ if the value is:
+
+
+
+
+
+deBry, et al. Experimental [Page 80]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ Value Description
+
+ '1' the Printer MUST place one print-stream page on a single
+ side of an instance of the selected medium (MAY add
+ some sort of translation, scaling, or rotation).
+ '2' the Printer MUST place two print-stream pages on a single
+ side of an instance of the selected medium (MAY add
+ some sort of translation, scaling, or rotation).
+ '4' the Printer MUST place four print-stream pages on a single
+ side of an instance of the selected medium (MAY add
+ some sort of translation, scaling, or rotation).
+
+ This attribute primarily controls the translation, scaling and
+ rotation of print-stream pages.
+
+ Note: The effect of this attribute on jobs with multiple documents is
+ controlled by the "multiple-document-handling" job attribute (section
+ 4.2.4) and the relationship of this attribute and the other
+ attributes that control document processing is described in section
+ 15.3.
+
+4.2.10 orientation-requested (type2 enum)
+
+ This attribute indicates the desired orientation for printed print-
+ stream pages; it does not describe the orientation of the client-
+ supplied print-stream pages.
+
+ For some document formats (such as 'application/postscript'), the
+ desired orientation of the print-stream pages is specified within the
+ document data. This information is generated by a device driver
+ prior to the submission of the print job. Other document formats
+ (such as 'text/plain') do not include the notion of desired
+ orientation within the document data. In the latter case it is
+ possible for the Printer object to bind the desired orientation to
+ the document data after it has been submitted. It is expected that a
+ Printer object would only support "orientations-requested" for some
+ document formats (e.g., 'text/plain' or 'text/html') but not others
+ (e.g., 'application/postscript'). This is no different than any
+ other Job Template attribute since section 4.2, item 1, points out
+ that a Printer object may support or not support any Job Template
+ attribute based on the document format supplied by the client.
+ However, a special mention is made here since it is very likely that
+ a Printer object will support "orientation-requested" for only a
+ subset of the supported document formats.
+
+
+
+
+
+
+
+deBry, et al. Experimental [Page 81]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ Standard enum values are:
+
+ Value Symbolic Name and Description
+
+ '3' 'portrait': The content will be imaged across the short
+ edge of the medium.
+ '4' 'landscape': The content will be imaged across the long
+ edge of the medium. Landscape is defined to be a
+ rotation of the print-stream page to be imaged by +90
+ degrees with respect to the medium (i.e. anti-
+ clockwise) from the portrait orientation. Note: The
+ +90 direction was chosen because simple finishing on
+ the long edge is the same edge whether portrait or
+ landscape
+ '5' 'reverse-landscape': The content will be imaged across the
+ long edge of the medium. Reverse-landscape is defined
+ to be a rotation of the print-stream page to be imaged
+ by - 90 degrees with respect to the medium (i.e.
+ clockwise) from the portrait orientation. Note: The '
+ reverse-landscape' value was added because some
+ applications rotate landscape -90 degrees from
+ portrait, rather than +90 degrees.
+ '6' 'reverse-portrait': The content will be imaged across the
+ short edge of the medium. Reverse-portrait is defined
+ to be a rotation of the print-stream page to be imaged
+ by 180 degrees with respect to the medium from the
+ portrait orientation. Note: The 'reverse-portrait'
+ value was added for use with the "finishings"
+ attribute in cases where the opposite edge is desired
+ for finishing a portrait document on simple finishing
+ devices that have only one finishing position. Thus a
+ 'text'/plain' portrait document can be stapled "on the
+ right" by a simple finishing device as is common use
+ with some middle eastern languages such as Hebrew.
+
+ Note: The effect of this attribute on jobs with multiple documents is
+ controlled by the "multiple-document-handling" job attribute (section
+ 4.2.4) and the relationship of this attribute and the other
+ attributes that control document processing is described in section
+ 15.3.
+
+4.2.11 media (type3 keyword | name(MAX))
+
+ This attribute identifies the medium that the Printer uses for all
+ impressions of the Job.
+
+ The values for "media" include medium-names, medium-sizes, input-
+ trays and electronic forms so that one attribute specifies the media.
+
+
+
+deBry, et al. Experimental [Page 82]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ If a Printer object supports a medium name as a value of this
+ attribute, such a medium name implicitly selects an input-tray that
+ contains the specified medium. If a Printer object supports a medium
+ size as a value of this attribute, such a medium size implicitly
+ selects a medium name that in turn implicitly selects an input-tray
+ that contains the medium with the specified size. If a Printer
+ object supports an input-tray as the value of this attribute, such an
+ input-tray implicitly selects the medium that is in that input-tray
+ at the time the job prints. This case includes manual-feed input-
+ trays. If a Printer object supports an electronic form as the value
+ of this attribute, such an electronic form implicitly selects a
+ medium-name that in turn implicitly selects an input-tray that
+ contains the medium specified by the electronic form. The electronic
+ form also implicitly selects an image that the Printer MUST merge
+ with the document data as its prints each page.
+
+ Standard keyword values are (taken from ISO DPA and the Printer MIB)
+ and are listed in section 14. An administrator MAY define additional
+ values using the 'name' or 'keyword' attribute syntax, depending on
+ implementation.
+
+ There is also an additional Printer attribute named "media-ready"
+ which differs from "media-supported" in that legal values only
+ include the subset of "media-supported" values that are physically
+ loaded and ready for printing with no operator intervention required.
+ If an IPP object supports "media-supported", it NEED NOT support
+ "media-ready".
+
+ The relationship of this attribute and the other attributes that
+ control document processing is described in section 15.3.
+
+4.2.12 printer-resolution (resolution)
+
+ This attribute identifies the resolution that Printer uses for the
+ Job.
+
+4.2.13 print-quality (type2 enum)
+
+ This attribute specifies the print quality that the Printer uses for
+ the Job.
+
+ The standard enum values are:
+
+ Value Symbolic Name and Description
+
+ '3' 'draft': lowest quality available on the printer
+ '4' 'normal': normal or intermediate quality on the printer
+ '5' 'high': highest quality available on the printer
+
+
+
+deBry, et al. Experimental [Page 83]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+4.3 Job Description Attributes
+
+ The attributes in this section form the attribute group called "job-
+ description". The following table summarizes these attributes. The
+ third column indicates whether the attribute is a REQUIRED attribute
+ that MUST be supported by Printer objects. If it is not indicated as
+ REQUIRED, then it is OPTIONAL. The maximum size in octets for 'text'
+ and 'name' attributes is indicated in parenthesizes.
+
+ +----------------------------+----------------------+----------------+
+ | Attribute | Syntax | REQUIRED? |
+ +----------------------------+----------------------+----------------+
+ | job-uri | uri | REQUIRED |
+ +----------------------------+----------------------+----------------+
+ | job-id | integer(1:MAX) | REQUIRED |
+ +----------------------------+----------------------+----------------+
+ | job-printer-uri | uri | REQUIRED |
+ +----------------------------+----------------------+----------------+
+ | job-more-info | uri | |
+ +----------------------------+----------------------+----------------+
+ | job-name | name (MAX) | REQUIRED |
+ +----------------------------+----------------------+----------------+
+ | job-originating-user-name | name (MAX) | REQUIRED |
+ +----------------------------+----------------------+----------------+
+ | job-state | type1 enum | REQUIRED |
+ +----------------------------+----------------------+----------------+
+ | job-state-reasons | 1setOf type2 keyword | |
+ +----------------------------+----------------------+----------------+
+ | job-state-message | text (MAX) | |
+ +----------------------------+----------------------+----------------+
+ | number-of-documents | integer (0:MAX) | |
+ +----------------------------+----------------------+----------------+
+ | output-device-assigned | name (127) | |
+ +----------------------------+----------------------+----------------+
+ | time-at-creation | integer (0:MAX) | |
+ +----------------------------+----------------------+----------------+
+ | time-at-processing | integer (0:MAX) | |
+ +----------------------------+----------------------+----------------+
+ | time-at-completed | integer (0:MAX) | |
+ +----------------------------+----------------------+----------------+
+ | number-of-intervening-jobs | integer (0:MAX) | |
+ +----------------------------+----------------------+----------------+
+ | job-message-from-operator | text (127) | |
+ +----------------------------+----------------------+----------------+
+ | job-k-octets | integer (0:MAX) | |
+ +----------------------------+----------------------+----------------+
+ | job-impressions | integer (0:MAX) | |
+ +----------------------------+----------------------+----------------+
+
+
+
+deBry, et al. Experimental [Page 84]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ +----------------------------+----------------------+----------------+
+ | Attribute | Syntax | REQUIRED? |
+ +----------------------------+----------------------+----------------+
+ | job-media-sheets | integer (0:MAX) | |
+ +----------------------------+----------------------+----------------+
+ | job-k-octets-processed | integer (0:MAX) | |
+ +----------------------------+----------------------+----------------+
+ | job-impressions-completed | integer (0:MAX) | |
+ +----------------------------+----------------------+----------------+
+ | job-media-sheets-completed | integer (0:MAX) | |
+ +----------------------------+----------------------+----------------+
+ | attributes-charset | charset | REQUIRED |
+ +----------------------------+----------------------+----------------+
+ | attributes-natural-language| naturalLanguage | REQUIRED |
+ +----------------------------+----------------------+----------------+
+
+
+4.3.1 job-uri (uri)
+
+ This REQUIRED attribute contains the URI for the job. The Printer
+ object, on receipt of a new job, generates a URI which identifies the
+ new Job. The Printer object returns the value of the "job-uri"
+ attribute as part of the response to a create request. The precise
+ format of a Job URI is implementation dependent. If the Printer
+ object supports more than one URI and there is some relationship
+ between the newly formed Job URI and the Printer object's URI, the
+ Printer object uses the Printer URI supplied by the client in the
+ create request. For example, if the create request comes in over a
+ secure channel, the new Job URI MUST use the same secure channel.
+ This can be guaranteed because the Printer object is responsible for
+ generating the Job URI and the Printer object is aware of its
+ security configuration and policy as well as the Printer URI used in
+ the create request.
+
+ For a description of this attribute and its relationship to "job-id"
+ and "job-printer-uri" attribute, see the discussion in section 2.4 on
+ "Object Identity".
+
+4.3.2 job-id (integer(1:MAX))
+
+ This REQUIRED attribute contains the ID of the job. The Printer, on
+ receipt of a new job, generates an ID which identifies the new Job on
+ that Printer. The Printer returns the value of the "job-id"
+ attribute as part of the response to a create request. The 0 value
+ is not included to allow for compatibility with SNMP index values
+ which also cannot be 0.
+
+
+
+
+
+deBry, et al. Experimental [Page 85]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ For a description of this attribute and its relationship to "job-uri"
+ and "job-printer-uri" attribute, see the discussion in section 2.4 on
+ "Object Identity".
+
+4.3.3 job-printer-uri (uri)
+
+ This REQUIRED attribute identifies the Printer object that created
+ this Job object. When a Printer object creates a Job object, it
+ populates this attribute with the Printer object URI that was used in
+ the create request. This attribute permits a client to identify the
+ Printer object that created this Job object when only the Job
+ object's URI is available to the client. The client queries the
+ creating Printer object to determine which languages, charsets,
+ operations, are supported for this Job.
+
+ For a description of this attribute and its relationship to "job-uri"
+ and "job-id" attribute, see the discussion in section 2.4 on "Object
+ Identity".
+
+4.3.4 job-more-info (uri)
+
+ Similar to "printer-more-info", this attribute contains the URI
+ referencing some resource with more information about this Job
+ object, perhaps an HTML page containing information about the Job.
+
+4.3.5 job-name (name(MAX))
+
+ This REQUIRED attribute is the name of the job. It is a name that is
+ more user friendly than the "job-uri" attribute value. It does not
+ need to be unique between Jobs. The Job's "job-name" attribute is
+ set to the value supplied by the client in the "job-name" operation
+ attribute in the create request (see Section 3.2.1.1). If, however,
+ the "job-name" operation attribute is not supplied by the client in
+ the create request, the Printer object, on creation of the Job, MUST
+ generate a name. The printer SHOULD generate the value of the Job's
+ "job-name" attribute from the first of the following sources that
+ produces a value: 1) the "document-name" operation attribute of the
+ first (or only) document, 2) the "document-URI" attribute of the
+ first (or only) document, or 3) any other piece of Job specific
+ and/or Document Content information.
+
+4.3.6 job-originating-user-name (name(MAX))
+
+ This REQUIRED attribute contains the name of the end user that
+ submitted the print job. The Printer object sets this attribute to
+ the most authenticated printable name that it can obtain from the
+ authentication service over which the IPP operation was received.
+
+
+
+
+deBry, et al. Experimental [Page 86]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ Only if such is not available, does the Printer object use the value
+ supplied by the client in the "requesting-user-name" operation
+ attribute of the create operation (see Section 8).
+
+ Note: The Printer object needs to keep an internal originating user
+ id of some form, typically as a credential of a principal, with the
+ Job object. Since such an internal attribute is implementation-
+ dependent and not of interest to clients, it is not specified as a
+ Job Description attribute. This originating user id is used for
+ authorization checks (if any) on all subsequent operation.
+
+4.3.7 job-state (type1 enum)
+
+ This REQUIRED attribute identifies the current state of the job.
+ Even though the IPP protocol defines eight values for job states,
+ implementations only need to support those states which are
+ appropriate for the particular implementation. In other words, a
+ Printer supports only those job states implemented by the output
+ device and available to the Printer object implementation.
+
+ Standard enum values are:
+
+ Values Symbolic Name and Description
+
+ '3' 'pending': The job is a candidate to start processing, but
+ is not yet processing.
+
+ '4' 'pending-held': The job is not a candidate for processing
+ for any number of reasons but will return to the '
+ pending' state as soon as the reasons are no longer
+ present. The job's "job-state-reason" attribute MUST
+ indicate why the job is no longer a candidate for
+ processing.
+
+ '5' 'processing': One or more of:
+
+ 1. the job is using, or is attempting to use, one or
+ more purely software processes that are analyzing,
+ creating, or interpreting a PDL, etc.,
+ 2. the job is using, or is attempting to use, one or
+ more hardware devices that are interpreting a PDL,
+ making marks on a medium, and/or performing finishing,
+ such as stapling, etc.,
+ 3. the Printer object has made the job ready for
+ printing, but the output device is not yet printing
+ it, either because the job hasn't reached the output
+
+
+
+
+
+deBry, et al. Experimental [Page 87]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ device or because the job is queued in the output
+ device or some other spooler, awaiting the output
+ device to print it.
+
+ When the job is in the 'processing' state, the entire
+ job state includes the detailed status represented in
+ the printer's "printer-state", "printer-state-
+ reasons", and "printer-state-message" attributes.
+
+ Implementations MAY, though they NEED NOT, include
+ additional values in the job's "job-state-reasons"
+ attribute to indicate the progress of the job, such as
+ adding the 'job-printing' value to indicate when the
+ output device is actually making marks on paper and/or
+ the 'processing-to-stop-point' value to indicate that
+ the IPP object is in the process of canceling or
+ aborting the job. Most implementations won't bother
+ with this nuance.
+
+ '6' 'processing-stopped': The job has stopped while processing
+ for any number of reasons and will return to the '
+ processing' state as soon as the reasons are no longer
+ present.
+
+ The job's "job-state-reason" attribute MAY indicate
+ why the job has stopped processing. For example, if
+ the output device is stopped, the 'printer-stopped'
+ value MAY be included in the job's "job-state-reasons"
+ attribute.
+
+ Note: When an output device is stopped, the device
+ usually indicates its condition in human readable form
+ locally at the device. A client can obtain more
+ complete device status remotely by querying the
+ Printer object's "printer-state", "printer-state-
+ reasons" and "printer-state-message" attributes.
+
+ '7' 'canceled': The job has been canceled by a Cancel-Job
+ operation and the Printer object has completed
+ canceling the job and all job status attributes have
+ reached their final values for the job. While the
+ Printer object is canceling the job, the job remains
+ in its current state, but the job's "job-state-
+ reasons" attribute SHOULD contain the 'processing-to-
+ stop-point' value and one of the 'canceled-by-user', '
+ canceled-by-operator', or 'canceled-at-device' value.
+
+
+
+
+
+deBry, et al. Experimental [Page 88]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ When the job moves to the 'canceled' state, the '
+ processing-to-stop-point' value, if present, MUST be
+ removed, but the 'canceled-by-xxx', if present, MUST
+ remain.
+
+ '8' 'aborted': The job has been aborted by the system, usually
+ while the job was in the 'processing' or 'processing-
+ stopped' state and the Printer has completed aborting
+ the job and all job status attributes have reached
+ their final values for the job. While the Printer
+ object is aborting the job, the job remains in its
+ current state, but the job's "job-state-reasons"
+ attribute SHOULD contain the 'processing-to-stop-
+ point' and 'aborted-by-system' values. When the job
+ moves to the 'aborted' state, the 'processing-to-
+ stop-point' value, if present, MUST be removed, but
+ the 'aborted-by-system' value, if present, MUST
+ remain.
+
+ '9' 'completed': The job has completed successfully or with
+ warnings or errors after processing and all of the job
+ media sheets have been successfully stacked in the
+ appropriate output bin(s) and all job status
+ attributes have reached their final values for the
+ job. The job's "job-state-reasons" attribute SHOULD
+ contain one of: 'completed-successfully', '
+ completed-with-warnings', or 'completed-with-errors'
+ values.
+
+ The final value for this attribute MUST be one of: 'completed', '
+ canceled', or 'aborted' before the Printer removes the job
+ altogether. The length of time that jobs remain in the 'canceled', '
+ aborted', and 'completed' states depends on implementation.
+
+ The following figure shows the normal job state transitions.
+
+ +----> canceled
+ /
+ +----> pending --------> processing ---------+------> completed
+ | ^ ^ \
+ --->+ | | +----> aborted
+ | v v /
+ +----> pending-held processing-stopped ---+
+
+ Normally a job progresses from left to right. Other state
+ transitions are unlikely, but are not forbidden. Not shown are the
+ transitions to the 'canceled' state from the 'pending', 'pending-
+ held', and 'processing-stopped' states.
+
+
+
+deBry, et al. Experimental [Page 89]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ Jobs reach one of the three terminal states: 'completed', 'canceled',
+ or 'aborted', after the jobs have completed all activity, including
+ stacking output media, after the jobs have completed all activity,
+ and all job status attributes have reached their final values for the
+ job.
+
+ Note: As with all other IPP attributes, if the implementation can not
+ determine the correct value for this attribute, it SHOULD respond
+ with the out-of-band value 'unknown' (see section 4.1) rather than
+ try to guess at some possibly incorrect value and give the end user
+ the wrong impression about the state of the Job object. For example,
+ if the implementation is just a gateway into some printing system
+ that does not provide detailed status about the print job, the IPP
+ Job object's state might literally be 'unknown'.
+
+4.3.8 job-state-reasons (1setOf type2 keyword)
+
+ This attribute provides additional information about the job's
+ current state, i.e., information that augments the value of the job's
+ "job-state" attribute.
+
+ Implementation of these values is OPTIONAL, i.e., a Printer NEED NOT
+ implement them, even if (1) the output device supports the
+ functionality represented by the reason and (2) is available to the
+ Printer object implementation. These values MAY be used with any job
+ state or states for which the reason makes sense. Furthermore, when
+ implemented, the Printer MUST return these values when the reason
+ applies and MUST NOT return them when the reason no longer applies
+ whether the value of the Job's "job-state" attribute changed or not.
+ When the Job does not have any reasons for being in its current
+ state, the value of the Job's "job-state-reasons" attribute MUST be '
+ none'.
+
+ Note: While values cannot be added to the 'job-state' attribute
+ without impacting deployed clients that take actions upon receiving
+ "job-state" values, it is the intent that additional "job-state-
+ reasons" values can be defined and registered without impacting such
+ deployed clients. In other words, the "job-state-reasons" attribute
+ is intended to be extensible.
+
+ The following standard keyword values are defined. For ease of
+ understanding, the values are presented in the order in which the
+ reasons are likely to occur (if implemented), starting with the '
+ job-incoming' value:
+
+ 'none': There are no reasons for the job's current state.
+ 'job-incoming': The Create-Job operation has been accepted by the
+ Printer, but the Printer is expecting additional Send-Document
+
+
+
+deBry, et al. Experimental [Page 90]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ and/or Send-URI operations and/or is accessing/accepting
+ document data.
+ 'submission-interrupted': The job was not completely submitted for
+ some unforeseen reason, such as: (1) the Printer has crashed
+ before the job was closed by the client, (2) the Printer or the
+ document transfer method has crashed in some non-recoverable way
+ before the document data was entirely transferred to the
+ Printer, (3) the client crashed or failed to close the job
+ before the time-out period. See section 4.4.28.
+ 'job-outgoing': The Printer is transmitting the job to the output
+ device.
+ 'job-hold-until-specified': The value of the job's "job-hold-
+ until" attribute was specified with a time period that is still
+ in the future. The job MUST NOT be a candidate for processing
+ until this reason is removed and there are no other reasons to
+ hold the job.
+ 'resources-are-not-ready': At least one of the resources needed by
+ the job, such as media, fonts, resource objects, etc., is not
+ ready on any of the physical printer's for which the job is a
+ candidate. This condition MAY be detected when the job is
+ accepted, or subsequently while the job is pending or
+ processing, depending on implementation. The job may remain in
+ its current state or be moved to the 'pending-held' state,
+ depending on implementation and/or job scheduling policy.
+ 'printer-stopped-partly': The value of the Printer's "printer-
+ state-reasons" attribute contains the value 'stopped-partly'.
+ 'printer-stopped': The value of the Printer's "printer-state"
+ attribute is 'stopped'.
+ 'job-interpreting': Job is in the 'processing' state, but more
+ specifically, the Printer is interpreting the document data.
+ 'job-queued': Job is in the 'processing' state, but more
+ specifically, the Printer has queued the document data.
+ 'job-transforming': Job is in the 'processing' state, but more
+ specifically, the Printer is interpreting document data and
+ producing another electronic representation.
+ 'job-printing': The output device is marking media. This value is
+ useful for Printers which spend a great deal of time processing
+ (1) when no marking is happening and then want to show that
+ marking is now happening or (2) when the job is in the process
+ of being canceled or aborted while the job remains in the '
+ processing' state, but the marking has not yet stopped so that
+ impression or sheet counts are still increasing for the job.
+ 'job-canceled-by-user': The job was canceled by the owner of the
+ job using the Cancel-Job request, i.e., by a user whose
+ authenticated identity is the same as the value of the
+ originating user that created the Job object, or by some other
+ authorized end-user, such as a member of the job owner's
+ security group.
+
+
+
+deBry, et al. Experimental [Page 91]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ 'job-canceled-by-operator': The job was canceled by the operator
+ using the Cancel-Job request, i.e., by a user who has been
+ authenticated as having operator privileges (whether local or
+ remote). If the security policy is to allow anyone to cancel
+ anyone's job, then this value may be used when the job is
+ canceled by other than the owner of the job. For such a
+ security policy, in effect, everyone is an operator as far as
+ canceling jobs with IPP is concerned.
+ 'job-canceled-at-device': The job was canceled by an unidentified
+ local user, i.e., a user at a console at the device.
+ 'aborted-by-system': The job (1) is in the process of being
+ aborted, (2) has been aborted by the system and placed in the '
+ aborted' state, or (3) has been aborted by the system and placed
+ in the 'pending-held' state, so that a user or operator can
+ manually try the job again.
+ 'processing-to-stop-point': The requester has issued a Cancel-Job
+ operation or the Printer object has aborted the job, but is
+ still performing some actions on the job until a specified stop
+ point occurs or job termination/cleanup is completed.
+
+ This reason is recommended to be used in conjunction with the '
+ processing' job state to indicate that the Printer object is
+ still performing some actions on the job while the job remains
+ in the 'processing' state. After all the job's job description
+ attributes have stopped incrementing, the Printer object moves
+ the job from the 'processing' state to the 'canceled' or '
+ aborted' job states.
+
+ 'service-off-line': The Printer is off-line and accepting no jobs.
+ All 'pending' jobs are put into the 'pending-held' state. This
+ situation could be true if the service's or document transform's
+ input is impaired or broken.
+ 'job-completed-successfully': The job completed successfully.
+ 'job-completed-with-warnings': The job completed with warnings.
+ 'job-completed-with-errors': The job completed with errors (and
+ possibly warnings too).
+
+4.3.9 job-state-message (text(MAX))
+
+ This attribute specifies information about the "job-state" and "job-
+ state-reasons" attributes in human readable text. If the Printer
+ object supports this attribute, the Printer object MUST be able to
+ generate this message in any of the natural languages identified by
+ the Printer's "generated-natural-language-supported" attribute (see
+ the "attributes-natural-language" operation attribute specified in
+ Section 3.1.4.1).
+
+
+
+
+
+deBry, et al. Experimental [Page 92]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ Note: the value SHOULD NOT contain additional information not
+ contained in the values of the "job-state" and "job-states-reasons"
+ attributes, such as interpreter error information. Otherwise,
+ application programs might attempt to parse the (localized text).
+ For such additional information such as interpreter errors for
+ application program consumption, a new attribute with keyword values,
+ needs to be developed and registered.
+
+4.3.10 number-of-documents (integer(0:MAX))
+
+ This attribute indicates the number of documents in the job, i.e.,
+ the number of Send-Document, Send-URI, Print-Job, or Print-URI
+ operations that the Printer has accepted for this job, regardless of
+ whether the document data has reached the Printer object or not.
+
+ Implementations supporting the OPTIONAL Create-Job/Send-
+ Document/Send-URI operations SHOULD support this attribute so that
+ clients can query the number of documents in each job.
+
+4.3.11 output-device-assigned (name(127))
+
+ This attribute identifies the output device to which the Printer
+ object has assigned this job. If an output device implements an
+ embedded Printer object, the Printer object NEED NOT set this
+ attribute. If a print server implements a Printer object, the value
+ MAY be empty (zero-length string) or not returned until the Printer
+ object assigns an output device to the job. This attribute is
+ particularly useful when a single Printer object support multiple
+ devices (so called "fan-out").
+
+4.3.12 time-at-creation (integer(0:MAX))
+
+ This attribute indicates the point in time at which the Job object
+ was created. In order to populate this attribute, the Printer object
+ uses the value in its "printer-up-time" attribute at the time the Job
+ object is created.
+
+4.3.13 time-at-processing (integer(0:MAX))
+
+ This attribute indicates the point in time at which the Job object
+ began processing. In order to populate this attribute, the Printer
+ object uses the value in its "printer-up-time" attribute at the time
+ the Job object is moved into the 'processing' state for the first
+ time.
+
+
+
+
+
+
+
+deBry, et al. Experimental [Page 93]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+4.3.14 time-at-completed (integer(0:MAX))
+
+ This attribute indicates the point in time at which the Job object
+ completed (or was cancelled or aborted). In order to populate this
+ attribute, the Printer object uses the value in its "printer-up-time"
+ attribute at the time the Job object is moved into the 'completed' or
+ 'canceled' or 'aborted' state.
+
+4.3.15 number-of-intervening-jobs (integer(0:MAX))
+
+ This attribute indicates the number of jobs that are "ahead" of this
+ job in the relative chronological order of expected time to complete
+ (i.e., the current scheduled order). For efficiency, it is only
+ necessary to calculate this value when an operation is performed that
+ requests this attribute.
+
+4.3.16 job-message-from-operator (text(127))
+
+ This attribute provides a message from an operator, system
+ administrator or "intelligent" process to indicate to the end user
+ the reasons for modification or other management action taken on a
+ job.
+
+4.3.17 job-k-octets (integer(0:MAX))
+
+ This attribute specifies the total size of the document(s) in K
+ octets, i.e., in units of 1024 octets requested to be processed in
+ the job. The value MUST be rounded up, so that a job between 1 and
+ 1024 octets MUST be indicated as being 1, 1025 to 2048 MUST be 2,
+ etc.
+
+ This value MUST NOT include the multiplicative factors contributed by
+ the number of copies specified by the "copies" attribute, independent
+ of whether the device can process multiple copies without making
+ multiple passes over the job or document data and independent of
+ whether the output is collated or not. Thus the value is independent
+ of the implementation and indicates the size of the document(s)
+ measured in K octets independent of the number of copies.
+
+ This value MUST also not include the multiplicative factor due to a
+ copies instruction embedded in the document data. If the document
+ data actually includes replications of the document data, this value
+ will include such replication. In other words, this value is always
+ the size of the source document data, rather than a measure of the
+ hardcopy output to be produced.
+
+
+
+
+
+
+deBry, et al. Experimental [Page 94]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ Note: This attribute and the following two attributes ("job-
+ impressions" and "job-media-sheets") are not intended to be counters;
+ they are intended to be useful routing and scheduling information if
+ known. For these three attributes, the Printer object may try to
+ compute the value if it is not supplied in the create request. Even
+ if the client does supply a value for these three attributes in the
+ create request, the Printer object MAY choose to change the value if
+ the Printer object is able to compute a value which is more accurate
+ than the client supplied value. The Printer object may be able to
+ determine the correct value for these three attributes either right
+ at job submission time or at any later point in time.
+
+4.3.18 job-impressions (integer(0:MAX))
+
+ This attribute specifies the total size in number of impressions of
+ the document(s) being submitted (see the definition of impression in
+ section 13.2.5).
+
+ As with "job-k-octets", this value MUST NOT include the
+ multiplicative factors contributed by the number of copies specified
+ by the "copies" attribute, independent of whether the device can
+ process multiple copies without making multiple passes over the job
+ or document data and independent of whether the output is collated or
+ not. Thus the value is independent of the implementation and
+ reflects the size of the document(s) measured in impressions
+ independent of the number of copies.
+
+ As with "job-k-octets", this value MUST also not include the
+ multiplicative factor due to a copies instruction embedded in the
+ document data. If the document data actually includes replications
+ of the document data, this value will include such replication. In
+ other words, this value is always the number of impressions in the
+ source document data, rather than a measure of the number of
+ impressions to be produced by the job.
+
+ See the Note in the "job-k-octets" attribute that also applies to
+ this attribute.
+
+4.3.19 job-media-sheets (integer(0:MAX))
+
+ This attribute specifies the total number of media sheets to be
+ produced for this job.
+
+ Unlike the "job-k-octets" and the "job-impressions" attributes, this
+ value MUST include the multiplicative factors contributed by the
+ number of copies specified by the "copies" attribute and a 'number of
+ copies' instruction embedded in the document data, if any. This
+ difference allows the system administrator to control the lower and
+
+
+
+deBry, et al. Experimental [Page 95]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ upper bounds of both (1) the size of the document(s) with "job-k-
+ octets-supported" and "job-impressions-supported" and (2) the size of
+ the job with "job-media-sheets-supported".
+
+ See the Note in the "job-k-octets" attribute that also applies to
+ this attribute.
+
+4.3.20 job-k-octets-processed (integer(0:MAX))
+
+ This attribute specifies the total number of octets processed in K
+ octets, i.e., in units of 1024 octets so far. The value MUST be
+ rounded up, so that a job between 1 and 1024 octets inclusive MUST be
+ indicated as being 1, 1025 to 2048 inclusive MUST be 2, etc.
+
+ For implementations where multiple copies are produced by the
+ interpreter with only a single pass over the data, the final value
+ MUST be equal to the value of the "job-k-octets" attribute. For
+ implementations where multiple copies are produced by the interpreter
+ by processing the data for each copy, the final value MUST be a
+ multiple of the value of the "job-k-octets" attribute.
+
+ Note: This attribute and the following two attributes ("job-
+ impressions-completed" and "job-sheets-completed") are intended to be
+ counters. That is, the value for a job that has not started
+ processing MUST be 0. When the job's "job-state" is 'processing' or
+ 'processing-stopped', this value is intended to contain the amount of
+ the job that has been processed to the time at which the attributes
+ are requested.
+
+4.3.21 job-impressions-completed (integer(0:MAX))
+
+ This job attribute specifies the number of impressions completed for
+ the job so far. For printing devices, the impressions completed
+ includes interpreting, marking, and stacking the output.
+
+ See the note in "job-k-octets-processed" which also applies to this
+ attribute.
+
+4.3.22 job-media-sheets-completed (integer(0:MAX))
+
+ This job attribute specifies the media-sheets completed marking and
+ stacking for the entire job so far whether those sheets have been
+ processed on one side or on both.
+
+ See the note in "job-k-octets-processed" which also applies to this
+ attribute.
+
+
+
+
+
+deBry, et al. Experimental [Page 96]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+4.3.23 attributes-charset (charset)
+
+ This REQUIRED attribute is populated using the value in the client
+ supplied "attributes-charset" attribute in the create request. It
+ identifies the charset (coded character set and encoding method) used
+ by any Job attributes with attribute syntax 'text' and 'name' that
+ were supplied by the client in the create request. See Section 3.1.4
+ for a complete description of the "attributes-charset" operation
+ attribute.
+
+ This attribute does not indicate the charset in which the 'text' and
+ 'name' values are stored internally in the Job object. The internal
+ charset is implementation-defined. The IPP object MUST convert from
+ whatever the internal charset is to that being requested in an
+ operation as specified in Section 3.1.4.
+
+4.3.24 attributes-natural-language (naturalLanguage)
+
+ This REQUIRED attribute is populated using the value in the client
+ supplied "attributes-natural-language" attribute in the create
+ request. It identifies the natural language used for any Job
+ attributes with attribute syntax 'text' and 'name' that were supplied
+ by the client in the create request. See Section 3.1.4 for a
+ complete description of the "attributes-natural-language" operation
+ attribute. See Sections 4.1.1.2 and 4.1.2.2 for how a Natural
+ Language Override may be supplied explicitly for each 'text' and '
+ name' attribute value that differs from the value identified by the
+ "attributes-natural-language" attribute.
+
+4.4 Printer Description Attributes
+
+ These attributes form the attribute group called "printer-
+ description". The following table summarizes these attributes, their
+ syntax, and whether or not they are REQUIRED for a Printer object to
+ support. If they are not indicated as REQUIRED, they are OPTIONAL.
+ The maximum size in octets for 'text' and 'name' attributes is
+ indicated in parenthesizes.
+
+ Note: How these attributes are set by an Administrator is outside the
+ scope of this specification.
+
+
+
+
+
+
+
+
+
+
+
+deBry, et al. Experimental [Page 97]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ +----------------------------+----------------------+----------------+
+ | Attribute | Syntax | REQUIRED? |
+ +----------------------------+----------------------+----------------+
+ | printer-uri-supported | 1setOf uri | REQUIRED |
+ +----------------------------+----------------------+----------------+
+ | uri-security-supported | 1setOf type2 keyword | REQUIRED |
+ +----------------------------+----------------------+----------------+
+ | printer-name | name (127) | REQUIRED |
+ +----------------------------+----------------------+----------------+
+ | printer-location | text (127) | |
+ +----------------------------+----------------------+----------------+
+ | printer-info | text (127) | |
+ +----------------------------+----------------------+----------------+
+ | printer-more-info | uri | |
+ +----------------------------+----------------------+----------------+
+ | printer-driver-installer | uri | |
+ +----------------------------+----------------------+----------------+
+ | printer-make-and-model | text (127) | |
+ +----------------------------+----------------------+----------------+
+ | printer-more-info- | uri | |
+ | manufacturer | | |
+ +----------------------------+----------------------+----------------+
+ | printer-state | type1 enum | REQUIRED |
+ +----------------------------+----------------------+----------------+
+ | printer-state-reasons | 1setOf type2 keyword | |
+ +----------------------------+----------------------+----------------+
+ | printer-state-message | text (MAX) | |
+ +----------------------------+----------------------+----------------+
+ | operations-supported | 1setOf type2 enum | REQUIRED |
+ +----------------------------+----------------------+----------------+
+ | charset-configured | charset | REQUIRED |
+ +----------------------------+----------------------+----------------+
+ | charset-supported | 1setOf charset | REQUIRED |
+ +----------------------------+----------------------+----------------+
+ | natural-language-configured| naturalLanguage | REQUIRED |
+ +----------------------------+----------------------+----------------+
+ | generated-natural-language-| 1setOf | REQUIRED |
+ | supported | naturalLanguage | |
+ +----------------------------+----------------------+----------------+
+ | document-format-default | mimeMediaType | REQUIRED |
+ +----------------------------+----------------------+----------------+
+ | document-format- | 1setOf | REQUIRED |
+ | supported | mimeMediaType | |
+ +----------------------------+----------------------+----------------+
+ | printer-is-accepting-jobs | boolean | REQUIRED |
+ +----------------------------+----------------------+----------------+
+ | queued-job-count | integer (0:MAX) | RECOMMENDED |
+ +----------------------------+----------------------+----------------+
+
+
+
+deBry, et al. Experimental [Page 98]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ +----------------------------+----------------------+----------------+
+ | Attribute | Syntax | REQUIRED? |
+ +----------------------------+----------------------+----------------+
+ | printer-message-from- | text (127) | |
+ | operator | | |
+ +----------------------------+----------------------+----------------+
+ | color-supported | boolean | |
+ +----------------------------+----------------------+----------------+
+ | reference-uri-schemes- | 1setOf uriScheme | |
+ | supported | | |
+ +----------------------------+----------------------+----------------+
+ | pdl-override-supported | type2 keyword | REQUIRED |
+ +----------------------------+----------------------+----------------+
+ | printer-up-time | integer (1:MAX) | REQUIRED |
+ +----------------------------+----------------------+----------------+
+ | printer-current-time | dateTime | |
+ +----------------------------+----------------------+----------------+
+ | multiple-operation-time-out| integer (1:MAX) | |
+ +----------------------------+----------------------+----------------+
+ | compression-supported | 1setOf type3 keyword | |
+ +----------------------------+----------------------+----------------+
+ | job-k-octets-supported | rangeOfInteger | |
+ | | (0:MAX) | |
+ +----------------------------+----------------------+----------------+
+ | job-impressions-supported | rangeOfInteger | |
+ | | (0:MAX) | |
+ +----------------------------+----------------------+----------------+
+ | job-media-sheets-supported | rangeOfInteger | |
+ | | (0:MAX) | |
+ +----------------------------+----------------------+----------------+
+
+4.4.1 printer-uri-supported (1setOf uri)
+
+ This REQUIRED Printer attribute contains at least one URI for the
+ Printer object. It OPTIONALLY contains more than one URI for the
+ Printer object. An administrator determines a Printer object's
+ URI(s) and configures this attribute to contain those URIs by some
+ means outside the scope of IPP/1.0. The precise format of this URI
+ is implementation dependent and depends on the protocol. See the
+ next section for a description "uri-security-supported" which is the
+ REQUIRED companion attribute to this "printer-uri-supported"
+ attribute. See section 2.4 on Printer object identity and section
+ 8.2 on security and URIs for more information.
+
+
+
+
+
+
+
+
+deBry, et al. Experimental [Page 99]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+4.4.2 uri-security-supported (1setOf type2 keyword)
+
+ This REQUIRED Printer attribute MUST have the same cardinality
+ (contain the same number of values) as the "printer-uri-supported"
+ attribute. This attribute identifies the security mechanisms used
+ for each URI listed in the "printer-uri-supported" attribute. The "i
+ th" value in "uri-security-supported" corresponds to the "i th" value
+ in "printer-uri-supported" and it describes the security mechanisms
+ used for accessing the Printer object via that URI. The following
+ standard values are defined:
+
+ 'none': There are no secure communication channel protocols in use
+ for the given URI.
+
+ 'ssl3': SSL3 [SSL] is the secure communications channel protocol in
+ use for the given URI.
+
+ Consider the following example. For a single Printer object, an
+ administrator configures the "printer-uri-supported" and "uri-
+ security-supported" attributes as follows:
+
+ "printer-uri-supported": 'http://acme.com/open-use-printer', '
+ http://acme.com/restricted-use-printer', '
+ http://acme.com/private-printer'
+ "uri-security-supported": 'none', 'none', 'ssl3'
+
+ In this case, one Printer object has three URIs.
+
+ - For the first URI, 'http://acme.com/open-use-printer', the value
+ 'none' in "uri-security-supported" indicates that there is no
+ secure channel protocol configured to run under HTTP. The name
+ implies that there is no Basic or Digest authentication being
+ used, but it is up to the client to determine that while using
+ HTTP underneath the IPP application protocol.
+ - For the second URI, 'http://acme.com/restricted-use-printer', the
+ value 'none' in "uri-security-supported" indicates that there is
+ no secure channel protocol configured to run under HTTP. In
+ this case, although the name does imply that there is some sort
+ of Basic or Digest authentication being used within HTTP, it is
+ up to the client to determine that while using HTTP and by
+ processing any '401 Unauthorized' HTTP error messages.
+ - For the third URI, 'http://acme.com/private-printer', the value '
+ ssl3' in "uri-security-supported" indicates that SSL3 is being
+ used to secure the channel. The client SHOULD be prepared to
+ use SSL3 framing to negotiate an acceptable ciphersuite to use
+ while communicating with the Printer object. In this case, the
+ name implies the use of a secure communications channel, but the
+ fact is made explicit by the presence of the 'ssl3' value in
+
+
+
+deBry, et al. Experimental [Page 100]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ "uri-security-supported". The client does not need to resort to
+ understanding which security it must use by following naming
+ conventions or by parsing the URI to determine which security
+ mechanisms are implied.
+
+ It is expected that many IPP Printer objects will be configured to
+ support only one channel (either configured to use SSL3 access or
+ not), and will therefore only ever have one URI listed in the
+ "printer-uri-supported" attribute. No matter the configuration of
+ the Printer object (whether it has only one URI or more than one
+ URI), a client MUST supply only one URI in the target "printer-uri"
+ operation attribute.
+
+4.4.3 printer-name (name(127))
+
+ This REQUIRED Printer attribute contains the name of the Printer
+ object. It is a name that is more end-user friendly than a URI. An
+ administrator determines a printer's name and sets this attribute to
+ that name. This name may be the last part of the printer's URI or it
+ may be unrelated. In non-US-English locales, a name may contain
+ characters that are not allowed in a URI.
+
+4.4.4 printer-location (text(127))
+
+ This Printer attribute identifies the location of the device. This
+ could include things like: "in Room 123A, second floor of building
+ XYZ".
+
+4.4.5 printer-info (text(127))
+
+ This Printer attribute identifies the descriptive information about
+ this Printer object. This could include things like: "This printer
+ can be used for printing color transparencies for HR presentations",
+ or "Out of courtesy for others, please print only small (1-5 page)
+ jobs at this printer", or even "This printer is going away on July 1,
+ 1997, please find a new printer".
+
+4.4.6 printer-more-info (uri)
+
+ This Printer attribute contains a URI used to obtain more information
+ about this specific Printer object. For example, this could be an
+ HTTP type URI referencing an HTML page accessible to a Web Browser.
+ The information obtained from this URI is intended for end user
+ consumption. Features outside the scope of IPP can be accessed from
+ this URI. The information is intended to be specific to this printer
+ instance and site specific services (e.g. job pricing, services
+ offered, end user assistance). The device manufacturer may initially
+ populate this attribute.
+
+
+
+deBry, et al. Experimental [Page 101]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+4.4.7 printer-driver-installer (uri)
+
+ This Printer attribute contains a URI to use to locate the driver
+ installer for this Printer object. This attribute is intended for
+ consumption by automata. The mechanics of print driver installation
+ is outside the scope of IPP. The device manufacturer may initially
+ populate this attribute.
+
+4.4.8 printer-make-and-model (text(127))
+
+ This Printer attribute identifies the make and model of the device.
+ The device manufacturer may initially populate this attribute.
+
+4.4.9 printer-more-info-manufacturer (uri)
+
+ This Printer attribute contains a URI used to obtain more information
+ about this type of device. The information obtained from this URI is
+ intended for end user consumption. Features outside the scope of IPP
+ can be accessed from this URI (e.g., latest firmware, upgrades, print
+ drivers, optional features available, details on color support). The
+ information is intended to be germane to this printer without regard
+ to site specific modifications or services. The device manufacturer
+ may initially populate this attribute.
+
+4.4.10 printer-state (type1 enum)
+
+ This REQUIRED Printer attribute identifies the current state of the
+ device. The "printer-state reasons" attribute augments the
+ "printer-state" attribute to give more detailed information about the
+ Printer in the given printer state.
+
+ A Printer object need only update this attribute before responding to
+ an operation which requests the attribute; the Printer object NEED
+ NOT update this attribute continually, since asynchronous event
+ notification is not part of IPP/1.0. A Printer NEED NOT implement
+ all values if they are not applicable to a given implementation.
+
+ The following standard enum values are defined:
+
+ Value Symbolic Name and Description
+
+ '3' 'idle': If a Printer receives a job (whose required
+ resources are ready) while in this state, such a job
+ MUST transit into the 'processing' state immediately.
+ If the "printer-state-reasons" attribute contains any
+ reasons, they MUST be reasons that would not prevent a
+ job from transiting into the 'processing' state
+ immediately, e.g., 'toner-low'. Note: if a Printer
+
+
+
+deBry, et al. Experimental [Page 102]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ controls more than one output device, the above
+ definition implies that a Printer is 'idle' if at
+ least one output device is idle.
+
+ '4' 'processing': If a Printer receives a job (whose required
+ resources are ready) while in this state, such a job
+ MUST transit into the 'pending' state immediately.
+ Such a job MUST transit into the 'processing' state
+ only after jobs ahead of it complete. If the
+ "printer-state-reasons" attribute contains any
+ reasons, they MUST be reasons that do not prevent the
+ current job from printing, e.g. 'toner-low'. Note:
+ if a Printer controls more than one output device, the
+ above definition implies that a Printer is '
+ processing' if at least one output device is
+ processing, and none is idle.
+
+ '5' 'stopped': If a Printer receives a job (whose required
+ resources are ready) while in this state, such a job
+ MUST transit into the 'pending' state immediately.
+ Such a job MUST transit into the 'processing' state
+ only after some human fixes the problem that stopped
+ the printer and after jobs ahead of it complete
+ processing. If supported, the "printer-state-reasons"
+ attribute MUST contain at least one reason, e.g. '
+ media-jam', which prevents it from either processing
+ the current job or transitioning a 'pending' job to
+ the 'processing' state.
+
+ Note: if a Printer controls more than one output
+ device, the above definition implies that a Printer is
+ 'stopped' only if all output devices are stopped.
+ Also, it is tempting to define 'stopped' as when a
+ sufficient number of output devices are stopped and
+ leave it to an implementation to define the sufficient
+ number. But such a rule complicates the definition of
+ 'stopped' and 'processing'. For example, with this
+ alternate definition of 'stopped', a job can move from
+ 'pending' to 'processing' without human intervention,
+ even though the Printer is stopped.
+
+4.4.11 printer-state-reasons (1setOf type2 keyword)
+
+ This Printer attribute supplies additional detail about the device's
+ state.
+
+
+
+
+
+
+deBry, et al. Experimental [Page 103]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ Each keyword value MAY have a suffix to indicate its level of
+ severity. The three levels are: report (least severe), warning, and
+ error (most severe).
+
+ - '-report': This suffix indicates that the reason is a "report".
+ An implementation may choose to omit some or all reports. Some
+ reports specify finer granularity about the printer state;
+ others serve as a precursor to a warning. A report MUST contain
+ nothing that could affect the printed output.
+ - '-warning': This suffix indicates that the reason is a "warning".
+ An implementation may choose to omit some or all warnings.
+ Warnings serve as a precursor to an error. A warning MUST
+ contain nothing that prevents a job from completing, though in
+ some cases the output may be of lower quality.
+ - '-error': This suffix indicates that the reason is an "error".
+ An implementation MUST include all errors. If this attribute
+ contains one or more errors, printer MUST be in the stopped
+ state.
+
+ If the implementation does not add any one of the three suffixes, all
+ parties MUST assume that the reason is an "error".
+
+ If a Printer object controls more than one output device, each value
+ of this attribute MAY apply to one or more of the output devices. An
+ error on one output device that does not stop the Printer object as a
+ whole MAY appear as a warning in the Printer's "printer-state-reasons
+ attribute". If the "printer-state" for such a Printer has a value of
+ 'stopped', then there MUST be an error reason among the values in the
+ "printer-state-reasons" attribute.
+
+ The following standard keyword values are defined:
+
+ 'other': The device has detected an error other than one listed in
+ this document.
+ 'none': There are not reasons. This state reason is semantically
+ equivalent to "printer-state-reasons" without any value.
+ 'media-needed': A tray has run out of media.
+ 'media-jam': The device has a media jam.
+ 'paused': Someone has paused the Printer object. In this state, a
+ Printer MUST NOT produce printed output, but it MUST perform
+ other operations requested by a client. If a Printer had been
+ printing a job when the Printer was paused, the Printer MUST
+ resume printing that job when the Printer is no longer paused
+ and leave no evidence in the printed output of such a pause.
+ 'shutdown': Someone has removed a Printer object from service, and
+ the device may be powered down or physically removed. In this
+ state, a Printer object MUST NOT produce printed output, and
+ unless the Printer object is realized by a print server that is
+
+
+
+deBry, et al. Experimental [Page 104]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ still active, the Printer object MUST perform no other
+ operations requested by a client, including returning this
+ value. If a Printer object had been printing a job when it was
+ shutdown, the Printer NEED NOT resume printing that job when the
+ Printer is no longer shutdown. If the Printer resumes printing
+ such a job, it may leave evidence in the printed output of such
+ a shutdown, e.g. the part printed before the shutdown may be
+ printed a second time after the shutdown.
+ 'connecting-to-device': The Printer object has scheduled a job on
+ the output device and is in the process of connecting to a
+ shared network output device (and might not be able to actually
+ start printing the job for an arbitrarily long time depending on
+ the usage of the output device by other servers on the network).
+ 'timed-out': The server was able to connect to the output device
+ (or is always connected), but was unable to get a response from
+ the output device.
+ 'stopping': The Printer object is in the process of stopping the
+ device and will be stopped in a while. When the device is
+ stopped, the Printer object will change the Printer object's
+ state to 'stopped'. The 'stopping-warning' reason is never an
+ error, even for a Printer with a single output device. When an
+ output-device ceases accepting jobs, the Printer will have this
+ reason while the output device completes printing.
+ 'stopped-partly': When a Printer object controls more than one
+ output device, this reason indicates that one or more output
+ devices are stopped. If the reason is a report, fewer than half
+ of the output devices are stopped. If the reason is a warning,
+ fewer than all of the output devices are stopped.
+ 'toner-low': The device is low on toner.
+ 'toner-empty': The device is out of toner.
+ 'spool-area-full': The limit of persistent storage allocated for
+ spooling has been reached.
+ 'cover-open': One or more covers on the device are open.
+ 'interlock-open': One or more interlock devices on the printer are
+ unlocked.
+ 'door-open': One or more doors on the device are open.
+ 'input-tray-missing': One or more input trays are not in the
+ device.
+ 'media-low': At least one input tray is low on media.
+ 'media-empty': At least one input tray is empty.
+ 'output-tray-missing': One or more output trays are not in the
+ device
+ 'output-area-almost-full': One or more output area is almost full
+ (e.g. tray, stacker, collator).
+ 'output-area-full': One or more output area is full. (e.g. tray,
+ stacker, collator)
+ 'marker-supply-low': The device is low on at least one marker
+ supply. (e.g. toner, ink, ribbon)
+
+
+
+deBry, et al. Experimental [Page 105]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ 'marker-supply-empty: The device is out of at least one marker
+ supply. (e.g. toner, ink, ribbon)
+ 'marker-waste-almost-full': The device marker supply waste
+ receptacle is almost full.
+ 'marker-waste-full': The device marker supply waste receptacle is
+ full.
+ 'fuser-over-temp': The fuser temperature is above normal.
+ 'fuser-under-temp': The fuser temperature is below normal.
+ 'opc-near-eol': The optical photo conductor is near end of life.
+ 'opc-life-over': The optical photo conductor is no longer
+ functioning.
+ 'developer-low': The device is low on developer.
+ 'developer-empty: The device is out of developer.
+ 'interpreter-resource-unavailable': An interpreter resource is
+ unavailable (i.e. font, form)
+
+4.4.12 printer-state-message (text(MAX))
+
+ This Printer attribute specifies the additional information about the
+ printer state and printer state reasons in human readable text. If
+ the Printer object supports this attribute, the Printer object MUST
+ be able to generate this message in any of the natural languages
+ identified by the Printer's "generated-natural-language-supported"
+ attribute (see the "attributes-natural-language" operation attribute
+ specified in Section 3.1.4.1).
+
+4.4.13 operations-supported (1setOf type2 enum)
+
+ This REQUIRED Printer attribute specifies the set of supported
+ operations for this Printer object and contained Job objects. All
+ 32-bit enum values for this attribute MUST NOT exceed 0x8FFF, since
+ these values are passed in two octets in each Protocol request
+ [RFC2565].
+
+ The following standard enum and "operation-id" (see section 3.1.2)
+ values are defined:
+
+ Value Operation Name
+ ----------------- -------------------------------------
+
+ 0x0000 reserved, not used
+ 0x0001 reserved, not used
+ 0x0002 Print-Job
+ 0x0003 Print-URI
+ 0x0004 Validate-Job
+ 0x0005 Create-Job
+ 0x0006 Send-Document
+ 0x0007 Send-URI
+
+
+
+deBry, et al. Experimental [Page 106]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ 0x0008 Cancel-Job
+ 0x0009 Get-Job-Attributes
+ 0x000A Get-Jobs
+ 0x000B Get-Printer-Attributes
+ 0x000C-0x3FFF reserved for future operations
+ 0x4000-0x8FFF reserved for private extensions
+
+ This allows for certain vendors to implement private extensions that
+ are guaranteed to not conflict with future registered extensions.
+ However, there is no guarantee that two or more private extensions
+ will not conflict.
+
+4.4.14 charset-configured (charset)
+
+ This REQUIRED Printer attribute identifies the charset that the
+ Printer object has been configured to represent 'text' and 'name'
+ Printer attributes that are set by the operator, system
+ administrator, or manufacturer, i.e., for "printer-name" (name),
+ "printer-location" (text), "printer-info" (text), and "printer-make-
+ and-model" (text). Therefore, the value of the Printer object's
+ "charset-configured" attribute MUST also be among the values of the
+ Printer object's "charset-supported" attribute.
+
+4.4.15 charset-supported (1setOf charset)
+
+ This REQUIRED Printer attribute identifies the set of charsets that
+ the Printer and contained Job objects support in attributes with
+ attribute syntax 'text' and 'name'. At least the value 'utf-8' MUST
+ be present, since IPP objects MUST support the UTF-8 [RFC2279]
+ charset. If a Printer object supports a charset, it means that for
+ all attributes of syntax 'text' and 'name' the IPP object MUST (1)
+ accept the charset in requests and return the charset in responses as
+ needed.
+
+ If more charsets than UTF-8 are supported, the IPP object MUST
+ perform charset conversion between the charsets as described in
+ Section 3.2.1.2.
+
+4.4.16 natural-language-configured (naturalLanguage)
+
+ This REQUIRED Printer attribute identifies the natural language that
+ the Printer object has been configured to represent 'text' and 'name'
+ Printer attributes that are set by the operator, system
+ administrator, or manufacturer, i.e., for "printer-name" (name),
+ "printer-location" (text), "printer-info" (text), and "printer-make-
+ and-model" (text). When returning these Printer attributes, the
+ Printer object MAY return them in the configured natural language
+ specified by this attribute, instead of the natural language
+
+
+
+deBry, et al. Experimental [Page 107]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ requested by the client in the "attributes-natural-language"
+ operation attribute. See Section 3.1.4.1 for the specification of
+ the OPTIONAL multiple natural language support. Therefore, the value
+ of the Printer object's "natural-language-configured" attribute MUST
+ also be among the values of the Printer object's "generated-natural-
+ language-supported" attribute.
+
+4.4.17 generated-natural-language-supported (1setOf naturalLanguage)
+
+ This REQUIRED Printer attribute identifies the natural language(s)
+ that the Printer object and contained Job objects support in
+ attributes with attribute syntax 'text' and 'name'. The natural
+ language(s) supported depends on implementation and/or configuration.
+ Unlike charsets, IPP objects MUST accept requests with any natural
+ language or any Natural Language Override whether the natural
+ language is supported or not.
+
+ If a Printer object supports a natural language, it means that for
+ any of the attributes for which the Printer or Job object generates
+ messages, i.e., for the "job-state-message" and "printer-state-
+ message" attributes and Operation Messages (see Section 3.1.5) in
+ operation responses, the Printer and Job objects MUST be able to
+ generate messages in any of the Printer's supported natural
+ languages. See section 3.1.4 for the specification of 'text' and '
+ name' attributes in operation requests and responses.
+
+ Note: A Printer object that supports multiple natural languages,
+ often has separate catalogs of messages, one for each natural
+ language supported.
+
+4.4.18 document-format-default (mimeMediaType)
+
+ This REQUIRED Printer attribute identifies the document format that
+ the Printer object has been configured to assume if the client does
+ not supply a "document-format" operation attribute in any of the
+ operation requests that supply document data. The standard values
+ for this attribute are Internet Media types (sometimes called MIME
+ types). For further details see the description of the '
+ mimeMediaType' attribute syntax in Section 4.1.9.
+
+4.4.19 document-format-supported (1setOf mimeMediaType)
+
+ This REQUIRED Printer attribute identifies the set of document
+ formats that the Printer object and contained Job objects can
+ support. For further details see the description of the '
+ mimeMediaType' attribute syntax in Section 4.1.9.
+
+4.4.20 printer-is-accepting-jobs (boolean)
+
+
+
+deBry, et al. Experimental [Page 108]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ This REQUIRED Printer attribute indicates whether the printer is
+ currently able to accept jobs, i.e., is accepting Print-Job, Print-
+ URI, and Create-Job requests. If the value is 'true', the printer is
+ accepting jobs. If the value is 'false', the Printer object is
+ currently rejecting any jobs submitted to it. In this case, the
+ Printer object returns the 'server-error-not-accepting-jobs' status
+ code.
+
+ Note: This value is independent of the "printer-state" and "printer-
+ state-reasons" attributes because its value does not affect the
+ current job; rather it affects future jobs. This attribute may cause
+ the Printer to reject jobs when the "printer-state" is 'idle' or it
+ may cause the Printer object to accepts jobs when the "printer-state"
+ is 'stopped'.
+
+4.4.21 queued-job-count (integer(0:MAX))
+
+ This RECOMMENDED Printer attribute contains a count of the number of
+ jobs that are either 'pending', 'processing', 'pending-held', or '
+ processing-stopped' and is set by the Printer object.
+
+4.4.22 printer-message-from-operator (text(127))
+
+ This Printer attribute provides a message from an operator, system
+ administrator or "intelligent" process to indicate to the end user
+ information or status of the printer, such as why it is unavailable
+ or when it is expected to be available.
+
+4.4.23 color-supported (boolean)
+
+ This Printer attribute identifies whether the device is capable of
+ any type of color printing at all, including highlight color. All
+ document instructions having to do with color are embedded within the
+ document PDL (none are external IPP attributes in IPP/1.0).
+
+ Note: end-users are able to determine the nature and details of the
+ color support by querying the "printer-more-info-manufacturer"
+ Printer attribute.
+
+4.4.24 reference-uri-schemes-supported (1setOf uriScheme)
+
+ This Printer attribute specifies which URI schemes are supported for
+ use in the "document-uri" operation attribute of the Print-URI or
+ Send-URI operation. If a Printer object supports these optional
+ operations, it MUST support the "reference-uri-schemes-supported"
+ Printer attribute with at least the following schemed URI value:
+
+ 'ftp': The Printer object will use an FTP 'get' operation as
+
+
+
+deBry, et al. Experimental [Page 109]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ defined in RFC 2228 [RFC2228] using FTP URLs as defined by
+ [RFC2396] and[RFC2316].
+
+ The Printer object MAY OPTIONALLY support other URI schemes (see
+ section 4.1.6).
+
+4.4.25 pdl-override-supported (type2 keyword)
+
+ This REQUIRED Printer attribute expresses the ability for a
+ particular Printer implementation to either attempt to override
+ document data instructions with IPP attributes or not.
+
+ This attribute takes on the following values:
+
+ - 'attempted': This value indicates that the Printer object
+ attempts to make the IPP attribute values take precedence over
+ embedded instructions in the document data, however there is no
+ guarantee.
+
+ - 'not-attempted': This value indicates that the Printer object
+ makes no attempt to make the IPP attribute values take precedence
+ over embedded instructions in the document data.
+
+ Section 15 contains a full description of how this attribute
+ interacts with and affects other IPP attributes, especially the
+ "ipp-attribute-fidelity" attribute.
+
+4.4.26 printer-up-time (integer(1:MAX))
+
+ This REQUIRED Printer attribute indicates the amount of time (in
+ seconds) that this instance of this Printer implementation has been
+ up and running. This value is used to populate the Job attributes
+ "time-at-creation", "time-at-processing", and "time-at-completed".
+ These time values are all measured in seconds and all have meaning
+ only relative to this attribute, "printer-up-time". The value is a
+ monotonically increasing value starting from 1 when the Printer
+ object is started-up (initialized, booted, etc.).
+
+ If the Printer object goes down at some value 'n', and comes back up,
+ the implementation MAY:
+
+ 1. Know how long it has been down, and resume at some value greater
+ than 'n', or
+ 2. Restart from 1.
+
+ In the first case, the Printer SHOULD not tweak any existing related
+ Job attributes ("time-at-creation", "time-at-processing", and "time-
+ at-completed"). In the second case, the Printer object SHOULD reset
+
+
+
+deBry, et al. Experimental [Page 110]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ those attributes to 0. If a client queries a time-related Job
+ attribute and finds the value to be 0, the client MUST assume that
+ the Job was submitted in some life other than the Printer's current
+ life.
+
+4.4.27 printer-current-time (dateTime)
+
+ This Printer attribute indicates the current absolute wall-clock
+ time. If an implementation supports this attribute, then a client
+ could calculate the absolute wall-clock time each Job's "time-at-
+ creation", "time-at-processing", and "time-at-completed" attributes
+ by using both "printer-up-time" and this attribute, "printer-
+ current-time". If an implementation does not support this attribute,
+ a client can only calculate the relative time of certain events based
+ on the REQUIRED "printer-up-time" attribute.
+
+4.4.28 multiple-operation-time-out (integer(1:MAX))
+
+ This Printer attributes identifies the minimum time (in seconds) that
+ the Printer object waits for additional Send-Document or Send-URI
+ operations to follow a still-open multi-document Job object before
+ taking any recovery actions, such as the ones indicated in section
+ 3.3.1.
+
+ It is RECOMMENDED that vendors supply a value for this attribute that
+ is between 60 and 240 seconds. An implementation MAY allow a system
+ administrator to set this attribute. If so, the system administrator
+ MAY be able to set values outside this range.
+
+4.4.29 compression-supported (1setOf type3 keyword)
+
+ This Printer attribute identifies the set of supported compression
+ algorithms for document data. Compression only applies to the
+ document data; compression does not apply to the encoding of the IPP
+ operation itself. The supported values are used to validate the
+ client supplied "compression" operation attributes in Print-Job,
+ Send-Document, and Send-URI requests.
+
+ Standard values are :
+
+ 'none': no compression is used.
+ 'deflate': ZIP public domain inflate/deflate) compression
+ technology
+ 'gzip' GNU zip compression technology described in RFC 1952
+ [RFC1952].
+ 'compress': UNIX compression technology
+
+4.4.30 job-k-octets-supported (rangeOfInteger(0:MAX))
+
+
+
+deBry, et al. Experimental [Page 111]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ This Printer attribute specifies the upper and lower bounds of total
+ sizes of jobs in K octets, i.e., in units of 1024 octets. The
+ supported values are used to validate the client supplied "job-k-
+ octets" operation attributes in create requests. The corresponding
+ job description attribute "job-k-octets" is defined in section
+ 4.3.17.
+
+ 4.4.31 job-impressions-supported (rangeOfInteger(0:MAX))
+
+ This Printer attribute specifies the upper and lower bounds for the
+ number of impressions per job. The supported values are used to
+ validate the client supplied "job-impressions" operation attributes
+ in create requests. The corresponding job description attribute
+ "job-impressions" is defined in section 4.3.18.
+
+4.4.32 job-media-sheets-supported (rangeOfInteger(0:MAX))
+
+ This Printer attribute specifies the upper and lower bounds for the
+ number of media sheets per job. The supported values are used to
+ validate the client supplied "job-media-sheets" operation attributes
+ in create requests. The corresponding Job attribute "job-media-
+ sheets" is defined in section 4.3.19.
+
+5. Conformance
+
+ This section describes conformance issues and requirements. This
+ document introduces model entities such as objects, operations,
+ attributes, attribute syntaxes, and attribute values. These
+ conformance sections describe the conformance requirements which
+ apply to these model entities.
+
+5.1 Client Conformance Requirements
+
+ A conforming client MUST support all REQUIRED operations as defined
+ in this document. For each attribute included in an operation
+ request, a conforming client MUST supply a value whose type and value
+ syntax conforms to the requirements of the Model document as
+ specified in Sections 3 and 4. A conforming client MAY supply any
+ registered extensions and/or private extensions in an operation
+ request, as long as they meet the requirements in Section 6.
+
+ Otherwise, there are no conformance requirements placed on the user
+ interfaces provided by IPP clients or their applications. For
+ example, one application might not allow an end user to submit
+ multiple documents per job, while another does. One application
+ might first query a Printer object in order to supply a graphical
+ user interface (GUI) dialogue box with supported and default values
+ whereas a different implementation might not.
+
+
+
+deBry, et al. Experimental [Page 112]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ When sending a request, an IPP client NEED NOT supply any attributes
+ that are indicated as OPTIONALLY supplied by the client.
+
+ A client MUST be able to accept any of the attribute syntaxes defined
+ in Section 4.1, including their full range, that may be returned to
+ it in a response from a Printer object. In particular for each
+ attribute that the client supports whose attribute syntax is 'text',
+ the client MUST accept and process both the 'textWithoutLanguage' and
+ 'textWithLanguage' forms. Similarly, for each attribute that the
+ client supports whose attribute syntax is 'name', the client MUST
+ accept and process both the 'nameWithoutLanguage' and '
+ nameWithLanguage' forms. For presentation purposes, truncation of
+ long attribute values is not recommended. A recommended approach
+ would be for the client implementation to allow the user to scroll
+ through long attribute values.
+
+ A query response may contain attribute groups, attributes, and values
+ that the client does not expect. Therefore, a client implementation
+ MUST gracefully handle such responses and not refuse to inter-operate
+ with a conforming Printer that is returning extended registered or
+ private attributes and/or attribute values that conform to Section 6.
+ Clients may choose to ignore any parameters, attributes, or values
+ that they do not understand.
+
+5.2 IPP Object Conformance Requirements
+
+ This section specifies the conformance requirements for conforming
+ implementations with respect to objects, operations, and attributes.
+
+5.2.1 Objects
+
+ Conforming implementations MUST implement all of the model objects as
+ defined in this specification in the indicated sections:
+
+ Section 2.1 - Printer Object
+ Section 2.2 - Job Object
+
+5.2.2 Operations
+
+ Conforming IPP object implementations MUST implement all of the
+ REQUIRED model operations, including REQUIRED responses, as defined
+ in this specification in the indicated sections:
+
+ For a Printer object:
+ Print-Job (section 3.2.1) REQUIRED
+ Print-URI (section 3.2.2) OPTIONAL
+ Validate-Job (section 3.2.3) REQUIRED
+ Create-Job (section 3.2.4) OPTIONAL
+
+
+
+deBry, et al. Experimental [Page 113]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ Get-Printer-Attributes (section 3.2.5) REQUIRED
+ Get-Jobs (section 3.2.6) REQUIRED
+
+ For a Job object:
+ Send-Document (section 3.3.1) OPTIONAL
+ Send-URI (section 3.3.2) OPTIONAL
+ Cancel-Job (section 3.3.3) REQUIRED
+ Get-Job-Attributes (section 3.3.4) REQUIRED
+
+ Conforming IPP objects MUST support all REQUIRED operation attributes
+ and all values of such attributes if so indicated in the description.
+ Conforming IPP objects MUST ignore all unsupported or unknown
+ operation attributes or operation attribute groups received in a
+ request, but MUST reject a request that contains a supported
+ operation attribute that contains an unsupported value.
+
+ The following section on object attributes specifies the support
+ required for object attributes.
+
+5.2.3 IPP Object Attributes
+
+ Conforming IPP objects MUST support all of the REQUIRED object
+ attributes, as defined in this specification in the indicated
+ sections.
+
+ If an object supports an attribute, it MUST support only those values
+ specified in this document or through the extension mechanism
+ described in section 5.2.4. It MAY support any non-empty subset of
+ these values. That is, it MUST support at least one of the specified
+ values and at most all of them.
+
+5.2.4 Extensions
+
+ A conforming IPP object MAY support registered extensions and private
+ extensions, as long as they meet the requirements specified in
+ Section 6.
+
+ For each attribute included in an operation response, a conforming
+ IPP object MUST return a value whose type and value syntax conforms
+ to the requirement of the Model document as specified in Sections 3
+ and 4.
+
+
+
+
+
+
+
+
+
+
+deBry, et al. Experimental [Page 114]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+5.2.5 Attribute Syntaxes
+
+ An IPP object MUST be able to accept any of the attribute syntaxes
+ defined in Section 4.1, including their full range, in any operation
+ in which a client may supply attributes or the system administrator
+ may configure attributes (by means outside the scope of IPP/1.0). In
+ particular for each attribute that the IPP object supports whose
+ attribute syntax is 'text', the IPP object MUST accept and process
+ both the 'textWithoutLanguage' and 'textWithLanguage' forms.
+ Similarly, for each attribute that the IPP object supports whose
+ attribute syntax is 'name', the IPP object MUST accept and process
+ both the 'nameWithoutLanguage' and 'nameWithLanguage' forms.
+ Furthermore, an IPP object MUST return attributes to the client in
+ operation responses that conform to the syntax specified in Section
+ 4.1, including their full range if supplied previously by a client.
+
+5.3 Charset and Natural Language Requirements
+
+ All clients and IPP objects MUST support the 'utf-8' charset as
+ defined in section 4.1.7.
+
+ IPP objects MUST be able to accept any client request which correctly
+ uses the "attributes-natural-language" operation attribute or the
+ Natural Language Override mechanism on any individual attribute
+ whether or not the natural language is supported by the IPP object.
+ If an IPP object supports a natural language, then it MUST be able to
+ translate (perhaps by table lookup) all generated 'text' or 'name'
+ attribute values into one of the supported languages (see section
+ 3.1.4). That is, the IPP object that supports a natural language
+ NEED NOT be a general purpose translator of any arbitrary 'text' or '
+ name' value supplied by the client into that natural language.
+ However, the object MUST be able to translate (automatically
+ generate) any of its own attribute values and messages into that
+ natural language.
+
+5.4 Security Conformance Requirements
+
+ Conforming IPP Printer objects MAY support Secure Socket Layer
+ Version 3 (SSL3) [SSL] access, support access without SSL3 or support
+ both means of access.
+
+ Conforming IPP clients SHOULD support SSL3 access and non-SSL3
+ access. Note: This client requirement to support both means that
+ conforming IPP clients will be able to inter-operate with any IPP
+ Printer object.
+
+
+
+
+
+
+deBry, et al. Experimental [Page 115]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ For a detailed discussion of security considerations and the IPP
+ application security profile required for SSL3 support, see section
+ 8.
+
+6. IANA Considerations (registered and private extensions)
+
+ This section describes how IPP can be extended to allow the following
+ registered and private extensions to IPP:
+
+ 1. keyword attribute values
+ 2. enum attribute values
+ 3. attributes
+ 4. attribute syntaxes
+ 5. operations
+ 6. attribute groups
+ 7. status codes
+
+ Extensions registered for use with IPP/1.0 are OPTIONAL for client
+ and IPP object conformance to the IPP/1.0 Model specification.
+
+ These extension procedures are aligned with the guidelines as set
+ forth by the IESG [RFC2434]. Section 11 describes how to propose new
+ registrations for consideration. IANA will reject registration
+ proposals that leave out required information or do not follow the
+ appropriate format described in Section 11. IPP/1.0 may also be
+ extended by an appropriate RFC that specifies any of the above
+ extensions.
+
+6.1 Typed 'keyword' and 'enum' Extensions
+
+ IPP allows for 'keyword' and 'enum' extensions (see sections 4.1.2.3
+ and 4.1.4). This document uses prefixes to the 'keyword' and 'enum'
+ basic attribute syntax type in order to communicate extra information
+ to the reader through its name. This extra information is not
+ represented in the protocol because it is unimportant to a client or
+ Printer object. The list below describes the prefixes and their
+ meaning.
+
+ "type1": The IPP specification must be revised to add a new
+ keyword or a new enum. No private keywords or enums are
+ allowed.
+
+ "type2": Implementers can, at any time, add new keyword or enum
+ values by proposing the complete specification to IANA:
+
+ iana@iana.org
+
+
+
+
+
+deBry, et al. Experimental [Page 116]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ IANA will forward the registration proposal to the IPP
+ Designated Expert who will review the proposal with a mailing
+ list that the Designated Expert keeps for this purpose.
+ Initially, that list will be the mailing list used by the IPP
+ WG:
+
+ ipp@pwg.org
+
+ even after the IPP WG is disbanded as permitted by [RFC2434].
+ The IPP Designated Expert is appointed by the IESG Area Director
+ responsible for IPP, according to [RFC2434].
+
+ When a type2 keyword or enum is approved, the IPP Designated
+ Expert becomes the point of contact for any future maintenance
+ that might be required for that registration.
+
+ "type3": Implementers can, at any time, add new keyword and enum
+ values by submitting the complete specification to IANA as for
+ type2 who will forward the proposal to the IPP Designated
+ Expert. While no additional technical review is required, the
+ IPP Designated Expert may, at his/her discretion, forward the
+ proposal to the same mailing list as for type2 registrations for
+ advice and comment.
+
+ When a type3 keyword or enum is approved by the IPP Designated
+ Expert, the original proposer becomes the point of contact for
+ any future maintenance that might be required for that
+ registration.
+
+ For type2 and type3 keywords, the proposer includes the name of the
+ keyword in the registration proposal and the name is part of the
+ technical review.
+
+ After type2 and type3 enums specifications are approved, the IPP
+ Designated Expert in consultation with IANA assigns the next
+ available enum number for each enum value.
+
+ IANA will publish approved type2 and type3 keyword and enum
+ attributes value registration specifications in:
+
+ ftp.isi.edu/iana/assignments/ipp/attribute-values/xxx/yyy.txt
+
+ where xxx is the attribute name that specifies the initial values and
+ yyy.txt is a descriptive file name that contains one or more enums or
+ keywords approved at the same time. For example, if several
+ additional enums for stapling are approved for use with the
+
+
+
+
+
+deBry, et al. Experimental [Page 117]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ "finishings" attribute (and "finishings-default" and "finishings-
+ supported" attributes), IANA will publish the additional values in
+ the file:
+
+ ftp.isi.edu/iana/assignments/ipp/attribute-
+ values/finishings/stapling.txt
+
+ Note: Some attributes are defined to be: 'type3 keywords' | 'name'
+ which allows for attribute values to be extended by a site
+ administrator with administrator defined names. Such names are not
+ registered with IANA.
+
+ By definition, each of the three types above assert some sort of
+ registry or review process in order for extensions to be considered
+ valid. Each higher numbered level (1, 2, 3) tends to be decreasingly
+ less stringent than the previous level. Therefore, any typeN value
+ MAY be registered using a process for some typeM where M is less than
+ N, however such registration is NOT REQUIRED. For example, a type3
+ value MAY be registered in a type 1 manner (by being included in a
+ future version of an IPP specification), however, it is NOT REQUIRED.
+
+ This specification defines keyword and enum values for all of the
+ above types, including type3 keywords.
+
+ For private (unregistered) keyword extensions, implementers SHOULD
+ use keywords with a suitable distinguishing prefix, such as "xxx-"
+ where xxx is the (lowercase) fully qualified company name registered
+ with IANA for use in domain names [RFC1035]. For example, if the
+ company XYZ Corp. had obtained the domain name "XYZ.com", then a
+ private keyword 'abc' would be: 'xyz.com-abc'.
+
+ Note: RFC 1035 [RFC1035] indicates that while upper and lower case
+ letters are allowed in domain names, no significance is attached to
+ the case. That is, two names with the same spelling but different
+ case are to be treated as if identical. Also, the labels in a domain
+ name must follow the rules for ARPANET host names: They must start
+ with a letter, end with a letter or digit, and have as interior
+ characters only letters, digits, and hyphen. Labels must be 63
+ characters or less. Labels are separated by the "." character.
+
+ For private (unregistered) enum extension, implementers MUST use
+ values in the reserved integer range which is 2**30 to 2**31-1.
+
+
+
+
+
+
+
+
+
+deBry, et al. Experimental [Page 118]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+6.2 Attribute Extensibility
+
+ Attribute names are type2 keywords. Therefore, new attributes may be
+ registered and have the same status as attributes in this document by
+ following the type2 extension rules. For private (unregistered)
+ attribute extensions, implementers SHOULD use keywords with a
+ suitable distinguishing prefix as described in Section 6.1.
+
+ IANA will publish approved attribute registration specifications as
+ separate files:
+
+ ftp.isi.edu/iana/assignments/ipp/attributes/xxx-yyy.txt
+
+ where "xxx-yyy" is the new attribute name.
+
+ If a new Printer object attribute is defined and its values can be
+ affected by a specific document format, its specification needs to
+ contain the following sentence:
+
+ "The value of this attribute returned in a Get-Printer-Attributes
+ response MAY depend on the "document-format" attribute supplied
+ (see Section 3.2.5.1)."
+
+ If the specification does not, then its value in the Get-Printer-
+ Attributes response MUST NOT depend on the "document-format" supplied
+ in the request. When a new Job Template attribute is registered, the
+ value of the Printer attributes MAY vary with "document-format"
+ supplied in the request without the specification having to indicate
+ so.
+
+6.3 Attribute Syntax Extensibility
+
+ Attribute syntaxes are like type2 enums. Therefore, new attribute
+ syntaxes may be registered and have the same status as attribute
+ syntaxes in this document by following the type2 extension rules
+ described in Section 6.1. The value codes that identify each of the
+ attribute syntaxes are assigned in the Encoding and Transport
+ specification [RFC2565], including a designated range for private,
+ experimental use.
+
+ For attribute syntaxes, the IPP Designated Expert in consultation
+ with IANA assigns the next attribute syntax code in the appropriate
+ range as specified in [RFC2565]. IANA will publish approved
+ attribute syntax registration specifications as separate files:
+
+ ftp.isi.edu/iana/assignments/ipp/attribute-syntaxes/xxx-yyy.txt
+
+ where 'xxx-yyy' is the new attribute syntax name.
+
+
+
+deBry, et al. Experimental [Page 119]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+6.4 Operation Extensibility
+
+ Operations may also be registered following the type2 procedures
+ described in Section 6.1, though major new operations will usually be
+ done by a new standards track RFC that augments this document. For
+ private (unregistered) operation extensions, implementers MUST use
+ the range for the "operation-id" in requests specified in Section
+ 4.4.13 "operations-supported" Printer attribute.
+
+ For operations, the IPP Designated Expert in consultation with IANA
+ assigns the next operation-id code as specified in Section 4.4.13.
+ IANA will publish approved operation registration specifications as
+ separate files:
+
+ ftp.isi.edu/iana/assignments/ipp/operations/Xxx-Yyy.txt
+
+ where "Xxx-Yyy" is the new operation name.
+
+6.5 Attribute Groups
+
+ Attribute groups passed in requests and responses may be registered
+ following the type2 procedures described in Section 6.1. The tags
+ that identify each of the attribute groups are assigned in [RFC2565].
+
+ For attribute groups, the IPP Designated Expert in consultation with
+ IANA assigns the next attribute group tag code in the appropriate
+ range as specified in [RFC2565]. IANA will publish approved
+ attribute group registration specifications as separate files:
+
+ ftp.isi.edu/iana/assignments/ipp/attribute-group-tags/xxx-yyy-
+ tag.txt
+
+ where 'xxx-yyy-tag' is the new attribute group tag name.
+
+6.6 Status Code Extensibility
+
+ Operation status codes may also be registered following the type2
+ procedures described in Section 6.1. The values for status codes are
+ allocated in ranges as specified in Section 13 for each status code
+ class:
+
+ "informational" - Request received, continuing process
+ "successful" - The action was successfully received, understood,
+ and accepted
+ "redirection" - Further action must be taken in order to complete
+ the request
+ "client-error" - The request contains bad syntax or cannot be
+ fulfilled
+
+
+
+deBry, et al. Experimental [Page 120]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ "server-error" - The IPP object failed to fulfill an apparently
+ valid request
+
+ For private (unregistered) operation status code extensions,
+ implementers MUST use the top of each range as specified in Section
+ 13.
+
+ For operation status codes, the IPP Designated Expert in consultation
+ with IANA assigns the next status code in the appropriate class range
+ as specified in Section 13. IANA will publish approved status code
+ registration specifications as separate files:
+
+ ftp.isi.edu/iana/assignments/ipp/status-codes/xxx-yyy.txt
+
+ where "xxx-yyy" is the new operation status code keyword.
+
+6.7 Registration of MIME types/sub-types for document-formats
+
+ The "document-format" attribute's syntax is 'mimeMediaType'. This
+ means that valid values are Internet Media Types (see Section 4.1.9).
+ RFC 2045 [RFC2045] defines the syntax for valid Internet media types.
+ IANA is the registry for all Internet media types.
+
+6.8 Registration of charsets for use in 'charset' attribute values
+
+ The "attributes-charset" attribute's syntax is 'charset'. This means
+ that valid values are charsets names. When a charset in the IANA
+ registry has more than one name (alias), the name labeled as
+ "(preferred MIME name)", if present, MUST be used (see Section
+ 4.1.7). IANA is the registry for charsets following the procedures
+ of [RFC2278].
+
+7. Internationalization Considerations
+
+ Some of the attributes have values that are text strings and names
+ which are intended for human understanding rather than machine
+ understanding (see the 'text' and 'name' attribute syntaxes in
+ Sections 4.1.1 and 4.1.2).
+
+ In each operation request, the client
+
+ - identifies the charset and natural language of the request which
+ affects each supplied 'text' and 'name' attribute value, and
+ - requests the charset and natural language for attributes returned
+ by the IPP object in operation responses (as described in Section
+ 3.1.4.1).
+
+
+
+
+
+deBry, et al. Experimental [Page 121]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ In addition, the client MAY separately and individually identify the
+ Natural Language Override of a supplied 'text' or 'name' attribute
+ using the 'textWithLanguage' and 'nameWithLanguage' technique
+ described section 4.1.1.2 and 4.1.2.2 respectively.
+
+ All IPP objects MUST support the UTF-8 [RFC2279] charset in all '
+ text' and 'name' attributes supported. If an IPP object supports
+ more than the UTF-8 charset, the object MUST convert between them in
+ order to return the requested charset to the client according to
+ Section 3.1.4.2. If an IPP object supports more than one natural
+ language, the object SHOULD return 'text' and 'name' values in the
+ natural language requested where those values are generated by the
+ Printer (see Section 3.1.4.1).
+
+ For Printers that support multiple charsets and/or multiple natural
+ languages in 'text' and 'name' attributes, different jobs may have
+ been submitted in differing charsets and/or natural languages. All
+ responses MUST be returned in the charset requested by the client.
+ However, the Get-Jobs operation uses the 'textWithLanguage' and '
+ nameWithLanguage' mechanism to identify the differing natural
+ languages with each job attribute returned.
+
+ The Printer object also has configured charset and natural language
+ attributes. The client can query the Printer object to determine
+ the list of charsets and natural languages supported by the Printer
+ object and what the Printer object's configured values are. See the
+ "charset-configured", "charset-supported", "natural-language-
+ configured", and "generated-natural-language-supported" Printer
+ description attributes for more details.
+
+ The "charset-supported" attributed identifies the supported charsets.
+ If a charset is supported, the IPP object MUST be capable of
+ converting to and from that charset into any other supported charset.
+ In many cases, an IPP object will support only one charset and it
+ MUST be the UTF-8 charset.
+
+ The "charset-configured" attribute identifies the one supported
+ charset which is the native charset given the current configuration
+ of the IPP object (administrator defined).
+
+ The "generated-natural-language-supported" attribute identifies the
+ set of supported natural languages for generated messages; it is not
+ related to the set of natural languages that must be accepted for
+ client supplied 'text' and 'name' attributes. For client supplied '
+ text' and 'name' attributes, an IPP object MUST accept ALL supplied
+ natural languages. Just because a Printer object is currently
+
+
+
+
+
+deBry, et al. Experimental [Page 122]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ configured to support 'en-us' natural language does not mean that the
+ Printer object should reject a job if the client supplies a job name
+ that is in 'fr-ca'.
+
+ The "natural-language-configured" attribute identifies the one
+ supported natural language for generated messages which is the native
+ natural language given the current configuration of the IPP object
+ (administrator defined).
+
+ Attributes of type 'text' and 'name' are populated from different
+ sources. These attributes can be categorized into following groups
+ (depending on the source of the attribute):
+
+ 1. Some attributes are supplied by the client (e.g., the client
+ supplied "job-name", "document-name", and "requesting-user-name"
+ operation attributes along with the corresponding Job object's
+ "job-name" and "job-originating-user-name" attributes). The IPP
+ object MUST accept these attributes in any natural language no
+ matter what the set of supported languages for generated
+ messages
+ 2. Some attributes are supplied by the system administrator (e.g.,
+ the Printer object's "printer-name" and "printer-location"
+ attributes). These too can be in any natural language. If the
+ natural language for these attributes is different than what a
+ client requests, then they must be reported using the Natural
+ Language Override mechanism.
+ 3. Some attributes are supplied by the device manufacturer (e.g.,
+ the Printer object's "printer-make-and-model" attribute). These
+ too can be in any natural language. If the natural language for
+ these attributes is different than what a client requests, then
+ they must be reported using the Natural Language Override
+ mechanism.
+ 4. Some attributes are supplied by the operator (e.g., the Job
+ object's "job-message-from-operator" attribute). These too can
+ be in any natural language. If the natural language for these
+ attributes is different than what a client requests, then they
+ must be reported using the Natural Language Override mechanism.
+ 5. Some attributes are generated by the IPP object (e.g., the Job
+ object's "job-state-message" attribute, the Printer object's
+ "printer-state-message" attribute, and the "status-message"
+ operation attribute). These attributes can only be in one of
+ the "generated-natural-language-supported" natural languages.
+ If a client requests some natural language for these attributes
+ other than one of the supported values, the IPP object SHOULD
+ respond using the value of the "natural-language-configured"
+ attribute (using the Natural Language Override mechanism if
+ needed).
+
+
+
+
+deBry, et al. Experimental [Page 123]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ The 'text' and 'name' attributes specified in this version of this
+ document (additional ones will be registered according to the
+ procedures in Section 6) are:
+
+ Attributes Source
+ -------------------------- ----------
+ Operation Attributes
+ job-name (name) client
+ document-name (name) client
+ requesting-user-name (name) client
+ status-message Job or Printer object
+
+ Job Template Attributes:
+ job-hold-until) client matches administrator-configured
+ (keyword | name
+ job-hold-until-default client matches administrator-configured
+ (keyword | name)
+ job-hold-until-supported client matches administrator-configured
+ (keyword | name)
+ job-sheets client matches administrator-configured
+ (keyword | name)
+ job-sheets-default client matches administrator-configured
+ (keyword | name)
+ job-sheets-supported client matches administrator-configured
+ (keyword | name)
+ media client matches administrator-configured
+ (keyword | name)
+ media-default client matches administrator-configured
+ (keyword | name)
+ media-supported client matches administrator-configured
+ (keyword | name)
+ media-ready client matches administrator-configured
+ (keyword | name)
+
+ Job Description Attributes:
+ job-name (name) client or Printer object
+ job-originating-user-name (name) Printer object
+ job-state-message (text) Job or Printer object
+ output-device-assigned (name(127)) administrator
+ job-message-from-operator (text(127)) operator
+
+ Printer Description Attributes:
+ printer-name (name(127)) administrator
+ printer-location (text(127)) administrator
+ printer-info (text(127)) administrator
+ printer-make-and-model (text(127)) administrator or manufacturer
+ printer-state-message (text) Printer object
+ printer-message-from-operator (text(127)) operator
+
+
+
+deBry, et al. Experimental [Page 124]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+8. Security Considerations
+
+ Some IPP objects MAY be deployed over protocol stacks that support
+ Secure Socket Layer Version 3 (SSL3) [SSL]. Note: SSL3 is not an
+ IETF standards track specification. Other IPP objects MAY be
+ deployed over protocol stacks that do not support SSL3. Some IPP
+ objects MAY be deployed over both types of protocol stacks. Those
+ IPP objects that support SSL3, are capable of supporting mutual
+ authentication as well as privacy of messages via multiple encryption
+ schemes. An important point about security related information for
+ SSL3 access to an IPP object, is that the security-related parameters
+ (authentication, encryption keys, etc.) are "out-of-band" to the
+ actual IPP protocol.
+
+ An IPP object that does not support SSL3 MAY elect to support a
+ transport layer that provides other security mechanisms. For
+ example, in a mapping of IPP over HTTP/1.1 [RFC2565], if the IPP
+ object does not support SSL3, HTTP still allows for client
+ authentication using Digest Access Authentication (DAA) [RFC2069].
+
+ It is difficult to anticipate the security risks that might exist in
+ any given IPP environment. For example, if IPP is used within a given
+ corporation over a private network, the risks of exposing document
+ data may be low enough that the corporation will choose not to use
+ encryption on that data. However, if the connection between the
+ client and the IPP object is over a public network, the client may
+ wish to protect the content of the information during transmission
+ through the network with encryption.
+
+ Furthermore, the value of the information being printed may vary from
+ one IPP environment to the next. Printing payroll checks, for
+ example, would have a different value than printing public
+ information from a file. There is also the possibly of denial-of-
+ service attacks, but denial-of-service attacks against printing
+ resources are not well understood and there is no published
+ precedents regarding this scenario.
+
+ Once the authenticated identity of the requester has been supplied to
+ the IPP object, the object uses that identity to enforce any
+ authorization policy that might be in place. For example, one site's
+ policy might be that only the job owner is allowed to cancel a job.
+ The details and mechanisms to set up a particular access control
+ policy are not part of IPP/1.0, and must be established via some
+ other type of administrative or access control framework. However,
+ there are operation status codes that allow an IPP server to return
+ information back to a client about any potential access control
+ violations for an IPP object.
+
+
+
+
+deBry, et al. Experimental [Page 125]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ During a create operation, the client's identity is recorded in the
+ Job object in an implementation-defined attribute. This information
+ can be used to verify a client's identity for subsequent operations
+ on that Job object in order to enforce any access control policy that
+ might be in effect. See section 8.3 below for more details.
+
+ Since the security levels or the specific threats that any given IPP
+ system administrator may be concerned with cannot be anticipated, IPP
+ MUST be capable of operating with different security mechanisms and
+ security policies as required by the individual installation.
+ Security policies might vary from very strong, to very weak, to none
+ at all, and corresponding security mechanisms will be required. SSL3
+ supports the type of negotiated levels of security required by most,
+ if not all, potential IPP environments. IPP environments that require
+ no security can elect to deploy IPP objects that do not utilize the
+ optional SSL3 security mechanisms.
+
+8.1 Security Scenarios
+
+ The following sections describe specific security attacks for IPP
+ environments. Where examples are provided they should be considered
+ illustrative of the environment and not an exhaustive set. Not all of
+ these environments will necessarily be addressed in initial
+ implementations of IPP.
+
+8.1.1 Client and Server in the Same Security Domain
+
+ This environment is typical of internal networks where traditional
+ office workers print the output of personal productivity applications
+ on shared work-group printers, or where batch applications print
+ their output on large production printers. Although the identity of
+ the user may be trusted in this environment, a user might want to
+ protect the content of a document against such attacks as
+ eavesdropping, replaying or tampering.
+
+8.1.2 Client and Server in Different Security Domains
+
+ Examples of this environment include printing a document created by
+ the client on a publicly available printer, such as at a commercial
+ print shop; or printing a document remotely on a business associate's
+ printer. This latter operation is functionally equivalent to sending
+ the document to the business associate as a facsimile. Printing
+ sensitive information on a Printer in a different security domain
+ requires strong security measures. In this environment authentication
+ of the printer is required as well as protection against unauthorized
+ use of print resources. Since the document crosses security domains,
+
+
+
+
+
+deBry, et al. Experimental [Page 126]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ protection against eavesdropping and document tampering are also
+ required. It will also be important in this environment to protect
+ Printers against "spamming" and malicious document content.
+
+8.1.3 Print by Reference
+
+ When the document is not stored on the client, printing can be done
+ by reference. That is, the print request can contain a reference, or
+ pointer, to the document instead of the actual document itself.
+ Standard methods currently do not exist for remote entities to
+ "assume" the credentials of a client for forwarding requests to a 3rd
+ party. It is anticipated that Print-By-Reference will be used to
+ access "public" documents and that sophisticated methods for
+ authenticating "proxies" will not be specified for version 1 of IPP.
+
+8.2 URIs for SSL3 and non-SSL3 Access
+
+ As described earlier, an IPP object can support SSL3 access, non-SSL3
+ access, or both. The "printer-uri-supported" attribute contains the
+ Printer object's URI(s). Its companion attribute, "uri-security-
+ supported", identifies the security mechanism used for each URI
+ listed in the "printer-uri-supported" attribute. For each Printer
+ operation request, a client MUST supply only one URI in the
+ "printer-uri" operation attribute. In other words, even though the
+ Printer supports more than one URI, the client only interacts with
+ the Printer object using one if its URIs. This duality is not needed
+ for Job objects, since the Printer objects is the factory for Job
+ objects, and the Printer object will generate the correct URI for new
+ Job objects depending on the Printer object's security configuration.
+
+8.3 The "requesting-user-name" (name(MAX)) Operation Attribute
+
+ Each operation MUST specify the user who is performing the operation
+ in both of the following two ways:
+
+ 1) via the REQUIRED "requesting-user-name" operation attribute that
+ a client SHOULD supply in all operations. The client MUST obtain
+ the value for this attribute from an environmental or network
+ login name for the user, rather than allowing the user to supply
+ any value. If the client does not supply a value for
+ "requesting-user-name", the printer MUST assume that the client
+ is supplying some anonymous name, such as "anonymous".
+ 2) via an authentication mechanism of the underlying transport
+ which may be configured to give no authentication information.
+
+
+
+
+
+
+
+deBry, et al. Experimental [Page 127]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ There are six cases to consider:
+
+ a) the authentication mechanism gives no information, and the
+ client doesn't specify "requesting-user-name".
+ b) the authentication mechanism gives no information, but the
+ client specifies "requesting-user-name".
+ c) the authentication mechanism specifies a user which has no human
+ readable representation, and the client doesn't specify
+ "requesting-user-name".
+ d) the authentication mechanism specifies a user which has no human
+ readable representation, but the client specifies "requesting-
+ user-name".
+ e) the authentication mechanism specifies a user which has a human
+ readable representation. The Printer object ignores the
+ "requesting-user-name".
+ f) the authentication mechanism specifies a user who is trusted and
+ whose name means that the value of the "requesting-user-name",
+ which MUST be present, is treated as the authenticated name.
+
+ Note: Case "f" is intended for a tightly coupled gateway and server
+ to work together so that the "user" name is able to be that of the
+ gateway client and not that of the gateway. Because most, if not
+ all, system vendors will initially implement IPP via a gateway into
+ their existing print system, this mechanism is necessary unless the
+ authentication mechanism allows a gateway (client) to act on behalf
+ of some other client.
+
+ The user-name has two forms:
+
+ - one that is human readable: it is held in the REQUIRED "job-
+ originating-user-name" Job Description attribute which is set
+ during the job creation operations. It is used for presentation
+ only, such as returning in queries or printing on start sheets
+ - one for authorization: it is held in an undefined (by IPP) Job
+ object attribute which is set by the job creation operation. It
+ is used to authorize other operations, such as Send-Document,
+ Send-URI, Cancel-Job, to determine the user when the "my-jobs"
+ attribute is specified with Get-Jobs, and to limit what
+ attributes and values to return with Get-Job-Attributes and Get-
+ Jobs.
+
+ The human readable user name:
+
+ - is the value of the "requesting-user-name" for cases b, d and f.
+ - comes from the authentication mechanism for case e
+ - is some anonymous name, such as "anonymous" for cases a and c.
+
+ The user name used for authorization:
+
+
+
+deBry, et al. Experimental [Page 128]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ - is the value of the "requesting-user-name" for cases b and f.
+ - comes from the authentication mechanism for cases c, d and e
+ - is some anonymous name, such as "anonymous" for case a.
+
+ The essence of these rules for resolving conflicting sources of
+ user-names is that a printer implementation is free to pick either
+ source as long as it achieves consistent results. That is, if a user
+ uses the same path for a series of requests, the requests MUST appear
+ to come from the same user from the standpoint of both the human-
+ readable user name and the user name for authorization. This rule
+ MUST continue to apply even if a request could be authenticated by
+ two or more mechanisms. It doesn't matter which of several
+ authentication mechanisms a Printer uses as long as it achieves
+ consistent results. If a client uses more than one authentication
+ mechanism, it is recommended that an administrator make all
+ credentials resolve to the same user and user-name as much as
+ possible.
+
+8.4 Restricted Queries
+
+ In many IPP operations, a client supplies a list of attributes to be
+ returned in the response. For security reasons, an IPP object may be
+ configured not to return all attributes (or all values) that a client
+ requests. The job attributes returned MAY depend on whether the
+ requesting user is the same as the user that submitted the job. The
+ IPP object MAY even return none of the requested attributes. In such
+ cases, the status returned is the same as if the object had returned
+ all requested attributes. The client cannot tell by such a response
+ whether the requested attribute was present or absent on the object.
+
+8.5 Queries on jobs submitted using non-IPP protocols
+
+ If the device that an IPP Printer is representing is able to accept
+ jobs using other job submission protocols in addition to IPP, it is
+ RECOMMENDED that such an implementation at least allow such "foreign"
+ jobs to be queried using Get-Jobs returning "job-id" and "job-uri" as
+ 'unknown'. Such an implementation NEED NOT support all of the same
+ IPP job attributes as for IPP jobs. The IPP object returns the '
+ unknown' out-of-band value for any requested attribute of a foreign
+ job that is supported for IPP jobs, but not for foreign jobs.
+
+ It is further RECOMMENDED, that the IPP Printer generate "job-id" and
+ "job-uri" values for such "foreign jobs", if possible, so that they
+ may be targets of other IPP operations, such as Get-Job-Attributes
+ and Cancel-Job. Such an implementation also needs to deal with the
+ problem of authentication of such foreign jobs. One approach would
+ be to treat all such foreign jobs as belonging to users other than
+ the user of the IPP client. Another approach would be for the
+
+
+
+deBry, et al. Experimental [Page 129]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ foreign job to belong to 'anonymous'. Only if the IPP client has
+ been authenticated as an operator or administrator of the IPP Printer
+ object, could the foreign jobs be queried by an IPP request.
+ Alternatively, if the security policy is to allow users to query
+ other users' jobs, then the foreign jobs would also be visible to an
+ end-user IPP client using Get-Jobs and Get-Job-Attributes.
+
+8.6 IPP Security Application Profile for SSL3
+
+ The IPP application profile for SSL3 follows the "Secure Socket
+ Layer" requirement as documented in the SSL3 specification [SSL].
+ For interoperability, the SSL3 cipher suites are:
+
+ SSL_RSA_WITH_RC4_128_MD5
+ SSL_RSA_WITH_3DES_EDE_CBC_SHA
+ SSL_RSA_WITH_DES_CBC_SHA
+ SSL_RSA_EXPORT_WITH_RC4_40_MD5
+ SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5
+ SSL_RSA_WITH_NULL_MD5
+
+ Client implementations MUST NOT assume any other cipher suites are
+ supported by an IPP Printer object.
+
+ If a conforming IPP object supports SSL3, it MUST implement and
+ support the cipher suites listed above and MAY support additional
+ cipher suites.
+
+ A conforming IPP client SHOULD support SSL3 including the cipher
+ suites listed above. A conforming IPP client MAY support additional
+ cipher suites.
+
+ It is possible that due to certain government export restrictions
+ some non-compliant versions of this extension could be deployed.
+ Implementations wishing to inter-operate with such non-compliant
+ versions MAY offer the SSL_RSA_EXPORT_WITH_RC4_40_MD5 and
+ SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5 mechanisms. However, since 40 bit
+ ciphers are known to be vulnerable to attack by current technology,
+ any client which actives a 40 bit cipher MUST NOT indicate to the
+ user that the connection is completely secure from eavesdropping.
+
+
+
+
+
+
+
+
+
+
+
+
+deBry, et al. Experimental [Page 130]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+9. References
+
+ [ASCII] Coded Character Set - 7-bit American Standard Code for
+ Information Interchange (ASCII), ANSI X3.4-1986. This
+ standard is the specification of the US-ASCII charset.
+
+ [HTPP] J. Barnett, K. Carter, R. DeBry, "Initial Draft -
+ Hypertext Printing Protocol - HTPP/1.0", October 1996.
+ ftp://ftp.pwg.org/pub/pwg/ipp/historic/htpp/
+ overview.ps.gz
+
+ [IANA-CS] IANA Registry of Coded Character Sets:
+ ftp://ftp.isi.edu/in-notes/iana/assignments/character-
+ sets
+
+ [IANA-MT] IANA Registry of Media Types: ftp://ftp.isi.edu/in-
+ notes/iana/assignments/media-types/
+
+ [ipp-iig] Hastings, T. and C. Manros, "Internet Printing
+ Protocol/1.0: Implementer's Guide", Work in Progress.
+
+ [ISO10646-1] ISO/IEC 10646-1:1993, "Information technology --
+ Universal Multiple-Octet Coded Character Set (UCS) -
+ Part 1: Architecture and Basic Multilingual Plane,
+ JTC1/SC2."
+
+ [ISO8859-1] ISO/IEC 8859-1:1987, "Information technology -- 8-bit
+ One-Byte Coded Character Set - Part 1: Latin Alphabet Nr
+ 1", 1987, JTC1/SC2.
+
+ [ISO10175] ISO/IEC 10175 Document Printing Application (DPA), June
+ 1996.
+
+ [LDPA] T. Hastings, S. Isaacson, M. MacKay, C. Manros, D. Taylor, P.
+ Zehler, "LDPA - Lightweight Document Printing
+ Application", October 1996,
+ ftp://ftp.pwg.org/pub/pwg/ipp/historic/ldpa/ldpa8.pdf.gz
+
+ [P1387.4] Kirk, M. (Editor), POSIX System Administration - Part 4:
+ Printing Interfaces, POSIX 1387.4 D8, 1994.
+
+ [PSIS] Herriot, R. (editor), X/Open A Printing System
+ Interoperability Specification (PSIS), August 1995.
+
+ [PWG] Printer Working Group, http://www.pwg.org.
+
+ [RFC1035] Mockapetris, P., "DOMAIN NAMES - IMPLEMENTATION AND
+ SPECIFICATION", STD 13, RFC 1035, November 1987.
+
+
+
+deBry, et al. Experimental [Page 131]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ [RFC1759] Smith, R., Wright, F., Hastings, T., Zilles, S. and J.
+ Gyllenskog, "Printer MIB", RFC 1759, March 1995.
+
+ [RFC1766] Alvestrand, H., "Tags for the Identification of
+ Languages", RFC 1766, March 1995.
+
+ [RFC1179] McLaughlin, L. (Editor), "Line Printer Daemon Protocol",
+ RFC 1179, August 1990.
+
+ [RFC1952] Deutsch, P., "GZIP file format specification version
+ 4.3", RFC 1952, May 1996.
+
+ [RFC2045] Freed, N. and N. Borenstein, " Multipurpose Internet
+ Mail Extensions (MIME) Part One: Format of Internet
+ Message Bodies", RFC 2045, November 1996.
+
+ [RFC2046] Freed, N. and N. Borenstein, "Multipurpose Internet Mail
+ Extensions (MIME) Part Two: Media Types", RFC 2046,
+ November 1996.
+
+ [RFC2048] Freed, N., Klensin, J. and J. Postel, "Multipurpose
+ Internet Mail Extension (MIME) Part Four: Registration
+ Procedures", RFC 2048, November 1996.
+
+ [RFC2068] Fielding, R., Gettys, J., Mogul, J., Frystyk, H. AND T.
+ Berners-Lee, "Hypertext Transfer Protocol - HTTP/1.1",
+ RFC 2068, January 1997.
+
+ [RFC2069] Franks, J., Hallam-Baker, P., Hostetler, J., Leach, P.,
+ Luotonen, A., Sink, E. and L. Stewart, "An Extension to
+ HTTP: Digest Access Authentication", RFC 2069, January
+ 1997.
+
+ [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate
+ Requirement Levels", BCP 14, RFC 2119, March 1997.
+
+ [RFC2228] Horowitz, M. and S. Lunt, "FTP Security Extensions", RFC
+ 2228, October 1997.
+
+ [RFC2277] Alvestrand, H., "IETF Policy on Character Sets and
+ Languages" RFC 2277, January 1998.
+
+ [RFC2278] Freed, N. and J. Postel: "IANA Charset Registration
+ Procedures", BCP 19, RFC 2278, January 1998.
+
+ [RFC2279] Yergeau, F., "UTF-8, a transformation format of ISO
+ 10646", RFC 2279, January 1998.
+
+
+
+
+deBry, et al. Experimental [Page 132]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ [RFC2316] Bellovin, S., "Report of the IAB Security Architecture
+ Workshop", RFC 2316, April 1998.
+
+ [RFC2396] Berners-Lee, T., Fielding, R. and L. Masinter, "Uniform
+ Resource Identifiers (URI): Generic Syntax", RFC 2396,
+ August 1998.
+
+ [RFC2434] Narten, T. and H. Alvestrand, "Guidelines for Writing an
+ IANA Considerations Section in RFCs", BCP 26, RFC 2434,
+ October 1998.
+
+ [RFC2565] Herriot, R., Butler, S., Moore, P. and R. Tuner
+ "Internet Printing Protocol/1.0: Encoding and
+ Transport", RFC 2565, April 1999.
+
+ [RFC2567] Wright, D., "Design Goals for an Internet Printing
+ Protocol", RFC 2567, April 1999.
+
+ [RFC2568] Zilles, S., "Rationale for the Structure and Model and
+ Protocol for the Internet Printing Protocol", RFC 2568,
+ April 1999.
+
+ [RFC2569] Herriot, R., Hastings, T., Jacobs, N. and J. Martin,
+ "Mapping between LPD and IPP Protocols", RFC 2569, April
+ 1999.
+
+ [RFC2579] McCloghrie, K., Perkins, D. and J. Schoenwaelder,
+ "Textual Conventions for SMIv2", STD 58, RFC 2579, April
+ 1999.
+
+ [SSL] Netscape, The SSL Protocol, Version 3, (Text version
+ 3.02), November 1996.
+
+ [SWP] P. Moore, B. Jahromi, S. Butler, "Simple Web Printing
+ SWP/1.0", May 7, 1997,
+ ftp://ftp.pwg.org/pub/pwg/ipp/new_PRO/swp9705.pdf
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+deBry, et al. Experimental [Page 133]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+10. Authors' Addresses
+
+ Scott A. Isaacson (Editor)
+ Novell, Inc.
+ 122 E 1700 S
+ Provo, UT 84606
+
+ Phone: 801-861-7366
+ Fax: 801-861-2517
+ EMail: sisaacson@novell.com
+
+
+ Tom Hastings
+ Xerox Corporation
+ 737 Hawaii St.
+ El Segundo, CA 90245
+
+ Phone: 310-333-6413
+ Fax: 310-333-5514
+ EMail: hastings@cp10.es.xerox.com
+
+
+ Robert Herriot
+ Xerox Corporation
+ 3400 Hillview Ave., Bldg #1
+ Palo Alto, CA 94304
+
+ Phone: 650-813-7696
+ Fax: 650-813-6860
+ EMail: robert.herriot@pahv.xerox.com
+
+
+ Roger deBry
+ Utah Valley State College
+ Orem, UT 84058
+
+ Phone: (801) 222-8000
+ EMail: debryro@uvsc.edu
+
+
+
+
+
+
+
+
+
+
+
+
+
+deBry, et al. Experimental [Page 134]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ Patrick Powell
+ Astart Technologies
+ 9475 Chesapeake Dr., Suite D
+ San Diego, CA 95123
+
+ Phone: (619) 874-6543
+ Fax: (619) 279-8424
+ EMail: papowell@astart.com
+
+ IPP Mailing List: ipp@pwg.org
+ IPP Mailing List Subscription: ipp-request@pwg.org
+ IPP Web Page: http://www.pwg.org/ipp/
+
+ Implementers of this specification are encouraged to join IPP Mailing
+ List in order to participate in any discussions of clarification
+ issues and review of registration proposals for additional attributes
+ and values.
+
+ Other Participants:
+
+ Chuck Adams - Tektronix
+ Jeff Barnett - IBM
+ Ron Bergman - Dataproducts Corp.
+ Sylvan Butler - HP
+ Keith Carter - IBM Corporation
+ Jeff Copeland - QMS
+ Andy Davidson - Tektronix
+ Mabry Dozier - QMS
+ Lee Farrell - Canon Information Systems
+ Steve Gebert - IBM
+ Babek Jahromi - Microsoft
+ David Kellerman - Northlake Software
+ Rick Landau - Digital
+ Greg LeClair - Epson
+ Harry Lewis - IBM
+ Pete Loya - HP
+ Ray Lutz - Cognisys
+ Mike MacKay - Novell, Inc.
+ Daniel Manchala - Xerox
+ Carl-Uno Manros - Xerox
+ Jay Martin - Underscore
+ Larry Masinter - Xerox
+ Stan McConnell - Xerox
+ Ira McDonald - High North Inc.
+ Paul Moore - Microsoft
+ Tetsuya Morita - Ricoh
+ Yuichi Niwa - Ricoh
+ Pat Nogay - IBM
+
+
+
+deBry, et al. Experimental [Page 135]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ Ron Norton - Printronics
+ Bob Pentecost - HP
+ Rob Rhoads - Intel
+ Xavier Riley - Xerox
+ David Roach - Unisys
+ Stuart Rowley - Kyocera
+ Hiroyuki Sato - Canon
+ Bob Setterbo - Adobe
+ Devon Taylor - Novell, Inc.
+ Mike Timperman - Lexmark
+ Randy Turner - Sharp
+ Atsushi Yuki - Kyocera
+ Rick Yardumian - Xerox
+ Lloyd Young - Lexmark
+ Bill Wagner - DPI
+ Jim Walker - DAZEL
+ Chris Wellens - Interworking Labs
+ Rob Whittle - Novell, Inc.
+ Don Wright - Lexmark
+ Peter Zehler - Xerox
+ Steve Zilles - Adobe
+
+11. Formats for IPP Registration Proposals
+
+ In order to propose an IPP extension for registration, the proposer
+ must submit an application to IANA by email to "iana@iana.org" or by
+ filling out the appropriate form on the IANA web pages
+ (http://www.iana.org). This section specifies the required
+ information and the formats for proposing registrations of extensions
+ to IPP as provided in Section 6 for:
+
+ 1. type2 'keyword' attribute values
+ 2. type3 'keyword' attribute values
+ 3. type2 'enum' attribute values
+ 4. type3 'enum' attribute values
+ 5. attributes
+ 6. attribute syntaxes
+ 7. operations
+ 8. status codes
+
+11.1 Type2 keyword attribute values registration
+
+ Type of registration: type2 keyword attribute value
+ Name of attribute to which this keyword specification is to be added:
+ Proposed keyword name of this keyword value:
+ Specification of this keyword value (follow the style of IPP Model
+ Section 4.1.2.3):
+ Name of proposer:
+
+
+
+deBry, et al. Experimental [Page 136]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ Address of proposer:
+ Email address of proposer:
+
+ Note: For type2 keywords, the Designated Expert will be the point of
+ contact for the approved registration specification, if any
+ maintenance of the registration specification is needed.
+
+11.2 Type3 keyword attribute values registration
+
+ Type of registration: type3 keyword attribute value
+ Name of attribute to which this keyword specification is to be added:
+ Proposed keyword name of this keyword value:
+ Specification of this keyword value (follow the style of IPP Model
+ Section 4.1.2.3):
+ Name of proposer:
+ Address of proposer:
+ Email address of proposer:
+
+ Note: For type3 keywords, the proposer will be the point of contact
+ for the approved registration specification, if any maintenance of
+ the registration specification is needed.
+
+11.3 Type2 enum attribute values registration
+
+ Type of registration: type2 enum attribute value
+ Name of attribute to which this enum specification is to be added:
+ Keyword symbolic name of this enum value:
+ Numeric value (to be assigned by the IPP Designated Expert in
+ consultation with IANA):
+ Specification of this enum value (follow the style of IPP Model
+ Section 4.1.4):
+ Name of proposer:
+ Address of proposer:
+ Email address of proposer:
+
+ Note: For type2 enums, the Designated Expert will be the point of
+ contact for the approved registration specification, if any
+ maintenance of the registration specification is needed.
+
+11.4 Type3 enum attribute values registration
+
+ Type of registration: type3 enum attribute value
+ Name of attribute to which this enum specification is to be added:
+ Keyword symbolic name of this enum value:
+ Numeric value (to be assigned by the IPP Designated Expert in
+ consultation with IANA):
+ Specification of this enum value (follow the style of IPP Model
+ Section 4.1.4):
+
+
+
+deBry, et al. Experimental [Page 137]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ Name of proposer:
+ Address of proposer:
+ Email address of proposer:
+
+ Note: For type3 enums, the proposer will be the point of contact for
+ the approved registration specification, if any maintenance of the
+ registration specification is needed.
+
+11.5 Attribute registration
+
+ Type of registration: attribute
+ Proposed keyword name of this attribute:
+ Types of attribute (Operation, Job Template, Job Description,
+ Printer Description):
+ Operations to be used with if the attribute is an operation
+ attribute:
+ Object (Job, Printer, etc. if bound to an object):
+ Attribute syntax(es) (include 1setOf and range as in Section 4.2):
+ If attribute syntax is 'keyword' or 'enum', is it type2 or type3:
+ If this is a Printer attribute, MAY the value returned depend on
+ "document-format" (See Section 6.2):
+ If this is a Job Template attribute, how does its specification
+ depend on the value of the "multiple-document-handling" attribute:
+ Specification of this attribute (follow the style of IPP Model
+ Section 4.2):
+ Name of proposer:
+ Address of proposer:
+ Email address of proposer:
+
+ Note: For attributes, the IPP Designated Expert will be the point of
+ contact for the approved registration specification, if any
+ maintenance of the registration specification is needed.
+
+11.6 Attribute Syntax registration
+
+ Type of registration: attribute syntax
+ Proposed name of this attribute syntax:
+ Type of attribute syntax (integer, octetString, character-string,
+ see [RFC2565]):
+ Numeric value (to be assigned by the IPP Designated Expert in
+ consultation with IANA):
+ Specification of this attribute (follow the style of IPP Model
+ Section 4.1):
+ Name of proposer:
+ Address of proposer:
+ Email address of proposer:
+
+
+
+
+
+deBry, et al. Experimental [Page 138]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ Note: For attribute syntaxes, the IPP Designated Expert will be the
+ point of contact for the approved registration specification, if any
+ maintenance of the registration specification is needed.
+
+11.7 Operation registration
+
+ Type of registration: operation
+ Proposed name of this operation:
+ Numeric operation-id value (to be assigned by the IPP Designated
+ Expert in consultation with IANA):
+ Object Target (Job, Printer, etc. that operation is upon):
+ Specification of this attribute (follow the style of IPP Model
+ Section 3):
+ Name of proposer:
+ Address of proposer:
+ Email address of proposer:
+
+ Note: For operations, the IPP Designated Expert will be the point of
+ contact for the approved registration specification, if any
+ maintenance of the registration specification is needed.
+
+11.8 Attribute Group registration
+
+ Type of registration: attribute group
+ Proposed name of this attribute group:
+ Numeric tag according to [RFC2565] (to be assigned by the IPP
+ Designated Expert in consultation with IANA):
+ Operation requests and group number for each operation in which the
+ attribute group occurs:
+ Operation responses and group number for each operation in which the
+ attribute group occurs:
+ Specification of this attribute group (follow the style of IPP Model
+ Section 3):
+ Name of proposer:
+ Address of proposer:
+ Email address of proposer:
+
+ Note: For attribute groups, the IPP Designated Expert will be the
+ point of contact for the approved registration specification, if any
+ maintenance of the registration specification is needed.
+
+11.9 Status code registration
+
+ Type of registration: status code
+ Keyword symbolic name of this status code value:
+ Numeric value (to be assigned by the IPP Designated Expert in
+ consultation with IANA):
+ Operations that this status code may be used with:
+
+
+
+deBry, et al. Experimental [Page 139]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ Specification of this status code (follow the style of IPP Model
+ Section 14 APPENDIX B: Status Codes and Suggested Status Code
+ Messages):
+ Name of proposer:
+ Address of proposer:
+ Email address of proposer:
+
+ Note: For status codes, the Designated Expert will be the point of
+ contact for the approved registration specification, if any
+ maintenance of the registration specification is needed.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+deBry, et al. Experimental [Page 140]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+12. APPENDIX A: Terminology
+
+ This specification uses the terminology defined in this section.
+
+12.1 Conformance Terminology
+
+ The key words "MUST", "MUST NOT", "REQUIRED", "SHOULD", "SHOULD NOT",
+ "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be
+ interpreted as described in RFC 2119 [RFC2119].
+
+12.1.1 NEED NOT
+
+ This term is not included in RFC 2119. The verb "NEED NOT" indicates
+ an action that the subject of the sentence does not have to implement
+ in order to claim conformance to the standard. The verb "NEED NOT"
+ is used instead of "MAY NOT" since "MAY NOT" sounds like a
+ prohibition.
+
+12.2 Model Terminology
+
+12.2.1 Keyword
+
+ Keywords are used within this document as identifiers of semantic
+ entities within the abstract model (see section 4.1.2.3). Attribute
+ names, some attribute values, attribute syntaxes, and attribute group
+ names are represented as keywords.
+
+12.2.2 Attributes
+
+ An attribute is an item of information that is associated with an
+ instance of an IPP object. An attribute consists of an attribute
+ name and one or more attribute values. Each attribute has a specific
+ attribute syntax. All object attributes are defined in section 4 and
+ all operation attributes are defined in section 3.
+
+ Job Template Attributes are described in section 4.2. The client
+ optionally supplies Job Template attributes in a create request
+ (operation requests that create Job objects). The Printer object has
+ associated attributes which define supported and default values for
+ the Printer.
+
+12.2.2.1 Attribute Name
+
+ Each attribute is uniquely identified in this document by its
+ attribute name. An attribute name is a keyword. The keyword
+ attribute name is given in the section header describing that
+
+
+
+
+
+deBry, et al. Experimental [Page 141]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ attribute. In running text in this document, attribute names are
+ indicated inside double quotation marks (") where the quotation marks
+ are not part of the keyword itself.
+
+12.2.2.2 Attribute Group Name
+
+ Related attributes are grouped into named groups. The name of the
+ group is a keyword. The group name may be used in place of naming
+ all the attributes in the group explicitly. Attribute groups are
+ defined in section 3.
+
+12.2.2.3 Attribute Value
+
+ Each attribute has one or more values. Attribute values are
+ represented in the syntax type specified for that attribute. In
+ running text in this document, attribute values are indicated inside
+ single quotation marks ('), whether their attribute syntax is
+ keyword, integer, text, etc. where the quotation marks are not part
+ of the value itself.
+
+12.2.2.4 Attribute Syntax
+
+ Each attribute is defined using an explicit syntax type. In this
+ document, each syntax type is defined as a keyword with specific
+ meaning. The Encoding and Transport document [RFC2565] indicates the
+ actual "on-the-wire" encoding rules for each syntax type. Attribute
+ syntax types are defined in section 4.1.
+
+12.2.3 Supports
+
+ By definition, a Printer object supports an attribute only if that
+ Printer object responds with the corresponding attribute populated
+ with some value(s) in a response to a query for that attribute. A
+ Printer object supports an attribute value if the value is one of the
+ Printer object's "supported values" attributes. The device behind a
+ Printer object may exhibit a behavior that corresponds to some IPP
+ attribute, but if the Printer object, when queried for that
+ attribute, doesn't respond with the attribute, then as far as IPP is
+ concerned, that implementation does not support that feature. If the
+ Printer object's "xxx-supported" attribute is not populated with a
+ particular value (even if that value is a legal value for that
+ attribute), then that Printer object does not support that particular
+ value.
+
+ A conforming implementation MUST support all REQUIRED attributes.
+ However, even for REQUIRED attributes, conformance to IPP does not
+ mandate that all implementations support all possible values
+ representing all possible job processing behaviors and features. For
+
+
+
+deBry, et al. Experimental [Page 142]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ example, if a given instance of a Printer supports only certain
+ document formats, then that Printer responds with the "document-
+ format-supported" attribute populated with a set of values, possibly
+ only one, taken from the entire set of possible values defined for
+ that attribute. This limited set of values represents the Printer's
+ set of supported document formats. Supporting an attribute and some
+ set of values for that attribute enables IPP end users to be aware of
+ and make use of those features associated with that attribute and
+ those values. If an implementation chooses to not support an
+ attribute or some specific value, then IPP end users would have no
+ ability to make use of that feature within the context of IPP itself.
+ However, due to existing practice and legacy systems which are not
+ IPP aware, there might be some other mechanism outside the scope of
+ IPP to control or request the "unsupported" feature (such as embedded
+ instructions within the document data itself).
+
+ For example, consider the "finishings-supported" attribute.
+
+ 1) If a Printer object is not physically capable of stapling, the
+ "finishings-supported" attribute MUST NOT be populated with the
+ value of 'staple'.
+ 2) A Printer object is physically capable of stapling, however an
+ implementation chooses not to support stapling in the IPP
+ "finishings" attribute. In this case, 'staple' MUST NOT be a
+ value in the "finishings-supported" Printer object attribute.
+ Without support for the value 'staple', an IPP end user would
+ have no means within the protocol itself to request that a Job
+ be stapled. However, an existing document data formatter might
+ be able to request that the document be stapled directly with an
+ embedded instruction within the document data. In this case,
+ the IPP implementation does not "support" stapling, however the
+ end user is still able to have some control over the stapling of
+ the completed job.
+ 3) A Printer object is physically capable of stapling, and an
+ implementation chooses to support stapling in the IPP
+ "finishings" attribute. In this case, 'staple' MUST be a value
+ in the "finishings-supported" Printer object attribute. Doing
+ so, would enable end users to be aware of and make use of the
+ stapling feature using IPP attributes.
+
+ Even though support for Job Template attributes by a Printer object
+ is OPTIONAL, it is RECOMMENDED that if the device behind a Printer
+ object is capable of realizing any feature or function that
+ corresponds to an IPP attribute and some associated value, then that
+ implementation SHOULD support that IPP attribute and value.
+
+
+
+
+
+
+deBry, et al. Experimental [Page 143]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ The set of values in any of the supported value attributes is set
+ (populated) by some administrative process or automatic sensing
+ mechanism that is outside the scope of IPP. For administrative
+ policy and control reasons, an administrator may choose to make only
+ a subset of possible values visible to the end user. In this case,
+ the real output device behind the IPP Printer abstraction may be
+ capable of a certain feature, however an administrator is specifying
+ that access to that feature not be exposed to the end user through
+ the IPP protocol. Also, since a Printer object may represent a
+ logical print device (not just a physical device) the actual process
+ for supporting a value is undefined and left up to the
+ implementation. However, if a Printer object supports a value, some
+ manual human action may be needed to realize the semantic action
+ associated with the value, but no end user action is required.
+
+ For example, if one of the values in the "finishings-supported"
+ attribute is 'staple', the actual process might be an automatic
+ staple action by a physical device controlled by some command sent to
+ the device. Or, the actual process of stapling might be a manual
+ action by an operator at an operator attended Printer object.
+
+ For another example of how supported attributes function, consider a
+ system administrator who desires to control all print jobs so that no
+ job sheets are printed in order to conserve paper. To force no job
+ sheets, the system administrator sets the only supported value for
+ the "job-sheets-supported" attribute to 'none'. In this case, if a
+ client requests anything except 'none', the create request is
+ rejected or the "job-sheets" value is ignored (depending on the value
+ of "ipp-attribute-fidelity"). To force the use of job start/end
+ sheets on all jobs, the administrator does not include the value '
+ none' in the "job-sheets-supported" attribute. In this case, if a
+ client requests 'none', the create request is rejected or the "job-
+ sheets" value is ignored (again depending on the value of "ipp-
+ attribute-fidelity").
+
+12.2.4 print-stream page
+
+ A "print-stream page" is a page according to the definition of pages
+ in the language used to express the document data.
+
+12.2.5 impression
+
+ An "impression" is the image (possibly many print-stream pages in
+ different configurations) imposed onto a single media page.
+
+
+
+
+
+
+
+deBry, et al. Experimental [Page 144]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+13. APPENDIX B: Status Codes and Suggested Status Code Messages
+
+ This section defines status code enum keywords and values that are
+ used to provide semantic information on the results of an operation
+ request. Each operation response MUST include a status code. The
+ response MAY also contain a status message that provides a short
+ textual description of the status. The status code is intended for
+ use by automata, and the status message is intended for the human end
+ user. Since the status message is an OPTIONAL component of the
+ operation response, an IPP application (i.e., a browser, GUI, print
+ driver or gateway) is NOT REQUIRED to examine or display the status
+ message, since it MAY not be returned to the application.
+
+ The prefix of the status keyword defines the class of response as
+ follows:
+
+ "informational" - Request received, continuing process
+ "successful" - The action was successfully received, understood,
+ and accepted
+ "redirection" - Further action must be taken in order to complete
+ the request
+ "client-error" - The request contains bad syntax or cannot be
+ fulfilled
+ "server-error" - The IPP object failed to fulfill an apparently
+ valid request
+
+ As with type2 enums, IPP status codes are extensible. IPP clients
+ are NOT REQUIRED to understand the meaning of all registered status
+ codes, though such understanding is obviously desirable. However,
+ IPP clients MUST understand the class of any status code, as
+ indicated by the prefix, and treat any unrecognized response as being
+ equivalent to the first status code of that class, with the exception
+ that an unrecognized response MUST NOT be cached. For example, if an
+ unrecognized status code of "client-error-xxx-yyy" is received by the
+ client, it can safely assume that there was something wrong with its
+ request and treat the response as if it had received a "client-
+ error-bad-request" status code. In such cases, IPP applications
+ SHOULD present the OPTIONAL message (if present) to the end user
+ since the message is likely to contain human readable information
+ which will help to explain the unusual status. The name of the enum
+ is the suggested status message for US English.
+
+ The status code values range from 0x0000 to 0x7FFF. The value ranges
+ for each status code class are as follows:
+
+ "successful" - 0x0000 to 0x00FF
+ "informational" - 0x0100 to 0x01FF
+ "redirection" - 0x0200 to 0x02FF
+
+
+
+deBry, et al. Experimental [Page 145]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ "client-error" - 0x0400 to 0x04FF
+ "server-error" - 0x0500 to 0x05FF
+
+ The top half (128 values) of each range (0x0n40 to 0x0nFF, for n = 0
+ to 5) is reserved for private use within each status code class.
+ Values 0x0600 to 0x7FFF are reserved for future assignment and MUST
+ NOT be used.
+
+13.1 Status Codes
+
+ Each status code is described below. Section 13.1.5.9 contains a
+ table that indicates which status codes apply to which operations.
+ The Implementer's Guide [ipp-iig] describe the suggested steps for
+ processing IPP attributes for all operations, including returning
+ status codes.
+
+13.1.1 Informational
+
+ This class of status code indicates a provisional response and is to
+ be used for informational purposes only.
+
+ There are no status codes defined in IPP/1.0 for this class of status
+ code.
+
+13.1.2 Successful Status Codes
+
+ This class of status code indicates that the client's request was
+ successfully received, understood, and accepted.
+
+13.1.2.1 successful-ok (0x0000)
+
+ The request has succeeded and no request attributes were substituted
+ or ignored. In the case of a response to a create request, the '
+ successful-ok' status code indicates that the request was
+ successfully received and validated, and that the Job object has been
+ created; it does not indicate that the job has been processed. The
+ transition of the Job object into the 'completed' state is the only
+ indicator that the job has been printed.
+
+13.1.2.2 successful-ok-ignored-or-substituted-attributes (0x0001)
+
+ The request has succeeded, but some supplied (1) attributes were
+ ignored or (2) unsupported values were substituted with supported
+ values or were ignored in order to perform the operation without
+ rejecting it. Unsupported attributes, attribute syntaxes, or values
+ MUST be returned in the Unsupported Attributes group of the response
+ for all operations. There is an exception to this rule for the query
+ operations: Get-Printer-Attributes, Get-Jobs, and Get-Job-Attributes
+
+
+
+deBry, et al. Experimental [Page 146]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ for the "requested-attributes" operation attribute only. When the
+ supplied values of the "requested-attributes" operation attribute are
+ requesting attributes that are not supported, the IPP object MAY, but
+ is NOT REQUIRED to, return the "requested-attributes" attribute in
+ the Unsupported Attribute response group (with the unsupported values
+ only). See section 3.2.1.2.
+
+13.1.2.3 successful-ok-conflicting-attributes (0x0002)
+
+ The request has succeeded, but some supplied attribute values
+ conflicted with the values of other supplied attributes. These
+ conflicting values were either (1) substituted with (supported)
+ values or (2) the attributes were removed in order to process the job
+ without rejecting it. Attributes or values which conflict with other
+ attributes and have been substituted or ignored MUST be returned in
+ the Unsupported Attributes group of the response for all operations
+ as supplied by the client. See section 3.2.1.2.
+
+13.1.3 Redirection Status Codes
+
+ This class of status code indicates that further action needs to be
+ taken to fulfill the request.
+
+ There are no status codes defined in IPP/1.0 for this class of status
+ code.
+
+13.1.4 Client Error Status Codes
+
+ This class of status code is intended for cases in which the client
+ seems to have erred. The IPP object SHOULD return a message
+ containing an explanation of the error situation and whether it is a
+ temporary or permanent condition.
+
+13.1.4.1 client-error-bad-request (0x0400)
+
+ The request could not be understood by the IPP object due to
+ malformed syntax (such as the value of a fixed length attribute whose
+ length does not match the prescribed length for that attribute - see
+ the Implementer's Guide [ipp-iig] ). The IPP application SHOULD NOT
+ repeat the request without modifications.
+
+13.1.4.2 client-error-forbidden (0x0401)
+
+ The IPP object understood the request, but is refusing to fulfill it.
+ Additional authentication information or authorization credentials
+ will not help and the request SHOULD NOT be repeated. This status
+
+
+
+
+
+deBry, et al. Experimental [Page 147]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ code is commonly used when the IPP object does not wish to reveal
+ exactly why the request has been refused or when no other response is
+ applicable.
+
+13.1.4.3 client-error-not-authenticated (0x0402)
+
+ The request requires user authentication. The IPP client may repeat
+ the request with suitable authentication information. If the request
+ already included authentication information, then this status code
+ indicates that authorization has been refused for those credentials.
+ If this response contains the same challenge as the prior response,
+ and the user agent has already attempted authentication at least
+ once, then the response message may contain relevant diagnostic
+ information. This status codes reveals more information than
+ "client-error-forbidden".
+
+13.1.4.4 client-error-not-authorized (0x0403)
+
+ The requester is not authorized to perform the request. Additional
+ authentication information or authorization credentials will not help
+ and the request SHOULD NOT be repeated. This status code is used
+ when the IPP object wishes to reveal that the authentication
+ information is understandable, however, the requester is explicitly
+ not authorized to perform the request. This status codes reveals
+ more information than "client-error-forbidden" and "client-error-
+ not-authenticated".
+
+13.1.4.5 client-error-not-possible (0x0404)
+
+ This status code is used when the request is for something that can
+ not happen. For example, there might be a request to cancel a job
+ that has already been canceled or aborted by the system. The IPP
+ client SHOULD NOT repeat the request.
+
+13.1.4.6 client-error-timeout (0x0405)
+
+ The client did not produce a request within the time that the IPP
+ object was prepared to wait. For example, a client issued a Create-
+ Job operation and then, after a long period of time, issued a Send-
+ Document operation and this error status code was returned in
+ response to the Send-Document request (see section 3.3.1). The IPP
+ object might have been forced to clean up resources that had been
+ held for the waiting additional Documents. The IPP object was forced
+ to close the Job since the client took too long. The client SHOULD
+ NOT repeat the request without modifications.
+
+
+
+
+
+
+deBry, et al. Experimental [Page 148]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+13.1.4.7 client-error-not-found (0x0406)
+
+ The IPP object has not found anything matching the request URI. No
+ indication is given of whether the condition is temporary or
+ permanent. For example, a client with an old reference to a Job (a
+ URI) tries to cancel the Job, however in the mean time the Job might
+ have been completed and all record of it at the Printer has been
+ deleted. This status code, 'client-error-not-found' is returned
+ indicating that the referenced Job can not be found. This error
+ status code is also used when a client supplies a URI as a reference
+ to the document data in either a Print-URI or Send-URI operation, but
+ the document can not be found.
+
+ In practice, an IPP application should avoid a not found situation by
+ first querying and presenting a list of valid Printer URIs and Job
+ URIs to the end-user.
+
+13.1.4.8 client-error-gone (0x0407)
+
+ The requested object is no longer available and no forwarding address
+ is known. This condition should be considered permanent. Clients
+ with link editing capabilities should delete references to the
+ request URI after user approval. If the IPP object does not know or
+ has no facility to determine, whether or not the condition is
+ permanent, the status code "client-error-not-found" should be used
+ instead.
+
+ This response is primarily intended to assist the task of maintenance
+ by notifying the recipient that the resource is intentionally
+ unavailable and that the IPP object administrator desires that remote
+ links to that resource be removed. It is not necessary to mark all
+ permanently unavailable resources as "gone" or to keep the mark for
+ any length of time -- that is left to the discretion of the IPP
+ object administrator.
+
+13.1.4.9 client-error-request-entity-too-large (0x0408)
+
+ The IPP object is refusing to process a request because the request
+ entity is larger than the IPP object is willing or able to process.
+ An IPP Printer returns this status code when it limits the size of
+ print jobs and it receives a print job that exceeds that limit or
+ when the attributes are so many that their encoding causes the
+ request entity to exceed IPP object capacity.
+
+
+
+
+
+
+
+
+deBry, et al. Experimental [Page 149]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+13.1.4.10 client-error-request-value-too-long (0x0409)
+
+ The IPP object is refusing to service the request because one or more
+ of the client-supplied attributes has a variable length value that is
+ longer than the maximum length specified for that attribute. The IPP
+ object might not have sufficient resources (memory, buffers, etc.) to
+ process (even temporarily), interpret, and/or ignore a value larger
+ than the maximum length. Another use of this error code is when the
+ IPP object supports the processing of a large value that is less than
+ the maximum length, but during the processing of the request as a
+ whole, the object may pass the value onto some other system component
+ which is not able to accept the large value. For more details, see
+ the Implementer's Guide [ipp-iig] .
+
+ Note: For attribute values that are URIs, this rare condition is
+ only likely to occur when a client has improperly submitted a request
+ with long query information (e.g. an IPP application allows an end-
+ user to enter an invalid URI), when the client has descended into a
+ URI "black hole" of redirection (e.g., a redirected URI prefix that
+ points to a suffix of itself), or when the IPP object is under attack
+ by a client attempting to exploit security holes present in some IPP
+ objects using fixed-length buffers for reading or manipulating the
+ Request-URI.
+
+13.1.4.11 client-error-document-format-not-supported (0x040A)
+
+ The IPP object is refusing to service the request because the
+ document data is in a format, as specified in the "document-format"
+ operation attribute, that is not supported by the Printer object.
+ This error is returned independent of the client-supplied "ipp-
+ attribute-fidelity". The Printer object MUST return this status
+ code, even if there are other attributes that are not supported as
+ well, since this error is a bigger problem than with Job Template
+ attributes.
+
+13.1.4.12 client-error-attributes-or-values-not-supported (0x040B)
+
+ In a create request, if the Printer object does not support one or
+ more attributes, attribute syntaxes, or attribute values supplied in
+ the request and the client supplied the "ipp-attributes-fidelity"
+ operation attribute with the 'true' value, the Printer object MUST
+ return this status code. For example, if the request indicates '
+ iso-a4' media, but that media type is not supported by the Printer
+ object. Or, if the client supplies an optional attribute and the
+ attribute itself is not even supported by the Printer. If the "ipp-
+ attribute-fidelity" attribute is 'false', the Printer MUST ignore or
+ substitute values for unsupported attributes and values rather than
+ reject the request and return this status code.
+
+
+
+deBry, et al. Experimental [Page 150]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ For any operation where a client requests attributes (such as a Get-
+ Jobs, Get-Printer-Attributes, or Get-Job-Attributes operation), if
+ the IPP object does not support one or more of the requested
+ attributes, the IPP object simply ignores the unsupported requested
+ attributes and processes the request as if they had not been
+ supplied, rather than returning this status code. In this case, the
+ IPP object MUST return the 'successful-ok-ignored-or-substituted-
+ attributes' status code and MAY return the unsupported attributes as
+ values of the "requested-attributes" in the Unsupported Attributes
+ Group (see section 13.1.2.2).
+
+13.1.4.13 client-error-uri-scheme-not-supported (0x040C)
+
+ The type of the client supplied URI in a Print-URI or a Send-URI
+ operation is not supported.
+
+13.1.4.14 client-error-charset-not-supported (0x040D)
+
+ For any operation, if the IPP Printer does not support the charset
+ supplied by the client in the "attributes-charset" operation
+ attribute, the Printer MUST reject the operation and return this
+ status and any 'text' or 'name' attributes using the 'utf-8' charset
+ (see Section 3.1.4.1).
+
+13.1.4.15 client-error-conflicting-attributes (0x040E)
+
+ The request is rejected because some attribute values conflicted with
+ the values of other attributes which this specification does not
+ permit to be substituted or ignored.
+
+13.1.5 Server Error Status Codes
+
+ This class of status codes indicates cases in which the IPP object is
+ aware that it has erred or is incapable of performing the request.
+ The IPP object SHOULD include a message containing an explanation of
+ the error situation, and whether it is a temporary or permanent
+ condition.
+
+13.1.5.1 server-error-internal-error (0x0500)
+
+ The IPP object encountered an unexpected condition that prevented it
+ from fulfilling the request. This error status code differs from
+ "server-error-temporary-error" in that it implies a more permanent
+ type of internal error. It also differs from "server-error-device-
+ error" in that it implies an unexpected condition (unlike a paper-jam
+ or out-of-toner problem which is undesirable but expected). This
+ error status code indicates that probably some knowledgeable human
+ intervention is required.
+
+
+
+deBry, et al. Experimental [Page 151]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+13.1.5.2 server-error-operation-not-supported (0x0501)
+
+ The IPP object does not support the functionality required to fulfill
+ the request. This is the appropriate response when the IPP object
+ does not recognize an operation or is not capable of supporting it.
+
+13.1.5.3 server-error-service-unavailable (0x0502)
+
+ The IPP object is currently unable to handle the request due to a
+ temporary overloading or maintenance of the IPP object. The
+ implication is that this is a temporary condition which will be
+ alleviated after some delay. If known, the length of the delay may be
+ indicated in the message. If no delay is given, the IPP application
+ should handle the response as it would for a "server-error-
+ temporary-error" response. If the condition is more permanent, the
+ error status codes "client-error-gone" or "client-error-not-found"
+ could be used.
+
+13.1.5.4 server-error-version-not-supported (0x0503)
+
+ The IPP object does not support, or refuses to support, the IPP
+ protocol version that was used in the request message. The IPP
+ object is indicating that it is unable or unwilling to complete the
+ request using the same version as supplied in the request other than
+ with this error message. The response should contain a Message
+ describing why that version is not supported and what other versions
+ are supported by that IPP object.
+
+ A conforming IPP/1.0 client MUST specify the valid version ('1.0') on
+ each request. A conforming IPP/1.0 object MUST NOT return this
+ status code to a conforming IPP/1.0 client. An IPP object MUST
+ return this status code to a non-conforming IPP client. The response
+ MUST identify in the "version-number" operation attribute the closest
+ version number that the IPP object does support.
+
+13.1.5.5 server-error-device-error (0x0504)
+
+ A printer error, such as a paper jam, occurs while the IPP object
+ processes a Print or Send operation. The response contains the true
+ Job Status (the values of the "job-state" and "job-state-reasons"
+ attributes). Additional information can be returned in the optional
+ "job-state-message" attribute value or in the OPTIONAL status message
+ that describes the error in more detail. This error status code is
+ only returned in situations where the Printer is unable to accept the
+ create request because of such a device error. For example, if the
+ Printer is unable to spool, and can only accept one job at a time,
+ the reason it might reject a create request is that the printer
+ currently has a paper jam. In many cases however, where the Printer
+
+
+
+deBry, et al. Experimental [Page 152]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ object can accept the request even though the Printer has some error
+ condition, the 'successful-ok' status code will be returned. In such
+ a case, the client would look at the returned Job Object Attributes
+ or later query the Printer to determine its state and state reasons.
+
+13.1.5.6 server-error-temporary-error (0x0505)
+
+ A temporary error such as a buffer full write error, a memory
+ overflow (i.e. the document data exceeds the memory of the Printer),
+ or a disk full condition, occurs while the IPP Printer processes an
+ operation. The client MAY try the unmodified request again at some
+ later point in time with an expectation that the temporary internal
+ error condition may have been cleared. Alternatively, as an
+ implementation option, a Printer object MAY delay the response until
+ the temporary condition is cleared so that no error is returned.
+
+13.1.5.7 server-error-not-accepting-jobs (0x0506)
+
+ A temporary error indicating that the Printer is not currently
+ accepting jobs, because the administrator has set the value of the
+ Printer's "printer-is-not-accepting-jobs" attribute to 'false' (by
+ means outside of IPP/1.0).
+
+13.1.5.8 server-error-busy (0x0507)
+
+ A temporary error indicating that the Printer is too busy processing
+ jobs and/or other requests. The client SHOULD try the unmodified
+ request again at some later point in time with an expectation that
+ the temporary busy condition will have been cleared.
+
+13.1.5.9 server-error-job-canceled (0x0508)
+
+ An error indicating that the job has been canceled by an operator or
+ the system while the client was transmitting the data to the IPP
+ Printer. If a job-id and job-uri had been created, then they are
+ returned in the Print-Job, Send-Document, or Send-URI response as
+ usual; otherwise, no job-id and job-uri are returned in the response.
+
+13.2 Status Codes for IPP Operations
+
+ PJ = Print-Job, PU = Print-URI, CJ = Create-Job, SD = Send-Document
+ SU = Send-URI, V = Validate-Job, GA = Get-Job-Attributes and
+ Get-Printer-Attributes, GJ = Get-Jobs, C = Cancel-Job
+
+
+
+
+
+
+
+
+deBry, et al. Experimental [Page 153]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ IPP Operations
+ IPP Status Keyword PJ PU CJ SD SU V GA GJ C
+ ------------------ -- -- -- -- -- - -- -- -
+ successful-ok x x x x x x x x x
+ successful-ok-ignored-or-substituted- x x x x x x x x x
+ attributes
+ successful-ok-conflicting-attributes x x x x x x x x x
+ client-error-bad-request x x x x x x x x x
+ client-error-forbidden x x x x x x x x x
+ client-error-not-authenticated x x x x x x x x x
+ client-error-not-authorized x x x x x x x x x
+ client-error-not-possible x x x x x x x x x
+ client-error-timeout x x
+ client-error-not-found x x x x x x x x x
+ client-error-gone x x x x x x x x x
+ client-error-request-entity-too-large x x x x x x x x x
+ client-error-request-value-too-long x x x x x x x x x
+ client-error-document-format-not- x x x x x x
+ supported
+ client-error-attributes-or-values-not- x x x x x x x x x
+ supported
+ client-error-uri-scheme-not-supported x x
+ client-error-charset-not-supported x x x x x x x x x
+ client-error-conflicting-attributes x x x x x x x x x
+ server-error-internal-error x x x x x x x x x
+ server-error-operation-not-supported x x x x
+ server-error-service-unavailable x x x x x x x x x
+ server-error-version-not-supported x x x x x x x x x
+ server-error-device-error x x x x x
+ server-error-temporary-error x x x x x
+ server-error-not-accepting-jobs x x x x
+ server-error-busy x x x x x x x x x
+ server-error-job-canceled x x
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+deBry, et al. Experimental [Page 154]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+14. APPENDIX C: "media" keyword values
+
+ Standard keyword values are taken from several sources.
+
+ Standard values are defined (taken from DPA[ISO10175] and the Printer
+ MIB[RFC1759]):
+
+ 'default': The default medium for the output device
+ 'iso-a4-white': Specifies the ISO A4 white medium
+ 'iso-a4-colored': Specifies the ISO A4 colored medium
+ 'iso-a4-transparent' Specifies the ISO A4 transparent medium
+ 'iso-a3-white': Specifies the ISO A3 white medium
+ 'iso-a3-colored': Specifies the ISO A3 colored medium
+ 'iso-a5-white': Specifies the ISO A5 white medium
+ 'iso-a5-colored': Specifies the ISO A5 colored medium
+ 'iso-b4-white': Specifies the ISO B4 white medium
+ 'iso-b4-colored': Specifies the ISO B4 colored medium
+ 'iso-b5-white': Specifies the ISO B5 white medium
+ 'iso-b5-colored': Specifies the ISO B5 colored medium
+ 'jis-b4-white': Specifies the JIS B4 white medium
+ 'jis-b4-colored': Specifies the JIS B4 colored medium
+ 'jis-b5-white': Specifies the JIS B5 white medium
+ 'jis-b5-colored': Specifies the JIS B5 colored medium
+
+ The following standard values are defined for North American media:
+
+ 'na-letter-white': Specifies the North American letter white medium
+ 'na-letter-colored': Specifies the North American letter colored
+ medium
+ 'na-letter-transparent': Specifies the North American letter
+ transparent medium
+ 'na-legal-white': Specifies the North American legal white medium
+ 'na-legal-colored': Specifies the North American legal colored
+ medium
+
+ The following standard values are defined for envelopes:
+
+ 'iso-b4-envelope': Specifies the ISO B4 envelope medium
+ 'iso-b5-envelope': Specifies the ISO B5 envelope medium
+ 'iso-c3-envelope': Specifies the ISO C3 envelope medium
+ 'iso-c4-envelope': Specifies the ISO C4 envelope medium
+ 'iso-c5-envelope': Specifies the ISO C5 envelope medium
+ 'iso-c6-envelope': Specifies the ISO C6 envelope medium
+ 'iso-designated-long-envelope': Specifies the ISO Designated Long
+ envelope medium
+ 'na-10x13-envelope': Specifies the North American 10x13 envelope
+ medium
+
+
+
+
+deBry, et al. Experimental [Page 155]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ 'na-9x12-envelope': Specifies the North American 9x12 envelope
+ medium
+ 'monarch-envelope': Specifies the Monarch envelope
+ 'na-number-10-envelope': Specifies the North American number 10
+ business envelope medium
+ 'na-7x9-envelope': Specifies the North American 7x9 inch envelope
+ 'na-9x11-envelope': Specifies the North American 9x11 inch envelope
+ 'na-10x14-envelope': Specifies the North American 10x14 inch
+ envelope
+ 'na-number-9-envelope': Specifies the North American number 9
+ business envelope
+ 'na-6x9-envelope': Specifies the North American 6x9 inch envelope
+ 'na-10x15-envelope': Specifies the North American 10x15 inch
+ envelope
+
+ The following standard values are defined for the less commonly used
+ media (white-only):
+
+ 'executive-white': Specifies the white executive medium
+ 'folio-white': Specifies the folio white medium
+ 'invoice-white': Specifies the white invoice medium
+ 'ledger-white': Specifies the white ledger medium
+ 'quarto-white': Specified the white quarto medium
+ 'iso-a0-white': Specifies the ISO A0 white medium
+ 'iso-a1-white': Specifies the ISO A1 white medium
+ 'iso-a2-white': Specifies the ISO A2 white medium
+ 'iso-a6-white': Specifies the ISO A6 white medium
+ 'iso-a7-white': Specifies the ISO A7 white medium
+ 'iso-a8-white': Specifies the ISO A8 white medium
+ 'iso-a9-white': Specifies the ISO A9 white medium
+ 'iso-10-white': Specifies the ISO A10 white medium
+ 'iso-b0-white': Specifies the ISO B0 white medium
+ 'iso-b1-white': Specifies the ISO B1 white medium
+ 'iso-b2-white': Specifies the ISO B2 white medium
+ 'iso-b3-white': Specifies the ISO B3 white medium
+ 'iso-b6-white': Specifies the ISO B6 white medium
+ 'iso-b7-white': Specifies the ISO B7 white medium
+ 'iso-b8-white': Specifies the ISO B8 white medium
+ 'iso-b9-white': Specifies the ISO B9 white medium
+ 'iso-b10-white': Specifies the ISO B10 white medium
+ 'jis-b0-white': Specifies the JIS B0 white medium
+ 'jis-b1-white': Specifies the JIS B1 white medium
+ 'jis-b2-white': Specifies the JIS B2 white medium
+ 'jis-b3-white': Specifies the JIS B3 white medium
+ 'jis-b6-white': Specifies the JIS B6 white medium
+ 'jis-b7-white': Specifies the JIS B7 white medium
+
+
+
+
+
+deBry, et al. Experimental [Page 156]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ 'jis-b8-white': Specifies the JIS B8 white medium
+ 'jis-b9-white': Specifies the JIS B9 white medium
+ 'jis-b10-white': Specifies the JIS B10 white medium
+
+
+ The following standard values are defined for engineering media:
+
+ 'a': Specifies the engineering A size medium
+ 'b': Specifies the engineering B size medium
+ 'c': Specifies the engineering C size medium
+ 'd': Specifies the engineering D size medium
+ 'e': Specifies the engineering E size medium
+
+
+ The following standard values are defined for input-trays (from ISO
+ DPA and the Printer MIB):
+
+ 'top': The top input tray in the printer.
+ 'middle': The middle input tray in the printer.
+ 'bottom': The bottom input tray in the printer.
+ 'envelope': The envelope input tray in the printer.
+ 'manual': The manual feed input tray in the printer.
+ 'large-capacity': The large capacity input tray in the printer.
+ 'main': The main input tray
+ 'side': The side input tray
+
+
+ The following standard values are defined for media sizes (from ISO
+ DPA):
+
+ 'iso-a0': Specifies the ISO A0 size: 841 mm by 1189 mm as defined
+ in ISO 216
+ 'iso-a1': Specifies the ISO A1 size: 594 mm by 841 mm as defined in
+ ISO 216
+ 'iso-a2': Specifies the ISO A2 size: 420 mm by 594 mm as defined in
+ ISO 216
+ 'iso-a3': Specifies the ISO A3 size: 297 mm by 420 mm as defined in
+ ISO 216
+ 'iso-a4': Specifies the ISO A4 size: 210 mm by 297 mm as defined in
+ ISO 216
+ 'iso-a5': Specifies the ISO A5 size: 148 mm by 210 mm as defined in
+ ISO 216
+ 'iso-a6': Specifies the ISO A6 size: 105 mm by 148 mm as defined in
+ ISO 216
+ 'iso-a7': Specifies the ISO A7 size: 74 mm by 105 mm as defined in
+ ISO 216
+ 'iso-a8': Specifies the ISO A8 size: 52 mm by 74 mm as defined in
+ ISO 216
+
+
+
+deBry, et al. Experimental [Page 157]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ 'iso-a9': Specifies the ISO A9 size: 37 mm by 52 mm as defined in
+ ISO 216
+ 'iso-a10': Specifies the ISO A10 size: 26 mm by 37 mm as defined in
+ ISO 216
+ 'iso-b0': Specifies the ISO B0 size: 1000 mm by 1414 mm as defined
+ in ISO 216
+ 'iso-b1': Specifies the ISO B1 size: 707 mm by 1000 mm as defined
+ in ISO 216
+ 'iso-b2': Specifies the ISO B2 size: 500 mm by 707 mm as defined in
+ ISO 216
+ 'iso-b3': Specifies the ISO B3 size: 353 mm by 500 mm as defined in
+ ISO 216
+ 'iso-b4': Specifies the ISO B4 size: 250 mm by 353 mm as defined in
+ ISO 216
+ 'iso-b5': Specifies the ISO B5 size: 176 mm by 250 mm as defined in
+ ISO 216
+ 'iso-b6': Specifies the ISO B6 size: 125 mm by 176 mm as defined in
+ ISO 216
+ 'iso-b7': Specifies the ISO B7 size: 88 mm by 125 mm as defined in
+ ISO 216
+ 'iso-b8': Specifies the ISO B8 size: 62 mm by 88 mm as defined in
+ ISO 216
+ 'iso-b9': Specifies the ISO B9 size: 44 mm by 62 mm as defined in
+ ISO 216
+ 'iso-b10': Specifies the ISO B10 size: 31 mm by 44 mm as defined in
+ ISO 216
+ 'na-letter': Specifies the North American letter size: 8.5 inches by
+ 11 inches
+ 'na-legal': Specifies the North American legal size: 8.5 inches by
+ 14 inches
+ 'executive': Specifies the executive size (7.25 X 10.5 in)
+ 'folio': Specifies the folio size (8.5 X 13 in)
+ 'invoice': Specifies the invoice size (5.5 X 8.5 in)
+ 'ledger': Specifies the ledger size (11 X 17 in)
+ 'quarto': Specifies the quarto size (8.5 X 10.83 in)
+ 'iso-c3': Specifies the ISO C3 size: 324 mm by 458 mm as defined in
+ ISO 269
+ 'iso-c4': Specifies the ISO C4 size: 229 mm by 324 mm as defined in
+ ISO 269
+ 'iso-c5': Specifies the ISO C5 size: 162 mm by 229 mm as defined in
+ ISO 269
+ 'iso-c6': Specifies the ISO C6 size: 114 mm by 162 mm as defined in
+ ISO 269
+ 'iso-designated-long': Specifies the ISO Designated Long size: 110
+ mm by 220 mm as defined in ISO 269
+ 'na-10x13-envelope': Specifies the North American 10x13 size: 10
+ inches by 13 inches
+
+
+
+
+deBry, et al. Experimental [Page 158]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ 'na-9x12-envelope': Specifies the North American 9x12 size: 9
+ inches by 12 inches
+ 'na-number-10-envelope': Specifies the North American number 10
+ business envelope size: 4.125 inches by 9.5 inches
+ 'na-7x9-envelope': Specifies the North American 7x9 inch envelope
+ size
+ 'na-9x11-envelope': Specifies the North American 9x11 inch envelope
+ size
+ 'na-10x14-envelope': Specifies the North American 10x14 inch
+ envelope size
+ 'na-number-9-envelope': Specifies the North American number 9
+ business envelope size
+ 'na-6x9-envelope': Specifies the North American 6x9 envelope size
+ 'na-10x15-envelope': Specifies the North American 10x15 envelope
+ size
+ 'monarch-envelope': Specifies the Monarch envelope size (3.87 x 7.5
+ in)
+ 'jis-b0': Specifies the JIS B0 size: 1030mm x 1456mm
+ 'jis-b1': Specifies the JIS B1 size: 728mm x 1030mm
+ 'jis-b2': Specifies the JIS B2 size: 515mm x 728mm
+ 'jis-b3': Specifies the JIS B3 size: 364mm x 515mm
+ 'jis-b4': Specifies the JIS B4 size: 257mm x 364mm
+ 'jis-b5': Specifies the JIS B5 size: 182mm x 257mm
+ 'jis-b6': Specifies the JIS B6 size: 128mm x 182mm
+ 'jis-b7': Specifies the JIS B7 size: 91mm x 128mm
+ 'jis-b8': Specifies the JIS B8 size: 64mm x 91mm
+ 'jis-b9': Specifies the JIS B9 size: 45mm x 64mm
+ 'jis-b10': Specifies the JIS B10 size: 32mm x 45mm
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+deBry, et al. Experimental [Page 159]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+15. APPENDIX D: Processing IPP Attributes
+
+ When submitting a print job to a Printer object, the IPP model allows
+ a client to supply operation and Job Template attributes along with
+ the document data. These Job Template attributes in the create
+ request affect the rendering, production and finishing of the
+ documents in the job. Similar types of instructions may also be
+ contained in the document to be printed, that is, embedded within the
+ print data itself. In addition, the Printer has a set of attributes
+ that describe what rendering and finishing options which are
+ supported by that Printer. This model, which allows for flexibility
+ and power, also introduces the potential that at job submission time,
+ these client-supplied attributes may conflict with either:
+
+ - what the implementation is capable of realizing (i.e., what the
+ Printer supports), as well as
+ - the instructions embedded within the print data itself.
+
+ The following sections describe how these two types of conflicts are
+ handled in the IPP model.
+
+15.1 Fidelity
+
+ If there is a conflict between what the client requests and what a
+ Printer object supports, the client may request one of two possible
+ conflict handling mechanisms:
+
+ 1) either reject the job since the job can not be processed exactly
+ as specified, or
+ 2) allow the Printer to make any changes necessary to proceed with
+ processing the Job the best it can.
+
+ In the first case the client is indicating to the Printer object:
+ "Print the job exactly as specified with no exceptions, and if that
+ can't be done, don't even bother printing the job at all." In the
+ second case, the client is indicating to the Printer object: "It is
+ more important to make sure the job is printed rather than be
+ processed exactly as specified; just make sure the job is printed
+ even if client supplied attributes need to be changed or ignored."
+
+ The IPP model accounts for this situation by introducing an "ipp-
+ attribute-fidelity" attribute.
+
+ In a create request, "ipp-attribute-fidelity" is a boolean operation
+ attribute that is OPTIONALLY supplied by the client. The value '
+ true' indicates that total fidelity to client supplied Job Template
+ attributes and values is required. The client is requesting that the
+ Job be printed exactly as specified, and if that is not possible then
+
+
+
+deBry, et al. Experimental [Page 160]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ the job MUST be rejected rather than processed incorrectly. The
+ value 'false' indicates that a reasonable attempt to print the Job is
+ acceptable. If a Printer does not support some of the client
+ supplied Job Template attributes or values, the Printer MUST ignore
+ them or substitute any supported value for unsupported values,
+ respectively. The Printer may choose to substitute the default value
+ associated with that attribute, or use some other supported value
+ that is similar to the unsupported requested value. For example, if
+ a client supplies a "media" value of 'na-letter', the Printer may
+ choose to substitute 'iso-a4' rather than a default value of '
+ envelope'. If the client does not supply the "ipp-attribute-fidelity"
+ attribute, the Printer assumes a value of 'false'.
+
+ Each Printer implementation MUST support both types of "fidelity"
+ printing (that is whether the client supplies a value of 'true' or '
+ false'):
+
+ - If the client supplies 'false' or does not supply the attribute,
+ the Printer object MUST always accept the request by ignoring
+ unsupported Job Template attributes and by substituting
+ unsupported values of supported Job Template attributes with
+ supported values.
+ - If the client supplies 'true', the Printer object MUST reject the
+ request if the client supplies unsupported Job Template
+ attributes.
+
+ Since a client can always query a Printer to find out exactly what is
+ and is not supported, "ipp-attribute-fidelity" set to 'false' is
+ useful when:
+
+ 1) The End-User uses a command line interface to request attributes
+ that might not be supported.
+ 2) In a GUI context, if the End User expects the job might be moved
+ to another printer and prefers a sub-optimal result to nothing
+ at all.
+ 3) The End User just wants something reasonable in lieu of nothing
+ at all.
+
+15.2 Page Description Language (PDL) Override
+
+ If there is a conflict between the value of an IPP Job Template
+ attribute and a corresponding instruction in the document data, the
+ value of the IPP attribute SHOULD take precedence over the document
+ instruction. Consider the case where a previously formatted file of
+ document data is sent to an IPP Printer. In this case, if the client
+ supplies any attributes at job submission time, the client desires
+ that those attributes override the embedded instructions. Consider
+ the case were a previously formatted document has embedded in it
+
+
+
+deBry, et al. Experimental [Page 161]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ commands to load 'iso-a4' media. However, the document is passed to
+ an end user that only has access to a printer with 'na-letter' media
+ loaded. That end user most likely wants to submit that document to
+ an IPP Printer with the "media" Job Template attribute set to 'na-
+ letter'. The job submission attribute should take precedence over
+ the embedded PDL instruction. However, until companies that supply
+ document data interpreters allow a way for external IPP attributes to
+ take precedence over embedded job production instructions, a Printer
+ might not be able to support the semantics that IPP attributes
+ override the embedded instructions.
+
+ The IPP model accounts for this situation by introducing a "pdl-
+ override-supported" attribute that describes the Printer objects
+ capabilities to override instructions embedded in the PDL data
+ stream. The value of the "pdl-override-supported" attribute is
+ configured by means outside IPP/1.0.
+
+ This REQUIRED Printer attribute takes on the following values:
+
+ - 'attempted': This value indicates that the Printer object
+ attempts to make the IPP attribute values take precedence over
+ embedded instructions in the document data, however there is no
+ guarantee.
+ - 'not-attempted': This value indicates that the Printer object
+ makes no attempt to make the IPP attribute values take precedence
+ over embedded instructions in the document data.
+
+ At job processing time, an implementation that supports the value of
+ 'attempted' might do one of several different actions:
+
+ 1) Generate an output device specific command sequence to realize
+ the feature represented by the IPP attribute value.
+ 2) Parse the document data itself and replace the conflicting
+ embedded instruction with a new embedded instruction that
+ matches the intent of the IPP attribute value.
+ 3) Indicate to the Printer that external supplied attributes take
+ precedence over embedded instructions and then pass the external
+ IPP attribute values to the document data interpreter.
+ 4) Anything else that allows for the semantics that IPP attributes
+ override embedded document data instructions.
+
+ Since 'attempted' does not offer any type of guarantee, even though a
+ given Printer object might not do a very "good" job of attempting to
+ ensure that IPP attributes take a higher precedence over instructions
+ embedded in the document data, it would still be a conforming
+ implementation.
+
+
+
+
+
+deBry, et al. Experimental [Page 162]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ At job processing time, an implementation that supports the value of
+ 'not-attempted' might do one of the following actions:
+
+ 1) Simply pre-pend the document data with the PDL instruction that
+ corresponds to the client-supplied PDL attribute, such that if
+ the document data also has the same PDL instruction, it will
+ override what the Printer object pre-pended. In other words,
+ this implementation is using the same implementation semantics
+ for the client-supplied IPP attributes as for the Printer object
+ defaults.
+ 2) Parse the document data and replace the conflicting embedded
+ instruction with a new embedded instruction that approximates,
+ but does not match, the semantic intent of the IPP attribute
+ value.
+
+ Note: The "ipp-attribute-fidelity" attribute applies to the
+ Printer's ability to either accept or reject other unsupported Job
+ Template attributes. In other words, if "ipp-attribute-fidelity" is
+ set to 'true', a Job is accepted if and only if the client supplied
+ Job Template attributes and values are supported by the Printer.
+ Whether these attributes actually affect the processing of the Job
+ when the document data contains embedded instructions depends on the
+ ability of the Printer to override the instructions embedded in the
+ document data with the semantics of the IPP attributes. If the
+ document data attributes can be overridden ("pdl-override-supported"
+ set to 'attempted'), the Printer makes an attempt to use the IPP
+ attributes when processing the Job. If the document data attributes
+ can not be overridden ("pdl-override-supported" set to 'not-
+ attempted'), the Printer makes no attempt to override the embedded
+ document data instructions with the IPP attributes when processing
+ the Job, and hence, the IPP attributes may fail to affect the Job
+ processing and output when the corresponding instruction is embedded
+ in the document data.
+
+15.3 Using Job Template Attributes During Document Processing.
+
+ The Printer object uses some of the Job object's Job Template
+ attributes during the processing of the document data associated with
+ that job. These include, but are not limited to, "orientation",
+ "number-up", "sides", "media", and "copies". The processing of each
+ document in a Job Object MUST follow the steps below. These steps are
+ intended only to identify when and how attributes are to be used in
+ processing document data and any alternative steps that accomplishes
+ the same effect can be used to implement this specification.
+
+ 1. Using the client supplied "document-format" attribute or some
+ form of document format detection algorithm (if the value of
+ "document- format" is not specific enough), determine whether or
+
+
+
+deBry, et al. Experimental [Page 163]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ not the document data has already been formatted for printing.
+ If the document data has been formatted, then go to step 2.
+ Otherwise, the document data MUST be formatted. The formatting
+ detection algorithm is implementation defined and is not
+ specified by this specification. The formatting of the document
+ data uses the "orientation-requested" attribute to determine how
+ the formatted print data should be placed on a print-stream
+ page, see section 4.2.10 for the details.
+
+ 2. The document data is in the form of a print-stream in a known
+ media type. The "page-ranges" attribute is used to select, as
+ specified in section 4.2.7, a sub-sequence of the pages in the
+ print-stream that are to be processed and images.
+
+ 3. The input to this step is a sequence of print-stream pages. This
+ step is controlled by the "number-up" attribute. If the value of
+ "number-up" is N, then during the processing of the print-stream
+ pages, each N print-stream pages are positioned, as specified in
+ section 4.2.9, to create a single impression. If a given
+ document does not have N more print-stream pages, then the
+ completion of the impression is controlled by the "multiple-
+ document-handling" attribute as described in section 4.2.4; when
+ the value of this attribute is 'single-document' or 'single-
+ document-new-sheet', the print-stream pages of document data
+ from subsequent documents is used to complete the impression.
+
+ The size(scaling), position(translation) and rotation of the
+ print-stream pages on the impression is implementation defined.
+ Note that during this process the print-stream pages may be
+ rendered to a form suitable for placing on the impression; this
+ rendering is controlled by the values of the "printer-
+ resolution" and "print- quality" attributes as described in
+ sections 4.2.12 and 4.2.13. In the case N=1, the impression is
+ nearly the same as the print-stream page; the differences would
+ only be in the size, position and rotation of the print-stream
+ page and/or any decoration, such as a frame to the page, that is
+ added by the implementation.
+
+ 4. The collection of impressions is placed, in sequence, onto sides
+ of the media sheets. This placement is controlled by the "sides"
+ attribute and the orientation of the print-stream page, as
+ described in section 4.2.8. The orientation of the print-stream
+ pages affects the orientation of the impression; for example, if
+ "number-up" equals 2, then, typically, two portrait print-stream
+ pages become one landscape impression. Note that the placement
+ of impressions onto media sheets is also controlled by the
+ "multiple-document-handling" attribute as described in section
+ 4.2.4.
+
+
+
+deBry, et al. Experimental [Page 164]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ 5. The "copies" and "multiple-document-handling" attributes are
+ used to determine how many copies of each media instance are
+ created and in what order. See sections 4.2.5 and 4.2.4 for the
+ details.
+
+ 6. When the correct number of copies are created, the media
+ instances are finished according to the values of the
+ "finishings" attribute as described in 4.2.6. Note that
+ sometimes finishing operations may require manual intervention
+ to perform the finishing operations on the copies, especially
+ uncollated copies. This specification allows any or all of the
+ processing steps to be performed automatically or manually at
+ the discretion of the Printer object.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+deBry, et al. Experimental [Page 165]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+16. APPENDIX E: Generic Directory Schema
+
+ This section defines a generic schema for an entry in a directory
+ service. A directory service is a means by which service users can
+ locate service providers. In IPP environments, this means that IPP
+ Printers can be registered (either automatically or with the help of
+ an administrator) as entries of type printer in the directory using
+ an implementation specific mechanism such as entry attributes, entry
+ type fields, specific branches, etc. IPP clients can search or
+ browse for entries of type printer. Clients use the directory
+ service to find entries based on naming, organizational contexts, or
+ filtered searches on attribute values of entries. For example, a
+ client can find all printers in the "Local Department" context.
+ Authentication and authorization are also often part of a directory
+ service so that an administrator can place limits on end users so
+ that they are only allowed to find entries to which they have certain
+ access rights. IPP itself does not require any specific directory
+ service protocol or provider.
+
+ Note: Some directory implementations allow for the notion of
+ "aliasing". That is, one directory entry object can appear as
+ multiple directory entry object with different names for each object.
+ In each case, each alias refers to the same directory entry object
+ which refers to a single IPP Printer object.
+
+ The generic schema is a subset of IPP Printer Job Template and
+ Printer Description attributes (sections 4.2 and 4.4). These
+ attributes are identified as either RECOMMENDED or OPTIONAL for the
+ directory entry itself. This conformance labeling is NOT the same
+ conformance labeling applied to the attributes of IPP Printers
+ objects. The conformance labeling in this Appendix is intended to
+ apply to directory templates and to IPP Printer implementations that
+ subscribe by adding one or more entries to a directory. RECOMMENDED
+ attributes SHOULD be associated with each directory entry. OPTIONAL
+ attributes MAY be associated with the directory entry (if known or
+ supported). In addition, all directory entry attributes SHOULD
+ reflect the current attribute values for the corresponding Printer
+ object.
+
+ The names of attributes in directory schema and entries SHOULD be the
+ same as the IPP Printer attribute names as shown.
+
+ In order to bridge between the directory service and the IPP Printer
+ object, one of the RECOMMENDED directory entry attributes is the
+ Printer object's "printer-uri-supported" attribute. The IPP client
+ queries the "printer-uri-supported" attribute in the directory entry
+
+
+
+
+
+deBry, et al. Experimental [Page 166]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ and then addresses the IPP Printer object using one of its URIs. The
+ "uri-security-supported" attribute identifies the protocol (if any)
+ used to secure a channel.
+
+ The following attributes define the generic schema for directory
+ entries of type PRINTER:
+
+ printer-uri-supported RECOMMENDED Section 4.4.1
+ uri-security-supported RECOMMENDED Section 4.4.2
+ printer-name RECOMMENDED Section 4.4.3
+ printer-location RECOMMENDED Section 4.4.4
+ printer-info OPTIONAL Section 4.4.5
+ printer-more-info OPTIONAL Section 4.4.6
+ printer-make-and-model RECOMMENDED Section 4.4.8
+ charset-supported OPTIONAL Section 4.4.15
+ generated-natural-language-
+ supported OPTIONAL Section 4.4.17
+ document-format-supported RECOMMENDED Section 4.4.19
+ color-supported RECOMMENDED Section 4.4.23
+ finishings-supported OPTIONAL Section 4.2.6
+ number-up-supported OPTIONAL Section 4.2.7
+ sides-supported RECOMMENDED Section 4.2.8
+ media-supported RECOMMENDED Section 4.2.11
+ printer-resolution-supported OPTIONAL Section 4.2.12
+ print-quality-supported OPTIONAL Section 4.2.13
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+deBry, et al. Experimental [Page 167]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+17. APPENDIX F: Change History for the IPP Model and Semantics document
+
+ The following substantive changes and major clarifications have been
+ made to this document from the June 30, 1998 version based on the
+ interoperability testing that took place September 23-25 1998 and
+ subsequent mailing list and meeting discussions. They are listed in
+ the order of occurrence in the document. These changes are the ones
+ that might affect implementations. Clarifications that are unlikely
+ to affect implementations are not listed. The issue numbers refer to
+ the IPP Issues List which is available in the following directory:
+
+ ftp://ftp.pwg.org/pub/pwg/ipp/approved-clarifications/
+
+ Section Description
+
+ global Replaced TLS references with SSL3 references as agreed with
+ our Area Director on 11/12/1998.
+
+ global Removed the indications that some of these IPP documents
+ are informational, since the intent is now to publish all
+ IPP/1.0 documents as informational as agreed with our Area
+ Director on 11/12/1998.
+
+ 3.1.2, Clarify that the IPP object SHOULD NOT validate the
+ 16.3.3 range of the request-id being 1 to 2**31-1, but accepts
+ [now ipp- and returns any value. Clients MUST still keep in the
+ iig] range 1 to 2**31 though. If the request is terminated
+ before the complete "request-id" is received, the IPP
+ object rejects the request and returns a response with a
+ "request-id" of 0 (Issue 1.36).
+
+ 3.1.4.1, Clarified that when a client submits a request in a
+ 13.1.4.14 charset that is not supported, the IPP object SHOULD
+ return any 'text' or 'name' attributes in the 'utf-8'
+ charset, if it returns any, since clients and IPP
+ objects MUST support 'utf-8'. (Issue 1.19)
+
+ 3.1.4.1 Clarified Section 3.1.4.1 Request Operation Attributes
+ that a client MAY use the attribute level natural
+ language override (text/nameWithLanguage) redundantly in
+ a request. (Issue 1.46)
+
+ 3.1.4.2 Clarified Section 3.1.4.2 Response Operation Attributes
+ that an IPP object MAY use the attribute level natural
+ language override (text/nameWithLanguage) redundantly in
+ a response. (Issue 1.46)
+
+
+
+
+
+deBry, et al. Experimental [Page 168]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ 3.1.6 Clarified section 3.1.6: If the Printer object supports
+ the "status-message" operation attribute, it NEED NOT
+ return a status message for the following error status
+ codes: 'client-error-bad-request', 'client-error-
+ charset-not-supported', 'server-error-internal-error',
+ 'server-error-operation-not-supported', and 'server-
+ error-version-not-supported'.
+
+ 3.2.1.1 Clarified that if a client is not supplying any Job
+ Template attributes in a request, the client SHOULD omit
+ Group 2 rather than sending an empty group. However, a
+ Printer object MUST be able to accept an empty group.
+ This makes [RFC2566] agree with [RFC2565]. (Issue 1.16)
+
+ 3.2.1.2, Clarified that if an IPP object is not returning any
+ 3.2.5.2, Unsupported Attributes in a response, the IPP object
+ 3.2.6.2, SHOULD omit Group 2 rather than sending an empty group.
+ 3.3.1.2, However, a client MUST be able to accept an empty group.
+ 3.3.3.2, This makes [RFC2566] agree with [RFC2565]. (Issue 1.17)
+ 3.3.4.2
+
+ 3.2.1.2, Clarified that an IPP object MUST treat an unsupported
+ 13.1.2.2, attribute syntax supplied in a request in the same way
+ 13.1.4.12 as an unsupported value. The IPP object MUST return the
+ attribute, the attribute syntax, and the value in the
+ Unsupported Attributes group. (Issue 1.26)
+
+ 3.2.5.2, Clarified for Get-Printer-Attributes, Get-Jobs, and Get-
+ 3.2.6.2, Job-Attributes that an IPP object MUST return
+ 3.3.4.2, 'successful-ok-ignored-or-substituted-attributes' (0x1),
+
+ 13.1.2.1, rather than 'successful-ok' (0x0), when a client
+ 13.1.2.2, supplies unsupported attributes as values of the
+ 13.1.4.12 'requested-attributes' operation attribute. (Issue
+ 1.24)
+ Also clarified that the response NEED NOT contain the
+ "requested-attributes" operation attribute with any
+ supplied values (attribute keywords) that were requested
+ by the client but are not supported by the IPP object.
+ (Issue 1.18)
+
+ 3.2.6.2 Deleted the job-level natural language override (NLO)
+ 4.1.1.2 from Section 3.2.6.2 Get-Jobs Response so that all
+ 4.3.24 operation responses are the same with respect to NLO.
+ (Issue 1.47)
+
+
+
+
+
+
+deBry, et al. Experimental [Page 169]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ 3.3.1 Clarified that an IPP Printer that supports the Create-
+ Job operation MUST handle the situation when a client
+ does not supply Send-Document or Send-URI operations
+ within a one- to four-minute time period. Also
+ clarified that a client MUST send documents in a multi-
+ document job without undue or unbounded delay. (Issue
+ 1.28)
+
+ 3.3.3 Clarified that the IPP object MUST reject a Cancel-Job
+ request if the job is in 'completed', 'canceled', or
+ 'aborted' job states. (Issue 1.12)
+
+ 4.1.2.3 Added this new sub-section: it specifies that
+ nameWithoutLanguage plus the implicit natural language
+ matches nameWithLanguage, if the values and natural
+ languages are the same. Also added that keyword never
+ matches nameWithLanguage or nameWithoutLanguage.
+ Clarified that if both have countries, that the
+ countries SHOULD match as well. If either do not, then
+ the country field SHOULD be ignored. (Issues 1.33 and
+ 1.34)
+
+ 4.1.5 Clarified regarding the case-insensitivity of URLs to
+ refer only to the RFCs that define them. (Issue 1.10)
+
+ 4.1.11 Clarified that 'boolean' is not a full-sized integer.
+ (Issue 1.38)
+
+ 4.1.15 Clarified that 'resolution' is not three full-sized
+ integers. (Issue 1.20)
+
+ 4.2.* Clarified that standard values are keywords or enums,
+ not names. (Issue 1.49).
+
+ 4.2.4 Added the 'single-document-new-sheet' value to Section
+ 4.2.4 multiple-document-handling. (Issue 1.54)
+
+ 4.4.18, Clarified that the "document-format-default" and
+ 4.4.19 "document-format-supported" Printer Description
+ attributes are REQUIRED to agree with the table. (Issue
+ 1.4)
+
+ 4.4.21 Changed "queued-job-count" from OPTIONAL to RECOMMENDED.
+ (Issue 1.14)
+
+
+
+
+
+
+
+deBry, et al. Experimental [Page 170]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ 4.4.28 Clarified that the implementation supplied value for the
+ "multiple-operation-time-out" attribute SHOULD be
+ between 30 and 240 seconds, though the implementation
+ MAY allow the administrator to set values, and MAY allow
+ values outside this range. (Issue 1.28)
+
+ 5.1, Clarified Client Conformance that if a client supports
+ 5.2.5 an attribute of 'text' attribute syntax, that it MUST
+ support both the textWithoutLanguage and the
+ textWithLanguage forms. Same for 'name' attribute
+ syntax. Same for an IPP object (Issue 1.48)
+
+ 6.5, Added new section to allow Attribute Groups to be
+ 12.8 registered as extensions for being passed in operation
+ requests and responses. (Issue 1.25)
+
+ 7. Updated the table of text and name attributes to agree
+ with Section 4.2.
+
+ 8.5 Added a new section RECOMMENDING that the Get-Jobs
+ SHOULD return non-IPP jobs whether or not assigning them
+ a job-id and job-uri. Also RECOMMENDED generating, if
+ possible, job-id and job-uri and supporting other IPP
+ operations on foreign jobs as an implementer option.
+ (Issue 1.32)
+
+ 9. Updated document references.
+
+ 13.1.4.14 Clarified 'client-error-charset-not-supported' that
+ 'utf-8' must be used for any 'text' or 'name' attributes
+ returned in the error response (Issue 1.19).
+
+ 13.1.5.9 Added a new error code 'server-error-job-canceled'
+ (0x0508) to be returned if a job is canceled by another
+ client or aborted by the IPP object while the first
+ client is still sending the document data. (Issue 1.29)
+
+ 15.3, Moved these sections recommending operation processing
+ 15.4 steps to the new Implementer's Guide (informational).
+ There indicated that all of the error checks are not
+ required, so an IPP object MAY be forgiving and accept
+ non-conforming requests. However, a conforming client
+ MUST supply requests that would pass all of the error
+ checks indicated. (Issue 1.21)
+
+
+
+
+
+
+
+deBry, et al. Experimental [Page 171]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+ 16 Changed directory schema attributes from REQUIRED to
+ RECOMMENDED. Changed some of the OPTIONAL to
+ RECOMMENDED to agree with the SLP template. Changed the
+ "charset-supported" and "natural-language-supported"
+ from REQUIRED to OPTIONAL. Recommended that the names
+ be the same in a directory entry as the IPP attribute
+ names. (Issue 1.53)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+deBry, et al. Experimental [Page 172]
+
+RFC 2566 IPP/1.0: Model and Semantics April 1999
+
+
+18. Full Copyright Statement
+
+ Copyright (C) The Internet Society (1999). All Rights Reserved.
+
+ This document and translations of it may be copied and furnished to
+ others, and derivative works that comment on or otherwise explain it
+ or assist in its implementation may be prepared, copied, published
+ and distributed, in whole or in part, without restriction of any
+ kind, provided that the above copyright notice and this paragraph are
+ included on all such copies and derivative works. However, this
+ document itself may not be modified in any way, such as by removing
+ the copyright notice or references to the Internet Society or other
+ Internet organizations, except as needed for the purpose of
+ developing Internet standards in which case the procedures for
+ copyrights defined in the Internet Standards process must be
+ followed, or as required to translate it into languages other than
+ English.
+
+ The limited permissions granted above are perpetual and will not be
+ revoked by the Internet Society or its successors or assigns.
+
+ This document and the information contained herein is provided on an
+ "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING
+ TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+ HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+ MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+deBry, et al. Experimental [Page 173]
+
diff --git a/standards/rfc2567.txt b/standards/rfc2567.txt
new file mode 100644
index 000000000..d5ef440be
--- /dev/null
+++ b/standards/rfc2567.txt
@@ -0,0 +1,2411 @@
+
+
+
+
+
+
+Network Working Group F.D. Wright
+Request for Comments: 2567 Lexmark International
+Category: Experimental April 1999
+
+
+ Design Goals for an Internet Printing Protocol
+
+Status of this Memo
+
+ This memo defines an Experimental Protocol for the Internet
+ community. It does not specify an Internet standard of any kind.
+ Discussion and suggestions for improvement are requested.
+ Distribution of this memo is unlimited.
+
+Copyright Notice
+
+ Copyright (C) The Internet Society (1999). All Rights Reserved.
+
+IESG Note
+
+ This document defines an Experimental protocol for the Internet
+ community. The IESG expects that a revised version of this protocol
+ will be published as Proposed Standard protocol. The Proposed
+ Standard, when published, is expected to change from the protocol
+ defined in this memo. In particular, it is expected that the
+ standards-track version of the protocol will incorporate strong
+ authentication and privacy features, and that an "ipp:" URL type will
+ be defined which supports those security measures. Other changes to
+ the protocol are also possible. Implementers are warned that future
+ versions of this protocol may not interoperate with the version of
+ IPP defined in this document, or if they do interoperate, that some
+ protocol features may not be available.
+
+ The IESG encourages experimentation with this protocol, especially in
+ combination with Transport Layer Security (TLS) [RFC2246], to help
+ determine how TLS may effectively be used as a security layer for
+ IPP.
+
+Abstract
+
+ This document is one of a set of documents, which together describe
+ all aspects of a new Internet Printing Protocol (IPP). IPP is an
+ application level protocol that can be used for distributed printing
+ using Internet tools and technologies. This document takes a broad
+ look at distributed printing functionality, and it enumerates real-
+ life scenarios that help to clarify the features that need to be
+ included in a printing protocol for the Internet. It identifies
+ requirements for three types of users: end users, operators, and
+
+
+
+Wright Experimental [Page 1]
+
+RFC 2567 Internet Printing Design Goals April 1999
+
+
+ administrators. The design goals document calls out a subset of end
+ user requirements that are satisfied in IPP/1.0. Operator and
+ administrator requirements are out of scope for version 1.0.
+
+ The full set of IPP documents includes:
+
+ Design Goals for an Internet Printing Protocol (this document)
+ Rationale for the Structure and Model and Protocol for the
+ Internet Printing Protocol [RFC2568]
+ Internet Printing Protocol/1.0: Model and Semantics [RFC2568]
+ Internet Printing Protocol/1.0: Encoding and Transport [RFC2565]
+ Internet Printing Protocol/1.0: Implementer's Guide [ipp-iig]
+ Mapping between LPD and IPP Protocols [RFC2569]
+
+ The "Rationale for the Structure and Model and Protocol for the
+ Internet Printing Protocol" document describes IPP from a high level
+ view, defines a roadmap for the various documents that form the suite
+ of IPP specifications, and gives background and rationale for the
+ IETF working group's major decisions.
+
+ The "Internet Printing Protocol/1.0: Model and Semantics" document
+ describes a simplified model consisting of abstract objects, their
+ attributes, and their operations that is independent of encoding and
+ transport. The model consists of a Printer and a Job object. The
+ Job optionally supports multiple documents. IPP 1.0 semantics allow
+ end-users and operators to query printer capabilities, submit print
+ jobs, inquire about the status of print jobs and printers, and cancel
+ print jobs. This document also addresses security,
+ internationalization, and directory issues.
+
+ The "Internet Printing Protocol/1.0: Encoding and Transport" document
+ is a formal mapping of the abstract operations and attributes defined
+ in the model document onto HTTP/1.1. It defines the encoding rules
+ for a new Internet media type called "application/ipp".
+
+ The "Internet Printing Protocol/1.0: Implementer's Guide" document
+ gives insight and advice to implementers of IPP clients and IPP
+ objects. It is intended to help them understand IPP/1.0 and some of
+ the considerations that may assist them in the design of their client
+ and/or IPP object implementations. For example, a typical order of
+ processing requests is given, including error checking. Motivation
+ for some of the specification decisions is also included.
+
+ The "Mapping between LPD and IPP Protocols" document gives some
+ advice to implementers of gateways between IPP and LPD (Line Printer
+ Daemon) implementations.
+
+
+
+
+
+Wright Experimental [Page 2]
+
+RFC 2567 Internet Printing Design Goals April 1999
+
+
+TABLE OF CONTENTS
+
+ 1. INTRODUCTION.....................................................4
+ 2. TERMINOLOGY......................................................4
+ 3. DESIGN GOALS.....................................................6
+ 3.1. End-user.......................................................6
+ 3.1.1. Finding or locating a printer................................6
+ 3.1.2. Create an instance of the printer............................7
+ 3.1.3. Viewing the status and capabilities of a printer.............7
+ 3.1.4. Submitting a print job.......................................8
+ 3.1.5. Viewing the status of a submitted print job..................9
+ 3.1.6. Canceling a Print Job........................................9
+ 3.2. Operator (NOT REQUIRED FOR V1.0)...............................9
+ 3.2.1. Alerting.....................................................9
+ 3.2.2. Changing Print and Job Status...............................10
+ 3.3. Administrator (NOT REQUIRED FOR v1.0).........................10
+ 4. OBJECTIVES OF THE PROTOCOL......................................10
+ 4.1. SECURITY CONSIDERATIONS.......................................11
+ 4.2. Interaction with LPD (RFC1179)................................12
+ 4.3. Extensibility.................................................12
+ 4.4. Firewalls.....................................................13
+ 4.5. Internationalization..........................................13
+ 5. IPP SCENARIOS...................................................13
+ 5.1. Printer Discovery.............................................14
+ 5.2. Driver Installation...........................................15
+ 5.3. Submitting a Print Job........................................15
+ 5.4. Getting Status/Capabilities...................................16
+ 5.5. Asynchronous Notification.....................................17
+ 5.6. Job Canceling.................................................17
+ 6. Security Considerations.........................................18
+ 7. REFERENCES......................................................18
+ 8. ACKNOWLEDGMENTS.................................................19
+ 9. AUTHOR'S ADDRESS................................................19
+ 10. APPENDIX - DETAILED SCENARIOS..................................20
+ 10.1. Printer discovery within an enterprise.......................20
+ 10.2. Printer discovery across enterprises.........................21
+ 10.3. Printer discovery on the Internet -logical operations........21
+ 10.4. Printer discovery on the Internet - authentication...........22
+ 10.5. Driver Download..............................................23
+ 10.6. Submitting a print job as a file.............................24
+ 10.7. Submitting a print job with two documents....................24
+ 10.8. Submitting a print job as a file, printing fails.............25
+ 10.9. Submitting a print job with authentication, PRIVACY and
+ payment......................................................26
+ 10.10. Submitting a print job with decryption error................27
+ 10.11. Submitting a print job with authentication..................28
+ 10.12. Submitting a print job generated dynamically................29
+ 10.13. Submitting a print job with a Printer jam - CANCELED........29
+
+
+
+Wright Experimental [Page 3]
+
+RFC 2567 Internet Printing Design Goals April 1999
+
+
+ 10.14. Submitting a print job with a Printer jam - recovered.......30
+ 10.15. Submitting a print job with server pull.....................31
+ 10.16. Submitting a print job with referenced resources............32
+ 10.17. Getting Capabilities........................................33
+ 10.17.1. Submission Attributes.....................................33
+ 10.17.2. Printer Capabilities......................................33
+ 10.18. Getting Status..............................................34
+ 10.18.1. Printer State/Status......................................34
+ 10.18.2. Job Status................................................34
+ 10.18.3. Status of All My Jobs.....................................34
+ 10.19. Asynchronous Notification...................................35
+ 10.19.1. Job Completion............................................35
+ 10.19.2. Job Complete with Data....................................35
+ 10.19.3. Print Job Fails...........................................35
+ 10.20. Cancel a job................................................36
+ 10.21. End to end Scenario - within an enterprise..................36
+ 10.22. End to end Scenario - across enterprises....................37
+ 10.23. End to End Scenario - on the internet.......................40
+ 11. Full Copyright Statement.......................................43
+
+1. INTRODUCTION
+
+ The IPP protocol is heavily influenced by the printing model
+ introduced in the Document Printing Application (DPA) [ISO10175]
+ standard. Although DPA specifies both end user and administrative
+ features, IPP version 1.0 (IPP/1.0) focuses only on end user
+ functionality.
+
+2. TERMINOLOGY
+
+ Internet Printing for the purposes of this document is the
+ application of Internet tools, programs, servers and networks to
+ allow end-users to print to a remote printer using, after initial
+ setup or configuration, the same methods, operations and paradigms as
+ would be used for a locally attached or a local area network attached
+ printer. This could include the use of HTTP servers and browsers and
+ other applications for providing static, dynamic and interactive
+ printer locating services, user installation, selection,
+ configuration, print job submission, printer capability inquiry and
+ status inquiry of remote printers and jobs.
+
+ For the purposes of this document, a WEB Browser is software
+ available from a number of sources including but not limited to the
+ following: Microsoft Internet Explorer, NCSA Mosaic, Netscape
+ Navigator, Sun Hot Java!. The major task of these products is to use
+ the Hypertext Transport Protocol (HTTP) to retrieve, interpret and
+ display Hypertext Markup Language (HTML). These products are often a
+ part of a complete Internet Printing system because they are often
+
+
+
+Wright Experimental [Page 4]
+
+RFC 2567 Internet Printing Design Goals April 1999
+
+
+ used as a means of obtaining the status of or more information about
+ the printing system; however, they may not be present in all
+ implementations.
+
+ Throughout this document, 'printer' shall be interpreted to include
+ any device which is capable of marking on a piece of media using any
+ available technology. These design goals do not include support for
+ multi-tiered printing solutions involving servers (single or
+ multiple) logically in front of the actual printing device yet all
+ such configurations shall be supported but shall appear to the end-
+ user as only a single device.
+
+ Throughout this document 'driver' refers to the code installed in
+ some client operating system to generate the print data stream for
+ the intended printer. Some computing environments may not include a
+ separate printer driver. Rather, the generation of the proper print
+ data stream is accomplished in an application on that computer. How
+ such a computer environment or application is updated to support a
+ new printer now made available using IPP is outside the scope of IPP.
+ The actual details for installing a printer driver are operating
+ system dependent and are also outside the scope of IPP. See also
+ section 4.1 (SECURITY CONSIDERATIONS) for security implications of
+ driver download and installation.
+
+ The IPP protocol will support the following physical configurations:
+
+ - An IPP client talking to an IPP Printer object imbedded in a
+ single, physical output device.
+ - An IPP Client talking to a server containing one or more IPP
+ Printer objects. Each Printer object is associated with exactly one
+ physical output device supported by the server. The protocol
+ between the server and the output devices is undefined.
+ - An IPP Client talking to an IPP Printer object in a server. The
+ Printer object is associated with one or more physical output
+ devices, but the client only sees the Printer object, which is an
+ abstraction and represents all of the associated physical output
+ devices. The protocol between the server and the physical output
+ devices is undefined.
+
+ Throughout this document, certain design goals will be identified as
+ not being a part of version 1.0 (or V1.0) of the protocol or as being
+ satisfied by means outside of IPP. IPP is assumed to be one part, an
+ enabler, of a complete Internet Printing solution. For example
+ printer instance creation is not performed by but is enabled by the
+ protocol. Globally, none of the operator or administrators wants and
+ needs are included in the design goals for version 1.0. Some of the
+ end-user wants and needs may also be excluded from version 1.0 and
+ will be so noted in the description of them. Subsequent versions of
+
+
+
+Wright Experimental [Page 5]
+
+RFC 2567 Internet Printing Design Goals April 1999
+
+
+ the protocol (e.g. V2.0) may include support for these initially
+ excluded wants and needs.
+
+3. DESIGN GOALS
+
+ The next three sections identify the design goals for an Internet
+ printing protocol from three roles assumed by humans: end-user,
+ operator, and administrator. The goals defined here are only those
+ that need to be addressed by an Internet printing protocol. Other
+ wants and needs, such as that the operator needs physical access to
+ the printer (e.g. to be able to load paper or clear jams) are not
+ covered by this document. Section 5 contains scenarios which provide
+ more detailed examples of the entire process including discovery,
+ status, printing and end-of-job reporting.
+
+3.1. END-USER
+
+ An end-user of a printer accepting jobs through the Internet is one
+ of the roles in which humans act. The end-user is the person that
+ will submit a job to be printed on the printer.
+
+ The wants and needs of the end-user are broken down into six
+ categories: finding/locating a printer, creating a local instance of
+ a printer, viewing printer status, viewing printer capabilities,
+ submitting a print job, viewing print job status, altering the
+ attributes of a print job.
+
+3.1.1. Finding or locating a printer.
+
+ End-users want to be able to find and locate printers to which they
+ are authorized to print. They want to be able to perform this
+ function using a standard WEB browser or other application. Multiple
+ criteria can be applied to find the printers needed. These criteria
+ include but are not limited to:
+
+ - by name (Printer 1, Joes-color-printer, etc.)
+ - by geographic location (bldg 1, Kentucky, etc.)
+ - by capability or attribute (color, duplex, legal paper, etc.)
+
+ Additionally, while it is outside of scope of IPP, end-users want to
+ be able to limit the scope of their searching to:
+
+ - inside a functional sub-domain
+ - include only a particular domain (lexmark.com)
+ - exclude specified domains
+
+
+
+
+
+
+Wright Experimental [Page 6]
+
+RFC 2567 Internet Printing Design Goals April 1999
+
+
+ While an Internet printing protocol may not of itself include this
+ function, IPP must define and enable a directory schema which will
+ provide the necessary information for a directory service
+ implementation to consistently represent printers by their IPP
+ attributes.
+
+3.1.2. Create an instance of the printer.
+
+ After finding the desired printer, an end-user needs to be able to
+ create a local instance of that printer within the end-user operating
+ system or desktop. This local instance will vary depending upon the
+ printing paradigm of the operating system. For example, some UNIX
+ users will only want a queue or a reference to a remote printer
+ created on their machine while other UNIX users and Windows NT users
+ will want the queue and also the necessary icons and registry entries
+ to be created and initialized. Where required, drivers may need to
+ be downloaded from some repository and installed on the computer.
+ All necessary decompressing, unpacking, and other installation
+ actions should occur without end-user interaction or intervention
+ excepting initial approval by the end-user. Once the local instance
+ of the printer has been installed, it shall appear to the end-user of
+ the operating system and to the applications running there as any
+ other printer (local, local area network connected, or network
+ operating system connected) on the end-user desktop or environment.
+ IPP's role in this goal is simply to enable the creation of the
+ printer instance providing information such as where to locate a
+ printer driver for this printer, as an attribute of an IPP Printer.
+
+3.1.3. Viewing the status and capabilities of a printer.
+
+ Before using a selected printer or, in fact at any time, the end-user
+ needs the ability to verify the characteristics and status of both
+ printers and jobs queued for that printer. When checking the
+ characteristics of a printer, the end-user typically wants to be able
+ to determine the capability of the device, e.g.:
+
+ - supported media, commonly paper, by size and type
+ - paper handling capability, e.g. duplex, collating, finishing
+ - color capability
+
+ When checking the status of the printer and its print jobs, the end-
+ user typically wants to be able to determine:
+
+ - is the printer on-line?
+ - what are the defaults to be used for printing?
+ - how many jobs are queued for the printer?
+ - how are job priorities assigned? (outside the scope of IPP)
+
+
+
+
+Wright Experimental [Page 7]
+
+RFC 2567 Internet Printing Design Goals April 1999
+
+
+3.1.4. Submitting a print job.
+
+ Once the desired printer has been located and installed, the end-user
+ wants to print to that printer from normal applications using
+ standard methods. These normal applications include such programs as
+ word processors, spreadsheets, data-base applications, WEB browsers,
+ production printing applications, etc. Additionally, the end-user
+ may want to print a file already existing on the end-user's computer
+ -- "simple push". In addition to printing from an application and
+ simple push, the end-user needs to have the ability to submit a print
+ job by reference. Printing by reference is defined to mean as
+ submitting a job by providing a reference to an existing document.
+ The reference, a URI, will be resolved before the actual print
+ process occurs. Submitting a job by reference relieves the user from
+ downloading the document from the remote server and then sending it
+ via IPP to the printer. This saves both time and network bandwidth.
+
+ Some means shall be provided to determine if the format of a job
+ matches the capability of the printer. This can be done by one of
+ the following (all of which are outside of scope of the IPP
+ protocol):
+
+ - the end-user selects the correct printer driver
+ - the printer automatically selects the proper interpreter
+ - the end-user uses some other manual procedure.
+
+ A standard action shall be defined should the job's requirements not
+ match the capabilities of the printer.
+
+ Because the end-user does not want to know the details of the
+ underlying printing process, the protocol must support job-to-printer
+ capability matching (all implementations are not necessarily required
+ to implement this function.) This matching capability requires
+ knowing both the printer's capabilities and attributes and those
+ capabilities and attributes required by the job. Actions taken when
+ a print job requires capabilities or attributes that are not
+ available on the printer vary and can include but are not limited to:
+
+ - rejecting the print job
+ - redirecting the print job to another printer (Not in V1.0)
+ - printing the job, accepting differences in the appearance
+
+ Print jobs will also be submitted by background or batch applications
+ without human intervention.
+
+ End-users need the ability to set certain print job parameters at the
+ time the job is submitted. These parameters include but are not
+ limited to:
+
+
+
+Wright Experimental [Page 8]
+
+RFC 2567 Internet Printing Design Goals April 1999
+
+
+ - number of copies
+ - single or two sided printing
+ - finishing
+ - job priority
+
+3.1.5. Viewing the status of a submitted print job.
+
+ After a job has been submitted to a printer, the end-user needs a way
+ to view the status of that job (i.e. job waiting, job printing, job
+ done) and to determine where the job is in the print queue.
+
+ In addition to the need to inquire about the status of a print job,
+ automatic notification of the completion of that job is also
+ required.
+
+ Notification means are not defined by the protocol but the protocol
+ must provide a means of enabling and disabling the notification.
+
+3.1.6. Canceling a Print Job
+
+ While a job is waiting to be printed or has been started but not yet
+ completed, the original creator/submitter of the print job (i.e. the
+ end-user) shall be able to cancel the job entirely (job is waiting)
+ or the remaining portion of it (job is printing.) Altering the print
+ job itself is not a V1.0 design goal.
+
+3.2. OPERATOR (NOT REQUIRED FOR V1.0)
+
+ An operator of a printer accepting jobs through the Internet is one
+ of the roles in which humans act. The operator has the
+ responsibility of monitoring the status of the printer as well as
+ managing and controlling the jobs at the device. These
+ responsibilities include but are not limited to the replenishing of
+ supplies (ink, toner, paper, etc.), the clearing of minor errors
+ (paper jams, etc.) and the re-prioritization of end-user jobs.
+ Operator wants and needs will not be addressed by V1.0 of the
+ protocol.
+
+ The wants and needs of the operator include all those of the end-user
+ but may include additional privileges. For example, an operator may
+ be able to view all print jobs on a printer while the end-user might
+ only be able to see his own jobs.
+
+3.2.1. Alerting.
+
+ One of the required operator functions is having the ability to
+ discover or to be alerted to changes in the status of a printer
+ particularly those changes that cause a printer to stop printing and
+
+
+
+Wright Experimental [Page 9]
+
+RFC 2567 Internet Printing Design Goals April 1999
+
+
+ to be able to correct those problems. As such, an Internet printing
+ protocol shall be able to alert a designated operator or operators to
+ these conditions such as 'out of paper', 'out of ink', etc.
+ Additionally. the operator shall be able to, asynchronous to other
+ printer activity, inquire as to a printer's or a job's status.
+
+3.2.2. Changing Print and Job Status.
+
+ Another of the required operator functions is the ability to affect
+ changes to printer and job status remotely. For example, the
+ operator will need to be able to re-prioritize or cancel any print
+ jobs on a printer to which the operator has authority.
+
+3.3. ADMINISTRATOR (NOT REQUIRED FOR V1.0)
+
+ An administrator of a printer accepting jobs through the Internet is
+ one of the roles in which humans act. The administrator has the
+ responsibility of creating the printer instances and controlling the
+ authorization of other end-users and operators. Administrator wants
+ and needs will not be addressed by V1.0 of the protocol.
+
+ The wants and needs of the administrator include all those of the
+ end-user and, in some environments, some or all of those of the
+ operator. Minimally, the administrator must also have the tools,
+ programs, utilities and supporting protocols available to be able to:
+
+ - create an instance of a printer
+ - create, edit and maintain the list of authorized end-users
+ - create, edit and maintain the list of authorized operators
+ - create, edit and maintain the list of authorized
+ administrators
+ - create, customize, change or otherwise alter the manner in
+ which the status capabilities and other information about printers
+ and jobs are presented
+ - create, customize, or change other printer or job features
+ - administrate billing or other charge-back mechanisms
+ - create sets of defaults
+ - create sets of capabilities
+
+ The administrator must have the capability to perform all the above
+ tasks locally or remotely to the printer.
+
+4. OBJECTIVES OF THE PROTOCOL
+
+ The protocol to be defined by an Internet printing working group will
+ address the wants and needs of the end-user (V1.0). It will not, at
+ least initially, address the operator or administrator wants and
+ needs (V2.0).
+
+
+
+Wright Experimental [Page 10]
+
+RFC 2567 Internet Printing Design Goals April 1999
+
+
+ The protocol defined shall be independent of the operating system of
+ both the client and the server. Generally, any platform capable of
+ supporting a WEB Browser should be capable of being a client.
+ Generally, any platform providing a WEB/HTTP server and printing
+ services should be capable of being a server. Usage of the WEB
+ Browser and Server is not required for IPP; the operating system,
+ operating system extensions or other applications may provide IPP
+ functionality directly.
+
+ In many environments such as Windows 95, Windows NT and OS/2, the
+ print data is created and transmitted to the printer on the fly
+ rather than being created, spooled and then transmitted to the
+ printer (a typical UNIX method.) The Internet Printing Protocol must
+ properly handle either methodology and make this transparent to the
+ end-user.
+
+4.1. SECURITY CONSIDERATIONS
+
+ It is required that the Internet Printing Protocol be able to operate
+ within a secure environment. Wherever reasonable, IPP ought to make
+ use of existing security protocols and services. IPP will not invent
+ new security features when the design goals described in this
+ document can be met by existing protocols and services. Examples of
+ such services include Secure Socket Layer Version 3 (SSL3) [SSL] and
+ HTTP Digest Access Authentication [RFC2069]. Note: SSL3 is not on
+ the IETF standards track.
+
+ Since we cannot anticipate the security levels or the specific
+ threats that any given IPP print administrator may be concerned with,
+ IPP must be capable of operating with different security mechanisms
+ and policies as required by the individual installation. The initial
+ security needs of IPP are derived from two primary considerations.
+ First, the printing environments described in this document take into
+ account that the client, the Printer, and the document to be printed
+ may each exist in different security domains. When objects are in
+ different security domains the design goals for authentication and
+ message protection may be much stronger than when they are all in the
+ same domain.
+
+ Secondly, the sensitivity and value of the content being printed will
+ vary from one instance of a print job to another. For example, a
+ publicly available document does not need the same level of
+ protection as a payroll document does. Message protection design
+ goals include data origin authentication, privacy, integrity, and
+ non-repudiation.
+
+
+
+
+
+
+Wright Experimental [Page 11]
+
+RFC 2567 Internet Printing Design Goals April 1999
+
+
+ In many environments (e.g. Windows, OS/2) a printer driver may be
+ needed to create the proper datastream for printer. This document
+ discusses downloading such a new driver from a variety of sources.
+ Downloading and installing any software, including drivers) on a
+ computer exposes that computer to a number of security risks
+ including but not limited to:
+
+ - defective software
+ - malicious software (e.g. Trojan horses)
+ - inappropriate software (i.e. software doing something
+ deemed unreasonable by the user.)
+
+ As such, proper security considerations and actions need to be taken
+ by the user and/or a system administrator to prevent the compromising
+ of the computer. Administrators should configure downloading
+ mechanism for printer drivers in such a way as to be able to verify
+ the source of driver software and encrypt or otherwise protect that
+ software during download.
+
+ Examples including security considerations can be found in sections 5
+ (IPP SCENARIOS) and 10 (APPENDIX - DETAILED SCENARIOS) later in this
+ document.
+
+4.2. INTERACTION WITH LPD (RFC1179)
+
+ Many versions of UNIX and in fact other operating systems provide a
+ means of printing as described in [RFC1179] (Line Printer Daemon
+ Protocol.) This document describes the file formats for the control
+ and data files as well as the messages used by the protocol. Because
+ of the simplistic approach taken by this protocol, many manufacturers
+ have include proprietary enhancements and extensions to 'lpd.'
+ Because of this divergence and due to other design goals described in
+ this document, there is no requirement for backward compatibility or
+ interoperability with 'lpd'. However, a mapping of LPD functionality
+ and IPP functionality shall be provided so as to enable a gateway
+ between LPD and IPP.
+
+4.3. EXTENSIBILITY
+
+ The Internet Printing Protocol shall be extensible by several means
+ that facilitate interoperability and prevent implementation
+ collisions:
+
+ - by providing a process whereby implementers can submit proposals
+ for registration of new attributes and new enumerated values for
+ existing attributes.
+
+
+
+
+
+Wright Experimental [Page 12]
+
+RFC 2567 Internet Printing Design Goals April 1999
+
+
+ * that require review and approval. The Internet Assigned
+ Number Authority (IANA) will be the repository for such
+ accepted registration proposals after review.
+
+ * that do not require review and approval. IANA will be the
+ repository for such registrations.
+
+ - by providing syntax in the protocol so that implementers may add
+ private (i.e. unregistered) attributes and enumerated attribute
+ values.
+
+ - by providing versioning and negotiation so as to enable future
+ implementations of IPP to interoperate with implementations of
+ version 1.0 of IPP.
+
+4.4. FIREWALLS
+
+ As stated in section 3 Design Goals, Internet printing shall, by
+ definition, support printing from one enterprise to another. As
+ such, the Internet printing protocol must be capable of passing
+ through firewalls and/or proxy servers (where enabled by the firewall
+ administrator) preferably without modification to the existing
+ firewall technology.
+
+4.5. INTERNATIONALIZATION
+
+ Users of Internet printing will come from all over the world. As
+ such, where appropriate, internationalization and localization will
+ be enabled for the protocol.
+
+5. IPP SCENARIOS
+
+ Each of the scenarios in this section describes a specific IPP
+ operation, such as submitting a print job. Section 10 contains
+ several detailed flows for each scenario to provide additional
+ detail. The examples should not be considered exhaustive, but
+ illustrative of the functions and features required in the protocol.
+ Flows are intended to be protocol neutral. It is not assumed that all
+ of the functions and features described in these scenarios will
+ necessarily be supported directly by IPP or in version 1.0 of IPP.
+
+ See the IPP Model and Semantics document for details on
+ configurations of clients, servers and firewalls.
+
+
+
+
+
+
+
+
+Wright Experimental [Page 13]
+
+RFC 2567 Internet Printing Design Goals April 1999
+
+
+5.1. PRINTER DISCOVERY
+
+ Client Directory Service
+ Service
+
+ +----------------------------------------------------------- >
+ give me information on printers with these characteristics
+
+
+ < -----------------------------------------------------------+
+ Information on Printers matching these characteristics
+
+ The objective of printer discovery is to locate printers that meet
+ the client's wants and needs. The Directory Service should provide
+ enough information for the client to make an initial choice. The
+ client may have to connect to each individual Printer offered to get
+ more detail. Not all information available from the Directory
+ Service is obtained using IPP; some information may be
+ administratively provided.
+
+ The actual protocol used between client and Directory or Name Service
+ is considered outside the scope of IPP. Printer Discover is included
+ in the scenarios to provide design goals for the directory schema for
+ IPP Printers and to further define Printer attributes.
+
+ Characteristics that might be considered when locating a Printer
+ include:
+
+ - capabilities of the Printer, e.g. PDLs supported
+ - physical location, e.g. in building 010
+ - driver required and location
+ - cost per page to print (outside the scope of IPP)
+ - whether or not printer is access controlled
+ - whether or not usage requires client authentication
+ - whether or not Printer can be authenticated
+ - whether or not payment is required for printing (outside the scope
+ of IPP)
+ - maximum job size (spool size) (outside the scope of IPP)
+ - whether or not Printer support compression (outside the scope of
+ IPP)
+ - whether or not Printer supports encryption
+ - administrative limits on this Printer
+ - maximum number of copies per job
+ - maximum number of pages per job
+
+
+
+
+
+
+
+Wright Experimental [Page 14]
+
+RFC 2567 Internet Printing Design Goals April 1999
+
+
+ Responses could additionally include:
+
+ - how to get more information
+ - web page
+ - telephone number
+ - help desk
+
+5.2. DRIVER INSTALLATION
+
+ Client Printer
+
+ +----------------------------------------------------------- >
+ Where can I find a driver & software to install it?
+
+
+ < -----------------------------------------------------------+
+ URIs for drivers and install software
+
+ Driver here refers to the code installed in some client operating
+ system to generate the print data stream for the intended printer.
+ The actual details for installing a printer driver are operating
+ system dependent and are also outside the scope of IPP. However, an
+ IPP printer or a directory service advertising an IPP Printer should
+ be capable of telling a client what drivers are available and/or
+ required, where they can be found, and provide pointers to
+ installation instructions, installation code or initialization
+ strings required to install the driver. See section 4.1 (SECURITY
+ CONSIDERATIONS) for security implications of driver download and
+ installation.
+
+5.3. SUBMITTING A PRINT JOB
+
+ Client IPP Printer
+
+ +----------------------------------------------------------- >
+ Here is a Print Job
+ - Job attributes
+ - Print data
+
+
+ < -----------------------------------------------------------+
+ Response
+
+ The protocol must support these sources of client data:
+
+ - Print data is a file submitted with the job
+ - Print data is generated on the fly by an application
+ - Print data is a file referenced by a URI
+
+
+
+Wright Experimental [Page 15]
+
+RFC 2567 Internet Printing Design Goals April 1999
+
+
+ The protocol must handle overrun conditions in the printer and must
+ support overlapped printing and downloading of the file in devices
+ that are unable to spool files before printing them.
+
+ Every print request will have a response. Responses will indicate
+ success or failure of the request and provide information on failures
+ when they occur. Responses would include things like:
+
+ - Got the print job and queued it
+ - Got the print job and am printing it
+ - Got the print job, started to print it, but printing failed
+ - why it failed (e.g. unrecoverable PostScript error)
+ - state of the printer
+ - how much printed
+ - Got the print job but couldn't print it
+ - why it can't be printed
+ - state of the printer
+ - Got the print job but don't know what to do with it
+ - Didn't get a complete print job (e.g. communication failure)
+
+5.4. GETTING STATUS/CAPABILITIES
+
+ Client IPP Printer
+
+ +----------------------------------------------------------- >
+ Get status and/or capabilities of Printer
+
+
+ < -----------------------------------------------------------+
+ Status/Capabilities
+
+ Clients will need to get information about
+
+ - Static capabilities of the device
+ - Dynamic state of the Printer (e.g. out of paper)
+ - State of a specific job owned by this client
+ - State of all jobs owned by this client
+ - queued
+ - printing
+ - completed
+
+
+
+
+
+
+
+
+
+
+
+Wright Experimental [Page 16]
+
+RFC 2567 Internet Printing Design Goals April 1999
+
+
+ - Job submission attributes supported/required
+ - scheduling attributes (e.g. priority)
+ - production attributes (e.g. number of copies)
+
+5.5. ASYNCHRONOUS NOTIFICATION
+
+ Client IPP Printer
+
+ +----------------------------------------------------------- >
+ Use the following method to notify me of Printer events
+
+ .
+ .
+ .
+ < -----------------------------------------------------------+
+ Asynchronous notification of Printer event
+
+ Clients must be able to request asynchronous notification for Printer
+ events such as
+
+ - job completion
+ - a fatal error that requires the job to be resubmitted
+ - a condition that severely impacts a queued job for this client
+ e.g. printer is out of paper
+
+ Note: end-user notification is a V1.0 design goal while operator
+ notification is for V2.0.
+
+5.6. JOB CANCELING
+
+ Client IPP Printer
+
+ +----------------------------------------------------------- >
+ Cancel the named job as indicated
+
+
+ < -----------------------------------------------------------+
+ Response (did it or not)
+
+ Similarly clients must be able to make changes to jobs which have
+ been submitted and are queued for printing. Changing of job
+ attributes should also be supported. Job modifications, holding and
+ releasing of jobs are not included in the design goals for IPP v1.0.
+
+
+
+
+
+
+
+
+Wright Experimental [Page 17]
+
+RFC 2567 Internet Printing Design Goals April 1999
+
+
+6. SECURITY CONSIDERATIONS
+
+ The security considerations for IPP are described in Section 4.1
+ above.
+
+7. REFERENCES
+
+ [ipp-iig] Hastings, T. and C. Manros, "Internet Printing
+ Protocol/1.0: Implementer's Guide", Work in Progress.
+
+ [RFC2569] Herriot, R., Hastings, T., Jacobs, N. and J. Martin,
+ "Mapping between LPD and IPP Protocols", RFC 2569, April
+ 1999.
+
+ [RFC2566] deBry, R., Hastings, T., Herriot, R., Isaacson, S. and P.
+ Powell, "Internet Printing Protocol/1.0: Model and
+ Semantics", RFC 2568, April 1999.
+
+ [RFC2565] Herriot, R., Butler, S., Moore, P. and R. Tuner, "Internet
+ Printing Protocol/1.0: Encoding and Transport", RFC 2565,
+ April 1999.
+
+ [RFC2568] Zilles, S., "Rationale for the Structure and Model and
+ Protocol for the Internet Printing Protocol", RFC 2568,
+ April 1999.
+
+ [ISO10175] ISO/IEC 10175, Document Printing Application, June 1996.
+
+ [RFC1179] McLaughlin, L., "Line Printer Daemon Protocol" RFC 1179,
+ August 1990.
+
+ [SSL] Netscape, The SSL Protocol, Version 3, (Text version
+ 3.02), November 1996.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Wright Experimental [Page 18]
+
+RFC 2567 Internet Printing Design Goals April 1999
+
+
+8. ACKNOWLEDGMENTS
+
+ This document draws heavily from preliminary work done by others
+ especially in the Printer Working Group (PWG). The author gratefully
+ acknowledges the specific contributions of:
+
+ Scott Isaacson Roger deBry
+ Novell Utah Valley State College
+ sisaacson@novell.com debryro@uvsc.edu
+
+ Carl-Uno Manros Robert Herriot
+ Xerox Sun
+ manros@cp10.es.xerox.com Robert.Herrior@pahv.xerox.xom
+
+ Tom Hastings Peter Zehler
+ Xerox Xerox
+ hastings@cp10.es.xerox.com Peter.Zehler@usa.xerox.com
+
+9. AUTHOR'S ADDRESS
+
+ F.D. (Don) Wright
+ Lexmark International
+ C14/035-3
+ 740 New Circle Rd
+ Lexington, KY 40550
+
+ Phone: 606-232-4808
+ Fax: 606-232-6740
+ EMail: don@lexmark.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Wright Experimental [Page 19]
+
+RFC 2567 Internet Printing Design Goals April 1999
+
+
+10. APPENDIX - DETAILED SCENARIOS
+
+ The following are more detailed scenarios illustrating how the
+ Internet Printing Protocol is expected to be used as a part of a
+ complete Internet Printing system. Some parts of the scenarios
+ include concepts, functions and information that may be outside of
+ the scope of version 1.0 of IPP (e.g. cost per page, payments means
+ available, etc.) The information contained herein is meant to be
+ generic. There may not be an exact wording or terminology match
+ between these scenarios and the implementation documents.
+
+10.1. PRINTER DISCOVERY WITHIN AN ENTERPRISE
+
+ A user wants to find a color Postscript printer in his/her enterprise
+ which will print transparencies. The client, directory service, and
+ printer are all behind the same corporate firewall. Because color
+ foils are expensive, printers of this type are access controlled and
+ require an account to be established so that printing can be billed
+ back to the using department. Note the request to find a printer
+ usable by Dept. J15. Drivers for all supported printers are
+ available from the server they are associated with. A help desk is
+ provided for end user support. The printer is unattended.
+
+ Client Directory Service
+
+ +---------------------------------------------------------- >
+ Find a printer with these characteristics
+ - prints color, prints transparencies
+ - prints Postscript
+ - is in building 003
+ - accessible by the client
+
+ < ----------------------------------------------------------+
+ Printer "Color-A"
+ - prints color, prints transparencies
+ - prints Postscript
+ - in room H-6, building 003
+ - driver ABC-Postscript-V1.3 required, here is URI
+ - cost is $.45 per page for color transparencies
+ - limit is 10 pages per job
+ - authentication required to use printer
+ - printer is unattended
+ - help desk at x5001
+
+ Printer "Color-B"
+ - prints color, prints transparencies
+ - prints Postscript
+ - in room J-10, building 003
+
+
+
+Wright Experimental [Page 20]
+
+RFC 2567 Internet Printing Design Goals April 1999
+
+
+ - driver XYZ-Postscript-V2.4 required, here is URI
+ - cost is $1.25 page for color transparencies
+ - limit is 5 pages per job
+ - authentication is required to use printer
+ - printer is unattended
+ - help desk at x5001
+
+10.2. PRINTER DISCOVERY ACROSS ENTERPRISES
+
+ A user in Company A wants to find a public printer in a business
+ partner's enterprise (Company B) on which to print a purchase order.
+ The client is behind one corporate firewall and the directory service
+ and the printer are behind a different corporate firewall. Drivers
+ for all supported printers are available from the server they are
+ associated with. A web page is provided for end user support for
+ public printers.
+
+ Client Company B Directory Service
+
+ +---------------------------------------------------------- >
+ Find a printer with these characteristics
+ - prints black and white
+ - is in El Segundo, building A
+ - is a public printer
+
+ < ----------------------------------------------------------+
+ Printer "Public-A"
+ - prints black and white
+ - prints Postscript
+ - in El Segundo, room H-6, building A
+ - driver ABC-Postscript-V1.3 required, here is URI
+ - printer is public
+ - help available at http://xerox/elSegundo/publicPrinters
+
+ Printer "Public-B"
+ - prints black and white
+ - prints PCL/5e
+ - is in El Segundo, room J-10, building A
+ - driver XYZ-PCL-V2.4 required, here is URI
+ - printer is public
+ - help available at http://xerox/elSegundo/publicPrinters
+
+10.3. PRINTER DISCOVERY ON THE INTERNET -LOGICAL OPERATIONS
+
+ A student wants to print a paper on a printer at his neighborhood
+ Ink-o's print shop. The report was written using Microsoft Word. The
+ student is interested in the cost of printing since his budget is
+ limited. Note the use of logical operators to find this information.
+
+
+
+Wright Experimental [Page 21]
+
+RFC 2567 Internet Printing Design Goals April 1999
+
+
+ Client Ink-o's Directory Service
+
+ +---------------------------------------------------------- >
+ Find a Printer with these characteristics
+ - prints color or black and white
+ - costs less than $.50 per page
+ - tell me about resolution and marking technology
+
+ < ----------------------------------------------------------+
+ Printer "Color-A"
+ - prints color
+ - 600 dpi laser printer
+ - prints Postscript
+ - driver ABC-Postscript-V1.3 required, here is URI
+ - cost is $.50 per page for color
+ - payment required prior to submitting print job
+ - here is URI for more information on Ink-o's
+
+ Printer "Mono-B"
+ - prints black and white
+ - 300 dpi inkjet printer
+ - prints Postscript
+ - driver XYZ-Postscript-V2.4 required, here is URI
+ - cost is $0.35 page for black and white
+ - payment required prior to submitting print job
+ - here is URI for more information on Ink-o's
+
+10.4. PRINTER DISCOVERY ON THE INTERNET - AUTHENTICATION
+
+ An executive in her hotel room is finishing an important presentation
+ on her laptop computer. She connects to a local print shop through
+ the web to get a copy of her charts printed for tomorrow's
+ presentation. She must find a print shop that is convenient to her
+ hotel and can print color transparencies. She wants to be sure that
+ the printer can be authenticated and can accept encrypted data.
+
+ Client SirZippy Directory Service
+
+ +---------------------------------------------------------- >
+ Find a Printer with these characteristics
+ - prints color transparencies
+ - is in Boulder, Colorado
+ - Printer can be authenticated
+ - Printer supports encryption
+
+
+
+
+
+
+
+Wright Experimental [Page 22]
+
+RFC 2567 Internet Printing Design Goals April 1999
+
+
+ Tell me when you are open for business
+
+ < ----------------------------------------------------------+
+ Printer "Color-A"
+ - prints color transparencies
+ - prints Postscript
+ - driver ABC-Postscript-V1.3 required, here is URI
+ - payment required prior to submitting print job
+ - Printer can be authenticated
+ - Data can be encrypted
+ - Located at 1670 Pearl Street, Boulder, CO
+ - This Branch is open 24 hours a day
+
+
+ Printer "Color-B"
+ - prints color transparencies
+ - prints Postscript
+ - driver ABC-Postscript-V1.3 required, here is URI
+ - payment required prior to submitting print job
+ - Printer can be authenticated
+ - Data can be encrypted
+ - Located at 1220 Arapahoe, Boulder, CO
+ - This Branch is open from 9:00 am to 6:30 pm
+
+10.5. DRIVER DOWNLOAD
+
+ An end user in an enterprise wants to print a lengthy report on a
+ newly installed high speed PostScript printer. Since she will likely
+ use this printer often, she would like to download a driver and
+ install it on her workstation. She is running Windows 95. Note:
+ Driver download is not a V1.0 design goal.
+
+ Client IPP Printer
+
+ +---------------------------------------------------------- >
+ Tell me where to find print drivers for you
+
+
+
+ < ----------------------------------------------------------+
+ Driver install file is at
+ http://www.ibm.com/drivers/NP12a/Win95
+
+
+
+
+
+
+
+
+
+Wright Experimental [Page 23]
+
+RFC 2567 Internet Printing Design Goals April 1999
+
+
+10.6. SUBMITTING A PRINT JOB AS A FILE
+
+ An end-user wants to submit a print job. The print file already
+ exists on his workstation. The client and printer are behind the same
+ corporate firewall. The printer is available to anyone behind the
+ firewall and no authorization or authentication is required. The data
+ is pushed to the printer. The printer is capable of spooling the
+ output. No errors occur.
+
+ Client IPP Printer
+
+ +---------------------------------------------------------- >
+ Here is a print job
+ - job name = MyJob
+ - notify me by email when done printing
+ - print on iso-a4-white paper
+ - print on both sides of the paper
+ - return status of the printer in response
+ - document is in Postscript format
+ - here is the document to print
+
+ < ----------------------------------------------------------+
+ Print job accepted and spooled
+ - job id = #12345
+ - current state of print job = spooled
+ - submission time = 02/12/97, 15:35
+ - printer state = printing
+
+10.7. SUBMITTING A PRINT JOB WITH TWO DOCUMENTS
+
+ An end-user wants to submit a print job. The print file already
+ exists on his workstation. The client and printer are behind the same
+ corporate firewall. The printer is available to anyone behind the
+ firewall and no authorization or authentication is required. The data
+ is pushed to the printer. The job consists of two separate documents.
+ The printer is capable of spooling the output. No errors occur.
+
+ Client IPP Printer
+
+ +---------------------------------------------------------- >
+ Here is a print job
+ - job name = MyJob
+ - notify me by email when done printing
+ - print on iso-a4-white paper
+ - print on both sides of the paper
+ - return status of the printer in response
+
+ < ----------------------------------------------------------+
+
+
+
+Wright Experimental [Page 24]
+
+RFC 2567 Internet Printing Design Goals April 1999
+
+
+ Print job accepted and spooled
+ - job id = #12345
+ - submission time = 02/12/97, 15:35
+ +---------------------------------------------------------- >
+ - here is the document to print
+
+ < ----------------------------------------------------------+
+ - OK
+
+ +---------------------------------------------------------- >
+ - here is the document to print, it is the last document.
+
+ < ----------------------------------------------------------+
+ - OK
+
+10.8. SUBMITTING A PRINT JOB AS A FILE, PRINTING FAILS
+
+ An end-user wants to submit a print job. The print file already
+ exists on his workstation. The client and printer are behind the same
+ corporate firewall. The printer is available to anyone behind the
+ firewall and no authorization or authentication is required. The data
+ is pushed to the printer. The printer is not capable of spooling the
+ output so it begins printing while still receiving the file. An error
+ occurs and the printer cannot complete printing (in this case the
+ user requires A4 paper and that paper size is not available on the
+ printer.)
+
+ Client IPP Printer
+
+ +---------------------------------------------------------- >
+ Here is a print job
+ - job name = MyJob
+ - notify me by email when done printing
+ - print on iso-a4-white paper
+ - print on both sides of the paper
+ - return status of the printer in response
+ - document is in Postscript format
+ - here is the document to print
+
+ < ----------------------------------------------------------+
+ Print job accepted
+
+ - printing failed
+ - current state of print job = canceled (A4 not available)
+ - submission time = 02/12/97, 15:35
+ - printer state = ready
+
+
+
+
+
+Wright Experimental [Page 25]
+
+RFC 2567 Internet Printing Design Goals April 1999
+
+
+10.9. SUBMITTING A PRINT JOB WITH AUTHENTICATION, PRIVACY AND PAYMENT
+
+ A traveling executive needs to print a set of transparencies for an
+ important business meeting. The charts are in Lotus Freelance format
+ on his notebook computer. He has located a SirZippy print shop near
+ his hotel that will print color transparencies. Because the
+ information on the charts is sensitive, he wants to be sure that his
+ data is sent to the Printer in an encrypted format. He also wants to
+ authenticate the Printer. The Printer also authenticates the user.
+ Payment occurs across the Internet.
+
+ Client IPP Printer
+
+ +---------------------------------------------------------- >
+ < ----------------------------------------------------------+
+
+ Mutual authentication and exchange of secret keys
+
+ +---------------------------------------------------------- >
+ Here is a print job (encrypted)
+ - job name = MyJob
+ - notify me by email when done printing
+ - print on iso-a4-white paper
+ - print on both sides of the paper
+ - return status of the printer in response
+ - tell me where to pick up output
+ - document is in Postscript format
+ - here is the document to print
+
+ < ----------------------------------------------------------+
+ Print job accepted and spooled (encrypted)
+ - job id = #12345
+ - current state of print job = spooled
+ - submission time = 02/12/97, 15:35
+ - printer state = printing
+ - payment required to proceed with job
+ - pick up at 230 East Main after 3:30 pm today
+
+ +---------------------------------------------------------- >
+ < ----------------------------------------------------------+
+ Payment transaction
+
+
+
+
+
+
+
+
+
+
+Wright Experimental [Page 26]
+
+RFC 2567 Internet Printing Design Goals April 1999
+
+
+10.10. SUBMITTING A PRINT JOB WITH DECRYPTION ERROR
+
+ A traveling executive needs to print a set of transparencies for an
+ important business meeting. The charts are in Lotus Freelance format
+ on his notebook computer. He has located a SirZippy print shop near
+ his hotel that will print color transparencies. Because the
+ information on the charts is sensitive, he wants to be sure that his
+ data is sent to the printer in an encrypted format. He also wants to
+ authenticate the printer. The printer also authenticates the user.
+ Payment occurs across the Internet. An error occurs during
+ decryption.
+
+ Client IPP Printer
+
+ +---------------------------------------------------------- >
+ < ----------------------------------------------------------+
+ Mutual authentication and exchange of secret keys
+
+
+ +---------------------------------------------------------- >
+ Here is a print job (encrypted)
+ - job name = MyJob
+ - notify me by email when done printing
+ - print on iso-a4-white paper
+ - print on both sides of the paper
+ - return status of the printer in response
+ - tell me where to pick up output
+ - document is in Postscript format
+ - here is the document to print
+
+ < ----------------------------------------------------------+
+ Print job accepted and spooled (encrypted)
+ - job id = #12345
+ - current state of print job = spooled
+ - submission time = 02/12/97, 15:35
+ - printer state = printing
+ - payment required to proceed with job
+ - pick up at 230 East Main after 3:30 pm today
+
+ +---------------------------------------------------------- >
+ < ----------------------------------------------------------+
+ Payment transaction
+ .
+ .
+ .
+ < ----------------------------------------------------------+
+ Asynchronous response (email in this case)
+ - decryption failed on job #12345
+
+
+
+Wright Experimental [Page 27]
+
+RFC 2567 Internet Printing Design Goals April 1999
+
+
+ - no pages printed
+ - current state of job = aborted
+
+10.11. SUBMITTING A PRINT JOB WITH AUTHENTICATION
+
+ An end-user wants to submit a print job. The print file already
+ exists on his workstation. The client and printer are behind the same
+ corporate firewall. The printer is available to anyone behind the
+ firewall but authentication and authorization is required.
+ Authorization takes place using the authenticated end-user's name.
+ The data is pushed to the printer. The printer is capable of spooling
+ the output.
+
+ Client IPP Printer
+
+ +---------------------------------------------------------- >
+ < ----------------------------------------------------------+
+ Authentication
+
+ Note: An authentication failure would end the transaction at
+ this point.
+
+ +---------------------------------------------------------- >
+ Here is a print job
+ - job name = MyJob
+ - notify me by email when done printing
+ - print on iso-a4-white paper
+ - print on both sides of the paper
+ - return status of the printer in response
+ - tell me where to pick up output
+ - document is in Postscript format
+ - here is the document to print
+
+ < ----------------------------------------------------------+
+ Print job accepted and spooled
+ - job id = #12345
+ - current state of print job = spooled
+ - submission time = 02/12/97, 15:35
+ - printer state = printing
+
+
+
+
+
+
+
+
+
+
+
+
+Wright Experimental [Page 28]
+
+RFC 2567 Internet Printing Design Goals April 1999
+
+
+10.12. SUBMITTING A PRINT JOB GENERATED DYNAMICALLY
+
+ An end-user wants to submit a print job. The print data is generated
+ dynamically and is being transmitted by a printer driver on the
+ client workstation as available. The client and printer are behind
+ the same corporate firewall. The printer is available to anyone
+ behind the firewall and no authentication and authorization is
+ required. The data is pushed to the printer. The printer is capable
+ of spooling the output. No error occurs.
+
+ Client IPP Printer
+
+ +---------------------------------------------------------- >
+ Here is a print job
+ - job name = MyJob
+ - notify me by email when done printing
+ - print on iso-a4-white paper
+ - print on both sides of the paper
+ - return status of the printer in response
+ - document is in Postscript format
+ - here is the print job
+
+
+ < ----------------------------------------------------------+
+ Print data accepted and spooling started
+ - job id = #12345
+ - current job state = spooled
+ - submission time = 02/12/97, 15:35
+ - printer state = printing
+
+10.13. SUBMITTING A PRINT JOB WITH A PRINTER JAM - CANCELED
+
+ An end-user wants to submit a print job. The print data is generated
+ dynamically and is being transmitted by a printer driver on the
+ client workstation as available. The client and printer are behind
+ the same corporate firewall. The printer is available to anyone
+ behind the firewall and no authentication and authorization is
+ required. The data is pushed to the printer. The printer is not
+ capable of spooling the output. The printer jams notifies the user
+ and the user chooses to cancel the job.
+
+ Client IPP Printer
+ +---------------------------------------------------------- >
+ Here is a print job
+ - job name = MyJob
+ - notify me by email when done printing
+ - print on iso-a4-white paper
+ - print on both sides of the paper
+
+
+
+Wright Experimental [Page 29]
+
+RFC 2567 Internet Printing Design Goals April 1999
+
+
+ - return status of the printer in response
+ - document is in Postscript format
+ - here is the document to print
+
+ < ----------------------------------------------------------+
+ Print data accepted and printing started
+ - job id = #12345
+
+ +---------------------------------------------------------- >
+ - What is the status of print job #12345?
+
+ < --------------------------------------------------------- +
+ - Job #12345 accepted but printer jammed, cannot continue
+
+ +---------------------------------------------------------- >
+ - Cancel job #12345
+
+ * Printer flushes remaining data
+ < ----------------------------------------------------------+
+ Print job terminated
+ - current job state = canceled
+ - submission time = 02/12/97, 15:35
+ - printer state = jammed
+
+10.14. SUBMITTING A PRINT JOB WITH A PRINTER JAM - RECOVERED
+
+ An end-user wants to submit a print job. The print data is generated
+ dynamically and is being transmitted by a printer driver on the
+ client workstation as available. The client and printer are behind
+ the same corporate firewall. The printer is available to anyone
+ behind the firewall and no authentication and authorization is
+ required. The data is pushed to the printer. The printer is not
+ capable of spooling the output. The printer jams, notifies the user
+ and the user clears the jam and elects to continue.
+
+ Client IPP Printer
+
+ +---------------------------------------------------------- >
+ Here is a print job
+ - job name = MyJob
+ - notify me by email when done printing
+ - print on iso-a4-white paper
+ - print on both sides of the paper
+ - return status of the printer in response
+ - document is in Postscript format
+ - here is the document to print
+
+ < ----------------------------------------------------------+
+
+
+
+Wright Experimental [Page 30]
+
+RFC 2567 Internet Printing Design Goals April 1999
+
+
+ Print data accepted and printing started
+ - job id = #12345
+
+ < --------------------------------------------------------- +
+ - Notification: printer jammed, cannot continue
+
+ * Jam is clear by human intervention, printing continues
+
+ +---------------------------------------------------------- >
+ Here is the last part of the document to print
+
+ < ----------------------------------------------------------+
+ Print job received
+ - current job state = printing
+ - submission time = 02/12/97, 15:35
+ - printer state = printing
+
+10.15. SUBMITTING A PRINT JOB WITH SERVER PULL
+
+ An end-user wants to submit a print job. The print data is in a file
+ and is publicly available. It is pulled by the printer. The client
+ and printer are behind the same corporate firewall. The printer is
+ available to anyone behind the firewall and no authentication and
+ authorization is required. The printer is capable of spooling the
+ output. Printing may start before the entire job has been pulled.
+
+ Client IPP Printer
+
+ +---------------------------------------------------------- >
+ Here is a print job
+
+ - job name = MyJob
+ - notify me by email when done printing
+ - print on iso-a4-white paper
+ - print on both sides of the paper
+ - return status of the printer in response
+ - here is a reference to the data to be printed
+
+ < ----------------------------------------------------------+
+ Print data accepted and printing started
+ - job id = #12345
+ - current state of job = spooled
+ - submission time = 02/12/97, 13:15
+ - printer state = printing
+
+ .
+ .
+ < ----------------------------------------------------------+
+
+
+
+Wright Experimental [Page 31]
+
+RFC 2567 Internet Printing Design Goals April 1999
+
+
+ Get the file to be printed
+
+ +---------------------------------------------------------- >
+ Here it is
+
+ Note: Failure to find the file, would end the transaction
+ with an error at this point and an asynchronous
+ notification would be send to the Client.
+
+ < ----------------------------------------------------------+
+ Data received
+
+10.16. SUBMITTING A PRINT JOB WITH REFERENCED RESOURCES
+
+ An end-user wants to submit a print job. Part of the print data is
+ on a file on the user's workstation. It is pushed by the client, but
+ the print job requires some resource not included in the print file.
+ The client and printer are behind the same corporate firewall. The
+ printer is available to anyone behind the firewall and no
+ authentication and authorization is required. The printer is capable
+ of spooling the output. No errors occur.
+
+ Client IPP Printer
+
+ +---------------------------------------------------------- >
+ Here is a print job
+ - job name = MyJob
+ - notify me by email when done printing
+ - print on iso-a4-white paper
+ - print on both sides of the paper
+ - return status of the printer in response
+
+ < ----------------------------------------------------------+
+ Print job accepted and spooled
+ - job id = #12345
+ - submission time = 02/12/97, 15:35
+
+ +---------------------------------------------------------- >
+ - here is the document to print
+
+ < ----------------------------------------------------------+
+ - OK
+
+ +---------------------------------------------------------- >
+ - here is the URI to print, it is the last document.
+
+ < ----------------------------------------------------------+
+ - OK
+
+
+
+Wright Experimental [Page 32]
+
+RFC 2567 Internet Printing Design Goals April 1999
+
+
+ < ----------------------------------------------------------+
+ Get the external resource
+
+ +---------------------------------------------------------- >
+ Here it is
+
+10.17. GETTING CAPABILITIES
+
+10.17.1. Submission Attributes
+
+ An end-user wants to get the production and scheduling attributes
+ that are supported or required when submitting jobs to this printer.
+ The client will use these attributes when forming the subsequent
+ print request.
+
+ Client IPP Printer
+ +---------------------------------------------------------- >
+ I'm going to submit a Postscript job
+ give me your job submission attributes
+
+ < ----------------------------------------------------------+
+ Postscript production attributes for this Printer are:
+ - medium-select = us-letter-white, us-legal-white
+ - default is us-letter-white
+ - copies = 1,2,3,4,5
+ - default is 1
+ - print-quality = draft, normal, high
+ - default is draft
+ - sides = 1-sided, 2-sided-long-edge
+ - default is 2-sided-long-edge
+ - Job scheduling attributes for this Printer are:
+ - job-priority = 1,2,3
+ - default = 3
+
+10.17.2. Printer Capabilities
+
+ An end-user wants to determine the resolution, marking technology,
+ and PDLs supported by the printer.
+
+ Client IPP Printer
+ +---------------------------------------------------------- >
+ Please tell me the
+ - resolution of the printer
+ - the marking technology of the printer
+ - PDLs supported
+ < ----------------------------------------------------------+
+ Printer resolution = 600 dpi
+ Marking Technology = laser
+
+
+
+Wright Experimental [Page 33]
+
+RFC 2567 Internet Printing Design Goals April 1999
+
+
+ PDLs supported = Postscript level 2, PCL/6
+
+10.18. GETTING STATUS
+
+10.18.1. Printer State/Status
+
+ An end-user wants to determine the state or status of the printer.
+
+ Client IPP Printer
+
+ +---------------------------------------------------------- >
+ What is the state of the printer?
+
+ < ----------------------------------------------------------+
+ Printer state = out-of-paper
+
+10.18.2. Job Status
+
+ An end user wants to get the status of a job he has submitted.
+
+ Client IPP Printer
+
+ +---------------------------------------------------------- >
+ Please tell me the status of job #12345
+
+ < ----------------------------------------------------------+
+ Job #12345 is queued
+ it is number 3 in the queue
+ printer state = printing
+
+10.18.3. Status of All My Jobs
+
+ An end user wants to get a list of all of the jobs he has submitted
+ to this Printer.
+
+ Client IPP Printer
+
+ +---------------------------------------------------------- >
+ Please tell me the status of my jobs
+
+ < ----------------------------------------------------------+
+ Job #00012 is complete
+ Printed at 12:35 on 01/23/97
+
+ Job #09876 is printing
+
+ Job #12345 is queued
+ it is number 3 in the queue
+
+
+
+Wright Experimental [Page 34]
+
+RFC 2567 Internet Printing Design Goals April 1999
+
+
+ Job #34567 is queued
+ it is number 7 in the queue
+
+10.19. ASYNCHRONOUS NOTIFICATION
+
+10.19.1. Job Completion
+
+ An end-user wants to get notification of events that affect his print
+ jobs. Print job completes without error.
+
+ Client IPP Printer
+
+ < ----------------------------------------------------------+
+ Print job #123 completed
+
+10.19.2. Job Complete with Data
+
+ An end-user wants to get notification of events that affect his print
+ jobs. Print job completes, users asked for all end of job
+ information.
+
+ Client IPP Printer
+
+ < ----------------------------------------------------------+
+ Print job #123 completed
+ - total pages printed = 15
+ - number of copies printed = 3
+ - total cost to print = $7.45
+ - pick up copies in room H-6, building 005
+
+10.19.3. Print Job Fails
+
+ An end-user wants to get notification of events that affect his print
+ jobs. Print job fails. Printer is unattended.
+
+ Client IPP Printer
+
+ < ----------------------------------------------------------+
+ Print job #123 failed
+ - total pages printed = 15
+ - number of pages submitted = 25
+ - printer-state = jammed
+
+
+
+
+
+
+
+
+
+Wright Experimental [Page 35]
+
+RFC 2567 Internet Printing Design Goals April 1999
+
+
+10.20. CANCEL A JOB
+
+ The end-user submits a print job and later decides to cancel it.
+
+ Client IPP Printer
+
+ +---------------------------------------------------------- >
+ < ----------------------------------------------------------+
+ Authentication.
+
+
+ +---------------------------------------------------------- >
+ Cancel job #1234
+
+ < ----------------------------------------------------------+
+ Job #1234 Canceled
+
+
+10.21. END TO END SCENARIO - WITHIN AN ENTERPRISE
+
+ An office worker prints on shared departmental printers. All printers
+ in the office are public, that is, no authentication or authorization
+ is required. Printers are protected from external access by a
+ firewall. No billing or accounting is required. Most printing is done
+ from desktop applications. A help desk is provided for printing
+ problems. Standard operating systems and applications are used.
+ Drivers are available, but are installed manually by support
+ personnel. This scenario assumes that drivers have been installed and
+ that drivers are not IPP aware, that is, they cannot communicate
+ across an IPP connection to obtain status and capabilities. IPP
+ printers appear in application pull-down menus. Printer
+ configuration data is hard wired into the driver.
+
+ End-user selects print from the application pull down menu. An IPP
+ printer is selected from the list of Printers offered
+
+ The driver puts up a dialogue with hard-wired set of options for this
+ printer. The end-user makes choices and submits job.
+
+ Client IPP Printer
+ +---------------------------------------------------------- >
+ Here is a print job
+ - job-name = memo-to-boss
+ - notify me by email when job is complete
+ - print on us-letter-white paper
+ - print 1 copy
+ - print at normal quality
+ - print on 1 side
+
+
+
+Wright Experimental [Page 36]
+
+RFC 2567 Internet Printing Design Goals April 1999
+
+
+ - give me the state of the printer in response
+
+ The driver generates the print data and passes it to the IPP driver a
+ piece at a time as it is generated.
+
+ +---------------------------------------------------------- >
+ Here is the print data
+
+
+ < ----------------------------------------------------------+
+ Print data received, file is spooled
+ - printer state = printing
+ - time submitted = 2/12/97, 15:35
+ - current job state = spooled
+
+ Client adds this job to list of current jobs. List of jobs and state
+ of each is available on a pull-down menu on the client.
+
+ End-user selects job #1234 from list and clicks on it to see its
+ status.
+
+ +---------------------------------------------------------- >
+ Give me the state of job #1234
+ and the state of the Printer
+
+ < ----------------------------------------------------------+
+ Job #1234 state = spooled
+ - it is number 3 in the queue
+ - printer state = printing
+
+ The job completes without error
+
+ < ----------------------------------------------------------+
+ Job #1234 completed
+ 12 of 12 pages printed
+
+10.22. END TO END SCENARIO - ACROSS ENTERPRISES
+
+ An office worker in Company A needs to print an office document on a
+ "public" printer at Company B, a business partner. Both companies
+ have corporate firewalls so the print request must flow out of A's
+ firewall and into B's firewall. The office worker can look at public
+ printers in Company B's directory service. The document is generated
+ by a desktop application. Since the printer is "public" no
+ authentication or authorization is required. A driver is downloaded.
+ The driver is IPP aware, that is, it can communicate dynamically
+ through the IPP protocol layer to obtain information about the
+ printer.
+
+
+
+Wright Experimental [Page 37]
+
+RFC 2567 Internet Printing Design Goals April 1999
+
+
+ Client Company B's Directory Service
+
+ End user connects to B's Directory service
+
+ +---------------------------------------------------------- >
+ Find a Printer with these characteristics
+ - public (no authorization or authentication required)
+ - is in Lexington, building 004
+ - prints black and white
+
+ < ----------------------------------------------------------+
+ Printer "Public-A"
+ - http://www.lexmark.com/pubprinter/a
+
+ Printer "Public-B"
+ - http://www.lexmark.com/pubprinter/b
+
+ End user selects Public-A
+
+ Client Public-A
+
+ +---------------------------------------------------------- >
+ Where can I find a driver for you?
+
+ < ----------------------------------------------------------+
+ Drivers at http://www.lexmark.com/pubprinters/a/os245
+
+ End user gets driver and installs it on his PC.
+
+ End-user selects print from the application pull down menu. "Public-
+ A" is selected from the list of Printers offered
+
+ +---------------------------------------------------------- >
+ I'm going to submit a print job
+ give me your job submission attributes
+
+ < ----------------------------------------------------------+
+
+ Production attributes for this Printer are:
+ - medium-select = us-letter-white, us-legal-white
+ - default is us-letter-white
+ - copies = 1,2,3,4,5
+ - default is 1
+ - print-quality = draft, normal, high
+ - default is draft
+ - sides = 1-sided, 2-sided-long-edge
+ - default is 2-sided-long-edge
+
+
+
+
+Wright Experimental [Page 38]
+
+RFC 2567 Internet Printing Design Goals April 1999
+
+
+ Job scheduling attributes for this Printer are:
+ - job-priority = 1,2,3
+ default = 3
+
+ Driver puts up dialogue with available options and fills in the
+ defaults.
+
+ End-user makes choices and submits job
+
+ +---------------------------------------------------------- >
+ Here is a print job
+ - job-name = memo-to-Don-Wright
+ - notify me by email when job is complete
+ - print on us-letter-white paper
+ - print 1 copy
+ - print at normal quality
+ - print on 1 side
+ - give me the state of the printer in response
+
+
+ The driver generates the print data and passes it to the IPP driver a
+ piece at a time.
+
+ +---------------------------------------------------------- >
+ Here is the print data
+
+ < ----------------------------------------------------------+
+ Print data received, and spooling started
+ print job id = #1234
+
+ Print data received, file is spooled
+
+ - printer state = printing
+ - time submitted = 2/12/97, 15:35
+ - current job state = spooled
+
+ Client adds this job to list of current jobs. List of jobs and state
+ of each is available on a pull-down menu on the client.
+
+ End-user selects job #1234 from list and clicks on it to see its
+ status.
+
+ +---------------------------------------------------------- >
+ Give me the state of job #1234
+ and the state of the Printer
+
+ < ----------------------------------------------------------+
+ Job #1234 state = spooled
+
+
+
+Wright Experimental [Page 39]
+
+RFC 2567 Internet Printing Design Goals April 1999
+
+
+ - it is number 3 in the queue
+ - printer state = printing
+
+ * The job completes without error
+ < ----------------------------------------------------------+
+ Job #1234 completed
+ 12 of 12 pages printed
+
+10.23. END TO END SCENARIO - ON THE INTERNET
+
+ An executive in her hotel room is finishing an important presentation
+ on her laptop computer. She connects to a local print shop through
+ the web to get a copy of her charts printed for tomorrow's
+ presentation. She must find a print shop that is convenient and can
+ print color transparencies. She must download and temporarily install
+ a driver in order to generate the PDL required by the print shop.
+ Mutual authentication is required by the print shop and payment must
+ be made in advance. The job is encrypted on the wire to prevent
+ eavesdropping.
+
+ End-user completes presentation. She goes to the web and connects to
+ the SirZippy home page.
+
+ Client SirZippy Directory Service
+ +---------------------------------------------------------- >
+
+ Find me a printer with these characteristics
+ - Near Market Street in San Jose
+ - Prints color transparencies
+ - drivers can be downloaded
+ - supports privacy (encryption)
+ -
+
+ Available Printers matching these characteristics are looked up in the
+ Directory Service
+
+ < ----------------------------------------------------------+
+
+ Printer "Color-A"
+ - located at 123 First Street in San Jose
+ - URI is http://www.SirZippy.com/FirstStreet/Color-A
+ - prints color transparencies
+ - 600 dpi laser
+ - driver ABC-Postscript-V1.3 available at this URI
+ - cost = $.75 per page
+ - authentication required to use printer
+ - payment required prior to printing
+
+
+
+
+Wright Experimental [Page 40]
+
+RFC 2567 Internet Printing Design Goals April 1999
+
+
+ Printer "Color-B"
+ - located at 67 San Carlos Street, San Jose
+ - URI is http://www.SirZippy.com/SanCarlos/Color-B
+ - prints color transparencies
+ - 1200 dpi laser
+ - driver XYZ-PostScript-V4.3 available at this URI
+ - cost = $1.25 per page
+ - authentication required to use printer
+ - payment required prior to printing
+ - more information at this URI
+
+ The user decides to use the first printer because it is closer. She
+ connects to the URI given to get a driver.
+
+ Client Driver URI
+
+ +---------------------------------------------------------- >
+ I need a driver for "Color-A"
+
+
+ < ----------------------------------------------------------+
+ Driver installer is at http://www.xerox.com/prtdrvrs
+
+ Driver is installed
+
+ User connects to
+ "Color-A"
+
+ Client IPP Printer "Color-A"
+
+ +---------------------------------------------------------- >
+ < ----------------------------------------------------------+
+ Mutual authentication and exchange of secret keys
+
+ +---------------------------------------------------------- >
+ I'm going to submit a print job
+ give me your job submission attributes
+
+ < ----------------------------------------------------------+
+ Production attributes for this Printer are:
+ - medium-select = us-letter-white, us-legal-white
+ - default is us-letter-white
+ - copies = 1,2,3,4,5
+ - default is 1
+ - print-quality = draft, normal, high
+ - default is draft
+ - sides = 1-sided, 2-sided-long-edge
+ - default is 2-sided-long-edge
+
+
+
+Wright Experimental [Page 41]
+
+RFC 2567 Internet Printing Design Goals April 1999
+
+
+ Job scheduling attributes for this Printer are:
+ - job-priority = 1,2,3
+ default = 3
+
+ Driver puts up dialogue with available options and fills in the
+ defaults.
+
+ End-user makes choices and submits job
+
+ +---------------------------------------------------------- >
+ Here is a print job
+
+ - job-name = presentation
+ - notify me by email when job is complete
+ - print on us-letter-transparency
+ - print 1 copy
+ - print at high quality
+ - print by 9:00 am tomorrow morning
+ - give me the state of the printer in response
+
+ The driver generates the print data and passes it to the IPP driver a
+ piece at a time.
+
+ +---------------------------------------------------------- >
+ Here is the print data
+
+ < ---------------------------------------------------------+
+ Print data received, and spooling started
+ print job id = #1234
+
+ Print data received, file is spooled
+ - printer state = printing
+ - time submitted = 2/12/97, 15:35
+ - current job state = held, waiting for payment
+
+ +---------------------------------------------------------- >
+ < ----------------------------------------------------------+
+ Payment transaction
+
+ < ----------------------------------------------------------+
+ Job is scheduled to print, pick up after 9:00am tomorrow
+ Thank you for using SirZippy
+
+
+
+
+
+
+
+
+
+Wright Experimental [Page 42]
+
+RFC 2567 Internet Printing Design Goals April 1999
+
+
+11. Full Copyright Statement
+
+ Copyright (C) The Internet Society (1999). All Rights Reserved.
+
+ This document and translations of it may be copied and furnished to
+ others, and derivative works that comment on or otherwise explain it
+ or assist in its implementation may be prepared, copied, published
+ and distributed, in whole or in part, without restriction of any
+ kind, provided that the above copyright notice and this paragraph are
+ included on all such copies and derivative works. However, this
+ document itself may not be modified in any way, such as by removing
+ the copyright notice or references to the Internet Society or other
+ Internet organizations, except as needed for the purpose of
+ developing Internet standards in which case the procedures for
+ copyrights defined in the Internet Standards process must be
+ followed, or as required to translate it into languages other than
+ English.
+
+ The limited permissions granted above are perpetual and will not be
+ revoked by the Internet Society or its successors or assigns.
+
+ This document and the information contained herein is provided on an
+ "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING
+ TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+ HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+ MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Wright Experimental [Page 43]
+
diff --git a/standards/rfc2568.txt b/standards/rfc2568.txt
new file mode 100644
index 000000000..2d3ae4905
--- /dev/null
+++ b/standards/rfc2568.txt
@@ -0,0 +1,563 @@
+
+
+
+
+
+
+Network Working Group S. Zilles
+Request for Comments: 2568 Adobe Systems Inc.
+Category: Experimental April 1999
+
+
+ Rationale for the Structure of the Model and Protocol
+ for the Internet Printing Protocol
+
+Status of this Memo
+
+ This memo defines an Experimental Protocol for the Internet
+ community. It does not specify an Internet standard of any kind.
+ Discussion and suggestions for improvement are requested.
+ Distribution of this memo is unlimited.
+
+Copyright Notice
+
+ Copyright (C) The Internet Society (1999). All Rights Reserved.
+
+IESG Note
+
+ This document defines an Experimental protocol for the Internet
+ community. The IESG expects that a revised version of this protocol
+ will be published as Proposed Standard protocol. The Proposed
+ Standard, when published, is expected to change from the protocol
+ defined in this memo. In particular, it is expected that the
+ standards-track version of the protocol will incorporate strong
+ authentication and privacy features, and that an "ipp:" URL type will
+ be defined which supports those security measures. Other changes to
+ the protocol are also possible. Implementors are warned that future
+ versions of this protocol may not interoperate with the version of
+ IPP defined in this document, or if they do interoperate, that some
+ protocol features may not be available.
+
+ The IESG encourages experimentation with this protocol, especially in
+ combination with Transport Layer Security (TLS) [RFC2246], to help
+ determine how TLS may effectively be used as a security layer for
+ IPP.
+
+ABSTRACT
+
+ This document is one of a set of documents, which together describe
+ all aspects of a new Internet Printing Protocol (IPP). IPP is an
+ application level protocol that can be used for distributed printing
+ using Internet tools and technologies. This document describes IPP
+ from a high level view, defines a roadmap for the various documents
+ that form the suite of IPP specifications, and gives background and
+ rationale for the IETF working group's major decisions.
+
+
+
+Zilles Experimental [Page 1]
+
+RFC 2568 Rationale for IPP April 1999
+
+
+ The full set of IPP documents includes:
+
+ Design Goals for an Internet Printing Protocol [RFC2567]
+ Rationale for the Structure and Model and Protocol for the
+ Internet Printing Protocol (this document)
+ Internet Printing Protocol/1.0: Model and Semantics [RFC2566]
+ Internet Printing Protocol/1.0: Encoding and Transport [RFC2565]
+ Internet Printing Protocol/1.0: Implementer's Guide [ipp-iig]
+ Mapping between LPD and IPP Protocols [RFC2569]
+
+ The "Design Goals for an Internet Printing Protocol" document takes a
+ broad look at distributed printing functionality, and it enumerates
+ real-life scenarios that help to clarify the features that need to be
+ included in a printing protocol for the Internet. It identifies
+ requirements for three types of users: end users, operators, and
+ administrators. The Design Goals document calls out a subset of end
+ user requirements that are satisfied in IPP/1.0. Operator and
+ administrator requirements are out of scope for version 1.0.
+
+ The "Internet Printing Protocol/1.0: Model and Semantics" document
+ describes a simplified model consisting of abstract objects, their
+ attributes, and their operations that is independent of encoding and
+ transport. The model consists of a Printer and a Job object. The
+ Job optionally supports multiple documents. This document also
+ addresses security, internationalization, and directory issues.
+
+ The "Internet Printing Protocol/1.0: Encoding and Transport" document
+ is a formal mapping of the abstract operations and attributes defined
+ in the model document onto HTTP/1.1. It defines the encoding rules
+ for a new Internet media type called "application/ipp".
+
+ The "Internet Printing Protocol/1.0: Implementer's Guide" document
+ gives insight and advice to implementers of IPP clients and IPP
+ objects. It is intended to help them understand IPP/1.0 and some of
+ the considerations that may assist them in the design of their client
+ and/or IPP object implementations. For example, a typical order of
+ processing requests is given, including error checking. Motivation
+ for some of the specification decisions is also included.
+
+ The "Mapping between LPD and IPP Protocols" document gives some
+ advice to implementers of gateways between IPP and LPD (Line Printer
+ Daemon) implementations.
+
+1. ARCHITECTURAL OVERVIEW
+
+ The Internet Printing Protocol (IPP) is an application level protocol
+ that can be used for distributed printing on the Internet. This
+ protocol defines interactions between a client and a server. The
+
+
+
+Zilles Experimental [Page 2]
+
+RFC 2568 Rationale for IPP April 1999
+
+
+ protocol allows a client to inquire about capabilities of a printer,
+ to submit print jobs and to inquire about and cancel print jobs. The
+ server for these requests is the Printer; the Printer is an
+ abstraction of a generic document output device and/or a print
+ service provider. Thus, the Printer could be a real printing device,
+ such as a computer printer or fax output device, or it could be a
+ service that interfaced with output devices.
+
+ The protocol is heavily influenced by the printing model introduced
+ in the Document Printing Application (DPA) [ISO10175] standard.
+ Although DPA specifies both end user and administrative features, IPP
+ version 1.0 (IPP/1.0) focuses only on end user functionality.
+
+ The architecture for IPP defines (in the Model and Semantics document
+ [RFC2566]) an abstract Model for the data which is used to control
+ the printing process and to provide information about the process and
+ the capabilities of the Printer. This abstract Model is hierarchical
+ in nature and reflects the structure of the Printer and the Jobs that
+ may be being processed by the Printer.
+
+ The Internet provides a channel between the client and the
+ server/Printer. Use of this channel requires flattening and
+ sequencing the hierarchical Model data. Therefore, the IPP also
+ defines (in the Encoding and Transport document [RFC2565]) an
+ encoding of the data in the model for transfer between the client and
+ server. This transfer of data may be either a request or the
+ response to a request.
+
+ Finally, the IPP defines (in the Encoding and Transport document
+ [RFC2565]) a protocol for transferring the encoded request and
+ response data between the client and the server/Printer.
+
+ An example of a typical interaction would be a request from the
+ client to create a print job. The client would assemble the Model
+ data to be associated with that job, such as the name of the job, the
+ media to use, the number of pages to place on each media instance,
+ etc. This data would then be encoded according to the Protocol and
+ would be transmitted according to the Protocol. The server/Printer
+ would receive the encoded Model data, decode it into a form
+ understood by the server/Printer and, based on that data, do one of
+ two things: (1) accept the job or (2) reject the job. In either case,
+ the server must construct a response in terms of the Model data,
+ encode that response according to the Protocol and transmit that
+ encoded Model data as the response to the request using the Protocol.
+
+ Another part of the IPP architecture is the Directory Schema
+ described in the model document. The role of a Directory Schema is to
+ provide a standard set of attributes which might be used to query a
+
+
+
+Zilles Experimental [Page 3]
+
+RFC 2568 Rationale for IPP April 1999
+
+
+ directory service for the URI of a Printer that is likely to meet the
+ needs of the client. The IPP architecture also addresses security
+ issues such as control of access to server/Printers and secure
+ transmissions of requests, response and the data to be printed.
+
+2. THE PRINTER
+
+ Because the (abstract) server/Printer encompasses a wide range of
+ implementations, it is necessary to make some assumptions about a
+ minimal implementation. The most likely minimal implementation is one
+ that is embedded in an output device running a specialized real time
+ operating system and with limited processing, memory and storage
+ capabilities. This printer will be connected to the Internet and will
+ have at least a TCP/IP capability with (likely) SNMP [RFC1905,
+ RFC1906] support for the Internet connection. In addition, it is
+ likely the the Printer will be an HTML/HTTP server to allow direct
+ user access to information about the printer.
+
+3. RATIONALE FOR THE MODEL
+
+ The Model [RFC2566] is defined independently of any encoding of the
+ Model data both to support the likely uses of IPP and to be robust
+ with respect to the possibility of alternate encoding.
+
+ It is expected that a client or server/Printer would represent the
+ Model data in some data structure within the applications/servers
+ that support IPP. Therefore, the Model was designed to make that
+ representation straightforward. Typically a parser or formatter would
+ be used to convert from or to the encoded data format. Once in an
+ internal form suitable to a product, the data can be manipulated by
+ the product. For example, the data sent with a Print Job can be used
+ to control the processing of that Print Job.
+
+ The semantics of IPP are attached to the (abstract) Model.
+ Therefore, the application/server is not dependent on the encoding of
+ the Model data, and it is possible to consider alternative mechanisms
+ and formats by which the data could be transmitted from a client to a
+ server; for example, a server could have a direct, client-less GUI
+ interface that might be used to accept some kinds of Print Jobs. This
+ independence would also allow a different encoding and/or
+ transmission mechanism to be used if the ones adopted here were shown
+ to be overly limiting in the future. Such a change could be migrated
+ into new products as an alternate protocol stack/parser for the Model
+ data.
+
+
+
+
+
+
+
+Zilles Experimental [Page 4]
+
+RFC 2568 Rationale for IPP April 1999
+
+
+ Having an abstract Model also allows the Model data to be aligned
+ with the (abstract) model used in the Printer [RFC1759], Job and Host
+ Resources MIBs. This provides consistency in interpretation of the
+ data obtained independently of how the data is accessed, whether via
+ IPP or via SNMP [RFC1905, RFC1906] and the Printer/Job MIBs.
+
+ There is one aspect of the Model that deserves some extra
+ explanation. There are two ways for identifying a Job object: (a)
+ with a Job URI and (b) using a combination of the Printer URI and a
+ Job ID (a 32 bit positive integer). Allowing Job objects to have URIs
+ allows for flexibility and scalability. For example a job could be
+ moved from a printer with a large backlog to one with a smaller load
+ and the job identification, the Job object URI, need not change.
+ However, many existing printing systems have local models or
+ interface constraints that force Job objects to be identified using
+ only a 32-bit positive integer rather than a URI. This numeric Job
+ ID is only unique within the context of the Printer object to which
+ the create request was originally submitted. In order to allow both
+ types of client access to Jobs (either by Job URI or by numeric Job
+ ID), when the Printer object successfully processes a create request
+ and creates a new Job, the Printer object generates both a Job URI
+ and a Job ID for the new Job object. This requirement allows all
+ clients to access Printer objects and Job objects independent of any
+ local constraints imposed on the client implementation.
+
+4. RATIONALE FOR THE PROTOCOL
+
+ There are two parts to the Protocol: (1) the encoding of the Model
+ data and (2) the mechanism for transmitting the model data between
+ client and server.
+
+4.1 The Encoding
+
+ To make it simpler to develop embedded printers, a very simple binary
+ encoding has been chosen. This encoding is adequate to represent the
+ kinds of data that occur within the Model. It has a simple structure
+ consisting of sequences of attributes. Each attribute has a name,
+ prefixed by a name length, and a value. The names are strings
+ constrained to characters from a subset of ASCII. The values are
+ either scalars or a sequence of scalars. Each scalar value has a
+ length specification and a value tag which indicates the type of the
+ value. The value type has two parts: a major class part, such as
+ integer or string, and a minor class part which distinguishes the
+ usage of the major class, such as dateTime string. Tagging of the
+ values with type information allows for introducing new value types
+ at some future time.
+
+
+
+
+
+Zilles Experimental [Page 5]
+
+RFC 2568 Rationale for IPP April 1999
+
+
+ A fully encoded request/response has a version number, an operation
+ (for a request) or a status and optionally a status message (for a
+ response), associated parameters and attributes which are encoded
+ Model data and, optionally (for a request), print data following the
+ Model data.
+
+4.2 The Transmission Mechanism
+
+ The chosen mechanism for transmitting the encoded Model data is HTTP
+ 1.1 Post (and associated response). No modifications to HTTP 1.1 are
+ proposed or required. The sole role of the Transmission Mechanism is
+ to provide a transfer of encoded Model data from/to the client
+ to/from the server. This could be done using any data delivery
+ mechanism. The key reasons why HTTP 1.1 Post is used are given below.
+ The most important of these is the first. With perhaps this
+ exception, these reasons could be satisfied by other mechanisms.
+ There is no claim that this list uniquely determines a choice of
+ mechanism.
+
+ 1. HTTP 1.0 is already widely deployed and, based on the recent
+ evidence, HTTP 1.1 is being widely deployed as the manufacturers
+ release new products. The performance benefits of HTTP 1.1 have
+ been shown and manufactures are reacting positively.
+
+ Wide deployment has meant that many of the problems of making a
+ protocol work in a wide range of environments from local net to
+ Intranet to Internet have been solved and will stay solved with
+ HTTP 1.1 deployment.
+
+ 2. HTTP 1.1 solves most of the problems that might have required a
+ new protocol to be developed. HTTP 1.1 allows persistent
+ connections that make a multi-message protocol be more efficient;
+ for example it is practical to have separate Create-Job and Send-
+ Document messages. Chunking allows the transmission of large print
+ files without having to pre-scan the file to determine the file
+ length. The accept headers allow the client's protocol and
+ localization desires to be transmitted with the IPP operations and
+ data. If the Model were to provide for the redirection of Job
+ requests, such as Cancel-Job, when a Job is moved, the HTTP
+ redirect response allows a client to be informed when a Job he is
+ interested in is moved to another server/Printer for any reason.
+
+ 3. Most network Printers will be implementing HTTP servers for
+ reasons other than IPP. These network attached Printers want to
+ provide information on how to use the printer, its current state,
+ HELP information, etc. in HTML. This requires having an HTTP
+ server which would be available to do IPP functions as well.
+
+
+
+
+Zilles Experimental [Page 6]
+
+RFC 2568 Rationale for IPP April 1999
+
+
+ 4. Most of the complexity of HTTP 1.1 is concerned with the
+ implementation of HTTP proxies and not the implementation of HTTP
+ clients and/or servers. Work is proceeding in the HTTP Working
+ Group to help identify what must be done by a server. As the
+ Encoding and Transport document shows, that is not very much.
+
+ 5. HTTP implementations provide support for handling URLs that
+ would have to be provided if a new protocol were defined.
+
+ 6. An HTTP based solution fits well with the Internet security
+ mechanisms that are currently deployed or being deployed. HTTP
+ will run over SSL3. The digest access authentication mechanism of
+ HTTP 1.1 provides an adequate level of access control. These
+ solutions are deployed and in practical use; a new solution would
+ require extensive use to have the same degree of confidence in its
+ security. Note: SSL3 is not on the IETF standards track.
+
+ 7. HTTP provides an extensibility model that a new protocol would
+ have to develop independently. In particular, the headers,
+ intent-types (via Internet Media Types) and error codes have wide
+ acceptance and a useful set of definitions and methods for
+ extension.
+
+ 8. Although not strictly a reason why IPP should use HTTP as the
+ transmission protocol, it is extremely helpful that there are many
+ prototyping tools that work with HTTP and that CGI scripts can be
+ used to test and debug parts of the protocol.
+
+ 9. Finally, the POST method was chosen to carry the print data
+ because its usage for data transmission has been established, it
+ works and the results are available via CGI scripts or servlets.
+ Creating a new method would have better identified the intended
+ use of the POSTed data, but a new method would be more difficult
+ to deploy. Assigning a new default port for IPP provided the
+ necessary identification with minimal impact to installed
+ infrastructure, so was chosen instead.
+
+5. RATIONALE FOR THE DIRECTORY SCHEMA
+
+ Successful use of IPP depends on the client finding a suitable IPP
+ enabled Printer to which to send a IPP requests, such as print a
+ job. This task is simplified if there is a Directory Service which
+ can be queried for a suitable Printer. The purpose of the
+ Directory Schema is to have a standard description of Printer
+ attributes that can be associated the URI for the printer. These
+ attributes are a subset of the Model attributes and can be encoded
+ in the appropriate query syntax for the Directory Service being
+ used by the client.
+
+
+
+Zilles Experimental [Page 7]
+
+RFC 2568 Rationale for IPP April 1999
+
+
+6. SECURITY CONSIDERATIONS - RATIONALE FOR SECURITY
+
+ Security is an area of active work on the Internet. Complete
+ solutions to a wide range of security concerns are not yet
+ available. Therefore, in the design of IPP, the focus has been on
+ identifying a set of security protocols/features that are
+ implemented (or currently implementable) and solve real problems
+ with distributed printing. The two areas that seem appropriate to
+ support are: (1) authorization to use a Printer and (2) secure
+ interaction with a printer. The chosen mechanisms are the digest
+ authentication mechanism of HTTP 1.1 and SSL3 [SSL] secure
+ communication mechanism.
+
+7. REFERENCES
+
+ [ipp-iig] Hastings, T. and C. Manros, "Internet Printing
+ Protocol/1.0:Implementer's Guide", Work in Progress.
+
+ [RFC2569] Herriot, R., Hastings, T., Jacobs, N. and J. Martin,
+ "Mapping between LPD and IPP Protocols", RFC 2569, April
+ 1999.
+
+ [RFC2566] deBry, R., Isaacson, S., Hastings, T., Herriot, R. and P.
+ Powell, "Internet Printing Protocol/1.0: Model and
+ Semantics", RFC 2566, April 1999.
+
+ [RFC2565] Herriot, R., Butler, S., Moore, P. and R. Tuner, "Internet
+ Printing Protocol/1.0: Encoding and Transport", RFC 2565,
+ April 1999.
+
+ [RFC2567] Wright, D., "Design Goals for an Internet Printing
+ Protocol", RFC 2567, April 1999.
+
+ [ISO10175] ISO/IEC 10175 "Document Printing Application (DPA)", June
+ 1996.
+
+ [RFC1759] Smith, R., Wright, F., Hastings, T., Zilles, S. and J.
+ Gyllenskog, "Printer MIB", RFC 1759, March 1995.
+
+ [RFC1905] Case, J., McCloghrie, K., Rose, M. and S. Waldbusser,
+ "Protocol Operations for Version 2 of the Simple Network
+ Management Protocol (SNMPv2)", RFC 1905, January 1996.
+
+ [RFC1906] Case, J., McCloghrie, K., Rose, M. and S. Waldbusser,
+ "Transport Mappings for Version 2 of the Simple Network
+ Management Protocol (SNMPv2)", RFC 1906, January 1996.
+
+
+
+
+
+Zilles Experimental [Page 8]
+
+RFC 2568 Rationale for IPP April 1999
+
+
+ [SSL] Netscape, The SSL Protocol, Version 3, (Text version
+ 3.02), November 1996.
+
+8. AUTHOR'S ADDRESS
+
+ Stephen Zilles
+ Adobe Systems Incorporated
+ 345 Park Avenue
+ MailStop W14
+ San Jose, CA 95110-2704
+
+ Phone: +1 408 536-4766
+ Fax: +1 408 537-4042
+ EMail: szilles@adobe.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Zilles Experimental [Page 9]
+
+RFC 2568 Rationale for IPP April 1999
+
+
+9. Full Copyright Statement
+
+ Copyright (C) The Internet Society (1999). All Rights Reserved.
+
+ This document and translations of it may be copied and furnished to
+ others, and derivative works that comment on or otherwise explain it
+ or assist in its implementation may be prepared, copied, published
+ and distributed, in whole or in part, without restriction of any
+ kind, provided that the above copyright notice and this paragraph are
+ included on all such copies and derivative works. However, this
+ document itself may not be modified in any way, such as by removing
+ the copyright notice or references to the Internet Society or other
+ Internet organizations, except as needed for the purpose of
+ developing Internet standards in which case the procedures for
+ copyrights defined in the Internet Standards process must be
+ followed, or as required to translate it into languages other than
+ English.
+
+ The limited permissions granted above are perpetual and will not be
+ revoked by the Internet Society or its successors or assigns.
+
+ This document and the information contained herein is provided on an
+ "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING
+ TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+ HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+ MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Zilles Experimental [Page 10]
+
diff --git a/standards/rfc2569.txt b/standards/rfc2569.txt
new file mode 100644
index 000000000..767857c34
--- /dev/null
+++ b/standards/rfc2569.txt
@@ -0,0 +1,1571 @@
+
+
+
+
+
+
+Network Working Group R. Herriot
+Request For Comments: 2569 Xerox Corporation
+Category: Experimental N. Jacobs
+ Sun Microsystems, Inc.
+ T. Hastings
+ Xerox Corporation
+ J. Martin
+ Underscore, Inc.
+ April 1999
+
+
+ Mapping between LPD and IPP Protocols
+
+Status of this Memo
+
+ This memo defines an Experimental Protocol for the Internet
+ community. It does not specify an Internet standard of any kind.
+ Discussion and suggestions for improvement are requested.
+ Distribution of this memo is unlimited.
+
+Copyright Notice
+
+ Copyright (C) The Internet Society (1999). All Rights Reserved.
+
+IESG Note
+
+ This document defines an Experimental protocol for the Internet
+ community. The IESG expects that a revised version of this protocol
+ will be published as Proposed Standard protocol. The Proposed
+ Standard, when published, is expected to change from the protocol
+ defined in this memo. In particular, it is expected that the
+ standards-track version of the protocol will incorporate strong
+ authentication and privacy features, and that an "ipp:" URL type will
+ be defined which supports those security measures. Other changes to
+ the protocol are also possible. Implementors are warned that future
+ versions of this protocol may not interoperate with the version of
+ IPP defined in this document, or if they do interoperate, that some
+ protocol features may not be available.
+
+ The IESG encourages experimentation with this protocol, especially in
+ combination with Transport Layer Security (TLS) [RFC 2246], to help
+ determine how TLS may effectively be used as a security layer for
+ IPP.
+
+
+
+
+
+
+
+
+Herriot, et al. Experimental [Page 1]
+
+RFC 2569 Mapping between LPD and IPP Protocols April 1999
+
+
+Abstract
+
+ This document is one of a set of documents, which together describe
+ all aspects of a new Internet Printing Protocol (IPP). IPP is an
+ application level protocol that can be used for distributed printing
+ using Internet tools and technologies. This document gives some
+ advice to implementers of gateways between IPP and LPD (Line Printer
+ Daemon). This document describes the mapping between (1) the commands
+ and operands of the 'Line Printer Daemon (LPD) Protocol' specified in
+ RFC 1179 and (2) the operations, operation attributes and job
+ template attributes of the Internet Printing Protocol/1.0 (IPP). One
+ of the purposes of this document is to compare the functionality of
+ the two protocols. Another purpose is to facilitate implementation
+ of gateways between LPD and IPP.
+
+ WARNING: RFC 1179 was not on the IETF standards track. While RFC
+ 1179 was intended to record existing practice, it fell short in some
+ areas. However, this specification maps between (1) the actual
+ current practice of RFC 1179 and (2) IPP. This document does not
+ attempt to map the numerous divergent extensions to the LPD protocol
+ that have been made by many implementers.
+
+ The full set of IPP documents includes:
+
+ Design Goals for an Internet Printing Protocol [RFC2567]
+ Rationale for the Structure and Model and Protocol for the
+ Internet Printing Protocol [RFC2568]
+ Internet Printing Protocol/1.0: Model and Semantics [RFC2566]
+ Internet Printing Protocol/1.0: Encoding and Transport [RFC2565]
+ Internet Printing Protocol/1.0: Implementors Guide [ipp-iig]
+ Mapping between LPD and IPP Protocols (this document)
+
+ The document, "Design Goals for an Internet Printing Protocol", takes
+ a broad look at distributed printing functionality, and it enumerates
+ real-life scenarios that help to clarify the features that need to be
+ included in a printing protocol for the Internet. It identifies
+ requirements for three types of users: end users, operators, and
+ administrators. It calls out a subset of end user requirements that
+ are satisfied in IPP/1.0. Operator and administrator requirements are
+ out of scope for version 1.0.
+
+ The document, "Rationale for the Structure and Model and Protocol for
+ the Internet Printing Protocol", describes IPP from a high level
+ view, defines a roadmap for the various documents that form the suite
+ of IPP specifications, and gives background and rationale for the
+ IETF working group's major decisions.
+
+
+
+
+
+Herriot, et al. Experimental [Page 2]
+
+RFC 2569 Mapping between LPD and IPP Protocols April 1999
+
+
+ The document, "Internet Printing Protocol/1.0: Model and Semantics",
+ describes a simplified model with abstract objects, their attributes,
+ and their operations. It introduces a Printer and a Job object. The
+ Job object supports multiple documents per Job. It also addresses
+ security, internationalization, and directory issues.
+
+ The document, "Internet Printing Protocol/1.0: Encoding and
+ Transport", is a formal mapping of the abstract operations and
+ attributes defined in the model document onto HTTP/1.1. It defines
+ the encoding rules for a new Internet media type called '
+ application/ipp'.
+
+ This document "Internet Printing Protocol/1.0: Implementer's Guide",
+ gives advice to implementers of IPP clients and IPP objects.
+
+TABLE OF CONTENTS
+
+ 1. Introduction.....................................................4
+ 2. Terminology......................................................5
+ 3. Mapping from LPD Commands to IPP Operations......................5
+ 3.1 Print any waiting jobs..........................................6
+ 3.2 Receive a printer job...........................................6
+ 3.2.1 Abort job.....................................................7
+ 3.2.2 Receive control file..........................................7
+ 3.2.3 Receive data file.............................................8
+ 3.3 Send queue state (short)........................................8
+ 3.4 Send queue state (long)........................................10
+ 3.5 Remove jobs....................................................12
+ 4. Mapping of LPD Control File Lines to IPP Operation and Job
+ Template Attributes.............................................13
+ 4.1 Required Job Functions.........................................13
+ 4.2 Optional Job Functions.........................................14
+ 4.3 Required Document Functions....................................14
+ 4.4 Recommended Document Functions.................................16
+ 5. Mapping from IPP operations to LPD commands.....................16
+ 5.1 Print-Job......................................................16
+ 5.2 Print-URI......................................................18
+ 5.3 Validate-Job...................................................18
+ 5.4 Create-Job.....................................................18
+ 5.5 Send-Document..................................................18
+ 5.6 Send-URI.......................................................18
+ 5.7 Cancel-Job.....................................................18
+ 5.8 Get-Printer-Attributes.........................................19
+ 5.9 Get-Job-Attributes.............................................19
+ 5.10 Get-Jobs......................................................20
+ 6. Mapping of IPP Attributes to LPD Control File Lines.............20
+ 6.1 Required Job Functions.........................................21
+ 6.2 Optional Job Functions.........................................21
+
+
+
+Herriot, et al. Experimental [Page 3]
+
+RFC 2569 Mapping between LPD and IPP Protocols April 1999
+
+
+ 6.3 Required Document Functions....................................22
+ 7. Security Considerations.........................................23
+ 8. References......................................................23
+ 9. Authors' Addresses..............................................24
+ 10.Appendix A: ABNF Syntax for response of Send-queue-state (short)25
+ 11.Appendix B: ABNF Syntax for response of Send-queue-state (long) 26
+ 12.Appendix C: Unsupported LPD functions...........................27
+ 13.Full Copyright Statement........................................28
+
+1. Introduction
+
+ The reader of this specification is expected to be familiar with the
+ IPP Model and Semantics specification [RFC2566], the IPP Encoding and
+ Transport [RF2565], and the Line Printer Daemon (LPD) protocol
+ specification [RFC1179] as described in RFC 1179.
+
+ RFC 1179 was written in 1990 in an attempt to document existing LPD
+ protocol implementations. Since then, a number of undocumented
+ extensions have been made by vendors to support functionality
+ specific to their printing solutions. All of these extensions
+ consist of additional control file commands. This document does not
+ address any of these vendor extensions. Rather it addresses existing
+ practice within the context of the features described by RFC 1179.
+ Deviations of existing practice from RFC 1179 are so indicated.
+
+ Other LPD control file commands in RFC 1179 are obsolete. They are
+ intended to work on "text" only formats and are inappropriate for
+ many contemporary document formats that completely specify each page.
+ This document does not address the support of these obsolete
+ features.
+
+ In the area of document formats, also known as page description
+ languages (PDL), RFC 1179 defines a fixed set with no capability for
+ extension. Consequently, some new PDL's are not supported, and some
+ of those that are supported are sufficiently unimportant now that
+ they have not been registered for use with the Printer MIB [RFC1759]
+ and IPP [RFC2566] [RFC2565], though they could be registered if
+ desired. See the Printer MIB specification [RFC1759] and/or the IPP
+ Model specification [RFC2566] for instructions for registration of
+ document-formats with IANA. IANA lists the registered document-
+ formats as "printer languages".
+
+ This document addresses the protocol mapping for both directions:
+ mapping of the LPD protocol to the IPP protocol and mapping of the
+ IPP protocol to the LPD protocol. The former is called the "LPD-to-
+ IPP mapper" and the latter is called the "IPP-to-LPD mapper".
+
+
+
+
+
+Herriot, et al. Experimental [Page 4]
+
+RFC 2569 Mapping between LPD and IPP Protocols April 1999
+
+
+ This document is an informational document that is not on the
+ standards track. It is intended to help implementers of gateways
+ between IPP and LPD. It also provides an example, which gives
+ additional insight into IPP.
+
+2. Terminology
+
+ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
+ "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
+ document are to be interpreted as described in RFC 2119 [RFC2119].
+
+ RFC 1179 uses the word "command" in two contexts: for over-the-wire
+ operations and for command file functions. This document SHALL use
+ the word "command" for the former and the phrase "functions" for the
+ latter. The syntax of the LPD commands is given using ABNF
+ [RFC2234].
+
+ The following tokens are used in order to make the syntax more
+ readable:
+
+ LF stands for %x0A (linefeed)
+ SP stands for %x20. (space)
+ DIGIT stands for %x30-39 ("0" to "9")
+
+3. Mapping from LPD Commands to IPP Operations
+
+ This section describes the mapping from LPD commands to IPP
+ operations. Each of the following sub-sections appear as sub-
+ sections of section 5 of RFC 1179.
+
+ The following table summarizes the IPP operation that the mapper uses
+ when it receives an LPD command. Each section below gives more
+ detail:
+
+ LPD command IPP operation
+
+
+ print-any-waiting-jobs ignore
+ receive-a-printer-job Print-Job or Create-Job/Send-Document
+ send queue state Get-Printer-Attributes and Get-Jobs
+ (short or long)
+ remove-jobs Cancel-Job
+
+
+
+
+
+
+
+
+
+Herriot, et al. Experimental [Page 5]
+
+RFC 2569 Mapping between LPD and IPP Protocols April 1999
+
+
+3.1 Print any waiting jobs
+
+ Command syntax:
+
+ print-waiting-jobs = %x01 printer-name LF
+
+ This command causes the LPD daemon check its queue and print any
+ waiting jobs. An IPP printer handles waiting jobs without such a
+ nudge.
+
+ If the mapper receives this LPD command, it SHALL ignore it and send
+ no IPP operation.
+
+3.2 Receive a printer job
+
+ Command syntax:
+
+ receive-job = %x02 printer-name LF
+
+ The control file and data files mentioned in the following paragraphs
+ are received via LPD sub-commands that follow this command. Their
+ mapping to IPP commands and attributes is described later in this
+ section.
+
+ The mapper maps the 'Receive a printer job' command to either:
+
+ - the Print-Job operation which includes a single data file or
+ - the Create-Job operation followed by one Send-Document operation
+ for each data file.
+
+ If the IPP printer supports both Create-Job and Send-Document, and if
+ a job consists of:
+
+ - a single data file, the mapper SHOULD use the Print-Job
+ operation, but MAY use the Create-Job and Send-Document
+ operations.
+ - more than one data file, the mapper SHALL use Create-Job
+ followed by one Send-Document for each received LPD data file.
+
+ If the IPP printer does not support both Create-Job and Send-
+ Document, and if a job consists of:
+
+ - a single data file, the mapper SHALL use the PrintJob
+ operation.
+ - more than one data file, the mapper SHALL submit each received
+ LPD data file as a separate Print-Job operation (thereby
+ converting a single LPD job into multiple IPP jobs).
+
+
+
+
+Herriot, et al. Experimental [Page 6]
+
+RFC 2569 Mapping between LPD and IPP Protocols April 1999
+
+
+ If the mapper uses Create-Job and Send-Document, it MUST send the
+ Create-Job operation before it sends any Send-Document operations
+ whether the LPD control file, which supplies attributes for Create-
+ Job, arrives before or after all LPD data files.
+
+ NOTE: This specification does not specify how the mapper maps: the
+ LPD Printer-name operand to the IPP "printer-uri" operation
+ attribute.
+
+ The following three sub-sections gives further details about the
+ mapping from LPD receive-a-printer-job sub-commands. Each of the
+ following subsections appear as sub-sections of section 6 of RFC
+ 1179.
+
+3.2.1 Abort job
+
+ Sub-command syntax:
+
+ abort-job = %x1 LF
+
+ This sub-command of receive-a-printer-job is intended to abort any
+ job transfer in process.
+
+ If the mapper receives this sub-command, it SHALL cancel the job that
+ it is in the process of transmitting.
+
+ If the mapper is in the process of sending a Print-Job or Create-Job
+ operation, it terminates the job either by closing the connection, or
+ performing the Cancel-Job operation with the job-uri that it received
+ from the Print-Job or Create-Job operation.
+
+ NOTE: This sub-command is implied if at any time the connection
+ between the LPD client and server is terminated before an entire
+ print job has been transferred via an LPD Receive-a-printer-job
+ request.
+
+3.2.2 Receive control file
+
+ Sub-command syntax:
+
+ receive-control-file = %x2 number-of-bytes SP name-of-control-file LF
+ number-of-bytes = 1*DIGIT
+ name-of-control-file = "cfA" job-number client-host-name
+ ; e.g. "cfA123woden"
+ job-number = 3DIGIT
+ client-host-name = <a host name>
+
+
+
+
+
+Herriot, et al. Experimental [Page 7]
+
+RFC 2569 Mapping between LPD and IPP Protocols April 1999
+
+
+ This sub-command is roughly equivalent to the IPP Create-Job
+ operation.
+
+ The mapper SHALL use the contents of the received LPD control file to
+ create IPP operation attribute and job template attribute values to
+ transmit with the Print-Job or Create-Job operation.
+
+3.2.3 Receive data file
+
+Sub-command syntax: %x3 number-of-bytes-in-data-file Name-of-data-file
+
+ receive-data-file = %x03 number-of-bytes SP name-of-data-file LF
+ number-of-bytes = 1*DIGIT
+ name-of-data-file = "df" letter job-number client-host-name
+ ; e.g. "dfA123woden for the first file
+ letter = %x41-5A / %x61-7A ; "A" to "Z", "a" to "z"
+ ; first file is "A",
+ ; second "B", and 52nd file is "z"
+ job-number = 3DIGIT
+ client-host-name = <a host name>
+
+ This sub-command is roughly equivalent to the IPP Send-Document
+ operation.
+
+ The mapper SHALL use the contents of the received LPD data file as
+ the data to transmit with the IPP Print-Job or Send-Document
+ operation.
+
+ Although RFC 1179 alludes to a method for passing an unspecified
+ length data file by using an octet-count of zero, no implementations
+ support this feature. The mapper SHALL reject a job that has a value
+ of 0 in the number-of-bytes field.
+
+3.3 Send queue state (short)
+
+ Command syntax:
+
+send-queue-short = %x03 printer-name *(SP(user-name / job-number)) LF
+
+ The mapper's response to this command includes information about the
+ printer and its jobs. RFC 1179 specifies neither the information nor
+ the format of its response. This document requires the mapper to
+ follow existing practice as specified in this document.
+
+ The mapper SHALL produce a response in the following format which
+ consists of a printer-status line optionally followed by a heading
+ line, and a list of jobs. This format is defined by examples below.
+ Appendix A contains the ABNF syntax.
+
+
+
+Herriot, et al. Experimental [Page 8]
+
+RFC 2569 Mapping between LPD and IPP Protocols April 1999
+
+
+ For an printer with no jobs, the response starts in column 1 and is:
+
+ no entries
+
+ For a printer with jobs, an example of the response is:
+
+ killtree is ready and printing
+ Rank Owner Job Files Total Size
+ active fred 123 stuff 1204 bytes
+ 1st smith 124 resume, foo 34576 bytes
+ 2nd fred 125 more 99 bytes
+ 3rd mary 126 mydoc 378 bytes
+ 4th jones 127 statistics.ps 4567 bytes
+ 5th fred 128 data.txt 9 bytes
+
+ The column numbers of above headings and job entries are:
+
+ | | | | |
+ 01 08 19 35 63
+
+ The mapper SHALL produce each field above from the following IPP
+ attribute:
+
+ LPD field IPP attribute special conversion details
+
+ printer- printer-state and For a printer-state of idle or
+ status printer-state-reasons processing, the mapper SHALL use
+ the formats above. For stopped,
+ the mapper SHALL use printer-
+ state-reasons to produce an
+ unspecified format for the error.
+ rank number-of- the mapper SHALL the format above
+ intervening-jobs
+ owner job-originating-user- unspecified conversion; job-
+ name originating-user-name may be the
+ mapper's user-name
+ job job-id the mapper shall use the job-id
+ files document-name the mapper shall create a comma
+ separated list of the document-
+ names and then truncate this list
+ to the first 24 characters
+ total- job-k- the mapper shall multiple the
+ size octets*copies*1024 value of job-k-octets by 1024 and
+ by the value of the "copies"
+ attribute.
+
+
+
+
+
+
+Herriot, et al. Experimental [Page 9]
+
+RFC 2569 Mapping between LPD and IPP Protocols April 1999
+
+
+ A mapper SHOULD use the job attribute number-of-intervening-jobs
+ rather than the job's position in a list of jobs to determine 'rank'
+ because a Printer may omit jobs that it wants to keep secret. If a
+ printer doesn't support the job attribute number-of-intervening-jobs,
+ a mapper MAY use the job's position.
+
+ Note: a Printer may set the value of job-originating-user-name to the
+ authenticated user or to the value of "requesting-user-name",
+ depending on the implementation and configuration. For a gateway, the
+ authenticated user is the user-id of the gateway, but the
+ "requesting-user-name" may contain the name of the user who is the
+ gateway's client.
+
+ In order to obtain the information specified above, The LPD-to-IPP
+ mapper SHALL use the Get-Printer-Attributes operation to get
+ printer-status and SHOULD use the Get-Jobs operation to get
+ information about all of the jobs. If the LPD command contains job-
+ numbers or user-names, the mapper MAY handle the filtering of the
+ response. If the LPD command contains job-numbers but no user-names,
+ the mapper MAY use Get-Job-Attributes on each converted job-number
+ rather than Get-Jobs. If the LPD command contains a single user-name
+ but no job-numbers, the mapper MAY use Get-Jobs with the my-jobs
+ option if the server supports this option and if the server allows
+ the client to be a proxy for the LPD user.
+
+ NOTE: This specification does not define how the mapper maps the LPD
+ Printer-name operand to the IPP "printer-uri" operation attribute.
+
+3.4 Send queue state (long)
+
+ Command syntax:
+
+ send-queue-long = %x04 printer-name *(SP(user-name / job-number)) LF
+
+ The mapper's response to this command includes information about the
+ printer and its jobs. RFC 1179 specifies neither the information nor
+ the format of its response. This document requires the mapper to
+ follow existing practice as specified in this document.
+
+ The mapper SHALL produce a response in the following format which
+ consists of a printer-status line optionally followed a list of jobs,
+ where each job consists of a blank line, a description line, and one
+ line for each file. The description line contains the user-name,
+ rank, job-number and host. This format is defined by examples below.
+ Appendix B contain the ABNF syntax.
+
+
+
+
+
+
+Herriot, et al. Experimental [Page 10]
+
+RFC 2569 Mapping between LPD and IPP Protocols April 1999
+
+
+ For an printer with no jobs the response is:
+
+ no entries
+
+ For a printer with jobs, an example of the response is:
+
+ killtree is ready and printing
+
+ fred: active [job 123 tiger]
+ 2 copies of stuff 602 bytes
+
+ smith: 1st [job 124 snail]
+ 2 copies of resume 7088 bytes
+ 2 copies of foo 10200 bytes
+
+ fred: 2nd [job 125 tiger]
+ more 99 bytes
+
+ The column numbers of above headings and job entries are:
+
+ | | |
+ 01 09 41
+
+ Although the format of the long form is different from the format of
+ the short form, their fields are identical except for a) the copies
+ and host fields which are only in the long form, and b) the "size"
+ field contains the single copy size of each file. Thus the sum of
+ the file sizes in the "size" field times the value of the "copies"
+ field produces the value for the "Total Size" field in the short
+ form. For fields other than the host and copies fields, see the
+ preceding section. For the host field see the table below.
+
+ LPD field IPP attribute special conversion details
+
+ host unspecified conversion; job-
+ originating-host may be the
+ mapper's host
+ copies copies the mapper shall assume the
+ value of copies precedes the
+ string "copies of "; otherwise,
+ the value of copies is 1.
+
+ NOTE: This specification does not define how the mapper maps the LPD
+ Printer-name operand to the IPP printer-uri operation attribute.
+
+
+
+
+
+
+
+Herriot, et al. Experimental [Page 11]
+
+RFC 2569 Mapping between LPD and IPP Protocols April 1999
+
+
+3.5 Remove jobs
+
+ Command syntax:
+
+ remove-jobs = %x05 printer-name SP agent
+ *(SP(user-name / job-number)) LF
+
+ The agent operand is the user-name of the user initiating the
+ remove-jobs command. The special user-name 'root' indicates a
+ privileged user who can remove jobs whose user-name differs from the
+ agent.
+
+ The mapper SHALL issue one Cancel-Job operation for each job
+ referenced by the remove-jobs command. Each job-number in the
+ remove-jobs command references a single job. Each user-name in the
+ remove-jobs command implicitly references all jobs owned by the
+ specified user. The active job is implicitly referenced when the
+ remove-jobs command contains neither job-numbers nor user-names. The
+ mapper MAY use Get-Jobs to determine the job-uri of implicitly
+ referenced jobs.
+
+ The mapper SHALL not use the agent name of 'root' when end-users
+ cancel their own jobs. Violation of this rule creates a potential
+ security violation, and it may cause the printer to issue a
+ notification that misleads a user into thinking that some other
+ person canceled the job.
+
+ If the agent of a remove-jobs command for a job J is the same as the
+ user name specified with the 'P' function in the control file for job
+ J, then the mapper SHALL ensure that the initiator of the Cancel-Job
+ command for job J is the same as job-originating-user for job J.
+
+ Note: This requirement means that a mapper must be consistent in who
+ the receiver perceives as the initiator of IPP operations. The mapper
+ either acts as itself or acts on behalf of another user. The latter
+ is preferable if it is possible. This consistency is necessary
+ between Print-Job/Create-Job and Cancel-Job in order for Cancel-Job
+ to work, but it is also desirable for other operations. For example,
+ Get-Jobs may give more information about job submitted by the
+ initiator of this operation.
+
+ NOTE: This specification does not define how the mapper maps: (1) the
+ LPD printer-name to the IPP "printer-uri" or (2) the LPD job-number
+ to the IPP "job-uri".
+
+ NOTE: This specification does not specify how the mapper maps the LPD
+ user-name to the IPP job-originating-user because the mapper may use
+ its own user-name with jobs.
+
+
+
+Herriot, et al. Experimental [Page 12]
+
+RFC 2569 Mapping between LPD and IPP Protocols April 1999
+
+
+4. Mapping of LPD Control File Lines to IPP Operation and Job Template
+ Attributes
+
+ This section describes the mapping from LPD control file lines
+ (called 'functions') to IPP operation attributes and job template
+ attributes. The mapper receives the control file lines via the LPD
+ receive-control-file sub-command. Each of the LPD functions appear
+ as sub-sections of section 7 of RFC 1179.
+
+ In LPD control file lines, the text operands have a maximum length of
+ 31 or 99 while IPP operation attribute and job template attribute
+ values have a maximum of 255 or 1023 octets, depending on the
+ attribute syntax. Therefore, no data is lost.
+
+ The mapper converts each supported LPD function to its corresponding
+ IPP operation or job template attribute as defined by tables in the
+ subsections that follow. These subsections group functions according
+ to whether they are:
+
+ - required with a job,
+ - optional with a job
+ - required with each document.
+
+ In the tables below, each LPD value is given a name, such as 'h'. If
+ an IPP value uses the LPD value, then the IPP value column contains
+ the LPD name, such as 'h' to denote this. Otherwise, the IPP value
+ column specifies the literal value.
+
+4.1 Required Job Functions
+
+ The following LPD functions MUST be in a received LPD job. The mapper
+ SHALL receive each of the following LPD functions and SHALL include
+ the information as a operation or job template attribute with each
+ IPP job. The functions SHOULD be in the order 'H', 'P' and they
+ SHOULD be the first two functions in the control file, but they MAY
+ be anywhere in the control file and in any order:
+
+ LPD function IPP
+ name value description name value
+
+ H h Originating Host h (in security layer)
+ P u User identification requesting- u (and in security
+ user-name layer)
+ none ipp- 'true'
+ attribute-
+ fidelity
+
+
+
+
+
+Herriot, et al. Experimental [Page 13]
+
+RFC 2569 Mapping between LPD and IPP Protocols April 1999
+
+
+ A mapper MAY send its own host rather than the client's host, and a
+ mapper MAY send its own user-name as user identification rather than
+ the client user. But in any case, the values sent SHALL be compatible
+ with the Cancel-Job operation. The IPP operation MAY have no way to
+ specify an originating host-name.
+
+ The mapper SHALL include ipp-attribute-fidelity = true so that it
+ doesn't have to determine which attributes a printer supports.
+
+4.2 Optional Job Functions
+
+ The following LPD functions MAY be present in a received job. These
+ functions SHOULD follow the required job functions and precede the
+ document functions, but they MAY be anywhere in the control file.
+
+ If the mapper receives such an LPD function, the mapper SHALL include
+ the corresponding IPP attribute with the value converted as specified
+ in the table below. If the mapper does not receive such an LPD
+ attribute, the mapper SHALL NOT include the corresponding IPP
+ attribute, except the 'L' LPD function whose absence has a special
+ meaning as noted in the table.
+
+ LPD function IPP
+ name value description name value
+
+ J j Job name for job-name j
+ banner page
+ L l Print banner page job-sheets 'standard' if 'L' is
+ present
+ 'none' if 'L' is present
+ M m Mail When Printed IPP has no notification
+ mechanism. To support
+ this LPD feature, the
+ gateway must poll using
+ the Get-Job-Attributes
+ operation.
+
+4.3 Required Document Functions
+
+ The mapper SHALL receive one set of the required document functions
+ with each copy of a document, and SHALL include the converted
+ information as operation or job template attributes with each IPP
+ document.
+
+ If the control file contains required and recommended document
+ functions, the required functions SHOULD precede the recommended ones
+ and if the job contains multiple documents, all the functions for
+
+
+
+
+Herriot, et al. Experimental [Page 14]
+
+RFC 2569 Mapping between LPD and IPP Protocols April 1999
+
+
+ each document are grouped together as shown in the example of section
+ 6.3 "Required Document Functions". However, the document functions
+ MAY be in any order.
+
+ LPD function IPP
+ name value description name value
+
+ f fff Print formatted document-format 'application/octet-
+ file stream'
+ l fff Print file leaving document-format 'application/octet-
+ control characters stream'
+ o fff Print Postscript document-format 'application/PostScri
+ output file pt'
+ copies see note
+
+ Note: In practice, the 'f' LPD function is often overloaded. It is
+ often used with any format of document data including PostScript and
+ PCL data.
+
+ Note: In practice, the 'l' LPD function is often used as a rough
+ equivalent to the 'f' function.
+
+ Note: When RFC 1179 was written, no implementation supported the 'o'
+ function; instead 'f' was used for PostScript. Windows NT now sends '
+ o' function for a PostScript file.
+
+ Note: the value 'fff' of the 'f', 'l' and 'o' functions is the name
+ of the data file as transferred, e.g. "dfA123woden".
+
+ If the mapper receives any other lower case letter, the mapper SHALL
+ reject the job because the document contains a format that the mapper
+ does not support.
+
+ The mapper determines the number of copies by counting the number of
+ occurrences of each 'fff' file with one of the lower-case functions
+ above. For example, if 'f dfA123woden' occurs 4 times, then copies
+ has a value of 4. Although the LPD protocol allows the value of
+ copies to be different for each document, the commands and the
+ receiving print systems don't support this.
+
+
+
+
+
+
+
+
+
+
+
+
+Herriot, et al. Experimental [Page 15]
+
+RFC 2569 Mapping between LPD and IPP Protocols April 1999
+
+
+4.4 Recommended Document Functions
+
+ The mapper SHOULD receive one set of the recommended document
+ functions with each document, and SHOULD include the converted
+ information as an operation or job template attribute with each IPP
+ document. The functions SHOULD be received in the order 'U' and 'N',
+ but they MAY arrive in any order.
+
+ LPD function IPP
+ name value description name value
+
+ U fff ignored
+ N n Name of source file document-name n
+
+ Note: the value 'fff' of the 'U' function is the name of the data
+ file as transferred, e.g. "dfA123woden".
+
+5. Mapping from IPP operations to LPD commands
+
+ If the IPP-to-LPD mapper receives an IPP operation, the following
+ table summarizes the LPD command that it uses. Each section below
+ gives the detail. Each of the following sub-sections appear as sub-
+ sections of section 3 in the document "Internet Printing
+ Protocol/1.0: Model and Semantics" [RFC2566].
+
+ IPP operation LPD command
+
+ Print-Job or Print-URI or receive-a-printer-job
+ Create-Job/Send-Document/Send-URI and then print-any-waiting-jobs
+ Validate-Job implemented by the mapper
+ Cancel-Job remove-jobs
+ Get-Printer-Attributes, Get-Job- send queue state (short or long)
+ Attributes or Get-Jobs
+
+5.1 Print-Job
+
+ The mapper SHALL send the following commands in the order listed
+ below:
+
+ - receive-a-printer-job command
+ - both receive-control-file sub-command and receive-data-file
+ sub-command (unspecified order, see Note below)
+ - print-any-waiting-jobs command, except that if the mapper is
+ sending a sequence of receive a printer-job commands, it MAY
+ omit sending print-any-waiting-jobs after any receive a
+ printer-job command that is neither the first nor last command
+ in this sequence
+
+
+
+
+Herriot, et al. Experimental [Page 16]
+
+RFC 2569 Mapping between LPD and IPP Protocols April 1999
+
+
+ Note: it is recommended that the order of the receive-control-file
+ subcommand and the receive-data-file sub-command be configurable
+ because either order fails for some print systems. Some print systems
+ assume that the control file follows all data files and start
+ printing immediately on receipt of the control file. When such a
+ print system tries to print a data file that has not arrived, it
+ produces an error. Other print systems assume that the control file
+ arrives before the data files and start printing when the first data
+ file arrives. Such a system ignores the control information, such as
+ banner page or copies.
+
+ NOTE: This specification does not define the mapping between the IPP
+ printer-uri and the LPD printer-name.
+
+ The mapper SHALL send the IPP operation attributes and job template
+ attributes received from the operation to the LPD printer by using
+ the LPD receive-control-file sub-command. The mapper SHALL create the
+ LPD job-number for use in the control file name, but the receiving
+ printer MAY, in some circumstances, assign a different job-number to
+ the job. The mapper SHALL create the IPP job-id and IPP job-uri
+ returned in the Print-Job response.
+
+ NOTE: This specification does not specify how the mapper determines
+ the LPD job-number, the IPP job-id or the IPP job-uri of a job that
+ it creates nor does it specify the relationship between the IPP job-
+ uri, IPP the job-id and the LPD job-number, both of which the mapper
+ creates. However, it is likely that the mapper will use the same
+ integer value for both the LPD job-number and the IPP job-id, and
+ that the IPP Job-uri is the printer's URI with the job-id
+ concatenated on the end.
+
+ The mapper SHALL send data received in the IPP operation to the LPD
+ printer by using the LPD receive-data-file sub-command. The mapper
+ SHALL specify the exact number of bytes being transmitted in the
+ number-of-bytes field of the receive-data-file sub-command. It SHALL
+ NOT use a value of 0 in this field.
+
+ If the mapper, while it is transmitting a receive-a-printer-job
+ command or sub-command, either detects that its IPP connection has
+ closed or receives a Cancel-Job operation, the mapper SHALL terminate
+ the LPD job either with the abort sub-command or the remove-jobs
+ command.
+
+ This document does not address error code conversion.
+
+
+
+
+
+
+
+Herriot, et al. Experimental [Page 17]
+
+RFC 2569 Mapping between LPD and IPP Protocols April 1999
+
+
+5.2 Print-URI
+
+ The mapper SHALL handle this operation in the same way as a Print-Job
+ operation except that it SHALL obtain data referenced by the
+ "document-uri" operation attribute and SHALL then treat that data as
+ if it had been received via a Print-Job operation.
+
+5.3 Validate-Job
+
+ The mapper SHALL perform this operation directly. Because LPD
+ supports very few attributes, this operation doesn't have much to
+ check.
+
+5.4 Create-Job
+
+ The mapper SHALL handle this operation like Print-Job, except:
+
+ - the mapper SHALL send the control file after it has received the
+ last Send-Document or Send-URI operation because the control
+ file contains all the document-name and document-format values
+ specified in the Send-Document and Send-URI operations.
+ - the mapper SHALL perform one receive-data-file sub-command for
+ each Send-Document or Send-URI operation received and in the
+ same order received.
+ - the mapper SHALL send the control file either before all data
+ files or after all data files. (See the note in the section on
+ Print-Job about the dilemma of sending the control file either
+ before or after the data files.
+
+5.5 Send-Document
+
+ The mapper performs a receive-data-file sub-command on the received
+ data. See the preceding section 5.4 "Create-Job" for the details.
+
+5.6 Send-URI
+
+ The mapper SHALL obtain the data referenced by the "document-uri"
+ operation attribute, and SHALL then treat that data as if it had been
+ received via a Send-Document operation. See the preceding section 5.5
+ "Send-Document" for the details.
+
+5.7 Cancel-Job
+
+ The mapper SHALL perform a remove-jobs command with the following
+ operation attributes:
+
+
+
+
+
+
+Herriot, et al. Experimental [Page 18]
+
+RFC 2569 Mapping between LPD and IPP Protocols April 1999
+
+
+ - the printer is the one to which the job was submitted, that is
+ the IPP printer-uri is mapped to an LPD printer-name by the same
+ mechanism as for all commands
+ - the agent is the authenticated user-name of the IPP client
+ - the job-number is the job-id returned by the Print-Job command,
+ that is, the LPD job-number has the same value as the IPP job-id
+ for likely implementations
+
+5.8 Get-Printer-Attributes
+
+ LPD severely limits the set of attributes that the mapper is able to
+ return in its response for this operation. The mapper SHALL support,
+ at most, the following printer attributes:
+
+ - printer-state
+ - printer-state-reasons
+
+ The mapper uses either the long or short form of the "send queue
+ state" command.
+
+ The mapper SHALL assume that the LPD response that it receives has
+ the format and information specified in section 3.3 "Send queue state
+ (short)" and section 3.4 "Send queue state (long)". The mapper SHALL
+ determine the value of each requested attribute by using the inverse
+ of the mapping specified in the two aforementioned sections.
+
+ Note: the mapper can determine the response from the printer-status
+ line without examining the rest of the LPD response.
+
+5.9 Get-Job-Attributes
+
+ LPD severely limits the set of attributes that the mapper is able to
+ return in its response for this operation. The mapper SHALL support,
+ at most, the following job attributes:
+
+ - number-of-intervening-jobs
+ - job-originating-user-name
+ - job-id
+ - document-name
+ - job-k-octets
+ - copies
+
+ The mapper uses either the long or short form of the "send queue
+ state" command. If it receives a request for the "job-k-octets" or
+ "copies" and supports the attribute it SHALL use the long form;
+ otherwise, it SHALL use the short form.
+
+
+
+
+
+Herriot, et al. Experimental [Page 19]
+
+RFC 2569 Mapping between LPD and IPP Protocols April 1999
+
+
+ Note: the value of job-k-octets is the value in the short form
+ divided by the number of "copies" which is on the long form only. Its
+ value can also be determined by adding the "size" field values for
+ each document in the job in the long form.
+
+ The mapper SHALL assume that the LPD response that it receives has
+ the format and information specified in section 3.3 "Send queue state
+ (short)" and section 3.4 "Send queue state (long)". The mapper SHALL
+ determine the value of each requested attribute by using the inverse
+ of the mapping specified in the two aforementioned sections.
+
+ Note: when the mapper uses the LPD short form, it can determine the
+ response from the single LPD line that pertains to the job specified
+ by the Get-Job-Attributes operation.
+
+ Note: the mapper can use its correspondence between the IPP job-id,
+ job-uri and the LPD job-number.
+
+5.10 Get-Jobs
+
+ The mapper SHALL perform this operation in the same way as Get-Job-
+ Attributes except that the mapper converts all the LPD job-lines, and
+ the IPP response contains one job object for each job-line in the LPD
+ response.
+
+6. Mapping of IPP Attributes to LPD Control File Lines
+
+ This section describes the mapping from IPP operation attributes and
+ job template attributes to LPD control file lines (called '
+ functions'). The mapper receives the IPP operation attributes and job
+ template atributes via the IPP operation. Each of the IPP operation
+ attributes and job template attributes appear as sub-sections of
+ section 3 and 4.2 in the IPP model document [RFC2566].
+
+ In the context of LPD control file lines, the text operands have a
+ maximum length of 31 or 99 while IPP operation attributes and job
+ template attributes have a maximum of 255 or 1023 octets, depending
+ on the attribute syntax. Therefore, there may be some data loss if
+ the IPP operation attribute and job template attribute values exceed
+ the maximum length of the LPD equivalent operands.
+
+ The mapper converts each supported IPP operation attribute and job
+ template attribute to its corresponding LPD function as defined by
+ tables in the subsections that follow. These subsections group
+ functions according to whether they are:
+
+
+
+
+
+
+Herriot, et al. Experimental [Page 20]
+
+RFC 2569 Mapping between LPD and IPP Protocols April 1999
+
+
+ - required with a job,
+ - optional with a job
+ - required with each document.
+
+ In the tables below, each IPP value is given a name, such as 'h'. If
+ an LPD value uses the IPP value, then the LPD value column contains
+ the IPP name, such as 'h' to denote this. Otherwise, the LPD value
+ column specifies the literal value.
+
+6.1 Required Job Functions
+
+ The mapper SHALL include the following LPD functions with each job,
+ and they SHALL have the specified value. They SHALL be the first
+ functions in the control file and they SHALL be in the order "H" and
+ then "P".
+
+ IPP LPD function
+ name value name value description
+
+ (perhaps in security h H gateway host Originating Host
+ layer)
+ requesting-user-name u P u User identification
+ and in the security
+ layer
+
+ A mapper SHALL sends its own host rather than the client's host,
+ because some LPD systems require that it be the same as the host from
+ which the remove-jobs command comes. A mapper MAY send its own user
+ name as user identification rather than the client user. But in any
+ case, the values sent SHALL be compatible with the LPD remove-jobs
+ operation.
+
+6.2 Optional Job Functions
+
+ The mapper MAY include the following LPD functions with each job.
+ They SHALL have the specified value if they are sent. These
+ functions, if present, SHALL follow the require job functions, and
+ they SHALL precede the required document functions.
+
+ IPP attribute LPD function
+ name value name value description
+
+ job-name j J j Job name for banner
+ page
+ job-sheets 'standard' L u Print banner page
+ job-sheets 'none' omit 'L' function
+
+
+
+
+
+Herriot, et al. Experimental [Page 21]
+
+RFC 2569 Mapping between LPD and IPP Protocols April 1999
+
+
+ Note: 'L' has special meaning when it is omitted. If 'J' is omitted,
+ some undefined behavior occurs with respect to the banner page.
+
+6.3 Required Document Functions
+
+ The mapper SHALL include one set of the following LPD functions with
+ each document, and they SHALL have the specified values. For each
+ document, the order of the functions SHALL be 'f', 'U' and then 'N',
+ where 'f' is replicated once for each copy.
+
+ IPP attribute LPD function
+
+ name value name value description
+
+ document- 'application/octet- f fff Print formatted file
+ format stream' or
+ 'application/PostScript'
+ copies c replicate 'f' 'c'
+ times
+ none U fff Unlink data file
+ document- n N n Name of source file
+ name
+
+ Note: the value 'fff' of the 'f' and 'U' functions is the name of the
+ data file as transferred, e.g. "dfA123woden".
+
+ Note: the mapper SHALL not send the 'o' function
+
+ ISSUE: should we register DVI, troff or ditroff?
+
+ If the mapper receives no "ipp-attribute-fidelitybest-effort" or it
+ has a value of false, then the mapper SHALL reject the job if it
+ specifies attributes or attribute values that are not among those
+ supported in the above tables.
+
+ Below is an example of the minimal control file for a job with three
+ copies of two files 'foo' and 'bar':
+
+ H tiger
+ P jones
+ f dfA123woden
+ f dfA123woden
+ f dfA123woden
+ U dfA123woden
+ N foo
+ f dfB123woden
+ f dfB123woden
+ f dfB123woden
+
+
+
+Herriot, et al. Experimental [Page 22]
+
+RFC 2569 Mapping between LPD and IPP Protocols April 1999
+
+
+ U dfB123woden
+ N bar
+
+7. Security Considerations
+
+ There are no security issues beyond those covered in the IPP Encoding
+ and Transport document [RFC2565], the IPP model document [RFC2566]
+ and the LPD document [RFC1179].
+
+8. References
+
+ [ipp-iig] Hasting, T., et al., "Internet Printing Protocol/1.0:
+ Implementer's Guide", Work in Progress.
+
+ [RFC1759] Smith, R., Wright, F., Hastings, T., Zilles, S., and J.
+ Gyllenskog, "Printer MIB", RFC 1759, March 1995.
+
+ [RFC1179] McLaughlin, L., "Line Printer Daemon Protocol", RFC 1179,
+ August 1990.
+
+ [RFC2119] Bradner, S. "Key words for use in RFCs to Indicate
+ Requirement Levels", BCP 14, RFC 2119, March 1997.
+
+ [RFC2234] D. Crocker et al., "Augmented BNF for Syntax
+ Specifications: ABNF", RFC 2234, November 1997.
+
+ [RFC2565] Herriot, R., Butler, S., Moore, P. and R. Tuner, "Internet
+ Printing Protocol/1.0: Encoding and Transport", RFC 2565,
+ April 1999.
+
+ [RFC2566] deBry, R., Hastings, T., Herriot, R., Isaacson, S., and P.
+ Powell, "Internet Printing Protocol/1.0: Model and
+ Semantics", RFC 2566, April 1999.
+
+ [RFC2567] Wright, D., "Design Goals for an Internet Printing
+ Protocol", RFC 2567, April 1999.
+
+ [RFC2568] Zilles, S., "Rationale for the Structure and Model and
+ Protocol for the Internet Printing Protocol", RFC 2568,
+ April 1999.
+
+
+
+
+
+
+
+
+
+
+
+Herriot, et al. Experimental [Page 23]
+
+RFC 2569 Mapping between LPD and IPP Protocols April 1999
+
+
+9. Authors' Addresses
+
+ Robert Herriot (Editor)
+ Xerox Corporation
+ 3400 Hillview Ave., Bldg #1
+ Palo Alto, CA 94304
+
+ Phone: 650-813-7696
+ Fax: 650-813-6860
+ EMail: rherriot@pahv.xerox.com
+
+
+ Norm Jacobs
+ Sun Microsystems Inc.
+ 1430 Owl Ridge Rd.
+ Colorado Springs, CO 80919
+
+ Phone: 719-532-9927
+ Fax: 719-535-0956
+ EMail: Norm.Jacobs@Central.sun.com
+
+
+ Thomas N. Hastings
+ Xerox Corporation
+ 701 S. Aviation Blvd., ESAE-231
+ El Segundo, CA 90245
+
+ Phone: 310-333-6413
+ Fax: 310-333-5514
+ EMail: hastings@cp10.es.xerox.com
+
+
+ Jay Martin
+ Underscore, Inc.
+ 41-C Sagamore Park Road
+ Hudson, NH 03051-4915
+
+ Phone: 603-889-7000
+ Fax: 603-889-2699
+ EMail: jkm@underscore.com
+
+
+
+
+
+
+
+
+
+
+
+Herriot, et al. Experimental [Page 24]
+
+RFC 2569 Mapping between LPD and IPP Protocols April 1999
+
+
+10. Appendix A: ABNF Syntax for response of Send-queue-state (short)
+
+ The syntax in ABNF for the response to the LPD command 'send-queue-
+ state (long)' is:
+
+ status-response = empty-queue / nonempty-queue
+ empty-queue = "no-entries" LF
+ nonempty-queue = printer-status LF heading LF *(job LF)
+ printer-status = OK-status / error-status
+ OK-status = printer-name SP "ready and printing" LF
+ error-status = < implementation dependent status information >
+ heading = "Rank" 3SP "Owner" 6SP "Job" 13SP "Files"
+ 23SP "Total Size" LF
+ ; the column headings and their values below begin
+ at the columns
+ ; 1, 8, 19, 35 and 63
+ job = rank *SP owner *SP job *SP files *SP total-size "bytes"
+ ; jobs are in order of oldest to newest
+ rank = "active" / "1st" / "2nd" / "3rd" / integer "th"
+ ; job that is printing is "active"
+ ; other values show position in the queue
+ owner = <user name of person who submitted the job>
+ job = 1*3DIGIT ; job-number
+ files = <file name> *( "," <file name>) ; truncated to 24 characters
+ total-size = 1*DIGIT ; combined size in bytes of all documents
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Herriot, et al. Experimental [Page 25]
+
+RFC 2569 Mapping between LPD and IPP Protocols April 1999
+
+
+11. Appendix B: ABNF Syntax for response of Send-queue-state (long)
+
+ The syntax in ABNF for the response to the LPD command 'send-queue-
+ state (long)' is:
+
+ status-response = empty-queue / nonempty-queue
+ empty-queue = "no-entries" LF
+ nonempty-queue = printer-status LF *job
+ printer-status = OK-status / error-status
+ OK-status = printer-name SP "ready and printing" LF
+ error-status = < implementation dependent status information >
+ job = LF line-1 LF line-2 LF
+ line-1 = owner ":" SP rank 1*SP "[job" job SP host "]"
+ line-2 = file-name 1*SP document-size "bytes"
+ ; jobs are in order of oldest to newest
+ rank = "active" / "1st" / "2nd" / "3rd" / integer "th"
+ ; job that is printing is "active"
+ ; other values show position in the queue
+ owner = <user name of person who submitted the job>
+ job = 1*3DIGIT
+ file-name = [ 1*DIGIT "copies of" SP ] <file name>
+ ; truncated to 24 characters
+ document-size = 1*DIGIT ;size of single copy of the document.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Herriot, et al. Experimental [Page 26]
+
+RFC 2569 Mapping between LPD and IPP Protocols April 1999
+
+
+12. Appendix C: Unsupported LPD functions
+
+ The follow LPD functions have no IPP equivalent. The LPD-to-IPP
+ mapper ignores them and the IPP-to-LPD mapper does not send them.
+
+ LPD command
+ name description
+
+ C Class for banner page
+ I Indent Printing
+ H Host of client
+ M Mail when printed
+ S Symbolic link data
+ T Title for pr
+ W Width of output
+ 1 troff R font
+ 2 troff I font
+ 3 troff B font
+ 4 troff S font
+
+ The follow LPD functions specify document-formats which have no IPP
+ equivalent, unless someone registers them. The LPD-to-IPP mapper
+ rejects jobs that request such a document format, and the IPP-to-LPD
+ mapper does not send them.
+
+ LPD command
+ name description
+
+ c Plot CIF file
+ d Print DVI file
+ g Plot file
+ k reserved for Kerberized clients and servers
+ n Print ditroff output file
+ p Print file with 'pr' format
+ r File to print with FORTRAN carriage control
+ t Print troff output file
+ v Print raster file
+ z reserved for future use with the Palladium
+ print system
+
+
+
+
+
+
+
+
+
+
+
+
+Herriot, et al. Experimental [Page 27]
+
+RFC 2569 Mapping between LPD and IPP Protocols April 1999
+
+
+13. Full Copyright Statement
+
+ Copyright (C) The Internet Society (1999). All Rights Reserved.
+
+ This document and translations of it may be copied and furnished to
+ others, and derivative works that comment on or otherwise explain it
+ or assist in its implementation may be prepared, copied, published
+ and distributed, in whole or in part, without restriction of any
+ kind, provided that the above copyright notice and this paragraph are
+ included on all such copies and derivative works. However, this
+ document itself may not be modified in any way, such as by removing
+ the copyright notice or references to the Internet Society or other
+ Internet organizations, except as needed for the purpose of
+ developing Internet standards in which case the procedures for
+ copyrights defined in the Internet Standards process must be
+ followed, or as required to translate it into languages other than
+ English.
+
+ The limited permissions granted above are perpetual and will not be
+ revoked by the Internet Society or its successors or assigns.
+
+ This document and the information contained herein is provided on an
+ "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING
+ TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+ HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+ MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Herriot, et al. Experimental [Page 28]
+
diff --git a/standards/rfc2616.txt b/standards/rfc2616.txt
new file mode 100644
index 000000000..45d7d08b8
--- /dev/null
+++ b/standards/rfc2616.txt
@@ -0,0 +1,9859 @@
+
+
+
+
+
+
+Network Working Group R. Fielding
+Request for Comments: 2616 UC Irvine
+Obsoletes: 2068 J. Gettys
+Category: Standards Track Compaq/W3C
+ J. Mogul
+ Compaq
+ H. Frystyk
+ W3C/MIT
+ L. Masinter
+ Xerox
+ P. Leach
+ Microsoft
+ T. Berners-Lee
+ W3C/MIT
+ June 1999
+
+
+ Hypertext Transfer Protocol -- HTTP/1.1
+
+Status of this Memo
+
+ This document specifies an Internet standards track protocol for the
+ Internet community, and requests discussion and suggestions for
+ improvements. Please refer to the current edition of the "Internet
+ Official Protocol Standards" (STD 1) for the standardization state
+ and status of this protocol. Distribution of this memo is unlimited.
+
+Copyright Notice
+
+ Copyright (C) The Internet Society (1999). All Rights Reserved.
+
+Abstract
+
+ The Hypertext Transfer Protocol (HTTP) is an application-level
+ protocol for distributed, collaborative, hypermedia information
+ systems. It is a generic, stateless, protocol which can be used for
+ many tasks beyond its use for hypertext, such as name servers and
+ distributed object management systems, through extension of its
+ request methods, error codes and headers [47]. A feature of HTTP is
+ the typing and negotiation of data representation, allowing systems
+ to be built independently of the data being transferred.
+
+ HTTP has been in use by the World-Wide Web global information
+ initiative since 1990. This specification defines the protocol
+ referred to as "HTTP/1.1", and is an update to RFC 2068 [33].
+
+
+
+
+
+
+Fielding, et al. Standards Track [Page 1]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+Table of Contents
+
+ 1 Introduction ...................................................7
+ 1.1 Purpose......................................................7
+ 1.2 Requirements .................................................8
+ 1.3 Terminology ..................................................8
+ 1.4 Overall Operation ...........................................12
+ 2 Notational Conventions and Generic Grammar ....................14
+ 2.1 Augmented BNF ...............................................14
+ 2.2 Basic Rules .................................................15
+ 3 Protocol Parameters ...........................................17
+ 3.1 HTTP Version ................................................17
+ 3.2 Uniform Resource Identifiers ................................18
+ 3.2.1 General Syntax ...........................................19
+ 3.2.2 http URL .................................................19
+ 3.2.3 URI Comparison ...........................................20
+ 3.3 Date/Time Formats ...........................................20
+ 3.3.1 Full Date ................................................20
+ 3.3.2 Delta Seconds ............................................21
+ 3.4 Character Sets ..............................................21
+ 3.4.1 Missing Charset ..........................................22
+ 3.5 Content Codings .............................................23
+ 3.6 Transfer Codings ............................................24
+ 3.6.1 Chunked Transfer Coding ..................................25
+ 3.7 Media Types .................................................26
+ 3.7.1 Canonicalization and Text Defaults .......................27
+ 3.7.2 Multipart Types ..........................................27
+ 3.8 Product Tokens ..............................................28
+ 3.9 Quality Values ..............................................29
+ 3.10 Language Tags ...............................................29
+ 3.11 Entity Tags .................................................30
+ 3.12 Range Units .................................................30
+ 4 HTTP Message ..................................................31
+ 4.1 Message Types ...............................................31
+ 4.2 Message Headers .............................................31
+ 4.3 Message Body ................................................32
+ 4.4 Message Length ..............................................33
+ 4.5 General Header Fields .......................................34
+ 5 Request .......................................................35
+ 5.1 Request-Line ................................................35
+ 5.1.1 Method ...................................................36
+ 5.1.2 Request-URI ..............................................36
+ 5.2 The Resource Identified by a Request ........................38
+ 5.3 Request Header Fields .......................................38
+ 6 Response ......................................................39
+ 6.1 Status-Line .................................................39
+ 6.1.1 Status Code and Reason Phrase ............................39
+ 6.2 Response Header Fields ......................................41
+
+
+
+Fielding, et al. Standards Track [Page 2]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ 7 Entity ........................................................42
+ 7.1 Entity Header Fields ........................................42
+ 7.2 Entity Body .................................................43
+ 7.2.1 Type .....................................................43
+ 7.2.2 Entity Length ............................................43
+ 8 Connections ...................................................44
+ 8.1 Persistent Connections ......................................44
+ 8.1.1 Purpose ..................................................44
+ 8.1.2 Overall Operation ........................................45
+ 8.1.3 Proxy Servers ............................................46
+ 8.1.4 Practical Considerations .................................46
+ 8.2 Message Transmission Requirements ...........................47
+ 8.2.1 Persistent Connections and Flow Control ..................47
+ 8.2.2 Monitoring Connections for Error Status Messages .........48
+ 8.2.3 Use of the 100 (Continue) Status .........................48
+ 8.2.4 Client Behavior if Server Prematurely Closes Connection ..50
+ 9 Method Definitions ............................................51
+ 9.1 Safe and Idempotent Methods .................................51
+ 9.1.1 Safe Methods .............................................51
+ 9.1.2 Idempotent Methods .......................................51
+ 9.2 OPTIONS .....................................................52
+ 9.3 GET .........................................................53
+ 9.4 HEAD ........................................................54
+ 9.5 POST ........................................................54
+ 9.6 PUT .........................................................55
+ 9.7 DELETE ......................................................56
+ 9.8 TRACE .......................................................56
+ 9.9 CONNECT .....................................................57
+ 10 Status Code Definitions ......................................57
+ 10.1 Informational 1xx ...........................................57
+ 10.1.1 100 Continue .............................................58
+ 10.1.2 101 Switching Protocols ..................................58
+ 10.2 Successful 2xx ..............................................58
+ 10.2.1 200 OK ...................................................58
+ 10.2.2 201 Created ..............................................59
+ 10.2.3 202 Accepted .............................................59
+ 10.2.4 203 Non-Authoritative Information ........................59
+ 10.2.5 204 No Content ...........................................60
+ 10.2.6 205 Reset Content ........................................60
+ 10.2.7 206 Partial Content ......................................60
+ 10.3 Redirection 3xx .............................................61
+ 10.3.1 300 Multiple Choices .....................................61
+ 10.3.2 301 Moved Permanently ....................................62
+ 10.3.3 302 Found ................................................62
+ 10.3.4 303 See Other ............................................63
+ 10.3.5 304 Not Modified .........................................63
+ 10.3.6 305 Use Proxy ............................................64
+ 10.3.7 306 (Unused) .............................................64
+
+
+
+Fielding, et al. Standards Track [Page 3]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ 10.3.8 307 Temporary Redirect ...................................65
+ 10.4 Client Error 4xx ............................................65
+ 10.4.1 400 Bad Request .........................................65
+ 10.4.2 401 Unauthorized ........................................66
+ 10.4.3 402 Payment Required ....................................66
+ 10.4.4 403 Forbidden ...........................................66
+ 10.4.5 404 Not Found ...........................................66
+ 10.4.6 405 Method Not Allowed ..................................66
+ 10.4.7 406 Not Acceptable ......................................67
+ 10.4.8 407 Proxy Authentication Required .......................67
+ 10.4.9 408 Request Timeout .....................................67
+ 10.4.10 409 Conflict ............................................67
+ 10.4.11 410 Gone ................................................68
+ 10.4.12 411 Length Required .....................................68
+ 10.4.13 412 Precondition Failed .................................68
+ 10.4.14 413 Request Entity Too Large ............................69
+ 10.4.15 414 Request-URI Too Long ................................69
+ 10.4.16 415 Unsupported Media Type ..............................69
+ 10.4.17 416 Requested Range Not Satisfiable .....................69
+ 10.4.18 417 Expectation Failed ..................................70
+ 10.5 Server Error 5xx ............................................70
+ 10.5.1 500 Internal Server Error ................................70
+ 10.5.2 501 Not Implemented ......................................70
+ 10.5.3 502 Bad Gateway ..........................................70
+ 10.5.4 503 Service Unavailable ..................................70
+ 10.5.5 504 Gateway Timeout ......................................71
+ 10.5.6 505 HTTP Version Not Supported ...........................71
+ 11 Access Authentication ........................................71
+ 12 Content Negotiation ..........................................71
+ 12.1 Server-driven Negotiation ...................................72
+ 12.2 Agent-driven Negotiation ....................................73
+ 12.3 Transparent Negotiation .....................................74
+ 13 Caching in HTTP ..............................................74
+ 13.1.1 Cache Correctness ........................................75
+ 13.1.2 Warnings .................................................76
+ 13.1.3 Cache-control Mechanisms .................................77
+ 13.1.4 Explicit User Agent Warnings .............................78
+ 13.1.5 Exceptions to the Rules and Warnings .....................78
+ 13.1.6 Client-controlled Behavior ...............................79
+ 13.2 Expiration Model ............................................79
+ 13.2.1 Server-Specified Expiration ..............................79
+ 13.2.2 Heuristic Expiration .....................................80
+ 13.2.3 Age Calculations .........................................80
+ 13.2.4 Expiration Calculations ..................................83
+ 13.2.5 Disambiguating Expiration Values .........................84
+ 13.2.6 Disambiguating Multiple Responses ........................84
+ 13.3 Validation Model ............................................85
+ 13.3.1 Last-Modified Dates ......................................86
+
+
+
+Fielding, et al. Standards Track [Page 4]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ 13.3.2 Entity Tag Cache Validators ..............................86
+ 13.3.3 Weak and Strong Validators ...............................86
+ 13.3.4 Rules for When to Use Entity Tags and Last-Modified Dates.89
+ 13.3.5 Non-validating Conditionals ..............................90
+ 13.4 Response Cacheability .......................................91
+ 13.5 Constructing Responses From Caches ..........................92
+ 13.5.1 End-to-end and Hop-by-hop Headers ........................92
+ 13.5.2 Non-modifiable Headers ...................................92
+ 13.5.3 Combining Headers ........................................94
+ 13.5.4 Combining Byte Ranges ....................................95
+ 13.6 Caching Negotiated Responses ................................95
+ 13.7 Shared and Non-Shared Caches ................................96
+ 13.8 Errors or Incomplete Response Cache Behavior ................97
+ 13.9 Side Effects of GET and HEAD ................................97
+ 13.10 Invalidation After Updates or Deletions ...................97
+ 13.11 Write-Through Mandatory ...................................98
+ 13.12 Cache Replacement .........................................99
+ 13.13 History Lists .............................................99
+ 14 Header Field Definitions ....................................100
+ 14.1 Accept .....................................................100
+ 14.2 Accept-Charset .............................................102
+ 14.3 Accept-Encoding ............................................102
+ 14.4 Accept-Language ............................................104
+ 14.5 Accept-Ranges ..............................................105
+ 14.6 Age ........................................................106
+ 14.7 Allow ......................................................106
+ 14.8 Authorization ..............................................107
+ 14.9 Cache-Control ..............................................108
+ 14.9.1 What is Cacheable .......................................109
+ 14.9.2 What May be Stored by Caches ............................110
+ 14.9.3 Modifications of the Basic Expiration Mechanism .........111
+ 14.9.4 Cache Revalidation and Reload Controls ..................113
+ 14.9.5 No-Transform Directive ..................................115
+ 14.9.6 Cache Control Extensions ................................116
+ 14.10 Connection ...............................................117
+ 14.11 Content-Encoding .........................................118
+ 14.12 Content-Language .........................................118
+ 14.13 Content-Length ...........................................119
+ 14.14 Content-Location .........................................120
+ 14.15 Content-MD5 ..............................................121
+ 14.16 Content-Range ............................................122
+ 14.17 Content-Type .............................................124
+ 14.18 Date .....................................................124
+ 14.18.1 Clockless Origin Server Operation ......................125
+ 14.19 ETag .....................................................126
+ 14.20 Expect ...................................................126
+ 14.21 Expires ..................................................127
+ 14.22 From .....................................................128
+
+
+
+Fielding, et al. Standards Track [Page 5]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ 14.23 Host .....................................................128
+ 14.24 If-Match .................................................129
+ 14.25 If-Modified-Since ........................................130
+ 14.26 If-None-Match ............................................132
+ 14.27 If-Range .................................................133
+ 14.28 If-Unmodified-Since ......................................134
+ 14.29 Last-Modified ............................................134
+ 14.30 Location .................................................135
+ 14.31 Max-Forwards .............................................136
+ 14.32 Pragma ...................................................136
+ 14.33 Proxy-Authenticate .......................................137
+ 14.34 Proxy-Authorization ......................................137
+ 14.35 Range ....................................................138
+ 14.35.1 Byte Ranges ...........................................138
+ 14.35.2 Range Retrieval Requests ..............................139
+ 14.36 Referer ..................................................140
+ 14.37 Retry-After ..............................................141
+ 14.38 Server ...................................................141
+ 14.39 TE .......................................................142
+ 14.40 Trailer ..................................................143
+ 14.41 Transfer-Encoding..........................................143
+ 14.42 Upgrade ..................................................144
+ 14.43 User-Agent ...............................................145
+ 14.44 Vary .....................................................145
+ 14.45 Via ......................................................146
+ 14.46 Warning ..................................................148
+ 14.47 WWW-Authenticate .........................................150
+ 15 Security Considerations .......................................150
+ 15.1 Personal Information....................................151
+ 15.1.1 Abuse of Server Log Information .........................151
+ 15.1.2 Transfer of Sensitive Information .......................151
+ 15.1.3 Encoding Sensitive Information in URI's .................152
+ 15.1.4 Privacy Issues Connected to Accept Headers ..............152
+ 15.2 Attacks Based On File and Path Names .......................153
+ 15.3 DNS Spoofing ...............................................154
+ 15.4 Location Headers and Spoofing ..............................154
+ 15.5 Content-Disposition Issues .................................154
+ 15.6 Authentication Credentials and Idle Clients ................155
+ 15.7 Proxies and Caching ........................................155
+ 15.7.1 Denial of Service Attacks on Proxies....................156
+ 16 Acknowledgments .............................................156
+ 17 References ..................................................158
+ 18 Authors' Addresses ..........................................162
+ 19 Appendices ..................................................164
+ 19.1 Internet Media Type message/http and application/http ......164
+ 19.2 Internet Media Type multipart/byteranges ...................165
+ 19.3 Tolerant Applications ......................................166
+ 19.4 Differences Between HTTP Entities and RFC 2045 Entities ....167
+
+
+
+Fielding, et al. Standards Track [Page 6]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ 19.4.1 MIME-Version ............................................167
+ 19.4.2 Conversion to Canonical Form ............................167
+ 19.4.3 Conversion of Date Formats ..............................168
+ 19.4.4 Introduction of Content-Encoding ........................168
+ 19.4.5 No Content-Transfer-Encoding ............................168
+ 19.4.6 Introduction of Transfer-Encoding .......................169
+ 19.4.7 MHTML and Line Length Limitations .......................169
+ 19.5 Additional Features ........................................169
+ 19.5.1 Content-Disposition .....................................170
+ 19.6 Compatibility with Previous Versions .......................170
+ 19.6.1 Changes from HTTP/1.0 ...................................171
+ 19.6.2 Compatibility with HTTP/1.0 Persistent Connections ......172
+ 19.6.3 Changes from RFC 2068 ...................................172
+ 20 Index .......................................................175
+ 21 Full Copyright Statement ....................................176
+
+1 Introduction
+
+1.1 Purpose
+
+ The Hypertext Transfer Protocol (HTTP) is an application-level
+ protocol for distributed, collaborative, hypermedia information
+ systems. HTTP has been in use by the World-Wide Web global
+ information initiative since 1990. The first version of HTTP,
+ referred to as HTTP/0.9, was a simple protocol for raw data transfer
+ across the Internet. HTTP/1.0, as defined by RFC 1945 [6], improved
+ the protocol by allowing messages to be in the format of MIME-like
+ messages, containing metainformation about the data transferred and
+ modifiers on the request/response semantics. However, HTTP/1.0 does
+ not sufficiently take into consideration the effects of hierarchical
+ proxies, caching, the need for persistent connections, or virtual
+ hosts. In addition, the proliferation of incompletely-implemented
+ applications calling themselves "HTTP/1.0" has necessitated a
+ protocol version change in order for two communicating applications
+ to determine each other's true capabilities.
+
+ This specification defines the protocol referred to as "HTTP/1.1".
+ This protocol includes more stringent requirements than HTTP/1.0 in
+ order to ensure reliable implementation of its features.
+
+ Practical information systems require more functionality than simple
+ retrieval, including search, front-end update, and annotation. HTTP
+ allows an open-ended set of methods and headers that indicate the
+ purpose of a request [47]. It builds on the discipline of reference
+ provided by the Uniform Resource Identifier (URI) [3], as a location
+ (URL) [4] or name (URN) [20], for indicating the resource to which a
+
+
+
+
+
+Fielding, et al. Standards Track [Page 7]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ method is to be applied. Messages are passed in a format similar to
+ that used by Internet mail [9] as defined by the Multipurpose
+ Internet Mail Extensions (MIME) [7].
+
+ HTTP is also used as a generic protocol for communication between
+ user agents and proxies/gateways to other Internet systems, including
+ those supported by the SMTP [16], NNTP [13], FTP [18], Gopher [2],
+ and WAIS [10] protocols. In this way, HTTP allows basic hypermedia
+ access to resources available from diverse applications.
+
+1.2 Requirements
+
+ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
+ "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
+ document are to be interpreted as described in RFC 2119 [34].
+
+ An implementation is not compliant if it fails to satisfy one or more
+ of the MUST or REQUIRED level requirements for the protocols it
+ implements. An implementation that satisfies all the MUST or REQUIRED
+ level and all the SHOULD level requirements for its protocols is said
+ to be "unconditionally compliant"; one that satisfies all the MUST
+ level requirements but not all the SHOULD level requirements for its
+ protocols is said to be "conditionally compliant."
+
+1.3 Terminology
+
+ This specification uses a number of terms to refer to the roles
+ played by participants in, and objects of, the HTTP communication.
+
+ connection
+ A transport layer virtual circuit established between two programs
+ for the purpose of communication.
+
+ message
+ The basic unit of HTTP communication, consisting of a structured
+ sequence of octets matching the syntax defined in section 4 and
+ transmitted via the connection.
+
+ request
+ An HTTP request message, as defined in section 5.
+
+ response
+ An HTTP response message, as defined in section 6.
+
+
+
+
+
+
+
+
+Fielding, et al. Standards Track [Page 8]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ resource
+ A network data object or service that can be identified by a URI,
+ as defined in section 3.2. Resources may be available in multiple
+ representations (e.g. multiple languages, data formats, size, and
+ resolutions) or vary in other ways.
+
+ entity
+ The information transferred as the payload of a request or
+ response. An entity consists of metainformation in the form of
+ entity-header fields and content in the form of an entity-body, as
+ described in section 7.
+
+ representation
+ An entity included with a response that is subject to content
+ negotiation, as described in section 12. There may exist multiple
+ representations associated with a particular response status.
+
+ content negotiation
+ The mechanism for selecting the appropriate representation when
+ servicing a request, as described in section 12. The
+ representation of entities in any response can be negotiated
+ (including error responses).
+
+ variant
+ A resource may have one, or more than one, representation(s)
+ associated with it at any given instant. Each of these
+ representations is termed a `varriant'. Use of the term `variant'
+ does not necessarily imply that the resource is subject to content
+ negotiation.
+
+ client
+ A program that establishes connections for the purpose of sending
+ requests.
+
+ user agent
+ The client which initiates a request. These are often browsers,
+ editors, spiders (web-traversing robots), or other end user tools.
+
+ server
+ An application program that accepts connections in order to
+ service requests by sending back responses. Any given program may
+ be capable of being both a client and a server; our use of these
+ terms refers only to the role being performed by the program for a
+ particular connection, rather than to the program's capabilities
+ in general. Likewise, any server may act as an origin server,
+ proxy, gateway, or tunnel, switching behavior based on the nature
+ of each request.
+
+
+
+
+Fielding, et al. Standards Track [Page 9]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ origin server
+ The server on which a given resource resides or is to be created.
+
+ proxy
+ An intermediary program which acts as both a server and a client
+ for the purpose of making requests on behalf of other clients.
+ Requests are serviced internally or by passing them on, with
+ possible translation, to other servers. A proxy MUST implement
+ both the client and server requirements of this specification. A
+ "transparent proxy" is a proxy that does not modify the request or
+ response beyond what is required for proxy authentication and
+ identification. A "non-transparent proxy" is a proxy that modifies
+ the request or response in order to provide some added service to
+ the user agent, such as group annotation services, media type
+ transformation, protocol reduction, or anonymity filtering. Except
+ where either transparent or non-transparent behavior is explicitly
+ stated, the HTTP proxy requirements apply to both types of
+ proxies.
+
+ gateway
+ A server which acts as an intermediary for some other server.
+ Unlike a proxy, a gateway receives requests as if it were the
+ origin server for the requested resource; the requesting client
+ may not be aware that it is communicating with a gateway.
+
+ tunnel
+ An intermediary program which is acting as a blind relay between
+ two connections. Once active, a tunnel is not considered a party
+ to the HTTP communication, though the tunnel may have been
+ initiated by an HTTP request. The tunnel ceases to exist when both
+ ends of the relayed connections are closed.
+
+ cache
+ A program's local store of response messages and the subsystem
+ that controls its message storage, retrieval, and deletion. A
+ cache stores cacheable responses in order to reduce the response
+ time and network bandwidth consumption on future, equivalent
+ requests. Any client or server may include a cache, though a cache
+ cannot be used by a server that is acting as a tunnel.
+
+ cacheable
+ A response is cacheable if a cache is allowed to store a copy of
+ the response message for use in answering subsequent requests. The
+ rules for determining the cacheability of HTTP responses are
+ defined in section 13. Even if a resource is cacheable, there may
+ be additional constraints on whether a cache can use the cached
+ copy for a particular request.
+
+
+
+
+Fielding, et al. Standards Track [Page 10]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ first-hand
+ A response is first-hand if it comes directly and without
+ unnecessary delay from the origin server, perhaps via one or more
+ proxies. A response is also first-hand if its validity has just
+ been checked directly with the origin server.
+
+ explicit expiration time
+ The time at which the origin server intends that an entity should
+ no longer be returned by a cache without further validation.
+
+ heuristic expiration time
+ An expiration time assigned by a cache when no explicit expiration
+ time is available.
+
+ age
+ The age of a response is the time since it was sent by, or
+ successfully validated with, the origin server.
+
+ freshness lifetime
+ The length of time between the generation of a response and its
+ expiration time.
+
+ fresh
+ A response is fresh if its age has not yet exceeded its freshness
+ lifetime.
+
+ stale
+ A response is stale if its age has passed its freshness lifetime.
+
+ semantically transparent
+ A cache behaves in a "semantically transparent" manner, with
+ respect to a particular response, when its use affects neither the
+ requesting client nor the origin server, except to improve
+ performance. When a cache is semantically transparent, the client
+ receives exactly the same response (except for hop-by-hop headers)
+ that it would have received had its request been handled directly
+ by the origin server.
+
+ validator
+ A protocol element (e.g., an entity tag or a Last-Modified time)
+ that is used to find out whether a cache entry is an equivalent
+ copy of an entity.
+
+ upstream/downstream
+ Upstream and downstream describe the flow of a message: all
+ messages flow from upstream to downstream.
+
+
+
+
+
+Fielding, et al. Standards Track [Page 11]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ inbound/outbound
+ Inbound and outbound refer to the request and response paths for
+ messages: "inbound" means "traveling toward the origin server",
+ and "outbound" means "traveling toward the user agent"
+
+1.4 Overall Operation
+
+ The HTTP protocol is a request/response protocol. A client sends a
+ request to the server in the form of a request method, URI, and
+ protocol version, followed by a MIME-like message containing request
+ modifiers, client information, and possible body content over a
+ connection with a server. The server responds with a status line,
+ including the message's protocol version and a success or error code,
+ followed by a MIME-like message containing server information, entity
+ metainformation, and possible entity-body content. The relationship
+ between HTTP and MIME is described in appendix 19.4.
+
+ Most HTTP communication is initiated by a user agent and consists of
+ a request to be applied to a resource on some origin server. In the
+ simplest case, this may be accomplished via a single connection (v)
+ between the user agent (UA) and the origin server (O).
+
+ request chain ------------------------>
+ UA -------------------v------------------- O
+ <----------------------- response chain
+
+ A more complicated situation occurs when one or more intermediaries
+ are present in the request/response chain. There are three common
+ forms of intermediary: proxy, gateway, and tunnel. A proxy is a
+ forwarding agent, receiving requests for a URI in its absolute form,
+ rewriting all or part of the message, and forwarding the reformatted
+ request toward the server identified by the URI. A gateway is a
+ receiving agent, acting as a layer above some other server(s) and, if
+ necessary, translating the requests to the underlying server's
+ protocol. A tunnel acts as a relay point between two connections
+ without changing the messages; tunnels are used when the
+ communication needs to pass through an intermediary (such as a
+ firewall) even when the intermediary cannot understand the contents
+ of the messages.
+
+ request chain -------------------------------------->
+ UA -----v----- A -----v----- B -----v----- C -----v----- O
+ <------------------------------------- response chain
+
+ The figure above shows three intermediaries (A, B, and C) between the
+ user agent and origin server. A request or response message that
+ travels the whole chain will pass through four separate connections.
+ This distinction is important because some HTTP communication options
+
+
+
+Fielding, et al. Standards Track [Page 12]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ may apply only to the connection with the nearest, non-tunnel
+ neighbor, only to the end-points of the chain, or to all connections
+ along the chain. Although the diagram is linear, each participant may
+ be engaged in multiple, simultaneous communications. For example, B
+ may be receiving requests from many clients other than A, and/or
+ forwarding requests to servers other than C, at the same time that it
+ is handling A's request.
+
+ Any party to the communication which is not acting as a tunnel may
+ employ an internal cache for handling requests. The effect of a cache
+ is that the request/response chain is shortened if one of the
+ participants along the chain has a cached response applicable to that
+ request. The following illustrates the resulting chain if B has a
+ cached copy of an earlier response from O (via C) for a request which
+ has not been cached by UA or A.
+
+ request chain ---------->
+ UA -----v----- A -----v----- B - - - - - - C - - - - - - O
+ <--------- response chain
+
+ Not all responses are usefully cacheable, and some requests may
+ contain modifiers which place special requirements on cache behavior.
+ HTTP requirements for cache behavior and cacheable responses are
+ defined in section 13.
+
+ In fact, there are a wide variety of architectures and configurations
+ of caches and proxies currently being experimented with or deployed
+ across the World Wide Web. These systems include national hierarchies
+ of proxy caches to save transoceanic bandwidth, systems that
+ broadcast or multicast cache entries, organizations that distribute
+ subsets of cached data via CD-ROM, and so on. HTTP systems are used
+ in corporate intranets over high-bandwidth links, and for access via
+ PDAs with low-power radio links and intermittent connectivity. The
+ goal of HTTP/1.1 is to support the wide diversity of configurations
+ already deployed while introducing protocol constructs that meet the
+ needs of those who build web applications that require high
+ reliability and, failing that, at least reliable indications of
+ failure.
+
+ HTTP communication usually takes place over TCP/IP connections. The
+ default port is TCP 80 [19], but other ports can be used. This does
+ not preclude HTTP from being implemented on top of any other protocol
+ on the Internet, or on other networks. HTTP only presumes a reliable
+ transport; any protocol that provides such guarantees can be used;
+ the mapping of the HTTP/1.1 request and response structures onto the
+ transport data units of the protocol in question is outside the scope
+ of this specification.
+
+
+
+
+Fielding, et al. Standards Track [Page 13]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ In HTTP/1.0, most implementations used a new connection for each
+ request/response exchange. In HTTP/1.1, a connection may be used for
+ one or more request/response exchanges, although connections may be
+ closed for a variety of reasons (see section 8.1).
+
+2 Notational Conventions and Generic Grammar
+
+2.1 Augmented BNF
+
+ All of the mechanisms specified in this document are described in
+ both prose and an augmented Backus-Naur Form (BNF) similar to that
+ used by RFC 822 [9]. Implementors will need to be familiar with the
+ notation in order to understand this specification. The augmented BNF
+ includes the following constructs:
+
+ name = definition
+ The name of a rule is simply the name itself (without any
+ enclosing "<" and ">") and is separated from its definition by the
+ equal "=" character. White space is only significant in that
+ indentation of continuation lines is used to indicate a rule
+ definition that spans more than one line. Certain basic rules are
+ in uppercase, such as SP, LWS, HT, CRLF, DIGIT, ALPHA, etc. Angle
+ brackets are used within definitions whenever their presence will
+ facilitate discerning the use of rule names.
+
+ "literal"
+ Quotation marks surround literal text. Unless stated otherwise,
+ the text is case-insensitive.
+
+ rule1 | rule2
+ Elements separated by a bar ("|") are alternatives, e.g., "yes |
+ no" will accept yes or no.
+
+ (rule1 rule2)
+ Elements enclosed in parentheses are treated as a single element.
+ Thus, "(elem (foo | bar) elem)" allows the token sequences "elem
+ foo elem" and "elem bar elem".
+
+ *rule
+ The character "*" preceding an element indicates repetition. The
+ full form is "<n>*<m>element" indicating at least <n> and at most
+ <m> occurrences of element. Default values are 0 and infinity so
+ that "*(element)" allows any number, including zero; "1*element"
+ requires at least one; and "1*2element" allows one or two.
+
+ [rule]
+ Square brackets enclose optional elements; "[foo bar]" is
+ equivalent to "*1(foo bar)".
+
+
+
+Fielding, et al. Standards Track [Page 14]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ N rule
+ Specific repetition: "<n>(element)" is equivalent to
+ "<n>*<n>(element)"; that is, exactly <n> occurrences of (element).
+ Thus 2DIGIT is a 2-digit number, and 3ALPHA is a string of three
+ alphabetic characters.
+
+ #rule
+ A construct "#" is defined, similar to "*", for defining lists of
+ elements. The full form is "<n>#<m>element" indicating at least
+ <n> and at most <m> elements, each separated by one or more commas
+ (",") and OPTIONAL linear white space (LWS). This makes the usual
+ form of lists very easy; a rule such as
+ ( *LWS element *( *LWS "," *LWS element ))
+ can be shown as
+ 1#element
+ Wherever this construct is used, null elements are allowed, but do
+ not contribute to the count of elements present. That is,
+ "(element), , (element) " is permitted, but counts as only two
+ elements. Therefore, where at least one element is required, at
+ least one non-null element MUST be present. Default values are 0
+ and infinity so that "#element" allows any number, including zero;
+ "1#element" requires at least one; and "1#2element" allows one or
+ two.
+
+ ; comment
+ A semi-colon, set off some distance to the right of rule text,
+ starts a comment that continues to the end of line. This is a
+ simple way of including useful notes in parallel with the
+ specifications.
+
+ implied *LWS
+ The grammar described by this specification is word-based. Except
+ where noted otherwise, linear white space (LWS) can be included
+ between any two adjacent words (token or quoted-string), and
+ between adjacent words and separators, without changing the
+ interpretation of a field. At least one delimiter (LWS and/or
+
+ separators) MUST exist between any two tokens (for the definition
+ of "token" below), since they would otherwise be interpreted as a
+ single token.
+
+2.2 Basic Rules
+
+ The following rules are used throughout this specification to
+ describe basic parsing constructs. The US-ASCII coded character set
+ is defined by ANSI X3.4-1986 [21].
+
+
+
+
+
+Fielding, et al. Standards Track [Page 15]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ OCTET = <any 8-bit sequence of data>
+ CHAR = <any US-ASCII character (octets 0 - 127)>
+ UPALPHA = <any US-ASCII uppercase letter "A".."Z">
+ LOALPHA = <any US-ASCII lowercase letter "a".."z">
+ ALPHA = UPALPHA | LOALPHA
+ DIGIT = <any US-ASCII digit "0".."9">
+ CTL = <any US-ASCII control character
+ (octets 0 - 31) and DEL (127)>
+ CR = <US-ASCII CR, carriage return (13)>
+ LF = <US-ASCII LF, linefeed (10)>
+ SP = <US-ASCII SP, space (32)>
+ HT = <US-ASCII HT, horizontal-tab (9)>
+ <"> = <US-ASCII double-quote mark (34)>
+
+ HTTP/1.1 defines the sequence CR LF as the end-of-line marker for all
+ protocol elements except the entity-body (see appendix 19.3 for
+ tolerant applications). The end-of-line marker within an entity-body
+ is defined by its associated media type, as described in section 3.7.
+
+ CRLF = CR LF
+
+ HTTP/1.1 header field values can be folded onto multiple lines if the
+ continuation line begins with a space or horizontal tab. All linear
+ white space, including folding, has the same semantics as SP. A
+ recipient MAY replace any linear white space with a single SP before
+ interpreting the field value or forwarding the message downstream.
+
+ LWS = [CRLF] 1*( SP | HT )
+
+ The TEXT rule is only used for descriptive field contents and values
+ that are not intended to be interpreted by the message parser. Words
+ of *TEXT MAY contain characters from character sets other than ISO-
+ 8859-1 [22] only when encoded according to the rules of RFC 2047
+ [14].
+
+ TEXT = <any OCTET except CTLs,
+ but including LWS>
+
+ A CRLF is allowed in the definition of TEXT only as part of a header
+ field continuation. It is expected that the folding LWS will be
+ replaced with a single SP before interpretation of the TEXT value.
+
+ Hexadecimal numeric characters are used in several protocol elements.
+
+ HEX = "A" | "B" | "C" | "D" | "E" | "F"
+ | "a" | "b" | "c" | "d" | "e" | "f" | DIGIT
+
+
+
+
+
+Fielding, et al. Standards Track [Page 16]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ Many HTTP/1.1 header field values consist of words separated by LWS
+ or special characters. These special characters MUST be in a quoted
+ string to be used within a parameter value (as defined in section
+ 3.6).
+
+ token = 1*<any CHAR except CTLs or separators>
+ separators = "(" | ")" | "<" | ">" | "@"
+ | "," | ";" | ":" | "\" | <">
+ | "/" | "[" | "]" | "?" | "="
+ | "{" | "}" | SP | HT
+
+ Comments can be included in some HTTP header fields by surrounding
+ the comment text with parentheses. Comments are only allowed in
+ fields containing "comment" as part of their field value definition.
+ In all other fields, parentheses are considered part of the field
+ value.
+
+ comment = "(" *( ctext | quoted-pair | comment ) ")"
+ ctext = <any TEXT excluding "(" and ")">
+
+ A string of text is parsed as a single word if it is quoted using
+ double-quote marks.
+
+ quoted-string = ( <"> *(qdtext | quoted-pair ) <"> )
+ qdtext = <any TEXT except <">>
+
+ The backslash character ("\") MAY be used as a single-character
+ quoting mechanism only within quoted-string and comment constructs.
+
+ quoted-pair = "\" CHAR
+
+3 Protocol Parameters
+
+3.1 HTTP Version
+
+ HTTP uses a "<major>.<minor>" numbering scheme to indicate versions
+ of the protocol. The protocol versioning policy is intended to allow
+ the sender to indicate the format of a message and its capacity for
+ understanding further HTTP communication, rather than the features
+ obtained via that communication. No change is made to the version
+ number for the addition of message components which do not affect
+ communication behavior or which only add to extensible field values.
+ The <minor> number is incremented when the changes made to the
+ protocol add features which do not change the general message parsing
+ algorithm, but which may add to the message semantics and imply
+ additional capabilities of the sender. The <major> number is
+ incremented when the format of a message within the protocol is
+ changed. See RFC 2145 [36] for a fuller explanation.
+
+
+
+Fielding, et al. Standards Track [Page 17]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ The version of an HTTP message is indicated by an HTTP-Version field
+ in the first line of the message.
+
+ HTTP-Version = "HTTP" "/" 1*DIGIT "." 1*DIGIT
+
+ Note that the major and minor numbers MUST be treated as separate
+ integers and that each MAY be incremented higher than a single digit.
+ Thus, HTTP/2.4 is a lower version than HTTP/2.13, which in turn is
+ lower than HTTP/12.3. Leading zeros MUST be ignored by recipients and
+ MUST NOT be sent.
+
+ An application that sends a request or response message that includes
+ HTTP-Version of "HTTP/1.1" MUST be at least conditionally compliant
+ with this specification. Applications that are at least conditionally
+ compliant with this specification SHOULD use an HTTP-Version of
+ "HTTP/1.1" in their messages, and MUST do so for any message that is
+ not compatible with HTTP/1.0. For more details on when to send
+ specific HTTP-Version values, see RFC 2145 [36].
+
+ The HTTP version of an application is the highest HTTP version for
+ which the application is at least conditionally compliant.
+
+ Proxy and gateway applications need to be careful when forwarding
+ messages in protocol versions different from that of the application.
+ Since the protocol version indicates the protocol capability of the
+ sender, a proxy/gateway MUST NOT send a message with a version
+ indicator which is greater than its actual version. If a higher
+ version request is received, the proxy/gateway MUST either downgrade
+ the request version, or respond with an error, or switch to tunnel
+ behavior.
+
+ Due to interoperability problems with HTTP/1.0 proxies discovered
+ since the publication of RFC 2068[33], caching proxies MUST, gateways
+ MAY, and tunnels MUST NOT upgrade the request to the highest version
+ they support. The proxy/gateway's response to that request MUST be in
+ the same major version as the request.
+
+ Note: Converting between versions of HTTP may involve modification
+ of header fields required or forbidden by the versions involved.
+
+3.2 Uniform Resource Identifiers
+
+ URIs have been known by many names: WWW addresses, Universal Document
+ Identifiers, Universal Resource Identifiers [3], and finally the
+ combination of Uniform Resource Locators (URL) [4] and Names (URN)
+ [20]. As far as HTTP is concerned, Uniform Resource Identifiers are
+ simply formatted strings which identify--via name, location, or any
+ other characteristic--a resource.
+
+
+
+Fielding, et al. Standards Track [Page 18]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+3.2.1 General Syntax
+
+ URIs in HTTP can be represented in absolute form or relative to some
+ known base URI [11], depending upon the context of their use. The two
+ forms are differentiated by the fact that absolute URIs always begin
+ with a scheme name followed by a colon. For definitive information on
+ URL syntax and semantics, see "Uniform Resource Identifiers (URI):
+ Generic Syntax and Semantics," RFC 2396 [42] (which replaces RFCs
+ 1738 [4] and RFC 1808 [11]). This specification adopts the
+ definitions of "URI-reference", "absoluteURI", "relativeURI", "port",
+ "host","abs_path", "rel_path", and "authority" from that
+ specification.
+
+ The HTTP protocol does not place any a priori limit on the length of
+ a URI. Servers MUST be able to handle the URI of any resource they
+ serve, and SHOULD be able to handle URIs of unbounded length if they
+ provide GET-based forms that could generate such URIs. A server
+ SHOULD return 414 (Request-URI Too Long) status if a URI is longer
+ than the server can handle (see section 10.4.15).
+
+ Note: Servers ought to be cautious about depending on URI lengths
+ above 255 bytes, because some older client or proxy
+ implementations might not properly support these lengths.
+
+3.2.2 http URL
+
+ The "http" scheme is used to locate network resources via the HTTP
+ protocol. This section defines the scheme-specific syntax and
+ semantics for http URLs.
+
+ http_URL = "http:" "//" host [ ":" port ] [ abs_path [ "?" query ]]
+
+ If the port is empty or not given, port 80 is assumed. The semantics
+ are that the identified resource is located at the server listening
+ for TCP connections on that port of that host, and the Request-URI
+ for the resource is abs_path (section 5.1.2). The use of IP addresses
+ in URLs SHOULD be avoided whenever possible (see RFC 1900 [24]). If
+ the abs_path is not present in the URL, it MUST be given as "/" when
+ used as a Request-URI for a resource (section 5.1.2). If a proxy
+ receives a host name which is not a fully qualified domain name, it
+ MAY add its domain to the host name it received. If a proxy receives
+ a fully qualified domain name, the proxy MUST NOT change the host
+ name.
+
+
+
+
+
+
+
+
+Fielding, et al. Standards Track [Page 19]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+3.2.3 URI Comparison
+
+ When comparing two URIs to decide if they match or not, a client
+ SHOULD use a case-sensitive octet-by-octet comparison of the entire
+ URIs, with these exceptions:
+
+ - A port that is empty or not given is equivalent to the default
+ port for that URI-reference;
+
+ - Comparisons of host names MUST be case-insensitive;
+
+ - Comparisons of scheme names MUST be case-insensitive;
+
+ - An empty abs_path is equivalent to an abs_path of "/".
+
+ Characters other than those in the "reserved" and "unsafe" sets (see
+ RFC 2396 [42]) are equivalent to their ""%" HEX HEX" encoding.
+
+ For example, the following three URIs are equivalent:
+
+ http://abc.com:80/~smith/home.html
+ http://ABC.com/%7Esmith/home.html
+ http://ABC.com:/%7esmith/home.html
+
+3.3 Date/Time Formats
+
+3.3.1 Full Date
+
+ HTTP applications have historically allowed three different formats
+ for the representation of date/time stamps:
+
+ Sun, 06 Nov 1994 08:49:37 GMT ; RFC 822, updated by RFC 1123
+ Sunday, 06-Nov-94 08:49:37 GMT ; RFC 850, obsoleted by RFC 1036
+ Sun Nov 6 08:49:37 1994 ; ANSI C's asctime() format
+
+ The first format is preferred as an Internet standard and represents
+ a fixed-length subset of that defined by RFC 1123 [8] (an update to
+ RFC 822 [9]). The second format is in common use, but is based on the
+ obsolete RFC 850 [12] date format and lacks a four-digit year.
+ HTTP/1.1 clients and servers that parse the date value MUST accept
+ all three formats (for compatibility with HTTP/1.0), though they MUST
+ only generate the RFC 1123 format for representing HTTP-date values
+ in header fields. See section 19.3 for further information.
+
+ Note: Recipients of date values are encouraged to be robust in
+ accepting date values that may have been sent by non-HTTP
+ applications, as is sometimes the case when retrieving or posting
+ messages via proxies/gateways to SMTP or NNTP.
+
+
+
+Fielding, et al. Standards Track [Page 20]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ All HTTP date/time stamps MUST be represented in Greenwich Mean Time
+ (GMT), without exception. For the purposes of HTTP, GMT is exactly
+ equal to UTC (Coordinated Universal Time). This is indicated in the
+ first two formats by the inclusion of "GMT" as the three-letter
+ abbreviation for time zone, and MUST be assumed when reading the
+ asctime format. HTTP-date is case sensitive and MUST NOT include
+ additional LWS beyond that specifically included as SP in the
+ grammar.
+
+ HTTP-date = rfc1123-date | rfc850-date | asctime-date
+ rfc1123-date = wkday "," SP date1 SP time SP "GMT"
+ rfc850-date = weekday "," SP date2 SP time SP "GMT"
+ asctime-date = wkday SP date3 SP time SP 4DIGIT
+ date1 = 2DIGIT SP month SP 4DIGIT
+ ; day month year (e.g., 02 Jun 1982)
+ date2 = 2DIGIT "-" month "-" 2DIGIT
+ ; day-month-year (e.g., 02-Jun-82)
+ date3 = month SP ( 2DIGIT | ( SP 1DIGIT ))
+ ; month day (e.g., Jun 2)
+ time = 2DIGIT ":" 2DIGIT ":" 2DIGIT
+ ; 00:00:00 - 23:59:59
+ wkday = "Mon" | "Tue" | "Wed"
+ | "Thu" | "Fri" | "Sat" | "Sun"
+ weekday = "Monday" | "Tuesday" | "Wednesday"
+ | "Thursday" | "Friday" | "Saturday" | "Sunday"
+ month = "Jan" | "Feb" | "Mar" | "Apr"
+ | "May" | "Jun" | "Jul" | "Aug"
+ | "Sep" | "Oct" | "Nov" | "Dec"
+
+ Note: HTTP requirements for the date/time stamp format apply only
+ to their usage within the protocol stream. Clients and servers are
+ not required to use these formats for user presentation, request
+ logging, etc.
+
+3.3.2 Delta Seconds
+
+ Some HTTP header fields allow a time value to be specified as an
+ integer number of seconds, represented in decimal, after the time
+ that the message was received.
+
+ delta-seconds = 1*DIGIT
+
+3.4 Character Sets
+
+ HTTP uses the same definition of the term "character set" as that
+ described for MIME:
+
+
+
+
+
+Fielding, et al. Standards Track [Page 21]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ The term "character set" is used in this document to refer to a
+ method used with one or more tables to convert a sequence of octets
+ into a sequence of characters. Note that unconditional conversion in
+ the other direction is not required, in that not all characters may
+ be available in a given character set and a character set may provide
+ more than one sequence of octets to represent a particular character.
+ This definition is intended to allow various kinds of character
+ encoding, from simple single-table mappings such as US-ASCII to
+ complex table switching methods such as those that use ISO-2022's
+ techniques. However, the definition associated with a MIME character
+ set name MUST fully specify the mapping to be performed from octets
+ to characters. In particular, use of external profiling information
+ to determine the exact mapping is not permitted.
+
+ Note: This use of the term "character set" is more commonly
+ referred to as a "character encoding." However, since HTTP and
+ MIME share the same registry, it is important that the terminology
+ also be shared.
+
+ HTTP character sets are identified by case-insensitive tokens. The
+ complete set of tokens is defined by the IANA Character Set registry
+ [19].
+
+ charset = token
+
+ Although HTTP allows an arbitrary token to be used as a charset
+ value, any token that has a predefined value within the IANA
+ Character Set registry [19] MUST represent the character set defined
+ by that registry. Applications SHOULD limit their use of character
+ sets to those defined by the IANA registry.
+
+ Implementors should be aware of IETF character set requirements [38]
+ [41].
+
+3.4.1 Missing Charset
+
+ Some HTTP/1.0 software has interpreted a Content-Type header without
+ charset parameter incorrectly to mean "recipient should guess."
+ Senders wishing to defeat this behavior MAY include a charset
+ parameter even when the charset is ISO-8859-1 and SHOULD do so when
+ it is known that it will not confuse the recipient.
+
+ Unfortunately, some older HTTP/1.0 clients did not deal properly with
+ an explicit charset parameter. HTTP/1.1 recipients MUST respect the
+ charset label provided by the sender; and those user agents that have
+ a provision to "guess" a charset MUST use the charset from the
+
+
+
+
+
+Fielding, et al. Standards Track [Page 22]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ content-type field if they support that charset, rather than the
+ recipient's preference, when initially displaying a document. See
+ section 3.7.1.
+
+3.5 Content Codings
+
+ Content coding values indicate an encoding transformation that has
+ been or can be applied to an entity. Content codings are primarily
+ used to allow a document to be compressed or otherwise usefully
+ transformed without losing the identity of its underlying media type
+ and without loss of information. Frequently, the entity is stored in
+ coded form, transmitted directly, and only decoded by the recipient.
+
+ content-coding = token
+
+ All content-coding values are case-insensitive. HTTP/1.1 uses
+ content-coding values in the Accept-Encoding (section 14.3) and
+ Content-Encoding (section 14.11) header fields. Although the value
+ describes the content-coding, what is more important is that it
+ indicates what decoding mechanism will be required to remove the
+ encoding.
+
+ The Internet Assigned Numbers Authority (IANA) acts as a registry for
+ content-coding value tokens. Initially, the registry contains the
+ following tokens:
+
+ gzip An encoding format produced by the file compression program
+ "gzip" (GNU zip) as described in RFC 1952 [25]. This format is a
+ Lempel-Ziv coding (LZ77) with a 32 bit CRC.
+
+ compress
+ The encoding format produced by the common UNIX file compression
+ program "compress". This format is an adaptive Lempel-Ziv-Welch
+ coding (LZW).
+
+ Use of program names for the identification of encoding formats
+ is not desirable and is discouraged for future encodings. Their
+ use here is representative of historical practice, not good
+ design. For compatibility with previous implementations of HTTP,
+ applications SHOULD consider "x-gzip" and "x-compress" to be
+ equivalent to "gzip" and "compress" respectively.
+
+ deflate
+ The "zlib" format defined in RFC 1950 [31] in combination with
+ the "deflate" compression mechanism described in RFC 1951 [29].
+
+
+
+
+
+
+Fielding, et al. Standards Track [Page 23]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ identity
+ The default (identity) encoding; the use of no transformation
+ whatsoever. This content-coding is used only in the Accept-
+ Encoding header, and SHOULD NOT be used in the Content-Encoding
+ header.
+
+ New content-coding value tokens SHOULD be registered; to allow
+ interoperability between clients and servers, specifications of the
+ content coding algorithms needed to implement a new value SHOULD be
+ publicly available and adequate for independent implementation, and
+ conform to the purpose of content coding defined in this section.
+
+3.6 Transfer Codings
+
+ Transfer-coding values are used to indicate an encoding
+ transformation that has been, can be, or may need to be applied to an
+ entity-body in order to ensure "safe transport" through the network.
+ This differs from a content coding in that the transfer-coding is a
+ property of the message, not of the original entity.
+
+ transfer-coding = "chunked" | transfer-extension
+ transfer-extension = token *( ";" parameter )
+
+ Parameters are in the form of attribute/value pairs.
+
+ parameter = attribute "=" value
+ attribute = token
+ value = token | quoted-string
+
+ All transfer-coding values are case-insensitive. HTTP/1.1 uses
+ transfer-coding values in the TE header field (section 14.39) and in
+ the Transfer-Encoding header field (section 14.41).
+
+ Whenever a transfer-coding is applied to a message-body, the set of
+ transfer-codings MUST include "chunked", unless the message is
+ terminated by closing the connection. When the "chunked" transfer-
+ coding is used, it MUST be the last transfer-coding applied to the
+ message-body. The "chunked" transfer-coding MUST NOT be applied more
+ than once to a message-body. These rules allow the recipient to
+ determine the transfer-length of the message (section 4.4).
+
+ Transfer-codings are analogous to the Content-Transfer-Encoding
+ values of MIME [7], which were designed to enable safe transport of
+ binary data over a 7-bit transport service. However, safe transport
+ has a different focus for an 8bit-clean transfer protocol. In HTTP,
+ the only unsafe characteristic of message-bodies is the difficulty in
+ determining the exact body length (section 7.2.2), or the desire to
+ encrypt data over a shared transport.
+
+
+
+Fielding, et al. Standards Track [Page 24]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ The Internet Assigned Numbers Authority (IANA) acts as a registry for
+ transfer-coding value tokens. Initially, the registry contains the
+ following tokens: "chunked" (section 3.6.1), "identity" (section
+ 3.6.2), "gzip" (section 3.5), "compress" (section 3.5), and "deflate"
+ (section 3.5).
+
+ New transfer-coding value tokens SHOULD be registered in the same way
+ as new content-coding value tokens (section 3.5).
+
+ A server which receives an entity-body with a transfer-coding it does
+ not understand SHOULD return 501 (Unimplemented), and close the
+ connection. A server MUST NOT send transfer-codings to an HTTP/1.0
+ client.
+
+3.6.1 Chunked Transfer Coding
+
+ The chunked encoding modifies the body of a message in order to
+ transfer it as a series of chunks, each with its own size indicator,
+ followed by an OPTIONAL trailer containing entity-header fields. This
+ allows dynamically produced content to be transferred along with the
+ information necessary for the recipient to verify that it has
+ received the full message.
+
+ Chunked-Body = *chunk
+ last-chunk
+ trailer
+ CRLF
+
+ chunk = chunk-size [ chunk-extension ] CRLF
+ chunk-data CRLF
+ chunk-size = 1*HEX
+ last-chunk = 1*("0") [ chunk-extension ] CRLF
+
+ chunk-extension= *( ";" chunk-ext-name [ "=" chunk-ext-val ] )
+ chunk-ext-name = token
+ chunk-ext-val = token | quoted-string
+ chunk-data = chunk-size(OCTET)
+ trailer = *(entity-header CRLF)
+
+ The chunk-size field is a string of hex digits indicating the size of
+ the chunk. The chunked encoding is ended by any chunk whose size is
+ zero, followed by the trailer, which is terminated by an empty line.
+
+ The trailer allows the sender to include additional HTTP header
+ fields at the end of the message. The Trailer header field can be
+ used to indicate which header fields are included in a trailer (see
+ section 14.40).
+
+
+
+
+Fielding, et al. Standards Track [Page 25]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ A server using chunked transfer-coding in a response MUST NOT use the
+ trailer for any header fields unless at least one of the following is
+ true:
+
+ a)the request included a TE header field that indicates "trailers" is
+ acceptable in the transfer-coding of the response, as described in
+ section 14.39; or,
+
+ b)the server is the origin server for the response, the trailer
+ fields consist entirely of optional metadata, and the recipient
+ could use the message (in a manner acceptable to the origin server)
+ without receiving this metadata. In other words, the origin server
+ is willing to accept the possibility that the trailer fields might
+ be silently discarded along the path to the client.
+
+ This requirement prevents an interoperability failure when the
+ message is being received by an HTTP/1.1 (or later) proxy and
+ forwarded to an HTTP/1.0 recipient. It avoids a situation where
+ compliance with the protocol would have necessitated a possibly
+ infinite buffer on the proxy.
+
+ An example process for decoding a Chunked-Body is presented in
+ appendix 19.4.6.
+
+ All HTTP/1.1 applications MUST be able to receive and decode the
+ "chunked" transfer-coding, and MUST ignore chunk-extension extensions
+ they do not understand.
+
+3.7 Media Types
+
+ HTTP uses Internet Media Types [17] in the Content-Type (section
+ 14.17) and Accept (section 14.1) header fields in order to provide
+ open and extensible data typing and type negotiation.
+
+ media-type = type "/" subtype *( ";" parameter )
+ type = token
+ subtype = token
+
+ Parameters MAY follow the type/subtype in the form of attribute/value
+ pairs (as defined in section 3.6).
+
+ The type, subtype, and parameter attribute names are case-
+ insensitive. Parameter values might or might not be case-sensitive,
+ depending on the semantics of the parameter name. Linear white space
+ (LWS) MUST NOT be used between the type and subtype, nor between an
+ attribute and its value. The presence or absence of a parameter might
+ be significant to the processing of a media-type, depending on its
+ definition within the media type registry.
+
+
+
+Fielding, et al. Standards Track [Page 26]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ Note that some older HTTP applications do not recognize media type
+ parameters. When sending data to older HTTP applications,
+ implementations SHOULD only use media type parameters when they are
+ required by that type/subtype definition.
+
+ Media-type values are registered with the Internet Assigned Number
+ Authority (IANA [19]). The media type registration process is
+ outlined in RFC 1590 [17]. Use of non-registered media types is
+ discouraged.
+
+3.7.1 Canonicalization and Text Defaults
+
+ Internet media types are registered with a canonical form. An
+ entity-body transferred via HTTP messages MUST be represented in the
+ appropriate canonical form prior to its transmission except for
+ "text" types, as defined in the next paragraph.
+
+ When in canonical form, media subtypes of the "text" type use CRLF as
+ the text line break. HTTP relaxes this requirement and allows the
+ transport of text media with plain CR or LF alone representing a line
+ break when it is done consistently for an entire entity-body. HTTP
+ applications MUST accept CRLF, bare CR, and bare LF as being
+ representative of a line break in text media received via HTTP. In
+ addition, if the text is represented in a character set that does not
+ use octets 13 and 10 for CR and LF respectively, as is the case for
+ some multi-byte character sets, HTTP allows the use of whatever octet
+ sequences are defined by that character set to represent the
+ equivalent of CR and LF for line breaks. This flexibility regarding
+ line breaks applies only to text media in the entity-body; a bare CR
+ or LF MUST NOT be substituted for CRLF within any of the HTTP control
+ structures (such as header fields and multipart boundaries).
+
+ If an entity-body is encoded with a content-coding, the underlying
+ data MUST be in a form defined above prior to being encoded.
+
+ The "charset" parameter is used with some media types to define the
+ character set (section 3.4) of the data. When no explicit charset
+ parameter is provided by the sender, media subtypes of the "text"
+ type are defined to have a default charset value of "ISO-8859-1" when
+ received via HTTP. Data in character sets other than "ISO-8859-1" or
+ its subsets MUST be labeled with an appropriate charset value. See
+ section 3.4.1 for compatibility problems.
+
+3.7.2 Multipart Types
+
+ MIME provides for a number of "multipart" types -- encapsulations of
+ one or more entities within a single message-body. All multipart
+ types share a common syntax, as defined in section 5.1.1 of RFC 2046
+
+
+
+Fielding, et al. Standards Track [Page 27]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ [40], and MUST include a boundary parameter as part of the media type
+ value. The message body is itself a protocol element and MUST
+ therefore use only CRLF to represent line breaks between body-parts.
+ Unlike in RFC 2046, the epilogue of any multipart message MUST be
+ empty; HTTP applications MUST NOT transmit the epilogue (even if the
+ original multipart contains an epilogue). These restrictions exist in
+ order to preserve the self-delimiting nature of a multipart message-
+ body, wherein the "end" of the message-body is indicated by the
+ ending multipart boundary.
+
+ In general, HTTP treats a multipart message-body no differently than
+ any other media type: strictly as payload. The one exception is the
+ "multipart/byteranges" type (appendix 19.2) when it appears in a 206
+ (Partial Content) response, which will be interpreted by some HTTP
+ caching mechanisms as described in sections 13.5.4 and 14.16. In all
+ other cases, an HTTP user agent SHOULD follow the same or similar
+ behavior as a MIME user agent would upon receipt of a multipart type.
+ The MIME header fields within each body-part of a multipart message-
+ body do not have any significance to HTTP beyond that defined by
+ their MIME semantics.
+
+ In general, an HTTP user agent SHOULD follow the same or similar
+ behavior as a MIME user agent would upon receipt of a multipart type.
+ If an application receives an unrecognized multipart subtype, the
+ application MUST treat it as being equivalent to "multipart/mixed".
+
+ Note: The "multipart/form-data" type has been specifically defined
+ for carrying form data suitable for processing via the POST
+ request method, as described in RFC 1867 [15].
+
+3.8 Product Tokens
+
+ Product tokens are used to allow communicating applications to
+ identify themselves by software name and version. Most fields using
+ product tokens also allow sub-products which form a significant part
+ of the application to be listed, separated by white space. By
+ convention, the products are listed in order of their significance
+ for identifying the application.
+
+ product = token ["/" product-version]
+ product-version = token
+
+ Examples:
+
+ User-Agent: CERN-LineMode/2.15 libwww/2.17b3
+ Server: Apache/0.8.4
+
+
+
+
+
+Fielding, et al. Standards Track [Page 28]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ Product tokens SHOULD be short and to the point. They MUST NOT be
+ used for advertising or other non-essential information. Although any
+ token character MAY appear in a product-version, this token SHOULD
+ only be used for a version identifier (i.e., successive versions of
+ the same product SHOULD only differ in the product-version portion of
+ the product value).
+
+3.9 Quality Values
+
+ HTTP content negotiation (section 12) uses short "floating point"
+ numbers to indicate the relative importance ("weight") of various
+ negotiable parameters. A weight is normalized to a real number in
+ the range 0 through 1, where 0 is the minimum and 1 the maximum
+ value. If a parameter has a quality value of 0, then content with
+ this parameter is `not acceptable' for the client. HTTP/1.1
+ applications MUST NOT generate more than three digits after the
+ decimal point. User configuration of these values SHOULD also be
+ limited in this fashion.
+
+ qvalue = ( "0" [ "." 0*3DIGIT ] )
+ | ( "1" [ "." 0*3("0") ] )
+
+ "Quality values" is a misnomer, since these values merely represent
+ relative degradation in desired quality.
+
+3.10 Language Tags
+
+ A language tag identifies a natural language spoken, written, or
+ otherwise conveyed by human beings for communication of information
+ to other human beings. Computer languages are explicitly excluded.
+ HTTP uses language tags within the Accept-Language and Content-
+ Language fields.
+
+ The syntax and registry of HTTP language tags is the same as that
+ defined by RFC 1766 [1]. In summary, a language tag is composed of 1
+ or more parts: A primary language tag and a possibly empty series of
+ subtags:
+
+ language-tag = primary-tag *( "-" subtag )
+ primary-tag = 1*8ALPHA
+ subtag = 1*8ALPHA
+
+ White space is not allowed within the tag and all tags are case-
+ insensitive. The name space of language tags is administered by the
+ IANA. Example tags include:
+
+ en, en-US, en-cockney, i-cherokee, x-pig-latin
+
+
+
+
+Fielding, et al. Standards Track [Page 29]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ where any two-letter primary-tag is an ISO-639 language abbreviation
+ and any two-letter initial subtag is an ISO-3166 country code. (The
+ last three tags above are not registered tags; all but the last are
+ examples of tags which could be registered in future.)
+
+3.11 Entity Tags
+
+ Entity tags are used for comparing two or more entities from the same
+ requested resource. HTTP/1.1 uses entity tags in the ETag (section
+ 14.19), If-Match (section 14.24), If-None-Match (section 14.26), and
+ If-Range (section 14.27) header fields. The definition of how they
+ are used and compared as cache validators is in section 13.3.3. An
+ entity tag consists of an opaque quoted string, possibly prefixed by
+ a weakness indicator.
+
+ entity-tag = [ weak ] opaque-tag
+ weak = "W/"
+ opaque-tag = quoted-string
+
+ A "strong entity tag" MAY be shared by two entities of a resource
+ only if they are equivalent by octet equality.
+
+ A "weak entity tag," indicated by the "W/" prefix, MAY be shared by
+ two entities of a resource only if the entities are equivalent and
+ could be substituted for each other with no significant change in
+ semantics. A weak entity tag can only be used for weak comparison.
+
+ An entity tag MUST be unique across all versions of all entities
+ associated with a particular resource. A given entity tag value MAY
+ be used for entities obtained by requests on different URIs. The use
+ of the same entity tag value in conjunction with entities obtained by
+ requests on different URIs does not imply the equivalence of those
+ entities.
+
+3.12 Range Units
+
+ HTTP/1.1 allows a client to request that only part (a range of) the
+ response entity be included within the response. HTTP/1.1 uses range
+ units in the Range (section 14.35) and Content-Range (section 14.16)
+ header fields. An entity can be broken down into subranges according
+ to various structural units.
+
+ range-unit = bytes-unit | other-range-unit
+ bytes-unit = "bytes"
+ other-range-unit = token
+
+ The only range unit defined by HTTP/1.1 is "bytes". HTTP/1.1
+ implementations MAY ignore ranges specified using other units.
+
+
+
+Fielding, et al. Standards Track [Page 30]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ HTTP/1.1 has been designed to allow implementations of applications
+ that do not depend on knowledge of ranges.
+
+4 HTTP Message
+
+4.1 Message Types
+
+ HTTP messages consist of requests from client to server and responses
+ from server to client.
+
+ HTTP-message = Request | Response ; HTTP/1.1 messages
+
+ Request (section 5) and Response (section 6) messages use the generic
+ message format of RFC 822 [9] for transferring entities (the payload
+ of the message). Both types of message consist of a start-line, zero
+ or more header fields (also known as "headers"), an empty line (i.e.,
+ a line with nothing preceding the CRLF) indicating the end of the
+ header fields, and possibly a message-body.
+
+ generic-message = start-line
+ *(message-header CRLF)
+ CRLF
+ [ message-body ]
+ start-line = Request-Line | Status-Line
+
+ In the interest of robustness, servers SHOULD ignore any empty
+ line(s) received where a Request-Line is expected. In other words, if
+ the server is reading the protocol stream at the beginning of a
+ message and receives a CRLF first, it should ignore the CRLF.
+
+ Certain buggy HTTP/1.0 client implementations generate extra CRLF's
+ after a POST request. To restate what is explicitly forbidden by the
+ BNF, an HTTP/1.1 client MUST NOT preface or follow a request with an
+ extra CRLF.
+
+4.2 Message Headers
+
+ HTTP header fields, which include general-header (section 4.5),
+ request-header (section 5.3), response-header (section 6.2), and
+ entity-header (section 7.1) fields, follow the same generic format as
+ that given in Section 3.1 of RFC 822 [9]. Each header field consists
+ of a name followed by a colon (":") and the field value. Field names
+ are case-insensitive. The field value MAY be preceded by any amount
+ of LWS, though a single SP is preferred. Header fields can be
+ extended over multiple lines by preceding each extra line with at
+ least one SP or HT. Applications ought to follow "common form", where
+ one is known or indicated, when generating HTTP constructs, since
+ there might exist some implementations that fail to accept anything
+
+
+
+Fielding, et al. Standards Track [Page 31]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ beyond the common forms.
+
+ message-header = field-name ":" [ field-value ]
+ field-name = token
+ field-value = *( field-content | LWS )
+ field-content = <the OCTETs making up the field-value
+ and consisting of either *TEXT or combinations
+ of token, separators, and quoted-string>
+
+ The field-content does not include any leading or trailing LWS:
+ linear white space occurring before the first non-whitespace
+ character of the field-value or after the last non-whitespace
+ character of the field-value. Such leading or trailing LWS MAY be
+ removed without changing the semantics of the field value. Any LWS
+ that occurs between field-content MAY be replaced with a single SP
+ before interpreting the field value or forwarding the message
+ downstream.
+
+ The order in which header fields with differing field names are
+ received is not significant. However, it is "good practice" to send
+ general-header fields first, followed by request-header or response-
+ header fields, and ending with the entity-header fields.
+
+ Multiple message-header fields with the same field-name MAY be
+ present in a message if and only if the entire field-value for that
+ header field is defined as a comma-separated list [i.e., #(values)].
+ It MUST be possible to combine the multiple header fields into one
+ "field-name: field-value" pair, without changing the semantics of the
+ message, by appending each subsequent field-value to the first, each
+ separated by a comma. The order in which header fields with the same
+ field-name are received is therefore significant to the
+ interpretation of the combined field value, and thus a proxy MUST NOT
+ change the order of these field values when a message is forwarded.
+
+4.3 Message Body
+
+ The message-body (if any) of an HTTP message is used to carry the
+ entity-body associated with the request or response. The message-body
+ differs from the entity-body only when a transfer-coding has been
+ applied, as indicated by the Transfer-Encoding header field (section
+ 14.41).
+
+ message-body = entity-body
+ | <entity-body encoded as per Transfer-Encoding>
+
+ Transfer-Encoding MUST be used to indicate any transfer-codings
+ applied by an application to ensure safe and proper transfer of the
+ message. Transfer-Encoding is a property of the message, not of the
+
+
+
+Fielding, et al. Standards Track [Page 32]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ entity, and thus MAY be added or removed by any application along the
+ request/response chain. (However, section 3.6 places restrictions on
+ when certain transfer-codings may be used.)
+
+ The rules for when a message-body is allowed in a message differ for
+ requests and responses.
+
+ The presence of a message-body in a request is signaled by the
+ inclusion of a Content-Length or Transfer-Encoding header field in
+ the request's message-headers. A message-body MUST NOT be included in
+ a request if the specification of the request method (section 5.1.1)
+ does not allow sending an entity-body in requests. A server SHOULD
+ read and forward a message-body on any request; if the request method
+ does not include defined semantics for an entity-body, then the
+ message-body SHOULD be ignored when handling the request.
+
+ For response messages, whether or not a message-body is included with
+ a message is dependent on both the request method and the response
+ status code (section 6.1.1). All responses to the HEAD request method
+ MUST NOT include a message-body, even though the presence of entity-
+ header fields might lead one to believe they do. All 1xx
+ (informational), 204 (no content), and 304 (not modified) responses
+ MUST NOT include a message-body. All other responses do include a
+ message-body, although it MAY be of zero length.
+
+4.4 Message Length
+
+ The transfer-length of a message is the length of the message-body as
+ it appears in the message; that is, after any transfer-codings have
+ been applied. When a message-body is included with a message, the
+ transfer-length of that body is determined by one of the following
+ (in order of precedence):
+
+ 1.Any response message which "MUST NOT" include a message-body (such
+ as the 1xx, 204, and 304 responses and any response to a HEAD
+ request) is always terminated by the first empty line after the
+ header fields, regardless of the entity-header fields present in
+ the message.
+
+ 2.If a Transfer-Encoding header field (section 14.41) is present and
+ has any value other than "identity", then the transfer-length is
+ defined by use of the "chunked" transfer-coding (section 3.6),
+ unless the message is terminated by closing the connection.
+
+ 3.If a Content-Length header field (section 14.13) is present, its
+ decimal value in OCTETs represents both the entity-length and the
+ transfer-length. The Content-Length header field MUST NOT be sent
+ if these two lengths are different (i.e., if a Transfer-Encoding
+
+
+
+Fielding, et al. Standards Track [Page 33]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ header field is present). If a message is received with both a
+ Transfer-Encoding header field and a Content-Length header field,
+ the latter MUST be ignored.
+
+ 4.If the message uses the media type "multipart/byteranges", and the
+ ransfer-length is not otherwise specified, then this self-
+ elimiting media type defines the transfer-length. This media type
+ UST NOT be used unless the sender knows that the recipient can arse
+ it; the presence in a request of a Range header with ultiple byte-
+ range specifiers from a 1.1 client implies that the lient can parse
+ multipart/byteranges responses.
+
+ A range header might be forwarded by a 1.0 proxy that does not
+ understand multipart/byteranges; in this case the server MUST
+ delimit the message using methods defined in items 1,3 or 5 of
+ this section.
+
+ 5.By the server closing the connection. (Closing the connection
+ cannot be used to indicate the end of a request body, since that
+ would leave no possibility for the server to send back a response.)
+
+ For compatibility with HTTP/1.0 applications, HTTP/1.1 requests
+ containing a message-body MUST include a valid Content-Length header
+ field unless the server is known to be HTTP/1.1 compliant. If a
+ request contains a message-body and a Content-Length is not given,
+ the server SHOULD respond with 400 (bad request) if it cannot
+ determine the length of the message, or with 411 (length required) if
+ it wishes to insist on receiving a valid Content-Length.
+
+ All HTTP/1.1 applications that receive entities MUST accept the
+ "chunked" transfer-coding (section 3.6), thus allowing this mechanism
+ to be used for messages when the message length cannot be determined
+ in advance.
+
+ Messages MUST NOT include both a Content-Length header field and a
+ non-identity transfer-coding. If the message does include a non-
+ identity transfer-coding, the Content-Length MUST be ignored.
+
+ When a Content-Length is given in a message where a message-body is
+ allowed, its field value MUST exactly match the number of OCTETs in
+ the message-body. HTTP/1.1 user agents MUST notify the user when an
+ invalid length is received and detected.
+
+4.5 General Header Fields
+
+ There are a few header fields which have general applicability for
+ both request and response messages, but which do not apply to the
+ entity being transferred. These header fields apply only to the
+
+
+
+Fielding, et al. Standards Track [Page 34]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ message being transmitted.
+
+ general-header = Cache-Control ; Section 14.9
+ | Connection ; Section 14.10
+ | Date ; Section 14.18
+ | Pragma ; Section 14.32
+ | Trailer ; Section 14.40
+ | Transfer-Encoding ; Section 14.41
+ | Upgrade ; Section 14.42
+ | Via ; Section 14.45
+ | Warning ; Section 14.46
+
+ General-header field names can be extended reliably only in
+ combination with a change in the protocol version. However, new or
+ experimental header fields may be given the semantics of general
+ header fields if all parties in the communication recognize them to
+ be general-header fields. Unrecognized header fields are treated as
+ entity-header fields.
+
+5 Request
+
+ A request message from a client to a server includes, within the
+ first line of that message, the method to be applied to the resource,
+ the identifier of the resource, and the protocol version in use.
+
+ Request = Request-Line ; Section 5.1
+ *(( general-header ; Section 4.5
+ | request-header ; Section 5.3
+ | entity-header ) CRLF) ; Section 7.1
+ CRLF
+ [ message-body ] ; Section 4.3
+
+5.1 Request-Line
+
+ The Request-Line begins with a method token, followed by the
+ Request-URI and the protocol version, and ending with CRLF. The
+ elements are separated by SP characters. No CR or LF is allowed
+ except in the final CRLF sequence.
+
+ Request-Line = Method SP Request-URI SP HTTP-Version CRLF
+
+
+
+
+
+
+
+
+
+
+
+Fielding, et al. Standards Track [Page 35]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+5.1.1 Method
+
+ The Method token indicates the method to be performed on the
+ resource identified by the Request-URI. The method is case-sensitive.
+
+ Method = "OPTIONS" ; Section 9.2
+ | "GET" ; Section 9.3
+ | "HEAD" ; Section 9.4
+ | "POST" ; Section 9.5
+ | "PUT" ; Section 9.6
+ | "DELETE" ; Section 9.7
+ | "TRACE" ; Section 9.8
+ | "CONNECT" ; Section 9.9
+ | extension-method
+ extension-method = token
+
+ The list of methods allowed by a resource can be specified in an
+ Allow header field (section 14.7). The return code of the response
+ always notifies the client whether a method is currently allowed on a
+ resource, since the set of allowed methods can change dynamically. An
+ origin server SHOULD return the status code 405 (Method Not Allowed)
+ if the method is known by the origin server but not allowed for the
+ requested resource, and 501 (Not Implemented) if the method is
+ unrecognized or not implemented by the origin server. The methods GET
+ and HEAD MUST be supported by all general-purpose servers. All other
+ methods are OPTIONAL; however, if the above methods are implemented,
+ they MUST be implemented with the same semantics as those specified
+ in section 9.
+
+5.1.2 Request-URI
+
+ The Request-URI is a Uniform Resource Identifier (section 3.2) and
+ identifies the resource upon which to apply the request.
+
+ Request-URI = "*" | absoluteURI | abs_path | authority
+
+ The four options for Request-URI are dependent on the nature of the
+ request. The asterisk "*" means that the request does not apply to a
+ particular resource, but to the server itself, and is only allowed
+ when the method used does not necessarily apply to a resource. One
+ example would be
+
+ OPTIONS * HTTP/1.1
+
+ The absoluteURI form is REQUIRED when the request is being made to a
+ proxy. The proxy is requested to forward the request or service it
+ from a valid cache, and return the response. Note that the proxy MAY
+ forward the request on to another proxy or directly to the server
+
+
+
+Fielding, et al. Standards Track [Page 36]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ specified by the absoluteURI. In order to avoid request loops, a
+ proxy MUST be able to recognize all of its server names, including
+ any aliases, local variations, and the numeric IP address. An example
+ Request-Line would be:
+
+ GET http://www.w3.org/pub/WWW/TheProject.html HTTP/1.1
+
+ To allow for transition to absoluteURIs in all requests in future
+ versions of HTTP, all HTTP/1.1 servers MUST accept the absoluteURI
+ form in requests, even though HTTP/1.1 clients will only generate
+ them in requests to proxies.
+
+ The authority form is only used by the CONNECT method (section 9.9).
+
+ The most common form of Request-URI is that used to identify a
+ resource on an origin server or gateway. In this case the absolute
+ path of the URI MUST be transmitted (see section 3.2.1, abs_path) as
+ the Request-URI, and the network location of the URI (authority) MUST
+ be transmitted in a Host header field. For example, a client wishing
+ to retrieve the resource above directly from the origin server would
+ create a TCP connection to port 80 of the host "www.w3.org" and send
+ the lines:
+
+ GET /pub/WWW/TheProject.html HTTP/1.1
+ Host: www.w3.org
+
+ followed by the remainder of the Request. Note that the absolute path
+ cannot be empty; if none is present in the original URI, it MUST be
+ given as "/" (the server root).
+
+ The Request-URI is transmitted in the format specified in section
+ 3.2.1. If the Request-URI is encoded using the "% HEX HEX" encoding
+ [42], the origin server MUST decode the Request-URI in order to
+ properly interpret the request. Servers SHOULD respond to invalid
+ Request-URIs with an appropriate status code.
+
+ A transparent proxy MUST NOT rewrite the "abs_path" part of the
+ received Request-URI when forwarding it to the next inbound server,
+ except as noted above to replace a null abs_path with "/".
+
+ Note: The "no rewrite" rule prevents the proxy from changing the
+ meaning of the request when the origin server is improperly using
+ a non-reserved URI character for a reserved purpose. Implementors
+ should be aware that some pre-HTTP/1.1 proxies have been known to
+ rewrite the Request-URI.
+
+
+
+
+
+
+Fielding, et al. Standards Track [Page 37]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+5.2 The Resource Identified by a Request
+
+ The exact resource identified by an Internet request is determined by
+ examining both the Request-URI and the Host header field.
+
+ An origin server that does not allow resources to differ by the
+ requested host MAY ignore the Host header field value when
+ determining the resource identified by an HTTP/1.1 request. (But see
+ section 19.6.1.1 for other requirements on Host support in HTTP/1.1.)
+
+ An origin server that does differentiate resources based on the host
+ requested (sometimes referred to as virtual hosts or vanity host
+ names) MUST use the following rules for determining the requested
+ resource on an HTTP/1.1 request:
+
+ 1. If Request-URI is an absoluteURI, the host is part of the
+ Request-URI. Any Host header field value in the request MUST be
+ ignored.
+
+ 2. If the Request-URI is not an absoluteURI, and the request includes
+ a Host header field, the host is determined by the Host header
+ field value.
+
+ 3. If the host as determined by rule 1 or 2 is not a valid host on
+ the server, the response MUST be a 400 (Bad Request) error message.
+
+ Recipients of an HTTP/1.0 request that lacks a Host header field MAY
+ attempt to use heuristics (e.g., examination of the URI path for
+ something unique to a particular host) in order to determine what
+ exact resource is being requested.
+
+5.3 Request Header Fields
+
+ The request-header fields allow the client to pass additional
+ information about the request, and about the client itself, to the
+ server. These fields act as request modifiers, with semantics
+ equivalent to the parameters on a programming language method
+ invocation.
+
+ request-header = Accept ; Section 14.1
+ | Accept-Charset ; Section 14.2
+ | Accept-Encoding ; Section 14.3
+ | Accept-Language ; Section 14.4
+ | Authorization ; Section 14.8
+ | Expect ; Section 14.20
+ | From ; Section 14.22
+ | Host ; Section 14.23
+ | If-Match ; Section 14.24
+
+
+
+Fielding, et al. Standards Track [Page 38]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ | If-Modified-Since ; Section 14.25
+ | If-None-Match ; Section 14.26
+ | If-Range ; Section 14.27
+ | If-Unmodified-Since ; Section 14.28
+ | Max-Forwards ; Section 14.31
+ | Proxy-Authorization ; Section 14.34
+ | Range ; Section 14.35
+ | Referer ; Section 14.36
+ | TE ; Section 14.39
+ | User-Agent ; Section 14.43
+
+ Request-header field names can be extended reliably only in
+ combination with a change in the protocol version. However, new or
+ experimental header fields MAY be given the semantics of request-
+ header fields if all parties in the communication recognize them to
+ be request-header fields. Unrecognized header fields are treated as
+ entity-header fields.
+
+6 Response
+
+ After receiving and interpreting a request message, a server responds
+ with an HTTP response message.
+
+ Response = Status-Line ; Section 6.1
+ *(( general-header ; Section 4.5
+ | response-header ; Section 6.2
+ | entity-header ) CRLF) ; Section 7.1
+ CRLF
+ [ message-body ] ; Section 7.2
+
+6.1 Status-Line
+
+ The first line of a Response message is the Status-Line, consisting
+ of the protocol version followed by a numeric status code and its
+ associated textual phrase, with each element separated by SP
+ characters. No CR or LF is allowed except in the final CRLF sequence.
+
+ Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF
+
+6.1.1 Status Code and Reason Phrase
+
+ The Status-Code element is a 3-digit integer result code of the
+ attempt to understand and satisfy the request. These codes are fully
+ defined in section 10. The Reason-Phrase is intended to give a short
+ textual description of the Status-Code. The Status-Code is intended
+ for use by automata and the Reason-Phrase is intended for the human
+ user. The client is not required to examine or display the Reason-
+ Phrase.
+
+
+
+Fielding, et al. Standards Track [Page 39]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ The first digit of the Status-Code defines the class of response. The
+ last two digits do not have any categorization role. There are 5
+ values for the first digit:
+
+ - 1xx: Informational - Request received, continuing process
+
+ - 2xx: Success - The action was successfully received,
+ understood, and accepted
+
+ - 3xx: Redirection - Further action must be taken in order to
+ complete the request
+
+ - 4xx: Client Error - The request contains bad syntax or cannot
+ be fulfilled
+
+ - 5xx: Server Error - The server failed to fulfill an apparently
+ valid request
+
+ The individual values of the numeric status codes defined for
+ HTTP/1.1, and an example set of corresponding Reason-Phrase's, are
+ presented below. The reason phrases listed here are only
+ recommendations -- they MAY be replaced by local equivalents without
+ affecting the protocol.
+
+ Status-Code =
+ "100" ; Section 10.1.1: Continue
+ | "101" ; Section 10.1.2: Switching Protocols
+ | "200" ; Section 10.2.1: OK
+ | "201" ; Section 10.2.2: Created
+ | "202" ; Section 10.2.3: Accepted
+ | "203" ; Section 10.2.4: Non-Authoritative Information
+ | "204" ; Section 10.2.5: No Content
+ | "205" ; Section 10.2.6: Reset Content
+ | "206" ; Section 10.2.7: Partial Content
+ | "300" ; Section 10.3.1: Multiple Choices
+ | "301" ; Section 10.3.2: Moved Permanently
+ | "302" ; Section 10.3.3: Found
+ | "303" ; Section 10.3.4: See Other
+ | "304" ; Section 10.3.5: Not Modified
+ | "305" ; Section 10.3.6: Use Proxy
+ | "307" ; Section 10.3.8: Temporary Redirect
+ | "400" ; Section 10.4.1: Bad Request
+ | "401" ; Section 10.4.2: Unauthorized
+ | "402" ; Section 10.4.3: Payment Required
+ | "403" ; Section 10.4.4: Forbidden
+ | "404" ; Section 10.4.5: Not Found
+ | "405" ; Section 10.4.6: Method Not Allowed
+ | "406" ; Section 10.4.7: Not Acceptable
+
+
+
+Fielding, et al. Standards Track [Page 40]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ | "407" ; Section 10.4.8: Proxy Authentication Required
+ | "408" ; Section 10.4.9: Request Time-out
+ | "409" ; Section 10.4.10: Conflict
+ | "410" ; Section 10.4.11: Gone
+ | "411" ; Section 10.4.12: Length Required
+ | "412" ; Section 10.4.13: Precondition Failed
+ | "413" ; Section 10.4.14: Request Entity Too Large
+ | "414" ; Section 10.4.15: Request-URI Too Large
+ | "415" ; Section 10.4.16: Unsupported Media Type
+ | "416" ; Section 10.4.17: Requested range not satisfiable
+ | "417" ; Section 10.4.18: Expectation Failed
+ | "500" ; Section 10.5.1: Internal Server Error
+ | "501" ; Section 10.5.2: Not Implemented
+ | "502" ; Section 10.5.3: Bad Gateway
+ | "503" ; Section 10.5.4: Service Unavailable
+ | "504" ; Section 10.5.5: Gateway Time-out
+ | "505" ; Section 10.5.6: HTTP Version not supported
+ | extension-code
+
+ extension-code = 3DIGIT
+ Reason-Phrase = *<TEXT, excluding CR, LF>
+
+ HTTP status codes are extensible. HTTP applications are not required
+ to understand the meaning of all registered status codes, though such
+ understanding is obviously desirable. However, applications MUST
+ understand the class of any status code, as indicated by the first
+ digit, and treat any unrecognized response as being equivalent to the
+ x00 status code of that class, with the exception that an
+ unrecognized response MUST NOT be cached. For example, if an
+ unrecognized status code of 431 is received by the client, it can
+ safely assume that there was something wrong with its request and
+ treat the response as if it had received a 400 status code. In such
+ cases, user agents SHOULD present to the user the entity returned
+ with the response, since that entity is likely to include human-
+ readable information which will explain the unusual status.
+
+6.2 Response Header Fields
+
+ The response-header fields allow the server to pass additional
+ information about the response which cannot be placed in the Status-
+ Line. These header fields give information about the server and about
+ further access to the resource identified by the Request-URI.
+
+ response-header = Accept-Ranges ; Section 14.5
+ | Age ; Section 14.6
+ | ETag ; Section 14.19
+ | Location ; Section 14.30
+ | Proxy-Authenticate ; Section 14.33
+
+
+
+Fielding, et al. Standards Track [Page 41]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ | Retry-After ; Section 14.37
+ | Server ; Section 14.38
+ | Vary ; Section 14.44
+ | WWW-Authenticate ; Section 14.47
+
+ Response-header field names can be extended reliably only in
+ combination with a change in the protocol version. However, new or
+ experimental header fields MAY be given the semantics of response-
+ header fields if all parties in the communication recognize them to
+ be response-header fields. Unrecognized header fields are treated as
+ entity-header fields.
+
+7 Entity
+
+ Request and Response messages MAY transfer an entity if not otherwise
+ restricted by the request method or response status code. An entity
+ consists of entity-header fields and an entity-body, although some
+ responses will only include the entity-headers.
+
+ In this section, both sender and recipient refer to either the client
+ or the server, depending on who sends and who receives the entity.
+
+7.1 Entity Header Fields
+
+ Entity-header fields define metainformation about the entity-body or,
+ if no body is present, about the resource identified by the request.
+ Some of this metainformation is OPTIONAL; some might be REQUIRED by
+ portions of this specification.
+
+ entity-header = Allow ; Section 14.7
+ | Content-Encoding ; Section 14.11
+ | Content-Language ; Section 14.12
+ | Content-Length ; Section 14.13
+ | Content-Location ; Section 14.14
+ | Content-MD5 ; Section 14.15
+ | Content-Range ; Section 14.16
+ | Content-Type ; Section 14.17
+ | Expires ; Section 14.21
+ | Last-Modified ; Section 14.29
+ | extension-header
+
+ extension-header = message-header
+
+ The extension-header mechanism allows additional entity-header fields
+ to be defined without changing the protocol, but these fields cannot
+ be assumed to be recognizable by the recipient. Unrecognized header
+ fields SHOULD be ignored by the recipient and MUST be forwarded by
+ transparent proxies.
+
+
+
+Fielding, et al. Standards Track [Page 42]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+7.2 Entity Body
+
+ The entity-body (if any) sent with an HTTP request or response is in
+ a format and encoding defined by the entity-header fields.
+
+ entity-body = *OCTET
+
+ An entity-body is only present in a message when a message-body is
+ present, as described in section 4.3. The entity-body is obtained
+ from the message-body by decoding any Transfer-Encoding that might
+ have been applied to ensure safe and proper transfer of the message.
+
+7.2.1 Type
+
+ When an entity-body is included with a message, the data type of that
+ body is determined via the header fields Content-Type and Content-
+ Encoding. These define a two-layer, ordered encoding model:
+
+ entity-body := Content-Encoding( Content-Type( data ) )
+
+ Content-Type specifies the media type of the underlying data.
+ Content-Encoding may be used to indicate any additional content
+ codings applied to the data, usually for the purpose of data
+ compression, that are a property of the requested resource. There is
+ no default encoding.
+
+ Any HTTP/1.1 message containing an entity-body SHOULD include a
+ Content-Type header field defining the media type of that body. If
+ and only if the media type is not given by a Content-Type field, the
+ recipient MAY attempt to guess the media type via inspection of its
+ content and/or the name extension(s) of the URI used to identify the
+ resource. If the media type remains unknown, the recipient SHOULD
+ treat it as type "application/octet-stream".
+
+7.2.2 Entity Length
+
+ The entity-length of a message is the length of the message-body
+ before any transfer-codings have been applied. Section 4.4 defines
+ how the transfer-length of a message-body is determined.
+
+
+
+
+
+
+
+
+
+
+
+
+Fielding, et al. Standards Track [Page 43]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+8 Connections
+
+8.1 Persistent Connections
+
+8.1.1 Purpose
+
+ Prior to persistent connections, a separate TCP connection was
+ established to fetch each URL, increasing the load on HTTP servers
+ and causing congestion on the Internet. The use of inline images and
+ other associated data often require a client to make multiple
+ requests of the same server in a short amount of time. Analysis of
+ these performance problems and results from a prototype
+ implementation are available [26] [30]. Implementation experience and
+ measurements of actual HTTP/1.1 (RFC 2068) implementations show good
+ results [39]. Alternatives have also been explored, for example,
+ T/TCP [27].
+
+ Persistent HTTP connections have a number of advantages:
+
+ - By opening and closing fewer TCP connections, CPU time is saved
+ in routers and hosts (clients, servers, proxies, gateways,
+ tunnels, or caches), and memory used for TCP protocol control
+ blocks can be saved in hosts.
+
+ - HTTP requests and responses can be pipelined on a connection.
+ Pipelining allows a client to make multiple requests without
+ waiting for each response, allowing a single TCP connection to
+ be used much more efficiently, with much lower elapsed time.
+
+ - Network congestion is reduced by reducing the number of packets
+ caused by TCP opens, and by allowing TCP sufficient time to
+ determine the congestion state of the network.
+
+ - Latency on subsequent requests is reduced since there is no time
+ spent in TCP's connection opening handshake.
+
+ - HTTP can evolve more gracefully, since errors can be reported
+ without the penalty of closing the TCP connection. Clients using
+ future versions of HTTP might optimistically try a new feature,
+ but if communicating with an older server, retry with old
+ semantics after an error is reported.
+
+ HTTP implementations SHOULD implement persistent connections.
+
+
+
+
+
+
+
+
+Fielding, et al. Standards Track [Page 44]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+8.1.2 Overall Operation
+
+ A significant difference between HTTP/1.1 and earlier versions of
+ HTTP is that persistent connections are the default behavior of any
+ HTTP connection. That is, unless otherwise indicated, the client
+ SHOULD assume that the server will maintain a persistent connection,
+ even after error responses from the server.
+
+ Persistent connections provide a mechanism by which a client and a
+ server can signal the close of a TCP connection. This signaling takes
+ place using the Connection header field (section 14.10). Once a close
+ has been signaled, the client MUST NOT send any more requests on that
+ connection.
+
+8.1.2.1 Negotiation
+
+ An HTTP/1.1 server MAY assume that a HTTP/1.1 client intends to
+ maintain a persistent connection unless a Connection header including
+ the connection-token "close" was sent in the request. If the server
+ chooses to close the connection immediately after sending the
+ response, it SHOULD send a Connection header including the
+ connection-token close.
+
+ An HTTP/1.1 client MAY expect a connection to remain open, but would
+ decide to keep it open based on whether the response from a server
+ contains a Connection header with the connection-token close. In case
+ the client does not want to maintain a connection for more than that
+ request, it SHOULD send a Connection header including the
+ connection-token close.
+
+ If either the client or the server sends the close token in the
+ Connection header, that request becomes the last one for the
+ connection.
+
+ Clients and servers SHOULD NOT assume that a persistent connection is
+ maintained for HTTP versions less than 1.1 unless it is explicitly
+ signaled. See section 19.6.2 for more information on backward
+ compatibility with HTTP/1.0 clients.
+
+ In order to remain persistent, all messages on the connection MUST
+ have a self-defined message length (i.e., one not defined by closure
+ of the connection), as described in section 4.4.
+
+
+
+
+
+
+
+
+
+Fielding, et al. Standards Track [Page 45]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+8.1.2.2 Pipelining
+
+ A client that supports persistent connections MAY "pipeline" its
+ requests (i.e., send multiple requests without waiting for each
+ response). A server MUST send its responses to those requests in the
+ same order that the requests were received.
+
+ Clients which assume persistent connections and pipeline immediately
+ after connection establishment SHOULD be prepared to retry their
+ connection if the first pipelined attempt fails. If a client does
+ such a retry, it MUST NOT pipeline before it knows the connection is
+ persistent. Clients MUST also be prepared to resend their requests if
+ the server closes the connection before sending all of the
+ corresponding responses.
+
+ Clients SHOULD NOT pipeline requests using non-idempotent methods or
+ non-idempotent sequences of methods (see section 9.1.2). Otherwise, a
+ premature termination of the transport connection could lead to
+ indeterminate results. A client wishing to send a non-idempotent
+ request SHOULD wait to send that request until it has received the
+ response status for the previous request.
+
+8.1.3 Proxy Servers
+
+ It is especially important that proxies correctly implement the
+ properties of the Connection header field as specified in section
+ 14.10.
+
+ The proxy server MUST signal persistent connections separately with
+ its clients and the origin servers (or other proxy servers) that it
+ connects to. Each persistent connection applies to only one transport
+ link.
+
+ A proxy server MUST NOT establish a HTTP/1.1 persistent connection
+ with an HTTP/1.0 client (but see RFC 2068 [33] for information and
+ discussion of the problems with the Keep-Alive header implemented by
+ many HTTP/1.0 clients).
+
+8.1.4 Practical Considerations
+
+ Servers will usually have some time-out value beyond which they will
+ no longer maintain an inactive connection. Proxy servers might make
+ this a higher value since it is likely that the client will be making
+ more connections through the same server. The use of persistent
+ connections places no requirements on the length (or existence) of
+ this time-out for either the client or the server.
+
+
+
+
+
+Fielding, et al. Standards Track [Page 46]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ When a client or server wishes to time-out it SHOULD issue a graceful
+ close on the transport connection. Clients and servers SHOULD both
+ constantly watch for the other side of the transport close, and
+ respond to it as appropriate. If a client or server does not detect
+ the other side's close promptly it could cause unnecessary resource
+ drain on the network.
+
+ A client, server, or proxy MAY close the transport connection at any
+ time. For example, a client might have started to send a new request
+ at the same time that the server has decided to close the "idle"
+ connection. From the server's point of view, the connection is being
+ closed while it was idle, but from the client's point of view, a
+ request is in progress.
+
+ This means that clients, servers, and proxies MUST be able to recover
+ from asynchronous close events. Client software SHOULD reopen the
+ transport connection and retransmit the aborted sequence of requests
+ without user interaction so long as the request sequence is
+ idempotent (see section 9.1.2). Non-idempotent methods or sequences
+ MUST NOT be automatically retried, although user agents MAY offer a
+ human operator the choice of retrying the request(s). Confirmation by
+ user-agent software with semantic understanding of the application
+ MAY substitute for user confirmation. The automatic retry SHOULD NOT
+ be repeated if the second sequence of requests fails.
+
+ Servers SHOULD always respond to at least one request per connection,
+ if at all possible. Servers SHOULD NOT close a connection in the
+ middle of transmitting a response, unless a network or client failure
+ is suspected.
+
+ Clients that use persistent connections SHOULD limit the number of
+ simultaneous connections that they maintain to a given server. A
+ single-user client SHOULD NOT maintain more than 2 connections with
+ any server or proxy. A proxy SHOULD use up to 2*N connections to
+ another server or proxy, where N is the number of simultaneously
+ active users. These guidelines are intended to improve HTTP response
+ times and avoid congestion.
+
+8.2 Message Transmission Requirements
+
+8.2.1 Persistent Connections and Flow Control
+
+ HTTP/1.1 servers SHOULD maintain persistent connections and use TCP's
+ flow control mechanisms to resolve temporary overloads, rather than
+ terminating connections with the expectation that clients will retry.
+ The latter technique can exacerbate network congestion.
+
+
+
+
+
+Fielding, et al. Standards Track [Page 47]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+8.2.2 Monitoring Connections for Error Status Messages
+
+ An HTTP/1.1 (or later) client sending a message-body SHOULD monitor
+ the network connection for an error status while it is transmitting
+ the request. If the client sees an error status, it SHOULD
+ immediately cease transmitting the body. If the body is being sent
+ using a "chunked" encoding (section 3.6), a zero length chunk and
+ empty trailer MAY be used to prematurely mark the end of the message.
+ If the body was preceded by a Content-Length header, the client MUST
+ close the connection.
+
+8.2.3 Use of the 100 (Continue) Status
+
+ The purpose of the 100 (Continue) status (see section 10.1.1) is to
+ allow a client that is sending a request message with a request body
+ to determine if the origin server is willing to accept the request
+ (based on the request headers) before the client sends the request
+ body. In some cases, it might either be inappropriate or highly
+ inefficient for the client to send the body if the server will reject
+ the message without looking at the body.
+
+ Requirements for HTTP/1.1 clients:
+
+ - If a client will wait for a 100 (Continue) response before
+ sending the request body, it MUST send an Expect request-header
+ field (section 14.20) with the "100-continue" expectation.
+
+ - A client MUST NOT send an Expect request-header field (section
+ 14.20) with the "100-continue" expectation if it does not intend
+ to send a request body.
+
+ Because of the presence of older implementations, the protocol allows
+ ambiguous situations in which a client may send "Expect: 100-
+ continue" without receiving either a 417 (Expectation Failed) status
+ or a 100 (Continue) status. Therefore, when a client sends this
+ header field to an origin server (possibly via a proxy) from which it
+ has never seen a 100 (Continue) status, the client SHOULD NOT wait
+ for an indefinite period before sending the request body.
+
+ Requirements for HTTP/1.1 origin servers:
+
+ - Upon receiving a request which includes an Expect request-header
+ field with the "100-continue" expectation, an origin server MUST
+ either respond with 100 (Continue) status and continue to read
+ from the input stream, or respond with a final status code. The
+ origin server MUST NOT wait for the request body before sending
+ the 100 (Continue) response. If it responds with a final status
+ code, it MAY close the transport connection or it MAY continue
+
+
+
+Fielding, et al. Standards Track [Page 48]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ to read and discard the rest of the request. It MUST NOT
+ perform the requested method if it returns a final status code.
+
+ - An origin server SHOULD NOT send a 100 (Continue) response if
+ the request message does not include an Expect request-header
+ field with the "100-continue" expectation, and MUST NOT send a
+ 100 (Continue) response if such a request comes from an HTTP/1.0
+ (or earlier) client. There is an exception to this rule: for
+ compatibility with RFC 2068, a server MAY send a 100 (Continue)
+ status in response to an HTTP/1.1 PUT or POST request that does
+ not include an Expect request-header field with the "100-
+ continue" expectation. This exception, the purpose of which is
+ to minimize any client processing delays associated with an
+ undeclared wait for 100 (Continue) status, applies only to
+ HTTP/1.1 requests, and not to requests with any other HTTP-
+ version value.
+
+ - An origin server MAY omit a 100 (Continue) response if it has
+ already received some or all of the request body for the
+ corresponding request.
+
+ - An origin server that sends a 100 (Continue) response MUST
+ ultimately send a final status code, once the request body is
+ received and processed, unless it terminates the transport
+ connection prematurely.
+
+ - If an origin server receives a request that does not include an
+ Expect request-header field with the "100-continue" expectation,
+ the request includes a request body, and the server responds
+ with a final status code before reading the entire request body
+ from the transport connection, then the server SHOULD NOT close
+ the transport connection until it has read the entire request,
+ or until the client closes the connection. Otherwise, the client
+ might not reliably receive the response message. However, this
+ requirement is not be construed as preventing a server from
+ defending itself against denial-of-service attacks, or from
+ badly broken client implementations.
+
+ Requirements for HTTP/1.1 proxies:
+
+ - If a proxy receives a request that includes an Expect request-
+ header field with the "100-continue" expectation, and the proxy
+ either knows that the next-hop server complies with HTTP/1.1 or
+ higher, or does not know the HTTP version of the next-hop
+ server, it MUST forward the request, including the Expect header
+ field.
+
+
+
+
+
+Fielding, et al. Standards Track [Page 49]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ - If the proxy knows that the version of the next-hop server is
+ HTTP/1.0 or lower, it MUST NOT forward the request, and it MUST
+ respond with a 417 (Expectation Failed) status.
+
+ - Proxies SHOULD maintain a cache recording the HTTP version
+ numbers received from recently-referenced next-hop servers.
+
+ - A proxy MUST NOT forward a 100 (Continue) response if the
+ request message was received from an HTTP/1.0 (or earlier)
+ client and did not include an Expect request-header field with
+ the "100-continue" expectation. This requirement overrides the
+ general rule for forwarding of 1xx responses (see section 10.1).
+
+8.2.4 Client Behavior if Server Prematurely Closes Connection
+
+ If an HTTP/1.1 client sends a request which includes a request body,
+ but which does not include an Expect request-header field with the
+ "100-continue" expectation, and if the client is not directly
+ connected to an HTTP/1.1 origin server, and if the client sees the
+ connection close before receiving any status from the server, the
+ client SHOULD retry the request. If the client does retry this
+ request, it MAY use the following "binary exponential backoff"
+ algorithm to be assured of obtaining a reliable response:
+
+ 1. Initiate a new connection to the server
+
+ 2. Transmit the request-headers
+
+ 3. Initialize a variable R to the estimated round-trip time to the
+ server (e.g., based on the time it took to establish the
+ connection), or to a constant value of 5 seconds if the round-
+ trip time is not available.
+
+ 4. Compute T = R * (2**N), where N is the number of previous
+ retries of this request.
+
+ 5. Wait either for an error response from the server, or for T
+ seconds (whichever comes first)
+
+ 6. If no error response is received, after T seconds transmit the
+ body of the request.
+
+ 7. If client sees that the connection is closed prematurely,
+ repeat from step 1 until the request is accepted, an error
+ response is received, or the user becomes impatient and
+ terminates the retry process.
+
+
+
+
+
+Fielding, et al. Standards Track [Page 50]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ If at any point an error status is received, the client
+
+ - SHOULD NOT continue and
+
+ - SHOULD close the connection if it has not completed sending the
+ request message.
+
+9 Method Definitions
+
+ The set of common methods for HTTP/1.1 is defined below. Although
+ this set can be expanded, additional methods cannot be assumed to
+ share the same semantics for separately extended clients and servers.
+
+ The Host request-header field (section 14.23) MUST accompany all
+ HTTP/1.1 requests.
+
+9.1 Safe and Idempotent Methods
+
+9.1.1 Safe Methods
+
+ Implementors should be aware that the software represents the user in
+ their interactions over the Internet, and should be careful to allow
+ the user to be aware of any actions they might take which may have an
+ unexpected significance to themselves or others.
+
+ In particular, the convention has been established that the GET and
+ HEAD methods SHOULD NOT have the significance of taking an action
+ other than retrieval. These methods ought to be considered "safe".
+ This allows user agents to represent other methods, such as POST, PUT
+ and DELETE, in a special way, so that the user is made aware of the
+ fact that a possibly unsafe action is being requested.
+
+ Naturally, it is not possible to ensure that the server does not
+ generate side-effects as a result of performing a GET request; in
+ fact, some dynamic resources consider that a feature. The important
+ distinction here is that the user did not request the side-effects,
+ so therefore cannot be held accountable for them.
+
+9.1.2 Idempotent Methods
+
+ Methods can also have the property of "idempotence" in that (aside
+ from error or expiration issues) the side-effects of N > 0 identical
+ requests is the same as for a single request. The methods GET, HEAD,
+ PUT and DELETE share this property. Also, the methods OPTIONS and
+ TRACE SHOULD NOT have side effects, and so are inherently idempotent.
+
+
+
+
+
+
+Fielding, et al. Standards Track [Page 51]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ However, it is possible that a sequence of several requests is non-
+ idempotent, even if all of the methods executed in that sequence are
+ idempotent. (A sequence is idempotent if a single execution of the
+ entire sequence always yields a result that is not changed by a
+ reexecution of all, or part, of that sequence.) For example, a
+ sequence is non-idempotent if its result depends on a value that is
+ later modified in the same sequence.
+
+ A sequence that never has side effects is idempotent, by definition
+ (provided that no concurrent operations are being executed on the
+ same set of resources).
+
+9.2 OPTIONS
+
+ The OPTIONS method represents a request for information about the
+ communication options available on the request/response chain
+ identified by the Request-URI. This method allows the client to
+ determine the options and/or requirements associated with a resource,
+ or the capabilities of a server, without implying a resource action
+ or initiating a resource retrieval.
+
+ Responses to this method are not cacheable.
+
+ If the OPTIONS request includes an entity-body (as indicated by the
+ presence of Content-Length or Transfer-Encoding), then the media type
+ MUST be indicated by a Content-Type field. Although this
+ specification does not define any use for such a body, future
+ extensions to HTTP might use the OPTIONS body to make more detailed
+ queries on the server. A server that does not support such an
+ extension MAY discard the request body.
+
+ If the Request-URI is an asterisk ("*"), the OPTIONS request is
+ intended to apply to the server in general rather than to a specific
+ resource. Since a server's communication options typically depend on
+ the resource, the "*" request is only useful as a "ping" or "no-op"
+ type of method; it does nothing beyond allowing the client to test
+ the capabilities of the server. For example, this can be used to test
+ a proxy for HTTP/1.1 compliance (or lack thereof).
+
+ If the Request-URI is not an asterisk, the OPTIONS request applies
+ only to the options that are available when communicating with that
+ resource.
+
+ A 200 response SHOULD include any header fields that indicate
+ optional features implemented by the server and applicable to that
+ resource (e.g., Allow), possibly including extensions not defined by
+ this specification. The response body, if any, SHOULD also include
+ information about the communication options. The format for such a
+
+
+
+Fielding, et al. Standards Track [Page 52]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ body is not defined by this specification, but might be defined by
+ future extensions to HTTP. Content negotiation MAY be used to select
+ the appropriate response format. If no response body is included, the
+ response MUST include a Content-Length field with a field-value of
+ "0".
+
+ The Max-Forwards request-header field MAY be used to target a
+ specific proxy in the request chain. When a proxy receives an OPTIONS
+ request on an absoluteURI for which request forwarding is permitted,
+ the proxy MUST check for a Max-Forwards field. If the Max-Forwards
+ field-value is zero ("0"), the proxy MUST NOT forward the message;
+ instead, the proxy SHOULD respond with its own communication options.
+ If the Max-Forwards field-value is an integer greater than zero, the
+ proxy MUST decrement the field-value when it forwards the request. If
+ no Max-Forwards field is present in the request, then the forwarded
+ request MUST NOT include a Max-Forwards field.
+
+9.3 GET
+
+ The GET method means retrieve whatever information (in the form of an
+ entity) is identified by the Request-URI. If the Request-URI refers
+ to a data-producing process, it is the produced data which shall be
+ returned as the entity in the response and not the source text of the
+ process, unless that text happens to be the output of the process.
+
+ The semantics of the GET method change to a "conditional GET" if the
+ request message includes an If-Modified-Since, If-Unmodified-Since,
+ If-Match, If-None-Match, or If-Range header field. A conditional GET
+ method requests that the entity be transferred only under the
+ circumstances described by the conditional header field(s). The
+ conditional GET method is intended to reduce unnecessary network
+ usage by allowing cached entities to be refreshed without requiring
+ multiple requests or transferring data already held by the client.
+
+ The semantics of the GET method change to a "partial GET" if the
+ request message includes a Range header field. A partial GET requests
+ that only part of the entity be transferred, as described in section
+ 14.35. The partial GET method is intended to reduce unnecessary
+ network usage by allowing partially-retrieved entities to be
+ completed without transferring data already held by the client.
+
+ The response to a GET request is cacheable if and only if it meets
+ the requirements for HTTP caching described in section 13.
+
+ See section 15.1.3 for security considerations when used for forms.
+
+
+
+
+
+
+Fielding, et al. Standards Track [Page 53]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+9.4 HEAD
+
+ The HEAD method is identical to GET except that the server MUST NOT
+ return a message-body in the response. The metainformation contained
+ in the HTTP headers in response to a HEAD request SHOULD be identical
+ to the information sent in response to a GET request. This method can
+ be used for obtaining metainformation about the entity implied by the
+ request without transferring the entity-body itself. This method is
+ often used for testing hypertext links for validity, accessibility,
+ and recent modification.
+
+ The response to a HEAD request MAY be cacheable in the sense that the
+ information contained in the response MAY be used to update a
+ previously cached entity from that resource. If the new field values
+ indicate that the cached entity differs from the current entity (as
+ would be indicated by a change in Content-Length, Content-MD5, ETag
+ or Last-Modified), then the cache MUST treat the cache entry as
+ stale.
+
+9.5 POST
+
+ The POST method is used to request that the origin server accept the
+ entity enclosed in the request as a new subordinate of the resource
+ identified by the Request-URI in the Request-Line. POST is designed
+ to allow a uniform method to cover the following functions:
+
+ - Annotation of existing resources;
+
+ - Posting a message to a bulletin board, newsgroup, mailing list,
+ or similar group of articles;
+
+ - Providing a block of data, such as the result of submitting a
+ form, to a data-handling process;
+
+ - Extending a database through an append operation.
+
+ The actual function performed by the POST method is determined by the
+ server and is usually dependent on the Request-URI. The posted entity
+ is subordinate to that URI in the same way that a file is subordinate
+ to a directory containing it, a news article is subordinate to a
+ newsgroup to which it is posted, or a record is subordinate to a
+ database.
+
+ The action performed by the POST method might not result in a
+ resource that can be identified by a URI. In this case, either 200
+ (OK) or 204 (No Content) is the appropriate response status,
+ depending on whether or not the response includes an entity that
+ describes the result.
+
+
+
+Fielding, et al. Standards Track [Page 54]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ If a resource has been created on the origin server, the response
+ SHOULD be 201 (Created) and contain an entity which describes the
+ status of the request and refers to the new resource, and a Location
+ header (see section 14.30).
+
+ Responses to this method are not cacheable, unless the response
+ includes appropriate Cache-Control or Expires header fields. However,
+ the 303 (See Other) response can be used to direct the user agent to
+ retrieve a cacheable resource.
+
+ POST requests MUST obey the message transmission requirements set out
+ in section 8.2.
+
+ See section 15.1.3 for security considerations.
+
+9.6 PUT
+
+ The PUT method requests that the enclosed entity be stored under the
+ supplied Request-URI. If the Request-URI refers to an already
+ existing resource, the enclosed entity SHOULD be considered as a
+ modified version of the one residing on the origin server. If the
+ Request-URI does not point to an existing resource, and that URI is
+ capable of being defined as a new resource by the requesting user
+ agent, the origin server can create the resource with that URI. If a
+ new resource is created, the origin server MUST inform the user agent
+ via the 201 (Created) response. If an existing resource is modified,
+ either the 200 (OK) or 204 (No Content) response codes SHOULD be sent
+ to indicate successful completion of the request. If the resource
+ could not be created or modified with the Request-URI, an appropriate
+ error response SHOULD be given that reflects the nature of the
+ problem. The recipient of the entity MUST NOT ignore any Content-*
+ (e.g. Content-Range) headers that it does not understand or implement
+ and MUST return a 501 (Not Implemented) response in such cases.
+
+ If the request passes through a cache and the Request-URI identifies
+ one or more currently cached entities, those entries SHOULD be
+ treated as stale. Responses to this method are not cacheable.
+
+ The fundamental difference between the POST and PUT requests is
+ reflected in the different meaning of the Request-URI. The URI in a
+ POST request identifies the resource that will handle the enclosed
+ entity. That resource might be a data-accepting process, a gateway to
+ some other protocol, or a separate entity that accepts annotations.
+ In contrast, the URI in a PUT request identifies the entity enclosed
+ with the request -- the user agent knows what URI is intended and the
+ server MUST NOT attempt to apply the request to some other resource.
+ If the server desires that the request be applied to a different URI,
+
+
+
+
+Fielding, et al. Standards Track [Page 55]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ it MUST send a 301 (Moved Permanently) response; the user agent MAY
+ then make its own decision regarding whether or not to redirect the
+ request.
+
+ A single resource MAY be identified by many different URIs. For
+ example, an article might have a URI for identifying "the current
+ version" which is separate from the URI identifying each particular
+ version. In this case, a PUT request on a general URI might result in
+ several other URIs being defined by the origin server.
+
+ HTTP/1.1 does not define how a PUT method affects the state of an
+ origin server.
+
+ PUT requests MUST obey the message transmission requirements set out
+ in section 8.2.
+
+ Unless otherwise specified for a particular entity-header, the
+ entity-headers in the PUT request SHOULD be applied to the resource
+ created or modified by the PUT.
+
+9.7 DELETE
+
+ The DELETE method requests that the origin server delete the resource
+ identified by the Request-URI. This method MAY be overridden by human
+ intervention (or other means) on the origin server. The client cannot
+ be guaranteed that the operation has been carried out, even if the
+ status code returned from the origin server indicates that the action
+ has been completed successfully. However, the server SHOULD NOT
+ indicate success unless, at the time the response is given, it
+ intends to delete the resource or move it to an inaccessible
+ location.
+
+ A successful response SHOULD be 200 (OK) if the response includes an
+ entity describing the status, 202 (Accepted) if the action has not
+ yet been enacted, or 204 (No Content) if the action has been enacted
+ but the response does not include an entity.
+
+ If the request passes through a cache and the Request-URI identifies
+ one or more currently cached entities, those entries SHOULD be
+ treated as stale. Responses to this method are not cacheable.
+
+9.8 TRACE
+
+ The TRACE method is used to invoke a remote, application-layer loop-
+ back of the request message. The final recipient of the request
+ SHOULD reflect the message received back to the client as the
+ entity-body of a 200 (OK) response. The final recipient is either the
+
+
+
+
+Fielding, et al. Standards Track [Page 56]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ origin server or the first proxy or gateway to receive a Max-Forwards
+ value of zero (0) in the request (see section 14.31). A TRACE request
+ MUST NOT include an entity.
+
+ TRACE allows the client to see what is being received at the other
+ end of the request chain and use that data for testing or diagnostic
+ information. The value of the Via header field (section 14.45) is of
+ particular interest, since it acts as a trace of the request chain.
+ Use of the Max-Forwards header field allows the client to limit the
+ length of the request chain, which is useful for testing a chain of
+ proxies forwarding messages in an infinite loop.
+
+ If the request is valid, the response SHOULD contain the entire
+ request message in the entity-body, with a Content-Type of
+ "message/http". Responses to this method MUST NOT be cached.
+
+9.9 CONNECT
+
+ This specification reserves the method name CONNECT for use with a
+ proxy that can dynamically switch to being a tunnel (e.g. SSL
+ tunneling [44]).
+
+10 Status Code Definitions
+
+ Each Status-Code is described below, including a description of which
+ method(s) it can follow and any metainformation required in the
+ response.
+
+10.1 Informational 1xx
+
+ This class of status code indicates a provisional response,
+ consisting only of the Status-Line and optional headers, and is
+ terminated by an empty line. There are no required headers for this
+ class of status code. Since HTTP/1.0 did not define any 1xx status
+ codes, servers MUST NOT send a 1xx response to an HTTP/1.0 client
+ except under experimental conditions.
+
+ A client MUST be prepared to accept one or more 1xx status responses
+ prior to a regular response, even if the client does not expect a 100
+ (Continue) status message. Unexpected 1xx status responses MAY be
+ ignored by a user agent.
+
+ Proxies MUST forward 1xx responses, unless the connection between the
+ proxy and its client has been closed, or unless the proxy itself
+ requested the generation of the 1xx response. (For example, if a
+
+
+
+
+
+
+Fielding, et al. Standards Track [Page 57]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ proxy adds a "Expect: 100-continue" field when it forwards a request,
+ then it need not forward the corresponding 100 (Continue)
+ response(s).)
+
+10.1.1 100 Continue
+
+ The client SHOULD continue with its request. This interim response is
+ used to inform the client that the initial part of the request has
+ been received and has not yet been rejected by the server. The client
+ SHOULD continue by sending the remainder of the request or, if the
+ request has already been completed, ignore this response. The server
+ MUST send a final response after the request has been completed. See
+ section 8.2.3 for detailed discussion of the use and handling of this
+ status code.
+
+10.1.2 101 Switching Protocols
+
+ The server understands and is willing to comply with the client's
+ request, via the Upgrade message header field (section 14.42), for a
+ change in the application protocol being used on this connection. The
+ server will switch protocols to those defined by the response's
+ Upgrade header field immediately after the empty line which
+ terminates the 101 response.
+
+ The protocol SHOULD be switched only when it is advantageous to do
+ so. For example, switching to a newer version of HTTP is advantageous
+ over older versions, and switching to a real-time, synchronous
+ protocol might be advantageous when delivering resources that use
+ such features.
+
+10.2 Successful 2xx
+
+ This class of status code indicates that the client's request was
+ successfully received, understood, and accepted.
+
+10.2.1 200 OK
+
+ The request has succeeded. The information returned with the response
+ is dependent on the method used in the request, for example:
+
+ GET an entity corresponding to the requested resource is sent in
+ the response;
+
+ HEAD the entity-header fields corresponding to the requested
+ resource are sent in the response without any message-body;
+
+ POST an entity describing or containing the result of the action;
+
+
+
+
+Fielding, et al. Standards Track [Page 58]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ TRACE an entity containing the request message as received by the
+ end server.
+
+10.2.2 201 Created
+
+ The request has been fulfilled and resulted in a new resource being
+ created. The newly created resource can be referenced by the URI(s)
+ returned in the entity of the response, with the most specific URI
+ for the resource given by a Location header field. The response
+ SHOULD include an entity containing a list of resource
+ characteristics and location(s) from which the user or user agent can
+ choose the one most appropriate. The entity format is specified by
+ the media type given in the Content-Type header field. The origin
+ server MUST create the resource before returning the 201 status code.
+ If the action cannot be carried out immediately, the server SHOULD
+ respond with 202 (Accepted) response instead.
+
+ A 201 response MAY contain an ETag response header field indicating
+ the current value of the entity tag for the requested variant just
+ created, see section 14.19.
+
+10.2.3 202 Accepted
+
+ The request has been accepted for processing, but the processing has
+ not been completed. The request might or might not eventually be
+ acted upon, as it might be disallowed when processing actually takes
+ place. There is no facility for re-sending a status code from an
+ asynchronous operation such as this.
+
+ The 202 response is intentionally non-committal. Its purpose is to
+ allow a server to accept a request for some other process (perhaps a
+ batch-oriented process that is only run once per day) without
+ requiring that the user agent's connection to the server persist
+ until the process is completed. The entity returned with this
+ response SHOULD include an indication of the request's current status
+ and either a pointer to a status monitor or some estimate of when the
+ user can expect the request to be fulfilled.
+
+10.2.4 203 Non-Authoritative Information
+
+ The returned metainformation in the entity-header is not the
+ definitive set as available from the origin server, but is gathered
+ from a local or a third-party copy. The set presented MAY be a subset
+ or superset of the original version. For example, including local
+ annotation information about the resource might result in a superset
+ of the metainformation known by the origin server. Use of this
+ response code is not required and is only appropriate when the
+ response would otherwise be 200 (OK).
+
+
+
+Fielding, et al. Standards Track [Page 59]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+10.2.5 204 No Content
+
+ The server has fulfilled the request but does not need to return an
+ entity-body, and might want to return updated metainformation. The
+ response MAY include new or updated metainformation in the form of
+ entity-headers, which if present SHOULD be associated with the
+ requested variant.
+
+ If the client is a user agent, it SHOULD NOT change its document view
+ from that which caused the request to be sent. This response is
+ primarily intended to allow input for actions to take place without
+ causing a change to the user agent's active document view, although
+ any new or updated metainformation SHOULD be applied to the document
+ currently in the user agent's active view.
+
+ The 204 response MUST NOT include a message-body, and thus is always
+ terminated by the first empty line after the header fields.
+
+10.2.6 205 Reset Content
+
+ The server has fulfilled the request and the user agent SHOULD reset
+ the document view which caused the request to be sent. This response
+ is primarily intended to allow input for actions to take place via
+ user input, followed by a clearing of the form in which the input is
+ given so that the user can easily initiate another input action. The
+ response MUST NOT include an entity.
+
+10.2.7 206 Partial Content
+
+ The server has fulfilled the partial GET request for the resource.
+ The request MUST have included a Range header field (section 14.35)
+ indicating the desired range, and MAY have included an If-Range
+ header field (section 14.27) to make the request conditional.
+
+ The response MUST include the following header fields:
+
+ - Either a Content-Range header field (section 14.16) indicating
+ the range included with this response, or a multipart/byteranges
+ Content-Type including Content-Range fields for each part. If a
+ Content-Length header field is present in the response, its
+ value MUST match the actual number of OCTETs transmitted in the
+ message-body.
+
+ - Date
+
+ - ETag and/or Content-Location, if the header would have been sent
+ in a 200 response to the same request
+
+
+
+
+Fielding, et al. Standards Track [Page 60]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ - Expires, Cache-Control, and/or Vary, if the field-value might
+ differ from that sent in any previous response for the same
+ variant
+
+ If the 206 response is the result of an If-Range request that used a
+ strong cache validator (see section 13.3.3), the response SHOULD NOT
+ include other entity-headers. If the response is the result of an
+ If-Range request that used a weak validator, the response MUST NOT
+ include other entity-headers; this prevents inconsistencies between
+ cached entity-bodies and updated headers. Otherwise, the response
+ MUST include all of the entity-headers that would have been returned
+ with a 200 (OK) response to the same request.
+
+ A cache MUST NOT combine a 206 response with other previously cached
+ content if the ETag or Last-Modified headers do not match exactly,
+ see 13.5.4.
+
+ A cache that does not support the Range and Content-Range headers
+ MUST NOT cache 206 (Partial) responses.
+
+10.3 Redirection 3xx
+
+ This class of status code indicates that further action needs to be
+ taken by the user agent in order to fulfill the request. The action
+ required MAY be carried out by the user agent without interaction
+ with the user if and only if the method used in the second request is
+ GET or HEAD. A client SHOULD detect infinite redirection loops, since
+ such loops generate network traffic for each redirection.
+
+ Note: previous versions of this specification recommended a
+ maximum of five redirections. Content developers should be aware
+ that there might be clients that implement such a fixed
+ limitation.
+
+10.3.1 300 Multiple Choices
+
+ The requested resource corresponds to any one of a set of
+ representations, each with its own specific location, and agent-
+ driven negotiation information (section 12) is being provided so that
+ the user (or user agent) can select a preferred representation and
+ redirect its request to that location.
+
+ Unless it was a HEAD request, the response SHOULD include an entity
+ containing a list of resource characteristics and location(s) from
+ which the user or user agent can choose the one most appropriate. The
+ entity format is specified by the media type given in the Content-
+ Type header field. Depending upon the format and the capabilities of
+
+
+
+
+Fielding, et al. Standards Track [Page 61]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ the user agent, selection of the most appropriate choice MAY be
+ performed automatically. However, this specification does not define
+ any standard for such automatic selection.
+
+ If the server has a preferred choice of representation, it SHOULD
+ include the specific URI for that representation in the Location
+ field; user agents MAY use the Location field value for automatic
+ redirection. This response is cacheable unless indicated otherwise.
+
+10.3.2 301 Moved Permanently
+
+ The requested resource has been assigned a new permanent URI and any
+ future references to this resource SHOULD use one of the returned
+ URIs. Clients with link editing capabilities ought to automatically
+ re-link references to the Request-URI to one or more of the new
+ references returned by the server, where possible. This response is
+ cacheable unless indicated otherwise.
+
+ The new permanent URI SHOULD be given by the Location field in the
+ response. Unless the request method was HEAD, the entity of the
+ response SHOULD contain a short hypertext note with a hyperlink to
+ the new URI(s).
+
+ If the 301 status code is received in response to a request other
+ than GET or HEAD, the user agent MUST NOT automatically redirect the
+ request unless it can be confirmed by the user, since this might
+ change the conditions under which the request was issued.
+
+ Note: When automatically redirecting a POST request after
+ receiving a 301 status code, some existing HTTP/1.0 user agents
+ will erroneously change it into a GET request.
+
+10.3.3 302 Found
+
+ The requested resource resides temporarily under a different URI.
+ Since the redirection might be altered on occasion, the client SHOULD
+ continue to use the Request-URI for future requests. This response
+ is only cacheable if indicated by a Cache-Control or Expires header
+ field.
+
+ The temporary URI SHOULD be given by the Location field in the
+ response. Unless the request method was HEAD, the entity of the
+ response SHOULD contain a short hypertext note with a hyperlink to
+ the new URI(s).
+
+
+
+
+
+
+
+Fielding, et al. Standards Track [Page 62]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ If the 302 status code is received in response to a request other
+ than GET or HEAD, the user agent MUST NOT automatically redirect the
+ request unless it can be confirmed by the user, since this might
+ change the conditions under which the request was issued.
+
+ Note: RFC 1945 and RFC 2068 specify that the client is not allowed
+ to change the method on the redirected request. However, most
+ existing user agent implementations treat 302 as if it were a 303
+ response, performing a GET on the Location field-value regardless
+ of the original request method. The status codes 303 and 307 have
+ been added for servers that wish to make unambiguously clear which
+ kind of reaction is expected of the client.
+
+10.3.4 303 See Other
+
+ The response to the request can be found under a different URI and
+ SHOULD be retrieved using a GET method on that resource. This method
+ exists primarily to allow the output of a POST-activated script to
+ redirect the user agent to a selected resource. The new URI is not a
+ substitute reference for the originally requested resource. The 303
+ response MUST NOT be cached, but the response to the second
+ (redirected) request might be cacheable.
+
+ The different URI SHOULD be given by the Location field in the
+ response. Unless the request method was HEAD, the entity of the
+ response SHOULD contain a short hypertext note with a hyperlink to
+ the new URI(s).
+
+ Note: Many pre-HTTP/1.1 user agents do not understand the 303
+ status. When interoperability with such clients is a concern, the
+ 302 status code may be used instead, since most user agents react
+ to a 302 response as described here for 303.
+
+10.3.5 304 Not Modified
+
+ If the client has performed a conditional GET request and access is
+ allowed, but the document has not been modified, the server SHOULD
+ respond with this status code. The 304 response MUST NOT contain a
+ message-body, and thus is always terminated by the first empty line
+ after the header fields.
+
+ The response MUST include the following header fields:
+
+ - Date, unless its omission is required by section 14.18.1
+
+
+
+
+
+
+
+Fielding, et al. Standards Track [Page 63]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ If a clockless origin server obeys these rules, and proxies and
+ clients add their own Date to any response received without one (as
+ already specified by [RFC 2068], section 14.19), caches will operate
+ correctly.
+
+ - ETag and/or Content-Location, if the header would have been sent
+ in a 200 response to the same request
+
+ - Expires, Cache-Control, and/or Vary, if the field-value might
+ differ from that sent in any previous response for the same
+ variant
+
+ If the conditional GET used a strong cache validator (see section
+ 13.3.3), the response SHOULD NOT include other entity-headers.
+ Otherwise (i.e., the conditional GET used a weak validator), the
+ response MUST NOT include other entity-headers; this prevents
+ inconsistencies between cached entity-bodies and updated headers.
+
+ If a 304 response indicates an entity not currently cached, then the
+ cache MUST disregard the response and repeat the request without the
+ conditional.
+
+ If a cache uses a received 304 response to update a cache entry, the
+ cache MUST update the entry to reflect any new field values given in
+ the response.
+
+10.3.6 305 Use Proxy
+
+ The requested resource MUST be accessed through the proxy given by
+ the Location field. The Location field gives the URI of the proxy.
+ The recipient is expected to repeat this single request via the
+ proxy. 305 responses MUST only be generated by origin servers.
+
+ Note: RFC 2068 was not clear that 305 was intended to redirect a
+ single request, and to be generated by origin servers only. Not
+ observing these limitations has significant security consequences.
+
+10.3.7 306 (Unused)
+
+ The 306 status code was used in a previous version of the
+ specification, is no longer used, and the code is reserved.
+
+
+
+
+
+
+
+
+
+
+Fielding, et al. Standards Track [Page 64]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+10.3.8 307 Temporary Redirect
+
+ The requested resource resides temporarily under a different URI.
+ Since the redirection MAY be altered on occasion, the client SHOULD
+ continue to use the Request-URI for future requests. This response
+ is only cacheable if indicated by a Cache-Control or Expires header
+ field.
+
+ The temporary URI SHOULD be given by the Location field in the
+ response. Unless the request method was HEAD, the entity of the
+ response SHOULD contain a short hypertext note with a hyperlink to
+ the new URI(s) , since many pre-HTTP/1.1 user agents do not
+ understand the 307 status. Therefore, the note SHOULD contain the
+ information necessary for a user to repeat the original request on
+ the new URI.
+
+ If the 307 status code is received in response to a request other
+ than GET or HEAD, the user agent MUST NOT automatically redirect the
+ request unless it can be confirmed by the user, since this might
+ change the conditions under which the request was issued.
+
+10.4 Client Error 4xx
+
+ The 4xx class of status code is intended for cases in which the
+ client seems to have erred. Except when responding to a HEAD request,
+ the server SHOULD include an entity containing an explanation of the
+ error situation, and whether it is a temporary or permanent
+ condition. These status codes are applicable to any request method.
+ User agents SHOULD display any included entity to the user.
+
+ If the client is sending data, a server implementation using TCP
+ SHOULD be careful to ensure that the client acknowledges receipt of
+ the packet(s) containing the response, before the server closes the
+ input connection. If the client continues sending data to the server
+ after the close, the server's TCP stack will send a reset packet to
+ the client, which may erase the client's unacknowledged input buffers
+ before they can be read and interpreted by the HTTP application.
+
+10.4.1 400 Bad Request
+
+ The request could not be understood by the server due to malformed
+ syntax. The client SHOULD NOT repeat the request without
+ modifications.
+
+
+
+
+
+
+
+
+Fielding, et al. Standards Track [Page 65]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+10.4.2 401 Unauthorized
+
+ The request requires user authentication. The response MUST include a
+ WWW-Authenticate header field (section 14.47) containing a challenge
+ applicable to the requested resource. The client MAY repeat the
+ request with a suitable Authorization header field (section 14.8). If
+ the request already included Authorization credentials, then the 401
+ response indicates that authorization has been refused for those
+ credentials. If the 401 response contains the same challenge as the
+ prior response, and the user agent has already attempted
+ authentication at least once, then the user SHOULD be presented the
+ entity that was given in the response, since that entity might
+ include relevant diagnostic information. HTTP access authentication
+ is explained in "HTTP Authentication: Basic and Digest Access
+ Authentication" [43].
+
+10.4.3 402 Payment Required
+
+ This code is reserved for future use.
+
+10.4.4 403 Forbidden
+
+ The server understood the request, but is refusing to fulfill it.
+ Authorization will not help and the request SHOULD NOT be repeated.
+ If the request method was not HEAD and the server wishes to make
+ public why the request has not been fulfilled, it SHOULD describe the
+ reason for the refusal in the entity. If the server does not wish to
+ make this information available to the client, the status code 404
+ (Not Found) can be used instead.
+
+10.4.5 404 Not Found
+
+ The server has not found anything matching the Request-URI. No
+ indication is given of whether the condition is temporary or
+ permanent. The 410 (Gone) status code SHOULD be used if the server
+ knows, through some internally configurable mechanism, that an old
+ resource is permanently unavailable and has no forwarding address.
+ This status code is commonly used when the server does not wish to
+ reveal exactly why the request has been refused, or when no other
+ response is applicable.
+
+10.4.6 405 Method Not Allowed
+
+ The method specified in the Request-Line is not allowed for the
+ resource identified by the Request-URI. The response MUST include an
+ Allow header containing a list of valid methods for the requested
+ resource.
+
+
+
+
+Fielding, et al. Standards Track [Page 66]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+10.4.7 406 Not Acceptable
+
+ The resource identified by the request is only capable of generating
+ response entities which have content characteristics not acceptable
+ according to the accept headers sent in the request.
+
+ Unless it was a HEAD request, the response SHOULD include an entity
+ containing a list of available entity characteristics and location(s)
+ from which the user or user agent can choose the one most
+ appropriate. The entity format is specified by the media type given
+ in the Content-Type header field. Depending upon the format and the
+ capabilities of the user agent, selection of the most appropriate
+ choice MAY be performed automatically. However, this specification
+ does not define any standard for such automatic selection.
+
+ Note: HTTP/1.1 servers are allowed to return responses which are
+ not acceptable according to the accept headers sent in the
+ request. In some cases, this may even be preferable to sending a
+ 406 response. User agents are encouraged to inspect the headers of
+ an incoming response to determine if it is acceptable.
+
+ If the response could be unacceptable, a user agent SHOULD
+ temporarily stop receipt of more data and query the user for a
+ decision on further actions.
+
+10.4.8 407 Proxy Authentication Required
+
+ This code is similar to 401 (Unauthorized), but indicates that the
+ client must first authenticate itself with the proxy. The proxy MUST
+ return a Proxy-Authenticate header field (section 14.33) containing a
+ challenge applicable to the proxy for the requested resource. The
+ client MAY repeat the request with a suitable Proxy-Authorization
+ header field (section 14.34). HTTP access authentication is explained
+ in "HTTP Authentication: Basic and Digest Access Authentication"
+ [43].
+
+10.4.9 408 Request Timeout
+
+ The client did not produce a request within the time that the server
+ was prepared to wait. The client MAY repeat the request without
+ modifications at any later time.
+
+10.4.10 409 Conflict
+
+ The request could not be completed due to a conflict with the current
+ state of the resource. This code is only allowed in situations where
+ it is expected that the user might be able to resolve the conflict
+ and resubmit the request. The response body SHOULD include enough
+
+
+
+Fielding, et al. Standards Track [Page 67]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ information for the user to recognize the source of the conflict.
+ Ideally, the response entity would include enough information for the
+ user or user agent to fix the problem; however, that might not be
+ possible and is not required.
+
+ Conflicts are most likely to occur in response to a PUT request. For
+ example, if versioning were being used and the entity being PUT
+ included changes to a resource which conflict with those made by an
+ earlier (third-party) request, the server might use the 409 response
+ to indicate that it can't complete the request. In this case, the
+ response entity would likely contain a list of the differences
+ between the two versions in a format defined by the response
+ Content-Type.
+
+10.4.11 410 Gone
+
+ The requested resource is no longer available at the server and no
+ forwarding address is known. This condition is expected to be
+ considered permanent. Clients with link editing capabilities SHOULD
+ delete references to the Request-URI after user approval. If the
+ server does not know, or has no facility to determine, whether or not
+ the condition is permanent, the status code 404 (Not Found) SHOULD be
+ used instead. This response is cacheable unless indicated otherwise.
+
+ The 410 response is primarily intended to assist the task of web
+ maintenance by notifying the recipient that the resource is
+ intentionally unavailable and that the server owners desire that
+ remote links to that resource be removed. Such an event is common for
+ limited-time, promotional services and for resources belonging to
+ individuals no longer working at the server's site. It is not
+ necessary to mark all permanently unavailable resources as "gone" or
+ to keep the mark for any length of time -- that is left to the
+ discretion of the server owner.
+
+10.4.12 411 Length Required
+
+ The server refuses to accept the request without a defined Content-
+ Length. The client MAY repeat the request if it adds a valid
+ Content-Length header field containing the length of the message-body
+ in the request message.
+
+10.4.13 412 Precondition Failed
+
+ The precondition given in one or more of the request-header fields
+ evaluated to false when it was tested on the server. This response
+ code allows the client to place preconditions on the current resource
+ metainformation (header field data) and thus prevent the requested
+ method from being applied to a resource other than the one intended.
+
+
+
+Fielding, et al. Standards Track [Page 68]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+10.4.14 413 Request Entity Too Large
+
+ The server is refusing to process a request because the request
+ entity is larger than the server is willing or able to process. The
+ server MAY close the connection to prevent the client from continuing
+ the request.
+
+ If the condition is temporary, the server SHOULD include a Retry-
+ After header field to indicate that it is temporary and after what
+ time the client MAY try again.
+
+10.4.15 414 Request-URI Too Long
+
+ The server is refusing to service the request because the Request-URI
+ is longer than the server is willing to interpret. This rare
+ condition is only likely to occur when a client has improperly
+ converted a POST request to a GET request with long query
+ information, when the client has descended into a URI "black hole" of
+ redirection (e.g., a redirected URI prefix that points to a suffix of
+ itself), or when the server is under attack by a client attempting to
+ exploit security holes present in some servers using fixed-length
+ buffers for reading or manipulating the Request-URI.
+
+10.4.16 415 Unsupported Media Type
+
+ The server is refusing to service the request because the entity of
+ the request is in a format not supported by the requested resource
+ for the requested method.
+
+10.4.17 416 Requested Range Not Satisfiable
+
+ A server SHOULD return a response with this status code if a request
+ included a Range request-header field (section 14.35), and none of
+ the range-specifier values in this field overlap the current extent
+ of the selected resource, and the request did not include an If-Range
+ request-header field. (For byte-ranges, this means that the first-
+ byte-pos of all of the byte-range-spec values were greater than the
+ current length of the selected resource.)
+
+ When this status code is returned for a byte-range request, the
+ response SHOULD include a Content-Range entity-header field
+ specifying the current length of the selected resource (see section
+ 14.16). This response MUST NOT use the multipart/byteranges content-
+ type.
+
+
+
+
+
+
+
+Fielding, et al. Standards Track [Page 69]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+10.4.18 417 Expectation Failed
+
+ The expectation given in an Expect request-header field (see section
+ 14.20) could not be met by this server, or, if the server is a proxy,
+ the server has unambiguous evidence that the request could not be met
+ by the next-hop server.
+
+10.5 Server Error 5xx
+
+ Response status codes beginning with the digit "5" indicate cases in
+ which the server is aware that it has erred or is incapable of
+ performing the request. Except when responding to a HEAD request, the
+ server SHOULD include an entity containing an explanation of the
+ error situation, and whether it is a temporary or permanent
+ condition. User agents SHOULD display any included entity to the
+ user. These response codes are applicable to any request method.
+
+10.5.1 500 Internal Server Error
+
+ The server encountered an unexpected condition which prevented it
+ from fulfilling the request.
+
+10.5.2 501 Not Implemented
+
+ The server does not support the functionality required to fulfill the
+ request. This is the appropriate response when the server does not
+ recognize the request method and is not capable of supporting it for
+ any resource.
+
+10.5.3 502 Bad Gateway
+
+ The server, while acting as a gateway or proxy, received an invalid
+ response from the upstream server it accessed in attempting to
+ fulfill the request.
+
+10.5.4 503 Service Unavailable
+
+ The server is currently unable to handle the request due to a
+ temporary overloading or maintenance of the server. The implication
+ is that this is a temporary condition which will be alleviated after
+ some delay. If known, the length of the delay MAY be indicated in a
+ Retry-After header. If no Retry-After is given, the client SHOULD
+ handle the response as it would for a 500 response.
+
+ Note: The existence of the 503 status code does not imply that a
+ server must use it when becoming overloaded. Some servers may wish
+ to simply refuse the connection.
+
+
+
+
+Fielding, et al. Standards Track [Page 70]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+10.5.5 504 Gateway Timeout
+
+ The server, while acting as a gateway or proxy, did not receive a
+ timely response from the upstream server specified by the URI (e.g.
+ HTTP, FTP, LDAP) or some other auxiliary server (e.g. DNS) it needed
+ to access in attempting to complete the request.
+
+ Note: Note to implementors: some deployed proxies are known to
+ return 400 or 500 when DNS lookups time out.
+
+10.5.6 505 HTTP Version Not Supported
+
+ The server does not support, or refuses to support, the HTTP protocol
+ version that was used in the request message. The server is
+ indicating that it is unable or unwilling to complete the request
+ using the same major version as the client, as described in section
+ 3.1, other than with this error message. The response SHOULD contain
+ an entity describing why that version is not supported and what other
+ protocols are supported by that server.
+
+11 Access Authentication
+
+ HTTP provides several OPTIONAL challenge-response authentication
+ mechanisms which can be used by a server to challenge a client
+ request and by a client to provide authentication information. The
+ general framework for access authentication, and the specification of
+ "basic" and "digest" authentication, are specified in "HTTP
+ Authentication: Basic and Digest Access Authentication" [43]. This
+ specification adopts the definitions of "challenge" and "credentials"
+ from that specification.
+
+12 Content Negotiation
+
+ Most HTTP responses include an entity which contains information for
+ interpretation by a human user. Naturally, it is desirable to supply
+ the user with the "best available" entity corresponding to the
+ request. Unfortunately for servers and caches, not all users have the
+ same preferences for what is "best," and not all user agents are
+ equally capable of rendering all entity types. For that reason, HTTP
+ has provisions for several mechanisms for "content negotiation" --
+ the process of selecting the best representation for a given response
+ when there are multiple representations available.
+
+ Note: This is not called "format negotiation" because the
+ alternate representations may be of the same media type, but use
+ different capabilities of that type, be in different languages,
+ etc.
+
+
+
+
+Fielding, et al. Standards Track [Page 71]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ Any response containing an entity-body MAY be subject to negotiation,
+ including error responses.
+
+ There are two kinds of content negotiation which are possible in
+ HTTP: server-driven and agent-driven negotiation. These two kinds of
+ negotiation are orthogonal and thus may be used separately or in
+ combination. One method of combination, referred to as transparent
+ negotiation, occurs when a cache uses the agent-driven negotiation
+ information provided by the origin server in order to provide
+ server-driven negotiation for subsequent requests.
+
+12.1 Server-driven Negotiation
+
+ If the selection of the best representation for a response is made by
+ an algorithm located at the server, it is called server-driven
+ negotiation. Selection is based on the available representations of
+ the response (the dimensions over which it can vary; e.g. language,
+ content-coding, etc.) and the contents of particular header fields in
+ the request message or on other information pertaining to the request
+ (such as the network address of the client).
+
+ Server-driven negotiation is advantageous when the algorithm for
+ selecting from among the available representations is difficult to
+ describe to the user agent, or when the server desires to send its
+ "best guess" to the client along with the first response (hoping to
+ avoid the round-trip delay of a subsequent request if the "best
+ guess" is good enough for the user). In order to improve the server's
+ guess, the user agent MAY include request header fields (Accept,
+ Accept-Language, Accept-Encoding, etc.) which describe its
+ preferences for such a response.
+
+ Server-driven negotiation has disadvantages:
+
+ 1. It is impossible for the server to accurately determine what
+ might be "best" for any given user, since that would require
+ complete knowledge of both the capabilities of the user agent
+ and the intended use for the response (e.g., does the user want
+ to view it on screen or print it on paper?).
+
+ 2. Having the user agent describe its capabilities in every
+ request can be both very inefficient (given that only a small
+ percentage of responses have multiple representations) and a
+ potential violation of the user's privacy.
+
+ 3. It complicates the implementation of an origin server and the
+ algorithms for generating responses to a request.
+
+
+
+
+
+Fielding, et al. Standards Track [Page 72]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ 4. It may limit a public cache's ability to use the same response
+ for multiple user's requests.
+
+ HTTP/1.1 includes the following request-header fields for enabling
+ server-driven negotiation through description of user agent
+ capabilities and user preferences: Accept (section 14.1), Accept-
+ Charset (section 14.2), Accept-Encoding (section 14.3), Accept-
+ Language (section 14.4), and User-Agent (section 14.43). However, an
+ origin server is not limited to these dimensions and MAY vary the
+ response based on any aspect of the request, including information
+ outside the request-header fields or within extension header fields
+ not defined by this specification.
+
+ The Vary header field can be used to express the parameters the
+ server uses to select a representation that is subject to server-
+ driven negotiation. See section 13.6 for use of the Vary header field
+ by caches and section 14.44 for use of the Vary header field by
+ servers.
+
+12.2 Agent-driven Negotiation
+
+ With agent-driven negotiation, selection of the best representation
+ for a response is performed by the user agent after receiving an
+ initial response from the origin server. Selection is based on a list
+ of the available representations of the response included within the
+ header fields or entity-body of the initial response, with each
+ representation identified by its own URI. Selection from among the
+ representations may be performed automatically (if the user agent is
+ capable of doing so) or manually by the user selecting from a
+ generated (possibly hypertext) menu.
+
+ Agent-driven negotiation is advantageous when the response would vary
+ over commonly-used dimensions (such as type, language, or encoding),
+ when the origin server is unable to determine a user agent's
+ capabilities from examining the request, and generally when public
+ caches are used to distribute server load and reduce network usage.
+
+ Agent-driven negotiation suffers from the disadvantage of needing a
+ second request to obtain the best alternate representation. This
+ second request is only efficient when caching is used. In addition,
+ this specification does not define any mechanism for supporting
+ automatic selection, though it also does not prevent any such
+ mechanism from being developed as an extension and used within
+ HTTP/1.1.
+
+
+
+
+
+
+
+Fielding, et al. Standards Track [Page 73]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ HTTP/1.1 defines the 300 (Multiple Choices) and 406 (Not Acceptable)
+ status codes for enabling agent-driven negotiation when the server is
+ unwilling or unable to provide a varying response using server-driven
+ negotiation.
+
+12.3 Transparent Negotiation
+
+ Transparent negotiation is a combination of both server-driven and
+ agent-driven negotiation. When a cache is supplied with a form of the
+ list of available representations of the response (as in agent-driven
+ negotiation) and the dimensions of variance are completely understood
+ by the cache, then the cache becomes capable of performing server-
+ driven negotiation on behalf of the origin server for subsequent
+ requests on that resource.
+
+ Transparent negotiation has the advantage of distributing the
+ negotiation work that would otherwise be required of the origin
+ server and also removing the second request delay of agent-driven
+ negotiation when the cache is able to correctly guess the right
+ response.
+
+ This specification does not define any mechanism for transparent
+ negotiation, though it also does not prevent any such mechanism from
+ being developed as an extension that could be used within HTTP/1.1.
+
+13 Caching in HTTP
+
+ HTTP is typically used for distributed information systems, where
+ performance can be improved by the use of response caches. The
+ HTTP/1.1 protocol includes a number of elements intended to make
+ caching work as well as possible. Because these elements are
+ inextricable from other aspects of the protocol, and because they
+ interact with each other, it is useful to describe the basic caching
+ design of HTTP separately from the detailed descriptions of methods,
+ headers, response codes, etc.
+
+ Caching would be useless if it did not significantly improve
+ performance. The goal of caching in HTTP/1.1 is to eliminate the need
+ to send requests in many cases, and to eliminate the need to send
+ full responses in many other cases. The former reduces the number of
+ network round-trips required for many operations; we use an
+ "expiration" mechanism for this purpose (see section 13.2). The
+ latter reduces network bandwidth requirements; we use a "validation"
+ mechanism for this purpose (see section 13.3).
+
+ Requirements for performance, availability, and disconnected
+ operation require us to be able to relax the goal of semantic
+ transparency. The HTTP/1.1 protocol allows origin servers, caches,
+
+
+
+Fielding, et al. Standards Track [Page 74]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ and clients to explicitly reduce transparency when necessary.
+ However, because non-transparent operation may confuse non-expert
+ users, and might be incompatible with certain server applications
+ (such as those for ordering merchandise), the protocol requires that
+ transparency be relaxed
+
+ - only by an explicit protocol-level request when relaxed by
+ client or origin server
+
+ - only with an explicit warning to the end user when relaxed by
+ cache or client
+
+ Therefore, the HTTP/1.1 protocol provides these important elements:
+
+ 1. Protocol features that provide full semantic transparency when
+ this is required by all parties.
+
+ 2. Protocol features that allow an origin server or user agent to
+ explicitly request and control non-transparent operation.
+
+ 3. Protocol features that allow a cache to attach warnings to
+ responses that do not preserve the requested approximation of
+ semantic transparency.
+
+ A basic principle is that it must be possible for the clients to
+ detect any potential relaxation of semantic transparency.
+
+ Note: The server, cache, or client implementor might be faced with
+ design decisions not explicitly discussed in this specification.
+ If a decision might affect semantic transparency, the implementor
+ ought to err on the side of maintaining transparency unless a
+ careful and complete analysis shows significant benefits in
+ breaking transparency.
+
+13.1.1 Cache Correctness
+
+ A correct cache MUST respond to a request with the most up-to-date
+ response held by the cache that is appropriate to the request (see
+ sections 13.2.5, 13.2.6, and 13.12) which meets one of the following
+ conditions:
+
+ 1. It has been checked for equivalence with what the origin server
+ would have returned by revalidating the response with the
+ origin server (section 13.3);
+
+
+
+
+
+
+
+Fielding, et al. Standards Track [Page 75]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ 2. It is "fresh enough" (see section 13.2). In the default case,
+ this means it meets the least restrictive freshness requirement
+ of the client, origin server, and cache (see section 14.9); if
+ the origin server so specifies, it is the freshness requirement
+ of the origin server alone.
+
+ If a stored response is not "fresh enough" by the most
+ restrictive freshness requirement of both the client and the
+ origin server, in carefully considered circumstances the cache
+ MAY still return the response with the appropriate Warning
+ header (see section 13.1.5 and 14.46), unless such a response
+ is prohibited (e.g., by a "no-store" cache-directive, or by a
+ "no-cache" cache-request-directive; see section 14.9).
+
+ 3. It is an appropriate 304 (Not Modified), 305 (Proxy Redirect),
+ or error (4xx or 5xx) response message.
+
+ If the cache can not communicate with the origin server, then a
+ correct cache SHOULD respond as above if the response can be
+ correctly served from the cache; if not it MUST return an error or
+ warning indicating that there was a communication failure.
+
+ If a cache receives a response (either an entire response, or a 304
+ (Not Modified) response) that it would normally forward to the
+ requesting client, and the received response is no longer fresh, the
+ cache SHOULD forward it to the requesting client without adding a new
+ Warning (but without removing any existing Warning headers). A cache
+ SHOULD NOT attempt to revalidate a response simply because that
+ response became stale in transit; this might lead to an infinite
+ loop. A user agent that receives a stale response without a Warning
+ MAY display a warning indication to the user.
+
+13.1.2 Warnings
+
+ Whenever a cache returns a response that is neither first-hand nor
+ "fresh enough" (in the sense of condition 2 in section 13.1.1), it
+ MUST attach a warning to that effect, using a Warning general-header.
+ The Warning header and the currently defined warnings are described
+ in section 14.46. The warning allows clients to take appropriate
+ action.
+
+ Warnings MAY be used for other purposes, both cache-related and
+ otherwise. The use of a warning, rather than an error status code,
+ distinguish these responses from true failures.
+
+ Warnings are assigned three digit warn-codes. The first digit
+ indicates whether the Warning MUST or MUST NOT be deleted from a
+ stored cache entry after a successful revalidation:
+
+
+
+Fielding, et al. Standards Track [Page 76]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ 1xx Warnings that describe the freshness or revalidation status of
+ the response, and so MUST be deleted after a successful
+ revalidation. 1XX warn-codes MAY be generated by a cache only when
+ validating a cached entry. It MUST NOT be generated by clients.
+
+ 2xx Warnings that describe some aspect of the entity body or entity
+ headers that is not rectified by a revalidation (for example, a
+ lossy compression of the entity bodies) and which MUST NOT be
+ deleted after a successful revalidation.
+
+ See section 14.46 for the definitions of the codes themselves.
+
+ HTTP/1.0 caches will cache all Warnings in responses, without
+ deleting the ones in the first category. Warnings in responses that
+ are passed to HTTP/1.0 caches carry an extra warning-date field,
+ which prevents a future HTTP/1.1 recipient from believing an
+ erroneously cached Warning.
+
+ Warnings also carry a warning text. The text MAY be in any
+ appropriate natural language (perhaps based on the client's Accept
+ headers), and include an OPTIONAL indication of what character set is
+ used.
+
+ Multiple warnings MAY be attached to a response (either by the origin
+ server or by a cache), including multiple warnings with the same code
+ number. For example, a server might provide the same warning with
+ texts in both English and Basque.
+
+ When multiple warnings are attached to a response, it might not be
+ practical or reasonable to display all of them to the user. This
+ version of HTTP does not specify strict priority rules for deciding
+ which warnings to display and in what order, but does suggest some
+ heuristics.
+
+13.1.3 Cache-control Mechanisms
+
+ The basic cache mechanisms in HTTP/1.1 (server-specified expiration
+ times and validators) are implicit directives to caches. In some
+ cases, a server or client might need to provide explicit directives
+ to the HTTP caches. We use the Cache-Control header for this purpose.
+
+ The Cache-Control header allows a client or server to transmit a
+ variety of directives in either requests or responses. These
+ directives typically override the default caching algorithms. As a
+ general rule, if there is any apparent conflict between header
+ values, the most restrictive interpretation is applied (that is, the
+ one that is most likely to preserve semantic transparency). However,
+
+
+
+
+Fielding, et al. Standards Track [Page 77]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ in some cases, cache-control directives are explicitly specified as
+ weakening the approximation of semantic transparency (for example,
+ "max-stale" or "public").
+
+ The cache-control directives are described in detail in section 14.9.
+
+13.1.4 Explicit User Agent Warnings
+
+ Many user agents make it possible for users to override the basic
+ caching mechanisms. For example, the user agent might allow the user
+ to specify that cached entities (even explicitly stale ones) are
+ never validated. Or the user agent might habitually add "Cache-
+ Control: max-stale=3600" to every request. The user agent SHOULD NOT
+ default to either non-transparent behavior, or behavior that results
+ in abnormally ineffective caching, but MAY be explicitly configured
+ to do so by an explicit action of the user.
+
+ If the user has overridden the basic caching mechanisms, the user
+ agent SHOULD explicitly indicate to the user whenever this results in
+ the display of information that might not meet the server's
+ transparency requirements (in particular, if the displayed entity is
+ known to be stale). Since the protocol normally allows the user agent
+ to determine if responses are stale or not, this indication need only
+ be displayed when this actually happens. The indication need not be a
+ dialog box; it could be an icon (for example, a picture of a rotting
+ fish) or some other indicator.
+
+ If the user has overridden the caching mechanisms in a way that would
+ abnormally reduce the effectiveness of caches, the user agent SHOULD
+ continually indicate this state to the user (for example, by a
+ display of a picture of currency in flames) so that the user does not
+ inadvertently consume excess resources or suffer from excessive
+ latency.
+
+13.1.5 Exceptions to the Rules and Warnings
+
+ In some cases, the operator of a cache MAY choose to configure it to
+ return stale responses even when not requested by clients. This
+ decision ought not be made lightly, but may be necessary for reasons
+ of availability or performance, especially when the cache is poorly
+ connected to the origin server. Whenever a cache returns a stale
+ response, it MUST mark it as such (using a Warning header) enabling
+ the client software to alert the user that there might be a potential
+ problem.
+
+
+
+
+
+
+
+Fielding, et al. Standards Track [Page 78]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ It also allows the user agent to take steps to obtain a first-hand or
+ fresh response. For this reason, a cache SHOULD NOT return a stale
+ response if the client explicitly requests a first-hand or fresh one,
+ unless it is impossible to comply for technical or policy reasons.
+
+13.1.6 Client-controlled Behavior
+
+ While the origin server (and to a lesser extent, intermediate caches,
+ by their contribution to the age of a response) are the primary
+ source of expiration information, in some cases the client might need
+ to control a cache's decision about whether to return a cached
+ response without validating it. Clients do this using several
+ directives of the Cache-Control header.
+
+ A client's request MAY specify the maximum age it is willing to
+ accept of an unvalidated response; specifying a value of zero forces
+ the cache(s) to revalidate all responses. A client MAY also specify
+ the minimum time remaining before a response expires. Both of these
+ options increase constraints on the behavior of caches, and so cannot
+ further relax the cache's approximation of semantic transparency.
+
+ A client MAY also specify that it will accept stale responses, up to
+ some maximum amount of staleness. This loosens the constraints on the
+ caches, and so might violate the origin server's specified
+ constraints on semantic transparency, but might be necessary to
+ support disconnected operation, or high availability in the face of
+ poor connectivity.
+
+13.2 Expiration Model
+
+13.2.1 Server-Specified Expiration
+
+ HTTP caching works best when caches can entirely avoid making
+ requests to the origin server. The primary mechanism for avoiding
+ requests is for an origin server to provide an explicit expiration
+ time in the future, indicating that a response MAY be used to satisfy
+ subsequent requests. In other words, a cache can return a fresh
+ response without first contacting the server.
+
+ Our expectation is that servers will assign future explicit
+ expiration times to responses in the belief that the entity is not
+ likely to change, in a semantically significant way, before the
+ expiration time is reached. This normally preserves semantic
+ transparency, as long as the server's expiration times are carefully
+ chosen.
+
+
+
+
+
+
+Fielding, et al. Standards Track [Page 79]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ The expiration mechanism applies only to responses taken from a cache
+ and not to first-hand responses forwarded immediately to the
+ requesting client.
+
+ If an origin server wishes to force a semantically transparent cache
+ to validate every request, it MAY assign an explicit expiration time
+ in the past. This means that the response is always stale, and so the
+ cache SHOULD validate it before using it for subsequent requests. See
+ section 14.9.4 for a more restrictive way to force revalidation.
+
+ If an origin server wishes to force any HTTP/1.1 cache, no matter how
+ it is configured, to validate every request, it SHOULD use the "must-
+ revalidate" cache-control directive (see section 14.9).
+
+ Servers specify explicit expiration times using either the Expires
+ header, or the max-age directive of the Cache-Control header.
+
+ An expiration time cannot be used to force a user agent to refresh
+ its display or reload a resource; its semantics apply only to caching
+ mechanisms, and such mechanisms need only check a resource's
+ expiration status when a new request for that resource is initiated.
+ See section 13.13 for an explanation of the difference between caches
+ and history mechanisms.
+
+13.2.2 Heuristic Expiration
+
+ Since origin servers do not always provide explicit expiration times,
+ HTTP caches typically assign heuristic expiration times, employing
+ algorithms that use other header values (such as the Last-Modified
+ time) to estimate a plausible expiration time. The HTTP/1.1
+ specification does not provide specific algorithms, but does impose
+ worst-case constraints on their results. Since heuristic expiration
+ times might compromise semantic transparency, they ought to used
+ cautiously, and we encourage origin servers to provide explicit
+ expiration times as much as possible.
+
+13.2.3 Age Calculations
+
+ In order to know if a cached entry is fresh, a cache needs to know if
+ its age exceeds its freshness lifetime. We discuss how to calculate
+ the latter in section 13.2.4; this section describes how to calculate
+ the age of a response or cache entry.
+
+ In this discussion, we use the term "now" to mean "the current value
+ of the clock at the host performing the calculation." Hosts that use
+ HTTP, but especially hosts running origin servers and caches, SHOULD
+ use NTP [28] or some similar protocol to synchronize their clocks to
+ a globally accurate time standard.
+
+
+
+Fielding, et al. Standards Track [Page 80]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ HTTP/1.1 requires origin servers to send a Date header, if possible,
+ with every response, giving the time at which the response was
+ generated (see section 14.18). We use the term "date_value" to denote
+ the value of the Date header, in a form appropriate for arithmetic
+ operations.
+
+ HTTP/1.1 uses the Age response-header to convey the estimated age of
+ the response message when obtained from a cache. The Age field value
+ is the cache's estimate of the amount of time since the response was
+ generated or revalidated by the origin server.
+
+ In essence, the Age value is the sum of the time that the response
+ has been resident in each of the caches along the path from the
+ origin server, plus the amount of time it has been in transit along
+ network paths.
+
+ We use the term "age_value" to denote the value of the Age header, in
+ a form appropriate for arithmetic operations.
+
+ A response's age can be calculated in two entirely independent ways:
+
+ 1. now minus date_value, if the local clock is reasonably well
+ synchronized to the origin server's clock. If the result is
+ negative, the result is replaced by zero.
+
+ 2. age_value, if all of the caches along the response path
+ implement HTTP/1.1.
+
+ Given that we have two independent ways to compute the age of a
+ response when it is received, we can combine these as
+
+ corrected_received_age = max(now - date_value, age_value)
+
+ and as long as we have either nearly synchronized clocks or all-
+ HTTP/1.1 paths, one gets a reliable (conservative) result.
+
+ Because of network-imposed delays, some significant interval might
+ pass between the time that a server generates a response and the time
+ it is received at the next outbound cache or client. If uncorrected,
+ this delay could result in improperly low ages.
+
+ Because the request that resulted in the returned Age value must have
+ been initiated prior to that Age value's generation, we can correct
+ for delays imposed by the network by recording the time at which the
+ request was initiated. Then, when an Age value is received, it MUST
+ be interpreted relative to the time the request was initiated, not
+
+
+
+
+
+Fielding, et al. Standards Track [Page 81]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ the time that the response was received. This algorithm results in
+ conservative behavior no matter how much delay is experienced. So, we
+ compute:
+
+ corrected_initial_age = corrected_received_age
+ + (now - request_time)
+
+ where "request_time" is the time (according to the local clock) when
+ the request that elicited this response was sent.
+
+ Summary of age calculation algorithm, when a cache receives a
+ response:
+
+ /*
+ * age_value
+ * is the value of Age: header received by the cache with
+ * this response.
+ * date_value
+ * is the value of the origin server's Date: header
+ * request_time
+ * is the (local) time when the cache made the request
+ * that resulted in this cached response
+ * response_time
+ * is the (local) time when the cache received the
+ * response
+ * now
+ * is the current (local) time
+ */
+
+ apparent_age = max(0, response_time - date_value);
+ corrected_received_age = max(apparent_age, age_value);
+ response_delay = response_time - request_time;
+ corrected_initial_age = corrected_received_age + response_delay;
+ resident_time = now - response_time;
+ current_age = corrected_initial_age + resident_time;
+
+ The current_age of a cache entry is calculated by adding the amount
+ of time (in seconds) since the cache entry was last validated by the
+ origin server to the corrected_initial_age. When a response is
+ generated from a cache entry, the cache MUST include a single Age
+ header field in the response with a value equal to the cache entry's
+ current_age.
+
+ The presence of an Age header field in a response implies that a
+ response is not first-hand. However, the converse is not true, since
+ the lack of an Age header field in a response does not imply that the
+
+
+
+
+
+Fielding, et al. Standards Track [Page 82]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ response is first-hand unless all caches along the request path are
+ compliant with HTTP/1.1 (i.e., older HTTP caches did not implement
+ the Age header field).
+
+13.2.4 Expiration Calculations
+
+ In order to decide whether a response is fresh or stale, we need to
+ compare its freshness lifetime to its age. The age is calculated as
+ described in section 13.2.3; this section describes how to calculate
+ the freshness lifetime, and to determine if a response has expired.
+ In the discussion below, the values can be represented in any form
+ appropriate for arithmetic operations.
+
+ We use the term "expires_value" to denote the value of the Expires
+ header. We use the term "max_age_value" to denote an appropriate
+ value of the number of seconds carried by the "max-age" directive of
+ the Cache-Control header in a response (see section 14.9.3).
+
+ The max-age directive takes priority over Expires, so if max-age is
+ present in a response, the calculation is simply:
+
+ freshness_lifetime = max_age_value
+
+ Otherwise, if Expires is present in the response, the calculation is:
+
+ freshness_lifetime = expires_value - date_value
+
+ Note that neither of these calculations is vulnerable to clock skew,
+ since all of the information comes from the origin server.
+
+ If none of Expires, Cache-Control: max-age, or Cache-Control: s-
+ maxage (see section 14.9.3) appears in the response, and the response
+ does not include other restrictions on caching, the cache MAY compute
+ a freshness lifetime using a heuristic. The cache MUST attach Warning
+ 113 to any response whose age is more than 24 hours if such warning
+ has not already been added.
+
+ Also, if the response does have a Last-Modified time, the heuristic
+ expiration value SHOULD be no more than some fraction of the interval
+ since that time. A typical setting of this fraction might be 10%.
+
+ The calculation to determine if a response has expired is quite
+ simple:
+
+ response_is_fresh = (freshness_lifetime > current_age)
+
+
+
+
+
+
+Fielding, et al. Standards Track [Page 83]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+13.2.5 Disambiguating Expiration Values
+
+ Because expiration values are assigned optimistically, it is possible
+ for two caches to contain fresh values for the same resource that are
+ different.
+
+ If a client performing a retrieval receives a non-first-hand response
+ for a request that was already fresh in its own cache, and the Date
+ header in its existing cache entry is newer than the Date on the new
+ response, then the client MAY ignore the response. If so, it MAY
+ retry the request with a "Cache-Control: max-age=0" directive (see
+ section 14.9), to force a check with the origin server.
+
+ If a cache has two fresh responses for the same representation with
+ different validators, it MUST use the one with the more recent Date
+ header. This situation might arise because the cache is pooling
+ responses from other caches, or because a client has asked for a
+ reload or a revalidation of an apparently fresh cache entry.
+
+13.2.6 Disambiguating Multiple Responses
+
+ Because a client might be receiving responses via multiple paths, so
+ that some responses flow through one set of caches and other
+ responses flow through a different set of caches, a client might
+ receive responses in an order different from that in which the origin
+ server sent them. We would like the client to use the most recently
+ generated response, even if older responses are still apparently
+ fresh.
+
+ Neither the entity tag nor the expiration value can impose an
+ ordering on responses, since it is possible that a later response
+ intentionally carries an earlier expiration time. The Date values are
+ ordered to a granularity of one second.
+
+ When a client tries to revalidate a cache entry, and the response it
+ receives contains a Date header that appears to be older than the one
+ for the existing entry, then the client SHOULD repeat the request
+ unconditionally, and include
+
+ Cache-Control: max-age=0
+
+ to force any intermediate caches to validate their copies directly
+ with the origin server, or
+
+ Cache-Control: no-cache
+
+ to force any intermediate caches to obtain a new copy from the origin
+ server.
+
+
+
+Fielding, et al. Standards Track [Page 84]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ If the Date values are equal, then the client MAY use either response
+ (or MAY, if it is being extremely prudent, request a new response).
+ Servers MUST NOT depend on clients being able to choose
+ deterministically between responses generated during the same second,
+ if their expiration times overlap.
+
+13.3 Validation Model
+
+ When a cache has a stale entry that it would like to use as a
+ response to a client's request, it first has to check with the origin
+ server (or possibly an intermediate cache with a fresh response) to
+ see if its cached entry is still usable. We call this "validating"
+ the cache entry. Since we do not want to have to pay the overhead of
+ retransmitting the full response if the cached entry is good, and we
+ do not want to pay the overhead of an extra round trip if the cached
+ entry is invalid, the HTTP/1.1 protocol supports the use of
+ conditional methods.
+
+ The key protocol features for supporting conditional methods are
+ those concerned with "cache validators." When an origin server
+ generates a full response, it attaches some sort of validator to it,
+ which is kept with the cache entry. When a client (user agent or
+ proxy cache) makes a conditional request for a resource for which it
+ has a cache entry, it includes the associated validator in the
+ request.
+
+ The server then checks that validator against the current validator
+ for the entity, and, if they match (see section 13.3.3), it responds
+ with a special status code (usually, 304 (Not Modified)) and no
+ entity-body. Otherwise, it returns a full response (including
+ entity-body). Thus, we avoid transmitting the full response if the
+ validator matches, and we avoid an extra round trip if it does not
+ match.
+
+ In HTTP/1.1, a conditional request looks exactly the same as a normal
+ request for the same resource, except that it carries a special
+ header (which includes the validator) that implicitly turns the
+ method (usually, GET) into a conditional.
+
+ The protocol includes both positive and negative senses of cache-
+ validating conditions. That is, it is possible to request either that
+ a method be performed if and only if a validator matches or if and
+ only if no validators match.
+
+
+
+
+
+
+
+
+Fielding, et al. Standards Track [Page 85]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ Note: a response that lacks a validator may still be cached, and
+ served from cache until it expires, unless this is explicitly
+ prohibited by a cache-control directive. However, a cache cannot
+ do a conditional retrieval if it does not have a validator for the
+ entity, which means it will not be refreshable after it expires.
+
+13.3.1 Last-Modified Dates
+
+ The Last-Modified entity-header field value is often used as a cache
+ validator. In simple terms, a cache entry is considered to be valid
+ if the entity has not been modified since the Last-Modified value.
+
+13.3.2 Entity Tag Cache Validators
+
+ The ETag response-header field value, an entity tag, provides for an
+ "opaque" cache validator. This might allow more reliable validation
+ in situations where it is inconvenient to store modification dates,
+ where the one-second resolution of HTTP date values is not
+ sufficient, or where the origin server wishes to avoid certain
+ paradoxes that might arise from the use of modification dates.
+
+ Entity Tags are described in section 3.11. The headers used with
+ entity tags are described in sections 14.19, 14.24, 14.26 and 14.44.
+
+13.3.3 Weak and Strong Validators
+
+ Since both origin servers and caches will compare two validators to
+ decide if they represent the same or different entities, one normally
+ would expect that if the entity (the entity-body or any entity-
+ headers) changes in any way, then the associated validator would
+ change as well. If this is true, then we call this validator a
+ "strong validator."
+
+ However, there might be cases when a server prefers to change the
+ validator only on semantically significant changes, and not when
+ insignificant aspects of the entity change. A validator that does not
+ always change when the resource changes is a "weak validator."
+
+ Entity tags are normally "strong validators," but the protocol
+ provides a mechanism to tag an entity tag as "weak." One can think of
+ a strong validator as one that changes whenever the bits of an entity
+ changes, while a weak value changes whenever the meaning of an entity
+ changes. Alternatively, one can think of a strong validator as part
+ of an identifier for a specific entity, while a weak validator is
+ part of an identifier for a set of semantically equivalent entities.
+
+ Note: One example of a strong validator is an integer that is
+ incremented in stable storage every time an entity is changed.
+
+
+
+Fielding, et al. Standards Track [Page 86]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ An entity's modification time, if represented with one-second
+ resolution, could be a weak validator, since it is possible that
+ the resource might be modified twice during a single second.
+
+ Support for weak validators is optional. However, weak validators
+ allow for more efficient caching of equivalent objects; for
+ example, a hit counter on a site is probably good enough if it is
+ updated every few days or weeks, and any value during that period
+ is likely "good enough" to be equivalent.
+
+ A "use" of a validator is either when a client generates a request
+ and includes the validator in a validating header field, or when a
+ server compares two validators.
+
+ Strong validators are usable in any context. Weak validators are only
+ usable in contexts that do not depend on exact equality of an entity.
+ For example, either kind is usable for a conditional GET of a full
+ entity. However, only a strong validator is usable for a sub-range
+ retrieval, since otherwise the client might end up with an internally
+ inconsistent entity.
+
+ Clients MAY issue simple (non-subrange) GET requests with either weak
+ validators or strong validators. Clients MUST NOT use weak validators
+ in other forms of request.
+
+ The only function that the HTTP/1.1 protocol defines on validators is
+ comparison. There are two validator comparison functions, depending
+ on whether the comparison context allows the use of weak validators
+ or not:
+
+ - The strong comparison function: in order to be considered equal,
+ both validators MUST be identical in every way, and both MUST
+ NOT be weak.
+
+ - The weak comparison function: in order to be considered equal,
+ both validators MUST be identical in every way, but either or
+ both of them MAY be tagged as "weak" without affecting the
+ result.
+
+ An entity tag is strong unless it is explicitly tagged as weak.
+ Section 3.11 gives the syntax for entity tags.
+
+ A Last-Modified time, when used as a validator in a request, is
+ implicitly weak unless it is possible to deduce that it is strong,
+ using the following rules:
+
+ - The validator is being compared by an origin server to the
+ actual current validator for the entity and,
+
+
+
+Fielding, et al. Standards Track [Page 87]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ - That origin server reliably knows that the associated entity did
+ not change twice during the second covered by the presented
+ validator.
+
+ or
+
+ - The validator is about to be used by a client in an If-
+ Modified-Since or If-Unmodified-Since header, because the client
+ has a cache entry for the associated entity, and
+
+ - That cache entry includes a Date value, which gives the time
+ when the origin server sent the original response, and
+
+ - The presented Last-Modified time is at least 60 seconds before
+ the Date value.
+
+ or
+
+ - The validator is being compared by an intermediate cache to the
+ validator stored in its cache entry for the entity, and
+
+ - That cache entry includes a Date value, which gives the time
+ when the origin server sent the original response, and
+
+ - The presented Last-Modified time is at least 60 seconds before
+ the Date value.
+
+ This method relies on the fact that if two different responses were
+ sent by the origin server during the same second, but both had the
+ same Last-Modified time, then at least one of those responses would
+ have a Date value equal to its Last-Modified time. The arbitrary 60-
+ second limit guards against the possibility that the Date and Last-
+ Modified values are generated from different clocks, or at somewhat
+ different times during the preparation of the response. An
+ implementation MAY use a value larger than 60 seconds, if it is
+ believed that 60 seconds is too short.
+
+ If a client wishes to perform a sub-range retrieval on a value for
+ which it has only a Last-Modified time and no opaque validator, it
+ MAY do this only if the Last-Modified time is strong in the sense
+ described here.
+
+ A cache or origin server receiving a conditional request, other than
+ a full-body GET request, MUST use the strong comparison function to
+ evaluate the condition.
+
+ These rules allow HTTP/1.1 caches and clients to safely perform sub-
+ range retrievals on values that have been obtained from HTTP/1.0
+
+
+
+Fielding, et al. Standards Track [Page 88]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ servers.
+
+13.3.4 Rules for When to Use Entity Tags and Last-Modified Dates
+
+ We adopt a set of rules and recommendations for origin servers,
+ clients, and caches regarding when various validator types ought to
+ be used, and for what purposes.
+
+ HTTP/1.1 origin servers:
+
+ - SHOULD send an entity tag validator unless it is not feasible to
+ generate one.
+
+ - MAY send a weak entity tag instead of a strong entity tag, if
+ performance considerations support the use of weak entity tags,
+ or if it is unfeasible to send a strong entity tag.
+
+ - SHOULD send a Last-Modified value if it is feasible to send one,
+ unless the risk of a breakdown in semantic transparency that
+ could result from using this date in an If-Modified-Since header
+ would lead to serious problems.
+
+ In other words, the preferred behavior for an HTTP/1.1 origin server
+ is to send both a strong entity tag and a Last-Modified value.
+
+ In order to be legal, a strong entity tag MUST change whenever the
+ associated entity value changes in any way. A weak entity tag SHOULD
+ change whenever the associated entity changes in a semantically
+ significant way.
+
+ Note: in order to provide semantically transparent caching, an
+ origin server must avoid reusing a specific strong entity tag
+ value for two different entities, or reusing a specific weak
+ entity tag value for two semantically different entities. Cache
+ entries might persist for arbitrarily long periods, regardless of
+ expiration times, so it might be inappropriate to expect that a
+ cache will never again attempt to validate an entry using a
+ validator that it obtained at some point in the past.
+
+ HTTP/1.1 clients:
+
+ - If an entity tag has been provided by the origin server, MUST
+ use that entity tag in any cache-conditional request (using If-
+ Match or If-None-Match).
+
+ - If only a Last-Modified value has been provided by the origin
+ server, SHOULD use that value in non-subrange cache-conditional
+ requests (using If-Modified-Since).
+
+
+
+Fielding, et al. Standards Track [Page 89]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ - If only a Last-Modified value has been provided by an HTTP/1.0
+ origin server, MAY use that value in subrange cache-conditional
+ requests (using If-Unmodified-Since:). The user agent SHOULD
+ provide a way to disable this, in case of difficulty.
+
+ - If both an entity tag and a Last-Modified value have been
+ provided by the origin server, SHOULD use both validators in
+ cache-conditional requests. This allows both HTTP/1.0 and
+ HTTP/1.1 caches to respond appropriately.
+
+ An HTTP/1.1 origin server, upon receiving a conditional request that
+ includes both a Last-Modified date (e.g., in an If-Modified-Since or
+ If-Unmodified-Since header field) and one or more entity tags (e.g.,
+ in an If-Match, If-None-Match, or If-Range header field) as cache
+ validators, MUST NOT return a response status of 304 (Not Modified)
+ unless doing so is consistent with all of the conditional header
+ fields in the request.
+
+ An HTTP/1.1 caching proxy, upon receiving a conditional request that
+ includes both a Last-Modified date and one or more entity tags as
+ cache validators, MUST NOT return a locally cached response to the
+ client unless that cached response is consistent with all of the
+ conditional header fields in the request.
+
+ Note: The general principle behind these rules is that HTTP/1.1
+ servers and clients should transmit as much non-redundant
+ information as is available in their responses and requests.
+ HTTP/1.1 systems receiving this information will make the most
+ conservative assumptions about the validators they receive.
+
+ HTTP/1.0 clients and caches will ignore entity tags. Generally,
+ last-modified values received or used by these systems will
+ support transparent and efficient caching, and so HTTP/1.1 origin
+ servers should provide Last-Modified values. In those rare cases
+ where the use of a Last-Modified value as a validator by an
+ HTTP/1.0 system could result in a serious problem, then HTTP/1.1
+ origin servers should not provide one.
+
+13.3.5 Non-validating Conditionals
+
+ The principle behind entity tags is that only the service author
+ knows the semantics of a resource well enough to select an
+ appropriate cache validation mechanism, and the specification of any
+ validator comparison function more complex than byte-equality would
+ open up a can of worms. Thus, comparisons of any other headers
+ (except Last-Modified, for compatibility with HTTP/1.0) are never
+ used for purposes of validating a cache entry.
+
+
+
+
+Fielding, et al. Standards Track [Page 90]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+13.4 Response Cacheability
+
+ Unless specifically constrained by a cache-control (section 14.9)
+ directive, a caching system MAY always store a successful response
+ (see section 13.8) as a cache entry, MAY return it without validation
+ if it is fresh, and MAY return it after successful validation. If
+ there is neither a cache validator nor an explicit expiration time
+ associated with a response, we do not expect it to be cached, but
+ certain caches MAY violate this expectation (for example, when little
+ or no network connectivity is available). A client can usually detect
+ that such a response was taken from a cache by comparing the Date
+ header to the current time.
+
+ Note: some HTTP/1.0 caches are known to violate this expectation
+ without providing any Warning.
+
+ However, in some cases it might be inappropriate for a cache to
+ retain an entity, or to return it in response to a subsequent
+ request. This might be because absolute semantic transparency is
+ deemed necessary by the service author, or because of security or
+ privacy considerations. Certain cache-control directives are
+ therefore provided so that the server can indicate that certain
+ resource entities, or portions thereof, are not to be cached
+ regardless of other considerations.
+
+ Note that section 14.8 normally prevents a shared cache from saving
+ and returning a response to a previous request if that request
+ included an Authorization header.
+
+ A response received with a status code of 200, 203, 206, 300, 301 or
+ 410 MAY be stored by a cache and used in reply to a subsequent
+ request, subject to the expiration mechanism, unless a cache-control
+ directive prohibits caching. However, a cache that does not support
+ the Range and Content-Range headers MUST NOT cache 206 (Partial
+ Content) responses.
+
+ A response received with any other status code (e.g. status codes 302
+ and 307) MUST NOT be returned in a reply to a subsequent request
+ unless there are cache-control directives or another header(s) that
+ explicitly allow it. For example, these include the following: an
+ Expires header (section 14.21); a "max-age", "s-maxage", "must-
+ revalidate", "proxy-revalidate", "public" or "private" cache-control
+ directive (section 14.9).
+
+
+
+
+
+
+
+
+Fielding, et al. Standards Track [Page 91]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+13.5 Constructing Responses From Caches
+
+ The purpose of an HTTP cache is to store information received in
+ response to requests for use in responding to future requests. In
+ many cases, a cache simply returns the appropriate parts of a
+ response to the requester. However, if the cache holds a cache entry
+ based on a previous response, it might have to combine parts of a new
+ response with what is held in the cache entry.
+
+13.5.1 End-to-end and Hop-by-hop Headers
+
+ For the purpose of defining the behavior of caches and non-caching
+ proxies, we divide HTTP headers into two categories:
+
+ - End-to-end headers, which are transmitted to the ultimate
+ recipient of a request or response. End-to-end headers in
+ responses MUST be stored as part of a cache entry and MUST be
+ transmitted in any response formed from a cache entry.
+
+ - Hop-by-hop headers, which are meaningful only for a single
+ transport-level connection, and are not stored by caches or
+ forwarded by proxies.
+
+ The following HTTP/1.1 headers are hop-by-hop headers:
+
+ - Connection
+ - Keep-Alive
+ - Proxy-Authenticate
+ - Proxy-Authorization
+ - TE
+ - Trailers
+ - Transfer-Encoding
+ - Upgrade
+
+ All other headers defined by HTTP/1.1 are end-to-end headers.
+
+ Other hop-by-hop headers MUST be listed in a Connection header,
+ (section 14.10) to be introduced into HTTP/1.1 (or later).
+
+13.5.2 Non-modifiable Headers
+
+ Some features of the HTTP/1.1 protocol, such as Digest
+ Authentication, depend on the value of certain end-to-end headers. A
+ transparent proxy SHOULD NOT modify an end-to-end header unless the
+ definition of that header requires or specifically allows that.
+
+
+
+
+
+
+Fielding, et al. Standards Track [Page 92]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ A transparent proxy MUST NOT modify any of the following fields in a
+ request or response, and it MUST NOT add any of these fields if not
+ already present:
+
+ - Content-Location
+
+ - Content-MD5
+
+ - ETag
+
+ - Last-Modified
+
+ A transparent proxy MUST NOT modify any of the following fields in a
+ response:
+
+ - Expires
+
+ but it MAY add any of these fields if not already present. If an
+ Expires header is added, it MUST be given a field-value identical to
+ that of the Date header in that response.
+
+ A proxy MUST NOT modify or add any of the following fields in a
+ message that contains the no-transform cache-control directive, or in
+ any request:
+
+ - Content-Encoding
+
+ - Content-Range
+
+ - Content-Type
+
+ A non-transparent proxy MAY modify or add these fields to a message
+ that does not include no-transform, but if it does so, it MUST add a
+ Warning 214 (Transformation applied) if one does not already appear
+ in the message (see section 14.46).
+
+ Warning: unnecessary modification of end-to-end headers might
+ cause authentication failures if stronger authentication
+ mechanisms are introduced in later versions of HTTP. Such
+ authentication mechanisms MAY rely on the values of header fields
+ not listed here.
+
+ The Content-Length field of a request or response is added or deleted
+ according to the rules in section 4.4. A transparent proxy MUST
+ preserve the entity-length (section 7.2.2) of the entity-body,
+ although it MAY change the transfer-length (section 4.4).
+
+
+
+
+
+Fielding, et al. Standards Track [Page 93]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+13.5.3 Combining Headers
+
+ When a cache makes a validating request to a server, and the server
+ provides a 304 (Not Modified) response or a 206 (Partial Content)
+ response, the cache then constructs a response to send to the
+ requesting client.
+
+ If the status code is 304 (Not Modified), the cache uses the entity-
+ body stored in the cache entry as the entity-body of this outgoing
+ response. If the status code is 206 (Partial Content) and the ETag or
+ Last-Modified headers match exactly, the cache MAY combine the
+ contents stored in the cache entry with the new contents received in
+ the response and use the result as the entity-body of this outgoing
+ response, (see 13.5.4).
+
+ The end-to-end headers stored in the cache entry are used for the
+ constructed response, except that
+
+ - any stored Warning headers with warn-code 1xx (see section
+ 14.46) MUST be deleted from the cache entry and the forwarded
+ response.
+
+ - any stored Warning headers with warn-code 2xx MUST be retained
+ in the cache entry and the forwarded response.
+
+ - any end-to-end headers provided in the 304 or 206 response MUST
+ replace the corresponding headers from the cache entry.
+
+ Unless the cache decides to remove the cache entry, it MUST also
+ replace the end-to-end headers stored with the cache entry with
+ corresponding headers received in the incoming response, except for
+ Warning headers as described immediately above. If a header field-
+ name in the incoming response matches more than one header in the
+ cache entry, all such old headers MUST be replaced.
+
+ In other words, the set of end-to-end headers received in the
+ incoming response overrides all corresponding end-to-end headers
+ stored with the cache entry (except for stored Warning headers with
+ warn-code 1xx, which are deleted even if not overridden).
+
+ Note: this rule allows an origin server to use a 304 (Not
+ Modified) or a 206 (Partial Content) response to update any header
+ associated with a previous response for the same entity or sub-
+ ranges thereof, although it might not always be meaningful or
+ correct to do so. This rule does not allow an origin server to use
+ a 304 (Not Modified) or a 206 (Partial Content) response to
+ entirely delete a header that it had provided with a previous
+ response.
+
+
+
+Fielding, et al. Standards Track [Page 94]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+13.5.4 Combining Byte Ranges
+
+ A response might transfer only a subrange of the bytes of an entity-
+ body, either because the request included one or more Range
+ specifications, or because a connection was broken prematurely. After
+ several such transfers, a cache might have received several ranges of
+ the same entity-body.
+
+ If a cache has a stored non-empty set of subranges for an entity, and
+ an incoming response transfers another subrange, the cache MAY
+ combine the new subrange with the existing set if both the following
+ conditions are met:
+
+ - Both the incoming response and the cache entry have a cache
+ validator.
+
+ - The two cache validators match using the strong comparison
+ function (see section 13.3.3).
+
+ If either requirement is not met, the cache MUST use only the most
+ recent partial response (based on the Date values transmitted with
+ every response, and using the incoming response if these values are
+ equal or missing), and MUST discard the other partial information.
+
+13.6 Caching Negotiated Responses
+
+ Use of server-driven content negotiation (section 12.1), as indicated
+ by the presence of a Vary header field in a response, alters the
+ conditions and procedure by which a cache can use the response for
+ subsequent requests. See section 14.44 for use of the Vary header
+ field by servers.
+
+ A server SHOULD use the Vary header field to inform a cache of what
+ request-header fields were used to select among multiple
+ representations of a cacheable response subject to server-driven
+ negotiation. The set of header fields named by the Vary field value
+ is known as the "selecting" request-headers.
+
+ When the cache receives a subsequent request whose Request-URI
+ specifies one or more cache entries including a Vary header field,
+ the cache MUST NOT use such a cache entry to construct a response to
+ the new request unless all of the selecting request-headers present
+ in the new request match the corresponding stored request-headers in
+ the original request.
+
+ The selecting request-headers from two requests are defined to match
+ if and only if the selecting request-headers in the first request can
+ be transformed to the selecting request-headers in the second request
+
+
+
+Fielding, et al. Standards Track [Page 95]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ by adding or removing linear white space (LWS) at places where this
+ is allowed by the corresponding BNF, and/or combining multiple
+ message-header fields with the same field name following the rules
+ about message headers in section 4.2.
+
+ A Vary header field-value of "*" always fails to match and subsequent
+ requests on that resource can only be properly interpreted by the
+ origin server.
+
+ If the selecting request header fields for the cached entry do not
+ match the selecting request header fields of the new request, then
+ the cache MUST NOT use a cached entry to satisfy the request unless
+ it first relays the new request to the origin server in a conditional
+ request and the server responds with 304 (Not Modified), including an
+ entity tag or Content-Location that indicates the entity to be used.
+
+ If an entity tag was assigned to a cached representation, the
+ forwarded request SHOULD be conditional and include the entity tags
+ in an If-None-Match header field from all its cache entries for the
+ resource. This conveys to the server the set of entities currently
+ held by the cache, so that if any one of these entities matches the
+ requested entity, the server can use the ETag header field in its 304
+ (Not Modified) response to tell the cache which entry is appropriate.
+ If the entity-tag of the new response matches that of an existing
+ entry, the new response SHOULD be used to update the header fields of
+ the existing entry, and the result MUST be returned to the client.
+
+ If any of the existing cache entries contains only partial content
+ for the associated entity, its entity-tag SHOULD NOT be included in
+ the If-None-Match header field unless the request is for a range that
+ would be fully satisfied by that entry.
+
+ If a cache receives a successful response whose Content-Location
+ field matches that of an existing cache entry for the same Request-
+ ]URI, whose entity-tag differs from that of the existing entry, and
+ whose Date is more recent than that of the existing entry, the
+ existing entry SHOULD NOT be returned in response to future requests
+ and SHOULD be deleted from the cache.
+
+13.7 Shared and Non-Shared Caches
+
+ For reasons of security and privacy, it is necessary to make a
+ distinction between "shared" and "non-shared" caches. A non-shared
+ cache is one that is accessible only to a single user. Accessibility
+ in this case SHOULD be enforced by appropriate security mechanisms.
+ All other caches are considered to be "shared." Other sections of
+
+
+
+
+
+Fielding, et al. Standards Track [Page 96]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ this specification place certain constraints on the operation of
+ shared caches in order to prevent loss of privacy or failure of
+ access controls.
+
+13.8 Errors or Incomplete Response Cache Behavior
+
+ A cache that receives an incomplete response (for example, with fewer
+ bytes of data than specified in a Content-Length header) MAY store
+ the response. However, the cache MUST treat this as a partial
+ response. Partial responses MAY be combined as described in section
+ 13.5.4; the result might be a full response or might still be
+ partial. A cache MUST NOT return a partial response to a client
+ without explicitly marking it as such, using the 206 (Partial
+ Content) status code. A cache MUST NOT return a partial response
+ using a status code of 200 (OK).
+
+ If a cache receives a 5xx response while attempting to revalidate an
+ entry, it MAY either forward this response to the requesting client,
+ or act as if the server failed to respond. In the latter case, it MAY
+ return a previously received response unless the cached entry
+ includes the "must-revalidate" cache-control directive (see section
+ 14.9).
+
+13.9 Side Effects of GET and HEAD
+
+ Unless the origin server explicitly prohibits the caching of their
+ responses, the application of GET and HEAD methods to any resources
+ SHOULD NOT have side effects that would lead to erroneous behavior if
+ these responses are taken from a cache. They MAY still have side
+ effects, but a cache is not required to consider such side effects in
+ its caching decisions. Caches are always expected to observe an
+ origin server's explicit restrictions on caching.
+
+ We note one exception to this rule: since some applications have
+ traditionally used GETs and HEADs with query URLs (those containing a
+ "?" in the rel_path part) to perform operations with significant side
+ effects, caches MUST NOT treat responses to such URIs as fresh unless
+ the server provides an explicit expiration time. This specifically
+ means that responses from HTTP/1.0 servers for such URIs SHOULD NOT
+ be taken from a cache. See section 9.1.1 for related information.
+
+13.10 Invalidation After Updates or Deletions
+
+ The effect of certain methods performed on a resource at the origin
+ server might cause one or more existing cache entries to become non-
+ transparently invalid. That is, although they might continue to be
+ "fresh," they do not accurately reflect what the origin server would
+ return for a new request on that resource.
+
+
+
+Fielding, et al. Standards Track [Page 97]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ There is no way for the HTTP protocol to guarantee that all such
+ cache entries are marked invalid. For example, the request that
+ caused the change at the origin server might not have gone through
+ the proxy where a cache entry is stored. However, several rules help
+ reduce the likelihood of erroneous behavior.
+
+ In this section, the phrase "invalidate an entity" means that the
+ cache will either remove all instances of that entity from its
+ storage, or will mark these as "invalid" and in need of a mandatory
+ revalidation before they can be returned in response to a subsequent
+ request.
+
+ Some HTTP methods MUST cause a cache to invalidate an entity. This is
+ either the entity referred to by the Request-URI, or by the Location
+ or Content-Location headers (if present). These methods are:
+
+ - PUT
+
+ - DELETE
+
+ - POST
+
+ In order to prevent denial of service attacks, an invalidation based
+ on the URI in a Location or Content-Location header MUST only be
+ performed if the host part is the same as in the Request-URI.
+
+ A cache that passes through requests for methods it does not
+ understand SHOULD invalidate any entities referred to by the
+ Request-URI.
+
+13.11 Write-Through Mandatory
+
+ All methods that might be expected to cause modifications to the
+ origin server's resources MUST be written through to the origin
+ server. This currently includes all methods except for GET and HEAD.
+ A cache MUST NOT reply to such a request from a client before having
+ transmitted the request to the inbound server, and having received a
+ corresponding response from the inbound server. This does not prevent
+ a proxy cache from sending a 100 (Continue) response before the
+ inbound server has sent its final reply.
+
+ The alternative (known as "write-back" or "copy-back" caching) is not
+ allowed in HTTP/1.1, due to the difficulty of providing consistent
+ updates and the problems arising from server, cache, or network
+ failure prior to write-back.
+
+
+
+
+
+
+Fielding, et al. Standards Track [Page 98]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+13.12 Cache Replacement
+
+ If a new cacheable (see sections 14.9.2, 13.2.5, 13.2.6 and 13.8)
+ response is received from a resource while any existing responses for
+ the same resource are cached, the cache SHOULD use the new response
+ to reply to the current request. It MAY insert it into cache storage
+ and MAY, if it meets all other requirements, use it to respond to any
+ future requests that would previously have caused the old response to
+ be returned. If it inserts the new response into cache storage the
+ rules in section 13.5.3 apply.
+
+ Note: a new response that has an older Date header value than
+ existing cached responses is not cacheable.
+
+13.13 History Lists
+
+ User agents often have history mechanisms, such as "Back" buttons and
+ history lists, which can be used to redisplay an entity retrieved
+ earlier in a session.
+
+ History mechanisms and caches are different. In particular history
+ mechanisms SHOULD NOT try to show a semantically transparent view of
+ the current state of a resource. Rather, a history mechanism is meant
+ to show exactly what the user saw at the time when the resource was
+ retrieved.
+
+ By default, an expiration time does not apply to history mechanisms.
+ If the entity is still in storage, a history mechanism SHOULD display
+ it even if the entity has expired, unless the user has specifically
+ configured the agent to refresh expired history documents.
+
+ This is not to be construed to prohibit the history mechanism from
+ telling the user that a view might be stale.
+
+ Note: if history list mechanisms unnecessarily prevent users from
+ viewing stale resources, this will tend to force service authors
+ to avoid using HTTP expiration controls and cache controls when
+ they would otherwise like to. Service authors may consider it
+ important that users not be presented with error messages or
+ warning messages when they use navigation controls (such as BACK)
+ to view previously fetched resources. Even though sometimes such
+ resources ought not to cached, or ought to expire quickly, user
+ interface considerations may force service authors to resort to
+ other means of preventing caching (e.g. "once-only" URLs) in order
+ not to suffer the effects of improperly functioning history
+ mechanisms.
+
+
+
+
+
+Fielding, et al. Standards Track [Page 99]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+14 Header Field Definitions
+
+ This section defines the syntax and semantics of all standard
+ HTTP/1.1 header fields. For entity-header fields, both sender and
+ recipient refer to either the client or the server, depending on who
+ sends and who receives the entity.
+
+14.1 Accept
+
+ The Accept request-header field can be used to specify certain media
+ types which are acceptable for the response. Accept headers can be
+ used to indicate that the request is specifically limited to a small
+ set of desired types, as in the case of a request for an in-line
+ image.
+
+ Accept = "Accept" ":"
+ #( media-range [ accept-params ] )
+
+ media-range = ( "*/*"
+ | ( type "/" "*" )
+ | ( type "/" subtype )
+ ) *( ";" parameter )
+ accept-params = ";" "q" "=" qvalue *( accept-extension )
+ accept-extension = ";" token [ "=" ( token | quoted-string ) ]
+
+ The asterisk "*" character is used to group media types into ranges,
+ with "*/*" indicating all media types and "type/*" indicating all
+ subtypes of that type. The media-range MAY include media type
+ parameters that are applicable to that range.
+
+ Each media-range MAY be followed by one or more accept-params,
+ beginning with the "q" parameter for indicating a relative quality
+ factor. The first "q" parameter (if any) separates the media-range
+ parameter(s) from the accept-params. Quality factors allow the user
+ or user agent to indicate the relative degree of preference for that
+ media-range, using the qvalue scale from 0 to 1 (section 3.9). The
+ default value is q=1.
+
+ Note: Use of the "q" parameter name to separate media type
+ parameters from Accept extension parameters is due to historical
+ practice. Although this prevents any media type parameter named
+ "q" from being used with a media range, such an event is believed
+ to be unlikely given the lack of any "q" parameters in the IANA
+ media type registry and the rare usage of any media type
+ parameters in Accept. Future media types are discouraged from
+ registering any parameter named "q".
+
+
+
+
+
+Fielding, et al. Standards Track [Page 100]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ The example
+
+ Accept: audio/*; q=0.2, audio/basic
+
+ SHOULD be interpreted as "I prefer audio/basic, but send me any audio
+ type if it is the best available after an 80% mark-down in quality."
+
+ If no Accept header field is present, then it is assumed that the
+ client accepts all media types. If an Accept header field is present,
+ and if the server cannot send a response which is acceptable
+ according to the combined Accept field value, then the server SHOULD
+ send a 406 (not acceptable) response.
+
+ A more elaborate example is
+
+ Accept: text/plain; q=0.5, text/html,
+ text/x-dvi; q=0.8, text/x-c
+
+ Verbally, this would be interpreted as "text/html and text/x-c are
+ the preferred media types, but if they do not exist, then send the
+ text/x-dvi entity, and if that does not exist, send the text/plain
+ entity."
+
+ Media ranges can be overridden by more specific media ranges or
+ specific media types. If more than one media range applies to a given
+ type, the most specific reference has precedence. For example,
+
+ Accept: text/*, text/html, text/html;level=1, */*
+
+ have the following precedence:
+
+ 1) text/html;level=1
+ 2) text/html
+ 3) text/*
+ 4) */*
+
+ The media type quality factor associated with a given type is
+ determined by finding the media range with the highest precedence
+ which matches that type. For example,
+
+ Accept: text/*;q=0.3, text/html;q=0.7, text/html;level=1,
+ text/html;level=2;q=0.4, */*;q=0.5
+
+ would cause the following values to be associated:
+
+ text/html;level=1 = 1
+ text/html = 0.7
+ text/plain = 0.3
+
+
+
+Fielding, et al. Standards Track [Page 101]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ image/jpeg = 0.5
+ text/html;level=2 = 0.4
+ text/html;level=3 = 0.7
+
+ Note: A user agent might be provided with a default set of quality
+ values for certain media ranges. However, unless the user agent is
+ a closed system which cannot interact with other rendering agents,
+ this default set ought to be configurable by the user.
+
+14.2 Accept-Charset
+
+ The Accept-Charset request-header field can be used to indicate what
+ character sets are acceptable for the response. This field allows
+ clients capable of understanding more comprehensive or special-
+ purpose character sets to signal that capability to a server which is
+ capable of representing documents in those character sets.
+
+ Accept-Charset = "Accept-Charset" ":"
+ 1#( ( charset | "*" )[ ";" "q" "=" qvalue ] )
+
+
+ Character set values are described in section 3.4. Each charset MAY
+ be given an associated quality value which represents the user's
+ preference for that charset. The default value is q=1. An example is
+
+ Accept-Charset: iso-8859-5, unicode-1-1;q=0.8
+
+ The special value "*", if present in the Accept-Charset field,
+ matches every character set (including ISO-8859-1) which is not
+ mentioned elsewhere in the Accept-Charset field. If no "*" is present
+ in an Accept-Charset field, then all character sets not explicitly
+ mentioned get a quality value of 0, except for ISO-8859-1, which gets
+ a quality value of 1 if not explicitly mentioned.
+
+ If no Accept-Charset header is present, the default is that any
+ character set is acceptable. If an Accept-Charset header is present,
+ and if the server cannot send a response which is acceptable
+ according to the Accept-Charset header, then the server SHOULD send
+ an error response with the 406 (not acceptable) status code, though
+ the sending of an unacceptable response is also allowed.
+
+14.3 Accept-Encoding
+
+ The Accept-Encoding request-header field is similar to Accept, but
+ restricts the content-codings (section 3.5) that are acceptable in
+ the response.
+
+ Accept-Encoding = "Accept-Encoding" ":"
+
+
+
+Fielding, et al. Standards Track [Page 102]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ 1#( codings [ ";" "q" "=" qvalue ] )
+ codings = ( content-coding | "*" )
+
+ Examples of its use are:
+
+ Accept-Encoding: compress, gzip
+ Accept-Encoding:
+ Accept-Encoding: *
+ Accept-Encoding: compress;q=0.5, gzip;q=1.0
+ Accept-Encoding: gzip;q=1.0, identity; q=0.5, *;q=0
+
+ A server tests whether a content-coding is acceptable, according to
+ an Accept-Encoding field, using these rules:
+
+ 1. If the content-coding is one of the content-codings listed in
+ the Accept-Encoding field, then it is acceptable, unless it is
+ accompanied by a qvalue of 0. (As defined in section 3.9, a
+ qvalue of 0 means "not acceptable.")
+
+ 2. The special "*" symbol in an Accept-Encoding field matches any
+ available content-coding not explicitly listed in the header
+ field.
+
+ 3. If multiple content-codings are acceptable, then the acceptable
+ content-coding with the highest non-zero qvalue is preferred.
+
+ 4. The "identity" content-coding is always acceptable, unless
+ specifically refused because the Accept-Encoding field includes
+ "identity;q=0", or because the field includes "*;q=0" and does
+ not explicitly include the "identity" content-coding. If the
+ Accept-Encoding field-value is empty, then only the "identity"
+ encoding is acceptable.
+
+ If an Accept-Encoding field is present in a request, and if the
+ server cannot send a response which is acceptable according to the
+ Accept-Encoding header, then the server SHOULD send an error response
+ with the 406 (Not Acceptable) status code.
+
+ If no Accept-Encoding field is present in a request, the server MAY
+ assume that the client will accept any content coding. In this case,
+ if "identity" is one of the available content-codings, then the
+ server SHOULD use the "identity" content-coding, unless it has
+ additional information that a different content-coding is meaningful
+ to the client.
+
+ Note: If the request does not include an Accept-Encoding field,
+ and if the "identity" content-coding is unavailable, then
+ content-codings commonly understood by HTTP/1.0 clients (i.e.,
+
+
+
+Fielding, et al. Standards Track [Page 103]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ "gzip" and "compress") are preferred; some older clients
+ improperly display messages sent with other content-codings. The
+ server might also make this decision based on information about
+ the particular user-agent or client.
+
+ Note: Most HTTP/1.0 applications do not recognize or obey qvalues
+ associated with content-codings. This means that qvalues will not
+ work and are not permitted with x-gzip or x-compress.
+
+14.4 Accept-Language
+
+ The Accept-Language request-header field is similar to Accept, but
+ restricts the set of natural languages that are preferred as a
+ response to the request. Language tags are defined in section 3.10.
+
+ Accept-Language = "Accept-Language" ":"
+ 1#( language-range [ ";" "q" "=" qvalue ] )
+ language-range = ( ( 1*8ALPHA *( "-" 1*8ALPHA ) ) | "*" )
+
+ Each language-range MAY be given an associated quality value which
+ represents an estimate of the user's preference for the languages
+ specified by that range. The quality value defaults to "q=1". For
+ example,
+
+ Accept-Language: da, en-gb;q=0.8, en;q=0.7
+
+ would mean: "I prefer Danish, but will accept British English and
+ other types of English." A language-range matches a language-tag if
+ it exactly equals the tag, or if it exactly equals a prefix of the
+ tag such that the first tag character following the prefix is "-".
+ The special range "*", if present in the Accept-Language field,
+ matches every tag not matched by any other range present in the
+ Accept-Language field.
+
+ Note: This use of a prefix matching rule does not imply that
+ language tags are assigned to languages in such a way that it is
+ always true that if a user understands a language with a certain
+ tag, then this user will also understand all languages with tags
+ for which this tag is a prefix. The prefix rule simply allows the
+ use of prefix tags if this is the case.
+
+ The language quality factor assigned to a language-tag by the
+ Accept-Language field is the quality value of the longest language-
+ range in the field that matches the language-tag. If no language-
+ range in the field matches the tag, the language quality factor
+ assigned is 0. If no Accept-Language header is present in the
+ request, the server
+
+
+
+
+Fielding, et al. Standards Track [Page 104]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ SHOULD assume that all languages are equally acceptable. If an
+ Accept-Language header is present, then all languages which are
+ assigned a quality factor greater than 0 are acceptable.
+
+ It might be contrary to the privacy expectations of the user to send
+ an Accept-Language header with the complete linguistic preferences of
+ the user in every request. For a discussion of this issue, see
+ section 15.1.4.
+
+ As intelligibility is highly dependent on the individual user, it is
+ recommended that client applications make the choice of linguistic
+ preference available to the user. If the choice is not made
+ available, then the Accept-Language header field MUST NOT be given in
+ the request.
+
+ Note: When making the choice of linguistic preference available to
+ the user, we remind implementors of the fact that users are not
+ familiar with the details of language matching as described above,
+ and should provide appropriate guidance. As an example, users
+ might assume that on selecting "en-gb", they will be served any
+ kind of English document if British English is not available. A
+ user agent might suggest in such a case to add "en" to get the
+ best matching behavior.
+
+14.5 Accept-Ranges
+
+ The Accept-Ranges response-header field allows the server to
+ indicate its acceptance of range requests for a resource:
+
+ Accept-Ranges = "Accept-Ranges" ":" acceptable-ranges
+ acceptable-ranges = 1#range-unit | "none"
+
+ Origin servers that accept byte-range requests MAY send
+
+ Accept-Ranges: bytes
+
+ but are not required to do so. Clients MAY generate byte-range
+ requests without having received this header for the resource
+ involved. Range units are defined in section 3.12.
+
+ Servers that do not accept any kind of range request for a
+ resource MAY send
+
+ Accept-Ranges: none
+
+ to advise the client not to attempt a range request.
+
+
+
+
+
+Fielding, et al. Standards Track [Page 105]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+14.6 Age
+
+ The Age response-header field conveys the sender's estimate of the
+ amount of time since the response (or its revalidation) was
+ generated at the origin server. A cached response is "fresh" if
+ its age does not exceed its freshness lifetime. Age values are
+ calculated as specified in section 13.2.3.
+
+ Age = "Age" ":" age-value
+ age-value = delta-seconds
+
+ Age values are non-negative decimal integers, representing time in
+ seconds.
+
+ If a cache receives a value larger than the largest positive
+ integer it can represent, or if any of its age calculations
+ overflows, it MUST transmit an Age header with a value of
+ 2147483648 (2^31). An HTTP/1.1 server that includes a cache MUST
+ include an Age header field in every response generated from its
+ own cache. Caches SHOULD use an arithmetic type of at least 31
+ bits of range.
+
+14.7 Allow
+
+ The Allow entity-header field lists the set of methods supported
+ by the resource identified by the Request-URI. The purpose of this
+ field is strictly to inform the recipient of valid methods
+ associated with the resource. An Allow header field MUST be
+ present in a 405 (Method Not Allowed) response.
+
+ Allow = "Allow" ":" #Method
+
+ Example of use:
+
+ Allow: GET, HEAD, PUT
+
+ This field cannot prevent a client from trying other methods.
+ However, the indications given by the Allow header field value
+ SHOULD be followed. The actual set of allowed methods is defined
+ by the origin server at the time of each request.
+
+ The Allow header field MAY be provided with a PUT request to
+ recommend the methods to be supported by the new or modified
+ resource. The server is not required to support these methods and
+ SHOULD include an Allow header in the response giving the actual
+ supported methods.
+
+
+
+
+
+Fielding, et al. Standards Track [Page 106]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ A proxy MUST NOT modify the Allow header field even if it does not
+ understand all the methods specified, since the user agent might
+ have other means of communicating with the origin server.
+
+14.8 Authorization
+
+ A user agent that wishes to authenticate itself with a server--
+ usually, but not necessarily, after receiving a 401 response--does
+ so by including an Authorization request-header field with the
+ request. The Authorization field value consists of credentials
+ containing the authentication information of the user agent for
+ the realm of the resource being requested.
+
+ Authorization = "Authorization" ":" credentials
+
+ HTTP access authentication is described in "HTTP Authentication:
+ Basic and Digest Access Authentication" [43]. If a request is
+ authenticated and a realm specified, the same credentials SHOULD
+ be valid for all other requests within this realm (assuming that
+ the authentication scheme itself does not require otherwise, such
+ as credentials that vary according to a challenge value or using
+ synchronized clocks).
+
+ When a shared cache (see section 13.7) receives a request
+ containing an Authorization field, it MUST NOT return the
+ corresponding response as a reply to any other request, unless one
+ of the following specific exceptions holds:
+
+ 1. If the response includes the "s-maxage" cache-control
+ directive, the cache MAY use that response in replying to a
+ subsequent request. But (if the specified maximum age has
+ passed) a proxy cache MUST first revalidate it with the origin
+ server, using the request-headers from the new request to allow
+ the origin server to authenticate the new request. (This is the
+ defined behavior for s-maxage.) If the response includes "s-
+ maxage=0", the proxy MUST always revalidate it before re-using
+ it.
+
+ 2. If the response includes the "must-revalidate" cache-control
+ directive, the cache MAY use that response in replying to a
+ subsequent request. But if the response is stale, all caches
+ MUST first revalidate it with the origin server, using the
+ request-headers from the new request to allow the origin server
+ to authenticate the new request.
+
+ 3. If the response includes the "public" cache-control directive,
+ it MAY be returned in reply to any subsequent request.
+
+
+
+
+Fielding, et al. Standards Track [Page 107]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+14.9 Cache-Control
+
+ The Cache-Control general-header field is used to specify directives
+ that MUST be obeyed by all caching mechanisms along the
+ request/response chain. The directives specify behavior intended to
+ prevent caches from adversely interfering with the request or
+ response. These directives typically override the default caching
+ algorithms. Cache directives are unidirectional in that the presence
+ of a directive in a request does not imply that the same directive is
+ to be given in the response.
+
+ Note that HTTP/1.0 caches might not implement Cache-Control and
+ might only implement Pragma: no-cache (see section 14.32).
+
+ Cache directives MUST be passed through by a proxy or gateway
+ application, regardless of their significance to that application,
+ since the directives might be applicable to all recipients along the
+ request/response chain. It is not possible to specify a cache-
+ directive for a specific cache.
+
+ Cache-Control = "Cache-Control" ":" 1#cache-directive
+
+ cache-directive = cache-request-directive
+ | cache-response-directive
+
+ cache-request-directive =
+ "no-cache" ; Section 14.9.1
+ | "no-store" ; Section 14.9.2
+ | "max-age" "=" delta-seconds ; Section 14.9.3, 14.9.4
+ | "max-stale" [ "=" delta-seconds ] ; Section 14.9.3
+ | "min-fresh" "=" delta-seconds ; Section 14.9.3
+ | "no-transform" ; Section 14.9.5
+ | "only-if-cached" ; Section 14.9.4
+ | cache-extension ; Section 14.9.6
+
+ cache-response-directive =
+ "public" ; Section 14.9.1
+ | "private" [ "=" <"> 1#field-name <"> ] ; Section 14.9.1
+ | "no-cache" [ "=" <"> 1#field-name <"> ]; Section 14.9.1
+ | "no-store" ; Section 14.9.2
+ | "no-transform" ; Section 14.9.5
+ | "must-revalidate" ; Section 14.9.4
+ | "proxy-revalidate" ; Section 14.9.4
+ | "max-age" "=" delta-seconds ; Section 14.9.3
+ | "s-maxage" "=" delta-seconds ; Section 14.9.3
+ | cache-extension ; Section 14.9.6
+
+ cache-extension = token [ "=" ( token | quoted-string ) ]
+
+
+
+Fielding, et al. Standards Track [Page 108]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ When a directive appears without any 1#field-name parameter, the
+ directive applies to the entire request or response. When such a
+ directive appears with a 1#field-name parameter, it applies only to
+ the named field or fields, and not to the rest of the request or
+ response. This mechanism supports extensibility; implementations of
+ future versions of the HTTP protocol might apply these directives to
+ header fields not defined in HTTP/1.1.
+
+ The cache-control directives can be broken down into these general
+ categories:
+
+ - Restrictions on what are cacheable; these may only be imposed by
+ the origin server.
+
+ - Restrictions on what may be stored by a cache; these may be
+ imposed by either the origin server or the user agent.
+
+ - Modifications of the basic expiration mechanism; these may be
+ imposed by either the origin server or the user agent.
+
+ - Controls over cache revalidation and reload; these may only be
+ imposed by a user agent.
+
+ - Control over transformation of entities.
+
+ - Extensions to the caching system.
+
+14.9.1 What is Cacheable
+
+ By default, a response is cacheable if the requirements of the
+ request method, request header fields, and the response status
+ indicate that it is cacheable. Section 13.4 summarizes these defaults
+ for cacheability. The following Cache-Control response directives
+ allow an origin server to override the default cacheability of a
+ response:
+
+ public
+ Indicates that the response MAY be cached by any cache, even if it
+ would normally be non-cacheable or cacheable only within a non-
+ shared cache. (See also Authorization, section 14.8, for
+ additional details.)
+
+ private
+ Indicates that all or part of the response message is intended for
+ a single user and MUST NOT be cached by a shared cache. This
+ allows an origin server to state that the specified parts of the
+
+
+
+
+
+Fielding, et al. Standards Track [Page 109]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ response are intended for only one user and are not a valid
+ response for requests by other users. A private (non-shared) cache
+ MAY cache the response.
+
+ Note: This usage of the word private only controls where the
+ response may be cached, and cannot ensure the privacy of the
+ message content.
+
+ no-cache
+ If the no-cache directive does not specify a field-name, then a
+ cache MUST NOT use the response to satisfy a subsequent request
+ without successful revalidation with the origin server. This
+ allows an origin server to prevent caching even by caches that
+ have been configured to return stale responses to client requests.
+
+ If the no-cache directive does specify one or more field-names,
+ then a cache MAY use the response to satisfy a subsequent request,
+ subject to any other restrictions on caching. However, the
+ specified field-name(s) MUST NOT be sent in the response to a
+ subsequent request without successful revalidation with the origin
+ server. This allows an origin server to prevent the re-use of
+ certain header fields in a response, while still allowing caching
+ of the rest of the response.
+
+ Note: Most HTTP/1.0 caches will not recognize or obey this
+ directive.
+
+14.9.2 What May be Stored by Caches
+
+ no-store
+ The purpose of the no-store directive is to prevent the
+ inadvertent release or retention of sensitive information (for
+ example, on backup tapes). The no-store directive applies to the
+ entire message, and MAY be sent either in a response or in a
+ request. If sent in a request, a cache MUST NOT store any part of
+ either this request or any response to it. If sent in a response,
+ a cache MUST NOT store any part of either this response or the
+ request that elicited it. This directive applies to both non-
+ shared and shared caches. "MUST NOT store" in this context means
+ that the cache MUST NOT intentionally store the information in
+ non-volatile storage, and MUST make a best-effort attempt to
+ remove the information from volatile storage as promptly as
+ possible after forwarding it.
+
+ Even when this directive is associated with a response, users
+ might explicitly store such a response outside of the caching
+ system (e.g., with a "Save As" dialog). History buffers MAY store
+ such responses as part of their normal operation.
+
+
+
+Fielding, et al. Standards Track [Page 110]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ The purpose of this directive is to meet the stated requirements
+ of certain users and service authors who are concerned about
+ accidental releases of information via unanticipated accesses to
+ cache data structures. While the use of this directive might
+ improve privacy in some cases, we caution that it is NOT in any
+ way a reliable or sufficient mechanism for ensuring privacy. In
+ particular, malicious or compromised caches might not recognize or
+ obey this directive, and communications networks might be
+ vulnerable to eavesdropping.
+
+14.9.3 Modifications of the Basic Expiration Mechanism
+
+ The expiration time of an entity MAY be specified by the origin
+ server using the Expires header (see section 14.21). Alternatively,
+ it MAY be specified using the max-age directive in a response. When
+ the max-age cache-control directive is present in a cached response,
+ the response is stale if its current age is greater than the age
+ value given (in seconds) at the time of a new request for that
+ resource. The max-age directive on a response implies that the
+ response is cacheable (i.e., "public") unless some other, more
+ restrictive cache directive is also present.
+
+ If a response includes both an Expires header and a max-age
+ directive, the max-age directive overrides the Expires header, even
+ if the Expires header is more restrictive. This rule allows an origin
+ server to provide, for a given response, a longer expiration time to
+ an HTTP/1.1 (or later) cache than to an HTTP/1.0 cache. This might be
+ useful if certain HTTP/1.0 caches improperly calculate ages or
+ expiration times, perhaps due to desynchronized clocks.
+
+ Many HTTP/1.0 cache implementations will treat an Expires value that
+ is less than or equal to the response Date value as being equivalent
+ to the Cache-Control response directive "no-cache". If an HTTP/1.1
+ cache receives such a response, and the response does not include a
+ Cache-Control header field, it SHOULD consider the response to be
+ non-cacheable in order to retain compatibility with HTTP/1.0 servers.
+
+ Note: An origin server might wish to use a relatively new HTTP
+ cache control feature, such as the "private" directive, on a
+ network including older caches that do not understand that
+ feature. The origin server will need to combine the new feature
+ with an Expires field whose value is less than or equal to the
+ Date value. This will prevent older caches from improperly
+ caching the response.
+
+
+
+
+
+
+
+Fielding, et al. Standards Track [Page 111]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ s-maxage
+ If a response includes an s-maxage directive, then for a shared
+ cache (but not for a private cache), the maximum age specified by
+ this directive overrides the maximum age specified by either the
+ max-age directive or the Expires header. The s-maxage directive
+ also implies the semantics of the proxy-revalidate directive (see
+ section 14.9.4), i.e., that the shared cache must not use the
+ entry after it becomes stale to respond to a subsequent request
+ without first revalidating it with the origin server. The s-
+ maxage directive is always ignored by a private cache.
+
+ Note that most older caches, not compliant with this specification,
+ do not implement any cache-control directives. An origin server
+ wishing to use a cache-control directive that restricts, but does not
+ prevent, caching by an HTTP/1.1-compliant cache MAY exploit the
+ requirement that the max-age directive overrides the Expires header,
+ and the fact that pre-HTTP/1.1-compliant caches do not observe the
+ max-age directive.
+
+ Other directives allow a user agent to modify the basic expiration
+ mechanism. These directives MAY be specified on a request:
+
+ max-age
+ Indicates that the client is willing to accept a response whose
+ age is no greater than the specified time in seconds. Unless max-
+ stale directive is also included, the client is not willing to
+ accept a stale response.
+
+ min-fresh
+ Indicates that the client is willing to accept a response whose
+ freshness lifetime is no less than its current age plus the
+ specified time in seconds. That is, the client wants a response
+ that will still be fresh for at least the specified number of
+ seconds.
+
+ max-stale
+ Indicates that the client is willing to accept a response that has
+ exceeded its expiration time. If max-stale is assigned a value,
+ then the client is willing to accept a response that has exceeded
+ its expiration time by no more than the specified number of
+ seconds. If no value is assigned to max-stale, then the client is
+ willing to accept a stale response of any age.
+
+ If a cache returns a stale response, either because of a max-stale
+ directive on a request, or because the cache is configured to
+ override the expiration time of a response, the cache MUST attach a
+ Warning header to the stale response, using Warning 110 (Response is
+ stale).
+
+
+
+Fielding, et al. Standards Track [Page 112]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ A cache MAY be configured to return stale responses without
+ validation, but only if this does not conflict with any "MUST"-level
+ requirements concerning cache validation (e.g., a "must-revalidate"
+ cache-control directive).
+
+ If both the new request and the cached entry include "max-age"
+ directives, then the lesser of the two values is used for determining
+ the freshness of the cached entry for that request.
+
+14.9.4 Cache Revalidation and Reload Controls
+
+ Sometimes a user agent might want or need to insist that a cache
+ revalidate its cache entry with the origin server (and not just with
+ the next cache along the path to the origin server), or to reload its
+ cache entry from the origin server. End-to-end revalidation might be
+ necessary if either the cache or the origin server has overestimated
+ the expiration time of the cached response. End-to-end reload may be
+ necessary if the cache entry has become corrupted for some reason.
+
+ End-to-end revalidation may be requested either when the client does
+ not have its own local cached copy, in which case we call it
+ "unspecified end-to-end revalidation", or when the client does have a
+ local cached copy, in which case we call it "specific end-to-end
+ revalidation."
+
+ The client can specify these three kinds of action using Cache-
+ Control request directives:
+
+ End-to-end reload
+ The request includes a "no-cache" cache-control directive or, for
+ compatibility with HTTP/1.0 clients, "Pragma: no-cache". Field
+ names MUST NOT be included with the no-cache directive in a
+ request. The server MUST NOT use a cached copy when responding to
+ such a request.
+
+ Specific end-to-end revalidation
+ The request includes a "max-age=0" cache-control directive, which
+ forces each cache along the path to the origin server to
+ revalidate its own entry, if any, with the next cache or server.
+ The initial request includes a cache-validating conditional with
+ the client's current validator.
+
+ Unspecified end-to-end revalidation
+ The request includes "max-age=0" cache-control directive, which
+ forces each cache along the path to the origin server to
+ revalidate its own entry, if any, with the next cache or server.
+ The initial request does not include a cache-validating
+
+
+
+
+Fielding, et al. Standards Track [Page 113]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ conditional; the first cache along the path (if any) that holds a
+ cache entry for this resource includes a cache-validating
+ conditional with its current validator.
+
+ max-age
+ When an intermediate cache is forced, by means of a max-age=0
+ directive, to revalidate its own cache entry, and the client has
+ supplied its own validator in the request, the supplied validator
+ might differ from the validator currently stored with the cache
+ entry. In this case, the cache MAY use either validator in making
+ its own request without affecting semantic transparency.
+
+ However, the choice of validator might affect performance. The
+ best approach is for the intermediate cache to use its own
+ validator when making its request. If the server replies with 304
+ (Not Modified), then the cache can return its now validated copy
+ to the client with a 200 (OK) response. If the server replies with
+ a new entity and cache validator, however, the intermediate cache
+ can compare the returned validator with the one provided in the
+ client's request, using the strong comparison function. If the
+ client's validator is equal to the origin server's, then the
+ intermediate cache simply returns 304 (Not Modified). Otherwise,
+ it returns the new entity with a 200 (OK) response.
+
+ If a request includes the no-cache directive, it SHOULD NOT
+ include min-fresh, max-stale, or max-age.
+
+ only-if-cached
+ In some cases, such as times of extremely poor network
+ connectivity, a client may want a cache to return only those
+ responses that it currently has stored, and not to reload or
+ revalidate with the origin server. To do this, the client may
+ include the only-if-cached directive in a request. If it receives
+ this directive, a cache SHOULD either respond using a cached entry
+ that is consistent with the other constraints of the request, or
+ respond with a 504 (Gateway Timeout) status. However, if a group
+ of caches is being operated as a unified system with good internal
+ connectivity, such a request MAY be forwarded within that group of
+ caches.
+
+ must-revalidate
+ Because a cache MAY be configured to ignore a server's specified
+ expiration time, and because a client request MAY include a max-
+ stale directive (which has a similar effect), the protocol also
+ includes a mechanism for the origin server to require revalidation
+ of a cache entry on any subsequent use. When the must-revalidate
+ directive is present in a response received by a cache, that cache
+ MUST NOT use the entry after it becomes stale to respond to a
+
+
+
+Fielding, et al. Standards Track [Page 114]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ subsequent request without first revalidating it with the origin
+ server. (I.e., the cache MUST do an end-to-end revalidation every
+ time, if, based solely on the origin server's Expires or max-age
+ value, the cached response is stale.)
+
+ The must-revalidate directive is necessary to support reliable
+ operation for certain protocol features. In all circumstances an
+ HTTP/1.1 cache MUST obey the must-revalidate directive; in
+ particular, if the cache cannot reach the origin server for any
+ reason, it MUST generate a 504 (Gateway Timeout) response.
+
+ Servers SHOULD send the must-revalidate directive if and only if
+ failure to revalidate a request on the entity could result in
+ incorrect operation, such as a silently unexecuted financial
+ transaction. Recipients MUST NOT take any automated action that
+ violates this directive, and MUST NOT automatically provide an
+ unvalidated copy of the entity if revalidation fails.
+
+ Although this is not recommended, user agents operating under
+ severe connectivity constraints MAY violate this directive but, if
+ so, MUST explicitly warn the user that an unvalidated response has
+ been provided. The warning MUST be provided on each unvalidated
+ access, and SHOULD require explicit user confirmation.
+
+ proxy-revalidate
+ The proxy-revalidate directive has the same meaning as the must-
+ revalidate directive, except that it does not apply to non-shared
+ user agent caches. It can be used on a response to an
+ authenticated request to permit the user's cache to store and
+ later return the response without needing to revalidate it (since
+ it has already been authenticated once by that user), while still
+ requiring proxies that service many users to revalidate each time
+ (in order to make sure that each user has been authenticated).
+ Note that such authenticated responses also need the public cache
+ control directive in order to allow them to be cached at all.
+
+14.9.5 No-Transform Directive
+
+ no-transform
+ Implementors of intermediate caches (proxies) have found it useful
+ to convert the media type of certain entity bodies. A non-
+ transparent proxy might, for example, convert between image
+ formats in order to save cache space or to reduce the amount of
+ traffic on a slow link.
+
+ Serious operational problems occur, however, when these
+ transformations are applied to entity bodies intended for certain
+ kinds of applications. For example, applications for medical
+
+
+
+Fielding, et al. Standards Track [Page 115]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ imaging, scientific data analysis and those using end-to-end
+ authentication, all depend on receiving an entity body that is bit
+ for bit identical to the original entity-body.
+
+ Therefore, if a message includes the no-transform directive, an
+ intermediate cache or proxy MUST NOT change those headers that are
+ listed in section 13.5.2 as being subject to the no-transform
+ directive. This implies that the cache or proxy MUST NOT change
+ any aspect of the entity-body that is specified by these headers,
+ including the value of the entity-body itself.
+
+14.9.6 Cache Control Extensions
+
+ The Cache-Control header field can be extended through the use of one
+ or more cache-extension tokens, each with an optional assigned value.
+ Informational extensions (those which do not require a change in
+ cache behavior) MAY be added without changing the semantics of other
+ directives. Behavioral extensions are designed to work by acting as
+ modifiers to the existing base of cache directives. Both the new
+ directive and the standard directive are supplied, such that
+ applications which do not understand the new directive will default
+ to the behavior specified by the standard directive, and those that
+ understand the new directive will recognize it as modifying the
+ requirements associated with the standard directive. In this way,
+ extensions to the cache-control directives can be made without
+ requiring changes to the base protocol.
+
+ This extension mechanism depends on an HTTP cache obeying all of the
+ cache-control directives defined for its native HTTP-version, obeying
+ certain extensions, and ignoring all directives that it does not
+ understand.
+
+ For example, consider a hypothetical new response directive called
+ community which acts as a modifier to the private directive. We
+ define this new directive to mean that, in addition to any non-shared
+ cache, any cache which is shared only by members of the community
+ named within its value may cache the response. An origin server
+ wishing to allow the UCI community to use an otherwise private
+ response in their shared cache(s) could do so by including
+
+ Cache-Control: private, community="UCI"
+
+ A cache seeing this header field will act correctly even if the cache
+ does not understand the community cache-extension, since it will also
+ see and understand the private directive and thus default to the safe
+ behavior.
+
+
+
+
+
+Fielding, et al. Standards Track [Page 116]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ Unrecognized cache-directives MUST be ignored; it is assumed that any
+ cache-directive likely to be unrecognized by an HTTP/1.1 cache will
+ be combined with standard directives (or the response's default
+ cacheability) such that the cache behavior will remain minimally
+ correct even if the cache does not understand the extension(s).
+
+14.10 Connection
+
+ The Connection general-header field allows the sender to specify
+ options that are desired for that particular connection and MUST NOT
+ be communicated by proxies over further connections.
+
+ The Connection header has the following grammar:
+
+ Connection = "Connection" ":" 1#(connection-token)
+ connection-token = token
+
+ HTTP/1.1 proxies MUST parse the Connection header field before a
+ message is forwarded and, for each connection-token in this field,
+ remove any header field(s) from the message with the same name as the
+ connection-token. Connection options are signaled by the presence of
+ a connection-token in the Connection header field, not by any
+ corresponding additional header field(s), since the additional header
+ field may not be sent if there are no parameters associated with that
+ connection option.
+
+ Message headers listed in the Connection header MUST NOT include
+ end-to-end headers, such as Cache-Control.
+
+ HTTP/1.1 defines the "close" connection option for the sender to
+ signal that the connection will be closed after completion of the
+ response. For example,
+
+ Connection: close
+
+ in either the request or the response header fields indicates that
+ the connection SHOULD NOT be considered `persistent' (section 8.1)
+ after the current request/response is complete.
+
+ HTTP/1.1 applications that do not support persistent connections MUST
+ include the "close" connection option in every message.
+
+ A system receiving an HTTP/1.0 (or lower-version) message that
+ includes a Connection header MUST, for each connection-token in this
+ field, remove and ignore any header field(s) from the message with
+ the same name as the connection-token. This protects against mistaken
+ forwarding of such header fields by pre-HTTP/1.1 proxies. See section
+ 19.6.2.
+
+
+
+Fielding, et al. Standards Track [Page 117]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+14.11 Content-Encoding
+
+ The Content-Encoding entity-header field is used as a modifier to the
+ media-type. When present, its value indicates what additional content
+ codings have been applied to the entity-body, and thus what decoding
+ mechanisms must be applied in order to obtain the media-type
+ referenced by the Content-Type header field. Content-Encoding is
+ primarily used to allow a document to be compressed without losing
+ the identity of its underlying media type.
+
+ Content-Encoding = "Content-Encoding" ":" 1#content-coding
+
+ Content codings are defined in section 3.5. An example of its use is
+
+ Content-Encoding: gzip
+
+ The content-coding is a characteristic of the entity identified by
+ the Request-URI. Typically, the entity-body is stored with this
+ encoding and is only decoded before rendering or analogous usage.
+ However, a non-transparent proxy MAY modify the content-coding if the
+ new coding is known to be acceptable to the recipient, unless the
+ "no-transform" cache-control directive is present in the message.
+
+ If the content-coding of an entity is not "identity", then the
+ response MUST include a Content-Encoding entity-header (section
+ 14.11) that lists the non-identity content-coding(s) used.
+
+ If the content-coding of an entity in a request message is not
+ acceptable to the origin server, the server SHOULD respond with a
+ status code of 415 (Unsupported Media Type).
+
+ If multiple encodings have been applied to an entity, the content
+ codings MUST be listed in the order in which they were applied.
+ Additional information about the encoding parameters MAY be provided
+ by other entity-header fields not defined by this specification.
+
+14.12 Content-Language
+
+ The Content-Language entity-header field describes the natural
+ language(s) of the intended audience for the enclosed entity. Note
+ that this might not be equivalent to all the languages used within
+ the entity-body.
+
+ Content-Language = "Content-Language" ":" 1#language-tag
+
+
+
+
+
+
+
+Fielding, et al. Standards Track [Page 118]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ Language tags are defined in section 3.10. The primary purpose of
+ Content-Language is to allow a user to identify and differentiate
+ entities according to the user's own preferred language. Thus, if the
+ body content is intended only for a Danish-literate audience, the
+ appropriate field is
+
+ Content-Language: da
+
+ If no Content-Language is specified, the default is that the content
+ is intended for all language audiences. This might mean that the
+ sender does not consider it to be specific to any natural language,
+ or that the sender does not know for which language it is intended.
+
+ Multiple languages MAY be listed for content that is intended for
+ multiple audiences. For example, a rendition of the "Treaty of
+ Waitangi," presented simultaneously in the original Maori and English
+ versions, would call for
+
+ Content-Language: mi, en
+
+ However, just because multiple languages are present within an entity
+ does not mean that it is intended for multiple linguistic audiences.
+ An example would be a beginner's language primer, such as "A First
+ Lesson in Latin," which is clearly intended to be used by an
+ English-literate audience. In this case, the Content-Language would
+ properly only include "en".
+
+ Content-Language MAY be applied to any media type -- it is not
+ limited to textual documents.
+
+14.13 Content-Length
+
+ The Content-Length entity-header field indicates the size of the
+ entity-body, in decimal number of OCTETs, sent to the recipient or,
+ in the case of the HEAD method, the size of the entity-body that
+ would have been sent had the request been a GET.
+
+ Content-Length = "Content-Length" ":" 1*DIGIT
+
+ An example is
+
+ Content-Length: 3495
+
+ Applications SHOULD use this field to indicate the transfer-length of
+ the message-body, unless this is prohibited by the rules in section
+ 4.4.
+
+
+
+
+
+Fielding, et al. Standards Track [Page 119]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ Any Content-Length greater than or equal to zero is a valid value.
+ Section 4.4 describes how to determine the length of a message-body
+ if a Content-Length is not given.
+
+ Note that the meaning of this field is significantly different from
+ the corresponding definition in MIME, where it is an optional field
+ used within the "message/external-body" content-type. In HTTP, it
+ SHOULD be sent whenever the message's length can be determined prior
+ to being transferred, unless this is prohibited by the rules in
+ section 4.4.
+
+14.14 Content-Location
+
+ The Content-Location entity-header field MAY be used to supply the
+ resource location for the entity enclosed in the message when that
+ entity is accessible from a location separate from the requested
+ resource's URI. A server SHOULD provide a Content-Location for the
+ variant corresponding to the response entity; especially in the case
+ where a resource has multiple entities associated with it, and those
+ entities actually have separate locations by which they might be
+ individually accessed, the server SHOULD provide a Content-Location
+ for the particular variant which is returned.
+
+ Content-Location = "Content-Location" ":"
+ ( absoluteURI | relativeURI )
+
+ The value of Content-Location also defines the base URI for the
+ entity.
+
+ The Content-Location value is not a replacement for the original
+ requested URI; it is only a statement of the location of the resource
+ corresponding to this particular entity at the time of the request.
+ Future requests MAY specify the Content-Location URI as the request-
+ URI if the desire is to identify the source of that particular
+ entity.
+
+ A cache cannot assume that an entity with a Content-Location
+ different from the URI used to retrieve it can be used to respond to
+ later requests on that Content-Location URI. However, the Content-
+ Location can be used to differentiate between multiple entities
+ retrieved from a single requested resource, as described in section
+ 13.6.
+
+ If the Content-Location is a relative URI, the relative URI is
+ interpreted relative to the Request-URI.
+
+ The meaning of the Content-Location header in PUT or POST requests is
+ undefined; servers are free to ignore it in those cases.
+
+
+
+Fielding, et al. Standards Track [Page 120]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+14.15 Content-MD5
+
+ The Content-MD5 entity-header field, as defined in RFC 1864 [23], is
+ an MD5 digest of the entity-body for the purpose of providing an
+ end-to-end message integrity check (MIC) of the entity-body. (Note: a
+ MIC is good for detecting accidental modification of the entity-body
+ in transit, but is not proof against malicious attacks.)
+
+ Content-MD5 = "Content-MD5" ":" md5-digest
+ md5-digest = <base64 of 128 bit MD5 digest as per RFC 1864>
+
+ The Content-MD5 header field MAY be generated by an origin server or
+ client to function as an integrity check of the entity-body. Only
+ origin servers or clients MAY generate the Content-MD5 header field;
+ proxies and gateways MUST NOT generate it, as this would defeat its
+ value as an end-to-end integrity check. Any recipient of the entity-
+ body, including gateways and proxies, MAY check that the digest value
+ in this header field matches that of the entity-body as received.
+
+ The MD5 digest is computed based on the content of the entity-body,
+ including any content-coding that has been applied, but not including
+ any transfer-encoding applied to the message-body. If the message is
+ received with a transfer-encoding, that encoding MUST be removed
+ prior to checking the Content-MD5 value against the received entity.
+
+ This has the result that the digest is computed on the octets of the
+ entity-body exactly as, and in the order that, they would be sent if
+ no transfer-encoding were being applied.
+
+ HTTP extends RFC 1864 to permit the digest to be computed for MIME
+ composite media-types (e.g., multipart/* and message/rfc822), but
+ this does not change how the digest is computed as defined in the
+ preceding paragraph.
+
+ There are several consequences of this. The entity-body for composite
+ types MAY contain many body-parts, each with its own MIME and HTTP
+ headers (including Content-MD5, Content-Transfer-Encoding, and
+ Content-Encoding headers). If a body-part has a Content-Transfer-
+ Encoding or Content-Encoding header, it is assumed that the content
+ of the body-part has had the encoding applied, and the body-part is
+ included in the Content-MD5 digest as is -- i.e., after the
+ application. The Transfer-Encoding header field is not allowed within
+ body-parts.
+
+ Conversion of all line breaks to CRLF MUST NOT be done before
+ computing or checking the digest: the line break convention used in
+ the text actually transmitted MUST be left unaltered when computing
+ the digest.
+
+
+
+Fielding, et al. Standards Track [Page 121]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ Note: while the definition of Content-MD5 is exactly the same for
+ HTTP as in RFC 1864 for MIME entity-bodies, there are several ways
+ in which the application of Content-MD5 to HTTP entity-bodies
+ differs from its application to MIME entity-bodies. One is that
+ HTTP, unlike MIME, does not use Content-Transfer-Encoding, and
+ does use Transfer-Encoding and Content-Encoding. Another is that
+ HTTP more frequently uses binary content types than MIME, so it is
+ worth noting that, in such cases, the byte order used to compute
+ the digest is the transmission byte order defined for the type.
+ Lastly, HTTP allows transmission of text types with any of several
+ line break conventions and not just the canonical form using CRLF.
+
+14.16 Content-Range
+
+ The Content-Range entity-header is sent with a partial entity-body to
+ specify where in the full entity-body the partial body should be
+ applied. Range units are defined in section 3.12.
+
+ Content-Range = "Content-Range" ":" content-range-spec
+
+ content-range-spec = byte-content-range-spec
+ byte-content-range-spec = bytes-unit SP
+ byte-range-resp-spec "/"
+ ( instance-length | "*" )
+
+ byte-range-resp-spec = (first-byte-pos "-" last-byte-pos)
+ | "*"
+ instance-length = 1*DIGIT
+
+ The header SHOULD indicate the total length of the full entity-body,
+ unless this length is unknown or difficult to determine. The asterisk
+ "*" character means that the instance-length is unknown at the time
+ when the response was generated.
+
+ Unlike byte-ranges-specifier values (see section 14.35.1), a byte-
+ range-resp-spec MUST only specify one range, and MUST contain
+ absolute byte positions for both the first and last byte of the
+ range.
+
+ A byte-content-range-spec with a byte-range-resp-spec whose last-
+ byte-pos value is less than its first-byte-pos value, or whose
+ instance-length value is less than or equal to its last-byte-pos
+ value, is invalid. The recipient of an invalid byte-content-range-
+ spec MUST ignore it and any content transferred along with it.
+
+ A server sending a response with status code 416 (Requested range not
+ satisfiable) SHOULD include a Content-Range field with a byte-range-
+ resp-spec of "*". The instance-length specifies the current length of
+
+
+
+Fielding, et al. Standards Track [Page 122]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ the selected resource. A response with status code 206 (Partial
+ Content) MUST NOT include a Content-Range field with a byte-range-
+ resp-spec of "*".
+
+ Examples of byte-content-range-spec values, assuming that the entity
+ contains a total of 1234 bytes:
+
+ . The first 500 bytes:
+ bytes 0-499/1234
+
+ . The second 500 bytes:
+ bytes 500-999/1234
+
+ . All except for the first 500 bytes:
+ bytes 500-1233/1234
+
+ . The last 500 bytes:
+ bytes 734-1233/1234
+
+ When an HTTP message includes the content of a single range (for
+ example, a response to a request for a single range, or to a request
+ for a set of ranges that overlap without any holes), this content is
+ transmitted with a Content-Range header, and a Content-Length header
+ showing the number of bytes actually transferred. For example,
+
+ HTTP/1.1 206 Partial content
+ Date: Wed, 15 Nov 1995 06:25:24 GMT
+ Last-Modified: Wed, 15 Nov 1995 04:58:08 GMT
+ Content-Range: bytes 21010-47021/47022
+ Content-Length: 26012
+ Content-Type: image/gif
+
+ When an HTTP message includes the content of multiple ranges (for
+ example, a response to a request for multiple non-overlapping
+ ranges), these are transmitted as a multipart message. The multipart
+ media type used for this purpose is "multipart/byteranges" as defined
+ in appendix 19.2. See appendix 19.6.3 for a compatibility issue.
+
+ A response to a request for a single range MUST NOT be sent using the
+ multipart/byteranges media type. A response to a request for
+ multiple ranges, whose result is a single range, MAY be sent as a
+ multipart/byteranges media type with one part. A client that cannot
+ decode a multipart/byteranges message MUST NOT ask for multiple
+ byte-ranges in a single request.
+
+ When a client requests multiple byte-ranges in one request, the
+ server SHOULD return them in the order that they appeared in the
+ request.
+
+
+
+Fielding, et al. Standards Track [Page 123]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ If the server ignores a byte-range-spec because it is syntactically
+ invalid, the server SHOULD treat the request as if the invalid Range
+ header field did not exist. (Normally, this means return a 200
+ response containing the full entity).
+
+ If the server receives a request (other than one including an If-
+ Range request-header field) with an unsatisfiable Range request-
+ header field (that is, all of whose byte-range-spec values have a
+ first-byte-pos value greater than the current length of the selected
+ resource), it SHOULD return a response code of 416 (Requested range
+ not satisfiable) (section 10.4.17).
+
+ Note: clients cannot depend on servers to send a 416 (Requested
+ range not satisfiable) response instead of a 200 (OK) response for
+ an unsatisfiable Range request-header, since not all servers
+ implement this request-header.
+
+14.17 Content-Type
+
+ The Content-Type entity-header field indicates the media type of the
+ entity-body sent to the recipient or, in the case of the HEAD method,
+ the media type that would have been sent had the request been a GET.
+
+ Content-Type = "Content-Type" ":" media-type
+
+ Media types are defined in section 3.7. An example of the field is
+
+ Content-Type: text/html; charset=ISO-8859-4
+
+ Further discussion of methods for identifying the media type of an
+ entity is provided in section 7.2.1.
+
+14.18 Date
+
+ The Date general-header field represents the date and time at which
+ the message was originated, having the same semantics as orig-date in
+ RFC 822. The field value is an HTTP-date, as described in section
+ 3.3.1; it MUST be sent in RFC 1123 [8]-date format.
+
+ Date = "Date" ":" HTTP-date
+
+ An example is
+
+ Date: Tue, 15 Nov 1994 08:12:31 GMT
+
+ Origin servers MUST include a Date header field in all responses,
+ except in these cases:
+
+
+
+
+Fielding, et al. Standards Track [Page 124]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ 1. If the response status code is 100 (Continue) or 101 (Switching
+ Protocols), the response MAY include a Date header field, at
+ the server's option.
+
+ 2. If the response status code conveys a server error, e.g. 500
+ (Internal Server Error) or 503 (Service Unavailable), and it is
+ inconvenient or impossible to generate a valid Date.
+
+ 3. If the server does not have a clock that can provide a
+ reasonable approximation of the current time, its responses
+ MUST NOT include a Date header field. In this case, the rules
+ in section 14.18.1 MUST be followed.
+
+ A received message that does not have a Date header field MUST be
+ assigned one by the recipient if the message will be cached by that
+ recipient or gatewayed via a protocol which requires a Date. An HTTP
+ implementation without a clock MUST NOT cache responses without
+ revalidating them on every use. An HTTP cache, especially a shared
+ cache, SHOULD use a mechanism, such as NTP [28], to synchronize its
+ clock with a reliable external standard.
+
+ Clients SHOULD only send a Date header field in messages that include
+ an entity-body, as in the case of the PUT and POST requests, and even
+ then it is optional. A client without a clock MUST NOT send a Date
+ header field in a request.
+
+ The HTTP-date sent in a Date header SHOULD NOT represent a date and
+ time subsequent to the generation of the message. It SHOULD represent
+ the best available approximation of the date and time of message
+ generation, unless the implementation has no means of generating a
+ reasonably accurate date and time. In theory, the date ought to
+ represent the moment just before the entity is generated. In
+ practice, the date can be generated at any time during the message
+ origination without affecting its semantic value.
+
+14.18.1 Clockless Origin Server Operation
+
+ Some origin server implementations might not have a clock available.
+ An origin server without a clock MUST NOT assign Expires or Last-
+ Modified values to a response, unless these values were associated
+ with the resource by a system or user with a reliable clock. It MAY
+ assign an Expires value that is known, at or before server
+ configuration time, to be in the past (this allows "pre-expiration"
+ of responses without storing separate Expires values for each
+ resource).
+
+
+
+
+
+
+Fielding, et al. Standards Track [Page 125]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+14.19 ETag
+
+ The ETag response-header field provides the current value of the
+ entity tag for the requested variant. The headers used with entity
+ tags are described in sections 14.24, 14.26 and 14.44. The entity tag
+ MAY be used for comparison with other entities from the same resource
+ (see section 13.3.3).
+
+ ETag = "ETag" ":" entity-tag
+
+ Examples:
+
+ ETag: "xyzzy"
+ ETag: W/"xyzzy"
+ ETag: ""
+
+14.20 Expect
+
+ The Expect request-header field is used to indicate that particular
+ server behaviors are required by the client.
+
+ Expect = "Expect" ":" 1#expectation
+
+ expectation = "100-continue" | expectation-extension
+ expectation-extension = token [ "=" ( token | quoted-string )
+ *expect-params ]
+ expect-params = ";" token [ "=" ( token | quoted-string ) ]
+
+
+ A server that does not understand or is unable to comply with any of
+ the expectation values in the Expect field of a request MUST respond
+ with appropriate error status. The server MUST respond with a 417
+ (Expectation Failed) status if any of the expectations cannot be met
+ or, if there are other problems with the request, some other 4xx
+ status.
+
+ This header field is defined with extensible syntax to allow for
+ future extensions. If a server receives a request containing an
+ Expect field that includes an expectation-extension that it does not
+ support, it MUST respond with a 417 (Expectation Failed) status.
+
+ Comparison of expectation values is case-insensitive for unquoted
+ tokens (including the 100-continue token), and is case-sensitive for
+ quoted-string expectation-extensions.
+
+
+
+
+
+
+
+Fielding, et al. Standards Track [Page 126]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ The Expect mechanism is hop-by-hop: that is, an HTTP/1.1 proxy MUST
+ return a 417 (Expectation Failed) status if it receives a request
+ with an expectation that it cannot meet. However, the Expect
+ request-header itself is end-to-end; it MUST be forwarded if the
+ request is forwarded.
+
+ Many older HTTP/1.0 and HTTP/1.1 applications do not understand the
+ Expect header.
+
+ See section 8.2.3 for the use of the 100 (continue) status.
+
+14.21 Expires
+
+ The Expires entity-header field gives the date/time after which the
+ response is considered stale. A stale cache entry may not normally be
+ returned by a cache (either a proxy cache or a user agent cache)
+ unless it is first validated with the origin server (or with an
+ intermediate cache that has a fresh copy of the entity). See section
+ 13.2 for further discussion of the expiration model.
+
+ The presence of an Expires field does not imply that the original
+ resource will change or cease to exist at, before, or after that
+ time.
+
+ The format is an absolute date and time as defined by HTTP-date in
+ section 3.3.1; it MUST be in RFC 1123 date format:
+
+ Expires = "Expires" ":" HTTP-date
+
+ An example of its use is
+
+ Expires: Thu, 01 Dec 1994 16:00:00 GMT
+
+ Note: if a response includes a Cache-Control field with the max-
+ age directive (see section 14.9.3), that directive overrides the
+ Expires field.
+
+ HTTP/1.1 clients and caches MUST treat other invalid date formats,
+ especially including the value "0", as in the past (i.e., "already
+ expired").
+
+ To mark a response as "already expired," an origin server sends an
+ Expires date that is equal to the Date header value. (See the rules
+ for expiration calculations in section 13.2.4.)
+
+
+
+
+
+
+
+Fielding, et al. Standards Track [Page 127]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ To mark a response as "never expires," an origin server sends an
+ Expires date approximately one year from the time the response is
+ sent. HTTP/1.1 servers SHOULD NOT send Expires dates more than one
+ year in the future.
+
+ The presence of an Expires header field with a date value of some
+ time in the future on a response that otherwise would by default be
+ non-cacheable indicates that the response is cacheable, unless
+ indicated otherwise by a Cache-Control header field (section 14.9).
+
+14.22 From
+
+ The From request-header field, if given, SHOULD contain an Internet
+ e-mail address for the human user who controls the requesting user
+ agent. The address SHOULD be machine-usable, as defined by "mailbox"
+ in RFC 822 [9] as updated by RFC 1123 [8]:
+
+ From = "From" ":" mailbox
+
+ An example is:
+
+ From: webmaster@w3.org
+
+ This header field MAY be used for logging purposes and as a means for
+ identifying the source of invalid or unwanted requests. It SHOULD NOT
+ be used as an insecure form of access protection. The interpretation
+ of this field is that the request is being performed on behalf of the
+ person given, who accepts responsibility for the method performed. In
+ particular, robot agents SHOULD include this header so that the
+ person responsible for running the robot can be contacted if problems
+ occur on the receiving end.
+
+ The Internet e-mail address in this field MAY be separate from the
+ Internet host which issued the request. For example, when a request
+ is passed through a proxy the original issuer's address SHOULD be
+ used.
+
+ The client SHOULD NOT send the From header field without the user's
+ approval, as it might conflict with the user's privacy interests or
+ their site's security policy. It is strongly recommended that the
+ user be able to disable, enable, and modify the value of this field
+ at any time prior to a request.
+
+14.23 Host
+
+ The Host request-header field specifies the Internet host and port
+ number of the resource being requested, as obtained from the original
+ URI given by the user or referring resource (generally an HTTP URL,
+
+
+
+Fielding, et al. Standards Track [Page 128]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ as described in section 3.2.2). The Host field value MUST represent
+ the naming authority of the origin server or gateway given by the
+ original URL. This allows the origin server or gateway to
+ differentiate between internally-ambiguous URLs, such as the root "/"
+ URL of a server for multiple host names on a single IP address.
+
+ Host = "Host" ":" host [ ":" port ] ; Section 3.2.2
+
+ A "host" without any trailing port information implies the default
+ port for the service requested (e.g., "80" for an HTTP URL). For
+ example, a request on the origin server for
+ <http://www.w3.org/pub/WWW/> would properly include:
+
+ GET /pub/WWW/ HTTP/1.1
+ Host: www.w3.org
+
+ A client MUST include a Host header field in all HTTP/1.1 request
+ messages . If the requested URI does not include an Internet host
+ name for the service being requested, then the Host header field MUST
+ be given with an empty value. An HTTP/1.1 proxy MUST ensure that any
+ request message it forwards does contain an appropriate Host header
+ field that identifies the service being requested by the proxy. All
+ Internet-based HTTP/1.1 servers MUST respond with a 400 (Bad Request)
+ status code to any HTTP/1.1 request message which lacks a Host header
+ field.
+
+ See sections 5.2 and 19.6.1.1 for other requirements relating to
+ Host.
+
+14.24 If-Match
+
+ The If-Match request-header field is used with a method to make it
+ conditional. A client that has one or more entities previously
+ obtained from the resource can verify that one of those entities is
+ current by including a list of their associated entity tags in the
+ If-Match header field. Entity tags are defined in section 3.11. The
+ purpose of this feature is to allow efficient updates of cached
+ information with a minimum amount of transaction overhead. It is also
+ used, on updating requests, to prevent inadvertent modification of
+ the wrong version of a resource. As a special case, the value "*"
+ matches any current entity of the resource.
+
+ If-Match = "If-Match" ":" ( "*" | 1#entity-tag )
+
+ If any of the entity tags match the entity tag of the entity that
+ would have been returned in the response to a similar GET request
+ (without the If-Match header) on that resource, or if "*" is given
+
+
+
+
+Fielding, et al. Standards Track [Page 129]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ and any current entity exists for that resource, then the server MAY
+ perform the requested method as if the If-Match header field did not
+ exist.
+
+ A server MUST use the strong comparison function (see section 13.3.3)
+ to compare the entity tags in If-Match.
+
+ If none of the entity tags match, or if "*" is given and no current
+ entity exists, the server MUST NOT perform the requested method, and
+ MUST return a 412 (Precondition Failed) response. This behavior is
+ most useful when the client wants to prevent an updating method, such
+ as PUT, from modifying a resource that has changed since the client
+ last retrieved it.
+
+ If the request would, without the If-Match header field, result in
+ anything other than a 2xx or 412 status, then the If-Match header
+ MUST be ignored.
+
+ The meaning of "If-Match: *" is that the method SHOULD be performed
+ if the representation selected by the origin server (or by a cache,
+ possibly using the Vary mechanism, see section 14.44) exists, and
+ MUST NOT be performed if the representation does not exist.
+
+ A request intended to update a resource (e.g., a PUT) MAY include an
+ If-Match header field to signal that the request method MUST NOT be
+ applied if the entity corresponding to the If-Match value (a single
+ entity tag) is no longer a representation of that resource. This
+ allows the user to indicate that they do not wish the request to be
+ successful if the resource has been changed without their knowledge.
+ Examples:
+
+ If-Match: "xyzzy"
+ If-Match: "xyzzy", "r2d2xxxx", "c3piozzzz"
+ If-Match: *
+
+ The result of a request having both an If-Match header field and
+ either an If-None-Match or an If-Modified-Since header fields is
+ undefined by this specification.
+
+14.25 If-Modified-Since
+
+ The If-Modified-Since request-header field is used with a method to
+ make it conditional: if the requested variant has not been modified
+ since the time specified in this field, an entity will not be
+ returned from the server; instead, a 304 (not modified) response will
+ be returned without any message-body.
+
+ If-Modified-Since = "If-Modified-Since" ":" HTTP-date
+
+
+
+Fielding, et al. Standards Track [Page 130]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ An example of the field is:
+
+ If-Modified-Since: Sat, 29 Oct 1994 19:43:31 GMT
+
+ A GET method with an If-Modified-Since header and no Range header
+ requests that the identified entity be transferred only if it has
+ been modified since the date given by the If-Modified-Since header.
+ The algorithm for determining this includes the following cases:
+
+ a) If the request would normally result in anything other than a
+ 200 (OK) status, or if the passed If-Modified-Since date is
+ invalid, the response is exactly the same as for a normal GET.
+ A date which is later than the server's current time is
+ invalid.
+
+ b) If the variant has been modified since the If-Modified-Since
+ date, the response is exactly the same as for a normal GET.
+
+ c) If the variant has not been modified since a valid If-
+ Modified-Since date, the server SHOULD return a 304 (Not
+ Modified) response.
+
+ The purpose of this feature is to allow efficient updates of cached
+ information with a minimum amount of transaction overhead.
+
+ Note: The Range request-header field modifies the meaning of If-
+ Modified-Since; see section 14.35 for full details.
+
+ Note: If-Modified-Since times are interpreted by the server, whose
+ clock might not be synchronized with the client.
+
+ Note: When handling an If-Modified-Since header field, some
+ servers will use an exact date comparison function, rather than a
+ less-than function, for deciding whether to send a 304 (Not
+ Modified) response. To get best results when sending an If-
+ Modified-Since header field for cache validation, clients are
+ advised to use the exact date string received in a previous Last-
+ Modified header field whenever possible.
+
+ Note: If a client uses an arbitrary date in the If-Modified-Since
+ header instead of a date taken from the Last-Modified header for
+ the same request, the client should be aware of the fact that this
+ date is interpreted in the server's understanding of time. The
+ client should consider unsynchronized clocks and rounding problems
+ due to the different encodings of time between the client and
+ server. This includes the possibility of race conditions if the
+ document has changed between the time it was first requested and
+ the If-Modified-Since date of a subsequent request, and the
+
+
+
+Fielding, et al. Standards Track [Page 131]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ possibility of clock-skew-related problems if the If-Modified-
+ Since date is derived from the client's clock without correction
+ to the server's clock. Corrections for different time bases
+ between client and server are at best approximate due to network
+ latency.
+
+ The result of a request having both an If-Modified-Since header field
+ and either an If-Match or an If-Unmodified-Since header fields is
+ undefined by this specification.
+
+14.26 If-None-Match
+
+ The If-None-Match request-header field is used with a method to make
+ it conditional. A client that has one or more entities previously
+ obtained from the resource can verify that none of those entities is
+ current by including a list of their associated entity tags in the
+ If-None-Match header field. The purpose of this feature is to allow
+ efficient updates of cached information with a minimum amount of
+ transaction overhead. It is also used to prevent a method (e.g. PUT)
+ from inadvertently modifying an existing resource when the client
+ believes that the resource does not exist.
+
+ As a special case, the value "*" matches any current entity of the
+ resource.
+
+ If-None-Match = "If-None-Match" ":" ( "*" | 1#entity-tag )
+
+ If any of the entity tags match the entity tag of the entity that
+ would have been returned in the response to a similar GET request
+ (without the If-None-Match header) on that resource, or if "*" is
+ given and any current entity exists for that resource, then the
+ server MUST NOT perform the requested method, unless required to do
+ so because the resource's modification date fails to match that
+ supplied in an If-Modified-Since header field in the request.
+ Instead, if the request method was GET or HEAD, the server SHOULD
+ respond with a 304 (Not Modified) response, including the cache-
+ related header fields (particularly ETag) of one of the entities that
+ matched. For all other request methods, the server MUST respond with
+ a status of 412 (Precondition Failed).
+
+ See section 13.3.3 for rules on how to determine if two entities tags
+ match. The weak comparison function can only be used with GET or HEAD
+ requests.
+
+
+
+
+
+
+
+
+Fielding, et al. Standards Track [Page 132]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ If none of the entity tags match, then the server MAY perform the
+ requested method as if the If-None-Match header field did not exist,
+ but MUST also ignore any If-Modified-Since header field(s) in the
+ request. That is, if no entity tags match, then the server MUST NOT
+ return a 304 (Not Modified) response.
+
+ If the request would, without the If-None-Match header field, result
+ in anything other than a 2xx or 304 status, then the If-None-Match
+ header MUST be ignored. (See section 13.3.4 for a discussion of
+ server behavior when both If-Modified-Since and If-None-Match appear
+ in the same request.)
+
+ The meaning of "If-None-Match: *" is that the method MUST NOT be
+ performed if the representation selected by the origin server (or by
+ a cache, possibly using the Vary mechanism, see section 14.44)
+ exists, and SHOULD be performed if the representation does not exist.
+ This feature is intended to be useful in preventing races between PUT
+ operations.
+
+ Examples:
+
+ If-None-Match: "xyzzy"
+ If-None-Match: W/"xyzzy"
+ If-None-Match: "xyzzy", "r2d2xxxx", "c3piozzzz"
+ If-None-Match: W/"xyzzy", W/"r2d2xxxx", W/"c3piozzzz"
+ If-None-Match: *
+
+ The result of a request having both an If-None-Match header field and
+ either an If-Match or an If-Unmodified-Since header fields is
+ undefined by this specification.
+
+14.27 If-Range
+
+ If a client has a partial copy of an entity in its cache, and wishes
+ to have an up-to-date copy of the entire entity in its cache, it
+ could use the Range request-header with a conditional GET (using
+ either or both of If-Unmodified-Since and If-Match.) However, if the
+ condition fails because the entity has been modified, the client
+ would then have to make a second request to obtain the entire current
+ entity-body.
+
+ The If-Range header allows a client to "short-circuit" the second
+ request. Informally, its meaning is `if the entity is unchanged, send
+ me the part(s) that I am missing; otherwise, send me the entire new
+ entity'.
+
+ If-Range = "If-Range" ":" ( entity-tag | HTTP-date )
+
+
+
+
+Fielding, et al. Standards Track [Page 133]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ If the client has no entity tag for an entity, but does have a Last-
+ Modified date, it MAY use that date in an If-Range header. (The
+ server can distinguish between a valid HTTP-date and any form of
+ entity-tag by examining no more than two characters.) The If-Range
+ header SHOULD only be used together with a Range header, and MUST be
+ ignored if the request does not include a Range header, or if the
+ server does not support the sub-range operation.
+
+ If the entity tag given in the If-Range header matches the current
+ entity tag for the entity, then the server SHOULD provide the
+ specified sub-range of the entity using a 206 (Partial content)
+ response. If the entity tag does not match, then the server SHOULD
+ return the entire entity using a 200 (OK) response.
+
+14.28 If-Unmodified-Since
+
+ The If-Unmodified-Since request-header field is used with a method to
+ make it conditional. If the requested resource has not been modified
+ since the time specified in this field, the server SHOULD perform the
+ requested operation as if the If-Unmodified-Since header were not
+ present.
+
+ If the requested variant has been modified since the specified time,
+ the server MUST NOT perform the requested operation, and MUST return
+ a 412 (Precondition Failed).
+
+ If-Unmodified-Since = "If-Unmodified-Since" ":" HTTP-date
+
+ An example of the field is:
+
+ If-Unmodified-Since: Sat, 29 Oct 1994 19:43:31 GMT
+
+ If the request normally (i.e., without the If-Unmodified-Since
+ header) would result in anything other than a 2xx or 412 status, the
+ If-Unmodified-Since header SHOULD be ignored.
+
+ If the specified date is invalid, the header is ignored.
+
+ The result of a request having both an If-Unmodified-Since header
+ field and either an If-None-Match or an If-Modified-Since header
+ fields is undefined by this specification.
+
+14.29 Last-Modified
+
+ The Last-Modified entity-header field indicates the date and time at
+ which the origin server believes the variant was last modified.
+
+ Last-Modified = "Last-Modified" ":" HTTP-date
+
+
+
+Fielding, et al. Standards Track [Page 134]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ An example of its use is
+
+ Last-Modified: Tue, 15 Nov 1994 12:45:26 GMT
+
+ The exact meaning of this header field depends on the implementation
+ of the origin server and the nature of the original resource. For
+ files, it may be just the file system last-modified time. For
+ entities with dynamically included parts, it may be the most recent
+ of the set of last-modify times for its component parts. For database
+ gateways, it may be the last-update time stamp of the record. For
+ virtual objects, it may be the last time the internal state changed.
+
+ An origin server MUST NOT send a Last-Modified date which is later
+ than the server's time of message origination. In such cases, where
+ the resource's last modification would indicate some time in the
+ future, the server MUST replace that date with the message
+ origination date.
+
+ An origin server SHOULD obtain the Last-Modified value of the entity
+ as close as possible to the time that it generates the Date value of
+ its response. This allows a recipient to make an accurate assessment
+ of the entity's modification time, especially if the entity changes
+ near the time that the response is generated.
+
+ HTTP/1.1 servers SHOULD send Last-Modified whenever feasible.
+
+14.30 Location
+
+ The Location response-header field is used to redirect the recipient
+ to a location other than the Request-URI for completion of the
+ request or identification of a new resource. For 201 (Created)
+ responses, the Location is that of the new resource which was created
+ by the request. For 3xx responses, the location SHOULD indicate the
+ server's preferred URI for automatic redirection to the resource. The
+ field value consists of a single absolute URI.
+
+ Location = "Location" ":" absoluteURI
+
+ An example is:
+
+ Location: http://www.w3.org/pub/WWW/People.html
+
+ Note: The Content-Location header field (section 14.14) differs
+ from Location in that the Content-Location identifies the original
+ location of the entity enclosed in the request. It is therefore
+ possible for a response to contain header fields for both Location
+ and Content-Location. Also see section 13.10 for cache
+ requirements of some methods.
+
+
+
+Fielding, et al. Standards Track [Page 135]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+14.31 Max-Forwards
+
+ The Max-Forwards request-header field provides a mechanism with the
+ TRACE (section 9.8) and OPTIONS (section 9.2) methods to limit the
+ number of proxies or gateways that can forward the request to the
+ next inbound server. This can be useful when the client is attempting
+ to trace a request chain which appears to be failing or looping in
+ mid-chain.
+
+ Max-Forwards = "Max-Forwards" ":" 1*DIGIT
+
+ The Max-Forwards value is a decimal integer indicating the remaining
+ number of times this request message may be forwarded.
+
+ Each proxy or gateway recipient of a TRACE or OPTIONS request
+ containing a Max-Forwards header field MUST check and update its
+ value prior to forwarding the request. If the received value is zero
+ (0), the recipient MUST NOT forward the request; instead, it MUST
+ respond as the final recipient. If the received Max-Forwards value is
+ greater than zero, then the forwarded message MUST contain an updated
+ Max-Forwards field with a value decremented by one (1).
+
+ The Max-Forwards header field MAY be ignored for all other methods
+ defined by this specification and for any extension methods for which
+ it is not explicitly referred to as part of that method definition.
+
+14.32 Pragma
+
+ The Pragma general-header field is used to include implementation-
+ specific directives that might apply to any recipient along the
+ request/response chain. All pragma directives specify optional
+ behavior from the viewpoint of the protocol; however, some systems
+ MAY require that behavior be consistent with the directives.
+
+ Pragma = "Pragma" ":" 1#pragma-directive
+ pragma-directive = "no-cache" | extension-pragma
+ extension-pragma = token [ "=" ( token | quoted-string ) ]
+
+ When the no-cache directive is present in a request message, an
+ application SHOULD forward the request toward the origin server even
+ if it has a cached copy of what is being requested. This pragma
+ directive has the same semantics as the no-cache cache-directive (see
+ section 14.9) and is defined here for backward compatibility with
+ HTTP/1.0. Clients SHOULD include both header fields when a no-cache
+ request is sent to a server not known to be HTTP/1.1 compliant.
+
+
+
+
+
+
+Fielding, et al. Standards Track [Page 136]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ Pragma directives MUST be passed through by a proxy or gateway
+ application, regardless of their significance to that application,
+ since the directives might be applicable to all recipients along the
+ request/response chain. It is not possible to specify a pragma for a
+ specific recipient; however, any pragma directive not relevant to a
+ recipient SHOULD be ignored by that recipient.
+
+ HTTP/1.1 caches SHOULD treat "Pragma: no-cache" as if the client had
+ sent "Cache-Control: no-cache". No new Pragma directives will be
+ defined in HTTP.
+
+ Note: because the meaning of "Pragma: no-cache as a response
+ header field is not actually specified, it does not provide a
+ reliable replacement for "Cache-Control: no-cache" in a response
+
+14.33 Proxy-Authenticate
+
+ The Proxy-Authenticate response-header field MUST be included as part
+ of a 407 (Proxy Authentication Required) response. The field value
+ consists of a challenge that indicates the authentication scheme and
+ parameters applicable to the proxy for this Request-URI.
+
+ Proxy-Authenticate = "Proxy-Authenticate" ":" 1#challenge
+
+ The HTTP access authentication process is described in "HTTP
+ Authentication: Basic and Digest Access Authentication" [43]. Unlike
+ WWW-Authenticate, the Proxy-Authenticate header field applies only to
+ the current connection and SHOULD NOT be passed on to downstream
+ clients. However, an intermediate proxy might need to obtain its own
+ credentials by requesting them from the downstream client, which in
+ some circumstances will appear as if the proxy is forwarding the
+ Proxy-Authenticate header field.
+
+14.34 Proxy-Authorization
+
+ The Proxy-Authorization request-header field allows the client to
+ identify itself (or its user) to a proxy which requires
+ authentication. The Proxy-Authorization field value consists of
+ credentials containing the authentication information of the user
+ agent for the proxy and/or realm of the resource being requested.
+
+ Proxy-Authorization = "Proxy-Authorization" ":" credentials
+
+ The HTTP access authentication process is described in "HTTP
+ Authentication: Basic and Digest Access Authentication" [43] . Unlike
+ Authorization, the Proxy-Authorization header field applies only to
+ the next outbound proxy that demanded authentication using the Proxy-
+ Authenticate field. When multiple proxies are used in a chain, the
+
+
+
+Fielding, et al. Standards Track [Page 137]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ Proxy-Authorization header field is consumed by the first outbound
+ proxy that was expecting to receive credentials. A proxy MAY relay
+ the credentials from the client request to the next proxy if that is
+ the mechanism by which the proxies cooperatively authenticate a given
+ request.
+
+14.35 Range
+
+14.35.1 Byte Ranges
+
+ Since all HTTP entities are represented in HTTP messages as sequences
+ of bytes, the concept of a byte range is meaningful for any HTTP
+ entity. (However, not all clients and servers need to support byte-
+ range operations.)
+
+ Byte range specifications in HTTP apply to the sequence of bytes in
+ the entity-body (not necessarily the same as the message-body).
+
+ A byte range operation MAY specify a single range of bytes, or a set
+ of ranges within a single entity.
+
+ ranges-specifier = byte-ranges-specifier
+ byte-ranges-specifier = bytes-unit "=" byte-range-set
+ byte-range-set = 1#( byte-range-spec | suffix-byte-range-spec )
+ byte-range-spec = first-byte-pos "-" [last-byte-pos]
+ first-byte-pos = 1*DIGIT
+ last-byte-pos = 1*DIGIT
+
+ The first-byte-pos value in a byte-range-spec gives the byte-offset
+ of the first byte in a range. The last-byte-pos value gives the
+ byte-offset of the last byte in the range; that is, the byte
+ positions specified are inclusive. Byte offsets start at zero.
+
+ If the last-byte-pos value is present, it MUST be greater than or
+ equal to the first-byte-pos in that byte-range-spec, or the byte-
+ range-spec is syntactically invalid. The recipient of a byte-range-
+ set that includes one or more syntactically invalid byte-range-spec
+ values MUST ignore the header field that includes that byte-range-
+ set.
+
+ If the last-byte-pos value is absent, or if the value is greater than
+ or equal to the current length of the entity-body, last-byte-pos is
+ taken to be equal to one less than the current length of the entity-
+ body in bytes.
+
+ By its choice of last-byte-pos, a client can limit the number of
+ bytes retrieved without knowing the size of the entity.
+
+
+
+
+Fielding, et al. Standards Track [Page 138]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ suffix-byte-range-spec = "-" suffix-length
+ suffix-length = 1*DIGIT
+
+ A suffix-byte-range-spec is used to specify the suffix of the
+ entity-body, of a length given by the suffix-length value. (That is,
+ this form specifies the last N bytes of an entity-body.) If the
+ entity is shorter than the specified suffix-length, the entire
+ entity-body is used.
+
+ If a syntactically valid byte-range-set includes at least one byte-
+ range-spec whose first-byte-pos is less than the current length of
+ the entity-body, or at least one suffix-byte-range-spec with a non-
+ zero suffix-length, then the byte-range-set is satisfiable.
+ Otherwise, the byte-range-set is unsatisfiable. If the byte-range-set
+ is unsatisfiable, the server SHOULD return a response with a status
+ of 416 (Requested range not satisfiable). Otherwise, the server
+ SHOULD return a response with a status of 206 (Partial Content)
+ containing the satisfiable ranges of the entity-body.
+
+ Examples of byte-ranges-specifier values (assuming an entity-body of
+ length 10000):
+
+ - The first 500 bytes (byte offsets 0-499, inclusive): bytes=0-
+ 499
+
+ - The second 500 bytes (byte offsets 500-999, inclusive):
+ bytes=500-999
+
+ - The final 500 bytes (byte offsets 9500-9999, inclusive):
+ bytes=-500
+
+ - Or bytes=9500-
+
+ - The first and last bytes only (bytes 0 and 9999): bytes=0-0,-1
+
+ - Several legal but not canonical specifications of the second 500
+ bytes (byte offsets 500-999, inclusive):
+ bytes=500-600,601-999
+ bytes=500-700,601-999
+
+14.35.2 Range Retrieval Requests
+
+ HTTP retrieval requests using conditional or unconditional GET
+ methods MAY request one or more sub-ranges of the entity, instead of
+ the entire entity, using the Range request header, which applies to
+ the entity returned as the result of the request:
+
+ Range = "Range" ":" ranges-specifier
+
+
+
+Fielding, et al. Standards Track [Page 139]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ A server MAY ignore the Range header. However, HTTP/1.1 origin
+ servers and intermediate caches ought to support byte ranges when
+ possible, since Range supports efficient recovery from partially
+ failed transfers, and supports efficient partial retrieval of large
+ entities.
+
+ If the server supports the Range header and the specified range or
+ ranges are appropriate for the entity:
+
+ - The presence of a Range header in an unconditional GET modifies
+ what is returned if the GET is otherwise successful. In other
+ words, the response carries a status code of 206 (Partial
+ Content) instead of 200 (OK).
+
+ - The presence of a Range header in a conditional GET (a request
+ using one or both of If-Modified-Since and If-None-Match, or
+ one or both of If-Unmodified-Since and If-Match) modifies what
+ is returned if the GET is otherwise successful and the
+ condition is true. It does not affect the 304 (Not Modified)
+ response returned if the conditional is false.
+
+ In some cases, it might be more appropriate to use the If-Range
+ header (see section 14.27) in addition to the Range header.
+
+ If a proxy that supports ranges receives a Range request, forwards
+ the request to an inbound server, and receives an entire entity in
+ reply, it SHOULD only return the requested range to its client. It
+ SHOULD store the entire received response in its cache if that is
+ consistent with its cache allocation policies.
+
+14.36 Referer
+
+ The Referer[sic] request-header field allows the client to specify,
+ for the server's benefit, the address (URI) of the resource from
+ which the Request-URI was obtained (the "referrer", although the
+ header field is misspelled.) The Referer request-header allows a
+ server to generate lists of back-links to resources for interest,
+ logging, optimized caching, etc. It also allows obsolete or mistyped
+ links to be traced for maintenance. The Referer field MUST NOT be
+ sent if the Request-URI was obtained from a source that does not have
+ its own URI, such as input from the user keyboard.
+
+ Referer = "Referer" ":" ( absoluteURI | relativeURI )
+
+ Example:
+
+ Referer: http://www.w3.org/hypertext/DataSources/Overview.html
+
+
+
+
+Fielding, et al. Standards Track [Page 140]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ If the field value is a relative URI, it SHOULD be interpreted
+ relative to the Request-URI. The URI MUST NOT include a fragment. See
+ section 15.1.3 for security considerations.
+
+14.37 Retry-After
+
+ The Retry-After response-header field can be used with a 503 (Service
+ Unavailable) response to indicate how long the service is expected to
+ be unavailable to the requesting client. This field MAY also be used
+ with any 3xx (Redirection) response to indicate the minimum time the
+ user-agent is asked wait before issuing the redirected request. The
+ value of this field can be either an HTTP-date or an integer number
+ of seconds (in decimal) after the time of the response.
+
+ Retry-After = "Retry-After" ":" ( HTTP-date | delta-seconds )
+
+ Two examples of its use are
+
+ Retry-After: Fri, 31 Dec 1999 23:59:59 GMT
+ Retry-After: 120
+
+ In the latter example, the delay is 2 minutes.
+
+14.38 Server
+
+ The Server response-header field contains information about the
+ software used by the origin server to handle the request. The field
+ can contain multiple product tokens (section 3.8) and comments
+ identifying the server and any significant subproducts. The product
+ tokens are listed in order of their significance for identifying the
+ application.
+
+ Server = "Server" ":" 1*( product | comment )
+
+ Example:
+
+ Server: CERN/3.0 libwww/2.17
+
+ If the response is being forwarded through a proxy, the proxy
+ application MUST NOT modify the Server response-header. Instead, it
+ SHOULD include a Via field (as described in section 14.45).
+
+ Note: Revealing the specific software version of the server might
+ allow the server machine to become more vulnerable to attacks
+ against software that is known to contain security holes. Server
+ implementors are encouraged to make this field a configurable
+ option.
+
+
+
+
+Fielding, et al. Standards Track [Page 141]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+14.39 TE
+
+ The TE request-header field indicates what extension transfer-codings
+ it is willing to accept in the response and whether or not it is
+ willing to accept trailer fields in a chunked transfer-coding. Its
+ value may consist of the keyword "trailers" and/or a comma-separated
+ list of extension transfer-coding names with optional accept
+ parameters (as described in section 3.6).
+
+ TE = "TE" ":" #( t-codings )
+ t-codings = "trailers" | ( transfer-extension [ accept-params ] )
+
+ The presence of the keyword "trailers" indicates that the client is
+ willing to accept trailer fields in a chunked transfer-coding, as
+ defined in section 3.6.1. This keyword is reserved for use with
+ transfer-coding values even though it does not itself represent a
+ transfer-coding.
+
+ Examples of its use are:
+
+ TE: deflate
+ TE:
+ TE: trailers, deflate;q=0.5
+
+ The TE header field only applies to the immediate connection.
+ Therefore, the keyword MUST be supplied within a Connection header
+ field (section 14.10) whenever TE is present in an HTTP/1.1 message.
+
+ A server tests whether a transfer-coding is acceptable, according to
+ a TE field, using these rules:
+
+ 1. The "chunked" transfer-coding is always acceptable. If the
+ keyword "trailers" is listed, the client indicates that it is
+ willing to accept trailer fields in the chunked response on
+ behalf of itself and any downstream clients. The implication is
+ that, if given, the client is stating that either all
+ downstream clients are willing to accept trailer fields in the
+ forwarded response, or that it will attempt to buffer the
+ response on behalf of downstream recipients.
+
+ Note: HTTP/1.1 does not define any means to limit the size of a
+ chunked response such that a client can be assured of buffering
+ the entire response.
+
+ 2. If the transfer-coding being tested is one of the transfer-
+ codings listed in the TE field, then it is acceptable unless it
+ is accompanied by a qvalue of 0. (As defined in section 3.9, a
+ qvalue of 0 means "not acceptable.")
+
+
+
+Fielding, et al. Standards Track [Page 142]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ 3. If multiple transfer-codings are acceptable, then the
+ acceptable transfer-coding with the highest non-zero qvalue is
+ preferred. The "chunked" transfer-coding always has a qvalue
+ of 1.
+
+ If the TE field-value is empty or if no TE field is present, the only
+ transfer-coding is "chunked". A message with no transfer-coding is
+ always acceptable.
+
+14.40 Trailer
+
+ The Trailer general field value indicates that the given set of
+ header fields is present in the trailer of a message encoded with
+ chunked transfer-coding.
+
+ Trailer = "Trailer" ":" 1#field-name
+
+ An HTTP/1.1 message SHOULD include a Trailer header field in a
+ message using chunked transfer-coding with a non-empty trailer. Doing
+ so allows the recipient to know which header fields to expect in the
+ trailer.
+
+ If no Trailer header field is present, the trailer SHOULD NOT include
+ any header fields. See section 3.6.1 for restrictions on the use of
+ trailer fields in a "chunked" transfer-coding.
+
+ Message header fields listed in the Trailer header field MUST NOT
+ include the following header fields:
+
+ . Transfer-Encoding
+
+ . Content-Length
+
+ . Trailer
+
+14.41 Transfer-Encoding
+
+ The Transfer-Encoding general-header field indicates what (if any)
+ type of transformation has been applied to the message body in order
+ to safely transfer it between the sender and the recipient. This
+ differs from the content-coding in that the transfer-coding is a
+ property of the message, not of the entity.
+
+ Transfer-Encoding = "Transfer-Encoding" ":" 1#transfer-coding
+
+ Transfer-codings are defined in section 3.6. An example is:
+
+ Transfer-Encoding: chunked
+
+
+
+Fielding, et al. Standards Track [Page 143]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ If multiple encodings have been applied to an entity, the transfer-
+ codings MUST be listed in the order in which they were applied.
+ Additional information about the encoding parameters MAY be provided
+ by other entity-header fields not defined by this specification.
+
+ Many older HTTP/1.0 applications do not understand the Transfer-
+ Encoding header.
+
+14.42 Upgrade
+
+ The Upgrade general-header allows the client to specify what
+ additional communication protocols it supports and would like to use
+ if the server finds it appropriate to switch protocols. The server
+ MUST use the Upgrade header field within a 101 (Switching Protocols)
+ response to indicate which protocol(s) are being switched.
+
+ Upgrade = "Upgrade" ":" 1#product
+
+ For example,
+
+ Upgrade: HTTP/2.0, SHTTP/1.3, IRC/6.9, RTA/x11
+
+ The Upgrade header field is intended to provide a simple mechanism
+ for transition from HTTP/1.1 to some other, incompatible protocol. It
+ does so by allowing the client to advertise its desire to use another
+ protocol, such as a later version of HTTP with a higher major version
+ number, even though the current request has been made using HTTP/1.1.
+ This eases the difficult transition between incompatible protocols by
+ allowing the client to initiate a request in the more commonly
+ supported protocol while indicating to the server that it would like
+ to use a "better" protocol if available (where "better" is determined
+ by the server, possibly according to the nature of the method and/or
+ resource being requested).
+
+ The Upgrade header field only applies to switching application-layer
+ protocols upon the existing transport-layer connection. Upgrade
+ cannot be used to insist on a protocol change; its acceptance and use
+ by the server is optional. The capabilities and nature of the
+ application-layer communication after the protocol change is entirely
+ dependent upon the new protocol chosen, although the first action
+ after changing the protocol MUST be a response to the initial HTTP
+ request containing the Upgrade header field.
+
+ The Upgrade header field only applies to the immediate connection.
+ Therefore, the upgrade keyword MUST be supplied within a Connection
+ header field (section 14.10) whenever Upgrade is present in an
+ HTTP/1.1 message.
+
+
+
+
+Fielding, et al. Standards Track [Page 144]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ The Upgrade header field cannot be used to indicate a switch to a
+ protocol on a different connection. For that purpose, it is more
+ appropriate to use a 301, 302, 303, or 305 redirection response.
+
+ This specification only defines the protocol name "HTTP" for use by
+ the family of Hypertext Transfer Protocols, as defined by the HTTP
+ version rules of section 3.1 and future updates to this
+ specification. Any token can be used as a protocol name; however, it
+ will only be useful if both the client and server associate the name
+ with the same protocol.
+
+14.43 User-Agent
+
+ The User-Agent request-header field contains information about the
+ user agent originating the request. This is for statistical purposes,
+ the tracing of protocol violations, and automated recognition of user
+ agents for the sake of tailoring responses to avoid particular user
+ agent limitations. User agents SHOULD include this field with
+ requests. The field can contain multiple product tokens (section 3.8)
+ and comments identifying the agent and any subproducts which form a
+ significant part of the user agent. By convention, the product tokens
+ are listed in order of their significance for identifying the
+ application.
+
+ User-Agent = "User-Agent" ":" 1*( product | comment )
+
+ Example:
+
+ User-Agent: CERN-LineMode/2.15 libwww/2.17b3
+
+14.44 Vary
+
+ The Vary field value indicates the set of request-header fields that
+ fully determines, while the response is fresh, whether a cache is
+ permitted to use the response to reply to a subsequent request
+ without revalidation. For uncacheable or stale responses, the Vary
+ field value advises the user agent about the criteria that were used
+ to select the representation. A Vary field value of "*" implies that
+ a cache cannot determine from the request headers of a subsequent
+ request whether this response is the appropriate representation. See
+ section 13.6 for use of the Vary header field by caches.
+
+ Vary = "Vary" ":" ( "*" | 1#field-name )
+
+ An HTTP/1.1 server SHOULD include a Vary header field with any
+ cacheable response that is subject to server-driven negotiation.
+ Doing so allows a cache to properly interpret future requests on that
+ resource and informs the user agent about the presence of negotiation
+
+
+
+Fielding, et al. Standards Track [Page 145]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ on that resource. A server MAY include a Vary header field with a
+ non-cacheable response that is subject to server-driven negotiation,
+ since this might provide the user agent with useful information about
+ the dimensions over which the response varies at the time of the
+ response.
+
+ A Vary field value consisting of a list of field-names signals that
+ the representation selected for the response is based on a selection
+ algorithm which considers ONLY the listed request-header field values
+ in selecting the most appropriate representation. A cache MAY assume
+ that the same selection will be made for future requests with the
+ same values for the listed field names, for the duration of time for
+ which the response is fresh.
+
+ The field-names given are not limited to the set of standard
+ request-header fields defined by this specification. Field names are
+ case-insensitive.
+
+ A Vary field value of "*" signals that unspecified parameters not
+ limited to the request-headers (e.g., the network address of the
+ client), play a role in the selection of the response representation.
+ The "*" value MUST NOT be generated by a proxy server; it may only be
+ generated by an origin server.
+
+14.45 Via
+
+ The Via general-header field MUST be used by gateways and proxies to
+ indicate the intermediate protocols and recipients between the user
+ agent and the server on requests, and between the origin server and
+ the client on responses. It is analogous to the "Received" field of
+ RFC 822 [9] and is intended to be used for tracking message forwards,
+ avoiding request loops, and identifying the protocol capabilities of
+ all senders along the request/response chain.
+
+ Via = "Via" ":" 1#( received-protocol received-by [ comment ] )
+ received-protocol = [ protocol-name "/" ] protocol-version
+ protocol-name = token
+ protocol-version = token
+ received-by = ( host [ ":" port ] ) | pseudonym
+ pseudonym = token
+
+ The received-protocol indicates the protocol version of the message
+ received by the server or client along each segment of the
+ request/response chain. The received-protocol version is appended to
+ the Via field value when the message is forwarded so that information
+ about the protocol capabilities of upstream applications remains
+ visible to all recipients.
+
+
+
+
+Fielding, et al. Standards Track [Page 146]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ The protocol-name is optional if and only if it would be "HTTP". The
+ received-by field is normally the host and optional port number of a
+ recipient server or client that subsequently forwarded the message.
+ However, if the real host is considered to be sensitive information,
+ it MAY be replaced by a pseudonym. If the port is not given, it MAY
+ be assumed to be the default port of the received-protocol.
+
+ Multiple Via field values represents each proxy or gateway that has
+ forwarded the message. Each recipient MUST append its information
+ such that the end result is ordered according to the sequence of
+ forwarding applications.
+
+ Comments MAY be used in the Via header field to identify the software
+ of the recipient proxy or gateway, analogous to the User-Agent and
+ Server header fields. However, all comments in the Via field are
+ optional and MAY be removed by any recipient prior to forwarding the
+ message.
+
+ For example, a request message could be sent from an HTTP/1.0 user
+ agent to an internal proxy code-named "fred", which uses HTTP/1.1 to
+ forward the request to a public proxy at nowhere.com, which completes
+ the request by forwarding it to the origin server at www.ics.uci.edu.
+ The request received by www.ics.uci.edu would then have the following
+ Via header field:
+
+ Via: 1.0 fred, 1.1 nowhere.com (Apache/1.1)
+
+ Proxies and gateways used as a portal through a network firewall
+ SHOULD NOT, by default, forward the names and ports of hosts within
+ the firewall region. This information SHOULD only be propagated if
+ explicitly enabled. If not enabled, the received-by host of any host
+ behind the firewall SHOULD be replaced by an appropriate pseudonym
+ for that host.
+
+ For organizations that have strong privacy requirements for hiding
+ internal structures, a proxy MAY combine an ordered subsequence of
+ Via header field entries with identical received-protocol values into
+ a single such entry. For example,
+
+ Via: 1.0 ricky, 1.1 ethel, 1.1 fred, 1.0 lucy
+
+ could be collapsed to
+
+ Via: 1.0 ricky, 1.1 mertz, 1.0 lucy
+
+
+
+
+
+
+
+Fielding, et al. Standards Track [Page 147]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ Applications SHOULD NOT combine multiple entries unless they are all
+ under the same organizational control and the hosts have already been
+ replaced by pseudonyms. Applications MUST NOT combine entries which
+ have different received-protocol values.
+
+14.46 Warning
+
+ The Warning general-header field is used to carry additional
+ information about the status or transformation of a message which
+ might not be reflected in the message. This information is typically
+ used to warn about a possible lack of semantic transparency from
+ caching operations or transformations applied to the entity body of
+ the message.
+
+ Warning headers are sent with responses using:
+
+ Warning = "Warning" ":" 1#warning-value
+
+ warning-value = warn-code SP warn-agent SP warn-text
+ [SP warn-date]
+
+ warn-code = 3DIGIT
+ warn-agent = ( host [ ":" port ] ) | pseudonym
+ ; the name or pseudonym of the server adding
+ ; the Warning header, for use in debugging
+ warn-text = quoted-string
+ warn-date = <"> HTTP-date <">
+
+ A response MAY carry more than one Warning header.
+
+ The warn-text SHOULD be in a natural language and character set that
+ is most likely to be intelligible to the human user receiving the
+ response. This decision MAY be based on any available knowledge, such
+ as the location of the cache or user, the Accept-Language field in a
+ request, the Content-Language field in a response, etc. The default
+ language is English and the default character set is ISO-8859-1.
+
+ If a character set other than ISO-8859-1 is used, it MUST be encoded
+ in the warn-text using the method described in RFC 2047 [14].
+
+ Warning headers can in general be applied to any message, however
+ some specific warn-codes are specific to caches and can only be
+ applied to response messages. New Warning headers SHOULD be added
+ after any existing Warning headers. A cache MUST NOT delete any
+ Warning header that it received with a message. However, if a cache
+ successfully validates a cache entry, it SHOULD remove any Warning
+ headers previously attached to that entry except as specified for
+
+
+
+
+Fielding, et al. Standards Track [Page 148]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ specific Warning codes. It MUST then add any Warning headers received
+ in the validating response. In other words, Warning headers are those
+ that would be attached to the most recent relevant response.
+
+ When multiple Warning headers are attached to a response, the user
+ agent ought to inform the user of as many of them as possible, in the
+ order that they appear in the response. If it is not possible to
+ inform the user of all of the warnings, the user agent SHOULD follow
+ these heuristics:
+
+ - Warnings that appear early in the response take priority over
+ those appearing later in the response.
+
+ - Warnings in the user's preferred character set take priority
+ over warnings in other character sets but with identical warn-
+ codes and warn-agents.
+
+ Systems that generate multiple Warning headers SHOULD order them with
+ this user agent behavior in mind.
+
+ Requirements for the behavior of caches with respect to Warnings are
+ stated in section 13.1.2.
+
+ This is a list of the currently-defined warn-codes, each with a
+ recommended warn-text in English, and a description of its meaning.
+
+ 110 Response is stale
+ MUST be included whenever the returned response is stale.
+
+ 111 Revalidation failed
+ MUST be included if a cache returns a stale response because an
+ attempt to revalidate the response failed, due to an inability to
+ reach the server.
+
+ 112 Disconnected operation
+ SHOULD be included if the cache is intentionally disconnected from
+ the rest of the network for a period of time.
+
+ 113 Heuristic expiration
+ MUST be included if the cache heuristically chose a freshness
+ lifetime greater than 24 hours and the response's age is greater
+ than 24 hours.
+
+ 199 Miscellaneous warning
+ The warning text MAY include arbitrary information to be presented
+ to a human user, or logged. A system receiving this warning MUST
+ NOT take any automated action, besides presenting the warning to
+ the user.
+
+
+
+Fielding, et al. Standards Track [Page 149]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ 214 Transformation applied
+ MUST be added by an intermediate cache or proxy if it applies any
+ transformation changing the content-coding (as specified in the
+ Content-Encoding header) or media-type (as specified in the
+ Content-Type header) of the response, or the entity-body of the
+ response, unless this Warning code already appears in the response.
+
+ 299 Miscellaneous persistent warning
+ The warning text MAY include arbitrary information to be presented
+ to a human user, or logged. A system receiving this warning MUST
+ NOT take any automated action.
+
+ If an implementation sends a message with one or more Warning headers
+ whose version is HTTP/1.0 or lower, then the sender MUST include in
+ each warning-value a warn-date that matches the date in the response.
+
+ If an implementation receives a message with a warning-value that
+ includes a warn-date, and that warn-date is different from the Date
+ value in the response, then that warning-value MUST be deleted from
+ the message before storing, forwarding, or using it. (This prevents
+ bad consequences of naive caching of Warning header fields.) If all
+ of the warning-values are deleted for this reason, the Warning header
+ MUST be deleted as well.
+
+14.47 WWW-Authenticate
+
+ The WWW-Authenticate response-header field MUST be included in 401
+ (Unauthorized) response messages. The field value consists of at
+ least one challenge that indicates the authentication scheme(s) and
+ parameters applicable to the Request-URI.
+
+ WWW-Authenticate = "WWW-Authenticate" ":" 1#challenge
+
+ The HTTP access authentication process is described in "HTTP
+ Authentication: Basic and Digest Access Authentication" [43]. User
+ agents are advised to take special care in parsing the WWW-
+ Authenticate field value as it might contain more than one challenge,
+ or if more than one WWW-Authenticate header field is provided, the
+ contents of a challenge itself can contain a comma-separated list of
+ authentication parameters.
+
+15 Security Considerations
+
+ This section is meant to inform application developers, information
+ providers, and users of the security limitations in HTTP/1.1 as
+ described by this document. The discussion does not include
+ definitive solutions to the problems revealed, though it does make
+ some suggestions for reducing security risks.
+
+
+
+Fielding, et al. Standards Track [Page 150]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+15.1 Personal Information
+
+ HTTP clients are often privy to large amounts of personal information
+ (e.g. the user's name, location, mail address, passwords, encryption
+ keys, etc.), and SHOULD be very careful to prevent unintentional
+ leakage of this information via the HTTP protocol to other sources.
+ We very strongly recommend that a convenient interface be provided
+ for the user to control dissemination of such information, and that
+ designers and implementors be particularly careful in this area.
+ History shows that errors in this area often create serious security
+ and/or privacy problems and generate highly adverse publicity for the
+ implementor's company.
+
+15.1.1 Abuse of Server Log Information
+
+ A server is in the position to save personal data about a user's
+ requests which might identify their reading patterns or subjects of
+ interest. This information is clearly confidential in nature and its
+ handling can be constrained by law in certain countries. People using
+ the HTTP protocol to provide data are responsible for ensuring that
+ such material is not distributed without the permission of any
+ individuals that are identifiable by the published results.
+
+15.1.2 Transfer of Sensitive Information
+
+ Like any generic data transfer protocol, HTTP cannot regulate the
+ content of the data that is transferred, nor is there any a priori
+ method of determining the sensitivity of any particular piece of
+ information within the context of any given request. Therefore,
+ applications SHOULD supply as much control over this information as
+ possible to the provider of that information. Four header fields are
+ worth special mention in this context: Server, Via, Referer and From.
+
+ Revealing the specific software version of the server might allow the
+ server machine to become more vulnerable to attacks against software
+ that is known to contain security holes. Implementors SHOULD make the
+ Server header field a configurable option.
+
+ Proxies which serve as a portal through a network firewall SHOULD
+ take special precautions regarding the transfer of header information
+ that identifies the hosts behind the firewall. In particular, they
+ SHOULD remove, or replace with sanitized versions, any Via fields
+ generated behind the firewall.
+
+ The Referer header allows reading patterns to be studied and reverse
+ links drawn. Although it can be very useful, its power can be abused
+ if user details are not separated from the information contained in
+
+
+
+
+Fielding, et al. Standards Track [Page 151]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ the Referer. Even when the personal information has been removed, the
+ Referer header might indicate a private document's URI whose
+ publication would be inappropriate.
+
+ The information sent in the From field might conflict with the user's
+ privacy interests or their site's security policy, and hence it
+ SHOULD NOT be transmitted without the user being able to disable,
+ enable, and modify the contents of the field. The user MUST be able
+ to set the contents of this field within a user preference or
+ application defaults configuration.
+
+ We suggest, though do not require, that a convenient toggle interface
+ be provided for the user to enable or disable the sending of From and
+ Referer information.
+
+ The User-Agent (section 14.43) or Server (section 14.38) header
+ fields can sometimes be used to determine that a specific client or
+ server have a particular security hole which might be exploited.
+ Unfortunately, this same information is often used for other valuable
+ purposes for which HTTP currently has no better mechanism.
+
+15.1.3 Encoding Sensitive Information in URI's
+
+ Because the source of a link might be private information or might
+ reveal an otherwise private information source, it is strongly
+ recommended that the user be able to select whether or not the
+ Referer field is sent. For example, a browser client could have a
+ toggle switch for browsing openly/anonymously, which would
+ respectively enable/disable the sending of Referer and From
+ information.
+
+ Clients SHOULD NOT include a Referer header field in a (non-secure)
+ HTTP request if the referring page was transferred with a secure
+ protocol.
+
+ Authors of services which use the HTTP protocol SHOULD NOT use GET
+ based forms for the submission of sensitive data, because this will
+ cause this data to be encoded in the Request-URI. Many existing
+ servers, proxies, and user agents will log the request URI in some
+ place where it might be visible to third parties. Servers can use
+ POST-based form submission instead
+
+15.1.4 Privacy Issues Connected to Accept Headers
+
+ Accept request-headers can reveal information about the user to all
+ servers which are accessed. The Accept-Language header in particular
+ can reveal information the user would consider to be of a private
+ nature, because the understanding of particular languages is often
+
+
+
+Fielding, et al. Standards Track [Page 152]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ strongly correlated to the membership of a particular ethnic group.
+ User agents which offer the option to configure the contents of an
+ Accept-Language header to be sent in every request are strongly
+ encouraged to let the configuration process include a message which
+ makes the user aware of the loss of privacy involved.
+
+ An approach that limits the loss of privacy would be for a user agent
+ to omit the sending of Accept-Language headers by default, and to ask
+ the user whether or not to start sending Accept-Language headers to a
+ server if it detects, by looking for any Vary response-header fields
+ generated by the server, that such sending could improve the quality
+ of service.
+
+ Elaborate user-customized accept header fields sent in every request,
+ in particular if these include quality values, can be used by servers
+ as relatively reliable and long-lived user identifiers. Such user
+ identifiers would allow content providers to do click-trail tracking,
+ and would allow collaborating content providers to match cross-server
+ click-trails or form submissions of individual users. Note that for
+ many users not behind a proxy, the network address of the host
+ running the user agent will also serve as a long-lived user
+ identifier. In environments where proxies are used to enhance
+ privacy, user agents ought to be conservative in offering accept
+ header configuration options to end users. As an extreme privacy
+ measure, proxies could filter the accept headers in relayed requests.
+ General purpose user agents which provide a high degree of header
+ configurability SHOULD warn users about the loss of privacy which can
+ be involved.
+
+15.2 Attacks Based On File and Path Names
+
+ Implementations of HTTP origin servers SHOULD be careful to restrict
+ the documents returned by HTTP requests to be only those that were
+ intended by the server administrators. If an HTTP server translates
+ HTTP URIs directly into file system calls, the server MUST take
+ special care not to serve files that were not intended to be
+ delivered to HTTP clients. For example, UNIX, Microsoft Windows, and
+ other operating systems use ".." as a path component to indicate a
+ directory level above the current one. On such a system, an HTTP
+ server MUST disallow any such construct in the Request-URI if it
+ would otherwise allow access to a resource outside those intended to
+ be accessible via the HTTP server. Similarly, files intended for
+ reference only internally to the server (such as access control
+ files, configuration files, and script code) MUST be protected from
+ inappropriate retrieval, since they might contain sensitive
+ information. Experience has shown that minor bugs in such HTTP server
+ implementations have turned into security risks.
+
+
+
+
+Fielding, et al. Standards Track [Page 153]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+15.3 DNS Spoofing
+
+ Clients using HTTP rely heavily on the Domain Name Service, and are
+ thus generally prone to security attacks based on the deliberate
+ mis-association of IP addresses and DNS names. Clients need to be
+ cautious in assuming the continuing validity of an IP number/DNS name
+ association.
+
+ In particular, HTTP clients SHOULD rely on their name resolver for
+ confirmation of an IP number/DNS name association, rather than
+ caching the result of previous host name lookups. Many platforms
+ already can cache host name lookups locally when appropriate, and
+ they SHOULD be configured to do so. It is proper for these lookups to
+ be cached, however, only when the TTL (Time To Live) information
+ reported by the name server makes it likely that the cached
+ information will remain useful.
+
+ If HTTP clients cache the results of host name lookups in order to
+ achieve a performance improvement, they MUST observe the TTL
+ information reported by DNS.
+
+ If HTTP clients do not observe this rule, they could be spoofed when
+ a previously-accessed server's IP address changes. As network
+ renumbering is expected to become increasingly common [24], the
+ possibility of this form of attack will grow. Observing this
+ requirement thus reduces this potential security vulnerability.
+
+ This requirement also improves the load-balancing behavior of clients
+ for replicated servers using the same DNS name and reduces the
+ likelihood of a user's experiencing failure in accessing sites which
+ use that strategy.
+
+15.4 Location Headers and Spoofing
+
+ If a single server supports multiple organizations that do not trust
+ one another, then it MUST check the values of Location and Content-
+ Location headers in responses that are generated under control of
+ said organizations to make sure that they do not attempt to
+ invalidate resources over which they have no authority.
+
+15.5 Content-Disposition Issues
+
+ RFC 1806 [35], from which the often implemented Content-Disposition
+ (see section 19.5.1) header in HTTP is derived, has a number of very
+ serious security considerations. Content-Disposition is not part of
+ the HTTP standard, but since it is widely implemented, we are
+ documenting its use and risks for implementors. See RFC 2183 [49]
+ (which updates RFC 1806) for details.
+
+
+
+Fielding, et al. Standards Track [Page 154]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+15.6 Authentication Credentials and Idle Clients
+
+ Existing HTTP clients and user agents typically retain authentication
+ information indefinitely. HTTP/1.1. does not provide a method for a
+ server to direct clients to discard these cached credentials. This is
+ a significant defect that requires further extensions to HTTP.
+ Circumstances under which credential caching can interfere with the
+ application's security model include but are not limited to:
+
+ - Clients which have been idle for an extended period following
+ which the server might wish to cause the client to reprompt the
+ user for credentials.
+
+ - Applications which include a session termination indication
+ (such as a `logout' or `commit' button on a page) after which
+ the server side of the application `knows' that there is no
+ further reason for the client to retain the credentials.
+
+ This is currently under separate study. There are a number of work-
+ arounds to parts of this problem, and we encourage the use of
+ password protection in screen savers, idle time-outs, and other
+ methods which mitigate the security problems inherent in this
+ problem. In particular, user agents which cache credentials are
+ encouraged to provide a readily accessible mechanism for discarding
+ cached credentials under user control.
+
+15.7 Proxies and Caching
+
+ By their very nature, HTTP proxies are men-in-the-middle, and
+ represent an opportunity for man-in-the-middle attacks. Compromise of
+ the systems on which the proxies run can result in serious security
+ and privacy problems. Proxies have access to security-related
+ information, personal information about individual users and
+ organizations, and proprietary information belonging to users and
+ content providers. A compromised proxy, or a proxy implemented or
+ configured without regard to security and privacy considerations,
+ might be used in the commission of a wide range of potential attacks.
+
+ Proxy operators should protect the systems on which proxies run as
+ they would protect any system that contains or transports sensitive
+ information. In particular, log information gathered at proxies often
+ contains highly sensitive personal information, and/or information
+ about organizations. Log information should be carefully guarded, and
+ appropriate guidelines for use developed and followed. (Section
+ 15.1.1).
+
+
+
+
+
+
+Fielding, et al. Standards Track [Page 155]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ Caching proxies provide additional potential vulnerabilities, since
+ the contents of the cache represent an attractive target for
+ malicious exploitation. Because cache contents persist after an HTTP
+ request is complete, an attack on the cache can reveal information
+ long after a user believes that the information has been removed from
+ the network. Therefore, cache contents should be protected as
+ sensitive information.
+
+ Proxy implementors should consider the privacy and security
+ implications of their design and coding decisions, and of the
+ configuration options they provide to proxy operators (especially the
+ default configuration).
+
+ Users of a proxy need to be aware that they are no trustworthier than
+ the people who run the proxy; HTTP itself cannot solve this problem.
+
+ The judicious use of cryptography, when appropriate, may suffice to
+ protect against a broad range of security and privacy attacks. Such
+ cryptography is beyond the scope of the HTTP/1.1 specification.
+
+15.7.1 Denial of Service Attacks on Proxies
+
+ They exist. They are hard to defend against. Research continues.
+ Beware.
+
+16 Acknowledgments
+
+ This specification makes heavy use of the augmented BNF and generic
+ constructs defined by David H. Crocker for RFC 822 [9]. Similarly, it
+ reuses many of the definitions provided by Nathaniel Borenstein and
+ Ned Freed for MIME [7]. We hope that their inclusion in this
+ specification will help reduce past confusion over the relationship
+ between HTTP and Internet mail message formats.
+
+ The HTTP protocol has evolved considerably over the years. It has
+ benefited from a large and active developer community--the many
+ people who have participated on the www-talk mailing list--and it is
+ that community which has been most responsible for the success of
+ HTTP and of the World-Wide Web in general. Marc Andreessen, Robert
+ Cailliau, Daniel W. Connolly, Bob Denny, John Franks, Jean-Francois
+ Groff, Phillip M. Hallam-Baker, Hakon W. Lie, Ari Luotonen, Rob
+ McCool, Lou Montulli, Dave Raggett, Tony Sanders, and Marc
+ VanHeyningen deserve special recognition for their efforts in
+ defining early aspects of the protocol.
+
+ This document has benefited greatly from the comments of all those
+ participating in the HTTP-WG. In addition to those already mentioned,
+ the following individuals have contributed to this specification:
+
+
+
+Fielding, et al. Standards Track [Page 156]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ Gary Adams Ross Patterson
+ Harald Tveit Alvestrand Albert Lunde
+ Keith Ball John C. Mallery
+ Brian Behlendorf Jean-Philippe Martin-Flatin
+ Paul Burchard Mitra
+ Maurizio Codogno David Morris
+ Mike Cowlishaw Gavin Nicol
+ Roman Czyborra Bill Perry
+ Michael A. Dolan Jeffrey Perry
+ David J. Fiander Scott Powers
+ Alan Freier Owen Rees
+ Marc Hedlund Luigi Rizzo
+ Greg Herlihy David Robinson
+ Koen Holtman Marc Salomon
+ Alex Hopmann Rich Salz
+ Bob Jernigan Allan M. Schiffman
+ Shel Kaphan Jim Seidman
+ Rohit Khare Chuck Shotton
+ John Klensin Eric W. Sink
+ Martijn Koster Simon E. Spero
+ Alexei Kosut Richard N. Taylor
+ David M. Kristol Robert S. Thau
+ Daniel LaLiberte Bill (BearHeart) Weinman
+ Ben Laurie Francois Yergeau
+ Paul J. Leach Mary Ellen Zurko
+ Daniel DuBois Josh Cohen
+
+
+ Much of the content and presentation of the caching design is due to
+ suggestions and comments from individuals including: Shel Kaphan,
+ Paul Leach, Koen Holtman, David Morris, and Larry Masinter.
+
+ Most of the specification of ranges is based on work originally done
+ by Ari Luotonen and John Franks, with additional input from Steve
+ Zilles.
+
+ Thanks to the "cave men" of Palo Alto. You know who you are.
+
+ Jim Gettys (the current editor of this document) wishes particularly
+ to thank Roy Fielding, the previous editor of this document, along
+ with John Klensin, Jeff Mogul, Paul Leach, Dave Kristol, Koen
+ Holtman, John Franks, Josh Cohen, Alex Hopmann, Scott Lawrence, and
+ Larry Masinter for their help. And thanks go particularly to Jeff
+ Mogul and Scott Lawrence for performing the "MUST/MAY/SHOULD" audit.
+
+
+
+
+
+
+
+Fielding, et al. Standards Track [Page 157]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ The Apache Group, Anselm Baird-Smith, author of Jigsaw, and Henrik
+ Frystyk implemented RFC 2068 early, and we wish to thank them for the
+ discovery of many of the problems that this document attempts to
+ rectify.
+
+17 References
+
+ [1] Alvestrand, H., "Tags for the Identification of Languages", RFC
+ 1766, March 1995.
+
+ [2] Anklesaria, F., McCahill, M., Lindner, P., Johnson, D., Torrey,
+ D. and B. Alberti, "The Internet Gopher Protocol (a distributed
+ document search and retrieval protocol)", RFC 1436, March 1993.
+
+ [3] Berners-Lee, T., "Universal Resource Identifiers in WWW", RFC
+ 1630, June 1994.
+
+ [4] Berners-Lee, T., Masinter, L. and M. McCahill, "Uniform Resource
+ Locators (URL)", RFC 1738, December 1994.
+
+ [5] Berners-Lee, T. and D. Connolly, "Hypertext Markup Language -
+ 2.0", RFC 1866, November 1995.
+
+ [6] Berners-Lee, T., Fielding, R. and H. Frystyk, "Hypertext Transfer
+ Protocol -- HTTP/1.0", RFC 1945, May 1996.
+
+ [7] Freed, N. and N. Borenstein, "Multipurpose Internet Mail
+ Extensions (MIME) Part One: Format of Internet Message Bodies",
+ RFC 2045, November 1996.
+
+ [8] Braden, R., "Requirements for Internet Hosts -- Communication
+ Layers", STD 3, RFC 1123, October 1989.
+
+ [9] Crocker, D., "Standard for The Format of ARPA Internet Text
+ Messages", STD 11, RFC 822, August 1982.
+
+ [10] Davis, F., Kahle, B., Morris, H., Salem, J., Shen, T., Wang, R.,
+ Sui, J., and M. Grinbaum, "WAIS Interface Protocol Prototype
+ Functional Specification," (v1.5), Thinking Machines
+ Corporation, April 1990.
+
+ [11] Fielding, R., "Relative Uniform Resource Locators", RFC 1808,
+ June 1995.
+
+ [12] Horton, M. and R. Adams, "Standard for Interchange of USENET
+ Messages", RFC 1036, December 1987.
+
+
+
+
+
+Fielding, et al. Standards Track [Page 158]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ [13] Kantor, B. and P. Lapsley, "Network News Transfer Protocol", RFC
+ 977, February 1986.
+
+ [14] Moore, K., "MIME (Multipurpose Internet Mail Extensions) Part
+ Three: Message Header Extensions for Non-ASCII Text", RFC 2047,
+ November 1996.
+
+ [15] Nebel, E. and L. Masinter, "Form-based File Upload in HTML", RFC
+ 1867, November 1995.
+
+ [16] Postel, J., "Simple Mail Transfer Protocol", STD 10, RFC 821,
+ August 1982.
+
+ [17] Postel, J., "Media Type Registration Procedure", RFC 1590,
+ November 1996.
+
+ [18] Postel, J. and J. Reynolds, "File Transfer Protocol", STD 9, RFC
+ 959, October 1985.
+
+ [19] Reynolds, J. and J. Postel, "Assigned Numbers", STD 2, RFC 1700,
+ October 1994.
+
+ [20] Sollins, K. and L. Masinter, "Functional Requirements for
+ Uniform Resource Names", RFC 1737, December 1994.
+
+ [21] US-ASCII. Coded Character Set - 7-Bit American Standard Code for
+ Information Interchange. Standard ANSI X3.4-1986, ANSI, 1986.
+
+ [22] ISO-8859. International Standard -- Information Processing --
+ 8-bit Single-Byte Coded Graphic Character Sets --
+ Part 1: Latin alphabet No. 1, ISO-8859-1:1987.
+ Part 2: Latin alphabet No. 2, ISO-8859-2, 1987.
+ Part 3: Latin alphabet No. 3, ISO-8859-3, 1988.
+ Part 4: Latin alphabet No. 4, ISO-8859-4, 1988.
+ Part 5: Latin/Cyrillic alphabet, ISO-8859-5, 1988.
+ Part 6: Latin/Arabic alphabet, ISO-8859-6, 1987.
+ Part 7: Latin/Greek alphabet, ISO-8859-7, 1987.
+ Part 8: Latin/Hebrew alphabet, ISO-8859-8, 1988.
+ Part 9: Latin alphabet No. 5, ISO-8859-9, 1990.
+
+ [23] Meyers, J. and M. Rose, "The Content-MD5 Header Field", RFC
+ 1864, October 1995.
+
+ [24] Carpenter, B. and Y. Rekhter, "Renumbering Needs Work", RFC
+ 1900, February 1996.
+
+ [25] Deutsch, P., "GZIP file format specification version 4.3", RFC
+ 1952, May 1996.
+
+
+
+Fielding, et al. Standards Track [Page 159]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ [26] Venkata N. Padmanabhan, and Jeffrey C. Mogul. "Improving HTTP
+ Latency", Computer Networks and ISDN Systems, v. 28, pp. 25-35,
+ Dec. 1995. Slightly revised version of paper in Proc. 2nd
+ International WWW Conference '94: Mosaic and the Web, Oct. 1994,
+ which is available at
+ http://www.ncsa.uiuc.edu/SDG/IT94/Proceedings/DDay/mogul/HTTPLat
+ ency.html.
+
+ [27] Joe Touch, John Heidemann, and Katia Obraczka. "Analysis of HTTP
+ Performance", <URL: http://www.isi.edu/touch/pubs/http-perf96/>,
+ ISI Research Report ISI/RR-98-463, (original report dated Aug.
+ 1996), USC/Information Sciences Institute, August 1998.
+
+ [28] Mills, D., "Network Time Protocol (Version 3) Specification,
+ Implementation and Analysis", RFC 1305, March 1992.
+
+ [29] Deutsch, P., "DEFLATE Compressed Data Format Specification
+ version 1.3", RFC 1951, May 1996.
+
+ [30] S. Spero, "Analysis of HTTP Performance Problems,"
+ http://sunsite.unc.edu/mdma-release/http-prob.html.
+
+ [31] Deutsch, P. and J. Gailly, "ZLIB Compressed Data Format
+ Specification version 3.3", RFC 1950, May 1996.
+
+ [32] Franks, J., Hallam-Baker, P., Hostetler, J., Leach, P.,
+ Luotonen, A., Sink, E. and L. Stewart, "An Extension to HTTP:
+ Digest Access Authentication", RFC 2069, January 1997.
+
+ [33] Fielding, R., Gettys, J., Mogul, J., Frystyk, H. and T.
+ Berners-Lee, "Hypertext Transfer Protocol -- HTTP/1.1", RFC
+ 2068, January 1997.
+
+ [34] Bradner, S., "Key words for use in RFCs to Indicate Requirement
+ Levels", BCP 14, RFC 2119, March 1997.
+
+ [35] Troost, R. and Dorner, S., "Communicating Presentation
+ Information in Internet Messages: The Content-Disposition
+ Header", RFC 1806, June 1995.
+
+ [36] Mogul, J., Fielding, R., Gettys, J. and H. Frystyk, "Use and
+ Interpretation of HTTP Version Numbers", RFC 2145, May 1997.
+ [jg639]
+
+ [37] Palme, J., "Common Internet Message Headers", RFC 2076, February
+ 1997. [jg640]
+
+
+
+
+
+Fielding, et al. Standards Track [Page 160]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ [38] Yergeau, F., "UTF-8, a transformation format of Unicode and
+ ISO-10646", RFC 2279, January 1998. [jg641]
+
+ [39] Nielsen, H.F., Gettys, J., Baird-Smith, A., Prud'hommeaux, E.,
+ Lie, H., and C. Lilley. "Network Performance Effects of
+ HTTP/1.1, CSS1, and PNG," Proceedings of ACM SIGCOMM '97, Cannes
+ France, September 1997.[jg642]
+
+ [40] Freed, N. and N. Borenstein, "Multipurpose Internet Mail
+ Extensions (MIME) Part Two: Media Types", RFC 2046, November
+ 1996. [jg643]
+
+ [41] Alvestrand, H., "IETF Policy on Character Sets and Languages",
+ BCP 18, RFC 2277, January 1998. [jg644]
+
+ [42] Berners-Lee, T., Fielding, R. and L. Masinter, "Uniform Resource
+ Identifiers (URI): Generic Syntax and Semantics", RFC 2396,
+ August 1998. [jg645]
+
+ [43] Franks, J., Hallam-Baker, P., Hostetler, J., Lawrence, S.,
+ Leach, P., Luotonen, A., Sink, E. and L. Stewart, "HTTP
+ Authentication: Basic and Digest Access Authentication", RFC
+ 2617, June 1999. [jg646]
+
+ [44] Luotonen, A., "Tunneling TCP based protocols through Web proxy
+ servers," Work in Progress. [jg647]
+
+ [45] Palme, J. and A. Hopmann, "MIME E-mail Encapsulation of
+ Aggregate Documents, such as HTML (MHTML)", RFC 2110, March
+ 1997.
+
+ [46] Bradner, S., "The Internet Standards Process -- Revision 3", BCP
+ 9, RFC 2026, October 1996.
+
+ [47] Masinter, L., "Hyper Text Coffee Pot Control Protocol
+ (HTCPCP/1.0)", RFC 2324, 1 April 1998.
+
+ [48] Freed, N. and N. Borenstein, "Multipurpose Internet Mail
+ Extensions (MIME) Part Five: Conformance Criteria and Examples",
+ RFC 2049, November 1996.
+
+ [49] Troost, R., Dorner, S. and K. Moore, "Communicating Presentation
+ Information in Internet Messages: The Content-Disposition Header
+ Field", RFC 2183, August 1997.
+
+
+
+
+
+
+
+Fielding, et al. Standards Track [Page 161]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+18 Authors' Addresses
+
+ Roy T. Fielding
+ Information and Computer Science
+ University of California, Irvine
+ Irvine, CA 92697-3425, USA
+
+ Fax: +1 (949) 824-1715
+ EMail: fielding@ics.uci.edu
+
+
+ James Gettys
+ World Wide Web Consortium
+ MIT Laboratory for Computer Science
+ 545 Technology Square
+ Cambridge, MA 02139, USA
+
+ Fax: +1 (617) 258 8682
+ EMail: jg@w3.org
+
+
+ Jeffrey C. Mogul
+ Western Research Laboratory
+ Compaq Computer Corporation
+ 250 University Avenue
+ Palo Alto, California, 94305, USA
+
+ EMail: mogul@wrl.dec.com
+
+
+ Henrik Frystyk Nielsen
+ World Wide Web Consortium
+ MIT Laboratory for Computer Science
+ 545 Technology Square
+ Cambridge, MA 02139, USA
+
+ Fax: +1 (617) 258 8682
+ EMail: frystyk@w3.org
+
+
+ Larry Masinter
+ Xerox Corporation
+ 3333 Coyote Hill Road
+ Palo Alto, CA 94034, USA
+
+ EMail: masinter@parc.xerox.com
+
+
+
+
+
+Fielding, et al. Standards Track [Page 162]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ Paul J. Leach
+ Microsoft Corporation
+ 1 Microsoft Way
+ Redmond, WA 98052, USA
+
+ EMail: paulle@microsoft.com
+
+
+ Tim Berners-Lee
+ Director, World Wide Web Consortium
+ MIT Laboratory for Computer Science
+ 545 Technology Square
+ Cambridge, MA 02139, USA
+
+ Fax: +1 (617) 258 8682
+ EMail: timbl@w3.org
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Fielding, et al. Standards Track [Page 163]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+19 Appendices
+
+19.1 Internet Media Type message/http and application/http
+
+ In addition to defining the HTTP/1.1 protocol, this document serves
+ as the specification for the Internet media type "message/http" and
+ "application/http". The message/http type can be used to enclose a
+ single HTTP request or response message, provided that it obeys the
+ MIME restrictions for all "message" types regarding line length and
+ encodings. The application/http type can be used to enclose a
+ pipeline of one or more HTTP request or response messages (not
+ intermixed). The following is to be registered with IANA [17].
+
+ Media Type name: message
+ Media subtype name: http
+ Required parameters: none
+ Optional parameters: version, msgtype
+ version: The HTTP-Version number of the enclosed message
+ (e.g., "1.1"). If not present, the version can be
+ determined from the first line of the body.
+ msgtype: The message type -- "request" or "response". If not
+ present, the type can be determined from the first
+ line of the body.
+ Encoding considerations: only "7bit", "8bit", or "binary" are
+ permitted
+ Security considerations: none
+
+ Media Type name: application
+ Media subtype name: http
+ Required parameters: none
+ Optional parameters: version, msgtype
+ version: The HTTP-Version number of the enclosed messages
+ (e.g., "1.1"). If not present, the version can be
+ determined from the first line of the body.
+ msgtype: The message type -- "request" or "response". If not
+ present, the type can be determined from the first
+ line of the body.
+ Encoding considerations: HTTP messages enclosed by this type
+ are in "binary" format; use of an appropriate
+ Content-Transfer-Encoding is required when
+ transmitted via E-mail.
+ Security considerations: none
+
+
+
+
+
+
+
+
+
+Fielding, et al. Standards Track [Page 164]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+19.2 Internet Media Type multipart/byteranges
+
+ When an HTTP 206 (Partial Content) response message includes the
+ content of multiple ranges (a response to a request for multiple
+ non-overlapping ranges), these are transmitted as a multipart
+ message-body. The media type for this purpose is called
+ "multipart/byteranges".
+
+ The multipart/byteranges media type includes two or more parts, each
+ with its own Content-Type and Content-Range fields. The required
+ boundary parameter specifies the boundary string used to separate
+ each body-part.
+
+ Media Type name: multipart
+ Media subtype name: byteranges
+ Required parameters: boundary
+ Optional parameters: none
+ Encoding considerations: only "7bit", "8bit", or "binary" are
+ permitted
+ Security considerations: none
+
+
+ For example:
+
+ HTTP/1.1 206 Partial Content
+ Date: Wed, 15 Nov 1995 06:25:24 GMT
+ Last-Modified: Wed, 15 Nov 1995 04:58:08 GMT
+ Content-type: multipart/byteranges; boundary=THIS_STRING_SEPARATES
+
+ --THIS_STRING_SEPARATES
+ Content-type: application/pdf
+ Content-range: bytes 500-999/8000
+
+ ...the first range...
+ --THIS_STRING_SEPARATES
+ Content-type: application/pdf
+ Content-range: bytes 7000-7999/8000
+
+ ...the second range
+ --THIS_STRING_SEPARATES--
+
+ Notes:
+
+ 1) Additional CRLFs may precede the first boundary string in the
+ entity.
+
+
+
+
+
+
+Fielding, et al. Standards Track [Page 165]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ 2) Although RFC 2046 [40] permits the boundary string to be
+ quoted, some existing implementations handle a quoted boundary
+ string incorrectly.
+
+ 3) A number of browsers and servers were coded to an early draft
+ of the byteranges specification to use a media type of
+ multipart/x-byteranges, which is almost, but not quite
+ compatible with the version documented in HTTP/1.1.
+
+19.3 Tolerant Applications
+
+ Although this document specifies the requirements for the generation
+ of HTTP/1.1 messages, not all applications will be correct in their
+ implementation. We therefore recommend that operational applications
+ be tolerant of deviations whenever those deviations can be
+ interpreted unambiguously.
+
+ Clients SHOULD be tolerant in parsing the Status-Line and servers
+ tolerant when parsing the Request-Line. In particular, they SHOULD
+ accept any amount of SP or HT characters between fields, even though
+ only a single SP is required.
+
+ The line terminator for message-header fields is the sequence CRLF.
+ However, we recommend that applications, when parsing such headers,
+ recognize a single LF as a line terminator and ignore the leading CR.
+
+ The character set of an entity-body SHOULD be labeled as the lowest
+ common denominator of the character codes used within that body, with
+ the exception that not labeling the entity is preferred over labeling
+ the entity with the labels US-ASCII or ISO-8859-1. See section 3.7.1
+ and 3.4.1.
+
+ Additional rules for requirements on parsing and encoding of dates
+ and other potential problems with date encodings include:
+
+ - HTTP/1.1 clients and caches SHOULD assume that an RFC-850 date
+ which appears to be more than 50 years in the future is in fact
+ in the past (this helps solve the "year 2000" problem).
+
+ - An HTTP/1.1 implementation MAY internally represent a parsed
+ Expires date as earlier than the proper value, but MUST NOT
+ internally represent a parsed Expires date as later than the
+ proper value.
+
+ - All expiration-related calculations MUST be done in GMT. The
+ local time zone MUST NOT influence the calculation or comparison
+ of an age or expiration time.
+
+
+
+
+Fielding, et al. Standards Track [Page 166]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ - If an HTTP header incorrectly carries a date value with a time
+ zone other than GMT, it MUST be converted into GMT using the
+ most conservative possible conversion.
+
+19.4 Differences Between HTTP Entities and RFC 2045 Entities
+
+ HTTP/1.1 uses many of the constructs defined for Internet Mail (RFC
+ 822 [9]) and the Multipurpose Internet Mail Extensions (MIME [7]) to
+ allow entities to be transmitted in an open variety of
+ representations and with extensible mechanisms. However, RFC 2045
+ discusses mail, and HTTP has a few features that are different from
+ those described in RFC 2045. These differences were carefully chosen
+ to optimize performance over binary connections, to allow greater
+ freedom in the use of new media types, to make date comparisons
+ easier, and to acknowledge the practice of some early HTTP servers
+ and clients.
+
+ This appendix describes specific areas where HTTP differs from RFC
+ 2045. Proxies and gateways to strict MIME environments SHOULD be
+ aware of these differences and provide the appropriate conversions
+ where necessary. Proxies and gateways from MIME environments to HTTP
+ also need to be aware of the differences because some conversions
+ might be required.
+
+19.4.1 MIME-Version
+
+ HTTP is not a MIME-compliant protocol. However, HTTP/1.1 messages MAY
+ include a single MIME-Version general-header field to indicate what
+ version of the MIME protocol was used to construct the message. Use
+ of the MIME-Version header field indicates that the message is in
+ full compliance with the MIME protocol (as defined in RFC 2045[7]).
+ Proxies/gateways are responsible for ensuring full compliance (where
+ possible) when exporting HTTP messages to strict MIME environments.
+
+ MIME-Version = "MIME-Version" ":" 1*DIGIT "." 1*DIGIT
+
+ MIME version "1.0" is the default for use in HTTP/1.1. However,
+ HTTP/1.1 message parsing and semantics are defined by this document
+ and not the MIME specification.
+
+19.4.2 Conversion to Canonical Form
+
+ RFC 2045 [7] requires that an Internet mail entity be converted to
+ canonical form prior to being transferred, as described in section 4
+ of RFC 2049 [48]. Section 3.7.1 of this document describes the forms
+ allowed for subtypes of the "text" media type when transmitted over
+ HTTP. RFC 2046 requires that content with a type of "text" represent
+ line breaks as CRLF and forbids the use of CR or LF outside of line
+
+
+
+Fielding, et al. Standards Track [Page 167]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ break sequences. HTTP allows CRLF, bare CR, and bare LF to indicate a
+ line break within text content when a message is transmitted over
+ HTTP.
+
+ Where it is possible, a proxy or gateway from HTTP to a strict MIME
+ environment SHOULD translate all line breaks within the text media
+ types described in section 3.7.1 of this document to the RFC 2049
+ canonical form of CRLF. Note, however, that this might be complicated
+ by the presence of a Content-Encoding and by the fact that HTTP
+ allows the use of some character sets which do not use octets 13 and
+ 10 to represent CR and LF, as is the case for some multi-byte
+ character sets.
+
+ Implementors should note that conversion will break any cryptographic
+ checksums applied to the original content unless the original content
+ is already in canonical form. Therefore, the canonical form is
+ recommended for any content that uses such checksums in HTTP.
+
+19.4.3 Conversion of Date Formats
+
+ HTTP/1.1 uses a restricted set of date formats (section 3.3.1) to
+ simplify the process of date comparison. Proxies and gateways from
+ other protocols SHOULD ensure that any Date header field present in a
+ message conforms to one of the HTTP/1.1 formats and rewrite the date
+ if necessary.
+
+19.4.4 Introduction of Content-Encoding
+
+ RFC 2045 does not include any concept equivalent to HTTP/1.1's
+ Content-Encoding header field. Since this acts as a modifier on the
+ media type, proxies and gateways from HTTP to MIME-compliant
+ protocols MUST either change the value of the Content-Type header
+ field or decode the entity-body before forwarding the message. (Some
+ experimental applications of Content-Type for Internet mail have used
+ a media-type parameter of ";conversions=<content-coding>" to perform
+ a function equivalent to Content-Encoding. However, this parameter is
+ not part of RFC 2045.)
+
+19.4.5 No Content-Transfer-Encoding
+
+ HTTP does not use the Content-Transfer-Encoding (CTE) field of RFC
+ 2045. Proxies and gateways from MIME-compliant protocols to HTTP MUST
+ remove any non-identity CTE ("quoted-printable" or "base64") encoding
+ prior to delivering the response message to an HTTP client.
+
+ Proxies and gateways from HTTP to MIME-compliant protocols are
+ responsible for ensuring that the message is in the correct format
+ and encoding for safe transport on that protocol, where "safe
+
+
+
+Fielding, et al. Standards Track [Page 168]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ transport" is defined by the limitations of the protocol being used.
+ Such a proxy or gateway SHOULD label the data with an appropriate
+ Content-Transfer-Encoding if doing so will improve the likelihood of
+ safe transport over the destination protocol.
+
+19.4.6 Introduction of Transfer-Encoding
+
+ HTTP/1.1 introduces the Transfer-Encoding header field (section
+ 14.41). Proxies/gateways MUST remove any transfer-coding prior to
+ forwarding a message via a MIME-compliant protocol.
+
+ A process for decoding the "chunked" transfer-coding (section 3.6)
+ can be represented in pseudo-code as:
+
+ length := 0
+ read chunk-size, chunk-extension (if any) and CRLF
+ while (chunk-size > 0) {
+ read chunk-data and CRLF
+ append chunk-data to entity-body
+ length := length + chunk-size
+ read chunk-size and CRLF
+ }
+ read entity-header
+ while (entity-header not empty) {
+ append entity-header to existing header fields
+ read entity-header
+ }
+ Content-Length := length
+ Remove "chunked" from Transfer-Encoding
+
+19.4.7 MHTML and Line Length Limitations
+
+ HTTP implementations which share code with MHTML [45] implementations
+ need to be aware of MIME line length limitations. Since HTTP does not
+ have this limitation, HTTP does not fold long lines. MHTML messages
+ being transported by HTTP follow all conventions of MHTML, including
+ line length limitations and folding, canonicalization, etc., since
+ HTTP transports all message-bodies as payload (see section 3.7.2) and
+ does not interpret the content or any MIME header lines that might be
+ contained therein.
+
+19.5 Additional Features
+
+ RFC 1945 and RFC 2068 document protocol elements used by some
+ existing HTTP implementations, but not consistently and correctly
+ across most HTTP/1.1 applications. Implementors are advised to be
+ aware of these features, but cannot rely upon their presence in, or
+ interoperability with, other HTTP/1.1 applications. Some of these
+
+
+
+Fielding, et al. Standards Track [Page 169]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ describe proposed experimental features, and some describe features
+ that experimental deployment found lacking that are now addressed in
+ the base HTTP/1.1 specification.
+
+ A number of other headers, such as Content-Disposition and Title,
+ from SMTP and MIME are also often implemented (see RFC 2076 [37]).
+
+19.5.1 Content-Disposition
+
+ The Content-Disposition response-header field has been proposed as a
+ means for the origin server to suggest a default filename if the user
+ requests that the content is saved to a file. This usage is derived
+ from the definition of Content-Disposition in RFC 1806 [35].
+
+ content-disposition = "Content-Disposition" ":"
+ disposition-type *( ";" disposition-parm )
+ disposition-type = "attachment" | disp-extension-token
+ disposition-parm = filename-parm | disp-extension-parm
+ filename-parm = "filename" "=" quoted-string
+ disp-extension-token = token
+ disp-extension-parm = token "=" ( token | quoted-string )
+
+ An example is
+
+ Content-Disposition: attachment; filename="fname.ext"
+
+ The receiving user agent SHOULD NOT respect any directory path
+ information present in the filename-parm parameter, which is the only
+ parameter believed to apply to HTTP implementations at this time. The
+ filename SHOULD be treated as a terminal component only.
+
+ If this header is used in a response with the application/octet-
+ stream content-type, the implied suggestion is that the user agent
+ should not display the response, but directly enter a `save response
+ as...' dialog.
+
+ See section 15.5 for Content-Disposition security issues.
+
+19.6 Compatibility with Previous Versions
+
+ It is beyond the scope of a protocol specification to mandate
+ compliance with previous versions. HTTP/1.1 was deliberately
+ designed, however, to make supporting previous versions easy. It is
+ worth noting that, at the time of composing this specification
+ (1996), we would expect commercial HTTP/1.1 servers to:
+
+ - recognize the format of the Request-Line for HTTP/0.9, 1.0, and
+ 1.1 requests;
+
+
+
+Fielding, et al. Standards Track [Page 170]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ - understand any valid request in the format of HTTP/0.9, 1.0, or
+ 1.1;
+
+ - respond appropriately with a message in the same major version
+ used by the client.
+
+ And we would expect HTTP/1.1 clients to:
+
+ - recognize the format of the Status-Line for HTTP/1.0 and 1.1
+ responses;
+
+ - understand any valid response in the format of HTTP/0.9, 1.0, or
+ 1.1.
+
+ For most implementations of HTTP/1.0, each connection is established
+ by the client prior to the request and closed by the server after
+ sending the response. Some implementations implement the Keep-Alive
+ version of persistent connections described in section 19.7.1 of RFC
+ 2068 [33].
+
+19.6.1 Changes from HTTP/1.0
+
+ This section summarizes major differences between versions HTTP/1.0
+ and HTTP/1.1.
+
+19.6.1.1 Changes to Simplify Multi-homed Web Servers and Conserve IP
+ Addresses
+
+ The requirements that clients and servers support the Host request-
+ header, report an error if the Host request-header (section 14.23) is
+ missing from an HTTP/1.1 request, and accept absolute URIs (section
+ 5.1.2) are among the most important changes defined by this
+ specification.
+
+ Older HTTP/1.0 clients assumed a one-to-one relationship of IP
+ addresses and servers; there was no other established mechanism for
+ distinguishing the intended server of a request than the IP address
+ to which that request was directed. The changes outlined above will
+ allow the Internet, once older HTTP clients are no longer common, to
+ support multiple Web sites from a single IP address, greatly
+ simplifying large operational Web servers, where allocation of many
+ IP addresses to a single host has created serious problems. The
+ Internet will also be able to recover the IP addresses that have been
+ allocated for the sole purpose of allowing special-purpose domain
+ names to be used in root-level HTTP URLs. Given the rate of growth of
+ the Web, and the number of servers already deployed, it is extremely
+
+
+
+
+
+Fielding, et al. Standards Track [Page 171]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ important that all implementations of HTTP (including updates to
+ existing HTTP/1.0 applications) correctly implement these
+ requirements:
+
+ - Both clients and servers MUST support the Host request-header.
+
+ - A client that sends an HTTP/1.1 request MUST send a Host header.
+
+ - Servers MUST report a 400 (Bad Request) error if an HTTP/1.1
+ request does not include a Host request-header.
+
+ - Servers MUST accept absolute URIs.
+
+19.6.2 Compatibility with HTTP/1.0 Persistent Connections
+
+ Some clients and servers might wish to be compatible with some
+ previous implementations of persistent connections in HTTP/1.0
+ clients and servers. Persistent connections in HTTP/1.0 are
+ explicitly negotiated as they are not the default behavior. HTTP/1.0
+ experimental implementations of persistent connections are faulty,
+ and the new facilities in HTTP/1.1 are designed to rectify these
+ problems. The problem was that some existing 1.0 clients may be
+ sending Keep-Alive to a proxy server that doesn't understand
+ Connection, which would then erroneously forward it to the next
+ inbound server, which would establish the Keep-Alive connection and
+ result in a hung HTTP/1.0 proxy waiting for the close on the
+ response. The result is that HTTP/1.0 clients must be prevented from
+ using Keep-Alive when talking to proxies.
+
+ However, talking to proxies is the most important use of persistent
+ connections, so that prohibition is clearly unacceptable. Therefore,
+ we need some other mechanism for indicating a persistent connection
+ is desired, which is safe to use even when talking to an old proxy
+ that ignores Connection. Persistent connections are the default for
+ HTTP/1.1 messages; we introduce a new keyword (Connection: close) for
+ declaring non-persistence. See section 14.10.
+
+ The original HTTP/1.0 form of persistent connections (the Connection:
+ Keep-Alive and Keep-Alive header) is documented in RFC 2068. [33]
+
+19.6.3 Changes from RFC 2068
+
+ This specification has been carefully audited to correct and
+ disambiguate key word usage; RFC 2068 had many problems in respect to
+ the conventions laid out in RFC 2119 [34].
+
+ Clarified which error code should be used for inbound server failures
+ (e.g. DNS failures). (Section 10.5.5).
+
+
+
+Fielding, et al. Standards Track [Page 172]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ CREATE had a race that required an Etag be sent when a resource is
+ first created. (Section 10.2.2).
+
+ Content-Base was deleted from the specification: it was not
+ implemented widely, and there is no simple, safe way to introduce it
+ without a robust extension mechanism. In addition, it is used in a
+ similar, but not identical fashion in MHTML [45].
+
+ Transfer-coding and message lengths all interact in ways that
+ required fixing exactly when chunked encoding is used (to allow for
+ transfer encoding that may not be self delimiting); it was important
+ to straighten out exactly how message lengths are computed. (Sections
+ 3.6, 4.4, 7.2.2, 13.5.2, 14.13, 14.16)
+
+ A content-coding of "identity" was introduced, to solve problems
+ discovered in caching. (section 3.5)
+
+ Quality Values of zero should indicate that "I don't want something"
+ to allow clients to refuse a representation. (Section 3.9)
+
+ The use and interpretation of HTTP version numbers has been clarified
+ by RFC 2145. Require proxies to upgrade requests to highest protocol
+ version they support to deal with problems discovered in HTTP/1.0
+ implementations (Section 3.1)
+
+ Charset wildcarding is introduced to avoid explosion of character set
+ names in accept headers. (Section 14.2)
+
+ A case was missed in the Cache-Control model of HTTP/1.1; s-maxage
+ was introduced to add this missing case. (Sections 13.4, 14.8, 14.9,
+ 14.9.3)
+
+ The Cache-Control: max-age directive was not properly defined for
+ responses. (Section 14.9.3)
+
+ There are situations where a server (especially a proxy) does not
+ know the full length of a response but is capable of serving a
+ byterange request. We therefore need a mechanism to allow byteranges
+ with a content-range not indicating the full length of the message.
+ (Section 14.16)
+
+ Range request responses would become very verbose if all meta-data
+ were always returned; by allowing the server to only send needed
+ headers in a 206 response, this problem can be avoided. (Section
+ 10.2.7, 13.5.3, and 14.27)
+
+
+
+
+
+
+Fielding, et al. Standards Track [Page 173]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ Fix problem with unsatisfiable range requests; there are two cases:
+ syntactic problems, and range doesn't exist in the document. The 416
+ status code was needed to resolve this ambiguity needed to indicate
+ an error for a byte range request that falls outside of the actual
+ contents of a document. (Section 10.4.17, 14.16)
+
+ Rewrite of message transmission requirements to make it much harder
+ for implementors to get it wrong, as the consequences of errors here
+ can have significant impact on the Internet, and to deal with the
+ following problems:
+
+ 1. Changing "HTTP/1.1 or later" to "HTTP/1.1", in contexts where
+ this was incorrectly placing a requirement on the behavior of
+ an implementation of a future version of HTTP/1.x
+
+ 2. Made it clear that user-agents should retry requests, not
+ "clients" in general.
+
+ 3. Converted requirements for clients to ignore unexpected 100
+ (Continue) responses, and for proxies to forward 100 responses,
+ into a general requirement for 1xx responses.
+
+ 4. Modified some TCP-specific language, to make it clearer that
+ non-TCP transports are possible for HTTP.
+
+ 5. Require that the origin server MUST NOT wait for the request
+ body before it sends a required 100 (Continue) response.
+
+ 6. Allow, rather than require, a server to omit 100 (Continue) if
+ it has already seen some of the request body.
+
+ 7. Allow servers to defend against denial-of-service attacks and
+ broken clients.
+
+ This change adds the Expect header and 417 status code. The message
+ transmission requirements fixes are in sections 8.2, 10.4.18,
+ 8.1.2.2, 13.11, and 14.20.
+
+ Proxies should be able to add Content-Length when appropriate.
+ (Section 13.5.2)
+
+ Clean up confusion between 403 and 404 responses. (Section 10.4.4,
+ 10.4.5, and 10.4.11)
+
+ Warnings could be cached incorrectly, or not updated appropriately.
+ (Section 13.1.2, 13.2.4, 13.5.2, 13.5.3, 14.9.3, and 14.46) Warning
+ also needed to be a general header, as PUT or other methods may have
+ need for it in requests.
+
+
+
+Fielding, et al. Standards Track [Page 174]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+ Transfer-coding had significant problems, particularly with
+ interactions with chunked encoding. The solution is that transfer-
+ codings become as full fledged as content-codings. This involves
+ adding an IANA registry for transfer-codings (separate from content
+ codings), a new header field (TE) and enabling trailer headers in the
+ future. Transfer encoding is a major performance benefit, so it was
+ worth fixing [39]. TE also solves another, obscure, downward
+ interoperability problem that could have occurred due to interactions
+ between authentication trailers, chunked encoding and HTTP/1.0
+ clients.(Section 3.6, 3.6.1, and 14.39)
+
+ The PATCH, LINK, UNLINK methods were defined but not commonly
+ implemented in previous versions of this specification. See RFC 2068
+ [33].
+
+ The Alternates, Content-Version, Derived-From, Link, URI, Public and
+ Content-Base header fields were defined in previous versions of this
+ specification, but not commonly implemented. See RFC 2068 [33].
+
+20 Index
+
+ Please see the PostScript version of this RFC for the INDEX.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Fielding, et al. Standards Track [Page 175]
+
+RFC 2616 HTTP/1.1 June 1999
+
+
+21. Full Copyright Statement
+
+ Copyright (C) The Internet Society (1999). All Rights Reserved.
+
+ This document and translations of it may be copied and furnished to
+ others, and derivative works that comment on or otherwise explain it
+ or assist in its implementation may be prepared, copied, published
+ and distributed, in whole or in part, without restriction of any
+ kind, provided that the above copyright notice and this paragraph are
+ included on all such copies and derivative works. However, this
+ document itself may not be modified in any way, such as by removing
+ the copyright notice or references to the Internet Society or other
+ Internet organizations, except as needed for the purpose of
+ developing Internet standards in which case the procedures for
+ copyrights defined in the Internet Standards process must be
+ followed, or as required to translate it into languages other than
+ English.
+
+ The limited permissions granted above are perpetual and will not be
+ revoked by the Internet Society or its successors or assigns.
+
+ This document and the information contained herein is provided on an
+ "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING
+ TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+ HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+ MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+Acknowledgement
+
+ Funding for the RFC Editor function is currently provided by the
+ Internet Society.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Fielding, et al. Standards Track [Page 176]
+
diff --git a/standards/rfc2617.txt b/standards/rfc2617.txt
new file mode 100644
index 000000000..771aa924a
--- /dev/null
+++ b/standards/rfc2617.txt
@@ -0,0 +1,1907 @@
+
+
+
+
+
+
+Network Working Group J. Franks
+Request for Comments: 2617 Northwestern University
+Obsoletes: 2069 P. Hallam-Baker
+Category: Standards Track Verisign, Inc.
+ J. Hostetler
+ AbiSource, Inc.
+ S. Lawrence
+ Agranat Systems, Inc.
+ P. Leach
+ Microsoft Corporation
+ A. Luotonen
+ Netscape Communications Corporation
+ L. Stewart
+ Open Market, Inc.
+ June 1999
+
+
+ HTTP Authentication: Basic and Digest Access Authentication
+
+Status of this Memo
+
+ This document specifies an Internet standards track protocol for the
+ Internet community, and requests discussion and suggestions for
+ improvements. Please refer to the current edition of the "Internet
+ Official Protocol Standards" (STD 1) for the standardization state
+ and status of this protocol. Distribution of this memo is unlimited.
+
+Copyright Notice
+
+ Copyright (C) The Internet Society (1999). All Rights Reserved.
+
+Abstract
+
+ "HTTP/1.0", includes the specification for a Basic Access
+ Authentication scheme. This scheme is not considered to be a secure
+ method of user authentication (unless used in conjunction with some
+ external secure system such as SSL [5]), as the user name and
+ password are passed over the network as cleartext.
+
+ This document also provides the specification for HTTP's
+ authentication framework, the original Basic authentication scheme
+ and a scheme based on cryptographic hashes, referred to as "Digest
+ Access Authentication". It is therefore also intended to serve as a
+ replacement for RFC 2069 [6]. Some optional elements specified by
+ RFC 2069 have been removed from this specification due to problems
+ found since its publication; other new elements have been added for
+ compatibility, those new elements have been made optional, but are
+ strongly recommended.
+
+
+
+Franks, et al. Standards Track [Page 1]
+
+RFC 2617 HTTP Authentication June 1999
+
+
+ Like Basic, Digest access authentication verifies that both parties
+ to a communication know a shared secret (a password); unlike Basic,
+ this verification can be done without sending the password in the
+ clear, which is Basic's biggest weakness. As with most other
+ authentication protocols, the greatest sources of risks are usually
+ found not in the core protocol itself but in policies and procedures
+ surrounding its use.
+
+Table of Contents
+
+ 1 Access Authentication................................ 3
+ 1.1 Reliance on the HTTP/1.1 Specification............ 3
+ 1.2 Access Authentication Framework................... 3
+ 2 Basic Authentication Scheme.......................... 5
+ 3 Digest Access Authentication Scheme.................. 6
+ 3.1 Introduction...................................... 6
+ 3.1.1 Purpose......................................... 6
+ 3.1.2 Overall Operation............................... 6
+ 3.1.3 Representation of digest values................. 7
+ 3.1.4 Limitations..................................... 7
+ 3.2 Specification of Digest Headers................... 7
+ 3.2.1 The WWW-Authenticate Response Header............ 8
+ 3.2.2 The Authorization Request Header................ 11
+ 3.2.3 The Authentication-Info Header.................. 15
+ 3.3 Digest Operation.................................. 17
+ 3.4 Security Protocol Negotiation..................... 18
+ 3.5 Example........................................... 18
+ 3.6 Proxy-Authentication and Proxy-Authorization...... 19
+ 4 Security Considerations.............................. 19
+ 4.1 Authentication of Clients using Basic
+ Authentication.................................... 19
+ 4.2 Authentication of Clients using Digest
+ Authentication.................................... 20
+ 4.3 Limited Use Nonce Values.......................... 21
+ 4.4 Comparison of Digest with Basic Authentication.... 22
+ 4.5 Replay Attacks.................................... 22
+ 4.6 Weakness Created by Multiple Authentication
+ Schemes........................................... 23
+ 4.7 Online dictionary attacks......................... 23
+ 4.8 Man in the Middle................................. 24
+ 4.9 Chosen plaintext attacks.......................... 24
+ 4.10 Precomputed dictionary attacks.................... 25
+ 4.11 Batch brute force attacks......................... 25
+ 4.12 Spoofing by Counterfeit Servers................... 25
+ 4.13 Storing passwords................................. 26
+ 4.14 Summary........................................... 26
+ 5 Sample implementation................................ 27
+ 6 Acknowledgments...................................... 31
+
+
+
+Franks, et al. Standards Track [Page 2]
+
+RFC 2617 HTTP Authentication June 1999
+
+
+ 7 References........................................... 31
+ 8 Authors' Addresses................................... 32
+ 9 Full Copyright Statement............................. 34
+
+1 Access Authentication
+
+1.1 Reliance on the HTTP/1.1 Specification
+
+ This specification is a companion to the HTTP/1.1 specification [2].
+ It uses the augmented BNF section 2.1 of that document, and relies on
+ both the non-terminals defined in that document and other aspects of
+ the HTTP/1.1 specification.
+
+1.2 Access Authentication Framework
+
+ HTTP provides a simple challenge-response authentication mechanism
+ that MAY be used by a server to challenge a client request and by a
+ client to provide authentication information. It uses an extensible,
+ case-insensitive token to identify the authentication scheme,
+ followed by a comma-separated list of attribute-value pairs which
+ carry the parameters necessary for achieving authentication via that
+ scheme.
+
+ auth-scheme = token
+ auth-param = token "=" ( token | quoted-string )
+
+ The 401 (Unauthorized) response message is used by an origin server
+ to challenge the authorization of a user agent. This response MUST
+ include a WWW-Authenticate header field containing at least one
+ challenge applicable to the requested resource. The 407 (Proxy
+ Authentication Required) response message is used by a proxy to
+ challenge the authorization of a client and MUST include a Proxy-
+ Authenticate header field containing at least one challenge
+ applicable to the proxy for the requested resource.
+
+ challenge = auth-scheme 1*SP 1#auth-param
+
+ Note: User agents will need to take special care in parsing the WWW-
+ Authenticate or Proxy-Authenticate header field value if it contains
+ more than one challenge, or if more than one WWW-Authenticate header
+ field is provided, since the contents of a challenge may itself
+ contain a comma-separated list of authentication parameters.
+
+ The authentication parameter realm is defined for all authentication
+ schemes:
+
+ realm = "realm" "=" realm-value
+ realm-value = quoted-string
+
+
+
+Franks, et al. Standards Track [Page 3]
+
+RFC 2617 HTTP Authentication June 1999
+
+
+ The realm directive (case-insensitive) is required for all
+ authentication schemes that issue a challenge. The realm value
+ (case-sensitive), in combination with the canonical root URL (the
+ absoluteURI for the server whose abs_path is empty; see section 5.1.2
+ of [2]) of the server being accessed, defines the protection space.
+ These realms allow the protected resources on a server to be
+ partitioned into a set of protection spaces, each with its own
+ authentication scheme and/or authorization database. The realm value
+ is a string, generally assigned by the origin server, which may have
+ additional semantics specific to the authentication scheme. Note that
+ there may be multiple challenges with the same auth-scheme but
+ different realms.
+
+ A user agent that wishes to authenticate itself with an origin
+ server--usually, but not necessarily, after receiving a 401
+ (Unauthorized)--MAY do so by including an Authorization header field
+ with the request. A client that wishes to authenticate itself with a
+ proxy--usually, but not necessarily, after receiving a 407 (Proxy
+ Authentication Required)--MAY do so by including a Proxy-
+ Authorization header field with the request. Both the Authorization
+ field value and the Proxy-Authorization field value consist of
+ credentials containing the authentication information of the client
+ for the realm of the resource being requested. The user agent MUST
+ choose to use one of the challenges with the strongest auth-scheme it
+ understands and request credentials from the user based upon that
+ challenge.
+
+ credentials = auth-scheme #auth-param
+
+ Note that many browsers will only recognize Basic and will require
+ that it be the first auth-scheme presented. Servers should only
+ include Basic if it is minimally acceptable.
+
+ The protection space determines the domain over which credentials can
+ be automatically applied. If a prior request has been authorized, the
+ same credentials MAY be reused for all other requests within that
+ protection space for a period of time determined by the
+ authentication scheme, parameters, and/or user preference. Unless
+ otherwise defined by the authentication scheme, a single protection
+ space cannot extend outside the scope of its server.
+
+ If the origin server does not wish to accept the credentials sent
+ with a request, it SHOULD return a 401 (Unauthorized) response. The
+ response MUST include a WWW-Authenticate header field containing at
+ least one (possibly new) challenge applicable to the requested
+ resource. If a proxy does not accept the credentials sent with a
+ request, it SHOULD return a 407 (Proxy Authentication Required). The
+ response MUST include a Proxy-Authenticate header field containing a
+
+
+
+Franks, et al. Standards Track [Page 4]
+
+RFC 2617 HTTP Authentication June 1999
+
+
+ (possibly new) challenge applicable to the proxy for the requested
+ resource.
+
+ The HTTP protocol does not restrict applications to this simple
+ challenge-response mechanism for access authentication. Additional
+ mechanisms MAY be used, such as encryption at the transport level or
+ via message encapsulation, and with additional header fields
+ specifying authentication information. However, these additional
+ mechanisms are not defined by this specification.
+
+ Proxies MUST be completely transparent regarding user agent
+ authentication by origin servers. That is, they must forward the
+ WWW-Authenticate and Authorization headers untouched, and follow the
+ rules found in section 14.8 of [2]. Both the Proxy-Authenticate and
+ the Proxy-Authorization header fields are hop-by-hop headers (see
+ section 13.5.1 of [2]).
+
+2 Basic Authentication Scheme
+
+ The "basic" authentication scheme is based on the model that the
+ client must authenticate itself with a user-ID and a password for
+ each realm. The realm value should be considered an opaque string
+ which can only be compared for equality with other realms on that
+ server. The server will service the request only if it can validate
+ the user-ID and password for the protection space of the Request-URI.
+ There are no optional authentication parameters.
+
+ For Basic, the framework above is utilized as follows:
+
+ challenge = "Basic" realm
+ credentials = "Basic" basic-credentials
+
+ Upon receipt of an unauthorized request for a URI within the
+ protection space, the origin server MAY respond with a challenge like
+ the following:
+
+ WWW-Authenticate: Basic realm="WallyWorld"
+
+ where "WallyWorld" is the string assigned by the server to identify
+ the protection space of the Request-URI. A proxy may respond with the
+ same challenge using the Proxy-Authenticate header field.
+
+ To receive authorization, the client sends the userid and password,
+ separated by a single colon (":") character, within a base64 [7]
+ encoded string in the credentials.
+
+ basic-credentials = base64-user-pass
+ base64-user-pass = <base64 [4] encoding of user-pass,
+
+
+
+Franks, et al. Standards Track [Page 5]
+
+RFC 2617 HTTP Authentication June 1999
+
+
+ except not limited to 76 char/line>
+ user-pass = userid ":" password
+ userid = *<TEXT excluding ":">
+ password = *TEXT
+
+ Userids might be case sensitive.
+
+ If the user agent wishes to send the userid "Aladdin" and password
+ "open sesame", it would use the following header field:
+
+ Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==
+
+ A client SHOULD assume that all paths at or deeper than the depth of
+ the last symbolic element in the path field of the Request-URI also
+ are within the protection space specified by the Basic realm value of
+ the current challenge. A client MAY preemptively send the
+ corresponding Authorization header with requests for resources in
+ that space without receipt of another challenge from the server.
+ Similarly, when a client sends a request to a proxy, it may reuse a
+ userid and password in the Proxy-Authorization header field without
+ receiving another challenge from the proxy server. See section 4 for
+ security considerations associated with Basic authentication.
+
+3 Digest Access Authentication Scheme
+
+3.1 Introduction
+
+3.1.1 Purpose
+
+ The protocol referred to as "HTTP/1.0" includes the specification for
+ a Basic Access Authentication scheme[1]. That scheme is not
+ considered to be a secure method of user authentication, as the user
+ name and password are passed over the network in an unencrypted form.
+ This section provides the specification for a scheme that does not
+ send the password in cleartext, referred to as "Digest Access
+ Authentication".
+
+ The Digest Access Authentication scheme is not intended to be a
+ complete answer to the need for security in the World Wide Web. This
+ scheme provides no encryption of message content. The intent is
+ simply to create an access authentication method that avoids the most
+ serious flaws of Basic authentication.
+
+3.1.2 Overall Operation
+
+ Like Basic Access Authentication, the Digest scheme is based on a
+ simple challenge-response paradigm. The Digest scheme challenges
+ using a nonce value. A valid response contains a checksum (by
+
+
+
+Franks, et al. Standards Track [Page 6]
+
+RFC 2617 HTTP Authentication June 1999
+
+
+ default, the MD5 checksum) of the username, the password, the given
+ nonce value, the HTTP method, and the requested URI. In this way, the
+ password is never sent in the clear. Just as with the Basic scheme,
+ the username and password must be prearranged in some fashion not
+ addressed by this document.
+
+3.1.3 Representation of digest values
+
+ An optional header allows the server to specify the algorithm used to
+ create the checksum or digest. By default the MD5 algorithm is used
+ and that is the only algorithm described in this document.
+
+ For the purposes of this document, an MD5 digest of 128 bits is
+ represented as 32 ASCII printable characters. The bits in the 128 bit
+ digest are converted from most significant to least significant bit,
+ four bits at a time to their ASCII presentation as follows. Each four
+ bits is represented by its familiar hexadecimal notation from the
+ characters 0123456789abcdef. That is, binary 0000 gets represented by
+ the character '0', 0001, by '1', and so on up to the representation
+ of 1111 as 'f'.
+
+3.1.4 Limitations
+
+ The Digest authentication scheme described in this document suffers
+ from many known limitations. It is intended as a replacement for
+ Basic authentication and nothing more. It is a password-based system
+ and (on the server side) suffers from all the same problems of any
+ password system. In particular, no provision is made in this protocol
+ for the initial secure arrangement between user and server to
+ establish the user's password.
+
+ Users and implementors should be aware that this protocol is not as
+ secure as Kerberos, and not as secure as any client-side private-key
+ scheme. Nevertheless it is better than nothing, better than what is
+ commonly used with telnet and ftp, and better than Basic
+ authentication.
+
+3.2 Specification of Digest Headers
+
+ The Digest Access Authentication scheme is conceptually similar to
+ the Basic scheme. The formats of the modified WWW-Authenticate header
+ line and the Authorization header line are specified below. In
+ addition, a new header, Authentication-Info, is specified.
+
+
+
+
+
+
+
+
+Franks, et al. Standards Track [Page 7]
+
+RFC 2617 HTTP Authentication June 1999
+
+
+3.2.1 The WWW-Authenticate Response Header
+
+ If a server receives a request for an access-protected object, and an
+ acceptable Authorization header is not sent, the server responds with
+ a "401 Unauthorized" status code, and a WWW-Authenticate header as
+ per the framework defined above, which for the digest scheme is
+ utilized as follows:
+
+ challenge = "Digest" digest-challenge
+
+ digest-challenge = 1#( realm | [ domain ] | nonce |
+ [ opaque ] |[ stale ] | [ algorithm ] |
+ [ qop-options ] | [auth-param] )
+
+
+ domain = "domain" "=" <"> URI ( 1*SP URI ) <">
+ URI = absoluteURI | abs_path
+ nonce = "nonce" "=" nonce-value
+ nonce-value = quoted-string
+ opaque = "opaque" "=" quoted-string
+ stale = "stale" "=" ( "true" | "false" )
+ algorithm = "algorithm" "=" ( "MD5" | "MD5-sess" |
+ token )
+ qop-options = "qop" "=" <"> 1#qop-value <">
+ qop-value = "auth" | "auth-int" | token
+
+ The meanings of the values of the directives used above are as
+ follows:
+
+ realm
+ A string to be displayed to users so they know which username and
+ password to use. This string should contain at least the name of
+ the host performing the authentication and might additionally
+ indicate the collection of users who might have access. An example
+ might be "registered_users@gotham.news.com".
+
+ domain
+ A quoted, space-separated list of URIs, as specified in RFC XURI
+ [7], that define the protection space. If a URI is an abs_path, it
+ is relative to the canonical root URL (see section 1.2 above) of
+ the server being accessed. An absoluteURI in this list may refer to
+ a different server than the one being accessed. The client can use
+ this list to determine the set of URIs for which the same
+ authentication information may be sent: any URI that has a URI in
+ this list as a prefix (after both have been made absolute) may be
+ assumed to be in the same protection space. If this directive is
+ omitted or its value is empty, the client should assume that the
+ protection space consists of all URIs on the responding server.
+
+
+
+Franks, et al. Standards Track [Page 8]
+
+RFC 2617 HTTP Authentication June 1999
+
+
+ This directive is not meaningful in Proxy-Authenticate headers, for
+ which the protection space is always the entire proxy; if present
+ it should be ignored.
+
+ nonce
+ A server-specified data string which should be uniquely generated
+ each time a 401 response is made. It is recommended that this
+ string be base64 or hexadecimal data. Specifically, since the
+ string is passed in the header lines as a quoted string, the
+ double-quote character is not allowed.
+
+ The contents of the nonce are implementation dependent. The quality
+ of the implementation depends on a good choice. A nonce might, for
+ example, be constructed as the base 64 encoding of
+
+ time-stamp H(time-stamp ":" ETag ":" private-key)
+
+ where time-stamp is a server-generated time or other non-repeating
+ value, ETag is the value of the HTTP ETag header associated with
+ the requested entity, and private-key is data known only to the
+ server. With a nonce of this form a server would recalculate the
+ hash portion after receiving the client authentication header and
+ reject the request if it did not match the nonce from that header
+ or if the time-stamp value is not recent enough. In this way the
+ server can limit the time of the nonce's validity. The inclusion of
+ the ETag prevents a replay request for an updated version of the
+ resource. (Note: including the IP address of the client in the
+ nonce would appear to offer the server the ability to limit the
+ reuse of the nonce to the same client that originally got it.
+ However, that would break proxy farms, where requests from a single
+ user often go through different proxies in the farm. Also, IP
+ address spoofing is not that hard.)
+
+ An implementation might choose not to accept a previously used
+ nonce or a previously used digest, in order to protect against a
+ replay attack. Or, an implementation might choose to use one-time
+ nonces or digests for POST or PUT requests and a time-stamp for GET
+ requests. For more details on the issues involved see section 4.
+ of this document.
+
+ The nonce is opaque to the client.
+
+ opaque
+ A string of data, specified by the server, which should be returned
+ by the client unchanged in the Authorization header of subsequent
+ requests with URIs in the same protection space. It is recommended
+ that this string be base64 or hexadecimal data.
+
+
+
+
+Franks, et al. Standards Track [Page 9]
+
+RFC 2617 HTTP Authentication June 1999
+
+
+ stale
+ A flag, indicating that the previous request from the client was
+ rejected because the nonce value was stale. If stale is TRUE
+ (case-insensitive), the client may wish to simply retry the request
+ with a new encrypted response, without reprompting the user for a
+ new username and password. The server should only set stale to TRUE
+ if it receives a request for which the nonce is invalid but with a
+ valid digest for that nonce (indicating that the client knows the
+ correct username/password). If stale is FALSE, or anything other
+ than TRUE, or the stale directive is not present, the username
+ and/or password are invalid, and new values must be obtained.
+
+ algorithm
+ A string indicating a pair of algorithms used to produce the digest
+ and a checksum. If this is not present it is assumed to be "MD5".
+ If the algorithm is not understood, the challenge should be ignored
+ (and a different one used, if there is more than one).
+
+ In this document the string obtained by applying the digest
+ algorithm to the data "data" with secret "secret" will be denoted
+ by KD(secret, data), and the string obtained by applying the
+ checksum algorithm to the data "data" will be denoted H(data). The
+ notation unq(X) means the value of the quoted-string X without the
+ surrounding quotes.
+
+ For the "MD5" and "MD5-sess" algorithms
+
+ H(data) = MD5(data)
+
+ and
+
+ KD(secret, data) = H(concat(secret, ":", data))
+
+ i.e., the digest is the MD5 of the secret concatenated with a colon
+ concatenated with the data. The "MD5-sess" algorithm is intended to
+ allow efficient 3rd party authentication servers; for the
+ difference in usage, see the description in section 3.2.2.2.
+
+ qop-options
+ This directive is optional, but is made so only for backward
+ compatibility with RFC 2069 [6]; it SHOULD be used by all
+ implementations compliant with this version of the Digest scheme.
+ If present, it is a quoted string of one or more tokens indicating
+ the "quality of protection" values supported by the server. The
+ value "auth" indicates authentication; the value "auth-int"
+ indicates authentication with integrity protection; see the
+
+
+
+
+
+Franks, et al. Standards Track [Page 10]
+
+RFC 2617 HTTP Authentication June 1999
+
+
+ descriptions below for calculating the response directive value for
+ the application of this choice. Unrecognized options MUST be
+ ignored.
+
+ auth-param
+ This directive allows for future extensions. Any unrecognized
+ directive MUST be ignored.
+
+3.2.2 The Authorization Request Header
+
+ The client is expected to retry the request, passing an Authorization
+ header line, which is defined according to the framework above,
+ utilized as follows.
+
+ credentials = "Digest" digest-response
+ digest-response = 1#( username | realm | nonce | digest-uri
+ | response | [ algorithm ] | [cnonce] |
+ [opaque] | [message-qop] |
+ [nonce-count] | [auth-param] )
+
+ username = "username" "=" username-value
+ username-value = quoted-string
+ digest-uri = "uri" "=" digest-uri-value
+ digest-uri-value = request-uri ; As specified by HTTP/1.1
+ message-qop = "qop" "=" qop-value
+ cnonce = "cnonce" "=" cnonce-value
+ cnonce-value = nonce-value
+ nonce-count = "nc" "=" nc-value
+ nc-value = 8LHEX
+ response = "response" "=" request-digest
+ request-digest = <"> 32LHEX <">
+ LHEX = "0" | "1" | "2" | "3" |
+ "4" | "5" | "6" | "7" |
+ "8" | "9" | "a" | "b" |
+ "c" | "d" | "e" | "f"
+
+ The values of the opaque and algorithm fields must be those supplied
+ in the WWW-Authenticate response header for the entity being
+ requested.
+
+ response
+ A string of 32 hex digits computed as defined below, which proves
+ that the user knows a password
+
+ username
+ The user's name in the specified realm.
+
+
+
+
+
+Franks, et al. Standards Track [Page 11]
+
+RFC 2617 HTTP Authentication June 1999
+
+
+ digest-uri
+ The URI from Request-URI of the Request-Line; duplicated here
+ because proxies are allowed to change the Request-Line in transit.
+
+ qop
+ Indicates what "quality of protection" the client has applied to
+ the message. If present, its value MUST be one of the alternatives
+ the server indicated it supports in the WWW-Authenticate header.
+ These values affect the computation of the request-digest. Note
+ that this is a single token, not a quoted list of alternatives as
+ in WWW- Authenticate. This directive is optional in order to
+ preserve backward compatibility with a minimal implementation of
+ RFC 2069 [6], but SHOULD be used if the server indicated that qop
+ is supported by providing a qop directive in the WWW-Authenticate
+ header field.
+
+ cnonce
+ This MUST be specified if a qop directive is sent (see above), and
+ MUST NOT be specified if the server did not send a qop directive in
+ the WWW-Authenticate header field. The cnonce-value is an opaque
+ quoted string value provided by the client and used by both client
+ and server to avoid chosen plaintext attacks, to provide mutual
+ authentication, and to provide some message integrity protection.
+ See the descriptions below of the calculation of the response-
+ digest and request-digest values.
+
+ nonce-count
+ This MUST be specified if a qop directive is sent (see above), and
+ MUST NOT be specified if the server did not send a qop directive in
+ the WWW-Authenticate header field. The nc-value is the hexadecimal
+ count of the number of requests (including the current request)
+ that the client has sent with the nonce value in this request. For
+ example, in the first request sent in response to a given nonce
+ value, the client sends "nc=00000001". The purpose of this
+ directive is to allow the server to detect request replays by
+ maintaining its own copy of this count - if the same nc-value is
+ seen twice, then the request is a replay. See the description
+ below of the construction of the request-digest value.
+
+ auth-param
+ This directive allows for future extensions. Any unrecognized
+ directive MUST be ignored.
+
+ If a directive or its value is improper, or required directives are
+ missing, the proper response is 400 Bad Request. If the request-
+ digest is invalid, then a login failure should be logged, since
+ repeated login failures from a single client may indicate an attacker
+ attempting to guess passwords.
+
+
+
+Franks, et al. Standards Track [Page 12]
+
+RFC 2617 HTTP Authentication June 1999
+
+
+ The definition of request-digest above indicates the encoding for its
+ value. The following definitions show how the value is computed.
+
+3.2.2.1 Request-Digest
+
+ If the "qop" value is "auth" or "auth-int":
+
+ request-digest = <"> < KD ( H(A1), unq(nonce-value)
+ ":" nc-value
+ ":" unq(cnonce-value)
+ ":" unq(qop-value)
+ ":" H(A2)
+ ) <">
+
+ If the "qop" directive is not present (this construction is for
+ compatibility with RFC 2069):
+
+ request-digest =
+ <"> < KD ( H(A1), unq(nonce-value) ":" H(A2) ) >
+ <">
+
+ See below for the definitions for A1 and A2.
+
+3.2.2.2 A1
+
+ If the "algorithm" directive's value is "MD5" or is unspecified, then
+ A1 is:
+
+ A1 = unq(username-value) ":" unq(realm-value) ":" passwd
+
+ where
+
+ passwd = < user's password >
+
+ If the "algorithm" directive's value is "MD5-sess", then A1 is
+ calculated only once - on the first request by the client following
+ receipt of a WWW-Authenticate challenge from the server. It uses the
+ server nonce from that challenge, and the first client nonce value to
+ construct A1 as follows:
+
+ A1 = H( unq(username-value) ":" unq(realm-value)
+ ":" passwd )
+ ":" unq(nonce-value) ":" unq(cnonce-value)
+
+ This creates a 'session key' for the authentication of subsequent
+ requests and responses which is different for each "authentication
+ session", thus limiting the amount of material hashed with any one
+ key. (Note: see further discussion of the authentication session in
+
+
+
+Franks, et al. Standards Track [Page 13]
+
+RFC 2617 HTTP Authentication June 1999
+
+
+ section 3.3.) Because the server need only use the hash of the user
+ credentials in order to create the A1 value, this construction could
+ be used in conjunction with a third party authentication service so
+ that the web server would not need the actual password value. The
+ specification of such a protocol is beyond the scope of this
+ specification.
+
+3.2.2.3 A2
+
+ If the "qop" directive's value is "auth" or is unspecified, then A2
+ is:
+
+ A2 = Method ":" digest-uri-value
+
+ If the "qop" value is "auth-int", then A2 is:
+
+ A2 = Method ":" digest-uri-value ":" H(entity-body)
+
+3.2.2.4 Directive values and quoted-string
+
+ Note that the value of many of the directives, such as "username-
+ value", are defined as a "quoted-string". However, the "unq" notation
+ indicates that surrounding quotation marks are removed in forming the
+ string A1. Thus if the Authorization header includes the fields
+
+ username="Mufasa", realm=myhost@testrealm.com
+
+ and the user Mufasa has password "Circle Of Life" then H(A1) would be
+ H(Mufasa:myhost@testrealm.com:Circle Of Life) with no quotation marks
+ in the digested string.
+
+ No white space is allowed in any of the strings to which the digest
+ function H() is applied unless that white space exists in the quoted
+ strings or entity body whose contents make up the string to be
+ digested. For example, the string A1 illustrated above must be
+
+ Mufasa:myhost@testrealm.com:Circle Of Life
+
+ with no white space on either side of the colons, but with the white
+ space between the words used in the password value. Likewise, the
+ other strings digested by H() must not have white space on either
+ side of the colons which delimit their fields unless that white space
+ was in the quoted strings or entity body being digested.
+
+ Also note that if integrity protection is applied (qop=auth-int), the
+ H(entity-body) is the hash of the entity body, not the message body -
+ it is computed before any transfer encoding is applied by the sender
+
+
+
+
+Franks, et al. Standards Track [Page 14]
+
+RFC 2617 HTTP Authentication June 1999
+
+
+ and after it has been removed by the recipient. Note that this
+ includes multipart boundaries and embedded headers in each part of
+ any multipart content-type.
+
+3.2.2.5 Various considerations
+
+ The "Method" value is the HTTP request method as specified in section
+ 5.1.1 of [2]. The "request-uri" value is the Request-URI from the
+ request line as specified in section 5.1.2 of [2]. This may be "*",
+ an "absoluteURL" or an "abs_path" as specified in section 5.1.2 of
+ [2], but it MUST agree with the Request-URI. In particular, it MUST
+ be an "absoluteURL" if the Request-URI is an "absoluteURL". The
+ "cnonce-value" is an optional client-chosen value whose purpose is
+ to foil chosen plaintext attacks.
+
+ The authenticating server must assure that the resource designated by
+ the "uri" directive is the same as the resource specified in the
+ Request-Line; if they are not, the server SHOULD return a 400 Bad
+ Request error. (Since this may be a symptom of an attack, server
+ implementers may want to consider logging such errors.) The purpose
+ of duplicating information from the request URL in this field is to
+ deal with the possibility that an intermediate proxy may alter the
+ client's Request-Line. This altered (but presumably semantically
+ equivalent) request would not result in the same digest as that
+ calculated by the client.
+
+ Implementers should be aware of how authenticated transactions
+ interact with shared caches. The HTTP/1.1 protocol specifies that
+ when a shared cache (see section 13.7 of [2]) has received a request
+ containing an Authorization header and a response from relaying that
+ request, it MUST NOT return that response as a reply to any other
+ request, unless one of two Cache-Control (see section 14.9 of [2])
+ directives was present in the response. If the original response
+ included the "must-revalidate" Cache-Control directive, the cache MAY
+ use the entity of that response in replying to a subsequent request,
+ but MUST first revalidate it with the origin server, using the
+ request headers from the new request to allow the origin server to
+ authenticate the new request. Alternatively, if the original response
+ included the "public" Cache-Control directive, the response entity
+ MAY be returned in reply to any subsequent request.
+
+3.2.3 The Authentication-Info Header
+
+ The Authentication-Info header is used by the server to communicate
+ some information regarding the successful authentication in the
+ response.
+
+
+
+
+
+Franks, et al. Standards Track [Page 15]
+
+RFC 2617 HTTP Authentication June 1999
+
+
+ AuthenticationInfo = "Authentication-Info" ":" auth-info
+ auth-info = 1#(nextnonce | [ message-qop ]
+ | [ response-auth ] | [ cnonce ]
+ | [nonce-count] )
+ nextnonce = "nextnonce" "=" nonce-value
+ response-auth = "rspauth" "=" response-digest
+ response-digest = <"> *LHEX <">
+
+ The value of the nextnonce directive is the nonce the server wishes
+ the client to use for a future authentication response. The server
+ may send the Authentication-Info header with a nextnonce field as a
+ means of implementing one-time or otherwise changing nonces. If the
+ nextnonce field is present the client SHOULD use it when constructing
+ the Authorization header for its next request. Failure of the client
+ to do so may result in a request to re-authenticate from the server
+ with the "stale=TRUE".
+
+ Server implementations should carefully consider the performance
+ implications of the use of this mechanism; pipelined requests will
+ not be possible if every response includes a nextnonce directive
+ that must be used on the next request received by the server.
+ Consideration should be given to the performance vs. security
+ tradeoffs of allowing an old nonce value to be used for a limited
+ time to permit request pipelining. Use of the nonce-count can
+ retain most of the security advantages of a new server nonce
+ without the deleterious affects on pipelining.
+
+ message-qop
+ Indicates the "quality of protection" options applied to the
+ response by the server. The value "auth" indicates authentication;
+ the value "auth-int" indicates authentication with integrity
+ protection. The server SHOULD use the same value for the message-
+ qop directive in the response as was sent by the client in the
+ corresponding request.
+
+ The optional response digest in the "response-auth" directive
+ supports mutual authentication -- the server proves that it knows the
+ user's secret, and with qop=auth-int also provides limited integrity
+ protection of the response. The "response-digest" value is calculated
+ as for the "request-digest" in the Authorization header, except that
+ if "qop=auth" or is not specified in the Authorization header for the
+ request, A2 is
+
+ A2 = ":" digest-uri-value
+
+ and if "qop=auth-int", then A2 is
+
+ A2 = ":" digest-uri-value ":" H(entity-body)
+
+
+
+Franks, et al. Standards Track [Page 16]
+
+RFC 2617 HTTP Authentication June 1999
+
+
+ where "digest-uri-value" is the value of the "uri" directive on the
+ Authorization header in the request. The "cnonce-value" and "nc-
+ value" MUST be the ones for the client request to which this message
+ is the response. The "response-auth", "cnonce", and "nonce-count"
+ directives MUST BE present if "qop=auth" or "qop=auth-int" is
+ specified.
+
+ The Authentication-Info header is allowed in the trailer of an HTTP
+ message transferred via chunked transfer-coding.
+
+3.3 Digest Operation
+
+ Upon receiving the Authorization header, the server may check its
+ validity by looking up the password that corresponds to the submitted
+ username. Then, the server must perform the same digest operation
+ (e.g., MD5) performed by the client, and compare the result to the
+ given request-digest value.
+
+ Note that the HTTP server does not actually need to know the user's
+ cleartext password. As long as H(A1) is available to the server, the
+ validity of an Authorization header may be verified.
+
+ The client response to a WWW-Authenticate challenge for a protection
+ space starts an authentication session with that protection space.
+ The authentication session lasts until the client receives another
+ WWW-Authenticate challenge from any server in the protection space. A
+ client should remember the username, password, nonce, nonce count and
+ opaque values associated with an authentication session to use to
+ construct the Authorization header in future requests within that
+ protection space. The Authorization header may be included
+ preemptively; doing so improves server efficiency and avoids extra
+ round trips for authentication challenges. The server may choose to
+ accept the old Authorization header information, even though the
+ nonce value included might not be fresh. Alternatively, the server
+ may return a 401 response with a new nonce value, causing the client
+ to retry the request; by specifying stale=TRUE with this response,
+ the server tells the client to retry with the new nonce, but without
+ prompting for a new username and password.
+
+ Because the client is required to return the value of the opaque
+ directive given to it by the server for the duration of a session,
+ the opaque data may be used to transport authentication session state
+ information. (Note that any such use can also be accomplished more
+ easily and safely by including the state in the nonce.) For example,
+ a server could be responsible for authenticating content that
+ actually sits on another server. It would achieve this by having the
+ first 401 response include a domain directive whose value includes a
+ URI on the second server, and an opaque directive whose value
+
+
+
+Franks, et al. Standards Track [Page 17]
+
+RFC 2617 HTTP Authentication June 1999
+
+
+ contains the state information. The client will retry the request, at
+ which time the server might respond with a 301/302 redirection,
+ pointing to the URI on the second server. The client will follow the
+ redirection, and pass an Authorization header , including the
+ <opaque> data.
+
+ As with the basic scheme, proxies must be completely transparent in
+ the Digest access authentication scheme. That is, they must forward
+ the WWW-Authenticate, Authentication-Info and Authorization headers
+ untouched. If a proxy wants to authenticate a client before a request
+ is forwarded to the server, it can be done using the Proxy-
+ Authenticate and Proxy-Authorization headers described in section 3.6
+ below.
+
+3.4 Security Protocol Negotiation
+
+ It is useful for a server to be able to know which security schemes a
+ client is capable of handling.
+
+ It is possible that a server may want to require Digest as its
+ authentication method, even if the server does not know that the
+ client supports it. A client is encouraged to fail gracefully if the
+ server specifies only authentication schemes it cannot handle.
+
+3.5 Example
+
+ The following example assumes that an access-protected document is
+ being requested from the server via a GET request. The URI of the
+ document is "http://www.nowhere.org/dir/index.html". Both client and
+ server know that the username for this document is "Mufasa", and the
+ password is "Circle Of Life" (with one space between each of the
+ three words).
+
+ The first time the client requests the document, no Authorization
+ header is sent, so the server responds with:
+
+ HTTP/1.1 401 Unauthorized
+ WWW-Authenticate: Digest
+ realm="testrealm@host.com",
+ qop="auth,auth-int",
+ nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093",
+ opaque="5ccc069c403ebaf9f0171e9517f40e41"
+
+ The client may prompt the user for the username and password, after
+ which it will respond with a new request, including the following
+ Authorization header:
+
+
+
+
+
+Franks, et al. Standards Track [Page 18]
+
+RFC 2617 HTTP Authentication June 1999
+
+
+ Authorization: Digest username="Mufasa",
+ realm="testrealm@host.com",
+ nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093",
+ uri="/dir/index.html",
+ qop=auth,
+ nc=00000001,
+ cnonce="0a4f113b",
+ response="6629fae49393a05397450978507c4ef1",
+ opaque="5ccc069c403ebaf9f0171e9517f40e41"
+
+3.6 Proxy-Authentication and Proxy-Authorization
+
+ The digest authentication scheme may also be used for authenticating
+ users to proxies, proxies to proxies, or proxies to origin servers by
+ use of the Proxy-Authenticate and Proxy-Authorization headers. These
+ headers are instances of the Proxy-Authenticate and Proxy-
+ Authorization headers specified in sections 10.33 and 10.34 of the
+ HTTP/1.1 specification [2] and their behavior is subject to
+ restrictions described there. The transactions for proxy
+ authentication are very similar to those already described. Upon
+ receiving a request which requires authentication, the proxy/server
+ must issue the "407 Proxy Authentication Required" response with a
+ "Proxy-Authenticate" header. The digest-challenge used in the
+ Proxy-Authenticate header is the same as that for the WWW-
+ Authenticate header as defined above in section 3.2.1.
+
+ The client/proxy must then re-issue the request with a Proxy-
+ Authorization header, with directives as specified for the
+ Authorization header in section 3.2.2 above.
+
+ On subsequent responses, the server sends Proxy-Authentication-Info
+ with directives the same as those for the Authentication-Info header
+ field.
+
+ Note that in principle a client could be asked to authenticate itself
+ to both a proxy and an end-server, but never in the same response.
+
+4 Security Considerations
+
+4.1 Authentication of Clients using Basic Authentication
+
+ The Basic authentication scheme is not a secure method of user
+ authentication, nor does it in any way protect the entity, which is
+ transmitted in cleartext across the physical network used as the
+ carrier. HTTP does not prevent additional authentication schemes and
+ encryption mechanisms from being employed to increase security or the
+ addition of enhancements (such as schemes to use one-time passwords)
+ to Basic authentication.
+
+
+
+Franks, et al. Standards Track [Page 19]
+
+RFC 2617 HTTP Authentication June 1999
+
+
+ The most serious flaw in Basic authentication is that it results in
+ the essentially cleartext transmission of the user's password over
+ the physical network. It is this problem which Digest Authentication
+ attempts to address.
+
+ Because Basic authentication involves the cleartext transmission of
+ passwords it SHOULD NOT be used (without enhancements) to protect
+ sensitive or valuable information.
+
+ A common use of Basic authentication is for identification purposes
+ -- requiring the user to provide a user name and password as a means
+ of identification, for example, for purposes of gathering accurate
+ usage statistics on a server. When used in this way it is tempting to
+ think that there is no danger in its use if illicit access to the
+ protected documents is not a major concern. This is only correct if
+ the server issues both user name and password to the users and in
+ particular does not allow the user to choose his or her own password.
+ The danger arises because naive users frequently reuse a single
+ password to avoid the task of maintaining multiple passwords.
+
+ If a server permits users to select their own passwords, then the
+ threat is not only unauthorized access to documents on the server but
+ also unauthorized access to any other resources on other systems that
+ the user protects with the same password. Furthermore, in the
+ server's password database, many of the passwords may also be users'
+ passwords for other sites. The owner or administrator of such a
+ system could therefore expose all users of the system to the risk of
+ unauthorized access to all those sites if this information is not
+ maintained in a secure fashion.
+
+ Basic Authentication is also vulnerable to spoofing by counterfeit
+ servers. If a user can be led to believe that he is connecting to a
+ host containing information protected by Basic authentication when,
+ in fact, he is connecting to a hostile server or gateway, then the
+ attacker can request a password, store it for later use, and feign an
+ error. This type of attack is not possible with Digest
+ Authentication. Server implementers SHOULD guard against the
+ possibility of this sort of counterfeiting by gateways or CGI
+ scripts. In particular it is very dangerous for a server to simply
+ turn over a connection to a gateway. That gateway can then use the
+ persistent connection mechanism to engage in multiple transactions
+ with the client while impersonating the original server in a way that
+ is not detectable by the client.
+
+4.2 Authentication of Clients using Digest Authentication
+
+ Digest Authentication does not provide a strong authentication
+ mechanism, when compared to public key based mechanisms, for example.
+
+
+
+Franks, et al. Standards Track [Page 20]
+
+RFC 2617 HTTP Authentication June 1999
+
+
+ However, it is significantly stronger than (e.g.) CRAM-MD5, which has
+ been proposed for use with LDAP [10], POP and IMAP (see RFC 2195
+ [9]). It is intended to replace the much weaker and even more
+ dangerous Basic mechanism.
+
+ Digest Authentication offers no confidentiality protection beyond
+ protecting the actual password. All of the rest of the request and
+ response are available to an eavesdropper.
+
+ Digest Authentication offers only limited integrity protection for
+ the messages in either direction. If qop=auth-int mechanism is used,
+ those parts of the message used in the calculation of the WWW-
+ Authenticate and Authorization header field response directive values
+ (see section 3.2 above) are protected. Most header fields and their
+ values could be modified as a part of a man-in-the-middle attack.
+
+ Many needs for secure HTTP transactions cannot be met by Digest
+ Authentication. For those needs TLS or SHTTP are more appropriate
+ protocols. In particular Digest authentication cannot be used for any
+ transaction requiring confidentiality protection. Nevertheless many
+ functions remain for which Digest authentication is both useful and
+ appropriate. Any service in present use that uses Basic should be
+ switched to Digest as soon as practical.
+
+4.3 Limited Use Nonce Values
+
+ The Digest scheme uses a server-specified nonce to seed the
+ generation of the request-digest value (as specified in section
+ 3.2.2.1 above). As shown in the example nonce in section 3.2.1, the
+ server is free to construct the nonce such that it may only be used
+ from a particular client, for a particular resource, for a limited
+ period of time or number of uses, or any other restrictions. Doing
+ so strengthens the protection provided against, for example, replay
+ attacks (see 4.5). However, it should be noted that the method
+ chosen for generating and checking the nonce also has performance and
+ resource implications. For example, a server may choose to allow
+ each nonce value to be used only once by maintaining a record of
+ whether or not each recently issued nonce has been returned and
+ sending a next-nonce directive in the Authentication-Info header
+ field of every response. This protects against even an immediate
+ replay attack, but has a high cost checking nonce values, and perhaps
+ more important will cause authentication failures for any pipelined
+ requests (presumably returning a stale nonce indication). Similarly,
+ incorporating a request-specific element such as the Etag value for a
+ resource limits the use of the nonce to that version of the resource
+ and also defeats pipelining. Thus it may be useful to do so for
+ methods with side effects but have unacceptable performance for those
+ that do not.
+
+
+
+Franks, et al. Standards Track [Page 21]
+
+RFC 2617 HTTP Authentication June 1999
+
+
+4.4 Comparison of Digest with Basic Authentication
+
+ Both Digest and Basic Authentication are very much on the weak end of
+ the security strength spectrum. But a comparison between the two
+ points out the utility, even necessity, of replacing Basic by Digest.
+
+ The greatest threat to the type of transactions for which these
+ protocols are used is network snooping. This kind of transaction
+ might involve, for example, online access to a database whose use is
+ restricted to paying subscribers. With Basic authentication an
+ eavesdropper can obtain the password of the user. This not only
+ permits him to access anything in the database, but, often worse,
+ will permit access to anything else the user protects with the same
+ password.
+
+ By contrast, with Digest Authentication the eavesdropper only gets
+ access to the transaction in question and not to the user's password.
+ The information gained by the eavesdropper would permit a replay
+ attack, but only with a request for the same document, and even that
+ may be limited by the server's choice of nonce.
+
+4.5 Replay Attacks
+
+ A replay attack against Digest authentication would usually be
+ pointless for a simple GET request since an eavesdropper would
+ already have seen the only document he could obtain with a replay.
+ This is because the URI of the requested document is digested in the
+ client request and the server will only deliver that document. By
+ contrast under Basic Authentication once the eavesdropper has the
+ user's password, any document protected by that password is open to
+ him.
+
+ Thus, for some purposes, it is necessary to protect against replay
+ attacks. A good Digest implementation can do this in various ways.
+ The server created "nonce" value is implementation dependent, but if
+ it contains a digest of the client IP, a time-stamp, the resource
+ ETag, and a private server key (as recommended above) then a replay
+ attack is not simple. An attacker must convince the server that the
+ request is coming from a false IP address and must cause the server
+ to deliver the document to an IP address different from the address
+ to which it believes it is sending the document. An attack can only
+ succeed in the period before the time-stamp expires. Digesting the
+ client IP and time-stamp in the nonce permits an implementation which
+ does not maintain state between transactions.
+
+ For applications where no possibility of replay attack can be
+ tolerated the server can use one-time nonce values which will not be
+ honored for a second use. This requires the overhead of the server
+
+
+
+Franks, et al. Standards Track [Page 22]
+
+RFC 2617 HTTP Authentication June 1999
+
+
+ remembering which nonce values have been used until the nonce time-
+ stamp (and hence the digest built with it) has expired, but it
+ effectively protects against replay attacks.
+
+ An implementation must give special attention to the possibility of
+ replay attacks with POST and PUT requests. Unless the server employs
+ one-time or otherwise limited-use nonces and/or insists on the use of
+ the integrity protection of qop=auth-int, an attacker could replay
+ valid credentials from a successful request with counterfeit form
+ data or other message body. Even with the use of integrity protection
+ most metadata in header fields is not protected. Proper nonce
+ generation and checking provides some protection against replay of
+ previously used valid credentials, but see 4.8.
+
+4.6 Weakness Created by Multiple Authentication Schemes
+
+ An HTTP/1.1 server may return multiple challenges with a 401
+ (Authenticate) response, and each challenge may use a different
+ auth-scheme. A user agent MUST choose to use the strongest auth-
+ scheme it understands and request credentials from the user based
+ upon that challenge.
+
+ Note that many browsers will only recognize Basic and will require
+ that it be the first auth-scheme presented. Servers should only
+ include Basic if it is minimally acceptable.
+
+ When the server offers choices of authentication schemes using the
+ WWW-Authenticate header, the strength of the resulting authentication
+ is only as good as that of the of the weakest of the authentication
+ schemes. See section 4.8 below for discussion of particular attack
+ scenarios that exploit multiple authentication schemes.
+
+4.7 Online dictionary attacks
+
+ If the attacker can eavesdrop, then it can test any overheard
+ nonce/response pairs against a list of common words. Such a list is
+ usually much smaller than the total number of possible passwords. The
+ cost of computing the response for each password on the list is paid
+ once for each challenge.
+
+ The server can mitigate this attack by not allowing users to select
+ passwords that are in a dictionary.
+
+
+
+
+
+
+
+
+
+Franks, et al. Standards Track [Page 23]
+
+RFC 2617 HTTP Authentication June 1999
+
+
+4.8 Man in the Middle
+
+ Both Basic and Digest authentication are vulnerable to "man in the
+ middle" (MITM) attacks, for example, from a hostile or compromised
+ proxy. Clearly, this would present all the problems of eavesdropping.
+ But it also offers some additional opportunities to the attacker.
+
+ A possible man-in-the-middle attack would be to add a weak
+ authentication scheme to the set of choices, hoping that the client
+ will use one that exposes the user's credentials (e.g. password). For
+ this reason, the client should always use the strongest scheme that
+ it understands from the choices offered.
+
+ An even better MITM attack would be to remove all offered choices,
+ replacing them with a challenge that requests only Basic
+ authentication, then uses the cleartext credentials from the Basic
+ authentication to authenticate to the origin server using the
+ stronger scheme it requested. A particularly insidious way to mount
+ such a MITM attack would be to offer a "free" proxy caching service
+ to gullible users.
+
+ User agents should consider measures such as presenting a visual
+ indication at the time of the credentials request of what
+ authentication scheme is to be used, or remembering the strongest
+ authentication scheme ever requested by a server and produce a
+ warning message before using a weaker one. It might also be a good
+ idea for the user agent to be configured to demand Digest
+ authentication in general, or from specific sites.
+
+ Or, a hostile proxy might spoof the client into making a request the
+ attacker wanted rather than one the client wanted. Of course, this is
+ still much harder than a comparable attack against Basic
+ Authentication.
+
+4.9 Chosen plaintext attacks
+
+ With Digest authentication, a MITM or a malicious server can
+ arbitrarily choose the nonce that the client will use to compute the
+ response. This is called a "chosen plaintext" attack. The ability to
+ choose the nonce is known to make cryptanalysis much easier [8].
+
+ However, no way to analyze the MD5 one-way function used by Digest
+ using chosen plaintext is currently known.
+
+ The countermeasure against this attack is for clients to be
+ configured to require the use of the optional "cnonce" directive;
+ this allows the client to vary the input to the hash in a way not
+ chosen by the attacker.
+
+
+
+Franks, et al. Standards Track [Page 24]
+
+RFC 2617 HTTP Authentication June 1999
+
+
+4.10 Precomputed dictionary attacks
+
+ With Digest authentication, if the attacker can execute a chosen
+ plaintext attack, the attacker can precompute the response for many
+ common words to a nonce of its choice, and store a dictionary of
+ (response, password) pairs. Such precomputation can often be done in
+ parallel on many machines. It can then use the chosen plaintext
+ attack to acquire a response corresponding to that challenge, and
+ just look up the password in the dictionary. Even if most passwords
+ are not in the dictionary, some might be. Since the attacker gets to
+ pick the challenge, the cost of computing the response for each
+ password on the list can be amortized over finding many passwords. A
+ dictionary with 100 million password/response pairs would take about
+ 3.2 gigabytes of disk storage.
+
+ The countermeasure against this attack is to for clients to be
+ configured to require the use of the optional "cnonce" directive.
+
+4.11 Batch brute force attacks
+
+ With Digest authentication, a MITM can execute a chosen plaintext
+ attack, and can gather responses from many users to the same nonce.
+ It can then find all the passwords within any subset of password
+ space that would generate one of the nonce/response pairs in a single
+ pass over that space. It also reduces the time to find the first
+ password by a factor equal to the number of nonce/response pairs
+ gathered. This search of the password space can often be done in
+ parallel on many machines, and even a single machine can search large
+ subsets of the password space very quickly -- reports exist of
+ searching all passwords with six or fewer letters in a few hours.
+
+ The countermeasure against this attack is to for clients to be
+ configured to require the use of the optional "cnonce" directive.
+
+4.12 Spoofing by Counterfeit Servers
+
+ Basic Authentication is vulnerable to spoofing by counterfeit
+ servers. If a user can be led to believe that she is connecting to a
+ host containing information protected by a password she knows, when
+ in fact she is connecting to a hostile server, then the hostile
+ server can request a password, store it away for later use, and feign
+ an error. This type of attack is more difficult with Digest
+ Authentication -- but the client must know to demand that Digest
+ authentication be used, perhaps using some of the techniques
+ described above to counter "man-in-the-middle" attacks. Again, the
+ user can be helped in detecting this attack by a visual indication of
+ the authentication mechanism in use with appropriate guidance in
+ interpreting the implications of each scheme.
+
+
+
+Franks, et al. Standards Track [Page 25]
+
+RFC 2617 HTTP Authentication June 1999
+
+
+4.13 Storing passwords
+
+ Digest authentication requires that the authenticating agent (usually
+ the server) store some data derived from the user's name and password
+ in a "password file" associated with a given realm. Normally this
+ might contain pairs consisting of username and H(A1), where H(A1) is
+ the digested value of the username, realm, and password as described
+ above.
+
+ The security implications of this are that if this password file is
+ compromised, then an attacker gains immediate access to documents on
+ the server using this realm. Unlike, say a standard UNIX password
+ file, this information need not be decrypted in order to access
+ documents in the server realm associated with this file. On the other
+ hand, decryption, or more likely a brute force attack, would be
+ necessary to obtain the user's password. This is the reason that the
+ realm is part of the digested data stored in the password file. It
+ means that if one Digest authentication password file is compromised,
+ it does not automatically compromise others with the same username
+ and password (though it does expose them to brute force attack).
+
+ There are two important security consequences of this. First the
+ password file must be protected as if it contained unencrypted
+ passwords, because for the purpose of accessing documents in its
+ realm, it effectively does.
+
+ A second consequence of this is that the realm string should be
+ unique among all realms which any single user is likely to use. In
+ particular a realm string should include the name of the host doing
+ the authentication. The inability of the client to authenticate the
+ server is a weakness of Digest Authentication.
+
+4.14 Summary
+
+ By modern cryptographic standards Digest Authentication is weak. But
+ for a large range of purposes it is valuable as a replacement for
+ Basic Authentication. It remedies some, but not all, weaknesses of
+ Basic Authentication. Its strength may vary depending on the
+ implementation. In particular the structure of the nonce (which is
+ dependent on the server implementation) may affect the ease of
+ mounting a replay attack. A range of server options is appropriate
+ since, for example, some implementations may be willing to accept the
+ server overhead of one-time nonces or digests to eliminate the
+ possibility of replay. Others may satisfied with a nonce like the one
+ recommended above restricted to a single IP address and a single ETag
+ or with a limited lifetime.
+
+
+
+
+
+Franks, et al. Standards Track [Page 26]
+
+RFC 2617 HTTP Authentication June 1999
+
+
+ The bottom line is that *any* compliant implementation will be
+ relatively weak by cryptographic standards, but *any* compliant
+ implementation will be far superior to Basic Authentication.
+
+5 Sample implementation
+
+ The following code implements the calculations of H(A1), H(A2),
+ request-digest and response-digest, and a test program which computes
+ the values used in the example of section 3.5. It uses the MD5
+ implementation from RFC 1321.
+
+ File "digcalc.h":
+
+#define HASHLEN 16
+typedef char HASH[HASHLEN];
+#define HASHHEXLEN 32
+typedef char HASHHEX[HASHHEXLEN+1];
+#define IN
+#define OUT
+
+/* calculate H(A1) as per HTTP Digest spec */
+void DigestCalcHA1(
+ IN char * pszAlg,
+ IN char * pszUserName,
+ IN char * pszRealm,
+ IN char * pszPassword,
+ IN char * pszNonce,
+ IN char * pszCNonce,
+ OUT HASHHEX SessionKey
+ );
+
+/* calculate request-digest/response-digest as per HTTP Digest spec */
+void DigestCalcResponse(
+ IN HASHHEX HA1, /* H(A1) */
+ IN char * pszNonce, /* nonce from server */
+ IN char * pszNonceCount, /* 8 hex digits */
+ IN char * pszCNonce, /* client nonce */
+ IN char * pszQop, /* qop-value: "", "auth", "auth-int" */
+ IN char * pszMethod, /* method from the request */
+ IN char * pszDigestUri, /* requested URL */
+ IN HASHHEX HEntity, /* H(entity body) if qop="auth-int" */
+ OUT HASHHEX Response /* request-digest or response-digest */
+ );
+
+File "digcalc.c":
+
+#include <global.h>
+#include <md5.h>
+
+
+
+Franks, et al. Standards Track [Page 27]
+
+RFC 2617 HTTP Authentication June 1999
+
+
+#include <string.h>
+#include "digcalc.h"
+
+void CvtHex(
+ IN HASH Bin,
+ OUT HASHHEX Hex
+ )
+{
+ unsigned short i;
+ unsigned char j;
+
+ for (i = 0; i < HASHLEN; i++) {
+ j = (Bin[i] >> 4) & 0xf;
+ if (j <= 9)
+ Hex[i*2] = (j + '0');
+ else
+ Hex[i*2] = (j + 'a' - 10);
+ j = Bin[i] & 0xf;
+ if (j <= 9)
+ Hex[i*2+1] = (j + '0');
+ else
+ Hex[i*2+1] = (j + 'a' - 10);
+ };
+ Hex[HASHHEXLEN] = '\0';
+};
+
+/* calculate H(A1) as per spec */
+void DigestCalcHA1(
+ IN char * pszAlg,
+ IN char * pszUserName,
+ IN char * pszRealm,
+ IN char * pszPassword,
+ IN char * pszNonce,
+ IN char * pszCNonce,
+ OUT HASHHEX SessionKey
+ )
+{
+ MD5_CTX Md5Ctx;
+ HASH HA1;
+
+ MD5Init(&Md5Ctx);
+ MD5Update(&Md5Ctx, pszUserName, strlen(pszUserName));
+ MD5Update(&Md5Ctx, ":", 1);
+ MD5Update(&Md5Ctx, pszRealm, strlen(pszRealm));
+ MD5Update(&Md5Ctx, ":", 1);
+ MD5Update(&Md5Ctx, pszPassword, strlen(pszPassword));
+ MD5Final(HA1, &Md5Ctx);
+ if (stricmp(pszAlg, "md5-sess") == 0) {
+
+
+
+Franks, et al. Standards Track [Page 28]
+
+RFC 2617 HTTP Authentication June 1999
+
+
+ MD5Init(&Md5Ctx);
+ MD5Update(&Md5Ctx, HA1, HASHLEN);
+ MD5Update(&Md5Ctx, ":", 1);
+ MD5Update(&Md5Ctx, pszNonce, strlen(pszNonce));
+ MD5Update(&Md5Ctx, ":", 1);
+ MD5Update(&Md5Ctx, pszCNonce, strlen(pszCNonce));
+ MD5Final(HA1, &Md5Ctx);
+ };
+ CvtHex(HA1, SessionKey);
+};
+
+/* calculate request-digest/response-digest as per HTTP Digest spec */
+void DigestCalcResponse(
+ IN HASHHEX HA1, /* H(A1) */
+ IN char * pszNonce, /* nonce from server */
+ IN char * pszNonceCount, /* 8 hex digits */
+ IN char * pszCNonce, /* client nonce */
+ IN char * pszQop, /* qop-value: "", "auth", "auth-int" */
+ IN char * pszMethod, /* method from the request */
+ IN char * pszDigestUri, /* requested URL */
+ IN HASHHEX HEntity, /* H(entity body) if qop="auth-int" */
+ OUT HASHHEX Response /* request-digest or response-digest */
+ )
+{
+ MD5_CTX Md5Ctx;
+ HASH HA2;
+ HASH RespHash;
+ HASHHEX HA2Hex;
+
+ // calculate H(A2)
+ MD5Init(&Md5Ctx);
+ MD5Update(&Md5Ctx, pszMethod, strlen(pszMethod));
+ MD5Update(&Md5Ctx, ":", 1);
+ MD5Update(&Md5Ctx, pszDigestUri, strlen(pszDigestUri));
+ if (stricmp(pszQop, "auth-int") == 0) {
+ MD5Update(&Md5Ctx, ":", 1);
+ MD5Update(&Md5Ctx, HEntity, HASHHEXLEN);
+ };
+ MD5Final(HA2, &Md5Ctx);
+ CvtHex(HA2, HA2Hex);
+
+ // calculate response
+ MD5Init(&Md5Ctx);
+ MD5Update(&Md5Ctx, HA1, HASHHEXLEN);
+ MD5Update(&Md5Ctx, ":", 1);
+ MD5Update(&Md5Ctx, pszNonce, strlen(pszNonce));
+ MD5Update(&Md5Ctx, ":", 1);
+ if (*pszQop) {
+
+
+
+Franks, et al. Standards Track [Page 29]
+
+RFC 2617 HTTP Authentication June 1999
+
+
+ MD5Update(&Md5Ctx, pszNonceCount, strlen(pszNonceCount));
+ MD5Update(&Md5Ctx, ":", 1);
+ MD5Update(&Md5Ctx, pszCNonce, strlen(pszCNonce));
+ MD5Update(&Md5Ctx, ":", 1);
+ MD5Update(&Md5Ctx, pszQop, strlen(pszQop));
+ MD5Update(&Md5Ctx, ":", 1);
+ };
+ MD5Update(&Md5Ctx, HA2Hex, HASHHEXLEN);
+ MD5Final(RespHash, &Md5Ctx);
+ CvtHex(RespHash, Response);
+};
+
+File "digtest.c":
+
+
+#include <stdio.h>
+#include "digcalc.h"
+
+void main(int argc, char ** argv) {
+
+ char * pszNonce = "dcd98b7102dd2f0e8b11d0f600bfb0c093";
+ char * pszCNonce = "0a4f113b";
+ char * pszUser = "Mufasa";
+ char * pszRealm = "testrealm@host.com";
+ char * pszPass = "Circle Of Life";
+ char * pszAlg = "md5";
+ char szNonceCount[9] = "00000001";
+ char * pszMethod = "GET";
+ char * pszQop = "auth";
+ char * pszURI = "/dir/index.html";
+ HASHHEX HA1;
+ HASHHEX HA2 = "";
+ HASHHEX Response;
+
+ DigestCalcHA1(pszAlg, pszUser, pszRealm, pszPass, pszNonce,
+pszCNonce, HA1);
+ DigestCalcResponse(HA1, pszNonce, szNonceCount, pszCNonce, pszQop,
+ pszMethod, pszURI, HA2, Response);
+ printf("Response = %s\n", Response);
+};
+
+
+
+
+
+
+
+
+
+
+
+Franks, et al. Standards Track [Page 30]
+
+RFC 2617 HTTP Authentication June 1999
+
+
+6 Acknowledgments
+
+ Eric W. Sink, of AbiSource, Inc., was one of the original authors
+ before the specification underwent substantial revision.
+
+ In addition to the authors, valuable discussion instrumental in
+ creating this document has come from Peter J. Churchyard, Ned Freed,
+ and David M. Kristol.
+
+ Jim Gettys and Larry Masinter edited this document for update.
+
+7 References
+
+ [1] Berners-Lee, T., Fielding, R. and H. Frystyk, "Hypertext
+ Transfer Protocol -- HTTP/1.0", RFC 1945, May 1996.
+
+ [2] Fielding, R., Gettys, J., Mogul, J., Frysyk, H., Masinter, L.,
+ Leach, P. and T. Berners-Lee, "Hypertext Transfer Protocol --
+ HTTP/1.1", RFC 2616, June 1999.
+
+ [3] Rivest, R., "The MD5 Message-Digest Algorithm", RFC 1321, April
+ 1992.
+
+ [4] Freed, N. and N. Borenstein. "Multipurpose Internet Mail
+ Extensions (MIME) Part One: Format of Internet Message Bodies",
+ RFC 2045, November 1996.
+
+ [5] Dierks, T. and C. Allen "The TLS Protocol, Version 1.0", RFC
+ 2246, January 1999.
+
+ [6] Franks, J., Hallam-Baker, P., Hostetler, J., Leach, P.,
+ Luotonen, A., Sink, E. and L. Stewart, "An Extension to HTTP :
+ Digest Access Authentication", RFC 2069, January 1997.
+
+ [7] Berners Lee, T, Fielding, R. and L. Masinter, "Uniform Resource
+ Identifiers (URI): Generic Syntax", RFC 2396, August 1998.
+
+ [8] Kaliski, B.,Robshaw, M., "Message Authentication with MD5",
+ CryptoBytes, Sping 1995, RSA Inc,
+ (http://www.rsa.com/rsalabs/pubs/cryptobytes/spring95/md5.htm)
+
+ [9] Klensin, J., Catoe, R. and P. Krumviede, "IMAP/POP AUTHorize
+ Extension for Simple Challenge/Response", RFC 2195, September
+ 1997.
+
+ [10] Morgan, B., Alvestrand, H., Hodges, J., Wahl, M.,
+ "Authentication Methods for LDAP", Work in Progress.
+
+
+
+
+Franks, et al. Standards Track [Page 31]
+
+RFC 2617 HTTP Authentication June 1999
+
+
+8 Authors' Addresses
+
+ John Franks
+ Professor of Mathematics
+ Department of Mathematics
+ Northwestern University
+ Evanston, IL 60208-2730, USA
+
+ EMail: john@math.nwu.edu
+
+
+ Phillip M. Hallam-Baker
+ Principal Consultant
+ Verisign Inc.
+ 301 Edgewater Place
+ Suite 210
+ Wakefield MA 01880, USA
+
+ EMail: pbaker@verisign.com
+
+
+ Jeffery L. Hostetler
+ Software Craftsman
+ AbiSource, Inc.
+ 6 Dunlap Court
+ Savoy, IL 61874
+
+ EMail: jeff@AbiSource.com
+
+
+ Scott D. Lawrence
+ Agranat Systems, Inc.
+ 5 Clocktower Place, Suite 400
+ Maynard, MA 01754, USA
+
+ EMail: lawrence@agranat.com
+
+
+ Paul J. Leach
+ Microsoft Corporation
+ 1 Microsoft Way
+ Redmond, WA 98052, USA
+
+ EMail: paulle@microsoft.com
+
+
+
+
+
+
+
+Franks, et al. Standards Track [Page 32]
+
+RFC 2617 HTTP Authentication June 1999
+
+
+ Ari Luotonen
+ Member of Technical Staff
+ Netscape Communications Corporation
+ 501 East Middlefield Road
+ Mountain View, CA 94043, USA
+
+
+ Lawrence C. Stewart
+ Open Market, Inc.
+ 215 First Street
+ Cambridge, MA 02142, USA
+
+ EMail: stewart@OpenMarket.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Franks, et al. Standards Track [Page 33]
+
+RFC 2617 HTTP Authentication June 1999
+
+
+9. Full Copyright Statement
+
+ Copyright (C) The Internet Society (1999). All Rights Reserved.
+
+ This document and translations of it may be copied and furnished to
+ others, and derivative works that comment on or otherwise explain it
+ or assist in its implementation may be prepared, copied, published
+ and distributed, in whole or in part, without restriction of any
+ kind, provided that the above copyright notice and this paragraph are
+ included on all such copies and derivative works. However, this
+ document itself may not be modified in any way, such as by removing
+ the copyright notice or references to the Internet Society or other
+ Internet organizations, except as needed for the purpose of
+ developing Internet standards in which case the procedures for
+ copyrights defined in the Internet Standards process must be
+ followed, or as required to translate it into languages other than
+ English.
+
+ The limited permissions granted above are perpetual and will not be
+ revoked by the Internet Society or its successors or assigns.
+
+ This document and the information contained herein is provided on an
+ "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING
+ TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+ HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+ MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+Acknowledgement
+
+ Funding for the RFC Editor function is currently provided by the
+ Internet Society.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Franks, et al. Standards Track [Page 34]
+
diff --git a/standards/rfc2639.txt b/standards/rfc2639.txt
new file mode 100644
index 000000000..e3ab71684
--- /dev/null
+++ b/standards/rfc2639.txt
@@ -0,0 +1,3587 @@
+
+
+
+
+
+
+Network Working Group T. Hastings
+Request for Comments: 2639 C. Manros
+Category: Informational Xerox Corporation
+ July 1999
+
+
+ Internet Printing Protocol/1.0: Implementer's Guide
+
+Status of this Memo
+
+ This memo provides information for the Internet community. It does
+ not specify an Internet standard of any kind. Distribution of this
+ memo is unlimited.
+
+Copyright Notice
+
+ Copyright (C) The Internet Society (1999). All Rights Reserved.
+
+Abstract
+
+ This document is one of a set of documents, which together describe
+ all aspects of a new Internet Printing Protocol (IPP). IPP is an
+ application level protocol that can be used for distributed printing
+ using Internet tools and technologies. This document contains
+ information that supplements the IPP Model and Semantics [RFC2566]
+ and the IPP Transport and Encoding [RFC2565] documents. It is
+ intended to help implementers understand IPP/1.0 and some of the
+ considerations that may assist them in the design of their client
+ and/or IPP object implementations. For example, a typical order of
+ processing requests is given, including error checking. Motivation
+ for some of the specification decisions is also included.
+
+ The full set of IPP documents includes:
+
+ Design Goals for an Internet Printing Protocol [RFC2567]
+ Rationale for the Structure and Model and Protocol for the Internet
+ Printing Protocol [RFC2568]
+ Internet Printing Protocol/1.0: Model and Semantics [RFC2566]
+ Internet Printing Protocol/1.0: Encoding and Transport [RFC2565]
+ Mapping between LPD and IPP Protocols [RFC2569]
+
+ The document, "Design Goals for an Internet Printing Protocol", takes
+ a broad look at distributed printing functionality, and it enumerates
+ real-life scenarios that help to clarify the features that need to be
+ included in a printing protocol for the Internet. It identifies
+ requirements for three types of users: end users, operators, and
+
+
+
+
+
+Hastings & Manros Informational [Page 1]
+
+RFC 2639 IPP/1.0: Implementer's Guide July 1999
+
+
+ administrators. The design goals document calls out a subset of end
+ user requirements that are satisfied in IPP/1.0. Operator and
+ administrator requirements are out of scope for version 1.0.
+
+ The document, "Rationale for the Structure and Model and Protocol for
+ the Internet Printing Protocol", describes IPP from a high level
+ view, defines a roadmap for the various documents that form the suite
+ of IPP specifications, and gives background and rationale for the
+ IETF working group's major decisions.
+
+ The document, "Internet Printing Protocol/1.0: Model and Semantics",
+ describes a simplified model with abstract objects, their attributes,
+ and their operations. The model introduces a Printer and a Job. The
+ Job supports multiple documents per Job. The model document also
+ addresses how security, internationalization, and directory issues
+ are addressed.
+
+ The document, "Internet Printing Protocol/1.0: Encoding and
+ Transport", is a formal mapping of the abstract operations and
+ attributes defined in the model document onto HTTP/1.1. It also
+ defines the encoding rules for a new Internet media type called
+ "application/ipp".
+
+ The document, "Mapping between LPD and IPP Protocols", gives some
+ advice to implementers of gateways between IPP and LPD (Line Printer
+ Daemon) implementations.
+
+Table of Contents
+
+ 1 Introduction......................................................4
+ 1.1 Conformance language............................................4
+ 1.2 Other terminology...............................................5
+ 2 Model and Semantics...............................................5
+ 2.1 Summary of Operation Attributes.................................5
+ 2.2 Suggested Operation Processing Steps for IPP Objects ..........10
+ 2.2.1 Suggested Operation Processing Steps for all Operations..11
+ 2.2.1.1 Validate version number...............................11
+ 2.2.1.2 Validate operation identifier.........................11
+ 2.2.1.3 Validate the request identifier.......................11
+ 2.2.1.4 Validate attribute group and attribute presence and
+ order.................................................12
+ 2.2.1.5 Validate the values of the REQUIRED Operation
+ attributes............................................19
+ 2.2.1.6 Validate the values of the OPTIONAL Operation
+ attributes............................................23
+ 2.2.2 Suggested Additional Processing Steps for Operations that
+ Create/Validate Jobs and Add Documents.....................26
+ 2.2.2.1 Default "ipp-attribute-fidelity" if not supplied......26
+
+
+
+Hastings & Manros Informational [Page 2]
+
+RFC 2639 IPP/1.0: Implementer's Guide July 1999
+
+
+ 2.2.2.2 Check that the Printer object is accepting jobs.......26
+ 2.2.2.3 Validate the values of the Job Template attributes....26
+ 2.2.3 Algorithm for job validation...............................27
+ 2.2.3.1 Check for conflicting Job Template attributes values..33
+ 2.2.3.2 Decide whether to REJECT the request..................33
+ 2.2.3.3 For the Validate-Job operation, RETURN one of the
+ success status codes..................................34
+ 2.2.3.4 Create the Job object with attributes to support......34
+ 2.2.3.5 Return one of the success status codes................36
+ 2.2.3.6 Accept appended Document Content......................36
+ 2.2.3.7 Scheduling and Starting to Process the Job............36
+ 2.2.3.8 Completing the Job....................................37
+ 2.2.3.9 Destroying the Job after completion...................37
+ 2.2.3.10 Interaction with "ipp-attribute-fidelity".............37
+ 2.3 Status codes returned by operation ............................37
+ 2.3.1 Printer Operations.........................................38
+ 2.3.1.1 Print-Job.............................................38
+ 2.3.1.2 Print-URI.............................................40
+ 2.3.1.3 Validate-Job..........................................40
+ 2.3.1.4 Create-Job............................................41
+ 2.3.1.5 Get-Printer-Attributes................................41
+ 2.3.1.6 Get-Jobs..............................................42
+ 2.3.2 Job Operations.............................................43
+ 2.3.2.1 Send-Document.........................................43
+ 2.3.2.2 Send-URI..............................................44
+ 2.3.2.3 Cancel-Job............................................44
+ 2.3.2.4 Get-Job-Attributes....................................45
+ 2.4 Validate-Job...................................................46
+ 2.5 Case Sensitivity in URIs ......................................46
+ 2.6 Character Sets, natural languages, and internationalization....46
+ 2.6.1 Character set code conversion support .....................46
+ 2.6.2 What charset to return when an unsupported charset is
+ requested?.................................................48
+ 2.6.3 Natural Language Override (NLO) ...........................48
+ 2.7 The "queued-job-count" Printer Description attribute...........50
+ 2.7.1 Why is "queued-job-count" RECOMMENDED?.....................50
+ 2.7.2 Is "queued-job-count" a good measure of how busy a printer
+ is?........................................................50
+ 2.8 Sending empty attribute groups ................................50
+ 2.9 Returning unsupported attributes in Get-Xxxx responses ........51
+ 2.10 Returning job-state in Print-Job response ....................51
+ 2.11 Flow controlling the data portion of a Print-Job request .....52
+ 2.12 Multi-valued attributes ......................................53
+ 2.13 Querying jobs with IPP that were submitted using other job
+ submission protocols .........................................53
+ 2.14 The 'none' value for empty sets ..............................54
+ 2.15 Get-Jobs, my-jobs='true', and 'requesting-user-name'?.........54
+
+
+
+
+Hastings & Manros Informational [Page 3]
+
+RFC 2639 IPP/1.0: Implementer's Guide July 1999
+
+
+ 2.16 The "multiple-document-handling" Job Template attribute and
+ support of multiple document jobs.............................54
+ 3 Encoding and Transport...........................................55
+ 3.1 General Headers................................................56
+ 3.2 Request Headers...............................................57
+ 3.3 Response Headers...............................................58
+ 3.4 Entity Headers................................................59
+ 3.5 Optional support for HTTP/1.0..................................60
+ 3.6 HTTP/1.1 Chunking..............................................60
+ 3.6.1 Disabling IPP Server Response Chunking.....................60
+ 3.6.2 Warning About the Support of Chunked Requests..............60
+ 4 References.......................................................61
+ 4.1 Authors' Addresses.............................................62
+ 5 Security Considerations..........................................62
+ 6 Notices..........................................................62
+ Full Copyright Statement............................................65
+
+1 Introduction
+
+ This document contains information that supplements the IPP Model and
+ Semantics [RFC2566] and the IPP Transport and Encoding [RFC2565]
+ documents. As such this information is not part of the formal
+ specifications. Instead information is presented to help implementers
+ understand the specification, including some of the motivation for
+ decisions taken by the committee in developing the specification.
+ Some of the implementation considerations are intended to help
+ implementers design their client and/or IPP object implementations.
+ If there are any contradictions between this document and [RFC2566] or
+ [RFC2565], those documents take precedence over this document.
+
+1.1 Conformance language
+
+ Usually, this document does not contain the terminology MUST, MUST
+ NOT, MAY, NEED NOT, SHOULD, SHOULD NOT, REQUIRED, and OPTIONAL.
+ However, when those terms do appear in this document, their intent is
+ to repeat what the [RFC2566] and [RFC2565] documents require and
+ allow, rather than specifying additional conformance requirements.
+ These terms are defined in section 13 on conformance terminology in
+ [RFC2566], most of which is taken from RFC 2119 [RFC2119].
+
+ Implementers should read section 13 in [RFC2566] in order to
+ understand these capitalized words. The words MUST, MUST NOT, and
+ REQUIRED indicate what implementations are required to support in a
+ client or IPP object in order to be conformant to [RFC2566] and
+ [RFC2565]. MAY, NEED NOT, and OPTIONAL indicate was is merely allowed
+ as an implementer option. The verbs SHOULD and SHOULD NOT indicate
+ suggested behavior, but which is not required or disallowed,
+ respectively, in order to conform to the specification.
+
+
+
+Hastings & Manros Informational [Page 4]
+
+RFC 2639 IPP/1.0: Implementer's Guide July 1999
+
+
+1.2 Other terminology
+
+ The term "sender" refers to the client that sends a request or an IPP
+ object that returns a response. The term "receiver" refers to the IPP
+ object that receives a request and to a client that receives a
+ response.
+
+2 Model and Semantics
+
+ This section discusses various aspects of IPP/1.0 Model and Semantics
+ [RFC2566].
+
+2.1 Summary of Operation Attributes
+
+ Legend for the following table:
+
+ R indicates a REQUIRED operation or attribute for an
+ implementation to support
+
+ O indicates an OPTIONAL operation or attribute for an
+ implementation to support
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Hastings & Manros Informational [Page 5]
+
+RFC 2639 IPP/1.0: Implementer's Guide July 1999
+
+
+ Table 1. Summary of operation attributes for Printer operations
+
+ Printer Operations
+
+ Requests Responses
+
+ Operation Print- Pri Crea Get- Get- All
+ Attributes Job, nt- te- Printer- Jobs Opera-
+ Validate URI Job Attribut tions
+ -Job (O) (O) es
+
+ Operation parameters--REQUIRED to be supplied by the sender
+
+ operation-id R R R R R
+
+ status-code R
+
+ request-id R R R R R R
+
+ version-number R R R R R R
+
+ Operation attributes-REQUIRED to be supplied by the sender
+
+ attributes-charset R R R R R R
+
+ attributes- R R R R R R
+ natural-language
+
+ document-uri R
+
+ job-id*
+
+ job-uri*
+
+ last-document
+
+ printer-uri R R R R R
+
+ Operation attributes-RECOMMENDED to be supplied by the sender
+
+ job-name R R R
+
+ requesting-user- R R R R R
+ name
+
+
+
+
+
+
+
+Hastings & Manros Informational [Page 6]
+
+RFC 2639 IPP/1.0: Implementer's Guide July 1999
+
+
+ Printer Operations
+
+ Requests Responses
+
+ Operation Print- Pri Crea Get- Get- All
+ Attributes Job, nt- te- Printer Jobs Opera-
+ Vali- URI Job Attri- tions
+ date-Job (O) (O) butes
+
+ Operation attributes-OPTIONAL to be supplied by the sender
+
+ status-message O
+
+ compression O O
+
+ document-format R R O
+
+ document-name O O
+
+ document-natural- O O
+ language
+
+ ipp-attribute- R R R
+ fidelity
+
+ job-impressions O O O
+
+ job-k-octets O O O
+
+ job-media-sheets O O O
+
+ limit R
+
+ message
+
+ my-jobs R
+
+ requested- R R
+ attributes
+
+ which-jobs R
+
+ * "job-id" is REQUIRED only if used together with
+ "printer-uri" to identify the target job; otherwise, "job-
+ uri" is REQUIRED.
+
+
+
+
+
+
+Hastings & Manros Informational [Page 7]
+
+RFC 2639 IPP/1.0: Implementer's Guide July 1999
+
+
+ Table 2. Summary of operation attributes for Job operations
+
+
+ Requests Responses
+
+ Operation Send- Send- Cancel Get- All
+ Attributes Document URI -Job Job- Opera-
+ (O) (O) Attri- tions
+ butes
+
+ Operation parameters--REQUIRED to be supplied by the sender
+
+ operation-id R R R R
+
+ status-code R
+
+ request-id R R R R R
+
+ version-number R R R R R
+
+ Operation attributes-REQUIRED to be supplied by the sender
+
+ attributes- R R R R R
+ charset
+
+ attributes- R R R R R
+ natural-language
+
+ document-uri R
+
+ job-id* R R R R
+
+ job-uri* R R R R
+
+ last-document R R
+
+ printer-uri R R R R
+
+ Operation attributes-RECOMMENDED to be supplied by the
+ sender
+
+ job-name
+
+ requesting-user- R R R R
+ name
+
+
+
+
+
+
+Hastings & Manros Informational [Page 8]
+
+RFC 2639 IPP/1.0: Implementer's Guide July 1999
+
+
+ Job Operations
+
+ Requests Responses
+
+ Operation Attributes Send- Send- Cance Get- All
+ Document URI l-Job Job- Opera-
+ (O) (O) Attri- tions
+ butes
+
+ Operation attributes.OPTIONAL to be supplied by the sender
+
+ status-message O
+
+ compression O O
+
+ document-format R R
+
+ document-name O O
+
+ document-natural- O O
+ language
+
+ ipp-attribute-
+ fidelity
+
+ job-impressions
+
+ job-k-octets
+
+ job-media-sheets
+
+ limit
+
+ message O
+
+ my-jobs
+
+ requested-attributes R
+
+ which-jobs
+
+ * "job-id" is REQUIRED only if used together with "printer-
+ uri" to identify the target job; otherwise, "job-uri" is
+ REQUIRED.
+
+
+
+
+
+
+
+Hastings & Manros Informational [Page 9]
+
+RFC 2639 IPP/1.0: Implementer's Guide July 1999
+
+
+2.2 Suggested Operation Processing Steps for IPP Objects
+
+ This section suggests the steps and error checks that an IPP object
+ MAY perform when processing requests and returning responses. An IPP
+ object MAY perform some or all of the error checks. However, some
+ implementations MAY choose to be more forgiving than the error checks
+ shown here, in order to be able to accept requests from non-
+ conforming clients. Not performing all of these error checks is a
+ so-called "forgiving" implementation. On the other hand, clients
+ that successfully submit requests to IPP objects that do perform all
+ the error checks will be more likely to be able to interoperate with
+ other IPP object implementations. Thus an implementer of an IPP
+ object needs to decide whether to be a "forgiving" or a "strict"
+ implementation. Therefore, the error status codes returned may
+ differ between implementations. Consequentially, client SHOULD NOT
+ expect exactly the error code processing described in this section.
+
+ When an IPP object receives a request, the IPP object either accepts
+ or rejects the request. In order to determine whether or not to
+ accept or reject the request, the IPP object SHOULD execute the
+ following steps. The order of the steps may be rearranged and/or
+ combined, including making one or multiple passes over the request.
+
+ A client MUST supply requests that would pass all of the error checks
+ indicated here in order to be a conforming client. Therefore, a
+ client SHOULD supply requests that are conforming, in order to avoid
+ being rejected by some IPP object implementations and/or risking
+ different semantics by different implementations of forgiving
+ implementations. For example, a forgiving implementation that
+ accepts multiple occurrences of the same attribute, rather than
+ rejecting the request might use the first occurrences, while another
+ might use the last occurrence. Thus such a non-conforming client
+ would get different results from the two forgiving implementations.
+
+ In the following, processing continues step by step until a "RETURNS
+ the xxx status code ." statement is encountered. Error returns are
+ indicated by the verb: "REJECTS". Since clients have difficulty
+ getting the status code before sending all of the document data in a
+ Print-Job request, clients SHOULD use the Validate-Job operation
+ before sending large documents to be printed, in order to validate
+ whether the IPP Printer will accept the job or not.
+
+ It is assumed that security authentication and authorization has
+ already taken place at a lower layer.
+
+
+
+
+
+
+
+Hastings & Manros Informational [Page 10]
+
+RFC 2639 IPP/1.0: Implementer's Guide July 1999
+
+
+2.2.1 Suggested Operation Processing Steps for all Operations
+
+ This section is intended to apply to all operations. The next
+ section contains the additional steps for the Print-Job, Validate-
+ Job, Print-URI, Create-Job, Send-Document, and Send-URI operations
+ that create jobs, adds documents, and validates jobs.
+
+2.2.1.1 Validate version number
+
+ Every request and every response contains the "version-number"
+ attribute. The value of this attribute is the major and minor
+ version number of the syntax and semantics that the client and IPP
+ object is using, respectively. The "version-number" attribute
+ remains in a fixed position across all future versions so that all
+ clients and IPP object that support future versions can determine
+ which version is being used. The IPP object checks to see if the
+ major version number supplied in the request is supported. If not,
+ the Printer object REJECTS the request and RETURNS the 'server-
+ error-version-not-supported' status code in the response. The IPP
+ object returns in the "version-number" response attribute the major
+ and minor version for the error response. Thus the client can learn
+ at least one major and minor version that the IPP object supports.
+ The IPP object is encouraged to return the closest version number to
+ the one supplied by the client.
+
+ The checking of the minor version number is implementation dependent,
+ however if the client supplied minor version is explicitly supported,
+ the IPP object MUST respond using that identical minor version
+ number. If the requested minor version is not supported (the
+ requested minor version is either higher or lower) than a supported
+ minor version, the IPP object SHOULD return the closest supported
+ minor version.
+
+2.2.1.2 Validate operation identifier
+
+ The Printer object checks to see if the "operation-id" attribute
+ supplied by the client is supported as indicated in the Printer
+ object's "operations-supported" attribute. If not, the Printer
+ REJECTS the request and returns the 'server-error-operation-not-
+ supported' status code in the response.
+
+2.2.1.3 Validate the request identifier
+
+ The Printer object SHOULD NOT check to see if the "request-id"
+ attribute supplied by the client is in range: between 1 and 2**31 - 1
+ (inclusive), but copies all 32 bits.
+
+
+
+
+
+Hastings & Manros Informational [Page 11]
+
+RFC 2639 IPP/1.0: Implementer's Guide July 1999
+
+
+ Note: The "version-number", "operation-id", and the "request-id"
+ parameters are in fixed octet positions in the IPP/1.0 encoding. The
+ "version-number" parameter will be the same fixed octet position in
+ all versions of the protocol. These fields are validated before
+ proceeding with the rest of the validation.
+
+2.2.1.4 Validate attribute group and attribute presence and order
+
+ The order of the following validation steps depends on
+ implementation.
+
+2.2.1.4.1 Validate the presence and order of attribute groups
+
+ Client requests and IPP object responses contain attribute groups
+ that Section 3 requires to be present and in a specified order. An
+ IPP object verifies that the attribute groups are present and in the
+ correct order in requests supplied by clients (attribute groups
+ without an * in the following tables).
+
+ If an IPP object receives a request with (1) required attribute
+ groups missing, or (2) the attributes groups are out of order, or (3)
+ the groups are repeated, the IPP object REJECTS the request and
+ RETURNS the 'client-error-bad-request' status code. For example, it
+ is an error for the Job Template Attributes group to occur before the
+ Operation Attributes group, for the Operation Attributes group to be
+ omitted, or for an attribute group to occur more than once, except in
+ the Get-Jobs response.
+
+ Since this kind of attribute group error is most likely to be an
+ error detected by a client developer rather than by a customer, the
+ IPP object NEED NOT return an indication of which attribute group was
+ in error in either the Unsupported Attributes group or the Status
+ Message. Also, the IPP object NEED NOT find all attribute group
+ errors before returning this error.
+
+2.2.1.4.2 Ignore unknown attribute groups in the expected position
+
+ Future attribute groups may be added to the specification at the end
+ of requests just before the Document Content and at the end of
+ response, except for the Get-Jobs response, where it maybe there or
+ before the first job attributes returned. If an IPP object receives
+ an unknown attribute group in these positions, it ignores the entire
+ group, rather than returning an error, since that group may be a new
+ group in a later minor version of the protocol that can be ignored.
+ (If the new attribute group cannot be ignored without confusing the
+ client, the major version number would have been increased in the
+
+
+
+
+
+Hastings & Manros Informational [Page 12]
+
+RFC 2639 IPP/1.0: Implementer's Guide July 1999
+
+
+ protocol document and in the request). If the unknown group occurs
+ in a different position, the IPP object REJECTS the request and
+ RETURNS the 'client-error-bad-request' status code.
+
+ Clients also ignore unknown attribute groups returned in a response.
+
+ Note: By validating that requests are in the proper form, IPP
+ objects force clients to use the proper form which, in turn,
+ increases the chances that customers will be able to use such clients
+ from multiple vendors with IPP objects from other vendors.
+
+2.2.1.4.3 Validate the presence of a single occurrence of required
+ Operation attributes
+
+ Client requests and IPP object responses contain Operation attributes
+ that [RFC2566] Section 3 requires to be present. Attributes within a
+ group may be in any order, except for the ordering of target,
+ charset, and natural languages attributes. These attributes MUST be
+ first, and MUST be supplied in the following order: charset, natural
+ language, and then target. An IPP object verifies that the attributes
+ that Section 4 requires to be supplied by the client have been
+ supplied in the request (attributes without an * in the following
+ tables). An asterisk (*) indicates groups and Operation attributes
+ that the client may omit in a request or an IPP object may omit in a
+ response.
+
+ If an IPP object receives a request with required attributes missing
+ or repeated from a group or in the wrong position, the behavior of
+ the IPP object is IMPLEMENTATION DEPENDENT. Some of the possible
+ implementations are:
+
+ 1.REJECTS the request and RETURNS the 'client-error-bad-request'
+ status code
+
+ 2.accepts the request and uses the first occurrence of the
+ attribute no matter where it is
+
+ 3.accepts the request and uses the last occurrence of the
+ attribute no matter where it is
+
+ 4.accept the request and assume some default value for the missing
+ attribute
+
+ Therefore, client MUST send conforming requests, if they want to
+ receive the same behavior from all IPP object implementations. For
+ example, it is an error for the "attributes-charset" or "attributes-
+ natural-language" attribute to be omitted in any operation request,
+ or for an Operation attribute to be supplied in a Job Template group
+
+
+
+Hastings & Manros Informational [Page 13]
+
+RFC 2639 IPP/1.0: Implementer's Guide July 1999
+
+
+ or a Job Template attribute to be supplied in an Operation Attribute
+ group in a create request. It is also an error to supply the
+ "attributes-charset" attribute twice.
+
+ Since these kinds of attribute errors are most likely to be detected
+ by a client developer rather than by a customer, the IPP object NEED
+ NOT return an indication of which attribute was in error in either
+ the Unsupported Attributes group or the Status Message. Also, the
+ IPP object NEED NOT find all attribute errors before returning this
+ error.
+
+ The following tables list all the attributes for all the operations
+ by attribute group in each request and each response. The order of
+ the groups is the order that the client supplies the groups as
+ specified in [RFC2566] Section 3. The order of the attributes within
+ a group is arbitrary, except as noted for some of the special
+ operation attributes (charset, natural language, and target). The
+ tables below use the following notation:
+
+ R indicates a REQUIRED attribute that an IPP object MUST support
+ O indicates an OPTIONAL attribute that an IPP object NEED NOT
+ support
+ * indicates that a client MAY omit the attribute in a request
+ and that an IPP object MAY omit the attribute in a
+ response. The absence of an * means that a client MUST
+ supply the attribute in a request and an IPP object MUST
+ supply the attribute in a response.
+
+ Operation Requests
+
+ The tables below show the attributes in their proper attribute groups
+ for operation requests:
+
+ Note: All operation requests contain "version-number", "operation-
+ id", and "request-id" parameters.
+
+ Print-Job Request:
+ Group 1: Operation Attributes (R)
+ attributes-charset (R)
+ attributes-natural-language (R)
+ printer-uri (R)
+ requesting-user-name (R*)
+ job-name (R*)
+ ipp-attribute-fidelity (R*)
+ document-name (R*)
+ document-format (R*)
+ document-natural-language (O*)
+ compression (O*)
+
+
+
+Hastings & Manros Informational [Page 14]
+
+RFC 2639 IPP/1.0: Implementer's Guide July 1999
+
+
+ job-k-octets (O*)
+ job-impressions (O*)
+ job-media-sheets (O*)
+ Group 2: Job Template Attributes (R*)
+ <Job Template attributes> (O*)
+ (see [RFC2566] Section 4.2)
+ Group 3: Document Content (R)
+ <document content>
+
+ Validate-Job Request:
+ Group 1: Operation Attributes (R)
+ attributes-charset (R)
+ attributes-natural-language (R)
+ printer-uri (R)
+ requesting-user-name (R*)
+ job-name (R*)
+ ipp-attribute-fidelity (R*)
+ document-name (R*)
+ document-format (R*)
+ document-natural-language (O*)
+ compression (O*)
+ job-k-octets (O*)
+ job-impressions (O*)
+ job-media-sheets (O*)
+ Group 2: Job Template Attributes (R*)
+ <Job Template attributes> (O*)
+ (see [RFC2566] Section 4.2)
+
+ Create-Job Request:
+ Group 1: Operation Attributes (R)
+ attributes-charset (R)
+ attributes-natural-language (R)
+ printer-uri (R)
+ requesting-user-name (R*)
+ job-name (R*)
+ ipp-attribute-fidelity (R*)
+ job-k-octets (O*)
+ job-impressions (O*)
+ job-media-sheets (O*)
+ Group 2: Job Template Attributes (R*)
+ <Job Template attributes> (O*) (see
+ (see [RFC2566] Section 4.2)
+
+ Print-URI Request:
+ Group 1: Operation Attributes (R)
+ attributes-charset (R)
+ attributes-natural-language (R)
+ printer-uri (R)
+
+
+
+Hastings & Manros Informational [Page 15]
+
+RFC 2639 IPP/1.0: Implementer's Guide July 1999
+
+
+ document-uri (R)
+ requesting-user-name (R*)
+ job-name (R*)
+ ipp-attribute-fidelity (R*)
+ document-name (R*)
+ document-format (R*)
+ document-natural-language (O*)
+ compression (O*)
+ job-k-octets (O*)
+ job-impressions (O*)
+ job-media-sheets (O*)
+ Group 2: Job Template Attributes (R*)
+ <Job Template attributes> (O*) (see
+ (see [RFC2566] Section 4.2)
+
+ Send-Document Request:
+ Group 1: Operation Attributes (R)
+ attributes-charset (R)
+ attributes-natural-language (R)
+ (printer-uri & job-id) | job-uri (R)
+ last-document (R)
+ requesting-user-name (R*)
+ document-name (R*)
+ document-format (R*)
+ document-natural-language (O*)
+ compression (O*)
+ Group 2: Document Content (R*)
+ <document content>
+
+ Send-URI Request:
+ Group 1: Operation Attributes (R)
+ attributes-charset (R)
+ attributes-natural-language (R)
+ (printer-uri & job-id) | job-uri (R)
+ last-document (R)
+ document-uri (R)
+ requesting-user-name (R*)
+ document-name (R*)
+ document-format (R*)
+ document-natural-language (O*)
+ compression (O*)
+
+
+
+
+
+
+
+
+
+
+Hastings & Manros Informational [Page 16]
+
+RFC 2639 IPP/1.0: Implementer's Guide July 1999
+
+
+ Cancel-Job Request:
+ Group 1: Operation Attributes (R)
+ attributes-charset (R)
+ attributes-natural-language (R)
+ (printer-uri & job-id) | job-uri (R)
+ requesting-user-name (R*)
+ message (O*)
+
+ Get-Printer-Attributes Request:
+ Group 1: Operation Attributes (R)
+ attributes-charset (R)
+ attributes-natural-language (R)
+ printer-uri (R)
+ requesting-user-name (R*)
+ requested-attributes (R*)
+ document-format (R*)
+
+ Get-Job-Attributes Request:
+ Group 1: Operation Attributes (R)
+ attributes-charset (R)
+ attributes-natural-language (R)
+ (printer-uri & job-id) | job-uri (R)
+ requesting-user-name (R*)
+ requested-attributes (R*)
+
+ Get-Jobs Request:
+ Group 1: Operation Attributes (R)
+ attributes-charset (R)
+ attributes-natural-language (R)
+ printer-uri (R)
+ requesting-user-name (R*)
+ limit (R*)
+ requested-attributes (R*)
+ which-jobs (R*)
+ my-jobs (R*)
+
+
+ Operation Responses
+
+ The tables below show the response attributes in their proper
+ attribute groups for responses.
+
+ Note: All operation responses contain "version-number", "status-
+ code", and "request-id" parameters.
+
+ Print-Job Response:
+ Print-URI Response:
+ Create-Job Response:
+
+
+
+Hastings & Manros Informational [Page 17]
+
+RFC 2639 IPP/1.0: Implementer's Guide July 1999
+
+
+ Send-Document Response:
+ Send-URI Response:
+ Group 1: Operation Attributes (R)
+ attributes-charset (R)
+ attributes-natural-language (R)
+ status-message (O*)
+ Group 2: Unsupported Attributes (R*) (see Note 3)
+ <unsupported attributes> (R*)
+ Group 3: Job Object Attributes(R*) (see Note 2)
+ job-uri (R)
+ job-id (R)
+ job-state (R)
+ job-state-reasons (O*)
+ job-state-message (O*)
+ number-of-intervening-jobs (O*)
+
+ Validate-Job Response:
+ Cancel-Job Response:
+ Group 1: Operation Attributes (R)
+ attributes-charset (R)
+ attributes-natural-language (R)
+ status-message (O*)
+ Group 2: Unsupported Attributes (R*) (see Note 3)
+ <unsupported attributes> (R*)
+
+ Note 2 - the Job Object Attributes and Printer Object Attributes are
+ returned only if the IPP object returns one of the success status
+ codes.
+
+ Note 3 - the Unsupported Attributes Group is present only if the
+ client included some Operation and/or Job Template attributes or
+ values that the Printer doesn't support whether a success or an error
+ return.
+
+ Get-Printer-Attributes Response:
+ Group 1: Operation Attributes (R)
+ attributes-charset (R)
+ attributes-natural-language (R)
+ status-message (O*)
+ Group 2: Unsupported Attributes (R*) (see Note 4)
+ <unsupported attributes> (R*)
+ Group 3: Printer Object Attributes(R*) (see Note 2)
+ <requested attributes> (R*)
+
+ Note 4 - the Unsupported Attributes Group is present only if the
+ client included some Operation attributes that the Printer doesn't
+ support whether a success or an error return.
+
+
+
+
+Hastings & Manros Informational [Page 18]
+
+RFC 2639 IPP/1.0: Implementer's Guide July 1999
+
+
+ Get-Job-Attributes Response:
+ Group 1: Operation Attributes (R)
+ attributes-charset (R)
+ attributes-natural-language (R)
+ status-message (O*)
+ Group 2: Unsupported Attributes (R*) (see Note 4)
+ <unsupported attributes> (R*)
+ Group 3: Job Object Attributes(R*) (see Note 2)
+ <requested attributes> (R*)
+
+ Get-Jobs Response:
+ Group 1: Operation Attributes (R)
+ attributes-charset (R)
+ attributes-natural-language (R)
+ status-message (O*)
+ Group 2: Unsupported Attributes (R*) (see Note 4)
+ <unsupported attributes> (R*)
+ Group 3: Job Object Attributes(R*) (see Note 2, 5)
+ <requested attributes> (R*)
+
+ Note 5: for the Get-Jobs operation the response contains a separate
+ Job Object Attributes group 3 to N containing requested-attributes
+ for each job object in the response.
+
+2.2.1.5 Validate the values of the REQUIRED Operation attributes
+
+ An IPP object validates the values supplied by the client of the
+ REQUIRED Operation attribute that the IPP object MUST support. The
+ next section specifies the validation of the values of the OPTIONAL
+ Operation attributes that IPP objects MAY support.
+
+ The IPP object performs the following syntactic validation checks of
+ each Operation attribute value:
+
+ a)that the length of each Operation attribute value is correct for
+ the attribute syntax tag supplied by the client according to
+ [RFC2566] Section 4.1,
+
+ b)that the attribute syntax tag is correct for that Operation
+ attribute according to [RFC2566] Section 3,
+
+ c)that the value is in the range specified for that Operation
+ attribute according to [RFC2566] Section 3,
+
+ d)that multiple values are supplied by the client only for
+ operation attributes that are multi-valued, i.e., that are
+ 1setOf X according to [RFC2566] Section 3.
+
+
+
+
+Hastings & Manros Informational [Page 19]
+
+RFC 2639 IPP/1.0: Implementer's Guide July 1999
+
+
+ If any of these checks fail, the IPP object REJECTS the request and
+ RETURNS the 'client-error-bad-request' or the 'client-error-request-
+ value-too-long' status code. Since such an error is most likely to
+ be an error detected by a client developer, rather than by an end-
+ user, the IPP object NEED NOT return an indication of which attribute
+ had the error in either the Unsupported Attributes Group or the
+
+ Status Message. The description for each of these syntactic checks
+ is explicitly expressed in the first IF statement in the following
+ table.
+
+ In addition, the IPP object checks each Operation attribute value
+ against some Printer object attribute or some hard-coded value if
+ there is no "xxx-supported" Printer object attribute defined. If its
+ value is not among those supported or is not in the range supported,
+ then the IPP object REJECTS the request and RETURNS the error status
+ code indicated in the table by the second IF statement. If the value
+ of the Printer object's "xxx-supported" attribute is 'no-value'
+ (because the system administrator hasn't configured a value), the
+ check always fails.
+
+ attributes-charset (charset)
+
+ IF NOT a single non-empty 'charset' value, REJECT/RETURN 'client-
+ error-bad-request'.
+
+ IF the value length is greater than 63 octets, REJECT/RETURN '
+ client-error-request-value-too-long'.
+ IF NOT in the Printer object's "charset-supported" attribute,
+ REJECT/RETURN "client-error-charset-not-supported".
+
+
+ attributes-natural-language(naturalLanguage)
+
+ IF NOT a single non-empty 'naturalLanguage' value, REJECT/RETURN
+ 'client-error-bad-request'.
+ IF the value length is greater than 63 octets, REJECT/RETURN '
+ client-error-request-value-too-long'.
+ ACCEPT the request even if not a member of the set in the Printer
+ object's "generated-natural-language-supported" attribute. If
+ the supplied value is not a member of the Printer object's
+ "generated-natural-language-supported" attribute, use the
+ Printer object's "natural-language-configured" value.
+
+
+
+
+
+
+
+
+Hastings & Manros Informational [Page 20]
+
+RFC 2639 IPP/1.0: Implementer's Guide July 1999
+
+
+ requesting-user-name
+
+ IF NOT a single 'name' value, REJECT/RETURN 'client-error-bad-
+ request'.
+ IF the value length is greater than 255 octets, REJECT/RETURN
+ 'client-error-request-value-too-long'.
+ IF the IPP object can obtain a better authenticated name, use it
+ instead.
+
+
+ job-name(name)
+
+ IF NOT a single 'name' value, REJECT/RETURN 'client-error-bad-
+ request'.
+ IF the value length is greater than 255 octets, REJECT/RETURN
+ 'client-error-request-value-too-long'.
+ IF NOT supplied by the client, the Printer object creates a name
+ from the document-name or document-uri.
+
+
+ document-name (name)
+
+ IF NOT a single 'name' value, REJECT/RETURN 'client-error-bad-
+ request'.
+ IF the value length is greater than 255 octets, REJECT/RETURN
+ 'client-error-request-value-too-long'.
+
+
+ ipp-attribute-fidelity (boolean)
+
+ IF NEITHER a single 'true' NOR a single 'false' 'boolean' value,
+ REJECT/RETURN 'client-error-bad-request'.
+ IF the value length is NOT equal to 1 octet, REJECT/RETURN '
+ client-error-request-value-too-long'
+ IF NOT supplied by the client, the IPP object assumes the value
+ 'false'.
+
+
+ document-format (mimeMediaType)
+
+ IF NOT a single non-empty 'mimeMediaType' value, REJECT/RETURN
+ 'client-error-bad-request'.
+ IF the value length is greater than 255 octets, REJECT/RETURN
+ 'client-error-request-value-too-long'.
+ IF NOT in the Printer object's "document-format-supported"
+ attribute, REJECT/RETURN 'client-error-document-format-not-
+ supported'
+
+
+
+
+Hastings & Manros Informational [Page 21]
+
+RFC 2639 IPP/1.0: Implementer's Guide July 1999
+
+
+ IF NOT supplied by the client, the IPP object assumes the value of
+ the Printer object's "document-format-default" attribute.
+
+
+ document-uri (uri)
+
+ IF NOT a single non-empty 'uri' value, REJECT/RETURN 'client-
+ error-bad-request'.
+ IF the value length is greater than 1023 octets, REJECT/RETURN
+ 'client-error-request-value-too-long'.
+ IF the URI syntax is not valid, REJECT/RETURN 'client-error-bad-
+ request'.
+ IF scheme is NOT in the Printer object's "reference-uri-schemes-
+ supported" attribute, REJECT/RETURN 'client-error-uri-scheme-
+ not-supported'.
+ The Printer object MAY check to see if the document exists and is
+ accessible. If the document is not found or is not accessible,
+ REJECT/RETURN 'client-error-not found'.
+
+
+ last-document (boolean)
+
+ IF NEITHER a single 'true' NOR a single 'false' 'boolean' value,
+ REJECT/RETURN 'client-error-bad-request'.
+ IF the value length is NOT equal to 1 octet, REJECT/RETURN '
+ client-error-request-value-too-long'
+
+
+ job-id (integer(1:MAX))
+
+ IF NOT an single 'integer' value equal to 4 octets AND in the
+ range 1 to MAX, REJECT/RETURN 'client-error-bad-request'.
+
+ IF NOT a job-id of an existing Job object, REJECT/RETURN 'client-
+ error-not-found' or 'client-error-gone' status code, if keep
+ track of recently deleted jobs.
+
+
+ requested-attributes (1setOf keyword)
+
+ IF NOT one or more 'keyword' values, REJECT/RETURN 'client-error-
+ bad-request'.
+ IF the value length is greater than 255 octets, REJECT/RETURN
+ 'client-error-request-value-too-long'.
+ Ignore unsupported values which are the keyword names of
+ unsupported attributes. Don't bother to copy such requested
+ (unsupported) attributes to the Unsupported Attribute response
+ group since the response will not return them.
+
+
+
+Hastings & Manros Informational [Page 22]
+
+RFC 2639 IPP/1.0: Implementer's Guide July 1999
+
+
+ which-jobs (type2 keyword)
+
+ IF NOT a single 'keyword' value, REJECT/RETURN 'client-error-bad-
+ request'.
+ IF the value length is greater than 255 octets, REJECT/RETURN
+ 'client-error-request-value-too-long'.
+ IF NEITHER 'completed' NOR 'not-completed', copy the attribute and
+ the unsupported value to the Unsupported Attributes response
+ group and REJECT/RETURN 'client-error-attributes-or-values-
+ not-supported'.
+ Note: a Printer still supports the 'completed' value even if it
+ keeps no completed/canceled/aborted jobs: by returning no jobs
+ when so queried.
+ IF NOT supplied by the client, the IPP object assumes the 'not-
+ completed' value.
+
+
+ my-jobs (boolean)
+
+ IF NEITHER a single 'true' NOR a single 'false' 'boolean' value,
+ REJECT/RETURN 'client-error-bad-request'.
+ IF the value length is NOT equal to 1 octet, REJECT/RETURN '
+ client-error-request-value-too-long'
+ IF NOT supplied by the client, the IPP object assumes the 'false'
+ value.
+
+
+ limit (integer(1:MAX))
+
+ IF NOT a single 'integer' value equal to 4 octets AND in the range
+ 1 to MAX, REJECT/RETURN 'client-error-bad-request'.
+ IF NOT supplied by the client, the IPP object returns all jobs, no
+ matter how many.
+
+2.2.1.6 Validate the values of the OPTIONAL Operation attributes
+
+ OPTIONAL Operation attributes are those that an IPP object MAY or MAY
+ NOT support. An IPP object validates the values of the OPTIONAL
+ attributes supplied by the client. The IPP object performs the same
+ syntactic validation checks for each OPTIONAL attribute value as in
+ Section 2.2.1.5. As in Section 2.2.1.5, if any fail, the IPP object
+ REJECTS the request and RETURNS the 'client-error-bad-request' or the
+ 'client-error-request-value-too-long' status code.
+
+ In addition, the IPP object checks each Operation attribute value
+ against some Printer attribute or some hard-coded value if there is
+ no "xxx-supported" Printer attribute defined. If its value is not
+ among those supported or is not in the range supported, then the IPP
+
+
+
+Hastings & Manros Informational [Page 23]
+
+RFC 2639 IPP/1.0: Implementer's Guide July 1999
+
+
+ object REJECTS the request and RETURNS the error status code
+ indicated in the table. If the value of the Printer object's "xxx-
+ supported" attribute is 'no-value' (because the system administrator
+ hasn't configured a value), the check always fails.
+
+ If the IPP object doesn't recognize/support an attribute, the IPP
+ object treats the attribute as an unknown or unsupported attribute
+ (see the last row in the table below).
+
+ document-natural-language (naturalLanguage)
+
+ IF NOT a single non-empty 'naturalLanguage' value, REJECT/RETURN '
+ client-error-bad-request'.
+ IF the value length is greater than 63 octets, REJECT/RETURN '
+ client-error-request-value-too-long'.
+ IF NOT a value that the Printer object supports in document
+ formats, (no corresponding "xxx-supported" Printer attribute),
+ REJECT/RETURN 'client-error-natural-language-not-supported'.
+
+
+ compression (type3 keyword)
+
+ IF NOT a single 'keyword' value, REJECT/RETURN 'client-error-bad-
+ request'.
+ IF the value length is greater than 255 octets, REJECT/RETURN '
+ client-error-request-value-too-long'.
+ IF NOT in the Printer object's "compression-supported" attribute,
+ copy the attribute and the unsupported value to the Unsupported
+ Attributes response group and REJECT/RETURN 'client-error-
+ attributes-or-values-not-supported'.
+
+
+ job-k-octets (integer(0:MAX))
+
+ IF NOT a single 'integer' value equal to 4 octets,
+ REJECT/RETURN 'client-error-bad-request'.
+ IF NOT in the range of the Printer object's "job-k-octets-
+ supported" attribute, copy the attribute and the unsupported
+ value to the Unsupported Attributes response group and
+ REJECT/RETURN 'client-error-attributes-or-values-not-
+ supported'.
+
+
+ job-impressions (integer(0:MAX))
+
+ IF NOT a single 'integer' value equal to 4 octets,
+ REJECT/RETURN 'client-error-bad-request'.
+
+
+
+
+Hastings & Manros Informational [Page 24]
+
+RFC 2639 IPP/1.0: Implementer's Guide July 1999
+
+
+ IF NOT in the range of the Printer object's "job-impressions-
+ supported" attribute, copy the attribute and the unsupported
+ value to the Unsupported Attributes response group and
+ REJECT/RETURN 'client-error-attributes-or-values-not-
+ supported'.
+
+
+ job-media-sheets (integer(0:MAX))
+
+ IF NOT a single 'integer' value equal to 4 octets,
+ REJECT/RETURN 'client-error-bad-request'.
+ IF NOT in the range of the Printer object's "job-media-sheets-
+ supported" attribute, copy the attribute and the unsupported
+ value to the Unsupported Attributes response group and
+ REJECT/RETURN 'client-error-attributes-or-values-not-
+ supported'.
+
+
+ message (text(127))
+
+ IF NOT a single 'text' value, REJECT/RETURN 'client-error-bad-
+ request'.
+ IF the value length is greater than 127 octets,
+ REJECT/RETURN 'client-error-request-value-too-long'.
+
+
+ unknown or unsupported attribute
+
+ IF the attribute syntax supplied by the client is supported but
+ the length is not legal for that attribute syntax,
+ REJECT/RETURN 'client-error-request-value-too-long'.
+ ELSE copy the attribute and value to the Unsupported Attributes
+ response group and change the attribute value to the "out-of-
+ band" 'unsupported' value, but otherwise ignore the attribute.
+
+ Note: Future Operation attributes may be added to the protocol
+ specification that may occur anywhere in the specified group.
+ When the operation is otherwise successful, the IPP object returns
+ the 'successful-ok-ignored-or-substituted-attributes' status code.
+ Ignoring unsupported Operation attributes in all operations is
+ analogous to the handling of unsupported Job Template attributes
+ in the create and Validate-Job operations when the client supplies
+ the "ipp-attribute-fidelity" Operation attribute with the 'false'
+ value. This last rule is so that we can add OPTIONAL Operation
+ attributes to future versions of IPP so that older clients can
+ inter-work with new IPP objects and newer clients can inter-work
+ with older IPP objects. (If the new attribute cannot be ignored
+ without performing unexpectedly, the major version number would
+
+
+
+Hastings & Manros Informational [Page 25]
+
+RFC 2639 IPP/1.0: Implementer's Guide July 1999
+
+
+ have been increased in the protocol document and in the request).
+ This rule for Operation attributes is independent of the value of
+ the "ipp-attribute-fidelity" attribute. For example, if an IPP
+ object doesn't support the OPTIONAL "job-k-octets" attribute', the
+ IPP object treats "job-k-octets" as an unknown attribute and only
+ checks the length for the 'integer' attribute syntax supplied by
+ the client. If it is not four octets, the IPP object REJECTS the
+ request and RETURNS the 'client-error-bad-request' status code,
+ else the IPP object copies the attribute to the Unsupported
+ Attribute response group, setting the value to the "out-of-band" '
+ unsupported' value, but otherwise ignores the attribute.
+
+2.2.2 Suggested Additional Processing Steps for Operations that
+ Create/Validate Jobs and Add Documents
+
+ This section in combination with the previous section recommends the
+ processing steps for the Print-Job, Validate-Job, Print-URI, Create-
+ Job, Send-Document, and Send-URI operations that IPP objects SHOULD
+ use. These are the operations that create jobs, validate a Print-Job
+ request, and add documents to a job.
+
+2.2.2.1 Default "ipp-attribute-fidelity" if not supplied
+
+ The Printer object checks to see if the client supplied an "ipp-
+ attribute-fidelity" Operation attribute. If the attribute is not
+ supplied by the client, the IPP object assumes that the value is
+ 'false'.
+
+2.2.2.2 Check that the Printer object is accepting jobs
+
+ If the value of the Printer object's "printer-is-accepting-jobs" is
+ 'false', the Printer object REJECTS the request and RETURNS the
+ 'server-error-not-accepting-jobs' status code.
+
+2.2.2.3 Validate the values of the Job Template attributes
+
+ An IPP object validates the values of all Job Template attribute
+ supplied by the client. The IPP object performs the analogous
+ syntactic validation checks of each Job Template attribute value that
+ it performs for Operation attributes (see Section 2.2.1.5.):
+
+ a)that the length of each value is correct for the attribute
+ syntax tag supplied by the client according to [RFC2566] Section
+ 4.1.
+
+ b)that the attribute syntax tag is correct for that attribute
+ according to [RFC2566] Sections 4.2 to 4.4.
+
+
+
+
+Hastings & Manros Informational [Page 26]
+
+RFC 2639 IPP/1.0: Implementer's Guide July 1999
+
+
+ c)that multiple values are supplied only for multi-valued
+ attributes, i.e., that are 1setOf X according to [RFC2566]
+ Sections 4.2 to 4.4.
+
+ As in Section 2.2.1.5, if any of these syntactic checks fail, the IPP
+ object REJECTS the request and RETURNS the 'client-error-bad-request'
+ or 'client-error-request-value-too-long' status code as appropriate,
+ independent of the value of the "ipp-attribute-fidelity". Since such
+ an error is most likely to be an error detected by a client
+ developer, rather than by an end-user, the IPP object NEED NOT return
+ an indication of which attribute had the error in either the
+ Unsupported Attributes Group or the Status Message. The description
+ for each of these syntactic checks is explicitly expressed in the
+ first IF statement in the following table.
+
+ Each Job Template attribute MUST occur no more than once. If an IPP
+ Printer receives a create request with multiple occurrences of a Job
+ Template attribute, it MAY:
+
+ 1.reject the operation and return the 'client-error-bad syntax'
+ error status code
+
+ 2.accept the operation and use the first occurrence of the
+ attribute
+
+ 3.accept the operation and use the last occurrence of the
+ attribute
+
+ depending on implementation. Therefore, clients MUST NOT supply
+ multiple occurrences of the same Job Template attribute in the Job
+ Attributes group in the request.
+
+2.2.3 Algorithm for job validation
+
+ The process of validating a Job-Template attribute "xxx" against a
+ Printer attribute "xxx-supported" can use the following validation
+ algorithm (see section 3.2.1.2 in [RFC2566]).
+
+ To validate the value U of Job-Template attribute "xxx" against the
+ value V of Printer "xxx-supported", perform the following algorithm:
+
+ 1.If U is multi-valued, validate each value X of U by performing
+ the algorithm in Table 3 with each value X. Each validation is
+ separate from the standpoint of returning unsupported values.
+
+ Example: If U is "finishings" that the client supplies with
+ 'staple', 'bind' values, then X takes on the successive values:
+ 'staple', then 'bind'
+
+
+
+Hastings & Manros Informational [Page 27]
+
+RFC 2639 IPP/1.0: Implementer's Guide July 1999
+
+
+ 2.If V is multi-valued, validate X against each Z of V by
+ performing the algorithm in Table 3 with each value Z. If a
+ value Z validates, the validation for the attribute value X
+ succeeds. If it fails, the algorithm is applied to the next
+ value Z of V. If there are no more values Z of V, validation
+ fails.
+
+ Example: If V is "sides-supported" with values: 'one-sided',
+ 'two-sided-long', and 'two-sided-short', then Z takes on the
+ successive values: 'one-sided', 'two-sided-long', and
+ 'two-sided-short'. If the client supplies "sides" with 'two-
+ sided-long', the first comparison fails ('one-sided' is not
+ equal to 'two-sided-long'), the second comparison succeeds
+ ('two-sided-long' is equal to 'two-sided-long"), and the third
+ comparison ('two-sided-short' with 'two-sided-long') is not even
+ performed.
+
+ 3.If both U and V are single-valued, let X be U and Z be V and use
+ the validation rules in Table 3.
+
+ Table 3 - Rules for validating single values X against Z
+
+ attribute attribute validated if:
+ syntax of X syntax of Z
+
+ integer rangeOfInteger X is within the range of
+ Z
+
+ uri uriScheme the uri scheme in X is
+ equal to Z
+
+ any boolean the value of Z is TRUE
+
+ any any X and Z are of the same
+ type and are equal.
+
+ If the value of the Printer object's "xxx-supported" attribute is '
+ no-value' (because the system administrator hasn't configured a
+ value), the check always fails. If the check fails, the IPP object
+ copies the attribute to the Unsupported Attributes response group
+ with its unsupported value. If the attribute contains more than one
+ value, each value is checked and each unsupported value is separately
+ copied, while supported values are not copied. If an IPP object
+ doesn't recognize/support a Job Template attribute, i.e., there is no
+ corresponding Printer object "xxx-supported" attribute, the IPP
+ object treats the attribute as an unknown or unsupported attribute
+ (see the last row in the table below).
+
+
+
+
+Hastings & Manros Informational [Page 28]
+
+RFC 2639 IPP/1.0: Implementer's Guide July 1999
+
+
+ If some Job Template attributes are supported for some document
+ formats and not for others or the values are different for different
+ document formats, the IPP object SHOULD take that into account in
+ this validation using the value of the "document-format" supplied by
+ the client (or defaulted to the value of the Printer's "document-
+ format-default" attribute, if not supplied by the client). For
+ example, if "number-up" is supported for the 'text/plain' document
+ format, but not for the 'application/postscript' document format, the
+ check SHOULD (though it NEED NOT) depend on the value of the
+ "document-format" operation attribute. See "document-format" in
+ [RFC2566] section 3.2.1.1 and 3.2.5.1.
+
+ Note: whether the request is accepted or rejected is determined by
+ the value of the "ipp-attribute-fidelity" attribute in a subsequent
+ step, so that all Job Template attribute supplied are examined and
+ all unsupported attributes and/or values are copied to the
+ Unsupported Attributes response group.
+
+ job-priority (integer(1:100))
+
+ IF NOT a single 'integer' value with a length equal to 4 octets,
+ REJECT/RETURN 'client-error-bad-request'.
+ IF NOT supplied by the client, use the value of the Printer
+ object's "job-priority-default" attribute at job submission
+ time.
+ IF NOT in the range 1 to 100, inclusive, copy the attribute and
+ the unsupported value to the Unsupported Attributes response
+ group.
+ Map the value to the nearest supported value in the range 1:100 as
+ specified by the number of discrete values indicated by the
+ value of the Printer's "job-priority-supported" attribute. See
+ the formula in [RFC2566] Section 4.2.1.
+
+ job-hold-until (type3 keyword | name)
+
+ IF NOT a single 'keyword' or 'name' value, REJECT/RETURN 'client-
+ error-bad-request'.
+ IF the value length is greater than 255 octets, REJECT/RETURN
+ 'client-error-request-value-too-long'.
+ IF NOT supplied by the client, use the value of the Printer
+ object's "job-hold-until" attribute at job submission time.
+ IF NOT in the Printer object's "job-hold-until-supported"
+ attribute, copy the attribute and the unsupported value to the
+ Unsupported Attributes response group.
+
+
+
+
+
+
+
+Hastings & Manros Informational [Page 29]
+
+RFC 2639 IPP/1.0: Implementer's Guide July 1999
+
+
+ job-sheets (type3 keyword | name)
+
+ IF NOT a single 'keyword' or 'name' value, REJECT/RETURN 'client-
+ error-bad-request'.
+ IF the value length is greater than 255 octets, REJECT/RETURN
+ 'client-error-request-value-too-long'.
+ IF NOT in the Printer object's "job-sheets-supported" attribute,
+ copy the attribute and the unsupported value to the Unsupported
+ Attributes response group.
+
+ multiple-document-handling (type2 keyword)
+
+ IF NOT a single 'keyword' value, REJECT/RETURN 'client-error-bad-
+ request'.
+ IF the value length is greater than 255 octets, REJECT/RETURN
+ 'client-error-request-value-too-long'.
+ IF NOT in the Printer object's "multiple-document-handling-
+ supported" attribute, copy the attribute and the unsupported
+ value to the Unsupported Attributes response group.
+
+ copies (integer(1:MAX))
+
+ IF NOT a single 'integer' value with a length equal to 4 octets,
+ REJECT/RETURN 'client-error-bad-request'.
+ IF NOT in range of the Printer object's "copies-supported"
+ attribute copy the attribute and the unsupported value to the
+ Unsupported
+ Attributes response group.
+
+ finishings (1setOf type2 enum)
+
+ IF NOT an 'enum' value(s) each with a length equal to 4 octets,
+ REJECT/RETURN 'client-error-bad-request'.
+ IF NOT in the Printer object's "finishings-supported" attribute,
+ copy the attribute and the unsupported value(s), but not any
+ supported values, to the Unsupported Attributes response group.
+
+ page-ranges (1setOf rangeOfInteger(1:MAX))
+
+ IF NOT a 'rangeOfInteger' value(s) each with a length equal to 8
+ octets, REJECT/RETURN 'client-error-bad-request'.
+ IF first value is greater than second value in any range, the
+ ranges are not in ascending order, or ranges overlap,
+ REJECT/RETURN 'client-error-bad-request'.
+ IF the value of the Printer object's "page-ranges-supported"
+ attribute is 'false', copy the attribute to the Unsupported
+ Attributes response group and set the value to the "out-of-
+ band" 'unsupported' value.
+
+
+
+Hastings & Manros Informational [Page 30]
+
+RFC 2639 IPP/1.0: Implementer's Guide July 1999
+
+
+ sides (type2 keyword)
+
+ IF NOT a single 'keyword' value, REJECT/RETURN 'client-error-bad-
+ request'.
+ IF the value length is greater than 255 octets, REJECT/RETURN
+ 'client-error-request-value-too-long'.
+ IF NOT in the Printer object's "sides-supported" attribute, copy
+ the attribute and the unsupported value to the Unsupported
+ Attributes response group.
+
+ number-up (integer(1:MAX))
+
+ IF NOT a single 'integer' value with a length equal to 4 octets,
+ REJECT/RETURN 'client-error-bad-request'.
+ IF NOT a value or in the range of one of the values of the Printer
+ object's "number-up-supported" attribute, copy the attribute
+ and value to the Unsupported Attribute response group.
+
+ orientation-requested (type2 enum)
+
+ IF NOT a single 'enum' value with a length equal to 4 octets,
+ REJECT/RETURN 'client-error-bad-request'.
+ IF NOT in the Printer object's "orientation-requested-supported"
+ attribute, copy the attribute and the unsupported value to the
+ Unsupported Attributes response group.
+
+ media (type3 keyword | name)
+
+ IF NOT a single 'keyword' or 'name' value, REJECT/RETURN 'client-
+ error-bad-request'.
+ IF the value length is greater than 255 octets, REJECT/RETURN
+ 'client-error-request-value-too-long'.
+ IF NOT in the Printer object's "media-supported" attribute, copy
+ the attribute and the unsupported value to the Unsupported
+ Attributes response group.
+
+ printer-resolution (resolution)
+
+ IF NOT a single 'resolution' value with a length equal to 9
+ octets,
+ REJECT/RETURN 'client-error-bad-request'.
+ IF NOT in the Printer object's "printer-resolution-supported"
+ attribute, copy the attribute and the unsupported value to the
+ Unsupported Attributes response group.
+
+
+
+
+
+
+
+Hastings & Manros Informational [Page 31]
+
+RFC 2639 IPP/1.0: Implementer's Guide July 1999
+
+
+ print-quality (type2 enum)
+
+ IF NOT a single 'enum' value with a length equal to 4 octets,
+ REJECT/RETURN 'client-error-bad-request'.
+ IF NOT in the Printer object's "print-quality-supported"
+ attribute, copy the attribute and the unsupported value to the
+ Unsupported Attributes response group.
+
+ unknown or unsupported attribute (i.e., there is no corresponding
+ Printer object "xxx-supported" attribute)
+
+ IF the attribute syntax supplied by the client is supported but
+ the length is not legal for that attribute syntax,
+ REJECT/RETURN 'client-error-bad-request' if the length of the
+ attribute syntax is fixed or 'client-error-request-value-too-
+ long' if the length of the attribute syntax is variable.
+ ELSE copy the attribute and value to the Unsupported Attributes
+ response group and change the attribute value to the "out-of-
+ band" 'unsupported' value. Any remaining Job Template
+ Attributes are either unknown or unsupported Job Template
+ attributes and are validated algorithmically according to their
+ attribute syntax for proper length (see below).
+
+ If the attribute syntax is supported AND the length check
+ fails, the IPP object REJECTS the request and RETURNS the '
+ client-error-bad-request' if the length of the attribute syntax
+ is fixed or the 'client-error-request-value-too-long' status
+ code if the length of the attribute syntax is variable.
+ Otherwise, the IPP object copies the unsupported Job Template
+ attribute to the Unsupported Attributes response group and
+ changes the attribute value to the "out-of-band" 'unsupported'
+ value. The following table shows the length checks for all
+ attribute syntaxes. In the following table: "<=" means less
+ than or equal, "=" means equal to:
+
+ Name Octet length check for read-write attributes
+ ----------- --------------------------------------------
+ 'textWithLanguage <= 1023 AND 'naturalLanguage' <= 63
+ 'textWithoutLanguage' <= 1023
+ 'nameWithLanguage' <= 255 AND 'naturalLanguage' <= 63
+ 'nameWithoutLanguage' <= 255
+ 'keyword' <= 255
+ 'enum' = 4
+ 'uri' <= 1023
+ 'uriScheme' <= 63
+ 'charset' <= 63
+ 'naturalLanguage' <= 63
+ 'mimeMediaType' <= 255
+
+
+
+Hastings & Manros Informational [Page 32]
+
+RFC 2639 IPP/1.0: Implementer's Guide July 1999
+
+
+ 'octetString' <= 1023
+ 'boolean' = 1
+ 'integer' = 4
+ 'rangeOfInteger' = 8
+ 'dateTime' = 11
+ 'resolution' = 9
+ '1setOf X'
+
+2.2.3.1 Check for conflicting Job Template attributes values
+
+ Once all the Operation and Job Template attributes have been checked
+ individually, the Printer object SHOULD check for any conflicting
+ values among all the supported values supplied by the client. For
+ example, a Printer object might be able to staple and to print on
+ transparencies, however due to physical stapling constraints, the
+ Printer object might not be able to staple transparencies. The IPP
+ object copies the supported attributes and their conflicting
+ attribute values to the Unsupported Attributes response group. The
+ Printer object only copies over those attributes that the Printer
+ object either ignores or substitutes in order to resolve the
+ conflict, and it returns the original values which were supplied by
+ the client. For example suppose the client supplies "finishings"
+ equals 'staple' and "media" equals 'transparency', but the Printer
+ object does not support stapling transparencies. If the Printer
+ chooses to ignore the stapling request in order to resolve the
+ conflict, the Printer objects returns "finishings" equal to 'staple'
+ in the Unsupported Attributes response group. If any attributes are
+ multi-valued, only the conflicting values of the attributes are
+ copied.
+
+ Note: The decisions made to resolve the conflict (if there is a
+ choice) is implementation dependent.
+
+2.2.3.2 Decide whether to REJECT the request
+
+ If there were any unsupported Job Template attributes or
+ unsupported/conflicting Job Template attribute values and the client
+ supplied the "ipp-attribute-fidelity" attribute with the 'true'
+ value, the Printer object REJECTS the request and return the status
+ code:
+
+ (1) 'client-error-conflicting-attributes' status code, if there
+ were any conflicts between attributes supplied by the client.
+ (2) 'client-error-attributes-or-values-not-supported' status code,
+ otherwise.
+
+
+
+
+
+
+Hastings & Manros Informational [Page 33]
+
+RFC 2639 IPP/1.0: Implementer's Guide July 1999
+
+
+ Note: Unsupported Operation attributes or values that are returned
+ do not affect the status returned in this step. If the unsupported
+ Operation attribute was a serious error, the above already rejected
+ the request in a previous step. If control gets to this step with
+ unsupported Operation attributes being returned, they are not serious
+ errors.
+
+2.2.3.3 For the Validate-Job operation, RETURN one of the success
+ status codes
+
+ If the requested operation is the Validate-Job operation, the Printer
+ object returns:
+
+ (1) the "successful-ok" status code, if there are no unsupported
+ or conflicting Job Template attributes or values.
+ (2) the "successful-ok-conflicting-attributes, if there are any
+ conflicting Job Template attribute or values.
+ (3) the "successful-ok-ignored-or-substituted-attributes, if there
+ are only unsupported Job Template attributes or values.
+
+ Note: Unsupported Operation attributes or values that are returned
+ do not affect the status returned in this step. If the unsupported
+ Operation attribute was a serious error, the above already rejected
+ the request in a previous step. If control gets to this step with
+ unsupported Operation attributes being returned, they are not serious
+ errors.
+
+2.2.3.4 Create the Job object with attributes to support
+
+ If "ipp-attribute-fidelity" is set to 'false' (or it was not supplied
+ by the client), the Printer object:
+
+ (1) creates a Job object, assigns a unique value to the job's
+ "job-uri" and "job-id" attributes, and initializes all of the
+ job's other supported Job Description attributes.
+ (2) removes all unsupported attributes from the Job object.
+ (3) for each unsupported value, removes either the unsupported
+ value or substitutes the unsupported attribute value with some
+ supported value. If an attribute has no values after removing
+ unsupported values from it, the attribute is removed from the
+ Job object (so that the normal default behavior at job
+ processing time will take place for that attribute).
+ (4) for each conflicting value, removes either the conflicting
+ value or substitutes the conflicting attribute value with some
+ other supported value. If an attribute has no values after
+ removing conflicting values from it, the attribute is removed
+ from the Job object (so that the normal default behavior at
+ job processing time will take place for that attribute).
+
+
+
+Hastings & Manros Informational [Page 34]
+
+RFC 2639 IPP/1.0: Implementer's Guide July 1999
+
+
+ If there were no attributes or values flagged as unsupported, or the
+ value of 'ipp-attribute-fidelity" was 'false', the Printer object is
+ able to accept the create request and create a new Job object. If
+ the "ipp-attribute-fidelity" attribute is set to 'true', the Job
+ Template attributes that populate the new Job object are necessarily
+ all the Job Template attributes supplied in the create request. If
+ the "ipp-attribute-fidelity" attribute is set to 'false', the Job
+ Template attributes that populate the new Job object are all the
+ client supplied Job Template attributes that are supported or that
+ have value substitution. Thus, some of the requested Job Template
+ attributes may not appear in the Job object because the Printer
+ object did not support those attributes. The attributes that
+ populate the Job object are persistently stored with the Job object
+ for that Job. A Get-Job-Attributes operation on that Job object will
+ return only those attributes that are persistently stored with the
+ Job object.
+
+ Note: All Job Template attributes that are persistently stored with
+ the Job object are intended to be "override values"; that is, they
+ that take precedence over whatever other embedded instructions might
+ be in the document data itself. However, it is not possible for all
+ Printer objects to realize the semantics of "override". End users
+ may query the Printer's "pdl-override-supported" attribute to
+ determine if the Printer either attempts or does not attempt to
+ override document data instructions with IPP attributes.
+
+ There are some cases, where a Printer supports a Job Template
+ attribute and has an associated default value set for that attribute.
+ In the case where a client does not supply the corresponding
+ attribute, the Printer does not use its default values to populate
+ Job attributes when creating the new Job object; only Job Template
+ attributes actually in the create request are used to populate the
+ Job object. The Printer's default values are only used later at Job
+ processing time if no other IPP attribute or instruction embedded in
+ the document data is present.
+
+ Note: If the default values associated with Job Template attributes
+ that the client did not supply were to be used to populate the Job
+ object, then these values would become "override values" rather than
+ defaults. If the Printer supports the 'attempted' value of the
+ "pdl-override-supported" attribute, then these override values could
+ replace values specified within the document data. This is not the
+ intent of the default value mechanism. A default value for an
+ attribute is used only if the create request did not specify that
+ attribute (or it was ignored when allowed by "ipp-attribute-fidelity"
+ being 'false') and no value was provided within the content of the
+ document data.
+
+
+
+
+Hastings & Manros Informational [Page 35]
+
+RFC 2639 IPP/1.0: Implementer's Guide July 1999
+
+
+ If the client does not supply a value for some Job Template
+ attribute, and the Printer does not support that attribute, as far as
+ IPP is concerned, the result of processing that Job (with respect to
+ the missing attribute) is undefined.
+
+2.2.3.5 Return one of the success status codes
+
+ Once the Job object has been created, the Printer object accepts the
+ request and returns to the client:
+
+ (1) the 'successful-ok' status code, if there are no unsupported
+ or conflicting Job Template attributes or values.
+ (2) the 'successful-ok-conflicting-attributes' status code, if
+ there are any conflicting Job Template attribute or values.
+ (3) the 'successful-ok-ignored-or-substituted-attributes' status
+ code, if there are only unsupported Job Template attributes or
+ values.
+
+ Note: Unsupported Operation attributes or values that are returned
+ do not affect the status returned in this step. If the unsupported
+ Operation attribute was a serious error, the above already rejected
+ the request in a previous step. If control gets to this step with
+ unsupported Operation attributes being returned, they are not serious
+ errors.
+
+ The Printer object also returns Job status attributes that indicate
+ the initial state of the Job ('pending', 'pending-held', '
+ processing', etc.), etc. See Print-Job Response, [RFC2566] section
+ 3.2.1.2.
+
+2.2.3.6 Accept appended Document Content
+
+ The Printer object accepts the appended Document Content data and
+ either starts it printing, or spools it for later processing.
+
+2.2.3.7 Scheduling and Starting to Process the Job
+
+ The Printer object uses its own configuration and implementation
+ specific algorithms for scheduling the Job in the correct processing
+ order. Once the Printer object begins processing the Job, the
+ Printer changes the Job's state to 'processing'. If the Printer
+ object supports PDL override (the "pdl-override-supported" attribute
+ set to 'attempted'), the implementation does its best to see that IPP
+ attributes take precedence over embedded instructions in the document
+ data.
+
+
+
+
+
+
+Hastings & Manros Informational [Page 36]
+
+RFC 2639 IPP/1.0: Implementer's Guide July 1999
+
+
+2.2.3.8 Completing the Job
+
+ The Printer object continues to process the Job until it can move the
+ Job into the 'completed' state. If an Cancel-Job operation is
+ received, the implementation eventually moves the Job into the '
+ canceled' state. If the system encounters errors during processing
+ that do not allow it to progress the Job into a completed state, the
+ implementation halts all processing, cleans up any resources, and
+ moves the Job into the 'aborted' state.
+
+2.2.3.9 Destroying the Job after completion
+
+ Once the Job moves to the 'completed', 'aborted', or 'canceled'
+ state, it is an implementation decision as to when to destroy the Job
+ object and release all associated resources. Once the Job has been
+ destroyed, the Printer would return either the "client-error-not-
+ found" or "client-error-gone" status codes for operations directed at
+ that Job.
+
+ Note: the Printer object SHOULD NOT re-use a "job-uri" or "job-id"
+ value for a sufficiently long time after a job has been destroyed, so
+ that stale references kept by clients are less likely to access the
+ wrong (newer) job.
+
+2.2.3.10 Interaction with "ipp-attribute-fidelity"
+
+ Some Printer object implementations may support "ipp-attribute-
+ fidelity" set to 'true' and "pdl-override-supported" set to '
+ attempted' and yet still not be able to realize exactly what the
+ client specifies in the create request. This is due to legacy
+ decisions and assumptions that have been made about the role of job
+ instructions embedded within the document data and external job
+ instructions that accompany the document data and how to handle
+ conflicts between such instructions. The inability to be 100%
+ precise about how a given implementation will behave is also
+ compounded by the fact that the two special attributes, "ipp-
+ attribute-fidelity" and "pdl-override-supported", apply to the whole
+ job rather than specific values for each attribute. For example, some
+ implementations may be able to override almost all Job Template
+ attributes except for "number-up".
+
+2.3 Status codes returned by operation
+
+ This section lists all status codes once in the first operation
+ (Print-Job). Then it lists the status codes that are different or
+ specialized for subsequent operations under each operation.
+
+
+
+
+
+Hastings & Manros Informational [Page 37]
+
+RFC 2639 IPP/1.0: Implementer's Guide July 1999
+
+
+2.3.1 Printer Operations
+
+2.3.1.1 Print-Job
+
+ The Printer object MUST return one of the following "status-code"
+ values for the indicated reason. Whether all of the document data
+ has been accepted or not before returning the success or error
+ response depends on implementation. See Section 14 for a more
+ complete description of each status code.
+
+ For the following success status codes, the Job object has been
+ created and the "job-id", and "job-uri" assigned and returned in the
+ response:
+
+ successful-ok: no request attributes were substituted or ignored.
+ successful-ok-ignored-or-substituted-attributes: some supplied
+ (1) attributes were ignored or (2) unsupported attribute
+ syntaxes or values were substituted with supported values or
+ were ignored. Unsupported attributes, attribute syntaxes, or
+ values MUST be returned in the Unsupported Attributes group of
+ the response.
+ successful-ok-conflicting-attributes: some supplied attribute
+ values conflicted with the values of other supplied attributes
+ and were either substituted or ignored. Attributes or values
+ which conflict with other attributes and have been substituted
+ or ignored MUST be returned in the Unsupported Attributes group
+ of the response as supplied by the client.
+
+ [RFC2566] section 3.1.6 Operation Status Codes and Messages states:
+
+ If the Printer object supports the "status-message" operation
+ attribute, it SHOULD use the REQUIRED 'utf-8' charset to return
+ a status message for the following error status codes (see
+ section 14): 'client-error-bad-request', 'client-error-
+ charset-not-supported', 'server-error-internal-error', '
+ server-error-operation-not-supported', and 'server-error-
+ version-not-supported'. In this case, it MUST set the value of
+ the "attributes-charset" operation attribute to 'utf-8' in the
+ error response.
+
+ For the following error status codes, no job is created and no "job-
+ id" or "job-uri" is returned:
+
+ client-error-bad-request: The request syntax does not conform to
+ the specification.
+
+
+
+
+
+
+Hastings & Manros Informational [Page 38]
+
+RFC 2639 IPP/1.0: Implementer's Guide July 1999
+
+
+ client-error-forbidden: The request is being refused for
+ authorization or authentication reasons. The implementation
+ security policy is to not reveal whether the failure is one of
+ authentication or authorization.
+ client-error-not-authenticated: Either the request requires
+ authentication information to be supplied or the authentication
+ information is not sufficient for authorization.
+ client-error-not-authorized: The requester is not authorized to
+ perform the request on the target object.
+ client-error-not-possible: The request cannot be carried out
+ because of the state of the system. See also 'server-error-
+ not-accepting-jobs' status code which MUST take precedence if
+ the Printer object's "printer-accepting-jobs" attribute is '
+ false'.
+ client-error-timeout: not applicable.
+ client-error-not-found: the target object does not exist.
+ client-error-gone: the target object no longer exists and no
+ forwarding address is known.
+ client-error-request-entity-too-large: the size of the request
+ and/or print data exceeds the capacity of the IPP Printer to
+ process it.
+ client-error-request-value-too-long: the size of request variable
+ length attribute values, such as 'text' and 'name' attribute
+ syntaxes, exceed the maximum length specified in [RFC2566] for
+ the attribute and MUST be returned in the Unsupported
+ Attributes Group.
+ client-error-document-format-not-supported: the document format
+ supplied is not supported. The "document-format" attribute
+ with the unsupported value MUST be returned in the Unsupported
+ Attributes Group. This error SHOULD take precedence over any
+ other 'xxx-not-supported' error, except 'client-error-charset-
+ not-supported'.
+ client-error-attributes-or-values-not-supported: one or more
+ supplied attributes, attribute syntaxes, or values are not
+ supported and the client supplied the "ipp-attributes-fidelity"
+ operation attribute with a 'true' value. They MUST be returned
+ in the Unsupported Attributes Group as explained below.
+ client-error-uri-scheme-not-supported: not applicable.
+ client-error-charset-not-supported: the charset supplied in the
+ "attributes-charset" operation attribute is not supported. The
+ Printer's "configured-charset" MUST be returned in the response
+ as the value of the "attributes-charset" operation attribute
+ and used for any 'text' and 'name' attributes returned in the
+ error response. This error SHOULD take precedence over any
+ other error, unless the request syntax is so bad that the
+ client's supplied "attributes-charset" cannot be determined.
+
+
+
+
+
+Hastings & Manros Informational [Page 39]
+
+RFC 2639 IPP/1.0: Implementer's Guide July 1999
+
+
+ client-error-conflicting-attributes: one or more supplied
+ attribute va attribute values conflicted with each other and
+ the client supplied the "ipp-attributes-fidelity" operation
+ attribute with a 'true' value. They MUST be returned in the
+ Unsupported Attributes Group as explained below.
+ server-error-internal-error: an unexpected condition prevents the
+ request from being fulfilled.
+ server-error-operation-not-supported: not applicable (since
+ Print-Job is REQUIRED).
+ server-error-service-unavailable: the service is temporarily
+ overloaded.
+ server-error-version-not-supported: the version in the request is
+ not supported. The "closest" version number supported MUST be
+ returned in the response.
+ server-error-device-error: a device error occurred while
+ receiving or spooling the request or document data or the IPP
+ Printer object can only accept one job at a time.
+ server-error-temporary-error: a temporary error such as a buffer
+ full write error, a memory overflow, or a disk full condition
+ occurred while receiving the request and/or the document data.
+ server-error-not-accepting-jobs: the Printer object's "printer-
+ is-not-accepting-jobs" attribute is 'false'.
+ server-error-busy: the Printer is too busy processing jobs to
+ accept another job at this time.
+ server-error-job-canceled: the job has been canceled by an
+ operator or the system while the client was transmitting the
+ document data.
+
+2.3.1.2 Print-URI
+
+ All of the Print-Job status codes described in Section 3.2.1.2
+ Print-Job Response are applicable to Print-URI with the following
+ specializations and differences. See Section 14 for a more complete
+ description of each status code.
+
+ server-error-uri-scheme-not-supported: the URI scheme supplied in
+ the "document-uri" operation attribute is not supported and is
+ returned in the Unsupported Attributes group.
+
+2.3.1.3 Validate-Job
+
+ All of the Print-Job status codes described in Section 3.2.1.2
+ Print-Job Response are applicable to Validate-Job. See Section 14
+ for a more complete description of each status code.
+
+
+
+
+
+
+
+Hastings & Manros Informational [Page 40]
+
+RFC 2639 IPP/1.0: Implementer's Guide July 1999
+
+
+2.3.1.4 Create-Job
+
+ All of the Print-Job status codes described in Section 3.2.1.2
+ Print-Job Response are applicable to Create-Job with the following
+ specializations and differences. See Section 14 for a more complete
+ description of each status code.
+
+ server-error-operation-not-supported: the Create-Job operation is
+ not supported.
+
+2.3.1.5 Get-Printer-Attributes
+
+ All of the Print-Job status codes described in Section 3.2.1.2
+ Print-Job Response are applicable to the Get-Printer-Attributes
+ operation with the following specializations and differences. See
+ Section 14 for a more complete description of each status code.
+
+ For the following success status codes, the requested attributes are
+ returned in Group 3 in the response:
+
+ successful-ok: no request attributes were substituted or ignored
+ (same as Print-Job) and no requested attributes were
+ unsupported.
+ successful-ok-ignored-or-substituted-attributes: same as Print-
+ Job, except the "requested-attributes" operation attribute MAY,
+ but NEED NOT, be returned with the unsupported values.
+ successful-ok-conflicting-attributes: same as Print-Job.
+
+ For the error status codes, Group 3 is returned containing no
+ attributes or is not returned at all:
+
+ client-error-not-possible: Same as Print-Job, in addition the
+ Printer object is not accepting any requests.
+ client-error-request-entity-too-large: same as Print-job, except
+ that no print data is involved.
+ client-error-attributes-or-values-not-supported: not applicable,
+ since unsupported operation attributes MUST be ignored and '
+ successful-ok-ignored-or-substituted-attributes' returned.
+ client-error-conflicting-attributes: same as Print-Job, except
+ that "ipp-attribute-fidelity" is not involved.
+ server-error-operation-not-supported: not applicable (since Get-
+ Printer-Attributes is REQUIRED).
+ server-error-device-error: same as Print-Job, except that no
+ document data is involved.
+ server-error-temporary-error: same as Print-Job, except that no
+ document data is involved.
+ server-error-not-accepting-jobs: not applicable.
+
+
+
+
+Hastings & Manros Informational [Page 41]
+
+RFC 2639 IPP/1.0: Implementer's Guide July 1999
+
+
+ server-error-busy: same as Print-Job, except the IPP object is
+ too busy to accept even query requests.
+ server-error-job-canceled: not applicable.
+
+2.3.1.6 Get-Jobs
+
+ All of the Print-Job status codes described in Section 3.2.1.2
+ Print-Job Response are applicable to the Get-Jobs operation with the
+ following specializations and differences. See Section 14 for a
+ more complete description of each status code.
+
+ For the following success status codes, the requested attributes are
+ returned in Group 3 in the response:
+
+ successful-ok: no request attributes were substituted or ignored
+ (same as Print-Job) and no requested attributes were
+ unsupported.
+ successful-ok-ignored-or-substituted-attributes: same as Print-
+ Job, except the "requested-attributes" operation attribute MAY,
+ but NEED NOT, be returned with the unsupported values.
+ successful-ok-conflicting-attributes: same as Print-Job.
+
+ For any error status codes, Group 3 is returned containing no
+ attributes or is not returned at all. The following brief error
+ status code descriptions contain unique information for use with
+ Get-Jobs operation. See section 14 for the other error status codes
+ that apply uniformly to all operations:
+
+ client-error-not-possible: Same as Print-Job, in addition the
+ Printer object is not accepting any requests.
+ client-error-request-entity-too-large: same as Print-job, except
+ that no print data is involved.
+ client-error-document-format-not-supported: not applicable.
+ client-error-attributes-or-values-not-supported: not applicable,
+ since unsupported operation attributes MUST be ignored and '
+ successful-ok-ignored-or-substituted-attributes' returned.
+ client-error-conflicting-attributes: same as Print-Job, except
+ that "ipp-attribute-fidelity" is not involved.
+ server-error-operation-not-supported: not applicable (since Get-
+ Jobs is REQUIRED).
+ server-error-device-error: same as Print-Job, except that no
+ document data is involved.
+ server-error-temporary-error: same as Print-Job, except that no
+ document data is involved.
+ server-error-not-accepting-jobs: not applicable.
+ server-error-job-canceled: not applicable.
+
+
+
+
+
+Hastings & Manros Informational [Page 42]
+
+RFC 2639 IPP/1.0: Implementer's Guide July 1999
+
+
+2.3.2 Job Operations
+
+2.3.2.1 Send-Document
+
+ All of the Print-Job status codes described in Section 3.2.1.2
+ Print-Job Response are applicable to the Get-Printer-Attributes
+ operation with the following specializations and differences. See
+ Section 14 for a more complete description of each status code.
+
+ For the following success status codes, the document has been added
+ to the specified Job object and the job's "number-of-documents"
+ attribute has been incremented:
+
+ successful-ok: no request attributes were substituted or ignored
+ (same as Print-Job).
+ successful-ok-ignored-or-substituted-attributes: same as Print-
+ Job.
+ successful-ok-conflicting-attributes: same as Print-Job.
+
+ For the error status codes, no document has been added to the Job
+ object and the job's "number-of-documents" attribute has not been
+ incremented:
+
+ client-error-not-possible: Same as Print-Job, except that the
+ Printer's "printer-is-accepting-jobs" attribute is not
+ involved, so that the client is able to finish submitting a
+ multi-document job after this attribute has been set to 'true'.
+ Another condition is that the state of the job precludes Send-
+ Document, i.e., the job has already been closed out by the
+ client. However, if the IPP Printer closed out the job due to
+ timeout, the 'client-error-timeout' error status SHOULD be
+ returned instead.
+ client-error-timeout: This request was sent after the Printer
+ closed the job, because it has not received a Send-Document or
+ Send-URI operation within the Printer's "multiple-operation-
+ time-out" period.
+ client-error-request-entity-too-large: same as Print-Job.
+ client-error-conflicting-attributes: same as Print-Job, except
+ that "ipp-attributes-fidelity" operation attribute is not
+ involved.
+ server-error-operation-not-supported: the Send-Document request
+ is not supported.
+ server-error-not-accepting-jobs: not applicable.
+ server-error-job-canceled: the job has been canceled by an
+ operator or the system while the client was transmitting the
+ data.
+
+
+
+
+
+Hastings & Manros Informational [Page 43]
+
+RFC 2639 IPP/1.0: Implementer's Guide July 1999
+
+
+2.3.2.2 Send-URI
+
+ All of the Print-Job status code descriptions in Section 3.2.1.2
+ Print-Job Response with the specializations described for Send-
+ Document are applicable to Send-URI. See Section 14 for a more
+ complete description of each status code.
+
+ server-error-uri-scheme-not-supported: the URI scheme supplied in
+ the "document-uri" operation attribute is not supported and the
+ "document-uri" attribute MUST be returned in the Unsupported
+ Attributes group.
+
+2.3.2.3 Cancel-Job
+
+ All of the Print-Job status codes described in Section 3.2.1.2
+ Print-Job Response are applicable to Cancel-Job with the following
+ specializations and differences. See Section 14 for a more complete
+ description of each status code.
+
+ For the following success status codes, the Job object is being
+ canceled or has been canceled:
+
+ successful-ok: no request attributes were substituted or ignored
+ (same as Print-Job).
+ successful-ok-ignored-or-substituted-attributes: same as Print-
+ Job.
+ successful-ok-conflicting-attributes: same as Print-Job.
+
+ For any of the error status codes, the Job object has not been
+ canceled or was previously canceled.
+
+ client-error-not-possible: The request cannot be carried out
+ because of the state of the Job object ('completed', '
+ canceled', or 'aborted') or the state of the system.
+ client-error-not-found: the target Printer and/or Job object does
+ not exist.
+ client-error-gone: the target Printer and/or Job object no longer
+ exists and no forwarding address is known.
+ client-error-request-entity-too-large: same as Print-Job, except
+ no document data is involved.
+ client-error-document-format-not-supported: not applicable.
+ client-error-attributes-or-values-not-supported: not applicable,
+ since unsupported operation attributes and values MUST be
+ ignored.
+ client-error-conflicting-attributes: same as Print-Job, except
+ that the Printer's "printer-is-accepting-jobs" attribute is not
+ involved.
+
+
+
+
+Hastings & Manros Informational [Page 44]
+
+RFC 2639 IPP/1.0: Implementer's Guide July 1999
+
+
+ server-error-operation-not-supported: not applicable (Cancel-Job
+ is REQUIRED).
+ server-error-device-error: same as Print-Job, except no document
+ data is involved.
+ server-error-temporary-error: same as Print-Job, except no
+ document data is involved.
+ server-error-not-accepting-jobs: not applicable.
+ server-error-job-canceled: not applicable.
+
+2.3.2.4 Get-Job-Attributes
+
+ All of the Print-Job status codes described in Section 3.2.1.2
+ Print-Job Response are applicable to Get-Job-Attributes with the
+ following specializations and differences. See Section 14 for a more
+ complete description of each status code.
+
+ For the following success status codes, the requested attributes are
+ returned in Group 3 in the response:
+
+ successful-ok: no request attributes were substituted or ignored
+ (same as Print-Job) and no requested attributes were
+ unsupported.
+ successful-ok-ignored-or-substituted-attributes: same as Print-
+ Job, except the "requested-attributes" operation attribute MAY,
+ but NEED NOT, be returned with the unsupported values.
+ successful-ok-conflicting-attributes: same as Print-Job.
+
+ For the error status codes, Group 3 is returned containing no
+ attributes or is not returned at all.
+
+ client-error-not-possible: Same as Print-Job, in addition the
+ Printer object is not accepting any requests.
+ client-error-document-format-not-supported: not applicable.
+ client-error-attributes-or-values-not-supported: not applicable.
+ client-error-uri-scheme-not-supported: not applicable.
+ client-error-conflicting-attributes: not applicable
+ server-error-operation-not-supported: not applicable (since Get-
+ Job-Attributes is REQUIRED).
+ server-error-device-error: same as Print-Job, except no document
+ data is involved.
+ server-error-temporary-error: sane as Print-Job, except no
+ document data is involved.
+ server-error-not-accepting-jobs: not applicable. server-error-
+ job-canceled: not applicable.
+
+
+
+
+
+
+
+Hastings & Manros Informational [Page 45]
+
+RFC 2639 IPP/1.0: Implementer's Guide July 1999
+
+
+2.4 Validate-Job
+
+ The Validate-Job operation has been designed so that its
+ implementation may be a part of the Print-Job operation. Therefore,
+ requiring Validate-Job is not a burden on implementers. Also it is
+ useful for client's to be able to count on its presence in all
+ conformance implementations, so that the client can determine before
+ sending a long document, whether the job will be accepted by the IPP
+ Printer or not.
+
+2.5 Case Sensitivity in URIs
+
+ IPP client and server implementations must be aware of the diverse
+ uppercase/lowercase nature of URIs. RFC 2396 defines URL schemes and
+ Host names as case insensitive but reminds us that the rest of the
+ URL may well demonstrate case sensitivity. When creating URL's for
+ fields where the choice is completely arbitrary, it is probably best
+ to select lower case. However, this cannot be guaranteed and
+ implementations MUST NOT rely on any fields being case-sensitive or
+ case-insensitive in the URL beyond the URL scheme and host name
+ fields.
+
+ The reason that the IPP specification does not make any restrictions
+ on URIs, is so that implementations of IPP may use off-the-shelf
+ components that conform to the standards that define URIs, such as
+ RFC 2396 and the HTTP/1.1 specifications [RFC2068]. See these
+ specifications for rules of matching, comparison, and case-
+ sensitivity.
+
+ It is also recommended that System Administrators and implementations
+ avoid creating URLs for different printers that differ only in their
+ case. For example, don't have Printer1 and printer1 as two different
+ IPP Printers.
+
+ The HTTP/1.1 specification [RFC2068] contains more details on
+ comparing URLs.
+
+2.6 Character Sets, natural languages, and internationalization
+
+ This section discusses character set support, natural language
+ support and internationalization.
+
+2.6.1 Character set code conversion support
+
+ IPP clients and IPP objects are REQUIRED to support UTF-8. They MAY
+ support additional charsets. It is RECOMMENDED that an IPP object
+ also support US-ASCII, since many clients support US-ASCII, and
+ indicate that UTF-8 and US-ASCII are supported by populating the
+
+
+
+Hastings & Manros Informational [Page 46]
+
+RFC 2639 IPP/1.0: Implementer's Guide July 1999
+
+
+ Printer's "charset-supported" with 'utf-8' and 'us-ascii' values. An
+ IPP object is required to code covert with as little loss as possible
+ between the charsets that it supports, as indicated in the Printer's
+ "charsets-supported" attribute.
+
+ How should the server handle the situation where the "attributes-
+ charset" of the response itself is "us-ascii", but one or more
+ attributes in that response is in the "utf-8" format?
+
+ Example: Consider a case where a client sends a Print-Job request
+ with "utf-8" as the value of "attributes-charset" and with the "job-
+ name" attribute supplied. Later another client submits a Get-Job-
+ Attribute or Get-Jobs request. This second request contains the
+ "attributes-charset" with value "us-ascii" and "requested-attributes"
+ attribute with exactly one value "job-name".
+
+ According to the RFC2566 document (section 3.1.4.2), the value of the
+ "attributes-charset" for the response of the second request must be
+ "us-ascii" since that is the charset specified in the request. The
+ "job-name" value, however, is in "utf-8" format. Should the request
+ be rejected even though both "utf-8" and "us-ascii" charsets are
+ supported by the server? or should the "job-name" value be converted
+ to "us-ascii" and return "successful-ok-conflicting-attributes"
+ (0x0002) as the status code?
+
+ Answer: An IPP object that supports both utf-8 (REQUIRED) and us-
+ ascii, the second paragraph of section 3.1.4.2 applies so that the
+ IPP object MUST accept the request, perform code set conversion
+ between these two charsets with "the highest fidelity possible" and
+ return 'successful-ok', rather than a warning 'successful-ok-
+ conflicting-attributes, or an error. The printer will do the best it
+ can to convert between each of the character sets that it supports--
+ even if that means providing a string of question marks because none
+ of the characters are representable in US ASCII. If it can't perform
+ such conversion, it MUST NOT advertise us-ascii as a value of its
+ "attributes-charset-supported" and MUST reject any request that
+ requests 'us-ascii'.
+
+ One IPP object implementation strategy is to convert all request text
+ and name values to a Unicode internal representation. This is 16-bit
+ and virtually universal. Then convert to the specified operation
+ attributes-charset on output.
+
+ Also it would be smarter for a client to ask for 'utf-8', rather than
+ 'us-ascii' and throw away characters that it doesn't understand,
+ rather than depending on the code conversion of the IPP object.
+
+
+
+
+
+Hastings & Manros Informational [Page 47]
+
+RFC 2639 IPP/1.0: Implementer's Guide July 1999
+
+
+2.6.2 What charset to return when an unsupported charset is requested?
+
+ Section 3.1.4.1 Request Operation attributes was clarified in
+ November 1998 as follows:
+
+ All clients and IPP objects MUST support the 'utf-8' charset
+ [RFC2044] and MAY support additional charsets provided that they
+ are registered with IANA [IANA-CS]. If the Printer object does
+ not support the client supplied charset value, the Printer object
+ MUST reject the request, set the "attributes-charset" to 'utf-8'
+ in the response, and return the 'client-error-charset-not-
+ supported' status code and any 'text' or 'name' attributes using
+ the 'utf-8' charset.
+
+ Since the client and IPP object MUST support UTF-8, returning any
+ text or name attributes in UTF-8 when the client requests a charset
+ that is not supported should allow the client to display the text or
+ name.
+
+ Since such an error is a client error, rather than a user error, the
+ client should check the status code first so that it can avoid
+ displaying any other returned 'text' and 'name' attributes that are
+ not in the charset requested.
+
+ Furthermore, [RFC2566] section 14.1.4.14 client-error-charset-not-
+ supported (0x040D) was clarified in November 1998 as follows:
+
+ For any operation, if the IPP Printer does not support the charset
+ supplied by the client in the "attributes-charset" operation
+ attribute, the Printer MUST reject the operation and return this
+ status and any 'text' or 'name' attributes using the 'utf-8'
+ charset (see Section 3.1.4.1).
+
+2.6.3 Natural Language Override (NLO)
+
+ The 'text' and 'name' attributes each have two forms. One has an
+ implicit natural language, and the other has an explicit natural
+ language. The 'textWithoutLanguage' and 'textWithoutLanguage' are
+ the two 'text' forms. The 'nameWithoutLanguage" and '
+ nameWithLanguage are the two 'name' forms. If a receiver (IPP object
+ or IPP client) supports an attribute with attribute syntax 'text', it
+ MUST support both forms in a request and a response. A sender (IPP
+ client or IPP object) MAY send either form for any such attribute.
+ When a sender sends a WithoutLanguage form, the implicit natural
+ language is specified in the "attributes-natural-language" operation
+ attribute which all senders MUST include in every request and
+ response.
+
+
+
+
+Hastings & Manros Informational [Page 48]
+
+RFC 2639 IPP/1.0: Implementer's Guide July 1999
+
+
+ When a sender sends a WithLanguage form, it MAY be different from the
+ implicit natural language supplied by the sender or it MAY be the
+ same. The receiver MUST treat either form equivalently.
+
+ There is an implementation decision for senders, whether to always
+ send the WithLanguage forms or use the WithoutLanguage form when the
+ attribute's natural language is the same as the request or response.
+ The former approach makes the sender implementation simpler. The
+ latter approach is more efficient on the wire and allows inter-
+ working with non-conforming receivers that fail to support the
+ WithLanguage forms. As each approach have advantages, the choice is
+ completely up to the implementer of the sender.
+
+ Furthermore, when a client receives a 'text' or 'name' job attribute
+ that it had previously supplied, that client MUST NOT expect to see
+ the attribute in the same form, i.e., in the same WithoutLanguage or
+ WithLanguage form as the client supplied when it created the job.
+ The IPP object is free to transform the attribute from the
+ WithLanguage form to the WithoutLanguage form and vice versa, as long
+ as the natural language is preserved. However, in order to meet this
+ latter requirement, it is usually simpler for the IPP object
+ implementation to store the natural language explicitly with the
+ attribute value, i.e., to store using an internal representation that
+ resembles the WithLanguage form.
+
+ The IPP Printer MUST copy the natural language of a job, i.e., the
+ value of the "attributes-natural-language" operation attribute
+ supplied by the client in the create operation, to the Job object as
+ a Job Description attribute, so that a client is able to query it.
+ In returning a Get-Job-Attributes response, the IPP object MAY return
+ one of three natural language values in the response's "attributes-
+ natural-language" operation attribute: (1) that requested by the
+ requester, (2) the natural language of the job, or (3) the configured
+ natural language of the IPP Printer, if the requested language is not
+ supported by the IPP Printer.
+
+ This "attributes-natural-language" Job Description attribute is
+ useful for an IPP object implementation that prints start sheets in
+ the language of the user who submitted the job. This same Job
+ Description attribute is useful to a multi-lingual operator who has
+ to communicate with different job submitters in different natural
+ languages. This same Job Description attribute is expected to be
+ used in the future to generate notification messages in the natural
+ language of the job submitter.
+
+ Early drafts of [RFC2566] contained a job-level natural language
+ override (NLO) for the Get-Jobs response. A job-level (NLO) is an
+ (unrequested) Job Attribute which then specified the implicit natural
+
+
+
+Hastings & Manros Informational [Page 49]
+
+RFC 2639 IPP/1.0: Implementer's Guide July 1999
+
+
+ language for any other WithoutLanguage job attributes returned in the
+ response for that job. Interoperability testing of early
+ implementations showed that no one was implementing the job-level NLO
+ in Get-Job responses. So the job-level NLO was eliminated from the
+ Get- Jobs response. This simplification makes all requests and
+ responses consistent in that the implicit natural language for any
+ WithoutLanguage 'text' or 'name' form is always supplied in the
+ request's or response's "attributes-natural-language" operation
+ attribute.
+
+2.7 The "queued-job-count" Printer Description attribute
+
+2.7.1 Why is "queued-job-count" RECOMMENDED?
+
+ The reason that "queued-job-count" is RECOMMENDED, is that some
+ clients look at that attribute alone when summarizing the status of a
+ list of printers, instead of doing a Get-Jobs to determine the number
+ of jobs in the queue. Implementations that fail to support the
+ "queued-job-count" will cause that client to display 0 jobs when
+ there are actually queued jobs.
+
+ We would have made it a REQUIRED Printer attribute, but some
+ implementations had already been completed before the issue was
+ raised, so making it a SHOULD was a compromise.
+
+2.7.2 Is "queued-job-count" a good measure of how busy a printer is?
+
+ The "queued-job-count" is not a good measure of how busy the printer
+ is when there are held jobs. A future registration could be to add a
+ "held-job-count" (or an "active-job-count") Printer Description
+ attribute if experience shows that such an attribute (combination) is
+ needed to quickly indicate how busy a printer really is.
+
+2.8 Sending empty attribute groups
+
+ The [RFC2566] and [RFC2565] specifications RECOMMEND that a sender
+ not send an empty attribute group in a request or a response.
+ However, they REQUIRE a receiver to accept an empty attribute group
+ as equivalent to the omission of that group. So a client SHOULD omit
+ the Job Template Attributes group entirely in a create operation that
+ is not supplying any Job Template attributes. Similarly, an IPP
+ object SHOULD omit an empty Unsupported Attributes group if there are
+ no unsupported attributes to be returned in a response.
+
+
+
+
+
+
+
+
+Hastings & Manros Informational [Page 50]
+
+RFC 2639 IPP/1.0: Implementer's Guide July 1999
+
+
+ The [RFC2565] specification REQUIRES a receiver to be able to receive
+ either an empty attribute group or an omitted attribute group and
+ treat them equivalently. The term "receiver" means an IPP object for
+ a request and a client for a response. The term "sender' means a
+ client for a request and an IPP object for a response.
+
+ There is an exception to the rule for Get-Jobs when there are no
+ attributes to be returned. [RFC2565] contains the following
+ paragraph:
+
+ The syntax allows an xxx-attributes-tag to be present when the
+ xxx-attribute-sequence that follows is empty. The syntax is
+ defined this way to allow for the response of Get-Jobs where no
+ attributes are returned for some job-objects. Although it is
+ RECOMMENDED that the sender not send an xxx-attributes-tag if
+ there are no attributes (except in the Get-Jobs response just
+ mentioned), the receiver MUST be able to decode such syntax.
+
+2.9 Returning unsupported attributes in Get-Xxxx responses
+
+ In the Get-Printer-Attributes, Get-Jobs, or Get-Job-Attributes
+ responses, the client cannot depend on getting unsupported attributes
+ returned in the Unsupported Attributes group that the client
+ requested, but are not supported by the IPP object. However, such
+ unsupported requested attributes will not be returned in the Job
+ Attributes or Printer Attributes group (since they are unsupported).
+ Furthermore, the IPP object is REQUIRED to return the 'successful-
+ ok-ignored-or-substituted-attributes' status code, so that the client
+ knows that not all that was requested has been returned.
+
+2.10 Returning job-state in Print-Job response
+
+ An IPP client submits a small job via Print-Job. By the time the IPP
+ printer/print server is putting together a response to the operation,
+ the job has finished printing and been removed as an object from the
+ print system. What should the job-state be in the response?
+
+ The Model suggests that the Printer return a response before it even
+ accepts the document content. The Job Object Attributes are returned
+ only if the IPP object returns one of the success status codes. Then
+ the job-state would always be "pending" or "pending-held".
+
+ This issue comes up for the implementation of an IPP Printer object
+ as a server that forwards jobs to devices that do not provide job
+ status back to the server. If the server is reasonably certain that
+ the job completed successfully, then it should return the job-state
+ as 'completed'. Also the server can keep the job in its "job
+ history" long after the job is no longer in the device. Then a user
+
+
+
+Hastings & Manros Informational [Page 51]
+
+RFC 2639 IPP/1.0: Implementer's Guide July 1999
+
+
+ could query the server and see that the job was in the 'completed'
+ state and completed as specified by the job's "time-at-completed"
+ time which would be the same as the server submitted the job to the
+ device.
+
+ An alternative is for the server to respond to the client before or
+ while sending the job to the device, instead of waiting until the
+ server has finished sending the job to the device. In this case, the
+ server can return the job's state as 'pending' with the 'job-
+ outgoing' value in the job's "job-state-reasons" attribute.
+
+ If the server doesn't know for sure whether the job completed
+ successfully (or at all), it could return the (out-of-band) 'unknown'
+ value.
+
+ On the other hand, if the server is able to query the device and/or
+ setup some sort of event notification that the device initiates when
+ the job makes state transitions, then the server can return the
+ current job state in the Print-Job response and in subsequent queries
+ because the server knows what the job state is in the device (or can
+ query the device).
+
+ All of these alternatives depend on implementation of the server and
+ the device.
+
+2.11 Flow controlling the data portion of a Print-Job request
+
+ A paused printer (or one that is stopped due to paper out or jam or
+ spool space full or buffer space full, may flow control the data of a
+ Print-Job operation (at the TCP/IP layer), so that the client is not
+ able to send all the document data. Consequently, the Printer will
+ not return a response until the condition is changed.
+
+ The Printer should not return a Print-Job response with an error code
+ in any of these conditions, since either the printer will be resumed
+ and/or the condition will be freed either by human intervention or as
+ jobs print.
+
+ In writing test scripts to test IPP Printers, the script must also be
+ written not to expect a response, if the printer has been paused,
+ until the printer is resumed, in order to work with all possible
+ implementations.
+
+2.12 Multi-valued attributes
+
+ What is the attribute syntax for a multi-valued attribute? Since
+ some attributes support values in more than one data type, such as
+ "media", "job-hold-until", and "job-sheets", IPP semantics associate
+
+
+
+Hastings & Manros Informational [Page 52]
+
+RFC 2639 IPP/1.0: Implementer's Guide July 1999
+
+
+ the attribute syntax with each value, not with the attribute as a
+ whole. The protocol associates the attribute syntax tag with each
+ value. Don't be fooled, just because the attribute syntax tag comes
+ before the attribute keyword. All attribute values after the first
+ have a zero length attribute keyword as the indication of a
+ subsequent value of the same attribute.
+
+2.13 Querying jobs with IPP that were submitted using other job
+ submission protocols
+
+ The following clarification was added to [RFC2566] section 8.5:
+
+ 8.5 Queries on jobs submitted using non-IPP protocols
+
+ If the device that an IPP Printer is representing is able to
+ accept jobs using other job submission protocols in addition to
+ IPP, it is RECOMMEND that such an implementation at least allow
+ such "foreign" jobs to be queried using Get-Jobs returning "job-
+ id" and "job-uri" as 'unknown'. Such an implementation NEED NOT
+ support all of the same IPP job attributes as for IPP jobs. The
+ IPP object returns the 'unknown' out-of-band value for any
+ requested attribute of a foreign job that is supported for IPP
+ jobs, but not for foreign jobs.
+
+ It is further RECOMMENDED, that the IPP Printer generate "job-id"
+ and "job-uri" values for such "foreign jobs", if possible, so that
+ they may be targets of other IPP operations, such as Get-Job-
+ Attributes and Cancel-Job. Such an implementation also needs to
+ deal with the problem of authentication of such foreign jobs. One
+ approach would be to treat all such foreign jobs as belonging to
+ users other than the user of the IPP client. Another approach
+ would be for the foreign job to belong to 'anonymous'. Only if
+ the IPP client has been authenticated as an operator or
+ administrator of the IPP Printer object, could the foreign jobs be
+ queried by an IPP request. Alternatively, if the security policy
+ is to allow users to query other users' jobs, then the foreign
+ jobs would also be visible to an end-user IPP client using Get-
+ Jobs and Get-Job-Attributes.
+
+ Thus IPP MAY be implemented as a "universal" protocol that provides
+ access to jobs submitted with any job submission protocol. As IPP
+ becomes widely implemented, providing a more universal access makes
+ sense.
+
+
+
+
+
+
+
+
+Hastings & Manros Informational [Page 53]
+
+RFC 2639 IPP/1.0: Implementer's Guide July 1999
+
+
+2.14 The 'none' value for empty sets
+
+ [RFC2566] states that the 'none' value should be used as the value of
+ a 1SetOf when the set is empty. In most cases, sets that are
+ potentially empty contain keywords so the keyword 'none' is used, but
+ for the 3 finishings attributes, the values are enums and thus the
+ empty set is represented by the enum 3. Currently there are no other
+ attributes with 1SetOf values which can be empty and can contain
+ values that are not keywords. This exception requires special code
+ and is a potential place for bugs. It would have been better if we
+ had chosen an out-of-band value, either "no-value" or some new value,
+ such as 'none'. Since we didn't, implementations have to deal with
+ the different representations of 'none', depending on the attribute
+ syntax.
+
+2.15 Get-Jobs, my-jobs='true', and 'requesting-user-name'?
+
+ In [RFC2566] section 3.2.6.1 'Get-Jobs Request', if the attribute '
+ my-jobs' is present and set to TRUE, MUST the 'requesting-user-name'
+ attribute be there to, and if it's not present what should the IPP
+ printer do?
+
+ [RFC2566] Section 8.3 describes the various cases of "requesting-
+ user-name" being present or not for any operation. If the client
+ does not supply a value for "requesting-user-name", the printer MUST
+ assume that the client is supplying some anonymous name, such as
+ "anonymous".
+
+2.16 The "multiple-document-handling" Job Template attribute and support
+ of multiple document jobs
+
+ ISSUE: IPP/1.0 is silent on which of the four effects an
+ implementation would perform if it supports Create-Job, but does not
+ support "multiple-document-handling".
+
+ A fix to IPP/1.0 would be to require implementing all four values of
+ "multiple-document-handling" if Create-Job is supported at all. Or
+ at least 'single-document-new-sheet' and 'separate-documents-
+ uncollated-copies'. In any case, an implementation that supports
+ Create-Job SHOULD also support "multiple-document-handling". Support
+ for all four values is RECOMMENDED, but at least the 'single-
+ document-new-sheet' and 'separate-documents-uncollated-copies'
+ values, along with the "multiple-document-handling-default"
+ indicating the default behavior and "multiple-document-handling-
+ supported" values. If an implementation spools the data, it should
+ also support the 'separate-documents-collated-copies' value as well.
+
+
+
+
+
+Hastings & Manros Informational [Page 54]
+
+RFC 2639 IPP/1.0: Implementer's Guide July 1999
+
+
+3 Encoding and Transport
+
+ This section discusses various aspects of IPP/1.0 Encoding and
+ Transport [RFC2565].
+
+ A server is not required to send a response until after it has
+ received the client.s entire request. Hence, a client must not
+ expect a response until after it has sent the entire request.
+ However, we recommend that the server return a response as soon as
+ possible if an error is detected while the client is still sending
+ the data, rather than waiting until all of the data is received.
+ Therefore, we also recommend that a client listen for an error
+ response that an IPP server MAY send before it receives all the data.
+ In this case a client, if chunking the data, can send a premature
+ zero-length chunk to end the request before sending all the data (and
+ so the client can keep the connection open for other requests, rather
+ than closing it). If the request is blocked for some reason, a client
+ MAY determine the reason by opening another connection to query the
+ server using Get-Printer-Attributes.
+
+ In the following sections, there are a tables of all HTTP headers
+ which describe their use in an IPP client or server. The following
+ is an explanation of each column in these tables.
+
+ - the .header. column contains the name of a header.
+ - the .request/client. column indicates whether a client sends the
+ header.
+ - the .request/ server. column indicates whether a server supports
+ the header when received.
+ - the .response/ server. column indicates whether a server sends
+ the header.
+ - the .response /client. column indicates whether a client
+ supports the header when received.
+ - the .values and conditions. column specifies the allowed header
+ values and the conditions for the header to be present in a
+ request/response.
+
+ The table for .request headers. does not have columns for responses,
+ and the table for .response headers. does not have columns for
+ requests.
+
+ The following is an explanation of the values in the .request/client.
+ and .response/ server. columns.
+
+ - must: the client or server MUST send the header,
+ - must-if: the client or server MUST send the header when the
+ condition described in the .values and conditions. column is
+ met,
+
+
+
+Hastings & Manros Informational [Page 55]
+
+RFC 2639 IPP/1.0: Implementer's Guide July 1999
+
+
+ - may: the client or server MAY send the header
+ - not: the client or server SHOULD NOT send the header. It is not
+ relevant to an IPP implementation.
+
+ The following is an explanation of the values in the
+ .response/client. and .request/ server. columns.
+
+ - must: the client or server MUST support the header,
+ - may: the client or server MAY support the header
+ - not: the client or server SHOULD NOT support the header. It is
+ not relevant to an IPP implementation.
+
+3.1 General Headers
+
+
+ The following is a table for the general headers.
+
+
+ General- Request Response Values and Conditions
+ Header
+
+ Client Server Server Client
+
+ Cache- must not must not .no-cache. only
+ Control
+
+ Connection must-if must must- must .close. only. Both
+ if client and server
+ SHOULD keep a
+ connection for the
+ duration of a sequence
+ of operations. The
+ client and server MUST
+ include this header
+ for the last operation
+ in such a sequence.
+
+ Date may may must may per RFC 1123 [RFC1123]
+ from RFC 2068
+ [RFC2068]
+
+ Pragma must not must not .no-cache. only
+
+ Transfer- must-if must must- must .chunked. only .
+ Encoding if Header MUST be present
+ if Content-Length is
+ absent.
+
+
+
+
+Hastings & Manros Informational [Page 56]
+
+RFC 2639 IPP/1.0: Implementer's Guide July 1999
+
+
+ Upgrade not not not not
+
+ Via not not not not
+
+3.2 Request Headers
+
+
+ The following is a table for the request headers.
+
+
+ Request-Header Client Server Request Values and Conditions
+
+ Accept may must .application/ipp. only. This
+ value is the default if the
+
+ Request-Header Client Server Request Values and Conditions
+
+ client omits it
+
+ Accept-Charset not not Charset information is within
+ the application/ipp entity
+
+ Accept-Encoding may must empty and per RFC 2068 [RFC2068]
+ and IANA registry for content-
+ codings
+
+ Accept-Language not not language information is within
+ the application/ipp entity
+
+ Authorization must-if must per RFC 2068. A client MUST send
+ this header when it receives a
+ 401 .Unauthorized. response and
+ does not receive a .Proxy-
+ Authenticate. header.
+
+ From not not per RFC 2068. Because RFC
+ recommends sending this header
+ only with the user.s approval, it
+ is not very useful
+
+ Host must must per RFC 2068
+
+ If-Match not not
+
+ If-Modified- not not
+ Since
+
+ If-None-Match not not
+
+
+
+Hastings & Manros Informational [Page 57]
+
+RFC 2639 IPP/1.0: Implementer's Guide July 1999
+
+
+ If-Range not not
+
+ If-Unmodified- not not
+ Since
+
+ Max-Forwards not not
+
+ Proxy- must-if not per RFC 2068. A client MUST send
+ Authorization this header when it receives a
+ 401 .Unauthorized. response and a
+ .Proxy-Authenticate. header.
+
+ Range not not
+
+ Referer not not
+
+ User-Agent not not
+
+
+3.3 Response Headers
+
+
+ The following is a table for the request headers.
+
+
+ Response- Server Client Response Values and Conditions
+ Header
+
+ Accept-Ranges not not
+
+ Age not not
+
+ Location must-if may per RFC 2068. When URI needs
+ redirection.
+
+ Proxy- not must per RFC 2068
+ Authenticate
+
+ Public may may per RFC 2068
+
+ Retry-After may may per RFC 2068
+
+ Server not not
+
+ Vary not not
+
+ Warning may may per RFC 2068
+
+
+
+
+Hastings & Manros Informational [Page 58]
+
+RFC 2639 IPP/1.0: Implementer's Guide July 1999
+
+
+ WWW- must-if must per RFC 2068. When a server needs to
+ Authenticate authenticate a client.
+
+3.4 Entity Headers
+
+
+ The following is a table for the entity headers.
+
+
+ Entity-Header Request Response Values and Conditions
+
+ Client Server Server Client
+
+ Allow not not not not
+
+ Content-Base not not not not
+
+ Content- may must must must per RFC 2068 and IANA
+ Encoding registry for content
+ codings.
+
+ Content- not not not not Application/ipp
+ Language handles language
+
+ Content- must-if must must-if must the length of the
+ Length message-body per RFC
+ 2068. Header MUST be
+ present if Transfer-
+
+ Entity-Header Request Response Values and Conditions
+
+ Client Server Server Client
+
+ Encoding is absent.
+
+ Content- not not not not
+ Location
+
+ Content-MD5 may may may may per RFC 2068
+
+ Content-Range not not not not
+
+ Content-Type must must must must .application/ipp.
+ only
+
+ ETag not not not not
+
+ Expires not not not not
+
+
+
+Hastings & Manros Informational [Page 59]
+
+RFC 2639 IPP/1.0: Implementer's Guide July 1999
+
+
+ Last-Modified not not not not
+
+
+3.5 Optional support for HTTP/1.0
+
+ IPP implementations consist of an HTTP layer and an IPP layer. In
+ the following discussion, the term "client" refers to the HTTP client
+ layer and the term "server" refers to the HTTP server layer. The
+ Encoding and Transport document [RFC2565] requires that HTTP 1.1 MUST
+ be supported by all clients and all servers. However, a client
+ and/or a server implementation may choose to also support HTTP 1.0.
+
+ - This option means that a server may choose to communicate with a
+ (non-conforming) client that only supports HTTP 1.0. In such cases
+ the server should not use any HTTP 1.1 specific parameters or
+ features and should respond using HTTP version number 1.0.
+
+ - This option also means that a client may choose to communicate with
+ a (non-conforming) server that only supports HTTP 1.0. In such
+ cases, if the server responds with an HTTP .unsupported version
+ number. to an HTTP 1.1 request, the client should retry using HTTP
+ version number 1.0.
+
+3.6 HTTP/1.1 Chunking
+
+3.6.1 Disabling IPP Server Response Chunking
+
+ Clients MUST anticipate that the HTTP/1.1 server may chunk responses
+ and MUST accept them in responses. However, a (non-conforming) HTTP
+ client that is unable to accept chunked responses may attempt to
+ request an HTTP 1.1 server not to use chunking in its response to an
+ operation by using the following HTTP header:
+
+ TE: identity
+
+ This mechanism should not be used by a server to disable a client
+ from chunking a request, since chunking of document data is an
+ important feature for clients to send long documents.
+
+3.6.2 Warning About the Support of Chunked Requests
+
+ This section describes some problems with the use of chunked requests
+ and HTTP/1.1 servers.
+
+ The HTTP/1.1 standard [HTTP] requires that conforming servers support
+ chunked requests for any method. However, in spite of this
+ requirement, some HTTP/1.1 implementations support chunked responses
+ in the GET method, but do not support chunked POST method requests.
+
+
+
+Hastings & Manros Informational [Page 60]
+
+RFC 2639 IPP/1.0: Implementer's Guide July 1999
+
+
+ Some HTTP/1.1 implementations that support CGI scripts [CGI] and/or
+ servlets [Servlet] require that the client supply a Content-Length.
+ These implementations might reject a chunked POST method and return a
+ 411 status code (Length Required), might attempt to buffer the
+ request and run out of room returning a 413 status code (Request
+ Entity Too Large), or might successfully accept the chunked request.
+
+ Because of this lack of conformance of HTTP servers to the HTTP/1.1
+ standard, the IPP standard [RFC2565] REQUIRES that a conforming IPP
+ Printer object implementation support chunked requests and that
+ conforming clients accept chunked responses. Therefore, IPP object
+ implementers are warned to seek HTTP server implementations that
+ support chunked POST requests in order to conform to the IPP standard
+ and/or use implementation techniques that support chunked POST
+ requests.
+
+4 References
+
+ [CGI] Coar, K. and D. Robinson, "The WWW Common Gateway Interface
+ Version 1.1 (CGI/1.1)", Work in Progress.
+
+ [HTTP] Fielding, R., Gettys,J., Mogul, J., Frystyk,, H., Masinter,
+ L., Leach, P. and T. Berners-Lee, "Hypertext Transfer
+ Protocol -- HTTP/1.1", RFC 2616, June 1999.
+
+ [RFC2569] Herriot, R., Hastings, T., Jacobs, N. and J. Martin,
+ "Mapping between LPD and IPP Protocols", RFC 2569, April
+ 1999.
+
+ [RFC2566] deBry, R., Hastings, T., Herriot, R., Isaacson, S. and P.
+ Powell, "Internet Printing Protocol/1.0: Model and
+ Semantics", RFC 2566, April 1999.
+
+ [RFC2565] Herriot, R., Butler, S., Moore, P. and R. Tuner, "Internet
+ Printing Protocol/1.0: Encoding and Transport", RFC 2565,
+ April 1999.
+
+ [RFC2568] Zilles, S., "Rationale for the Structure and Model and
+ Protocol for the Internet Printing Protocol", RFC 2568,
+ April 1999.
+
+ [RFC2567] Wright, D., "Design Goals for an Internet Printing
+ Protocol", RFC 2567, April 1999.
+
+ [RFC1123] Braden, S., "Requirements for Internet Hosts - Application
+ and Support", STD 3, RFC 1123, October 1989.
+
+
+
+
+
+Hastings & Manros Informational [Page 61]
+
+RFC 2639 IPP/1.0: Implementer's Guide July 1999
+
+
+ [RFC2026] Bradner, S., "The Internet Standards Process -- Revision
+ 3", BCP 9, RFC 2026, October 1996.
+
+ [RFC2068] Fielding, R., Gettys, J., Mogul, J., Frystyk, H. and T.
+ Berners-Lee, "Hypertext Transfer Protocol -- HTTP/1.1", RFC
+ 2068, January 1997.
+
+ [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate
+ Requirement Levels", BCP 14, RFC 2119, March 1997.
+
+ [RFC2396] Berners-Lee, T., Fielding, R. and L. Masinter, "Uniform
+ Resource Identifiers (URI): Generic Syntax", RFC 2396,
+ August 1998.
+
+ [Servlet] Servlet Specification Version 2.1
+ (http://java.sun.com/products/servlet/2.1/index.html).
+
+ [SSL] Netscape, The SSL Protocol, Version 3, (Text version 3.02),
+ November 1996.
+
+4.1 Authors' Addresses
+
+ Thomas N. Hastings
+ Xerox Corporation
+ 701 Aviation Blvd.
+ El Segundo, CA 90245
+
+ EMail: hastings@cp10.es.xerox.com
+
+
+ Carl-Uno Manros
+ Xerox Corporation
+ 701 Aviation Blvd.
+ El Segundo, CA 90245
+
+ EMail: manros@cp10.es.xerox.com
+
+5 Security Considerations
+
+ Security issues are discussed in sections 2.2, 2.3.1, and 8.5.
+
+6 Notices
+
+ The IETF takes no position regarding the validity or scope of any
+ intellectual property or other rights that might be claimed to
+ pertain to the implementation or use of the technology described in
+ this document or the extent to which any license under such rights
+ might or might not be available; neither does it represent that it
+
+
+
+Hastings & Manros Informational [Page 62]
+
+RFC 2639 IPP/1.0: Implementer's Guide July 1999
+
+
+ has made any effort to identify any such rights. Information on the
+ IETF's procedures with respect to rights in standards-track and
+ standards-related documentation can be found in BCP-11 [BCP-11].
+ Copies of claims of rights made available for publication and any
+ assurances of licenses to be made available, or the result of an
+ attempt made to obtain a general license or permission for the use of
+ such proprietary rights by implementers or users of this
+ specification can be obtained from the IETF Secretariat.
+
+ The IETF invites any interested party to bring to its attention any
+ copyrights, patents or patent applications, or other proprietary
+ rights which may cover technology that may be required to practice
+ this standard. Please address the information to the IETF Executive
+ Director.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Hastings & Manros Informational [Page 63]
+
+RFC 2639 IPP/1.0: Implementer's Guide July 1999
+
+
+Full Copyright Statement
+
+ Copyright (C) The Internet Society (1999). All Rights Reserved.
+
+ This document and translations of it may be copied and furnished to
+ others, and derivative works that comment on or otherwise explain it
+ or assist in its implementation may be prepared, copied, published
+ and distributed, in whole or in part, without restriction of any
+ kind, provided that the above copyright notice and this paragraph are
+ included on all such copies and derivative works. However, this
+ document itself may not be modified in any way, such as by removing
+ the copyright notice or references to the Internet Society or other
+ Internet organizations, except as needed for the purpose of
+ developing Internet standards in which case the procedures for
+ copyrights defined in the Internet Standards process must be
+ followed, or as required to translate it into languages other than
+ English.
+
+ The limited permissions granted above are perpetual and will not be
+ revoked by the Internet Society or its successors or assigns.
+
+ This document and the information contained herein is provided on an
+ "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING
+ TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+ HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+ MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+Acknowledgement
+
+ Funding for the RFC Editor function is currently provided by the
+ Internet Society.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Hastings & Manros Informational [Page 64]
+
diff --git a/systemv/Makefile b/systemv/Makefile
new file mode 100644
index 000000000..4877d7fb4
--- /dev/null
+++ b/systemv/Makefile
@@ -0,0 +1,151 @@
+#
+# "$Id$"
+#
+# System V commands makefile for the Common UNIX Printing System (CUPS).
+#
+# Copyright 1997-2000 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 lpoptions lppasswd lpstat
+OBJS = accept.o cancel.o lp.o lpadmin.o lpoptions.o lppasswd.o lpstat.o
+
+
+#
+# Make all targets...
+#
+
+all: $(TARGETS)
+
+
+#
+# Clean all object files...
+#
+
+clean:
+ $(RM) $(OBJS) $(TARGETS)
+
+
+#
+# Install all targets...
+#
+
+install:
+ -$(MKDIR) $(BINDIR)
+ -$(MKDIR) $(LIBDIR)
+ $(INSTALL_PROGRAM) accept lpadmin $(SBINDIR)
+ $(INSTALL_PROGRAM) cancel lp lpoptions lppasswd lpstat $(BINDIR)
+ $(CHMOD) u+s $(BINDIR)/lppasswd
+ $(RM) $(SBINDIR)/reject
+ $(LN) accept $(SBINDIR)/reject
+ $(RM) $(LIBDIR)/lpadmin
+ $(LN) ../sbin/lpadmin $(LIBDIR)
+ $(RM) $(LIBDIR)/accept
+ $(LN) ../sbin/accept $(LIBDIR)
+ $(RM) $(LIBDIR)/reject
+ $(LN) ../sbin/accept $(LIBDIR)/reject
+ $(RM) $(BINDIR)/disable
+ $(LN) ../sbin/accept $(BINDIR)/disable
+ $(RM) $(BINDIR)/enable
+ $(LN) ../sbin/accept $(BINDIR)/enable
+
+
+#
+# accept
+#
+
+accept: accept.o ../cups/$(LIBCUPS)
+ echo Linking $@...
+ $(CC) $(LDFLAGS) -o accept accept.o $(LIBS)
+
+accept.o: ../cups/cups.h
+
+
+#
+# cancel
+#
+
+cancel: cancel.o ../cups/$(LIBCUPS)
+ echo Linking $@...
+ $(CC) $(LDFLAGS) -o cancel cancel.o $(LIBS)
+
+cancel.o: ../cups/cups.h
+
+
+#
+# lp
+#
+
+lp: lp.o ../cups/$(LIBCUPS)
+ echo Linking $@...
+ $(CC) $(LDFLAGS) -o lp lp.o $(LIBS)
+
+lp.o: ../cups/cups.h
+
+
+#
+# lpadmin
+#
+
+lpadmin: lpadmin.o ../cups/$(LIBCUPS)
+ echo Linking $@...
+ $(CC) $(LDFLAGS) -o lpadmin lpadmin.o $(LIBZ) $(LIBS)
+
+lpadmin.o: ../cups/cups.h
+
+
+#
+# lpoptions
+#
+
+lpoptions: lpoptions.o ../cups/$(LIBCUPS)
+ echo Linking $@...
+ $(CC) $(LDFLAGS) -o lpoptions lpoptions.o $(LIBZ) $(LIBS)
+
+lpoptions.o: ../cups/cups.h
+
+
+#
+# lppasswd
+#
+
+lppasswd: lppasswd.o ../cups/$(LIBCUPS)
+ echo Linking $@...
+ $(CC) $(LDFLAGS) -o lppasswd lppasswd.o $(LIBZ) $(LIBS)
+
+lppasswd.o: ../cups/cups.h ../cups/md5.h ../cups/string.h ../config.h
+
+
+#
+# lpstat
+#
+
+lpstat: lpstat.o ../cups/$(LIBCUPS)
+ echo Linking $@...
+ $(CC) $(LDFLAGS) -o lpstat lpstat.o $(LIBS)
+
+lpstat.o: ../cups/cups.h
+
+
+$(OBJS): ../Makedefs
+
+#
+# End of "$Id$".
+#
diff --git a/systemv/accept.c b/systemv/accept.c
new file mode 100644
index 000000000..f59b29c9f
--- /dev/null
+++ b/systemv/accept.c
@@ -0,0 +1,293 @@
+/*
+ * "$Id$"
+ *
+ * "accept", "disable", "enable", and "reject" commands for the Common
+ * UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-2000 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 */
+ int cancel; /* Cancel jobs? */
+
+
+ /*
+ * See what operation we're supposed to do...
+ */
+
+ if ((command = strrchr(argv[0], '/')) != NULL)
+ command ++;
+ else
+ command = argv[0];
+
+ cancel = 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 'c' : /* Cancel jobs */
+ cancel = 1;
+ break;
+
+ case 'h' : /* Connect to host */
+ if (http != NULL)
+ httpClose(http);
+
+ if (argv[i][2] != '\0')
+ http = httpConnect(argv[i] + 2, ippPort());
+ else
+ {
+ i ++;
+ if (i >= argc)
+ {
+ fprintf(stderr, "%s: Expected server name after -h!\n",
+ command);
+ return (1);
+ }
+
+ 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], "%1023[^@]@%1023s", 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)
+ {
+ fputs(argv[0], stderr);
+ perror(": Unable to connect to server");
+ 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);
+
+ snprintf(uri, sizeof(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)
+ {
+ if (response->request.status.status_code > IPP_OK_CONFLICT)
+ {
+ fprintf(stderr, "%s: Operation failed: %s\n", command,
+ ippErrorString(cupsLastError()));
+ return (1);
+ }
+
+ ippDelete(response);
+ }
+ else
+ {
+ fprintf(stderr, "%s: Operation failed: %s\n", command,
+ ippErrorString(cupsLastError()));
+ return (1);
+ }
+
+ /*
+ * Cancel all jobs if requested...
+ */
+
+ if (cancel)
+ {
+ /*
+ * Build an IPP_PURGE_JOBS request, which requires the following
+ * attributes:
+ *
+ * attributes-charset
+ * attributes-natural-language
+ * printer-uri
+ */
+
+ request = ippNew();
+
+ request->request.op.operation_id = IPP_PURGE_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);
+
+ snprintf(uri, sizeof(uri), "ipp://%s:%d/printers/%s", hostname, ippPort(), printer);
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
+ "printer-uri", NULL, uri);
+
+ if ((response = cupsDoRequest(http, request, "/admin/")) != NULL)
+ {
+ if (response->request.status.status_code > IPP_OK_CONFLICT)
+ {
+ fprintf(stderr, "%s: Operation failed: %s\n", command,
+ ippErrorString(cupsLastError()));
+ return (1);
+ }
+
+ ippDelete(response);
+ }
+ else
+ {
+ fprintf(stderr, "%s: Operation failed: %s\n", command,
+ ippErrorString(cupsLastError()));
+ 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..89a8bfcfa
--- /dev/null
+++ b/systemv/cancel.c
@@ -0,0 +1,236 @@
+/*
+ * "$Id$"
+ *
+ * "cancel" command for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-2000 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;
+ http = NULL;
+
+ /*
+ * 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 */
+ if (http != NULL)
+ httpClose(http);
+
+ if (argv[i][2] != '\0')
+ http = httpConnect(argv[i] + 2, ippPort());
+ else
+ {
+ i ++;
+
+ if (i >= argc)
+ {
+ fputs("Error: need hostname after \'-h\' option!\n", stderr);
+ return (1);
+ }
+ else
+ 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], "%254[^-]-%d", name, &job_id);
+ if (job_id)
+ op = IPP_CANCEL_JOB;
+
+ if ((host = strchr(name, '@')) != NULL)
+ {
+ /*
+ * Reconnect to the named host...
+ */
+
+ if (http != NULL)
+ httpClose(http);
+
+ *host++ = '\0';
+
+ if ((http = httpConnect(host, ippPort())) == NULL)
+ {
+ perror("cancel: Unable to connect to server");
+ return (1);
+ }
+ }
+ }
+
+ /*
+ * Open a connection to the server...
+ */
+
+ if (http == NULL)
+ if ((http = httpConnect(cupsServer(), ippPort())) == NULL)
+ {
+ fputs("cancel: Unable to contact server!\n", stderr);
+ return (1);
+ }
+
+ /*
+ * 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)
+ {
+ snprintf(uri, sizeof(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);
+ }
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
+ "requesting-user-name", NULL, cupsUser());
+
+ /*
+ * 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 ||
+ response->request.status.status_code > IPP_OK_CONFLICT)
+ {
+ fprintf(stderr, "cancel: %s failed: %s\n",
+ op == IPP_PURGE_JOBS ? "purge-jobs" : "cancel-job",
+ response ? ippErrorString(response->request.status.status_code) :
+ ippErrorString(cupsLastError()));
+
+ if (response)
+ ippDelete(response);
+
+ return (1);
+ }
+
+ ippDelete(response);
+ }
+
+ return (0);
+}
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/systemv/lp.c b/systemv/lp.c
new file mode 100644
index 000000000..fa3d412e8
--- /dev/null
+++ b/systemv/lp.c
@@ -0,0 +1,381 @@
+/*
+ * "$Id$"
+ *
+ * "lp" command for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-2000 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.
+ * sighandler() - Signal catcher for when we print from stdin...
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <cups/cups.h>
+#include <cups/string.h>
+
+
+#ifndef WIN32
+# include <signal.h>
+
+
+/*
+ * Local functions.
+ */
+
+void sighandler(int);
+#endif /* !WIN32 */
+
+
+/*
+ * Globals...
+ */
+
+char tempfile[1024]; /* Temporary file for printing from stdin */
+
+
+/*
+ * '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, j; /* Looping vars */
+ int job_id; /* Job ID */
+ char *printer, /* Printer name */
+ *instance; /* Instance name */
+ 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_dests; /* Number of destinations */
+ cups_dest_t *dests, /* Destinations */
+ *dest; /* Selected destination */
+ int num_options; /* Number of options */
+ cups_option_t *options; /* Options */
+ int silent; /* Silent or verbose output? */
+ char buffer[8192]; /* Copy buffer */
+ FILE *temp; /* Temporary file pointer */
+ char server[1024]; /* Server name */
+#if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
+ struct sigaction action; /* Signal action */
+#endif /* HAVE_SIGACTION && !HAVE_SIGSET*/
+
+
+ silent = 0;
+ printer = NULL;
+ num_dests = 0;
+ dests = NULL;
+ 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')
+ printer = argv[i] + 2;
+ else
+ {
+ i ++;
+ printer = argv[i];
+ }
+
+ if ((instance = strrchr(printer, '/')) != NULL)
+ *instance++ = '\0';
+
+ if (num_dests == 0)
+ num_dests = cupsGetDests(&dests);
+
+ if ((dest = cupsGetDest(printer, instance, num_dests, dests)) != NULL)
+ {
+ for (j = 0; j < dest->num_options; j ++)
+ num_options = cupsAddOption(dest->options[j].name,
+ dest->options[j].value,
+ num_options, &options);
+ }
+ break;
+
+ case 'h' : /* Destination host */
+ if (argv[i][2] != '\0')
+ snprintf(server, sizeof(server), "CUPS_SERVER=%s", argv[i] + 2);
+ else
+ {
+ i ++;
+ snprintf(server, sizeof(server), "CUPS_SERVER=%s", argv[i]);
+ }
+ putenv(server);
+ 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 (printer == NULL)
+ {
+ if (num_dests == 0)
+ num_dests = cupsGetDests(&dests);
+
+ for (j = 0, dest = dests; j < num_dests; j ++, dest ++)
+ if (dest->is_default)
+ {
+ printer = dests[j].name;
+
+ for (j = 0; j < dest->num_options; j ++)
+ num_options = cupsAddOption(dest->options[j].name,
+ dest->options[j].value,
+ num_options, &options);
+ break;
+ }
+ }
+
+ if (printer == NULL)
+ {
+ fputs("lp: error - no default destination available.\n", stderr);
+ return (1);
+ }
+
+ num_files ++;
+ if (title)
+ job_id = cupsPrintFile(printer, argv[i], title, num_options, options);
+ else
+ {
+ char *filename;
+
+ if ((filename = strrchr(argv[i], '/')) != NULL)
+ filename ++;
+ else
+ filename = argv[i];
+
+ job_id = cupsPrintFile(printer, argv[i], filename, num_options, options);
+ }
+
+ if (job_id < 1)
+ {
+ fprintf(stderr, "lp: unable to print file \'%s\': %s\n",
+ argv[i], ippErrorString(cupsLastError()));
+ return (1);
+ }
+ else if (!silent)
+ fprintf(stderr, "request id is %s-%d (1 file(s))\n", printer, job_id);
+ }
+
+ /*
+ * See if we printed anything; if not, print from stdin...
+ */
+
+ if (num_files == 0)
+ {
+ if (printer == NULL)
+ {
+ if (num_dests == 0)
+ num_dests = cupsGetDests(&dests);
+
+ for (j = 0, dest = dests; j < num_dests; j ++, dest ++)
+ if (dest->is_default)
+ {
+ printer = dests[j].name;
+
+ for (j = 0; j < dest->num_options; j ++)
+ num_options = cupsAddOption(dest->options[j].name,
+ dest->options[j].value,
+ num_options, &options);
+ break;
+ }
+ }
+
+ if (printer == NULL)
+ {
+ fputs("lp: error - no default destination available.\n", stderr);
+ return (1);
+ }
+
+#ifndef WIN32
+# if defined(HAVE_SIGSET)
+ sigset(SIGHUP, sighandler);
+ sigset(SIGINT, sighandler);
+ sigset(SIGTERM, sighandler);
+# elif defined(HAVE_SIGACTION)
+ memset(&action, 0, sizeof(action));
+ action.sa_handler = sighandler;
+
+ sigaction(SIGHUP, &action, NULL);
+ sigaction(SIGINT, &action, NULL);
+ sigaction(SIGTERM, &action, NULL);
+# else
+ signal(SIGHUP, sighandler);
+ signal(SIGINT, sighandler);
+ signal(SIGTERM, sighandler);
+# endif
+#endif /* !WIN32 */
+
+ 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(printer, tempfile, title, num_options, options);
+ else
+ job_id = cupsPrintFile(printer, tempfile, "(stdin)", num_options, options);
+
+ unlink(tempfile);
+
+ if (job_id < 1)
+ {
+ fprintf(stderr, "lp: unable to print stdin: %s\n",
+ ippErrorString(cupsLastError()));
+ return (1);
+ }
+ else if (!silent)
+ fprintf(stderr, "request id is %s-%d (1 file(s))\n", printer, job_id);
+ }
+
+ return (0);
+}
+
+
+#ifndef WIN32
+/*
+ * 'sighandler()' - Signal catcher for when we print from stdin...
+ */
+
+void
+sighandler(int s) /* I - Signal number */
+{
+ /*
+ * Remove the temporary file we're using to print from stdin...
+ */
+
+ unlink(tempfile);
+
+ /*
+ * Exit...
+ */
+
+ exit(s);
+}
+#endif /* !WIN32 */
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/systemv/lpadmin.c b/systemv/lpadmin.c
new file mode 100644
index 000000000..681b6ee00
--- /dev/null
+++ b/systemv/lpadmin.c
@@ -0,0 +1,1331 @@
+/*
+ * "$Id$"
+ *
+ * "lpadmin" command for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-2000 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 */
+
+
+ http = NULL;
+ printer = NULL;
+
+ for (i = 1; i < argc; i ++)
+ if (argv[i][0] == '-')
+ switch (argv[i][1])
+ {
+ case 'c' : /* Add printer to class */
+ if (!http)
+ {
+ http = httpConnect(cupsServer(), ippPort());
+
+ if (http == NULL)
+ {
+ perror("lpadmin: Unable to connect to server");
+ return (1);
+ }
+ }
+
+ 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 (!http)
+ {
+ http = httpConnect(cupsServer(), ippPort());
+
+ if (http == NULL)
+ {
+ perror("lpadmin: Unable to connect to server");
+ return (1);
+ }
+ }
+
+ 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 */
+ if (http)
+ httpClose(http);
+
+ if (argv[i][2] != '\0')
+ http = httpConnect(argv[i] + 2, ippPort());
+ else
+ {
+ i ++;
+
+ if (i >= argc)
+ {
+ fputs("Error: need hostname after \'-h\' option!\n", stderr);
+ return (1);
+ }
+ else
+ 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 (!http)
+ {
+ http = httpConnect(cupsServer(), ippPort());
+
+ if (http == NULL)
+ {
+ perror("lpadmin: Unable to connect to server");
+ return (1);
+ }
+ }
+
+ 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 (!http)
+ {
+ http = httpConnect(cupsServer(), ippPort());
+
+ if (http == NULL)
+ {
+ perror("lpadmin: Unable to connect to server");
+ return (1);
+ }
+ }
+
+ 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 (!http)
+ {
+ http = httpConnect(cupsServer(), ippPort());
+
+ if (http == NULL)
+ {
+ perror("lpadmin: Unable to connect to server");
+ return (1);
+ }
+ }
+
+ 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])
+ snprintf(filename, sizeof(filename), CUPS_DATADIR "/model/%s", argv[i] + 2);
+ else
+ {
+ i ++;
+ snprintf(filename, sizeof(filename), CUPS_DATADIR "/model/%s", argv[i]);
+ }
+
+ set_printer_file(http, printer, filename);
+ break;
+
+ case 'p' : /* Add/modify a printer */
+ if (!http)
+ {
+ http = httpConnect(cupsServer(), ippPort());
+
+ if (http == NULL)
+ {
+ perror("lpadmin: Unable to connect to server");
+ return (1);
+ }
+ }
+
+ 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 (!http)
+ {
+ http = httpConnect(cupsServer(), ippPort());
+
+ if (http == NULL)
+ {
+ perror("lpadmin: Unable to connect to server");
+ return (1);
+ }
+ }
+
+ 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 (!http)
+ {
+ http = httpConnect(cupsServer(), ippPort());
+
+ if (http == NULL)
+ {
+ perror("lpadmin: Unable to connect to server");
+ return (1);
+ }
+ }
+
+ 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 (!http)
+ {
+ http = httpConnect(cupsServer(), ippPort());
+
+ if (http == NULL)
+ {
+ perror("lpadmin: Unable to connect to server");
+ return (1);
+ }
+ }
+
+ 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 (!http)
+ {
+ http = httpConnect(cupsServer(), ippPort());
+
+ if (http == NULL)
+ {
+ perror("lpadmin: Unable to connect to server");
+ return (1);
+ }
+ }
+
+ 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 (!http)
+ {
+ http = httpConnect(cupsServer(), ippPort());
+
+ if (http == NULL)
+ {
+ perror("lpadmin: Unable to connect to server");
+ return (1);
+ }
+ }
+
+ 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 (!http)
+ {
+ http = httpConnect(cupsServer(), ippPort());
+
+ if (http == NULL)
+ {
+ perror("lpadmin: Unable to connect to server");
+ return (1);
+ }
+ }
+
+ 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("");
+ }
+
+ if (http)
+ 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
+ */
+
+ snprintf(uri, sizeof(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, "/");
+
+ /*
+ * 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...
+ */
+
+ snprintf(uri, sizeof(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)
+ fprintf(stderr, "lpadmin: add-class failed: %s\n",
+ ippErrorString(cupsLastError()));
+ else
+ {
+ if (response->request.status.status_code > IPP_OK_CONFLICT)
+ fprintf(stderr, "lpadmin: add-class failed: %s\n",
+ ippErrorString(response->request.status.status_code));
+
+ 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
+ */
+
+ snprintf(uri, sizeof(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)
+ fprintf(stderr, "lpadmin: set-default failed: %s\n",
+ ippErrorString(cupsLastError()));
+ else
+ {
+ if (response->request.status.status_code > IPP_OK_CONFLICT)
+ fprintf(stderr, "lpadmin: set-default failed: %s\n",
+ ippErrorString(response->request.status.status_code));
+
+ 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
+ */
+
+ snprintf(uri, sizeof(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)
+ fprintf(stderr, "lpadmin: delete-printer failed: %s\n",
+ ippErrorString(cupsLastError()));
+ else
+ {
+ if (response->request.status.status_code > IPP_OK_CONFLICT)
+ fprintf(stderr, "lpadmin: delete-printer failed: %s\n",
+ ippErrorString(response->request.status.status_code));
+
+ 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
+ */
+
+ snprintf(uri, sizeof(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)
+ fprintf(stderr, "lpadmin: add/delete-class failed: %s\n",
+ ippErrorString(cupsLastError()));
+ else
+ {
+ if (response->request.status.status_code > IPP_OK_CONFLICT)
+ fprintf(stderr, "lpadmin: add/delete-class failed: %s\n",
+ ippErrorString(response->request.status.status_code));
+
+ 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
+ */
+
+ snprintf(uri, sizeof(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)
+ fprintf(stderr, "lpadmin: add-printer failed: %s\n",
+ ippErrorString(cupsLastError()));
+ else
+ {
+ if (response->request.status.status_code > IPP_OK_CONFLICT)
+ fprintf(stderr, "lpadmin: add-printer failed: %s\n",
+ ippErrorString(response->request.status.status_code));
+
+ 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
+ */
+
+ snprintf(uri, sizeof(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...
+ */
+
+ snprintf(uri, sizeof(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)
+ fprintf(stderr, "lpadmin: add-printer failed: %s\n",
+ ippErrorString(cupsLastError()));
+ else
+ {
+ if (response->request.status.status_code > IPP_OK_CONFLICT)
+ fprintf(stderr, "lpadmin: add-printer failed: %s\n",
+ ippErrorString(response->request.status.status_code));
+
+ 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
+ */
+
+ snprintf(uri, sizeof(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)
+ fprintf(stderr, "lpadmin: add-printer failed: %s\n",
+ ippErrorString(cupsLastError()));
+ else
+ {
+ if (response->request.status.status_code > IPP_OK_CONFLICT)
+ fprintf(stderr, "lpadmin: add-printer failed: %s\n",
+ ippErrorString(response->request.status.status_code));
+
+ 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
+ */
+
+ snprintf(uri, sizeof(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)
+ fprintf(stderr, "lpadmin: add-printer failed: %s\n",
+ ippErrorString(cupsLastError()));
+ else
+ {
+ if (response->request.status.status_code > IPP_OK_CONFLICT)
+ fprintf(stderr, "lpadmin: add-printer failed: %s\n",
+ ippErrorString(response->request.status.status_code));
+
+ 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
+ */
+
+ snprintf(uri, sizeof(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)
+ fprintf(stderr, "lpadmin: add-printer failed: %s\n",
+ ippErrorString(cupsLastError()));
+ else
+ {
+ if (response->request.status.status_code > IPP_OK_CONFLICT)
+ fprintf(stderr, "lpadmin: add-printer failed: %s\n",
+ ippErrorString(response->request.status.status_code));
+
+ ippDelete(response);
+ }
+}
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/systemv/lpoptions.c b/systemv/lpoptions.c
new file mode 100644
index 000000000..eb0de52d3
--- /dev/null
+++ b/systemv/lpoptions.c
@@ -0,0 +1,294 @@
+/*
+ * "$Id$"
+ *
+ * Printer option program for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-2000 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.
+ * usage() - Show program usage and exit.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include <cups/cups.h>
+#include <stdlib.h>
+
+
+/*
+ * Local functions...
+ */
+
+void usage(void);
+
+
+/*
+ * 'main()' - Main entry.
+ */
+
+int /* O - Exit status */
+main(int argc, /* I - Number of command-line arguments */
+ char *argv[]) /* I - Command-line arguments */
+{
+ int i, j; /* Looping vars */
+ char server[1024]; /* Print server */
+ int num_options; /* Number of options */
+ cups_option_t *options; /* Options */
+ int num_dests; /* Number of destinations */
+ cups_dest_t *dests; /* Destinations */
+ cups_dest_t *dest; /* Current destination */
+ char *printer, /* Printer name */
+ *instance; /* Instance name */
+
+
+ /*
+ * Loop through the command-line arguments...
+ */
+
+ snprintf(server, sizeof(server), "CUPS_SERVER=%s", cupsServer());
+ putenv(server);
+
+ dest = NULL;
+ num_dests = 0;
+ dests = NULL;
+ num_options = 0;
+ options = NULL;
+
+ for (i = 1; i < argc; i ++)
+ if (argv[i][0] == '-')
+ {
+ switch (argv[i][1])
+ {
+ case 'h' : /* -h server */
+ if (argv[i][2])
+ snprintf(server, sizeof(server), "CUPS_SERVER=%s", argv[i] + 2);
+ else
+ {
+ i ++;
+ if (i >= argc)
+ usage();
+
+ snprintf(server, sizeof(server), "CUPS_SERVER=%s", argv[i]);
+ }
+
+ putenv(server);
+ break;
+
+ case 'd' : /* -d printer */
+ if (argv[i][2])
+ printer = argv[i] + 2;
+ else
+ {
+ i ++;
+ if (i >= argc)
+ usage();
+
+ printer = argv[i];
+ }
+
+ if ((instance = strrchr(printer, '/')) != NULL)
+ *instance++ = '\0';
+
+ if (num_dests == 0)
+ num_dests = cupsGetDests(&dests);
+
+ if ((dest = cupsGetDest(printer, instance, num_dests, dests)) == NULL)
+ {
+ fputs("lpoptions: Unknown printer or class!\n", stderr);
+ return (1);
+ }
+
+ /*
+ * Set the default destination...
+ */
+
+ for (j = 0; j < num_dests; j ++)
+ dests[j].is_default = 0;
+
+ dest->is_default = 1;
+
+ cupsSetDests(num_dests, dests);
+ break;
+
+ case 'p' : /* -p printer */
+ if (argv[i][2])
+ printer = argv[i] + 2;
+ else
+ {
+ i ++;
+ if (i >= argc)
+ usage();
+
+ printer = argv[i];
+ }
+
+ if ((instance = strrchr(printer, '/')) != NULL)
+ *instance++ = '\0';
+
+ if (num_dests == 0)
+ num_dests = cupsGetDests(&dests);
+
+ if ((dest = cupsGetDest(printer, instance, num_dests, dests)) == NULL)
+ {
+ num_dests = cupsAddDest(printer, instance, num_dests, &dests);
+ dest = cupsGetDest(printer, instance, num_dests, dests);
+
+ if (dest == NULL)
+ {
+ perror("lpoptions: Unable to add printer or instance");
+ return (1);
+ }
+ }
+ break;
+
+ case 'o' : /* -o option[=value] */
+ if (argv[i][2])
+ num_options = cupsParseOptions(argv[i] + 2, num_options, &options);
+ else
+ {
+ i ++;
+ if (i >= argc)
+ usage();
+
+ num_options = cupsParseOptions(argv[i], num_options, &options);
+ }
+ break;
+
+ case 'x' : /* -x printer */
+ if (argv[i][2])
+ printer = argv[i] + 2;
+ else
+ {
+ i ++;
+ if (i >= argc)
+ usage();
+
+ printer = argv[i];
+ }
+
+ if ((instance = strrchr(printer, '/')) != NULL)
+ *instance++ = '\0';
+
+ if (num_dests == 0)
+ num_dests = cupsGetDests(&dests);
+
+ if ((dest = cupsGetDest(printer, instance, num_dests, dests)) == NULL)
+ {
+ if (instance)
+ fprintf(stderr, "lpoptions: No such printer/instance: %s/%s\n",
+ printer, instance);
+ else
+ fprintf(stderr, "lpoptions: No such printer: %s\n", printer);
+
+ return (1);
+ }
+
+ cupsFreeOptions(dest->num_options, dest->options);
+ num_dests --;
+
+ j = dest - dests;
+ if (j < num_dests)
+ memcpy(dest, dest + 1, (num_dests - j) * sizeof(cups_dest_t));
+
+ cupsSetDests(num_dests, dests);
+ dest = NULL;
+ break;
+
+ default :
+ usage();
+ }
+ }
+ else
+ usage();
+
+ if (num_dests == 0)
+ num_dests = cupsGetDests(&dests);
+
+ if (dest == NULL)
+ for (i = 0; i < num_dests; i ++)
+ if (dests[i].is_default)
+ {
+ dest = dests + i;
+ break;
+ }
+
+ if (dest == NULL)
+ return (0);
+
+ if (num_options > 0)
+ {
+ /*
+ * Set printer options...
+ */
+
+ cupsFreeOptions(dest->num_options, dest->options);
+
+ dest->num_options = num_options;
+ dest->options = options;
+
+ cupsSetDests(num_dests, dests);
+ }
+ else
+ {
+ num_options = dest->num_options;
+ options = dest->options;
+
+ for (i = 0; i < num_options; i ++)
+ {
+ if (i)
+ putchar(' ');
+
+ if (!options[i].value[0])
+ printf("%s", options[i].name);
+ else if (strchr(options[i].value, ' ') != NULL ||
+ strchr(options[i].value, '\t') != NULL)
+ printf("%s=\'%s\'", options[i].name, options[i].value);
+ else
+ printf("%s=%s", options[i].name, options[i].value);
+ }
+
+ putchar('\n');
+ }
+
+ return (0);
+}
+
+
+/*
+ * 'usage()' - Show program usage and exit.
+ */
+
+void
+usage(void)
+{
+ puts("Usage: lpoptions -d printer");
+ puts(" lpoptions -p printer -o option[=value] ...");
+ puts(" lpoptions -x printer");
+
+ exit(1);
+}
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/systemv/lppasswd.c b/systemv/lppasswd.c
new file mode 100644
index 000000000..93811f343
--- /dev/null
+++ b/systemv/lppasswd.c
@@ -0,0 +1,449 @@
+/*
+ * "$Id$"
+ *
+ * MD5 password program for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-2000 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() - Add, change, or delete passwords from the MD5 password file.
+ * md5_passwd() - Compute the MD5 sum of the username:group:password.
+ * usage() - Show program usage.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <grp.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <cups/cups.h>
+#include <cups/md5.h>
+#include <cups/string.h>
+
+
+/*
+ * Operations...
+ */
+
+#define ADD 0
+#define CHANGE 1
+#define DELETE 2
+
+
+/*
+ * Local functions...
+ */
+
+char *md5_passwd(const char *username, const char *groupname,
+ const char *passwd, char *md5);
+void usage(FILE *fp);
+
+
+/*
+ * 'main()' - Add, change, or delete passwords from the MD5 password file.
+ */
+
+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 pointer */
+ const char *username; /* Pointer to username */
+ const char *groupname; /* Pointer to group name */
+ int op; /* Operation (add, change, delete) */
+ struct group *group; /* System group */
+ const char *passwd; /* Password string */
+ FILE *infile, /* Input file */
+ *outfile; /* Output file */
+ char line[256], /* Line from file */
+ userline[17], /* User from line */
+ groupline[17], /* Group from line */
+ md5line[33], /* MD5-sum from line */
+ md5new[33]; /* New MD5 sum */
+
+
+ /*
+ * Find the default system group: "sys", "system", or "root"...
+ */
+
+ group = getgrnam("sys");
+ endgrent();
+
+ if (group != NULL)
+ groupname = "sys";
+ else
+ {
+ group = getgrnam("system");
+ endgrent();
+
+ if (group != NULL)
+ groupname = "system";
+ else
+ {
+ group = getgrnam("root");
+ endgrent();
+
+ if (group != NULL)
+ groupname = "root";
+ else
+ groupname = "unknown";
+ }
+ }
+
+ username = NULL;
+ op = CHANGE;
+
+ /*
+ * Parse command-line options...
+ */
+
+ for (i = 1; i < argc; i ++)
+ if (argv[i][0] == '-')
+ for (opt = argv[i] + 1; *opt; opt ++)
+ switch (*opt)
+ {
+ case 'a' : /* Add */
+ op = ADD;
+ break;
+ case 'x' : /* Delete */
+ op = DELETE;
+ break;
+ case 'g' : /* Group */
+ i ++;
+ if (i >= argc)
+ usage(stderr);
+
+ groupname = argv[i];
+ break;
+ case 'h' : /* Help */
+ usage(stdout);
+ break;
+ default : /* Bad option */
+ usage(stderr);
+ break;
+ }
+ else if (!username)
+ username = argv[i];
+ else
+ usage(stderr);
+
+ /*
+ * See if we are trying to add or delete a password when we aren't logged in
+ * as root...
+ */
+
+ if (getuid() && (op != CHANGE || username))
+ {
+ fputs("lppasswd: Only root can add or delete passwords!\n", stderr);
+ return (1);
+ }
+
+ /*
+ * Fill in missing info...
+ */
+
+ if (!username)
+ username = cupsUser();
+
+ /*
+ * Try locking the password file...
+ */
+
+ if (access(CUPS_SERVERROOT "/passwd.old", 0) == 0)
+ {
+ fputs("lppasswd: Password file busy!\n", stderr);
+ return (1);
+ }
+
+ if (rename(CUPS_SERVERROOT "/passwd.md5", CUPS_SERVERROOT "/passwd.old"))
+ {
+ perror("lppasswd: Unable to rename password file");
+ return (1);
+ }
+
+ /*
+ * Open the existing password file and create a new one...
+ */
+
+ infile = fopen(CUPS_SERVERROOT "/passwd.old", "r");
+ if (infile == NULL && op != ADD)
+ {
+ fputs("lppasswd: No password file to add to or delete from!\n", stderr);
+ return (1);
+ }
+
+ if ((outfile = fopen(CUPS_SERVERROOT "/passwd.md5", "w")) == NULL)
+ {
+ perror("lppasswd: Unable to create password file");
+ rename(CUPS_SERVERROOT "/passwd.old", CUPS_SERVERROOT "/passwd.md5");
+ return (1);
+ }
+
+ fchmod(fileno(outfile), 0400);
+
+ /*
+ * Read lines from the password file; the format is:
+ *
+ * username:group:MD5-sum
+ */
+
+ if (infile)
+ {
+ while (fgets(line, sizeof(line), infile) != NULL)
+ {
+ if (sscanf(line, "%16[^:]:%16[^:]:%32s", userline, groupline, md5line) != 3)
+ continue;
+
+ if (strcmp(username, userline) == 0 &&
+ strcmp(groupname, groupline) == 0)
+ break;
+
+ fputs(line, outfile);
+ }
+
+ while (fgets(line, sizeof(line), infile) != NULL)
+ fputs(line, outfile);
+ }
+ else
+ {
+ userline[0] = '\0';
+ groupline[0] = '\0';
+ md5line[0] = '\0';
+ }
+
+ if (op == CHANGE &&
+ (strcmp(username, userline) != 0 ||
+ strcmp(groupname, groupline) != 0))
+ fprintf(stderr, "lppasswd: user \"%s\" and group \"%s\" do not exist.\n",
+ username, groupname);
+ else if (op != DELETE)
+ {
+ if (op == CHANGE && getuid())
+ {
+ if ((passwd = cupsGetPassword("Enter old password:")) == NULL)
+ {
+ /*
+ * Close the files and remove the old password file...
+ */
+
+ fclose(infile);
+ fclose(outfile);
+
+ unlink(CUPS_SERVERROOT "/passwd.old");
+
+ return (0);
+ }
+
+ if (strcmp(md5_passwd(username, groupname, passwd, md5new), md5line) != 0)
+ {
+ fputs("lppasswd: Sorry, password doesn't match!\n", stderr);
+
+ /*
+ * Close the files and remove the old password file...
+ */
+
+ fclose(infile);
+ fclose(outfile);
+
+ rename(CUPS_SERVERROOT "/passwd.old", CUPS_SERVERROOT "/passwd.md5");
+
+ return (1);
+ }
+ }
+
+ if ((passwd = cupsGetPassword("Enter password:")) == NULL)
+ {
+ /*
+ * Close the files and remove the old password file...
+ */
+
+ fclose(infile);
+ fclose(outfile);
+
+ rename(CUPS_SERVERROOT "/passwd.old", CUPS_SERVERROOT "/passwd.md5");
+
+ return (0);
+ }
+
+ strcpy(line, passwd);
+
+ if ((passwd = cupsGetPassword("Enter password again:")) == NULL)
+ {
+ /*
+ * Close the files and remove the old password file...
+ */
+
+ fclose(infile);
+ fclose(outfile);
+
+ rename(CUPS_SERVERROOT "/passwd.old", CUPS_SERVERROOT "/passwd.md5");
+
+ return (0);
+ }
+
+ if (strcmp(passwd, line) != 0)
+ {
+ fputs("lppasswd: Sorry, passwords don't match!\n", stderr);
+
+ /*
+ * Close the files and remove the old password file...
+ */
+
+ fclose(infile);
+ fclose(outfile);
+
+ rename(CUPS_SERVERROOT "/passwd.old", CUPS_SERVERROOT "/passwd.md5");
+
+ return (1);
+ }
+
+ /*
+ * Check that the password contains at least one letter and number.
+ */
+
+ for (passwd = line; *passwd; passwd ++)
+ if (isdigit(*passwd))
+ break;
+
+ if (*passwd)
+ for (passwd = line; *passwd; passwd ++)
+ if (isalpha(*passwd))
+ break;
+
+ /*
+ * Only allow passwords that are at least 6 chars, have a letter and
+ * a number, and don't contain the username.
+ */
+
+ if (strlen(line) < 6 || strstr(line, username) != NULL || !*passwd)
+ {
+ fputs("lppasswd: Sorry, password must be at least 6 characters long, cannot contain\n"
+ " your username, and must contain at least one letter and number.\n", stderr);
+
+ /*
+ * Close the files and remove the old password file...
+ */
+
+ fclose(infile);
+ fclose(outfile);
+
+ rename(CUPS_SERVERROOT "/passwd.old", CUPS_SERVERROOT "/passwd.md5");
+
+ return (1);
+ }
+
+ fprintf(outfile, "%s:%s:%s\n", username, groupname,
+ md5_passwd(username, groupname, passwd, md5new));
+ }
+
+ /*
+ * Close the files and remove the old password file...
+ */
+
+ fclose(infile);
+ fclose(outfile);
+
+ unlink(CUPS_SERVERROOT "/passwd.old");
+
+ return (0);
+}
+
+
+/*
+ * 'md5_passwd()' - Compute the MD5 sum of the username:group:password.
+ */
+
+char * /* O - MD5 sum */
+md5_passwd(const char *username, /* I - User name */
+ const char *groupname, /* I - Group name */
+ const char *passwd, /* I - Password string */
+ char *md5) /* O - MD5 string */
+{
+ int i; /* Looping var */
+ md5_state_t state; /* MD5 state info */
+ md5_byte_t sum[16], /* Sum data */
+ *sumptr; /* Pointer into sum */
+ char *md5ptr; /* Pointer into MD5 string */
+ char line[256]; /* Line to sum */
+ static char *hex = "0123456789abcdef";
+ /* Hex digits */
+
+
+ /*
+ * Compute the MD5 sum of the user name, group name, and password.
+ */
+
+ sprintf(line, "%s:%s:%s", username, groupname, passwd);
+ md5_init(&state);
+ md5_append(&state, (md5_byte_t *)line, strlen(line));
+ md5_finish(&state, sum);
+
+ /*
+ * Convert the MD5 sum to hexadecimal...
+ */
+
+ for (i = 0, sumptr = sum, md5ptr = md5; i < 16; i ++, sumptr ++)
+ {
+ *md5ptr++ = hex[*sumptr >> 4];
+ *md5ptr++ = hex[*sumptr & 15];
+ }
+
+ *md5ptr = '\0';
+
+ /*
+ * Return the sum...
+ */
+
+ return (md5);
+}
+
+
+/*
+ * 'usage()' - Show program usage.
+ */
+
+void
+usage(FILE *fp) /* I - File to send usage to */
+{
+ if (getuid())
+ {
+ fputs("Usage: lppasswd [-g groupname]\n", fp);
+ }
+ else
+ {
+ fputs("Usage: lppasswd [-g groupname] [username]\n", fp);
+ fputs(" lppasswd [-g groupname] -a [username]\n", fp);
+ fputs(" lppasswd [-g groupname] -x [username]\n", fp);
+ }
+
+ exit(1);
+}
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/systemv/lpstat.c b/systemv/lpstat.c
new file mode 100644
index 000000000..d0c1a2d08
--- /dev/null
+++ b/systemv/lpstat.c
@@ -0,0 +1,1494 @@
+/*
+ * "$Id$"
+ *
+ * "lpstat" command for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-2000 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 *, int, cups_dest_t *);
+static void show_classes(http_t *, const char *);
+static void show_default(int, cups_dest_t *);
+static void show_devices(http_t *, const char *, int, cups_dest_t *);
+static void show_jobs(http_t *, const char *, const char *);
+static void show_printers(http_t *, const char *, int, cups_dest_t *);
+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 */
+ char server[1024]; /* CUPS_SERVER environment variable */
+ int num_dests; /* Number of user destinations */
+ cups_dest_t *dests; /* User destinations */
+
+
+ http = NULL;
+ num_dests = 0;
+ dests = NULL;
+
+ for (i = 1; i < argc; i ++)
+ if (argv[i][0] == '-')
+ switch (argv[i][1])
+ {
+ case 'a' : /* Show acceptance status */
+ if (!http)
+ {
+ http = httpConnect(cupsServer(), ippPort());
+
+ if (http == NULL)
+ {
+ perror("lpstat: Unable to connect to server");
+ return (1);
+ }
+ }
+
+ if (num_dests == 0)
+ num_dests = cupsGetDests(&dests);
+
+ if (argv[i][2] != '\0')
+ show_accepting(http, argv[i] + 2, num_dests, dests);
+ else if ((i + 1) < argc && argv[i + 1][0] != '-')
+ {
+ i ++;
+ show_accepting(http, argv[i], num_dests, dests);
+ }
+ else
+ show_accepting(http, NULL, num_dests, dests);
+ break;
+
+ case 'c' : /* Show classes and members */
+ if (!http)
+ {
+ http = httpConnect(cupsServer(), ippPort());
+
+ if (http == NULL)
+ {
+ perror("lpstat: Unable to connect to server");
+ return (1);
+ }
+ }
+
+ 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 */
+ if (num_dests == 0)
+ num_dests = cupsGetDests(&dests);
+
+ show_default(num_dests, dests);
+ break;
+
+ case 'h' : /* Connect to host */
+ if (http)
+ httpClose(http);
+
+ if (argv[i][2] != '\0')
+ {
+ http = httpConnect(argv[i] + 2, ippPort());
+ snprintf(server, sizeof(server), "CUPS_SERVER=%s", argv[i] + 2);
+ }
+ else
+ {
+ i ++;
+
+ if (i >= argc)
+ {
+ fputs("Error: need hostname after \'-h\' option!\n", stderr);
+ return (1);
+ }
+
+ http = httpConnect(argv[i], ippPort());
+ snprintf(server, sizeof(server), "CUPS_SERVER=%s", argv[i] + 2);
+ }
+
+ putenv(server);
+ if (http == NULL)
+ {
+ perror("lpstat: Unable to connect to server");
+ return (1);
+ }
+ break;
+
+ case 'o' : /* Show jobs by destination */
+ if (!http)
+ {
+ http = httpConnect(cupsServer(), ippPort());
+
+ if (http == NULL)
+ {
+ perror("lpstat: Unable to connect to server");
+ return (1);
+ }
+ }
+
+ 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 (!http)
+ {
+ http = httpConnect(cupsServer(), ippPort());
+
+ if (http == NULL)
+ {
+ perror("lpstat: Unable to connect to server");
+ return (1);
+ }
+ }
+
+ if (num_dests == 0)
+ num_dests = cupsGetDests(&dests);
+
+ if (argv[i][2] != '\0')
+ show_printers(http, argv[i] + 2, num_dests, dests);
+ else if ((i + 1) < argc && argv[i + 1][0] != '-')
+ {
+ i ++;
+ show_printers(http, argv[i], num_dests, dests);
+ }
+ else
+ show_printers(http, NULL, num_dests, dests);
+ break;
+
+ case 'r' : /* Show scheduler status */
+ if (!http)
+ {
+ http = httpConnect(cupsServer(), ippPort());
+
+ if (http == NULL)
+ {
+ perror("lpstat: Unable to connect to server");
+ return (1);
+ }
+ }
+
+ show_scheduler(http);
+ break;
+
+ case 's' : /* Show summary */
+ if (!http)
+ {
+ http = httpConnect(cupsServer(), ippPort());
+
+ if (http == NULL)
+ {
+ perror("lpstat: Unable to connect to server");
+ return (1);
+ }
+ }
+
+ if (num_dests == 0)
+ num_dests = cupsGetDests(&dests);
+
+ show_default(num_dests, dests);
+ show_classes(http, NULL);
+ show_devices(http, NULL, num_dests, dests);
+ break;
+
+ case 't' : /* Show all info */
+ if (!http)
+ {
+ http = httpConnect(cupsServer(), ippPort());
+
+ if (http == NULL)
+ {
+ perror("lpstat: Unable to connect to server");
+ return (1);
+ }
+ }
+
+ if (num_dests == 0)
+ num_dests = cupsGetDests(&dests);
+
+ show_scheduler(http);
+ show_default(num_dests, dests);
+ show_classes(http, NULL);
+ show_devices(http, NULL, num_dests, dests);
+ show_accepting(http, NULL, num_dests, dests);
+ show_printers(http, NULL, num_dests, dests);
+ show_jobs(http, NULL, NULL);
+ break;
+
+ case 'u' : /* Show jobs by user */
+ if (!http)
+ {
+ http = httpConnect(cupsServer(), ippPort());
+
+ if (http == NULL)
+ {
+ perror("lpstat: Unable to connect to server");
+ return (1);
+ }
+ }
+
+ 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 (!http)
+ {
+ http = httpConnect(cupsServer(), ippPort());
+
+ if (http == NULL)
+ {
+ perror("lpstat: Unable to connect to server");
+ return (1);
+ }
+ }
+
+ if (num_dests == 0)
+ num_dests = cupsGetDests(&dests);
+
+ if (argv[i][2] != '\0')
+ show_devices(http, argv[i] + 2, num_dests, dests);
+ else if ((i + 1) < argc && argv[i + 1][0] != '-')
+ {
+ i ++;
+ show_devices(http, argv[i], num_dests, dests);
+ }
+ else
+ show_devices(http, NULL, num_dests, dests);
+ 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)
+ {
+ if (!http)
+ {
+ http = httpConnect(cupsServer(), ippPort());
+
+ if (http == NULL)
+ {
+ perror("lpstat: Unable to connect to server");
+ return (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 *printers, /* I - Destinations */
+ int num_dests, /* I - Number of user-defined dests */
+ cups_dest_t *dests) /* I - User-defined 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 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, printers));
+
+ 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, "/")) != NULL)
+ {
+ DEBUG_puts("show_accepting: request succeeded...");
+
+ if (response->request.status.status_code > IPP_OK_CONFLICT)
+ {
+ fprintf(stderr, "lpstat: get-printers failed: %s\n",
+ ippErrorString(response->request.status.status_code));
+ ippDelete(response);
+ return;
+ }
+
+ /*
+ * 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 printer...
+ */
+
+ while (attr != NULL && attr->group_tag != IPP_TAG_PRINTER)
+ attr = attr->next;
+
+ if (attr == NULL)
+ break;
+
+ /*
+ * Pull the needed attributes from this printer...
+ */
+
+ 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 = printers == NULL;
+
+ if (printers != NULL)
+ {
+ for (dptr = printers; *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);
+
+ for (i = 0; i < num_dests; i ++)
+ if (strcasecmp(dests[i].name, printer) == 0 && dests[i].instance)
+ {
+ if (accepting)
+ printf("%s/%s accepting requests\n", printer, dests[i].instance);
+ else
+ printf("%s/%s not accepting requests -\n\t%s\n", printer,
+ dests[i].instance,
+ message == NULL ? "reason unknown" : message);
+ }
+ }
+
+ if (attr == NULL)
+ break;
+ }
+
+ ippDelete(response);
+ }
+ else
+ fprintf(stderr, "lpstat: get-printers failed: %s\n",
+ ippErrorString(cupsLastError()));
+}
+
+
+/*
+ * '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, "/")) != NULL)
+ {
+ DEBUG_puts("show_classes: request succeeded...");
+
+ if (response->request.status.status_code > IPP_OK_CONFLICT)
+ {
+ fprintf(stderr, "lpstat: get-classes failed: %s\n",
+ ippErrorString(response->request.status.status_code));
+ ippDelete(response);
+ return;
+ }
+
+ /*
+ * 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);
+ }
+ else
+ fprintf(stderr, "lpstat: get-classes failed: %s\n",
+ ippErrorString(cupsLastError()));
+}
+
+
+/*
+ * 'show_default()' - Show default destination.
+ */
+
+static void
+show_default(int num_dests, /* I - Number of user-defined dests */
+ cups_dest_t *dests) /* I - User-defined destinations */
+{
+ int i; /* Looping var */
+
+
+ for (i = 0; i < num_dests; i ++)
+ if (dests[i].is_default)
+ break;
+
+ if (i < num_dests)
+ {
+ if (dests[i].instance)
+ printf("system default destination: %s/%s\n", dests[i].name,
+ dests[i].instance);
+ else
+ printf("system default destination: %s\n", dests[i].name);
+ }
+ 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 *printers, /* I - Destinations */
+ int num_dests, /* I - Number of user-defined dests */
+ cups_dest_t *dests) /* I - User-defined 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 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, "/")) != NULL)
+ {
+ DEBUG_puts("show_devices: request succeeded...");
+
+ if (response->request.status.status_code > IPP_OK_CONFLICT)
+ {
+ fprintf(stderr, "lpstat: get-printers failed: %s\n",
+ ippErrorString(response->request.status.status_code));
+ ippDelete(response);
+ return;
+ }
+
+ /*
+ * 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)
+ {
+ if (attr == NULL)
+ break;
+ else
+ continue;
+ }
+
+ /*
+ * See if this is a printer we're interested in...
+ */
+
+ match = printers == NULL;
+
+ if (printers != NULL)
+ {
+ for (dptr = printers; *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 (device == NULL)
+ printf("device for %s: /dev/null\n", printer);
+ else if (strncmp(device, "file:", 5) == 0)
+ printf("device for %s: %s\n", printer, device + 5);
+ else
+ printf("device for %s: %s\n", printer, device);
+
+ for (i = 0; i < num_dests; i ++)
+ if (strcasecmp(printer, dests[i].name) == 0 && dests[i].instance)
+ {
+ if (device == NULL)
+ printf("device for %s/%s: /dev/null\n", printer, dests[i].instance);
+ else if (strncmp(device, "file:", 5) == 0)
+ printf("device for %s/%s: %s\n", printer, dests[i].instance, device + 5);
+ else
+ printf("device for %s/%s: %s\n", printer, dests[i].instance, device);
+ }
+ }
+
+ if (attr == NULL)
+ break;
+ }
+
+ ippDelete(response);
+ }
+ else
+ fprintf(stderr, "lpstat: get-printers failed: %s\n",
+ ippErrorString(cupsLastError()));
+}
+
+
+/*
+ * '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, "/")) != NULL)
+ {
+ /*
+ * Loop through the job list and display them...
+ */
+
+ if (response->request.status.status_code > IPP_OK_CONFLICT)
+ {
+ fprintf(stderr, "lpstat: get-jobs failed: %s\n",
+ ippErrorString(response->request.status.status_code));
+ ippDelete(response);
+ return;
+ }
+
+ 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);
+ }
+ else
+ fprintf(stderr, "lpstat: get-jobs failed: %s\n",
+ ippErrorString(cupsLastError()));
+}
+
+
+/*
+ * 'show_printers()' - Show printers.
+ */
+
+static void
+show_printers(http_t *http, /* I - HTTP connection to server */
+ const char *printers, /* I - Destinations */
+ int num_dests, /* I - Number of user-defined dests */
+ cups_dest_t *dests) /* I - User-defined destinations */
+{
+ int i; /* Looping var */
+ ipp_t *request, /* IPP Request */
+ *response, /* IPP Response */
+ *jobs; /* IPP Get Jobs response */
+ ipp_attribute_t *attr; /* Current attribute */
+ ipp_attribute_t *jobattr; /* Job ID 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, "/")) != NULL)
+ {
+ DEBUG_puts("show_printers: request succeeded...");
+
+ if (response->request.status.status_code > IPP_OK_CONFLICT)
+ {
+ fprintf(stderr, "lpstat: get-printers failed: %s\n",
+ ippErrorString(response->request.status.status_code));
+ ippDelete(response);
+ return;
+ }
+
+ /*
+ * 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 = printers == NULL;
+
+ if (printers != NULL)
+ {
+ for (dptr = printers; *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 ((jobattr = ippFindAttribute(jobs, "job-id", IPP_TAG_INTEGER)) != NULL)
+ jobid = jobattr->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;
+ }
+
+ for (i = 0; i < num_dests; i ++)
+ if (strcasecmp(printer, dests[i].name) == 0 && dests[i].instance)
+ switch (pstate)
+ {
+ case IPP_PRINTER_IDLE :
+ printf("printer %s/%s is idle.\n", printer, dests[i].instance);
+ break;
+ case IPP_PRINTER_PROCESSING :
+ printf("printer %s/%s now printing %s-%d.\n", printer,
+ dests[i].instance, printer, jobid);
+ break;
+ case IPP_PRINTER_STOPPED :
+ printf("printer %s/%s disabled -\n\t%s\n", printer,
+ dests[i].instance,
+ message == NULL ? "reason unknown" : message);
+ break;
+ }
+ }
+
+ if (attr == NULL)
+ break;
+ }
+
+ ippDelete(response);
+ }
+ else
+ fprintf(stderr, "lpstat: get-printers failed: %s\n",
+ ippErrorString(cupsLastError()));
+}
+
+
+/*
+ * '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$".
+ */
diff --git a/templates/Makefile b/templates/Makefile
new file mode 100644
index 000000000..e01e40cad
--- /dev/null
+++ b/templates/Makefile
@@ -0,0 +1,100 @@
+#
+# "$Id$"
+#
+# Template makefile for the Common UNIX Printing System (CUPS).
+#
+# Copyright 1993-2000 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
+
+#
+# Template files...
+#
+
+FILES = add-class.tmpl \
+ add-printer.tmpl \
+ admin-op.tmpl \
+ admin.tmpl \
+ choose-device.tmpl \
+ choose-make.tmpl \
+ choose-members.tmpl \
+ choose-model.tmpl \
+ choose-serial.tmpl \
+ choose-uri.tmpl \
+ class-added.tmpl \
+ class-confirm.tmpl \
+ class-deleted.tmpl \
+ class-modified.tmpl \
+ classes.tmpl \
+ config-printer.tmpl \
+ config-printer2.tmpl \
+ error.tmpl \
+ header.tmpl \
+ jobs.tmpl \
+ job-cancel.tmpl \
+ job-hold.tmpl \
+ job-release.tmpl \
+ modify-class.tmpl \
+ modify-printer.tmpl \
+ option-boolean.tmpl \
+ option-header.tmpl \
+ option-pickmany.tmpl \
+ option-pickone.tmpl \
+ option-trailer.tmpl \
+ printers.tmpl \
+ printer-accept.tmpl \
+ printer-added.tmpl \
+ printer-configured.tmpl \
+ printer-confirm.tmpl \
+ printer-deleted.tmpl \
+ printer-modified.tmpl \
+ printer-reject.tmpl \
+ printer-start.tmpl \
+ printer-stop.tmpl \
+ test-page.tmpl \
+ trailer.tmpl
+
+
+#
+# Make everything...
+#
+
+all:
+
+
+#
+# Clean all config and object files...
+#
+
+clean:
+
+
+#
+# Install files...
+#
+
+install:
+ -$(MKDIR) $(DATADIR)/templates
+ $(INSTALL_DATA) $(FILES) $(DATADIR)/templates
+
+
+#
+# End of "$Id$".
+#
diff --git a/templates/add-class.tmpl b/templates/add-class.tmpl
new file mode 100644
index 000000000..019a3f9fc
--- /dev/null
+++ b/templates/add-class.tmpl
@@ -0,0 +1,33 @@
+<FORM METHOD="POST" ACTION="/admin">
+<INPUT TYPE="HIDDEN" NAME="OP" VALUE="{op}">
+<TABLE CELLPADDING="0" CELLSPACING="0" BORDER="0" WIDTH="100%">
+<TR BGCOLOR="#999966">
+ <TH WIDTH="16"><IMG SRC="/images/left.gif" ALT=""></TH>
+ <TH COLSPAN="2">Add New Class</TH>
+ <TH BGCOLOR="#cccc99" WIDTH="16"><IMG SRC="/images/right.gif" ALT=""></TH>
+</TR>
+<TR>
+ <TD HEIGHT="4"></TD>
+</TR>
+<TR>
+ <TD></TD>
+ <TD ALIGN="RIGHT">Name:</TD>
+ <TD><INPUT TYPE="TEXT" NAME="PRINTER_NAME" SIZE="40" MAXLENGTH="127"></TD>
+</TR>
+<TR>
+ <TD></TD>
+ <TD ALIGN="RIGHT">Location:</TD>
+ <TD><INPUT TYPE="TEXT" NAME="PRINTER_LOCATION" SIZE="40" MAXLENGTH="127"></TD>
+</TR>
+<TR>
+ <TD></TD>
+ <TD ALIGN="RIGHT">Description:</TD>
+ <TD><INPUT TYPE="TEXT" NAME="PRINTER_INFO" SIZE="40" MAXLENGTH="127"></TD>
+</TR>
+<TR>
+ <TD></TD>
+ <TD></TD>
+ <TD><INPUT TYPE="IMAGE" SRC="/images/continue.gif" ALT="Continue" BORDER="0"></TD>
+</TR>
+</TABLE>
+</FORM>
diff --git a/templates/add-printer.tmpl b/templates/add-printer.tmpl
new file mode 100644
index 000000000..bf4a33d83
--- /dev/null
+++ b/templates/add-printer.tmpl
@@ -0,0 +1,33 @@
+<FORM METHOD="POST" ACTION="/admin">
+<INPUT TYPE="HIDDEN" NAME="OP" VALUE="{op}">
+<TABLE CELLPADDING="0" CELLSPACING="0" BORDER="0" WIDTH="100%">
+<TR BGCOLOR="#999966">
+ <TH WIDTH="16"><IMG SRC="/images/left.gif" ALT=""></TH>
+ <TH COLSPAN="2">Add New Printer</TH>
+ <TH BGCOLOR="#cccc99" WIDTH="16"><IMG SRC="/images/right.gif" ALT=""></TH>
+</TR>
+<TR>
+ <TD HEIGHT="4"></TD>
+</TR>
+<TR>
+ <TD></TD>
+ <TD ALIGN="RIGHT">Name:</TD>
+ <TD><INPUT TYPE="TEXT" NAME="PRINTER_NAME" SIZE="40" MAXLENGTH="127"></TD>
+</TR>
+<TR>
+ <TD></TD>
+ <TD ALIGN="RIGHT">Location:</TD>
+ <TD><INPUT TYPE="TEXT" NAME="PRINTER_LOCATION" SIZE="40" MAXLENGTH="127"></TD>
+</TR>
+<TR>
+ <TD></TD>
+ <TD ALIGN="RIGHT">Description:</TD>
+ <TD><INPUT TYPE="TEXT" NAME="PRINTER_INFO" SIZE="40" MAXLENGTH="127"></TD>
+</TR>
+<TR>
+ <TD></TD>
+ <TD></TD>
+ <TD><INPUT TYPE="IMAGE" SRC="/images/continue.gif" ALT="Continue" BORDER="0"></TD>
+</TR>
+</TABLE>
+</FORM>
diff --git a/templates/admin-op.tmpl b/templates/admin-op.tmpl
new file mode 100644
index 000000000..16699f815
--- /dev/null
+++ b/templates/admin-op.tmpl
@@ -0,0 +1 @@
+<P>Unsupported administration operation "{op}".
diff --git a/templates/admin.tmpl b/templates/admin.tmpl
new file mode 100644
index 000000000..65a711fd7
--- /dev/null
+++ b/templates/admin.tmpl
@@ -0,0 +1,57 @@
+<TABLE CELLPADDING="0" CELLSPACING="0" BORDER="0" WIDTH="100%">
+<TR>
+ <TH BGCOLOR="#999966"><IMG SRC="/images/left.gif" ALT=""></TH>
+ <TH BGCOLOR="#999966">Classes</TH>
+ <TH WIDTH="16"><IMG SRC="/images/right.gif" ALT=""></TH>
+</TR>
+<TR>
+ <TD HEIGHT="4"></TD>
+</TR>
+<TR>
+ <TD></TD>
+ <TD><A HREF="/admin/?op=add-class">
+ <IMG SRC="/images/add-class.gif" ALT="Add a New Class" BORDER="0"></A>
+ <A HREF="/classes/">
+ <IMG SRC="/images/manage-classes.gif" ALT="Manage Available Classes" BORDER="0"></A>
+ </TD>
+</TR>
+<TR>
+ <TD>&nbsp;</TD>
+</TR>
+<TR>
+ <TH BGCOLOR="#999966"><IMG SRC="/images/left.gif" ALT=""></TH>
+ <TH BGCOLOR="#999966">Jobs</TH>
+ <TH WIDTH="16"><IMG SRC="/images/right.gif" ALT=""></TH>
+</TR>
+<TR>
+ <TD HEIGHT="4"></TD>
+</TR>
+<TR>
+ <TD></TD>
+ <TD><A HREF="/jobs/">
+ <IMG SRC="/images/manage-jobs.gif" ALT="Manage Jobs" BORDER="0"></A>
+ </TD>
+</TR>
+<TR>
+ <TD>&nbsp;</TD>
+</TR>
+<TR>
+ <TH BGCOLOR="#999966"><IMG SRC="/images/left.gif" ALT=""></TH>
+ <TH BGCOLOR="#999966">Printers</TH>
+ <TH WIDTH="16"><IMG SRC="/images/right.gif" ALT=""></TH>
+</TR>
+<TR>
+ <TD HEIGHT="4"></TD>
+</TR>
+<TR>
+ <TD></TD>
+ <TD><A HREF="/admin/?op=add-printer">
+ <IMG SRC="/images/add-printer.gif" ALT="Add a New Printer" BORDER="0"></A>
+ <A HREF="/printers/">
+ <IMG SRC="/images/manage-printers.gif" ALT="Manage Available Printers" BORDER="0"></A>
+ </TD>
+</TR>
+<TR>
+ <TD>&nbsp;</TD>
+</TR>
+</TABLE>
diff --git a/templates/choose-device.tmpl b/templates/choose-device.tmpl
new file mode 100644
index 000000000..90660bb79
--- /dev/null
+++ b/templates/choose-device.tmpl
@@ -0,0 +1,32 @@
+<FORM METHOD="POST" ACTION="/admin">
+<INPUT TYPE="HIDDEN" NAME="OP" VALUE="{op}">
+<INPUT TYPE="HIDDEN" NAME="PRINTER_NAME" VALUE="{printer_name}">
+<INPUT TYPE="HIDDEN" NAME="PRINTER_LOCATION" VALUE="{?printer_location}">
+<INPUT TYPE="HIDDEN" NAME="PRINTER_INFO" VALUE="{?printer_info}">
+<INPUT TYPE="HIDDEN" NAME="CURRENT_MAKE_AND_MODEL" VALUE="{?current_make_and_model}">
+<TABLE CELLPADDING="0" CELLSPACING="0" BORDER="0" WIDTH="100%">
+<TR BGCOLOR="#999966">
+ <TH WIDTH="16"><IMG SRC="/images/left.gif" ALT=""></TH>
+ <TH COLSPAN="2">Device for {printer_name}</TH>
+ <TH BGCOLOR="#cccc99" WIDTH="16"><IMG SRC="/images/right.gif" ALT=""></TH>
+</TR>
+<TR>
+ <TD HEIGHT="4"></TD>
+</TR>
+<TR>
+ <TD></TD>
+ <TD ALIGN="RIGHT">Device:</TD>
+ <TD>
+ <SELECT NAME="DEVICE_URI">
+ {[device_uri]<OPTION VALUE="{device_uri}" {?current_device_uri={device_uri}?SELECTED:}>
+ {device_info} {?device_make_and_model!Unknown?({device_make_and_model}):}
+ }</SELECT>
+ </TD>
+</TR>
+<TR>
+ <TD></TD>
+ <TD></TD>
+ <TD><INPUT TYPE="IMAGE" SRC="/images/continue.gif" ALT="Continue" BORDER="0"></TD>
+</TR>
+</TABLE>
+</FORM>
diff --git a/templates/choose-make.tmpl b/templates/choose-make.tmpl
new file mode 100644
index 000000000..93076a582
--- /dev/null
+++ b/templates/choose-make.tmpl
@@ -0,0 +1,35 @@
+<FORM METHOD="POST" ACTION="/admin">
+<INPUT TYPE="HIDDEN" NAME="OP" VALUE="{op}">
+<INPUT TYPE="HIDDEN" NAME="PRINTER_NAME" VALUE="{printer_name}">
+<INPUT TYPE="HIDDEN" NAME="PRINTER_LOCATION" VALUE="{?printer_location}">
+<INPUT TYPE="HIDDEN" NAME="PRINTER_INFO" VALUE="{?printer_info}">
+<INPUT TYPE="HIDDEN" NAME="DEVICE_URI" VALUE="{device_uri}">
+<INPUT TYPE="HIDDEN" NAME="BAUDRATE" VALUE="{?baudrate}">
+<INPUT TYPE="HIDDEN" NAME="BITS" VALUE="{?bits}">
+<INPUT TYPE="HIDDEN" NAME="PARITY" VALUE="{?parity}">
+<INPUT TYPE="HIDDEN" NAME="FLOW" VALUE="{?flow}">
+<TABLE CELLPADDING="0" CELLSPACING="0" BORDER="0" WIDTH="100%">
+<TR BGCOLOR="#999966">
+ <TH WIDTH="16"><IMG SRC="/images/left.gif" ALT=""></TH>
+ <TH COLSPAN="2">Model/Driver for {printer_name}</TH>
+ <TH BGCOLOR="#cccc99" WIDTH="16"><IMG SRC="/images/right.gif" ALT=""></TH>
+</TR>
+<TR>
+ <TD HEIGHT="4"></TD>
+</TR>
+<TR>
+ <TD></TD>
+ <TD ALIGN="RIGHT">Make:</TD>
+ <TD>
+ <SELECT NAME="PPD_MAKE" SIZE="10">
+ {[ppd_make]<OPTION VALUE="{ppd_make}" {?current_make={ppd_make}?SELECTED:}>{ppd_make}}
+ </SELECT>
+ </TD>
+</TR>
+<TR>
+ <TD></TD>
+ <TD></TD>
+ <TD><INPUT TYPE="IMAGE" SRC="/images/continue.gif" ALT="Continue" BORDER="0"></TD>
+</TR>
+</TABLE>
+</FORM>
diff --git a/templates/choose-members.tmpl b/templates/choose-members.tmpl
new file mode 100644
index 000000000..f55929adf
--- /dev/null
+++ b/templates/choose-members.tmpl
@@ -0,0 +1,30 @@
+<FORM METHOD="POST" ACTION="/admin">
+<INPUT TYPE="HIDDEN" NAME="OP" VALUE="{op}">
+<INPUT TYPE="HIDDEN" NAME="PRINTER_NAME" VALUE="{printer_name}">
+<INPUT TYPE="HIDDEN" NAME="PRINTER_LOCATION" VALUE="{?printer_location}">
+<INPUT TYPE="HIDDEN" NAME="PRINTER_INFO" VALUE="{?printer_info}">
+<TABLE CELLPADDING="0" CELLSPACING="0" BORDER="0" WIDTH="100%">
+<TR BGCOLOR="#999966">
+ <TH WIDTH="16"><IMG SRC="/images/left.gif" ALT=""></TH>
+ <TH COLSPAN="2">Members for {printer_name}</TH>
+ <TH BGCOLOR="#cccc99" WIDTH="16"><IMG SRC="/images/right.gif" ALT=""></TH>
+</TR>
+<TR>
+ <TD HEIGHT="4"></TD>
+</TR>
+<TR>
+ <TD></TD>
+ <TD ALIGN="RIGHT">Members:</TD>
+ <TD>
+ <SELECT NAME="MEMBER_URIS" SIZE="10" MULTIPLE>
+ {[member_uris]<OPTION VALUE="{member_uris}" {?member_selected}>{member_names}}
+ </SELECT>
+ </TD>
+</TR>
+<TR>
+ <TD></TD>
+ <TD></TD>
+ <TD><INPUT TYPE="IMAGE" SRC="/images/continue.gif" ALT="Continue" BORDER="0"></TD>
+</TR>
+</TABLE>
+</FORM>
diff --git a/templates/choose-model.tmpl b/templates/choose-model.tmpl
new file mode 100644
index 000000000..c95f2816c
--- /dev/null
+++ b/templates/choose-model.tmpl
@@ -0,0 +1,35 @@
+<FORM METHOD="POST" ACTION="/admin">
+<INPUT TYPE="HIDDEN" NAME="OP" VALUE="{op}">
+<INPUT TYPE="HIDDEN" NAME="PRINTER_NAME" VALUE="{printer_name}">
+<INPUT TYPE="HIDDEN" NAME="PRINTER_LOCATION" VALUE="{?printer_location}">
+<INPUT TYPE="HIDDEN" NAME="PRINTER_INFO" VALUE="{?printer_info}">
+<INPUT TYPE="HIDDEN" NAME="DEVICE_URI" VALUE="{device_uri}">
+<INPUT TYPE="HIDDEN" NAME="BAUDRATE" VALUE="{?baudrate}">
+<INPUT TYPE="HIDDEN" NAME="BITS" VALUE="{?bits}">
+<INPUT TYPE="HIDDEN" NAME="PARITY" VALUE="{?parity}">
+<INPUT TYPE="HIDDEN" NAME="FLOW" VALUE="{?flow}">
+<TABLE CELLPADDING="0" CELLSPACING="0" BORDER="0" WIDTH="100%">
+<TR BGCOLOR="#999966">
+ <TH WIDTH="16"><IMG SRC="/images/left.gif" ALT=""></TH>
+ <TH COLSPAN="2">Model/Driver for {printer_name}</TH>
+ <TH BGCOLOR="#cccc99" WIDTH="16"><IMG SRC="/images/right.gif" ALT=""></TH>
+</TR>
+<TR>
+ <TD HEIGHT="4"></TD>
+</TR>
+<TR>
+ <TD></TD>
+ <TD ALIGN="RIGHT">Model:</TD>
+ <TD>
+ <SELECT NAME="PPD_NAME" SIZE="10">
+ {[ppd_name]<OPTION VALUE="{ppd_name}" {?current_make_and_model={ppd_make_and_model}?SELECTED:}>{ppd_make_and_model} ({ppd_natural_language})
+ }</SELECT>
+ </TD>
+</TR>
+<TR>
+ <TD></TD>
+ <TD></TD>
+ <TD><INPUT TYPE="IMAGE" SRC="/images/continue.gif" ALT="Continue" BORDER="0"></TD>
+</TR>
+</TABLE>
+</FORM>
diff --git a/templates/choose-serial.tmpl b/templates/choose-serial.tmpl
new file mode 100644
index 000000000..0b83036b9
--- /dev/null
+++ b/templates/choose-serial.tmpl
@@ -0,0 +1,55 @@
+<FORM METHOD="POST" ACTION="/admin">
+<INPUT TYPE="HIDDEN" NAME="OP" VALUE="{op}">
+<INPUT TYPE="HIDDEN" NAME="PRINTER_NAME" VALUE="{printer_name}">
+<INPUT TYPE="HIDDEN" NAME="PRINTER_LOCATION" VALUE="{?printer_location}">
+<INPUT TYPE="HIDDEN" NAME="PRINTER_INFO" VALUE="{?printer_info}">
+<INPUT TYPE="HIDDEN" NAME="DEVICE_URI" VALUE="{device_uri}">
+<TABLE CELLPADDING="0" CELLSPACING="0" BORDER="0" WIDTH="100%">
+<TR BGCOLOR="#999966">
+ <TH WIDTH="16"><IMG SRC="/images/left.gif" ALT=""></TH>
+ <TH COLSPAN="2">Serial Port Settings for {printer_name}</TH>
+ <TH BGCOLOR="#cccc99" WIDTH="16"><IMG SRC="/images/right.gif" ALT=""></TH>
+</TR>
+<TR>
+ <TD HEIGHT="4"></TD>
+</TR>
+<TR>
+ <TD></TD>
+ <TD ALIGN="RIGHT">Baud Rate:</TD>
+ <TD><SELECT NAME="BAUDRATE">
+ {[baudrates]<OPTION {?baudrate={baudrates}?SELECTED:}>{baudrates}}
+ </SELECT></TD>
+</TR>
+<TR>
+ <TD></TD>
+ <TD ALIGN="RIGHT">Parity:</TD>
+ <TD><SELECT NAME="PARITY">
+ <OPTION VALUE="none" {?parity=none?SELECTED:}>None
+ <OPTION VALUE="even" {?parity=even?SELECTED:}>Even
+ <OPTION VALUE="odd" {?parity=odd?SELECTED:}>Odd
+ </SELECT></TD>
+</TR>
+<TR>
+ <TD></TD>
+ <TD ALIGN="RIGHT">Data Bits:</TD>
+ <TD><SELECT NAME="BITS">
+ <OPTION {?bits=8?SELECTED:}>8
+ <OPTION {?bits=7?SELECTED:}>7
+ </SELECT></TD>
+</TR>
+<TR>
+ <TD></TD>
+ <TD ALIGN="RIGHT">Flow Control:</TD>
+ <TD><SELECT NAME="FLOW">
+ <OPTION VALUE="none" {?flow=none?SELECTED:}>None
+ <OPTION VALUE="soft" {?flow=soft?SELECTED:}>XON/XOFF (Software)
+ <OPTION VALUE="hard" {?flow=hard?SELECTED:}>CTS/RTS (Hardware)
+ </SELECT></TD>
+</TR>
+<TR>
+ <TD></TD>
+ <TD></TD>
+ <TD><INPUT TYPE="IMAGE" SRC="/images/continue.gif" ALT="Continue" BORDER="0"></TD>
+</TR>
+</TABLE>
+</FORM>
diff --git a/templates/choose-uri.tmpl b/templates/choose-uri.tmpl
new file mode 100644
index 000000000..808f2009c
--- /dev/null
+++ b/templates/choose-uri.tmpl
@@ -0,0 +1,41 @@
+<FORM METHOD="POST" ACTION="/admin">
+<INPUT TYPE="HIDDEN" NAME="OP" VALUE="{op}">
+<INPUT TYPE="HIDDEN" NAME="PRINTER_NAME" VALUE="{printer_name}">
+<INPUT TYPE="HIDDEN" NAME="PRINTER_LOCATION" VALUE="{?printer_location}">
+<INPUT TYPE="HIDDEN" NAME="PRINTER_INFO" VALUE="{?printer_info}">
+<INPUT TYPE="HIDDEN" NAME="CURRENT_MAKE_AND_MODEL" VALUE="{?current_make_and_model}">
+<TABLE CELLPADDING="0" CELLSPACING="0" BORDER="0" WIDTH="100%">
+<TR BGCOLOR="#999966">
+ <TH WIDTH="16"><IMG SRC="/images/left.gif" ALT=""></TH>
+ <TH COLSPAN="2">Device URI for {printer_name}</TH>
+ <TH BGCOLOR="#cccc99" WIDTH="16"><IMG SRC="/images/right.gif" ALT=""></TH>
+</TR>
+<TR>
+ <TD HEIGHT="4"></TD>
+</TR>
+<TR>
+ <TD></TD>
+ <TD ALIGN="RIGHT">Device URI:</TD>
+ <TD><INPUT TYPE="TEXT" SIZE="40" MAXLENGTH="1024" NAME="DEVICE_URI" VALUE="{device_uri}"></TD>
+</TR>
+<TR>
+ <TD></TD>
+ <TD></TD>
+ <TD>Examples:
+ <UL>
+ <PRE>file:/path/to/filename.prn</PRE>
+ <PRE>http://hostname:631/ipp/port1</PRE>
+ <PRE>ipp://hostname/ipp/port1</PRE>
+ <PRE>lpd://hostname/queue</PRE>
+ <PRE>socket://hostname</PRE>
+ <PRE>socket://hostname:9100</PRE>
+ </UL>
+ </TD>
+</TR>
+<TR>
+ <TD></TD>
+ <TD></TD>
+ <TD><INPUT TYPE="IMAGE" SRC="/images/continue.gif" ALT="Continue" BORDER="0"></TD>
+</TR>
+</TABLE>
+</FORM>
diff --git a/templates/class-added.tmpl b/templates/class-added.tmpl
new file mode 100644
index 000000000..f740581ad
--- /dev/null
+++ b/templates/class-added.tmpl
@@ -0,0 +1,2 @@
+<P>Class <A HREF="/classes/{printer_name}">{printer_name}</A> has been added
+successfully.
diff --git a/templates/class-confirm.tmpl b/templates/class-confirm.tmpl
new file mode 100644
index 000000000..0fbbd6d53
--- /dev/null
+++ b/templates/class-confirm.tmpl
@@ -0,0 +1,6 @@
+<P><B>Warning:</B> About to delete class {printer_name}! Do you wish to
+continue?
+
+<P ALIGN="CENTER">
+<A HREF="/admin/?op=delete-class&printer_name={printer_name}&confirm=yes">
+<IMG SRC="/images/continue.gif" ALT="Continue" BORDER="0"></A>
diff --git a/templates/class-deleted.tmpl b/templates/class-deleted.tmpl
new file mode 100644
index 000000000..269ddd87a
--- /dev/null
+++ b/templates/class-deleted.tmpl
@@ -0,0 +1 @@
+<P>Class {printer_name} has been deleted successfully.
diff --git a/templates/class-modified.tmpl b/templates/class-modified.tmpl
new file mode 100644
index 000000000..861790732
--- /dev/null
+++ b/templates/class-modified.tmpl
@@ -0,0 +1,2 @@
+<P>Class <A HREF="/classes/{printer_name}">{printer_name}</A> has been
+modified successfully.
diff --git a/templates/classes.tmpl b/templates/classes.tmpl
new file mode 100644
index 000000000..28ca76314
--- /dev/null
+++ b/templates/classes.tmpl
@@ -0,0 +1,50 @@
+{#printer_name=0?No classes:
+<TABLE CELLPADDING="0" CELLSPACING="0" BORDER="0" WIDTH="100%">
+{[printer_name]
+<TR>
+ <TH BGCOLOR="#999966"><IMG SRC="/images/left.gif" ALT=""></TH>
+ <TH BGCOLOR="#999966"><A HREF="{printer_uri_supported}">{printer_name}</A></TH>
+ <TH BGCOLOR="#999966">Printer Class</TH>
+ <TH><IMG SRC="/images/right.gif" ALT=""></TH>
+</TR>
+<TR>
+ <TD HEIGHT="4"></TD>
+</TR>
+<TR>
+ <TD></TD>
+ <TD VALIGN=TOP><A HREF="{printer_uri_supported}">
+ <IMG SRC="../images/classes.gif" BORDER="0" ALT=""></A></TD>
+ <TD VALIGN=TOP>Description: {printer_info}<BR>
+ Location: {printer_location}<BR>
+ Class State: {printer_state=3?idle:{printer_state=4?processing:stopped}},
+ {printer_is_accepting_jobs=0?rejecting jobs:accepting jobs}.
+ {?printer_state_message=?:<BR><I>"{printer_state_message}"</I>}
+ {?member_names=?:<BR>Members: {member_names}}
+ <P>
+ <A HREF="{printer_uri_supported}?op=print-test-page">
+ <IMG SRC="/images/print-test-page.gif" ALT="Print Test Page" BORDER="0"></A>
+ {printer_state=5?
+ <A HREF="/admin/?op=start-class&printer_name={printer_name}">
+ <IMG SRC="/images/start-class.gif" ALT="Start Class" BORDER="0"></A>
+ :
+ <A HREF="/admin/?op=stop-printer&printer_name={printer_name}">
+ <IMG SRC="/images/stop-class.gif" ALT="Stop Class" BORDER="0"></A>
+ }
+ {printer_is_accepting_jobs=0?
+ <A HREF="/admin/?op=accept-jobs&printer_name={printer_name}">
+ <IMG SRC="/images/accept-jobs.gif" ALT="Accept Jobs" BORDER="0"></A>
+ :
+ <A HREF="/admin/?op=reject-jobs&printer_name={printer_name}">
+ <IMG SRC="/images/reject-jobs.gif" ALT="Reject Jobs" BORDER="0"></A>
+ }
+ <A HREF="/admin/?op=modify-class&printer_name={printer_name}">
+ <IMG SRC="/images/modify-class.gif" ALT="Modify Class" BORDER="0"></A>
+ <A HREF="/admin/?op=delete-class&printer_name={printer_name}">
+ <IMG SRC="/images/delete-class.gif" ALT="Delete Class" BORDER="0"></A>
+ </TD>
+</TR>
+}
+</TABLE>
+}
+<P><A HREF="/admin/?op=add-class">
+<IMG SRC="/images/add-class.gif" ALT="Add Class" BORDER="0"></A>
diff --git a/templates/config-printer.tmpl b/templates/config-printer.tmpl
new file mode 100644
index 000000000..f24b08f12
--- /dev/null
+++ b/templates/config-printer.tmpl
@@ -0,0 +1,6 @@
+<P>Choose default options for {printer_name}.
+
+<FORM METHOD="POST" ACTION="/admin">
+<INPUT TYPE="HIDDEN" NAME="PRINTER_NAME" VALUE="{printer_name}">
+<INPUT TYPE="HIDDEN" NAME="OP" VALUE="{op}">
+<TABLE CELLPADDING="0" CELLSPACING="0" BORDER="0" WIDTH="100%">
diff --git a/templates/config-printer2.tmpl b/templates/config-printer2.tmpl
new file mode 100644
index 000000000..bfb42d69f
--- /dev/null
+++ b/templates/config-printer2.tmpl
@@ -0,0 +1,2 @@
+</TABLE>
+</FORM>
diff --git a/templates/error.tmpl b/templates/error.tmpl
new file mode 100644
index 000000000..eb15a06ff
--- /dev/null
+++ b/templates/error.tmpl
@@ -0,0 +1,3 @@
+<P>Error:
+
+<BLOCKQUOTE>{error}</BLOCKQUOTE>
diff --git a/templates/header.tmpl b/templates/header.tmpl
new file mode 100644
index 000000000..a60bafd74
--- /dev/null
+++ b/templates/header.tmpl
@@ -0,0 +1,20 @@
+<HTML>
+<HEAD>
+ <TITLE>{title} on {server_name} - {cups_version}</TITLE>
+ <LINK REL=STYLESHEET TYPE="text/css" HREF="/cups.css">
+ <MAP NAME="navbar">
+ <AREA SHAPE="RECT" COORDS="12,10,50,20" HREF="http://www.easysw.com" ALT="Easy Software Products Home Page">
+ <AREA SHAPE="RECT" COORDS="82,10,196,20" HREF="/admin" ALT="Do Administration Tasks">
+ <AREA SHAPE="RECT" COORDS="216,10,280,20" HREF="/classes" ALT="Manage Printer Classes Status">
+ <AREA SHAPE="RECT" COORDS="300,10,336,20" HREF="/documentation.html" ALT="On-Line Help">
+ <AREA SHAPE="RECT" COORDS="356,10,394,20" HREF="/jobs" ALT="Manage Jobs">
+ <AREA SHAPE="RECT" COORDS="414,10,476,20" HREF="/printers" ALT="Manage Printers">
+ <AREA SHAPE="RECT" COORDS="496,10,568,20" HREF="http://www.cups.org" ALT="Download the Current CUPS Software">
+ </MAP>
+</HEAD>
+<BODY BGCOLOR="#cccc99" TEXT="#000000" LINK="#0000FF" VLINK="#FF00FF">
+<CENTER>
+<IMG SRC="/images/navbar.gif" WIDTH="583" HEIGHT="30" USEMAP="#navbar" BORDER="0" ALT="Common UNIX Printing System">
+</CENTER>
+
+<H1>{title}</H1>
diff --git a/templates/job-cancel.tmpl b/templates/job-cancel.tmpl
new file mode 100644
index 000000000..52ba31137
--- /dev/null
+++ b/templates/job-cancel.tmpl
@@ -0,0 +1 @@
+<P>Job {job_id} has been cancelled.
diff --git a/templates/job-hold.tmpl b/templates/job-hold.tmpl
new file mode 100644
index 000000000..10fa6b369
--- /dev/null
+++ b/templates/job-hold.tmpl
@@ -0,0 +1 @@
+<P>Job {job_id} has been held from printing.
diff --git a/templates/job-release.tmpl b/templates/job-release.tmpl
new file mode 100644
index 000000000..8dd395b28
--- /dev/null
+++ b/templates/job-release.tmpl
@@ -0,0 +1 @@
+<P>Job {job_id} has been released for printing.
diff --git a/templates/jobs.tmpl b/templates/jobs.tmpl
new file mode 100644
index 000000000..a32677b88
--- /dev/null
+++ b/templates/jobs.tmpl
@@ -0,0 +1,55 @@
+<P>{#job_id=0?No {?which_jobs=completed?Completed:Active} Jobs:
+<TABLE CELLPADDING="0" CELLSPACING="0" BORDER="0" WIDTH="100%">
+<TR BGCOLOR="#999966">
+ <TH WIDTH="16"><IMG SRC="/images/left.gif" ALT=""></TH>
+ <TH>ID</TH>
+ <TH>Name</TH>
+ <TH>User</TH>
+ <TH>Size</TH>
+ <TH>State</TH>
+ <TH>Control</TH>
+ <TH BGCOLOR="#cccc99" WIDTH="16"><IMG SRC="/images/right.gif" ALT=""></TH>
+</TR>
+<TR>
+ <TD HEIGHT="4"></TD>
+</TR>
+{[job_id]
+<TR>
+ <TD HEIGHT="28"></TD>
+ <TD>{job_id}</TD>
+ <TD>{?job_name=?Unknown:{job_name}}</TD>
+ <TD>{job_originating_user_name}</TD>
+ <TD>{job_k_octets}k</TD>
+ <TD>{job_state=3?pending:{job_state=4?held:
+ {job_state=5?processing:{job_state=6?stopped:
+ {job_state=7?cancelled:{job_state=8?aborted:completed}}}}}}</TD>
+ <TD>
+ {job_state>5?
+ {job_k_octets>0?
+ <A HREF="/admin/?op=restart-job&job_id={job_id}">
+ <IMG SRC="/images/restart-job.gif" ALT="Restart Job" BORDER="0"></A>
+ :}:}
+ {job_state>6?:
+ {job_state=4?
+ <A HREF="/admin/?op=release-job&job_id={job_id}">
+ <IMG SRC="/images/release-job.gif" ALT="Release Job" BORDER="0"></A>
+ :
+ <A HREF="/admin/?op=hold-job&job_id={job_id}">
+ <IMG SRC="/images/hold-job.gif" ALT="Hold Job" BORDER="0"></A>
+ }
+ <A HREF="/admin/?op=cancel-job&job_id={job_id}">
+ <IMG SRC="/images/cancel-job.gif" ALT="Cancel Job" BORDER="0"></A>
+ }
+ </FORM>
+ </TD>
+</TR>
+}
+</TABLE>
+}
+<P>{?which_jobs=?
+<A HREF="{?printer_name=?/jobs:/printers/{printer_name}}?which_jobs=completed">
+<IMG SRC="/images/show-completed.gif" BORDER="0" ALT="Show Completed Jobs"></A>
+:
+<A HREF="{?printer_name=?/jobs:/printers/{printer_name}}">
+<IMG SRC="/images/show-active.gif" BORDER="0" ALT="Show Active Jobs"></A>
+}
diff --git a/templates/modify-class.tmpl b/templates/modify-class.tmpl
new file mode 100644
index 000000000..17aeedbba
--- /dev/null
+++ b/templates/modify-class.tmpl
@@ -0,0 +1,34 @@
+<FORM METHOD="POST" ACTION="/admin">
+<INPUT TYPE="HIDDEN" NAME="OP" VALUE="{op}">
+<TABLE CELLPADDING="0" CELLSPACING="0" BORDER="0" WIDTH="100%">
+<TR BGCOLOR="#999966">
+ <TH WIDTH="16"><IMG SRC="/images/left.gif" ALT=""></TH>
+ <TH COLSPAN="2">Modify Class {printer_name}</TH>
+ <TH BGCOLOR="#cccc99" WIDTH="16"><IMG SRC="/images/right.gif" ALT=""></TH>
+</TR>
+<TR>
+ <TD HEIGHT="4"></TD>
+</TR>
+<TR>
+ <TD></TD>
+ <TD ALIGN="RIGHT">Name:</TD>
+ <TD><INPUT TYPE="HIDDEN" NAME="PRINTER_NAME" VALUE="{printer_name}">
+ {printer_name}</TD>
+</TR>
+<TR>
+ <TD></TD>
+ <TD ALIGN="RIGHT">Location:</TD>
+ <TD><INPUT TYPE="TEXT" NAME="PRINTER_LOCATION" VALUE="{?printer_location}" SIZE="40" MAXLENGTH="127"></TD>
+</TR>
+<TR>
+ <TD></TD>
+ <TD ALIGN="RIGHT">Description:</TD>
+ <TD><INPUT TYPE="TEXT" NAME="PRINTER_INFO" VALUE="{?printer_info}" SIZE="40" MAXLENGTH="127"></TD>
+</TR>
+<TR>
+ <TD></TD>
+ <TD></TD>
+ <TD><INPUT TYPE="IMAGE" SRC="/images/continue.gif" ALT="Continue" BORDER="0"></TD>
+</TR>
+</TABLE>
+</FORM>
diff --git a/templates/modify-printer.tmpl b/templates/modify-printer.tmpl
new file mode 100644
index 000000000..68e36cd8e
--- /dev/null
+++ b/templates/modify-printer.tmpl
@@ -0,0 +1,36 @@
+<FORM METHOD="POST" ACTION="/admin">
+<INPUT TYPE="HIDDEN" NAME="OP" VALUE="{op}">
+{?device_uri=?:<INPUT TYPE="HIDDEN" NAME="CURRENT_DEVICE_URI" VALUE="{device_uri}">}
+{?printer_make_and_model=?:<INPUT TYPE="HIDDEN" NAME="CURRENT_MAKE_AND_MODEL" VALUE="{printer_make_and_model}">}
+<TABLE CELLPADDING="0" CELLSPACING="0" BORDER="0" WIDTH="100%">
+<TR BGCOLOR="#999966">
+ <TH WIDTH="16"><IMG SRC="/images/left.gif" ALT=""></TH>
+ <TH COLSPAN="2">Modify Printer {printer_name}</TH>
+ <TH BGCOLOR="#cccc99" WIDTH="16"><IMG SRC="/images/right.gif" ALT=""></TH>
+</TR>
+<TR>
+ <TD HEIGHT="4"></TD>
+</TR>
+<TR>
+ <TD></TD>
+ <TD ALIGN="RIGHT">Name:</TD>
+ <TD><INPUT TYPE="HIDDEN" NAME="PRINTER_NAME" VALUE="{printer_name}">
+ {printer_name}</TD>
+</TR>
+<TR>
+ <TD></TD>
+ <TD ALIGN="RIGHT">Location:</TD>
+ <TD><INPUT TYPE="TEXT" NAME="PRINTER_LOCATION" VALUE="{?printer_location}" SIZE="40" MAXLENGTH="127"></TD>
+</TR>
+<TR>
+ <TD></TD>
+ <TD ALIGN="RIGHT">Description:</TD>
+ <TD><INPUT TYPE="TEXT" NAME="PRINTER_INFO" VALUE="{?printer_info}" SIZE="40" MAXLENGTH="127"></TD>
+</TR>
+<TR>
+ <TD></TD>
+ <TD></TD>
+ <TD><INPUT TYPE="IMAGE" SRC="/images/continue.gif" ALT="Continue" BORDER="0"></TD>
+</TR>
+</TABLE>
+</FORM>
diff --git a/templates/option-boolean.tmpl b/templates/option-boolean.tmpl
new file mode 100644
index 000000000..a8360fc82
--- /dev/null
+++ b/templates/option-boolean.tmpl
@@ -0,0 +1,7 @@
+<TR>
+ <TD></TD>
+ <TD ALIGN="RIGHT">{keytext}:</TD>
+ <TD>
+ {[choices]<INPUT TYPE="RADIO" NAME="{keyword}" {choices={defchoice}?CHECKED:} VALUE="{choices}">{text}}
+ </TD>
+</TR>
diff --git a/templates/option-header.tmpl b/templates/option-header.tmpl
new file mode 100644
index 000000000..a23b1d2c1
--- /dev/null
+++ b/templates/option-header.tmpl
@@ -0,0 +1,8 @@
+<TR>
+ <TH BGCOLOR="#999966"><IMG SRC="/images/left.gif" ALT=""></TH>
+ <TH BGCOLOR="#999966" COLSPAN="2">{group}</A></TH>
+ <TH><IMG SRC="/images/right.gif" ALT=""></TH>
+</TR>
+<TR>
+ <TD HEIGHT="4"></TD>
+</TR>
diff --git a/templates/option-pickmany.tmpl b/templates/option-pickmany.tmpl
new file mode 100644
index 000000000..8340889b4
--- /dev/null
+++ b/templates/option-pickmany.tmpl
@@ -0,0 +1,7 @@
+<TR>
+ <TD></TD>
+ <TD ALIGN="RIGHT" VALIGN="TOP">{keytext}:</TD>
+ <TD><SELECT NAME="{keyword}" MULTIPLE SIZE="10">
+ {[choices]<OPTION {choices={defchoice}?SELECTED:} VALUE="{choices}">{text}}
+ </SELECT></TD>
+</TR>
diff --git a/templates/option-pickone.tmpl b/templates/option-pickone.tmpl
new file mode 100644
index 000000000..6880b968b
--- /dev/null
+++ b/templates/option-pickone.tmpl
@@ -0,0 +1,7 @@
+<TR>
+ <TD></TD>
+ <TD ALIGN="RIGHT">{keytext}:</TD>
+ <TD><SELECT NAME="{keyword}">
+ {[choices]<OPTION {choices={defchoice}?SELECTED:} VALUE="{choices}">{text}}
+ </SELECT></TD>
+</TR>
diff --git a/templates/option-trailer.tmpl b/templates/option-trailer.tmpl
new file mode 100644
index 000000000..803022837
--- /dev/null
+++ b/templates/option-trailer.tmpl
@@ -0,0 +1,8 @@
+<TR>
+ <TD></TD>
+ <TD></TD>
+ <TD><INPUT TYPE="IMAGE" SRC="/images/continue.gif" ALT="Continue" BORDER="0"></TD>
+</TR>
+<TR>
+ <TD HEIGHT="4"></TD>
+</TR>
diff --git a/templates/printer-accept.tmpl b/templates/printer-accept.tmpl
new file mode 100644
index 000000000..23d8c2f3c
--- /dev/null
+++ b/templates/printer-accept.tmpl
@@ -0,0 +1 @@
+<P>Printer {printer_name} is now accepting jobs.
diff --git a/templates/printer-added.tmpl b/templates/printer-added.tmpl
new file mode 100644
index 000000000..ba5f550c2
--- /dev/null
+++ b/templates/printer-added.tmpl
@@ -0,0 +1,2 @@
+<P>Printer <A HREF="/printers/{printer_name}">{printer_name}</A> has been added
+successfully.
diff --git a/templates/printer-configured.tmpl b/templates/printer-configured.tmpl
new file mode 100644
index 000000000..7ec86879a
--- /dev/null
+++ b/templates/printer-configured.tmpl
@@ -0,0 +1,2 @@
+<P>Printer <A HREF="/printers/{printer_name}">{printer_name}</A> has
+been configured successfully.
diff --git a/templates/printer-confirm.tmpl b/templates/printer-confirm.tmpl
new file mode 100644
index 000000000..690c6479b
--- /dev/null
+++ b/templates/printer-confirm.tmpl
@@ -0,0 +1,6 @@
+<P><B>Warning:</B> About to delete printer {printer_name}! Do you wish to
+continue?
+
+<P ALIGN="CENTER">
+<A HREF="/admin/?op=delete-printer&printer_name={printer_name}&confirm=yes">
+<IMG SRC="/images/continue.gif" ALT="Continue" BORDER="0"></A>
diff --git a/templates/printer-deleted.tmpl b/templates/printer-deleted.tmpl
new file mode 100644
index 000000000..f1336b037
--- /dev/null
+++ b/templates/printer-deleted.tmpl
@@ -0,0 +1 @@
+<P>Printer {printer_name} has been deleted successfully.
diff --git a/templates/printer-modified.tmpl b/templates/printer-modified.tmpl
new file mode 100644
index 000000000..a16abd169
--- /dev/null
+++ b/templates/printer-modified.tmpl
@@ -0,0 +1,2 @@
+<P>Printer <A HREF="/printers/{printer_name}">{printer_name}</A> has been
+modified successfully.
diff --git a/templates/printer-reject.tmpl b/templates/printer-reject.tmpl
new file mode 100644
index 000000000..6cf330c4c
--- /dev/null
+++ b/templates/printer-reject.tmpl
@@ -0,0 +1 @@
+<P>Printer {printer_name} is no longer accepting jobs.
diff --git a/templates/printer-start.tmpl b/templates/printer-start.tmpl
new file mode 100644
index 000000000..e8d9f497f
--- /dev/null
+++ b/templates/printer-start.tmpl
@@ -0,0 +1,2 @@
+<P>Printer <A HREF="/printers/{printer_name}">{printer_name}</A> has been
+started.
diff --git a/templates/printer-stop.tmpl b/templates/printer-stop.tmpl
new file mode 100644
index 000000000..e01b82677
--- /dev/null
+++ b/templates/printer-stop.tmpl
@@ -0,0 +1,2 @@
+<P>Printer <A HREF="/printers/{printer_name}">{printer_name}</A> has been
+stopped.
diff --git a/templates/printers.tmpl b/templates/printers.tmpl
new file mode 100644
index 000000000..8608048ef
--- /dev/null
+++ b/templates/printers.tmpl
@@ -0,0 +1,56 @@
+{#printer_name=0?No printers:
+<TABLE CELLPADDING="0" CELLSPACING="0" BORDER="0" WIDTH="100%">
+{[printer_name]
+<TR>
+ <TH BGCOLOR="#999966"><IMG SRC="/images/left.gif" ALT=""></TH>
+ <TH BGCOLOR="#999966"><A HREF="{printer_uri_supported}">{printer_name}</A></TH>
+ <TH BGCOLOR="#999966">{printer_make_and_model}</TH>
+ <TH><IMG SRC="/images/right.gif" ALT=""></TH>
+</TR>
+<TR>
+ <TD HEIGHT="4"></TD>
+</TR>
+<TR>
+ <TD></TD>
+ <TD VALIGN="TOP"><A HREF="{printer_uri_supported}">
+ <IMG SRC="../images/printer-{printer_state=3?idle:{printer_state=4?processing:stopped}}.gif" BORDER="0" ALT=""></A>
+ </TD>
+ <TD VALIGN="TOP">Description: {printer_info}<BR>
+ Location: {printer_location}<BR>
+ Printer State: {printer_state=3?idle:{printer_state=4?processing:stopped}},
+ {printer_is_accepting_jobs=0?rejecting jobs:accepting jobs}.
+ {?printer_state_message=?:<BR><I>"{printer_state_message}"</I>}
+ {?device_uri=?:<BR>Device URI: {device_uri}}
+ <P>
+ <A HREF="{printer_uri_supported}?op=print-test-page">
+ <IMG SRC="/images/print-test-page.gif" ALT="Print Test Page" BORDER="0"></A>
+ {printer_state=5?
+ <A HREF="/admin/?op=start-printer&printer_name={printer_name}">
+ <IMG SRC="/images/start-printer.gif" ALT="Start Printer" BORDER="0"></A>
+ :
+ <A HREF="/admin/?op=stop-printer&printer_name={printer_name}">
+ <IMG SRC="/images/stop-printer.gif" ALT="Stop Printer" BORDER="0"></A>
+ }
+ {printer_is_accepting_jobs=0?
+ <A HREF="/admin/?op=accept-jobs&printer_name={printer_name}">
+ <IMG SRC="/images/accept-jobs.gif" ALT="Accept Jobs" BORDER="0"></A>
+ :
+ <A HREF="/admin/?op=reject-jobs&printer_name={printer_name}">
+ <IMG SRC="/images/reject-jobs.gif" ALT="Reject Jobs" BORDER="0"></A>
+ }
+ <A HREF="/admin/?op=modify-printer&printer_name={printer_name}">
+ <IMG SRC="/images/modify-printer.gif" ALT="Modify Printer" BORDER="0"></A>
+ <A HREF="/admin/?op=config-printer&printer_name={printer_name}">
+ <IMG SRC="/images/config-printer.gif" ALT="Configure Printer" BORDER="0"></A>
+ <A HREF="/admin/?op=delete-printer&printer_name={printer_name}">
+ <IMG SRC="/images/delete-printer.gif" ALT="Delete Printer" BORDER="0"></A>
+ </TD>
+</TR>
+<TR>
+ <TD>&nbsp;</TD>
+</TR>
+}
+</TABLE>
+}
+<P><A HREF="/admin/?op=add-printer">
+<IMG SRC="/images/add-printer.gif" ALT="Add Printer" BORDER="0"></A>
diff --git a/templates/test-page.tmpl b/templates/test-page.tmpl
new file mode 100644
index 000000000..5c5005438
--- /dev/null
+++ b/templates/test-page.tmpl
@@ -0,0 +1,2 @@
+<P>Test page sent; job ID is <A HREF="/printers/{printer_name}">
+{printer_name}-{job_id}</A>.
diff --git a/templates/trailer.tmpl b/templates/trailer.tmpl
new file mode 100644
index 000000000..74706939a
--- /dev/null
+++ b/templates/trailer.tmpl
@@ -0,0 +1,7 @@
+<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-2000 by Easy Software Products,
+All Rights Reserved.
+</BODY>
+</HTML>
diff --git a/visualc/config.h b/visualc/config.h
new file mode 100644
index 000000000..c4757200c
--- /dev/null
+++ b/visualc/config.h
@@ -0,0 +1,114 @@
+/*
+ * "$Id$"
+ *
+ * Configuration file for the Common UNIX Printing System (CUPS) under WIN32.
+ *
+ * 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.3"
+
+/*
+ * Where are files stored?
+ */
+
+#define CUPS_LOCALEDIR "C:/CUPS/locale"
+#define CUPS_SERVERROOT "C:/CUPS/var"
+#define CUPS_DATADIR "C:/CUPS/share"
+
+/*
+ * 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 PAM stuff?
+ */
+
+#ifndef HAVE_LIBPAM
+#define HAVE_LIBPAM 0
+#endif /* !HAVE_LIBPAM */
+
+/*
+ * Do we have <shadow.h>?
+ */
+
+#undef HAVE_SHADOW_H
+
+/*
+ * Do we have <crypt.h>?
+ */
+
+#undef HAVE_CRYPT_H
+
+/*
+ * Do we have the strXXX() functions?
+ */
+
+#define HAVE_STRDUP
+#define HAVE_STRCASECMP
+#define HAVE_STRNCASECMP
+
+/*
+ * Do we have the (v)snprintf() functions?
+ */
+
+#undef HAVE_SNPRINTF
+#undef HAVE_VSNPRINTF
+
+/*
+ * 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$".
+ */